4
\$\begingroup\$

I made a simple Blackjack/21 game in C++. It does not use any fancy graphics, just the console output. It is as simple as it can get.

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <algorithm>

void printData(int pTotal, int dCard) {

    std::cout << "\nYour total is " << pTotal;
    std::cout << "\nDealer has a " << dCard << " showing.\n";

}

int getCard() {

    return std::rand() % 11 + 2;

}

void turn(int& pTotal, char choice, bool& stay) {

    // If the user wants to hit
    if (choice == 'H' || choice == 'h') {

        // If the user hits 21 or above
        if ((pTotal += getCard()) >= 21) {

            std::cout << "Your total is " << pTotal;
            stay = true;

        }

        return;
    }

    // Since the only other option can be to stay, then stay
    stay = true;

}

int main() {

    bool stay;
    char choice = NULL;

    while (true) {

        std::cout << "================================";
        stay = false;

        // Initialize srand and get random card numbers
        std::srand(std::time(0));
        int dCard = getCard();
        int pTotal = getCard() + getCard();
        int dTotal = dCard + getCard();


        // Player's turn
        while (!stay) {

            printData(pTotal, dCard);
            std::cout << "[H]it or [S]tay?\n";
            std::cin >> choice;
            turn(pTotal, choice, stay);

        }

        stay = false;
        std::cout << "\n\nIt is now the dealer's turn!\n";

        // Dealer's turn
        while (dTotal <= 21 && !stay) {

            if (dTotal >= 17)
                stay = true;
            else
                dTotal += getCard();

        }

        // Display winner
        std::cout << "\n\nThe player has " << pTotal << ".\nThe dealer has " << dTotal << ".\n\n";
        if ((std::abs(21 - pTotal)) < (std::abs(21 - dTotal))) std::cout << "The player wins!\n";
        else std::cout << "The dealer wins!\n";

    }

}
  1. Is this code efficient?
  2. Is the code easy to read/understand and to follow?
  3. Are variable names appropriate?
\$\endgroup\$

3 Answers 3

5
\$\begingroup\$

I want my money back!

Your total is 15
Dealer has a 4 showing.
[H]it or [S]tay?
s


It is now the dealer's turn!


The player has 15.
The dealer has 23.   <<=====

The dealer wins!     <<=====

Your casino is cheating! Also you're not handling aces, all face cards are supposed to have a value of 10, and there are 13 ranks in a suit but your getCard function can only return 11 different values (2 to 12).

This means that you can lose right off the bat:

$ ./a.out 
================================
Your total is 24       <<======     :-(
Dealer has a 9 showing.
[H]it or [S]tay?

In turn, if the player enters h and doesn't bust, you're not setting stay - this happens to work given the code flow, but it looks odd. Set it before returning.

If the user enters gibberish, you always interpret that as stay. That's not user-friendly.

In main:

bool stay;
char choice = NULL;

That's not a valid initializer for a char - chars are not pointers. And leaving a variable (stay) uninitialized is not a good idea. You should move stay inside the loop, only declare it when you give it its first value. (I'd also use two separate variables for the dealer and the player.)
Remove choice from main altogether. Only turn needs it, so handle the user input in there completely. (And have turn return hit/stay so you don't need that out parameter.)

This shouldn't be inside the loop:

std::srand(std::time(0));

You should initialize the RNG only once, do that at the very start of main. C++11 introduced new random generation facilities, you should look into them.

These lines are too long:

// Display winner
std::cout << "\n\nThe player has " << pTotal << ".\nThe dealer has " << dTotal << ".\n\n";
if ((std::abs(21 - pTotal)) < (std::abs(21 - dTotal))) std::cout << "The player wins!\n";
else std::cout << "The dealer wins!\n";

Try something like:

// Display winner
std::cout << "\n\nThe player has " << pTotal
          << ".\nThe dealer has " << dTotal << ".\n\n";

if ((std::abs(21 - pTotal)) < (std::abs(21 - dTotal))) {
  std::cout << "The player wins!\n";
} else {
  std::cout << "The dealer wins!\n";
}

And once you fix your logic so that a bust directly ends the players turn, and a dealer bust makes the player win, you can get rid of the std::abs since both scores will be at most 21.

\$\endgroup\$
2
\$\begingroup\$

Your deck isn't fair

This code will happily deal a card that's already been dealt:

int getCard() {

    return std::rand() % 11 + 2;

}

Real Blackjack uses a finite set of cards, usually a fixed number of standard decks shuffled together. The code here is equivalent to dealing from an infinite deck (or to replacing cards to be drawn again).

The difference may well be important to anyone using this program to practice their card-counting technique.

\$\endgroup\$
-2
\$\begingroup\$

Handle the Aces! (That's my only problem, I got a 22 at start)

\$\endgroup\$
1
  • 6
    \$\begingroup\$ Welcome to Code Review! The code's inability to handle aces properly has already been mentioned in the accepted answer, though. Can you provide any other advice? \$\endgroup\$
    – Null
    Commented Jan 9, 2020 at 19:15

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.