7
\$\begingroup\$

This is my first program, ever. It comprises a choice of casino games:

  1. A number guessing game in which you guess a number 1 through ten. If you guess the correct number, you double the money you bet.
  2. Blackjack. Yes, I know the rules for this blackjack aren't exactly the same as they really are.
#include <iostream> #include <cstdlib> #include <ctime> using namespace std; double money = 100; int blackJack(); int numberguess(); void main2() { srand(static_cast<unsigned int>(time(0))); if (money == 0) { cout << "Sorry you hit 0 dollars \a"; return; } cout << "Hello! Welcome to my casino. \n" << "Which game would you like to play? \n" << "1. Guess the number \n" << "2. Blackjack \n"; while (true) { int choice; cin >> choice; switch (choice) { case 1: numberguess(); break; case 2: blackJack(); break; default: cout << "This is not a valid answer \n"; break; } } } double bet; int main() { main2(); return 0; } int numberguess() { while (true) { // Seed the random number generator int answerNumberGuess = (rand() % 10) + 1; cout << "You have " << money << " dollars." << endl; cout << "Now please enter your bet: " << endl; cin >> bet; while (true) { if (bet > money || bet <= 0) { cout << "Your too broke! Try again" << endl; cin >> bet; } else { break; } } cout << "now please enter your guess:" << endl; int guess; cin >> guess; money = money - bet; if (guess == answerNumberGuess) { cout << "Congradulations you win! the correct answerNumberGuess was " << answerNumberGuess << "!" << endl; money = money + bet * 2; cout << "You now have " << money << " dollars!" << endl; } else { char q; cout << "Sorry you lose" << endl << "Would you like to try again? [Y/N]"; cin >> q; if (q == 'y' || q == 'Y') { system("cls"); main2(); } else { return 1; } } if (money <= 0) { cout << "Wow, you lost all your money. \a" << endl; break; } } return 0; } int dHand = 0; int hasAce = 0; int pHand = 0; bool pStand = false; void checkStand(); void addDealerCard() { if (dHand > 21) { return; } int dCardDrawn = (rand() % 12) + 2; dHand += dCardDrawn; checkStand(); } void addPlayerCard() { int cardDrawn; cardDrawn = (rand() % 12) + 1; system("cls"); switch (cardDrawn) { case 8: case 9: case 10: case 11: cout << "You drew a face card worth ten! \n"; pHand += 10; break; case 12: cout << "You drew " << cardDrawn << " " << endl; cout << "You drew an ace! \n"; hasAce = 1; break; default: cout << "You drew " << cardDrawn + 1 << endl; pHand += cardDrawn + 1; break; } addDealerCard(); } void checkStand() { cout << "You're hand is " << pHand << endl; cout << "Would you like to hit[0] or stand[1]? \n"; cin >> pStand; if (pStand == true) { if (hasAce == true) { cout << "would you like your ace to be worth one[0] or eleven[1] \n"; bool aceValue; cin >> aceValue; if (aceValue == 1) { pHand += 11; } else if (aceValue == 0) { pHand += 1; } cout << "You're hand is now " << pHand << endl; } if (dHand > 21) { if (pHand > 21) { cout << "You lose! \n"; cout << "Dealers hand was " << dHand << endl; } if (pHand <= 21) { cout << "You win! \n"; money = money + (bet * 2); cout << "Dealers hand was " << dHand << endl; } } else if (dHand > pHand && dHand <= 21) { cout << "You lose! \n"; cout << "Dealers hand was " << dHand << endl; } else if (pHand > dHand && pHand <= 21) { cout << "You win! \n"; money = money + (bet * 2); cout << "Dealers hand was " << dHand << endl; } else if (pHand > dHand && pHand > 21) { cout << "You lose! \n"; cout << "Dealers hand was " << dHand << endl; } else if (dHand == pHand) { cout << "Its a push! \n"; money += bet; cout << "Dealers hand was " << dHand << endl; } cout << "Would you like to play another game? [Y/N] \n"; pHand = 0; dHand = 0; char question = 'A'; cin >> question; if (question == 'y' || question == 'Y') { system("cls"); main2(); } else { return; } } else { addPlayerCard(); } } int blackJack() { cout << "You have " << money << " dollars." << endl; cout << "Now please enter your bet: " << endl; cin >> bet; while (true) { if (bet > money || bet <= 0) { cout << "Your too broke! Try again" << endl; cin >> bet; } else { break; } } money = money - bet; addPlayerCard(); addDealerCard(); cout << "You're hand is now " << (pHand) << endl; checkStand(); return 0; } 

I was wondering if there were any ways I could make my program faster. Is there also a way I could simplify the if statements in the checkstand function?

\$\endgroup\$
6
  • \$\begingroup\$ Nice first question! I can’t go too in depth at the moment because I’m on mobile so I’ll just leave some quick tips. Your code would be more readable if you moved your global declarations to the top of the file together, including function headers. I can take a better look later and help you with getting around calling main2() from the other functions. Also for now you can just put your main2 code into main and forget main2 altogether. \$\endgroup\$ Commented Aug 27, 2024 at 19:30
  • 1
    \$\begingroup\$ @Confettimaker I did the main2() because I read it would cause a stack overflow if I call main(). Is this right? \$\endgroup\$ Commented Aug 27, 2024 at 21:00
  • \$\begingroup\$ That will happen in your implementation as well. main2 calls numberguess which in turn calls main2 which calls numberguess, into infinity. Consider returning back to main when the player loses a game and requesting user input for what game they want to play next in a loop like you do to get bets. I’ll be on my computer in a bit and I will write up my full review. \$\endgroup\$ Commented Aug 27, 2024 at 21:09
  • \$\begingroup\$ @RiverScheepens Where did you read that main2 won't stack overflow but main will? \$\endgroup\$ Commented Aug 27, 2024 at 23:31
  • 1
    \$\begingroup\$ Hi River, you misunderstood, in the resource you read main was just being used an example - any method that you call in this way will eventually result in an overflow due to the error in logic that Confettimaker points out. \$\endgroup\$ Commented Aug 28, 2024 at 19:43

3 Answers 3

7
\$\begingroup\$

Here are a few suggestions:

  • Give more instructions for user input and validate it carefully. I tried out the program, entered "1.", and immediately got stuck in an infinite loop.
  • It is much clearer when functions are declared before use. When two functions depend on each other, there is a better solution >99% of the time.
  • Mutable (i.e. not constant) global variables add complexity because they are essentially hidden parameters for every function. There is a better solution >99% of the time.
  • Be careful comparing money (double) to 0 (int). In general, integers are preferable for currencies because they have consistent precision. You can make the smallest unit something like 0.000001 dollars.
  • Use functions for repeated code, such as getting input or asking for a bet.
  • Make your error messages as precise as possible. As an example, you might want a separate message for negative bets.

Here's the implementation with suggestions for the first game:

#include <iostream> #include <string> #include <sstream> #include <ctime> using namespace std; // type input and press enter stringstream getline_stream() { string user_input; getline(cin, user_input); return stringstream(user_input); } // asks the user to enter a bet int get_bet(int money) { while (true) { cout << "\nYou have " << money << " dollars.\n"; cout << "Please enter your bet (integer >= 0).\n"; int bet; getline_stream() >> bet; if (bet > money) { cout << "You do not have enough money!\n"; continue; } if (bet < 0) { cout << "You cannot bet a negative amount!\n"; continue; } return bet; } } // returns your money after playing Guess The Number int guess_the_number(int money) { cout << "\nYou are playing Guess The Number!\n"; cout << "If you win, you receive double your bet!\n"; int answer = (rand() % 10) + 1; // get a valid bet int bet = get_bet(money); money -= bet; // get the guess cout << "\nPlease type your guess and press enter.\n"; cout << "The answer is an integer from 1 to 10 inclusive.\n"; int guess; getline_stream() >> guess; cout << "\nThe correct answer was " << answer << ".\n"; if (guess == answer) { cout << "Congratulations, you win!\n"; return money + 2 * bet; } else { cout << "Sorry, you lose!\n"; return money; } } int main() { // initialize the random number generator srand(static_cast<unsigned int>(time(0))); // welcome message cout << "Hello! Welcome to my casino.\n"; int money = 100; // dialogue loop while (true) { cout << "\nYou have " << money << " dollars.\n"; cout << ">> Quit: type 'q' and press enter.\n"; cout << ">> Reset: type 'r' and press enter.\n"; cout << ">> Guess The Number: type '1' and press enter.\n"; cout << ">> Blackjack: type '2' and press enter.\n"; char choice; getline_stream() >> choice; switch (choice) { case 'q': return 0; case 'r': money = 100; break; case '1': money = guess_the_number(money); break; case '2': money = blackjack(money); break; default: cout << "\nYour input could not be parsed.\n"; } } } 

Good job on a well-contained MVP. Hope this helps!

Blackjack (Update)

// text for each card const char cards[13][3] = { "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" }; // gets a card index int get_index() { return rand() % 13; } // updates number of aces, number of other points void update_state(int *other, int *aces, int index) { if (index < 9) // 2 to 10 (*other) += index + 2; else if (index < 12) // J, Q, K (*other) += 10; else // A (*aces) += 1; } // gets best score, avoid bust if able int best_with_bust(int other, int aces) { int score = other + 11 * aces; for (int i = 0; i < aces; ++i) { if (score <= 21) return score; score -= 10; } return score; } // returns your money after playing Blackjack int blackjack(int money) { cout << "\nYou are playing Blackjack!\n"; cout << "If you win, you receive double your bet!\n"; // get a valid bet int bet = get_bet(money); money -= bet; // game state int p_other = 0; int p_aces = 0; int d_other = 0; int d_aces = 0; // player's first draw int p_index = get_index(); cout << "\nYou draw " << cards[p_index] << ".\n"; update_state(&p_other, &p_aces, p_index); // dealer's first draw int d_index = get_index(); cout << "The dealer draws " << cards[d_index] << ".\n"; update_state(&d_other, &d_aces, d_index); // player's draws int p_score; while (true) { p_index = get_index(); cout << "\nYou draw " << cards[p_index] << ".\n"; update_state(&p_other, &p_aces, p_index); p_score = best_with_bust(p_other, p_aces); // bust if (p_score > 21) { cout << "Sorry, you lose by bust!\n"; return money; } // natural 21 if (p_score == 21) break; // hit or stand cout << ">> Hit: type 'h' and press enter.\n"; cout << ">> Stand: type 's' and press enter.\n"; char hit_or_stand; getline_stream() >> hit_or_stand; if (hit_or_stand == 's') break; } cout << "\nYou stand with " << p_score << " points.\n"; // dealer's draws int d_score; while (true) { d_index = get_index(); cout << "The dealer draws " << cards[d_index] << ".\n"; update_state(&d_other, &d_aces, d_index); d_score = best_with_bust(d_other, d_aces); // bust if (d_score > 21) { cout << "\nCongratulations, you win by dealer bust!\n"; return money + 2 * bet; } // hit or stand if (d_score > 16) break; } cout << "\nThe dealer stands with " << d_score << " points.\n"; // win by comparison if (p_score > d_score) { cout << "Congratulations, you win!\n"; return money + 2 * bet; } // tie by comparison if (p_score == d_score) { cout << "It's a tie!\n"; return money + bet; } // lose by comparison cout << "Sorry, you lose!\n"; return money; } 

Imitating Blackjack logic is rather complicated, but here was my best attempt. Simplifying the checks can be done by reducing chances for user input (e.g. automate the treatment of aces as 1 or 11) and different methods of control flow, like continue, break, and return. Note that I pass references (pointers) instead of values to update_state. Pointers are a rather infamous topic, but I think a small example like this one could be illustrative. Feel free to ask questions.

\$\endgroup\$
1
  • 4
    \$\begingroup\$ When you write an answer, target the capabilities of the original poster. Words like Mutable global variables are not going to mean much to a beginning programmer. +1 for a basically good answer. \$\endgroup\$ Commented Aug 28, 2024 at 1:05
5
\$\begingroup\$

Some things not mentioned in other answers:

\$\endgroup\$
2
  • \$\begingroup\$ Thanks while writing the code I did wonder what the difference between \n and endl was but never got to looking it up. Also, why would you need to flush a buffer? \$\endgroup\$ Commented Aug 29, 2024 at 1:41
  • \$\begingroup\$ One good reason to flush std::cout is when you're about to execute some other program (e.g. using std::system()) whose output needs to come after the possibly-buffered output recently written. \$\endgroup\$ Commented Aug 29, 2024 at 6:34
4
\$\begingroup\$

In general, I would avoid using global variables. Variables such as pHand should be defined in blackJack and then passed by reference to checkStand and addPlayerCard.

For the global variables that you determine do need to remain global, you can look into creating a header file that can be included. You could call such a header file defaults.h. You could also move your function headers into a header file as well.

I would suggest getting rid of main2 and putting the code from main2 back into main. Then, I would take a crack at avoiding recursively calling main or any of the other functions, as it is not needed, and could cause a crash if you were to continue playing long enough.

Here is the fix I suggest for this, without altering the code too much:

Your main function could look something like this:

int main() { int choice = 0; srand(static_cast<unsigned int>(time(0))); while (true) { if (money == 0) { cout << "Sorry you hit 0 dollars" << endl; return 0; } cout << "Hello! Welcome to my casino. \n" << "Which game would you like to play? \n" << "1. Guess the number \n" << "2. Blackjack \n" << "3. Quit \n"; cin >> choice; system("cls"); switch (choice) { case 1: numberguess(); break; case 2: blackJack(); break; case 3: return 0; default: cout << "This is not a valid answer \n"; } } return 0; } 

Notice that I moved the declaration and instantiation of choice to the top of main, and out of the body of the while loop. While modern compilers will optimize that out for you, it is good practice to avoid re-declaration of variables.

If you apply the fix I suggested for main, you could then remove the code asking if the player would like to play again in blackJack and numberguess, as the main function can now properly handle that. You could then remove the calls to main as well.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ A better answer would be to make more observations about the code, and less rewriting of the code. Most of your observations are at the end of the answer. \$\endgroup\$ Commented Aug 28, 2024 at 1:09

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.