10
\$\begingroup\$

I made a casino game that runs in the console for my grade 10 final project, and I received a 93% as my grade. I'm looking for feedback in areas such as syntax, user experience and efficiency. I would like to know where I'm doing good, and where I need improvement. I am a relative novice in C++ computer programming, so anything helps. Also, I am willing to grow in knowledge, but please don't start introducing advanced topics.

The "21" game only counts aces as 1 and not 11.

I am using std:: prefixes currently.

#include <windows.h>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cctype>
#include <ctime>
using namespace std;
int main() {
 //Random number generator
 srand (time(NULL));
 //variables
 int choice = 0; //Choice of floor
 int choiceB = 0; //Choice for on-floor menus
 char choiceC = 0; //Choice of hit or stand in 21, or difficulty of Number Guesser
 int randNumCards = 0; //Random number used for new cards
 int randNum = 0; //Random number used for dice roll
 int randNum2 = 0; //Random number used for second dice roll
 int threshold = 0; //Number Banker will stand at
 int balance = 250000; //Bank balance in dollars
 int played = 0; //Checks if user has accessed the Bank at the start of the program
 int total = 0; //Total combination of user's cards in 21
 int bTotal = 0; //Total combination of Banker's cards in 21
 int card1 = rand() % 10 + 1; //First starting card for user in 21
 int card2 = rand() % 10 + 1; //Second starting card for user in 21
 int bCard1 = rand() % 10 + 1; //First starting card for Banker in 21
 int bCard2 = rand() % 10 + 1; //Second starting card for Banker in 21
 int guess = 0; //Number guess for Number Guesser and Crazy Dice
 int tries = 0; //Amount of tries to guess the number in Number Guesser
 int deduct = 0; //Deduction of money from chips/chips from money
 int chips = 0; //Chips; Casino currency
 int bet = 0; //Amount of chips user bets
 cout << R"(
 /__ ___/
 / / / __ ___
 / / // ) ) //___) )
 / / // / / //
 / / // / / ((____
 // ) )
 // / / ( ) ___ _ __ ___ __ ___ / ___
 // / / / / // ) ) // ) ) ) ) // ) ) // ) ) // ) / //___) )
 // / / / / // / / // / / / / // / / // / / // / / //
//____/ / / / ((___( ( // / / / / ((___/ / // / / ((___/ / ((____
 // ) )
 // ___ ___ ( ) __ ___
 // // ) ) (( ) ) / / // ) ) // ) )
 // // / / \ \ / / // / / // / /
 ((____/ / ((___( ( // ) ) / / // / / ((___/ /
===================================================================================================
Copyright (c) 2013 Popstar Games, Inc. No rights reserved. All parts of this work may be reproduced,
distributed, or transmitted in any form or by any means, including photocopying,
recording, or other electronic or mechanical methods, without prior written permission of the
non-existent copyright owner :))" << endl;
 Sleep(3000);
 system("PAUSE");
 system("CLS");
 cout << R"(Warning: Gambling in video games is for entertainment and can create false perceptions of real-world
risks. We encourage you to not replicate these behaviors in real life.)" << "\n" << endl;
 Sleep(3000);
 system("PAUSE");
 while (choice != 4 && (balance + chips) > 0) {
 system("CLS");
 //Prompt user for menu choice
 cout << "Welcome to the Diamonde Casino!" << endl;
 cout << "Would you like to: " << endl;
 cout << " (1): Games - View the Casino games" << endl;
 cout << " (2): Bank - Check your finances" << endl;
 cout << " (3): Rules - View the rules of the games provided" << endl;
 cout << " (4): Exit - Exit the Casino" << endl;
 cout << "Please select a choice: ";
 cin >> choice;
 while (choice != 1 && choice != 2 && choice != 3 && choice != 4) {
 cout << "\nInvalid choice! Please choose 1, 2 or 3: ";
 cin >> choice;
 }
 while (choice == 1) {
 system("CLS");
 if (played == 0) {
 cout << "You start with $" << balance << endl;
 cout << "Go to the bank to trade some cash for chips." << endl;
 system("PAUSE");
 played = 1;
 } else {
 system("COLOR 5b");
 cout << R"(
 ______ __ __ _
 .' ___ | [ | [ | (_)
/ .' \_| ,--. _ .--..--. | |.--. | | __ _ .--. .--./)
| | ____ `'_\ : [ `.-. .-. | | '/'`\ \| | [ | [ `.-. | / /'`\;
\ `.___] |// | |, | | | | | | | \__/ || | | | | | | | \ \._//
 `._____.' \'-;__/[___||__||__][__;.__.'[___][___][___||__].',__`
 ________ __ ( ( __))
 |_ __ |[ |
 | |_ \_| | | .--. .--. _ .--.
 | _| | |/ .'`\ \/ .'`\ \[ `/'`\]
 _| |_ | || \__. || \__. | | |
 |_____| [___]'.__.' '.__.' [___])" << "\n\n" << endl;
 //Prompt user for game choice
 cout << "Welcome to the Gambling Floor!" << endl;
 cout << "What game do you want to play?" << endl;
 cout << " (1): 21 - Classic but fun" << endl;
 cout << " (2): Number Guesser - Take a break from spending your money" << endl;
 cout << " (3): Crazy Dice - Quadruple your earnings!" << endl;
 cout << " (4): Exit the Gambling Floor" << endl;
 cout << "Please select a choice: ";
 cin >> choiceB;
 while (choiceB != 1 && choiceB != 2 && choiceB != 3 && choiceB != 4) {
 cout << "\nInvalid choice! Please choose 1, 2, 3 or 4: ";
 cin >> choiceB;
 }
 if (chips == 0 && choice != 4) {
 cout << "You have no chips! Go to the bank to get some" << endl;
 system("PAUSE");
 break;
 }
 if (choiceB == 1) {
 system("CLS");
 system("COLOR 0e");
 //Prompt user for bet
 cout << "Welcome to 21!" << endl;
 cout << "You have " << chips << " chips." << endl;
 cout << "How many would you like to bet?: ";
 cin >> bet;
 while (bet < 0 || bet > chips) {
 cout << "Invalid input! Please enter a positive integer less than or equal to your chips: ";
 cin >> bet;
 }
 //Calculate new balance
 chips-=bet;
 system("CLS");
 //Output bet
 cout << "Bet: " << bet << endl;
 //Calculate total
 total = card1+ card2;
 //Output cards drawn
 cout << "You drew a " << card1 << " and a " << card2 << endl;
 cout << "Your total: " << total << endl;
 cout << "(H)it or (S)tand?: ";
 cin >> choiceC;
 choiceC = toupper(choiceC);
 while (choiceC != 'H' && choiceC != 'S') {
 cout << "Invalid input! Please enter \"H\" or \"S\": ";
 cin >> choiceC;
 choiceC = toupper(choiceC);
 }
 while (choiceC == 'H') {
 randNumCards = rand() % 10 + 1;
 //Draw new card and output total
 cout << "You drew a " << randNumCards << endl;
 total+=randNumCards;
 if (total > 21) break;
 cout << "Your total: " << total << endl;
 cout << "(H)it or (S)tand?: ";
 cin >> choiceC;
 choiceC = toupper(choiceC);
 while (choiceC != 'H' && choiceC != 'S') {
 cout << "Invalid input! Please enter \"H\" or \"S\": ";
 cin >> choiceC;
 choiceC = toupper(choiceC);
 }
 }
 if (total > 21) {
 cout << "\nYou bust! Your total is " << total << endl;
 }
 threshold = rand() % 4 + 15;
 bTotal = bCard1+ bCard2;
 cout << "\nBanker's turn" << endl;
 cout << "Banker drew a " << bCard1 << " and a " << bCard2 << endl;
 cout << "Banker's total: " << bTotal << endl;
 if (total > 21) {
 cout << "Banker wins!" << endl;
 } else {
 while (bTotal < threshold) {
 randNumCards = rand() % 10 + 1;
 cout << "Banker chose to hit." << endl;
 cout << "Banker drew a " << randNumCards << endl;
 bTotal+=randNumCards;
 }
 //Output winner
 if (bTotal > 21) {
 cout << "\nBanker bust! Banker's total is " << bTotal << endl;
 cout << "You win!" << endl;
 chips+=bet * 2;
 } else if ((bTotal >= total) || (total > 21 && bTotal <= 21)) {
 cout << "Banker stands. Banker's total is " << bTotal << endl;
 cout << "Banker wins!" << endl;
 } else if ((total > bTotal && total <= 21) || (bTotal > 21 && total <= 21)) {
 cout << "Banker stands. Banker's total is " << bTotal << endl;
 cout << "You win!" << endl;
 //Calculate new balance
 chips+=bet * 2;
 }
 }
 //Output total chips
 system("PAUSE");
 system("CLS");
 cout << "You currently have " << chips << " chips" << endl;
 system("PAUSE");
 system("CLS");
 if (balance + chips == 0) {
 cout << "You're out of money and chips! Security will now escort you out";
 for (int i = 0; i < 5; ++i) {
 Sleep(1000);
 cout << ".";
 }
 choice = 4;
 break;
 }
 } else if (choiceB == 2) {
 system("CLS");
 system("COLOR 8f");
 //Prompt user for game difficulty
 cout << "Welcome to Number Guesser!" << endl;
 cout << "What difficulty would you like to play:" << endl;
 cout << " (1): Easy - 1 to 10" << endl;
 cout << " (2): Medium - 1 to 100" << endl;
 cout << " (3): Hard - 1 to 1,000" << endl;
 cout << " (4): Insane - 1 to 10,000" << endl;
 cout << "Please select a choice: ";
 cin >> choiceB;
 while (choiceB != 1 && choiceB != 2 && choiceB != 3 && choiceB != 4) {
 cout << "Invalid input! Please choose 1, 2, 3 or 4: ";
 cin >> choiceB;
 }
 system("CLS");
 if (choiceB == 1) {
 randNum = rand() % 10 + 1;
 //Prompt user for number
 cout << "Guess the number (1 to 10): ";
 cin >> guess;
 while (guess < 1 || guess > 10) {
 cout << "Invalid input! Please enter an integer between 1 and 10 inclusive: ";
 cin >> guess;
 }
 ++tries;
 //Output if number is higher or lower
 while (guess != randNum) {
 if (guess > randNum) {
 cout << "Lower" << endl;
 } else if (guess < randNum) {
 cout << "Higher" << endl;
 }
 //Update statement
 cout << "Guess the number: ";
 cin >> guess;
 while (guess < 1 || guess > 10) {
 cout << "Invalid input! Please enter an integer between 1 and 10 inclusive: ";
 cin >> guess;
 }
 ++tries;
 }
 cout << "You guessed the number in " << tries << " tries!" << endl;
 system("PAUSE");
 } else if (choiceB == 2) {
 randNum = rand() % 100 + 1;
 //Prompt user for number
 cout << "Guess the number (1 to 100): ";
 cin >> guess;
 while (guess < 1 || guess > 100) {
 cout << "Invalid input! Please enter an integer between 1 and 100 inclusive: ";
 cin >> guess;
 }
 ++tries;
 //Output if number is higher or lower
 while (guess != randNum) {
 if (guess > randNum) {
 cout << "Lower" << endl;
 } else if (guess < randNum) {
 cout << "Higher" << endl;
 }
 //Update statement
 cout << "Guess the number: ";
 cin >> guess;
 while (guess < 1 || guess > 100) {
 cout << "Invalid input! Please enter an integer between 1 and 100 inclusive: ";
 cin >> guess;
 }
 ++tries;
 }
 cout << "You guessed the number in " << tries << " tries!" << endl;
 system("PAUSE");
 } else if (choiceB == 3) {
 randNum = rand() % 1000 + 1;
 //Prompt user for number
 cout << "Guess the number (1 to 1,000): ";
 cin >> guess;
 while (guess < 1 || guess > 1000) {
 cout << "Invalid input! Please enter an integer between 1 and 1,000 inclusive: ";
 cin >> guess;
 }
 ++tries;
 //Output if number is higher or lower
 while (guess != randNum) {
 if (guess > randNum) {
 cout << "Lower" << endl;
 } else if (guess < randNum) {
 cout << "Higher" << endl;
 }
 //Update statement
 cout << "Guess the number: ";
 cin >> guess;
 while (guess < 1 || guess > 1000) {
 cout << "Invalid input! Please enter an integer between 1 and 1,000 inclusive: ";
 cin >> guess;
 }
 ++tries;
 }
 cout << "You guessed the number in " << tries << " tries!" << endl;
 system("PAUSE");
 } else if (choiceB == 4) {
 randNum = rand() % 10000 + 1;
 //Prompt user for number
 cout << "Guess the number (1 to 10,000): ";
 cin >> guess;
 while (guess < 1 || guess > 10000) {
 cout << "Invalid input! Please enter an integer between 1 and 10,000 inclusive: ";
 cin >> guess;
 }
 ++tries;
 //Output if number is higher or lower
 while (guess != randNum) {
 if (guess > randNum) {
 cout << "Lower" << endl;
 } else if (guess < randNum) {
 cout << "Higher" << endl;
 }
 //Update statement
 cout << "Guess the number: ";
 cin >> guess;
 while (guess < 1 || guess > 10000) {
 cout << "Invalid input! Please enter an integer between 1 and 10,000 inclusive: ";
 cin >> guess;
 }
 ++tries;
 }
 cout << "You guessed the number in " << tries << " tries!" << endl;
 system("PAUSE");
 }
 system("CLS");
 } else if (choiceB == 3) {
 system("CLS");
 system("COLOR 1f");
 randNum = rand() % 6 + 1;
 randNum2 = rand() % 6 + 1;
 //Prompt user for bet
 cout << "Welcome to Crazy Dice!" << endl;
 cout << "You have " << chips << " chips." << endl;
 cout << "How many would you like to bet?: ";
 cin >> bet;
 //Calculate bet
 while (bet < 0 || bet > chips) {
 cout << "Invalid input! Please enter a positive integer less than or equal to your chips: ";
 cin >> bet;
 }
 //Calculate new balance
 chips-=bet;
 //Prompt user for number
 cout << "What do you think the dice roll will be (between 2 and 12)?: ";
 cin >> guess;
 while (guess < 2 || guess > 12) {
 cout << "Invalid input! Please enter a number between 2 and 12 inclusive: ";
 cin >> guess;
 }
 cout << "\nThe dice are rolling";
 for (int i = 0; i < 5; ++i) {
 Sleep(1000);
 cout << ".";
 }
 cout << "\n";
 cout << "The dice rolled are " << randNum << " and " << randNum2 << endl;
 cout << "Your guess is " << guess << endl;
 //Output if user wins or loses
 if (guess == (randNum + randNum2)) {
 cout << "You win!" << endl;
 chips+=bet * 4;
 } else {
 cout << "You lose!" << endl;
 }
 //Output total chips
 system("PAUSE");
 system("CLS");
 cout << "You currently have " << chips << " chips" << endl;
 system("PAUSE");
 system("CLS");
 if (balance + chips == 0) {
 cout << "You're out of money and chips! Security will now escort you out";
 for (int i = 0; i < 5; ++i) {
 Sleep(1000);
 cout << ".";
 }
 choice = 4;
 break;
 }
 }
 //Reset variables
 randNum = 0;
 randNum2 = 0;
 card1 = rand() % 10 + 1;
 card2 = rand() % 10 + 1;
 bCard1 = rand() % 10 + 1;
 bCard2 = rand() % 10 + 1;
 tries = 0;
 bet = 0;
 system("COLOR 0f");
 //Prompt user to stay/leave
 cout << "Do you wish to stay on the Gambling Floor (enter 1 to stay, 2 to leave the floor, and 3 to exit the Casino)?: ";
 cin >> choiceB;
 while (choiceB != 1 && choiceB != 2 && choiceB != 3 && choiceB != 4) {
 cout << "Invalid input! Please enter 1, 2 or 3: ";
 cin >> choiceB;
 }
 if (choiceB == 2) {
 choice = 0;
 break;
 } else if (choiceB == 3) {
 choice = 4;
 break;
 }
 }
 }
 if (choice == 2) {
 system("CLS");
 system("COLOR 72");
 if (played == 0) played = 1;
 cout << R"(
/$$$$$$$ /$$
| $$__ $$ | $$
| $$ \ $$ /$$$$$$ /$$$$$$$ | $$ /$$
| $$$$$$$ |____ $$| $$__ $$| $$ /$$/
| $$__ $$ /$$$$$$$| $$ \ $$| $$$$$$/
| $$ \ $$ /$$__ $$| $$ | $$| $$_ $$
| $$$$$$$/| $$$$$$$| $$ | $$| $$ \ $$
|_______/ \_______/|__/ |__/|__/ \__/)" << endl;
 //Prompt user for exchange choice
 cout << "\nWelcome to the Bank!" << endl;
 cout << "Would you like to: " << endl;
 cout << " (1): Convert money to chips" << endl;
 cout << " (2): Convert chips to money" << endl;
 cout << "Please select a choice: ";
 cin >> choiceB;
 while (choiceB != 1 && choiceB != 2) {
 cout << "\nInvalid choice! Please choose 1 or 2: ";
 cin >> choiceB;
 }
 if (choiceB == 1) {
 system("CLS");
 //Prompt user for amount to exchange
 cout << "Your current balance is: $" << balance << endl;
 cout << "You have " << chips << " chips." << endl;
 cout << "How much would you like to convert to chips?: $";
 cin >> deduct;
 while (deduct < 0 || deduct > balance) {
 cout << "Invalid input! Please enter a positive integer less than or equal to your balance, or zero: ";
 cin >> deduct;
 }
 //Calculate new balance
 balance-=deduct;
 chips+=deduct;
 cout << "\nYour new balance is: $" << balance << endl;
 cout << "You now have " << chips << " chips." << endl;
 system("PAUSE");
 choiceB = 0;
 } else if (choiceB == 2) {
 system("CLS");
 //Prompt user for amount to exchange
 cout << "Your current balance is: $" << balance << endl;
 cout << "You have " << chips << " chips." << endl;
 cout << "How many chips would you like to convert to cash?: ";
 cin >> deduct;
 while (deduct < 0 || deduct > chips) {
 cout << "Invalid input! Please enter a positive integer less than or equal to your chips, or zero: ";
 cin >> deduct;
 }
 //Calculate new balance
 balance+=deduct;
 chips-=deduct;
 //Output new balance
 cout << "\nYour new balance is: $" << balance << endl;
 cout << "You now have " << chips << " chips." << endl;
 system("PAUSE");
 choiceB = 0;
 }
 } else if (choice == 3) {
 system("CLS");
 //Output game rules
 cout << "Game Rules:" << endl;
 cout << "\n -21: Try to get as close to or equal to 21 and try to get more than the Banker, without going over 21." << endl;
 cout << " If you go over, you bust! Same for the Banker. If you win, you receive double the benefit" << endl;
 cout << "\n -Number Guesser: Try to guess the random number in the lowest amount of tries." << endl;
 cout << "\n -Crazy Dice: Try to guess the dice roll. If you lose, you lose your bet. If you win, you receive quadruple your bet.\n" << endl;
 system("PAUSE");
 }
 system("CLS");
 system("COLOR 07");
 }
 system("CLS");
 balance+=chips;
 //Output user balance
 if (balance + chips == 0) {
 cout << "Your broke!\nBetter luck next time!" << endl;
 } else {
 cout << "You traded in " << chips << " chips for cash." << endl;
 cout << "Your final balance is $" << balance << endl;
 }
 system("PAUSE");
 system("CLS");
 //Special ending - 1 in 7 chance
 if (rand() % 7 == 0) {
 system("CLS");
 Sleep(1000);
 cout << "You leave the warmth of the Casino, into the rain." << endl;
 Sleep(3000);
 cout << "\nAs you stroll through the cold wet night, a shadowy figure approaches you." << endl;
 Sleep(2500);
 cout << "It says, \"You should not be here.\"" << endl;
 Sleep(2500);
 cout << "\nNext thing you know, everything turns black." << endl;
 Sleep(3000);
 system("CLS");
 Sleep(3000);
 } else {
 cout << "See you again soon!" << endl;
 }
 return 0;
}
pacmaninbw
26.2k13 gold badges47 silver badges113 bronze badges
asked Jul 21 at 20:53
\$\endgroup\$
3
  • 8
    \$\begingroup\$ Why does it say "Copyright (c) 2013 Popstar Games" ? Apart from that, the code looks very procedural to me (even in 2013 this was not so common any more). The code is executing from top to bottom in one big process, with a ton of nested while loops. That makes it hard to find bugs or change the functionality later. You can improve that by defining functions and keeping track of state centrally. And since you use C++ you could create classes? \$\endgroup\$ Commented Jul 22 at 10:33
  • 3
    \$\begingroup\$ @Kokodoko For the copyright, I added it as an easter egg because GTA V (made by 'Rockstar' Games) came out in 2013, and that's where I got the casino and logo idea (Diamond Casino in GTAO) from. As for your other feedback, I've said below that I am rewriting my code. I only copy-pasted this from the .cpp file, only removing private info in the process. \$\endgroup\$ Commented Jul 22 at 20:45
  • \$\begingroup\$ Still not sure what you mean by "I only copy>pasted", but I hope the answers below will help you improve the code :) \$\endgroup\$ Commented Jul 23 at 9:36

5 Answers 5

11
\$\begingroup\$

Functions

Your 600 lines of code are in a single function (main). It is good practice to break up the code into multiple smaller functions. There is plenty of opportunity to do so here in the name of ...

DRY

For example, for the number guesser part of the code, these lines are repeated twice:

cout << "Guess the number (1 to 10): ";
cin >> guess;
while (guess < 1 || guess > 10) {
 cout << "Invalid input! Please enter an integer between 1 and 10 inclusive: ";
 cin >> guess;
}

Those lines can be factored out into a function where the input is the maximum guess value and the return value is the user input guess. This function can be used with any maximum (10, 100, etc.). In that way, your 4 sets of the repeated lines simply become one line each.

There are likely other opportunities for reducing the repetition in your other games as well.

Simpler

In this code:

//Output if number is higher or lower
while (guess != randNum) {
 if (guess > randNum) {
 cout << "Lower" << endl;
 } else if (guess < randNum) {
 cout << "Higher" << endl;
 }

there is no need for the else if condition. Since the while condition already checks that the 2 values are not equal, and the if checks for "greater than", you just need an else to check for "less than":

 if (guess > randNum) {
 cout << "Lower" << endl;
 } else {
 cout << "Higher" << endl;
 }

This makes the code simpler to read, and it is more efficient because there is no need to check if guess < randNum.

Layout

You have inconsistent whitespace around operators. For example, this line:

balance-=deduct;

would be easier to read with whitespace around -=:

balance -= deduct;

Documentation

It would be helpful to add a comment block at the top of the code to summarize its purpose:

/*
Console-based Casino Game.
 BlackJack
 Number-guessing
 Whatever else...
*/

Typo

In this line:

cout << "Your broke!\nBetter luck next time!" << endl;

"Your" should be "You're" (as in "You are").

Fe2O3
5,6081 gold badge9 silver badges26 bronze badges
answered Jul 22 at 11:27
\$\endgroup\$
0
8
\$\begingroup\$

General Observations:

First a warning, on the Code Review site we can only review code that you have written yourself, we can not review code written by others (The copyright is a problem).

The variable names are reasonably good. There are no global variables and that is good.

Computer programming is typically a case of breaking problems or tasks into smaller and smaller pieces until they are easy to program. It isn't clear that your teacher taught you about functions and procedures, but that would have helped you a lot.

There are much better random number generators available in C++ than srand().

Avoid using namespace std;

If you are coding professionally you probably should get out of the habit of using the using namespace std; statement. The code will more clearly define where cout and other identifiers are coming from (std::cin, std::cout). As you start using namespaces in your code it is better to identify where each function comes from because there may be function name collisions from different namespaces. The identifiercout you may overload within your own classes, and you may overload the operator << in your own classes as well. This stack overflow question discusses this in more detail.

Magic Numbers

There are Magic Numbers in the main() function (1, 2, 3, ...), it might be better to create symbolic constants for them to make the code more readable and easier to maintain. These numbers may be used in many places and being able to change them by editing only one line makes maintainence easier. It is also possible to use enumerators (enum).

Numeric constants in code are sometimes referred to as Magic Numbers, because there is no obvious meaning for them. There is a discussion of this on stackoverflow.

Complexity

This was observed in the answer by toolic. Generally functions or procedures are limited to one screen or less (less than 55 to 60 lines). It is very difficult to follow the flow of the logic in anything larger.

The function main() is too complex (does too much). As programs grow in size the use of main() should be limited to calling functions that parse the command line, calling functions that set up for processing, calling functions that execute the desired function of the program, and calling functions to clean up after the main portion of the program.

There is also a programming principle called the Single Responsibility Principle that applies here. The Single Responsibility Principle states:

that every module, class, or function should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by that module, class or function.

There are at least 3 possible functions in main().

  • A menu function that drives the rest of the program.
  • A banking function.
  • A function to implement each of the games separately.
answered Jul 22 at 13:30
\$\endgroup\$
6
  • 1
    \$\begingroup\$ "Generally functions or procedures are limited to one screen or less". My former boss and his ~300 lines functions on a 34" vertical display would agree :) \$\endgroup\$ Commented Jul 23 at 14:25
  • \$\begingroup\$ It's your decision how you wish to compose each sentence, and why the information might be relevant here. Concerning to me is that "operator override" is still present in that paragraph... We all want the OP to set off on the right foot... \$\endgroup\$ Commented Aug 23 at 20:52
  • 1
    \$\begingroup\$ @Fe2O3 Is this better? \$\endgroup\$ Commented Aug 24 at 13:16
  • 1
    \$\begingroup\$ Don't know that my "stamp of approval" is needed, but yes! I've been skewered on SO before for sometimes being a little loose with terminology. FWIW, the OP may benefit from 'picking up' "overload" from several sources in next year's class, too. Repetition is how those neurons learn to cooperate with one-another... Cheers! :-) (Feel free to "tidy up" these comments if you like. I hope you like this version better, too. :-) \$\endgroup\$ Commented Aug 24 at 14:06
  • 1
    \$\begingroup\$ @Fe2O3 I just wish you had made these comments a few years ago, I've used this paragraph before. :). \$\endgroup\$ Commented Aug 24 at 14:21
5
\$\begingroup\$

Others have already pointed out that you should use functions (or classes with methods), let me elaborate a bit why you should do that.
In short: It helps yourself.

1. To avoid repetitions:
Code like

while (guess < 1 || guess > 10) {
 cout << "Invalid input! Please enter an integer between 1 and 10 inclusive: ";
 cin >> guess;
}

can easily be encapsulated in a function like a bool validateInputNumber(int guess).
It makes the code reading much easier, but what is more important, you implement this code only once. That means in case of required changes, you only need to fix this code once and cannot miss any duplicates.

2. To put the code into more managable units
Having a small and independend piece of code makes it easier to read and test.
Especially the testing part will get more and more importand the bigger your code base gets, e.g. after adding 10 different slot machines.
Searching for bugs in a wall of code is the worst and you may not want to re-run the entiry casino to test a bug in one of your games, instead execute the game directly.

3. To make the calls more readable
For some not so obvious code you can produce functions which make the usage more obvious and prevents to decyper the purpose from the algorithm.
Maybe not a very good example but a call like rand() % 1000 + 1 could be replaced by a function like randomFromTo(1, 1000).
In short, the easier to understand the code the less errors you will make.

4. To encapsulate dependent stuff for maintainability or testability:
Calls like system("CLS") will work only on a Windows system.
Here a hypothetic question: How much time and effort are you willing to spend to make the program runnable on Linux?
The smart answer should be "as few as possible".
Put such dependencies into extra functions (preferrably even into separate modules). The same would go for any other dependencies, e.g. use a graphical window instead of a console, play some beeps or entire audio files ...
Then you can adopt your code to a different environment within a small area while the main program logic can stay untouched.
Another example could be to capsulate the random number generator, e.g. to provide certain fixed numbers for testing (or cheating).

I feel like I even missed some points, but I hope you got a bit of the idea.
Use functions to keep the code in small, flexible, readable and reusable pieces.

answered Jul 23 at 17:31
\$\endgroup\$
3
\$\begingroup\$

Before the criticism, the layout must be complimented.
For a beginner's code, the consistent use of braces, parentheses and whitespace is superior.
Generally speaking, the code is quite readable.


Not really very advanced for a term/year project

Two other answers have pointed out that this C++ program does not make use of any of the language's facilities.

  • Every cin/cout could be replaced with a C equivalent to make a C program.
  • Copy/paste/adapt replication of code
  • No use of functions, let alone classes
  • All variables within main() are, therefore, effectively 'global' scope.

Stripping away padding

  • 630 lines initially
  • 540 lines after removing unnecessary final story and ASCII art graphics
  • 494 lines without system() and Sleep() (UX niceties)
  • 393 lines after removing 3 of 4 'duplicate' versions of "Guess" number game
  • 236 lines after removing "simpler dialog" cout/cin
  • 170 lines after removing verifying int or char with only if() and while()

Conclusion: Roughly 3/4 of this source code is repetitious user interaction that, in the opinion of this reviewer, indicates too much time spent on "window dressing" at the expense of time facing more difficult semantic challenges (like "Ace = 1 or 11").

The 'bones' that remain are a handful of simple integer calculation/assignment statements amongst only some remaining if() and while() statements. There's no demonstration of floating point i/o or calculations. User input is always integer input or a single character, so there's no string manipulation challenge overcome. In short, this seems an overblown version of:

"Please enter your age" 
age = get_integer()
"You are ", age, "years old"

that being an example of one of the first programs any beginner writes as their first or second program.

Use of subordinate functions (and even do/while()) would have demonstrated more attention to design of the source code algorithms. "Parameter driven" functions would have shown a desire to "do more with less". Concise code is better code because there are fewer opportunities for bugs to crawl in, and it is easier to maintain. (Example: To add another "Guess Number" level, only a couple of lines would need to be tweaked versus copy/paste of ca. 30 lines of code.)


Helpful tip #0 - getting started

Some hints with full acknowledgement of the OP's code being that of a youngish student's (presumed to be first) "major" coding project:

The context for evaluation is likely to be layered.

  • From "the outside":
    • reasonably good "appearance"
    • understandable/intuitive for a new user,
    • functions correctly for a "cooperative" user,
  • From "the inside":
    • code is "readable" and "sensible" from 1000ft view,
    • 2-3ft view finds reasonable subset of course material demonstrated,
    • code is "readable" reflecting its having been thoughtfully crafted,
    • comments stating "why?" explain coder's "less obvious" decisions.

For a "one coder" project, recommend much effort be devoted to dividing time and attention to best results sought (i.e. "highest grade").

For a new student of any programming language, I recommend most effort be directed to what the executable will do "out of sight". Start by writing (in words), or sketching-out, the project's objectives, recursively breaking each down into "subcomponents". Look for commonalities that might be shared (coded once, used often). Think about how each smaller fragment is intended to "fit back together" as intermediate or major components.

Every minute spent "planning" will save 30-60-240 minutes of "coding, and then re-coding". Every alteration after code has been "tested" VOIDs those test results, and testing must be repeated as code evolves.

Lexicon (aka "jargon"):
The OP is likely, sometime in the future, to encounter the term "Doc/View". Its essence is that "Doc" means a program's internal representation of the data it works with, and "View" corresponds to how some-or-all of that data is presented to the program's user as information for the user to use. (A very loose definition.)

There is no "deck of cards" inside this software casino, but there can be a representation of a deck of 52 individual cards that is the "Doc" aspect. How each of the 52 values is presented to the user makes up the program's "View" aspect. For some programs, the View(s) is/are sometimes called "skin(s)". Some consumer apps allow each user to choose one-or-another skin from a gallery.

Much like the duality of the curvature of spacetime, "Matter tells space how to curve, and curved space tells matter how to move." --attributed to John Wheeler. The Doc's purpose is to best serve the View, and the View's purpose is to be a bridge between the user and the Doc.

Appreciating the significance of this would allow the OP to code either aspect of the program while "wearing different hats" to conceive of how best to 'interface' between any Doc via any View(s) with the human user.

FWIW:
From 30,000ft up, most users would regard ChatGPT as a "console app".
It's "View" has only a few "simpler" options available to the user, including input/output.
But, the app's "Doc" implements a very advanced scheme managing a very advanced "data model" to achieve its purpose.
There's a lot of sophistication going-on "under the hood".
You can envision this Casino app in a similar light.

Analogy:
Were I to evaluate a student's "multi-function calculator" program, I would be less interested in its 'skin' (that could be replaced) than its 'doc'. Has this coder done the research to prevent "dividing by zero" from crashing the executable? Does the executable protect its operation from "overflow" delivering erroneous results? Appearance and arrangement of the GUI's 'digit keys' should be easily revisable, so less significant overall (imo).

Too often, time runs out, and work must be submitted "as is".

A "good strategy" to follow when writing an exam is to "get in the groove" by first scanning-for and knocking-off the easily answered questions, then loop back to target the intermediate effort questions. Don't get stuck on question #3 while the clock ticks down. A similar strategy can be applied to a novice's early works.

The "dreamt-of" aspiration might be "a bit too much" to achieve, given real world constraints.
E.g. music lessons, football practice, time with friends.

Once a minimum "deliverable" is "in the bank", preserve it as insurance, then set about making iterative "improvements", each a "better deliverable". For the casino project, get "Dice roll" working (simpler code), then "Blackjack" rudiments, then "Blackjack" fully operational, then "Number guess". I put Blackjack as #2 on my list as "Dice" would be relatively easy to "bank", and "Number Guess" is another 'simpler' bit of coding. Blackjack demonstrates development and use of more 'complicated' logic. Grading, in my book, mirrors how much challenge the student undertook.

The "logic replication" in this code can (mostly) be reduced to:
-- prompt
-- response
-- evaluate
-- react
-- repeat.
Two or three instances of this logic would likely have been sufficient to demonstrate the coder's understanding of design. Once those have been established, the student could have moved on to more 'difficult' challenges such as dealing with those two values of an Ace in Blackjack instead of coding additional instances of the same fundamental logic. How to handle being dealt a 4 followed by two Aces? 15 suggests 'hit' after two cards dealt, but how does 15 become 6 after the third card? IMO, this represents the "interesting" challenges not addressed by the code.

Again, it is my opinion that "garnishes" of 'appearance' are less important than reasonable sophistication shown in the "backroom" processing and code. IOW, in this example, I would assess more credit for solving "Ace = 1 or 11" than the parking lot mugging story, or the screen's colour changing.

The meta-objective of this "educational project" is evaluation of the student's demonstrated proficiency with course material covered. However, this is just my opinion. YMMV.


Helpful tip #1

 while (choice == 1) {
 //Reset variables
 int randNum = 0;
 int randNum2 = 0;
 int card1 = rand() % 10 + 1;
 int card2 = rand() % 10 + 1;
 int bCard1 = rand() % 10 + 1;
 int bCard2 = rand() % 10 + 1;
 int tries = 0;
 int bet = 0;

Moving the "reset" operation from the bottom to the top of the loop's body, AND using the same lines to 'declare' the variables, both reduces the volume of code and limits the scope of those variables. (I've not tried to check if all are accounted for. 600 lines of code is simply too much.)

Here, every time any game completes, four Blackjack cards are (re-)dealt and other variables' values are reset. Efficient code should correctly do all that it must do, but no more. Needlessly burning these unnecessary processor cycles leans toward wasteful. Needs more attention paid to "localising" variables and operations.

This can be improved by reducing the scope of the variables (as would result from use of functions and/or class objects.)

  • Re-use of logic (and temporary variables) that is called from many locations is good.
  • Re-use of long duration variables (eg: guess) for unrelated purposes is bad.

Helpful tip #2

Where applicable, use "0 - Exit" (or 1) at the TOP of the menu of options. Later versions may rearrange the order of games, or more games may be added. It's very likely that only 'Exit' will remain consistent across all menus and/or later versions. Users appreciate consistency.


Helpful tip #3

If each of these three games had been developed independently (as standalone functions) using only the lightest 'scaffolding' of extra code like #include blah and a main(), then each would appear here as its own function, all three united under some over-arching "menu()" facility.


Helpful tip #4

User input must always be handled as if it were toxic waste.

Try typing "foobar" when asked to enter some integer amount (e.g. buying chips). How does the program react? How does a program detect & recover from bad user input (or corrupt data loaded from somewhere)?

Detecting/dealing-with inevitable bad data is more important than the code that works, but only works on sunny days.

The recent Crowdstrike calamity is said to have cost businesses over 10ドル billion. It's unknown how many millions of lives were affected by (instances of) a program that did not thoroughly check & reject bad data.

The cause of the 370ドル million Ariane 5 launch disaster is reported to have been a simple case of integer overflow in a guidance system.

Mighty Smaug was defeated because the gigantic dragon had one small gap in his scaly armour. (Reminiscent of Achilles and his unprotected heel.)

Do not lose sight of the importance of writing reliable code. Mistakes and accidents happen, and malicious actors abound. Develop a habit of striving to detect and protect your program's execution from invalid data.

See also: Murphy's Law and For want of a nail.


Helpful tip #5

Garnered from movies such as "Rain Man" and "Oceans 11", I'm pretty sure a "Blackjack Shoe" starts out with 6 decks of cards (312 cards), scrambled in unpredictable sequence for dealing.

Consider that 4-of-13 cards in any suit (10, Jack, Queen, King) are all worth 10 points.
The chance of being dealt a 10 point card is 4:13, versus 1:13 for Ace..9.

Dealing Blackjack hands from an initialised array (that has been thoroughly shuffled) would create a much more Casino-like experience for the user. Coding this would demonstrate the OP has an understanding of using arrays (or other C++ objects.)

There are other unaddressed Blackjack matters, too, such as "dealer stands on 17". In the usual play of the game, the dealer's first two cards are dealt, one face-up revealing its value, before the player has the option of 'hit' or 'stand'...


Helpful tip #6

 //Prompt user for menu choice
 cout << 
 "Welcome to the Diamonde Casino!\n"
 "Would you like to:\n"
 " (1): Games - View the Casino games\n"
 " (2): Bank - Check your finances\n"
 " (3): Rules - View the rules of the games provided\n"
 " (4): Exit - Exit the Casino" << endl;
 cout << "Your choice: ";

According to the manual, endl not only appends a 'newline' character, but it also causes the output buffer to be flushed each time it is sent to cout.

Since you're using '\n' in other locations, this kind of operation can be simplified to use fewer cout invocations, and fewer endl 'tokens'. The compiler will 'see' a series of strings with only whitespace between and concatenate them for you.

In fact, as there's more than one menu in the program, write a function that receives the long menu string and the range of acceptable values (1 and 4). The function prompts and then assesses the user input returning a valid value (and, perhaps, -1 to indicate unrecoverable user error...)


Helpful tip #7

 cout << R"(Warning: Gambling in video games [...]
[...] replicate these behaviors in real life.)" << "\n" << endl;

That isolated newline is distracting. Better:

 cout <<
R"(Warning: Gambling in video games [...]
[...] replicate these behaviors in real life.
)" << endl;

If using a raw string that has one end-of-line among its raw characters, why not let it have two EOL characters?

C++ coder's tip: Where a single character is fed into an output stream (e.g. whitespace between integer values), it is preferable to use apostrophes versus coding any single character as a string using double quotes.

Eg: std::cout << val_1 << '\n' << val_2 << std::endl;

Then, cout deals with one character instead of using all the machinery to handle a string of initially unknown length and content.

BTW: There is no reason for this overly-long line to be a raw string.


Helpful tip #8

For all three games, there is no way for the user to quit a game other than playing through to the game's conclusion. A single annoying user experience has the power to negate MANY positive gaming experiences. Users will return to wherever they consistently have enjoyable experiences.

E.g. With the "Number Guesser" section, most users selecting option 4 (1 to 10,000) will become bored after mere seconds of use (imo).

Although it requires more effort, providing a quit option during play is (imo) a minimal user expectation; more important than ASCII art that frequent users are likely to quickly learn to ignore.

The dark story told to some users (1 chance in 7) before terminating delays the end of execution. Learn about Splash Screens and search out the web's opinion of irrelevant "marketing flourishes".

Most users rapidly become annoyed with programs that impose "good idea" entertainment but don't properly meet their expectations in other areas.

Novice coders need to learn when, where and how "Easter Eggs" may be appreciated and enjoyed.

Don't waste time with "flash" embellishments whose "wow factor" quickly transforms into annoyance.


Helpful tip #9 - bug (minor)

The variable names are poor.

The variable names are indistinct and all have 'function scope' within main(), the only function in the code.

One (minor) bug, corrected below, has gone unnoticed. It may never be detected until a paying customer complains.

 ...
 while (choiceB != 1 && choiceB != 2 && choiceB != 3 && choiceB != 4) {
 cout << "\nInvalid choice! Please choose 1, 2, 3 or 4: ";
 cin >> choiceB;
 }
 // if (chips == 0 && choice != 4) { // wrong variable used here
 if (chips == 0 && choiceB != 4) {
 cout << "You have no chips! Go to the bank to get some" << endl;
 ...

The similar token names have hidden this bug.

Use meaningful, distinctive token names. Good naming leads to self-documenting code that requires fewer explanatory comments.

Aside:

 // while (choiceB != 1 && choiceB != 2 && choiceB != 3 && choiceB != 4) {
 while( choiceB < 1 || 4 < choiceB ) { // user's value outside of range
 ... // handle invalid value

is a concise, idiomatic C/C++ predicate testing that an integer value is OUTSIDE of the contiguous range of acceptable values. The optional comments make clear the coder's intent.

Perhaps finding only one minor bug in this beginner's code portends a bright future as a coder. Perhaps being almost bug-free is a result of replication of a single, simple design pattern (re-)implemented at various scales.

Working code is highly laudable. It is less laudable when, as a school project meant to demonstrate proficiency, the code avoids challenges.

Functions and/or classes would limit the scope of variables. Had this logic been isolated into either, this bug may never have occurred, or the compiler may have been able to flag the error.

School is the time to struggle through learning experiences to be better prepared for the time when making mistakes will have significant consequences. You need to experiment and learn to use the full range (or an appreciable subset) of tools and techniques that are available to you. One does not become a proficient swimmer by splashing only in the shallow end of the pool for extended periods.

For the student OP:
There a few ways this (minor, and well hidden) bug may have been avoided. The simplest for you would have been to use more distinctive variable names.
The reason an experienced coder makes greater effort in this regard is that, in this particular case, choice relates to one menu of four entries, and choiceB relates to different menu of four entries. As it stands, this error may have no consequences even with thorough testing, so the code is assessed as "sound". During the next enhancement, one of those menus may gain extra options and "energise" this error unleashing aberrant behaviour. Oftentimes, the 'buggy code' is not in-frame of any "new work". The coder can spend days searching their new work looking for the cause of a problem, eventually to discover that the bug "over there" has come to life, the sleeper bug in thoroughly tested code has been awoken. One of the most frustrating experiences of coding; makes a rabbit want to kick a bear.
Use distinctive and meaningful token names.


Helpful tip #10

The "Bank" (aka 'Cashier') section is over 70 lines of code,
primarily to separate conversions "cash to chips" from "chips to cash".

This can be simplified using one instance of:

 ...
 int deduct = 0; // variable defined inside block reducing its scope
 int limit = ( choiceB == 1 ) ? balance : chips; // ternary operator. Something new.
 ...
 while( deduct < 0 || limit < deduct ) { // Does 0 make sense? Exchange 0 for 0???
 ... // user's input value out of range
 ...
 if (choiceB == 1) deduct = 0 - deduct; // minus -'ve is same as add +'ve
 balance += deduct;
 chips -= deduct;

The "Bank" section should be a function (or class) handling the logic and adjusting the user's cash/chips amounts.

PS: the generic variable named balance should probably be renamed to cash.
The comment //Calculate new balance explains adjusting the 'balance' of the user's chips.

PS: the UX inconsistently occasionally refers to user's "cash" as "money". Consistency is a virtue.

A similar technique of using a temporary variable as the upper limit of ANY "Number Guessing" game would have allowed all four instances totalling ~120 lines to be only ~30 lines of code instead. With four different upper bounds, there was an opportunity to demonstrate use of switch, case, default, and break or, more advanced, using /*FALLTHROUGH*/ to be 'clever'.


Helpful tip #11

C/C++ (and other languages) are "structured". This means, in part, that nested, indented blocks of code, when viewed from a distance, can reveal an overall outline providing a coarse indication of its complexity.

This code's structure is interrupted by three 'blobs' of long-ish raw string literals that interrupt structured indentation.

Below is an alternative that uses #include in an unconventional way.

  • This is unconventional, but neatly solves one source code layout problem for this particular instance.
  • This is one of several alternatives that could separate the three 'blobs' from logic flow through the code.
  • I've presumed implementing, compiling and linking multiple code modules to be beyond the OP's nascent capabilities.

Caveat: This solution is a measured and comprehensible workaround. The technique is NOT recommended for general use as it is likely to give some readers indigestion. Refer to the K&R Bible, "The C Programming Language", section 4.11.1 File Inclusion for more information.

In the example below, two representative 'blobs' of raw string literals have been put into a separate file named casino.art. This 'placeholder' file can be used by the coder(s) while the Graphic Designers fuss with the ASCII art graphics. When the program code is fully operational, and the artists deliver their artwork(s), this file can be updated with that 'art' with little effort.

Note that the content of this file not only looks like code, it is "compile-able" C/C++ code (defining two char pointers to raw string literals plus a few comments.)

It's expected that the OP has already seen, in a textbook/classroom, a char * pointer used in this simple fashion.

// casino.art - collection of ASCII art raw strings
char *foo_art = // qualifiers like 'const' etc. come later
R"(******
 foo foo foo
 foo foo foo foo
******)";
//
//
//
char *bar_art =
R"(******
 bar bar
******)";

Then, use the #include directive - in an unconventional way - to have the preprocessor splice those lines into the primary source code as file scope variables. When the preprocessor phase concludes, the compiler will begin its parsing of the entire translation unit.

NB: #include means to copy the named file's content verbatim at the point in the source code where the preprocessor directive appears.

// casino.cpp - simulate a "Casino Experience"
// Note: A good habit (shown above) is to record
// each file's name and purpose at the top of the file
#include <windows.h>
...
#include "casino.art" // constant strings 'spliced-into' source code right here
...
int main() {
 ...
 ...
 std::cout << foo_art << std::endl; // output one 'blob' string
 ...
 ...
 std::cout << bar_art << std::endl; // output other 'blob' string
 ...

In this way, large blobs of raw string data that require non-conforming formatting are separated from source code statements. The source code structure of the .cpp file remains clean.

These 'blobs' could have been gathered together at the top or bottom of this single source file (out-of-the-way while not really out of the way.) Keeping them external to the source code, improves the code's appearance, and enables the artwork to be developed by the graphic artists while the source code is developed and tested.

Alternatively, runtime loading of artwork/data from an external file has a number of drawbacks:

  • Validated file I/O may be beyond the OP's current skill level.
  • Dynamic loading would (usually) involve validated dynamic memory allocation which is a "more advanced" topic.
  • The distributable executable would not be entirely self-contained, but spread across multiple files instead.
  • Companion data file(s) for customers to install may become misplaced, corrupted or out-of-date. (On-site customers could easily replace the intended ASCII art with whatever they choose. No sophistication required.)
  • Portability concerns/issues arise for external data just as they would for executables.

Some might consider this to be an abhorrent abuse of #include. Reiterating the above, this "technique/trick" is very simple to write and easy to understand.

The Holy Grail of software development is coding clear, concise, effective, minimal and sensible solutions developed to meet specific requirements while bound by constraints.

When the OP's skillset grows to working with multiple app source code files and necessary app specific header files, this version could be quickly adapted to conventional coding practices.

The ASCII art is data, and placing ANY data in 'header files' is NOT a good practice. As stated in the noted K&R reference, a conventional "header file" is for

"#define statements and extern declarations, [and] function prototype declarations".

Note: "extern declarations", not "definitions".

One should consider carefully before considering using #define to create a preprocessor token whose value is significant (such as a long string, especially a wrapped raw string). Although they work, the token does NOT make it through preprocessing to the compiler. The consequence is that the token will not be searchable by a debugger that looks at the binary executable. Better to use named variables as demonstrated above.

If the term assignment requires submitting source code for compilation, the companion file casino.art must be included in the submission. The OP can explain its purpose in the submission description, or even add source code comments that explain what's been done and why.

Further notes:

  • While 80 character wide output lines are common, this artwork uses 100 character wide text. (In the "Rules" section, line length reaches ~120.) These may 'disappear' off the right side of a user's small console window, or lines may be wrapped into an incomprehensible mess. Coders should make every effort to work within "reasonable" expectations of a user's terminal window.

  • Caveat: Some IDE editors may be configured to automatically replace, where possible, contiguous leading SP (space) characters with an appropriate number of TAB characters when loading source code or a text file for editing (or compiling). There is no guarantee that a teacher or a buddy, using their own computer, will work with the exact-same ASCII art as raw string literals as the author, or that a version with invisible TABS will corrupt a user's experience of the program because their console regards one tabstop as being up to 8 characters wide.

  • The different 'typefaces' used in the OP's ASCII art are inconsistent. Whether this is good or bad project design is a matter of opinion.

  • This reviewer wonders why each separate game did not have its own ASCII art "banner" welcoming the user to different casino 'rooms'.


Helpful tip #12 - Execution flow

Consider:

 if (played == 0) {
 cout << "You start with $" << balance << endl;
 cout << "Go to the bank to trade some cash for chips." << endl;
 system("PAUSE");
 played = 1;
 } else {
 ...

versus

 if( !played ) {
 cout << "You start with $" << balance << '\n'
 << "Go to the bank to trade some cash for chips." << endl;
 system( "PAUSE" );
 played = 1;
 continue; // New keyword
 }
 ... // LESS indent is required here

played is a poor name that fails to describe the variable's purpose.
Perhaps bankingAdvised is what the OP intended.
Giving the flag a non-zero value at this point in the program is questionable.

Using continue here eliminates the need for else. All the subsequent 400 lines of code (i.e. all of the else body's statements) can be 'un-indented' one level.

Oddly, however, if the user ignores the directive, they can still go "onto the floor" without ever buying chips. It's an unusual casino that issues a toothless directive, then allows the user "onto the floor" only to be admonished a second time that they have no chips. In other words, the logic (flow) of this UX aspect has not been well designed or implemented.

Perhaps a more useful predicate would involve the number of chips the user possesses. But this would prevent a chip-less user from playing the number guessing game that does not require wagering chips.

On this matter of spendable chips, it is odd that the minimum wager for the currently two "betting games" is 0 chips.

If having zero chips does not prevent a user from playing any game, what has been the point of all this palaver?

Related: Isolating into a function functionality that requires multiple statements (an exercise called "refactoring") also reduces the necessity of "too many" levels of indentation. The reader can presume the functionality of an extended operation when execution is transferred to a well named function.

For instance, sqrt() is the terse (1970s) name of the very involved (math library) function that calculates the square root of a number. The reader sees the call to the function and that is sufficient.

Too much indentation is termed a "code smell" indicating that a region of code should be considered for refactoring. Some statements of the "Number Guess" regions have been indented to 8 tab stops.

Better than refactoring existing code is pencil & paper design before typing even one line of code. Minutes spent "envisioning" and sketching-out operations are repaid as hours saved over a long career. This is a craft skill that develops with experience, time and effort invested.

Further, refactoring is often non-trivial "surgery" on established source code in order to alter or augment its functionality. The very process of refactoring carries the risk of introducing a new bug, or activating what has so far been an innocuous bug. One more reason why, to the greatest extent possible, it is better to plan things out ahead of time to reduce the need to pause to refactor code that is evolving.


Helpful tip #13

Stripping out MANY lines of indented code reveals the three options of "Games, Bank or Rules" are selected with

 ...
 while(choice == 1) {
 ... // offer 3 different games here
 }
 if(choice == 2) {
 ... // do banking
 } else if(choice == 3) {
 ... // display rules of 3 games
 }

A discerning reader is left perplexed, needing to trace through the entire program attempting to perceive the possible execution flow paths through the code. At the bottom of this while(), the value of choiceB is used to alter the value of choice, and break and else feel like pinball machine bumpers.

THIS invokes one side of the oft-mentioned bogeyman named 'Globally Scoped Variables or Functions'. A casual (inattentive) reader might believe that the value of choice is assigned from user input only. Turns out, tucked away close to the end of the while() loop, its value may be assigned by the program itself...

When the scope of variables is too large ("more than required"), it becomes tempting to manipulate their value "locally" to achieve an end. These can become "boobytraps" for the next person who works with the code. "Don't touch anything, 'cuz it's all spring-loaded and ready to explode." It should never be the case that one "sub-region" of code exceeds its authority and grabs the steering wheel ('altering values') thought to be under the control of its larger bounding region. Strive to maintain a hierarchy of 'authority' for where, during execution, values are set/modified.

An experienced coder sees this and surmises that the code was almost complete before flaws in its logic surfaced. Convoluted, band-aid logic was then devised to be a quick fix.

Warning: Band-aid logic is treacherous if considering ever extending any code. Use of band-aid logic drives the first handful of nails into the ultimate coffin of any code. Its longevity and flexibility both drop dramatically, necessitating eventually "starting over from scratch", effectively voiding all the time and effort expended to date.

This complexity could have been easily resolved had a few functions been used:

 if( choice == 1 ) {
 GamingFloor();
 } else if( choice == 2 ) {
 visitBank();
 } else if( choice == 3 ) {
 showRules();
 } else {
 ... // invalid selection
 }

as one alternative. Each these three 'subordinate' functions need not have any knowledge of the existence of choice, therefore they could not alter its value.

Notice there is no need to first validate user entry is "in range". Dispatching to worker functions seamlessly incorporates rejecting invalid user input.

Time spent drawing boxes and arrows on multiple pages of paper before coding is time well spent. Practice planning ahead and outlining logic flows as is done by 'expert' coders with years of experience.

Aside: Each game's individual "rules" could/should be available during game play, not requiring leaving the gaming floor (imo).


Helpful tip #14 - missed opportunity (array traversal)

The "Dice" game invokes rand() twice, once for each value of two dice that are then summed. This is fine, and works here.

An alternative is to recognise that each possible sum, 2..12, has a certain probability of arising.
E.g. 7 is of sum of 6 different combinations [1,6], [2,5], [3,4], ...
Altogether a 6:36 chance of arising, whereas the sum 2 is 1:36 as ONLY [1,1] sums to 2.

 // No. of combos for each sum 2..12 (11 sums, 36 combinations)
 int combos[] = { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1 };
 // One random integer in range 0..35
 // Traverse combos until matching probability range that was "sim rolled"
 int sum = 0;
 for ( int roll = rand() % 36; roll >= 0; ++sum )
 roll -= combos[ sum ]; // stepping down toward zero
 sum += 1; // bias up as 0th element stores first dice pair [1,1].
 ... // sum contains the simulated sum of two dice reflecting probabilities

Capturing outputs when roll is tested with 0..35:

2,3,3,4,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,7,7,8,8,8,8,8,9,9,9,9,10,10,10,11,11,12

Note that each sum of two dice is represented in this dump in proportion to its probability. This loop will run, maximally, 11 iterations; an array of 11 integers is smaller than an array of 36 integers. The loop represents the trade off between 'space' and 'time' that is typical of many coding decisions to be made.

Task: Work out the iterations vs. space trade-offs if the dice game Yahtzee - five dice - were being simulated. (Hint: how much is 65?)

Although "overkill" in this simple instance, in developing an adventure game (as the OP stated is an ambition), varying numbers of arbitrary weights can be established in the code suited to the coder's wishes for game play.
E.g. Wound inflicted: fatal:10%, severe:15%, light:25%, none:50%.
double wounds[] = { 0.10, 0.15, 0.25, 0.50 };
Here, the coder would use floating point to erode a random number that is initially pre-scaled to 0.0 to 1.0. The index (like sum above) when the looping halts is the category of the wound inflicted. This allows branching (integer) to one of several alternative scenarios. The criteria thresholds are represented together in the array wounds[] for easy revision (or even dynamic alteration while the program is executing.)

Many real world physical, chemical and biological transformations have idiosyncratic sets of probable outcomes. Simply rolling some dice or flipping some coins will make it difficult to simulate these processes with fine-grain resolution that matches the empirical results of experiments. A quick, iterative algorithm similar to this example can simulate results that align closely with the real world.

Finally, the simpler d1 = rand(); d2 = rand(); version may be given as a future job interview question with the request to write an alternative algorithm. Being able to "think outside the box" and demonstrate that there are "several ways to skin a cat" will position the OP toward the top of short-listed candidates.


Helpful tip #15 - perceiving classes

"I am rewriting my code." --OP in a comment

This code is strictly imperative; a missed opportunity to demonstrate understanding of "Object Oriented" programming paradigms.

Try to 'see' a player's "wallet" as an object (a C++ class) that stores amounts of both cash and chips. The Bank knows how to instruct the wallet object to move a quantity between its two accumulators. A Dealer knows how to instruct the wallet object to place a bet (chips -= betChips) or how to add winnings (chips += winChips). The 'facilities' offered by the wallet class would be public member functions (NOT called "methods" in C++).

Try to 'see' the "Blackjack Card Shoe" as a C++ class object. It controls some data (6 decks of 52 cards shuffled) accessible through a "class member function" that deals one card at a time. (When and how to trigger (re-)loading with shuffled cards?)

Coming back to the concept of Doc/View in regard of a "card game", an actual Blackjack card shoe would be loaded with 6x52 actual cards that are then shuffled. This is the same as 6*4*13, or 24*13 for Blackjack where the suit of each card is irrelevant. If Blackjack will ever be the only card game in the Casino, this would suffice for that version. Code that establishes 24 repetitions of 1..13 could initialise the 'shoe' array, then the array's 312 elements shuffled.

Should "future prospects" be envisioned to add other card games (eg: poker) in which 'suit' plays an important part, it may be worth working out how-to fill the 'shoe' with 6*52 cards and deal with individual Blackjack's "suit agnosticism" using a modulo operation. NB: this would require abstracting the FOUR Ace..King ranges of one deck to each be perceived as 1-13 (or 1-10,10,10,10) to be "mapped" to ONE range of 0-51; modulo division will 'extract' the appropriate card value AFTER 1 is added to that value. And, "integer division" would yield 0-3 that represents each of four different "suits".

This 'intricacy' is best contained within the class shoe object. Meeting this challenge paves the way for implementing class poker that has "face value AND suit" requirements applied to a single deck. Its implementation logic may be able to 'share' functionality with already existing class shoe in the code. These "backroom" intricacies handle these complexities, providing each game's 'View' with all the View needs to interact with the player. The 'View' could assess two (or more) class hand valuations to determine the "unfinished or win/tie/loss " state of each game's ongoing play.

Planning and implementing well designed classes, although initially slow to "get off the ground", result in super-fast code elaborations that compensate for the inevitable "progress slowdowns" that result from increased app complexity and its extended functionality. A student project that undertook this depth of implementation would likely be worth a grade of 100%; no "distracting auxiliary embellishments" needed.

Try to 'see' each dealt Blackjack hand as an object containing a collection (minimally an 'array[]') of 'n' cards (including 11-13) that can receive and keep track of individual dealt cards and report the best sum of their point values.

Accounting for the dual nature of an Ace would be the logic encapsulated within that class object. For reference: 'Integer Arrays' are introduced early in training, appearing in chapter 1.6 of the "K&R C Bible". Blackjack, in this code, is a missed opportunity to code and manipulate arrays.

Subtlety: Both the player and dealer need a "dealt hand" object at the same time. Two "instances" of the class object hand would mean providing for the two values of any Ace, and multiple aces in one hand, need only be written once, and thoroughly tested.

Going beyond:
These user interactions are repetitions of procedural code the could be "factored out" to a parameter driven functions. Such functions ("functionality") would begin to suggest more objects, aka C++ classes.

When the OP is ready, a generic menu base class could be used to standardise the operation of any menu interaction, with instances of derived classes written to handle specific menus as required.

When the OP is ready, a generic game base class could handle common details applicable to all the games, with instances of derived classes written to handle specific game logic and scoring.

This would begin to enjoy the benefit of Object Oriented design and would demonstrate how OO improves upon the limitations of imperative design. User's would experience more consistent behaviour of the program, and coders could be laser-focussed at development of one small aspect of code at a given time.

Well designed classes make elaboration (new games, bug fixes) almost trivial; often termed "code reuse".

Avoiding learning/using C++ offerings is a self-imposed handicap for any student dreaming of writing useful programs in MOST modern languages; not just C++.


Helpful tip #16 - testing

As noted in another answer, available are better PRNGs (Pseudo Random Number Generators) than srand().

For a beginner's project, srand() is sufficient. It is unlikely a mathematician will take the time or make the effort to "crack" this program in order to cheat.

 // seed the random number generator
 srand( time( NULL ) );

This conventional statement (used by the OP) seeds the RNG with a value that is based on the changing time, in seconds, that the program begins executing.

The OP is advised that srand( 42 ); will seed the RNG with the constant "magic number" 42, resulting in the RNG always generating the same long sequence of values. This can/could be useful to repeat the same "random" sequence over-and-over for testing. Seed using a different constant value to get a different, but again repeatable, sequence.

However, the "tabulating card values" code for Blackjack should be controllable by the coder; not subject to whatever values any particular sequence delivers. One wants to check that tabulating calculates correctly for any 'tricky' sequence of dealt card values as any Ace is a shape-shifter between two values. Also, the dealer's 'rule' of "hit on 16-, stand on 17+" can be forced when the dealt card value sequence can be forced.

Suggestion:
Implement a constant array of deliberately assigned integers. Temporarily substitute a local function for the library function rand(). In this way, the coder can test and re-test that the tabulating code operates as intended when specific cards are dealt in a specific sequence.

Right now, the three conditionals that determine Blackjacks' win, tie, or loss are something of a tangle to read and verify.
(Admission: I've not compiled, nor run, the code, and reading through those three conditionals makes me feel queasy.)


Helpful tip #17 - more testing

Comprehensive testing and re-testing is laborious, but crucial.

There appears to be only one (minor) bug in this code. That is commendable.

There are a number of aspects to this currently 'correct' code that could be improved. A scary hazard of revising working code is the risk of introducing new bugs that may go undetected.

Professional workplaces use complicated (and sometimes expensive) "automated testing suites" that are fields of study unto themselves.

Suggestion:
The OP establishes and grows a number of "test script" text files that each simulate one sequence of user keystrokes. The OP then runs each test with a command line such as
casino < script_01.txt > out_01.txt
that would complete in less than a second.

Well, it would complete quickly if the code were not hobbled by a number of calls to Sleep(). Perhaps writing and using a 'shim' function that allows the coder to skip those delays while performing tests. This is left as an exercise. Dozens of tests could be run in less than a minute, with another few minutes spent verifying that all went well.

Do each of the outputs and end results demonstrate correct functionality?

With such an "almost automated test suite", the OP could proceed to gradually upgrade the source code implementing some of the recommendations made herein. Revising code in small steps and frequently testing that the revised program functions correctly should build the OP's confidence to risk implementing more of these source code "improvements".

Assignment: Research DOS copy command and work out how to "concatenate" content from multiple files to 'feed' into your Casino executable. Pause to consider how to 'pump in' any number of standard deliberate keystrokes to bring executable to a particular state.

Testing reliability is every bit as important as design and coding. Do yourself a favour by also considering how to efficiently and completely test any code you write or modify.


Helpful tip #18 - a metaphor

Consider the similarity between writing code and composing a piece for a musical instrument
(e.g. a harpsichord).

A harpsichord's 50-60 keys (notes) played by 10 fingers imposes a finite set of moment-by-moment possibilities.

A programming language (with its libraries of functions) provides a finite collection of tools to work with.

The diverse range of compositions for harpsichord is a product of composers developing their skill for arranging those resources in a manner pleasing to a listener while constrained by rules of 'musical keys', chords, note durations, etc.

The vast and diverse range of software compositions is a product of coders developing their skill for utilising a language's resources to achieve a purpose.

The contrast between ponderously simplistic and richly satisfying may be illustrated in this scene from the movie, "Amadeus".

This code is reminiscent of Ravel's much loved Boléro: repetitious. The composer's own assessment of his piece was, "Orchestral tissue without music" [emphasis mine].

One must be adventurous in order to grow to develop great works.

Great code is like great jazz:
thematic :: simple, yet complex :: robust, yet malleable :: complete, yet extensible.


Helpful tip #19

Already addressed in the answer from @toolic is simplification by recognising what has been coded as 2/3 of a tripartite operation is, in fact, a simpler if/else bifurcation.

Toolic's appropriate "Branching Simplified":

 while (guess != randNum) {
 if (guess > randNum) {
 cout << "Lower" << endl;
 } else {
 cout << "Higher" << endl;
 }

However, the player, looking at this result, cannot intuit which is meant:
"Guess is "Lower" (than target)" or "Try again "Lower"".
(Same applies to the OP's too-brief: "Higher".)

The player has no access to read the code to see that the program's too terse response to the guess has the latter meaning.

How about:

 while (guess != randNum) {
 if (guess > randNum) {
 cout << "Too High" << endl; // or: "Over"
 } else {
 cout << "Too Low" << endl; // or: "Under"
 }

Notice the subtle change. The response has been augmented (or changed), AND the text matches the logic that selects which text is output.

Pause to take a breath and reflect on how any logical statement is written. Read it "out loud" with its context. The original version above is slightly jarring as the condition determines "Greater than", but the result when true is "Lower (aka less than)".

Try this audio evaluation of the code of the second version, too.

Brevity is good, but should not be overdone.
(Player has seen massive ASCII art displays, then gets one word responses to the guess???)
Likewise, code readers will appreciate efforts that smooth the flow of logic.

Overtime example/analogy:
"Do you want to go to the movies?"
versus
"Don't you want to go to the movies?"

For the latter, what does "Yes" mean?; what would "No" mean?
The former has been careful to eliminate ambiguity from the situation.
Positivity has its rewards, one being 'clarity'.
Apply this principle to logic statements, conditionals and general 'flow' through code.


Summary

This review represents a somewhat thorough assessment of the OP's posted source code, and includes a handful of recommendations and alternatives.

Based on the student having had ~80+ hours of C++ classroom instruction, this C++ project presents as overly simplistic and needlessly voluminous.

None of this review comes from generic copy/paste 'boilerplate' applicable to many of the C++ programs present in SE/CR. Nor does this reviewer expect to see more than the presumed capabilities of a student who has completed one term of lessons/labs.

The source code is repetitious through copy/paste/adapt, and bloated with ASCII art and a "parking lot mugging" story, perhaps in an attempt to distract from its fundamental shortcoming: avoiding using much that is presumed to have been taught in class, and avoiding C++ code design challenges.

Obviously, I have only assumptions of what material was presented in the course, and have no acquaintance with its teacher. Therefore, there is no window through which to determine how much of the very deep and complex C++ language had been taught, how well it had been taught and drilled, or the expectations or skills of the person evaluating each student's work.

One reasonable extrapolation is that 20+ student projects, each made-up of roughly 600 lines of source code, means 12,000+ lines of code would have to be evaluated and graded. Those evaluations may not have been as thorough as attempted here.

IMO, feedback from every assignment, test and project forms a part of a student's learning experience. Distillation of a term project to a simple grade is a missed educational opportunity.

It would be a disservice to the OP to write anything other than pointing out what I view as its deficiencies:

  • at least one 'home-brew' function using parameters,
  • at least one 'home-brew' C++ class with one constructor and one-or-more member functions,
  • some indication of string i/o and string manipulation (because dealing with string buffers plays a significant role in many programs),
  • self-discovery (exploration) and use of more C++ operators and/or library functions,
  • etc.

Tertiary study programs and/or workplaces will demand much more from any pilgrim.

It would also be a disservice to any neophyte to talk over their head with casual mention, with no explanation, of semi-advanced concepts of:

  • namespaces,
  • friend functions & operator overloading,
  • pass by reference,
  • portability requirements,
  • multiple code modules,
  • runtime use of external data files,
  • dynamic memory allocation,
  • etc.

It is this reviewer's honest opinion that these 630 lines of code do not represent a serious attempt at writing a "Casino" program. The moderator-erased question I'd posted an hour after the OP's post was made, and the OP's erased answer about their future ambitions as a coder, suggest a greater seriousness in attitude must be found if those ambitions are to be fulfilled.


"I am rewriting my code." --OP in a comment

Please consider improving this code based on the reviews you've received, then posting a "follow-up" version as a new 'question' for review. Link back to this post allowing SE/CR readers to follow-along with your blossoming skills.

Reminder: SE/CR is for (presumed) "working code" as this version is.
Asking "how to improve" is welcome here.
Asking "how to fix/do {something}" belongs on Stackoverflow.

answered Jul 22 at 20:48
\$\endgroup\$
0
2
\$\begingroup\$

I will avoid repeating what everyone else said. A good tip for making your code way more readable is to avoid printing all the strings inside your functions.

For example, the "The diamond casino" print could just be defined in a string at the top of the program (or in a .h file even better):

#define WELCOME_STRING "(\
 /__ ___/\
 / / / __ ___\
 / / // ) ) //___) )\
 / / // / / //\
 / / // / / ((____\
\
 // ) )\
 // / / ( ) ___ _ __ ___ __ ___ / ___\
 // / / / / // ) ) // ) ) ) ) // ) ) // ) ) // ) / //___) )\
 // / / / / // / / // / / / / // / / // / / // / / //\
//____/ / / / ((___( ( // / / / / ((___/ / // / / ((___/ / ((____\
\
 // ) )\
 // ___ ___ ( ) __ ___\
 // // ) ) (( ) ) / / // ) ) // ) )\
 // // / / \ \ / / // / / // / /\
 ((____/ / ((___( ( // ) ) / / // / / ((___/ /\
\
===================================================================================================\
\
Copyright (c) 2013 Popstar Games, Inc. No rights reserved. All parts of this\ work may be reproduced,\
distributed, or transmitted in any form or by any means, including photocopying,\
recording, or other electronic or mechanical methods, without prior written permission of the\
non-existent copyright owner :))\
"

And then in your main function you could do a: std::cout << WELCOME_STRING << std::endl;

toolic
14.3k5 gold badges29 silver badges200 bronze badges
answered Jul 24 at 7:15
\$\endgroup\$
2
  • 6
    \$\begingroup\$ Good grief, no. We do not use macros for this kind of thing in C++ (or even in modern C). For this you should use a constexpr raw string. (Or load it from a file at runtime, of course.) \$\endgroup\$ Commented Jul 24 at 19:33
  • \$\begingroup\$ @indi yeah i'm sorry for my wrong usage of define, i work in C essentially and i'm not so trained to work in C++. The point was about declaring the string somewhere else and then using it somewhere else. I should have made it more obvious for people like you i apologize. \$\endgroup\$ Commented Jul 28 at 11:56

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.