7
\$\begingroup\$

I was just wondering what every one thinks about my Blackjack game. If there are some things I could change to simple the code down, or if I put some repetitive things I don't need. This is my first real extensive coding project.

One thing I was wondering was, when I ask for a bet (numerical input) from the user, how do I check the input to make sure it's a char? If so, how do I ask the user to input another answer? I tried previously, but it would just enter an infinite loop.

Also as a side note. I tried to make the code seem more real by trying to print the actual symbols for the cards (clubs, spades, diamonds, and hearts), but when I run codeblocks on this computer those symbols come out as squares with a question mark inside of it. However, when I run it on my other computer they show up like normal. Any idea why?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int currentCards(int n, int i);
void cardSymbolNums(int n);
void cardSymbolFaces(char face);
char cardFace();
int cardDraw();
char cardColor();
int askBet(int userBet, int userMoney, int bankersMoney);
int checkBet(int userBet, int userMoney, int bankersMoney);
int cardValue(int v);
int pick(int v);
int results(int uHand, int userInput, int userMoney, int userBet, int bankersMoney);
int bankersDraw(int userBet, int userMoney, int uHand, int bankersBet, int bankersMoney);
int smartBanker(int bankersHand, int uHand);
int BankercardValue(int v, int bankersHand);
int Bankerpick(int v, int bankersHand);
int gameover();

int main()
{
    int n;
    int userInput = 0;
    srand(time(NULL));
    int userMoney;
    int userBet;
    int value = 0;
    int v;
    int uHand = 0;
    int WinOrLoss;
    int game;
    int bankersBet;
    int bankersMoney;
    int bankersHand;

    printf("Welcome to Blackjack.\n");
    printf("Please Enter The Amount Of Money You Would Like To Start With: ");
    scanf("%d", &userMoney);
    bankersMoney = userMoney;
while((userMoney > 0) && (bankersMoney > 0)){
        userBet = askBet(userBet, userMoney, bankersMoney);
        bankersBet = userBet;
        userMoney = userMoney - userBet;
        bankersMoney = bankersMoney - bankersBet;
        userInput = 1;
    while((userInput != 2)){
        printf("=============================================================\n");
        n = cardDraw();
        v =cardValue(n);
        cardColor();
        uHand = uHand + v;
        printf("Your Money: %d\n", userMoney);
        printf("Card value is = %d\n", v);
        printf("Your Total Hand Value: %d\n", uHand);
        printf("--------------------------------\n");
        WinOrLoss = results(uHand, userInput, userMoney, userBet, bankersMoney);
        if(WinOrLoss == 0){
            userMoney = userMoney + (2 * userBet);
            uHand = 0;
            userInput = 2;
        }
        else if ((WinOrLoss == 3) && (userMoney == 0)){
            printf("Uh Oh! You Lost All Of Your Money!!!\n\n\n");
            printf("GAME OVER\n\n");
            printf("Would You Like To Try Again?\nYes or No?\n\n");
            userMoney = gameover();
            uHand = 0;
            userInput = 2;
}
        else if (WinOrLoss == 3){
            printf("You Busted! Lost Bet!\n\n");
            printf("Banker's Earnings: %d\n", userBet + bankersBet);
            bankersMoney = bankersMoney + userBet + bankersBet;
            uHand = 0;
            bankersHand = 0;
            userInput = 2;
        }
        else if(WinOrLoss == 1){
            userInput = 1;
        }
        else if(WinOrLoss == 2){
        printf("-------------------BANKER'S TURN----------------------------------\n");
        game = bankersDraw(userBet, userMoney, uHand, bankersBet, bankersMoney);
        if((game == 1) && (userMoney <= 0)){
            userMoney = 0;
            userInput = 2;
        }
        else if(game == 1){
        bankersMoney = bankersMoney + bankersBet + userBet;
        uHand = 0;
        bankersHand = 0;
        userInput = 2;
        }
        else if(game == 2){
        userMoney = userMoney + bankersBet + userBet;
        printf("Your Total Amount: %d\n", userMoney);
        userInput = 2;
        uHand = 0;
        bankersHand = 0;
        }
        else{userInput = 2;}

        }
        else{gameover();}
}
    }
    userMoney = gameover();

    printf("\n\nThank You For Playing Blackjack, See You Soon!\n");

    return 0;
}
int cardDraw(){
int n;
n = 1 + rand() % 13;
if( (n > 1) && (n <= 10)){
    printf("\nYour Card is:\n");
    cardSymbolNums(n);
}
else{cardFace(n);}
return n;
}
//--------------------------------------------------------------------------Determine Color of Card
char cardColor(){
    char* color[5];
    int i;
    i = rand() % 2;
    if(i == 0){
        *color = "Black";
    }
    else{*color = "Red";}
    printf("%s\n", *color);
}
//-----------------------------------------------------------------------------Determine Color of Card
//-----------------------------------------------------------------------------Determine Face Card
char cardFace(int card){
    char face;

    if(card == 1){
    face = 'A';
}

    else if(card == 11){
    face = 'J';
}
    else if (card == 12){
    face = 'Q';
}
    else{ face = 'K';}
    cardSymbolFaces(face);
}
//-------------------------------------------------------------------------------Determine Face Card
//------------------------------------------------------------------------------- Printing of the Cards
void cardSymbolNums(int n){
int face;
face = rand() % 4;
char cardType;

if(face == 0){
    cardType = 'S';
    printf("%d-------\n", n);
    printf("%c       |\n", cardType);
    printf("|       |\n", n);
    printf("|       %c\n", cardType);
    printf(" -------%d\n", n);
}
else if(face == 1){
    cardType = 'D';
    printf("%d-------\n", n);
    printf("%c       |\n", cardType);
    printf("|       |\n", n);
    printf("|       %c\n", cardType);
    printf(" -------%d\n", n);
}
else if(face == 2){
    cardType = 'C';
    printf("%d-------\n", n);
    printf("%c       |\n", cardType);
    printf("|       |\n", n);
    printf("|       %c\n", cardType);
    printf(" -------%d\n", n);     //Club
}
else{ cardType = 'H';
    printf("%d-------\n", n);
    printf("%c       |\n", cardType);
    printf("|       |\n", n);
    printf("|       %c\n", cardType);
    printf(" -------%d\n", n);}      //Heart
}

void cardSymbolFaces(char face){
int type;
type = rand() % 4;
char cardType;

if(type == 0){
    type = 'S';
    printf("%c-------\n", face);
    printf("%c       |\n", type);
    printf("|       |\n", face);
    printf("|       %c\n", type);
    printf(" -------%c\n", face);    //Spade
}
else if(type == 1){
    type = 'D';
    printf("%c-------\n", face);
    printf("%c       |\n", type);
    printf("|       |\n", face);
    printf("|       %c\n", type);
    printf(" -------%c\n", face);//Diamond
}
else if(type == 2){
    type = 'C';
    printf("%c-------\n", face);
    printf("%c       |\n", type);
    printf("|       |\n", face);
    printf("|       %c\n", type);
    printf(" -------%c\n", face);   //Club
}
else{ type = 'H';
    printf("%c-------\n", face);
    printf("%c       |\n", type);
    printf("|       |\n", face);
    printf("|       %c\n", type);
    printf(" -------%c\n", face);    //Heart
}
}
//----------------------------------------------------------------------------Printing of the Cards
//----------------------------------------------------------------------------Asking and Checking for Bet
int askBet(int userBet, int userMoney, int bankersMoney){
    printf("Your Money : %d\nBanker's Money: %d\n", userMoney, bankersMoney);
    printf("\n\nPlease Place Your Bet: ");
    scanf("%d", &userBet);
    userBet = checkBet(userBet, userMoney, bankersMoney);

    return userBet;
}

int checkBet(int userBet, int userMoney, int bankersMoney){
    if((userBet > userMoney) || (userBet > bankersMoney)){
        printf("\nInsufficent Funds, Please Re-Enter Bet.\n");
        askBet(userBet, userMoney, bankersMoney);
    }
    else{ return userBet; }
    }
//----------------------------------------------------------------------------Asking and Checking for Bet

int cardValue(int v){
    if(v >= 10){
        v = 10;
    }
    else if((v >= 2) && (v <= 9)){
        v = v;
    }
    else{v = pick(v);}

    return v;
}

int pick(int v){

char decision[4];
printf("You Recieved an Ace! Would you like it High (11) or Low(1)?\n");
scanf("%s", decision);
if((strcmp(decision, "High") == 0) || (strcmp(decision, "high") == 0)){
    v = 11;
    return v;
}
else if((strcmp(decision, "Low") == 0) || (strcmp(decision, "low") == 0)){
    v = 1;
    return v;
}
else{printf("Error, please Re-Enter Decision.\n");
    pick(v);
}
}


int results(int uHand, int userInput, int userMoney, int userBet, int bankersMoney){
int userChoice;
if(uHand == 21){
    printf("Blackjack! You won the Hand!\n");
    printf("Earnings: %d\n", (2 * userBet));
    return 0;
}
else if(uHand > 21){
    return 3;
}
else if(uHand < 21){
    printf("What would you like to do?\n1 for hit\n2 for stay\n");
    printf("Your Choice: ");
    scanf("%d", &userInput);
    if((userInput == 1)){
    return userInput;
    }
    else if(userInput == 2){
        return userInput;
    }
    else{printf("Invalid Choice. Please Pick Again.\n");
    results(uHand, userInput, userMoney, userBet, bankersMoney);
}
}
}

int bankersDraw(int userBet, int userMoney, int uHand, int bankersBet, int bankersMoney){
    int t;
    int u;
    int finish;
    int bankersHand = 0;
    while(bankersHand <= 21){
        t = cardDraw();
        u = BankercardValue(t, bankersHand);
        cardColor();
        bankersHand = bankersHand + u;
        printf("Banker's card value is = %d\n", u);
        printf("Banker's Total Hand Value: %d\n", bankersHand);
        finish = smartBanker(bankersHand, uHand);
    if(finish == 1 ){
        return 1;
    }
    else if(finish == 2){
        return 2;
    }
    }
    }

int smartBanker(int bankersHand, int uHand){
    if((bankersHand > uHand) && (bankersHand <= 21)){
        printf("Banker Won! You Lost This Round.\n");
        return 1;
    }
    else if(bankersHand <= uHand){
        return 0;
    }
    else if (bankersHand == uHand){
        return 0;;
    }
    else if(bankersHand > 21){
        printf("Banker Busted! You Won!\n");
        return 2;
    }
}

int BankercardValue(int v, int bankersHand){
    if(v >= 10){
        v = 10;
    }
    else if((v >= 2) && (v <= 9)){
        v = v;
    }
    else{v = Bankerpick(v, bankersHand);}

    return v;
}

int Bankerpick(int v, int bankersHand){

printf("Banker Recieved an Ace!\n");
if(bankersHand <= 10){
    printf("Banker Chose High For Ace.\n");
    v = 11;
    return v;
}
else{
    v = 1;
    printf("Banker Chose Low For Ace.\n");
    return v;
}
}

int gameover(){
char gameOverDecision[3];
int d;
    scanf("%s", gameOverDecision);
    if((strcmp(gameOverDecision, "Yes") == 0) || (strcmp(gameOverDecision, "yes") == 0)){
    printf("Welcome Back!\nPlease Enter New Amount.\n");
    scanf("%d", &d);
    return d;
}
else if((strcmp(gameOverDecision, "No") == 0) || (strcmp(gameOverDecision, "no") == 0)){
    return 0;
}
else{printf("I am Sorry, I did not understand that./n Please Re-Enter Decision.\n");
    gameover();
}
}
\$\endgroup\$

closed as off-topic by Jamal Dec 7 '15 at 2:00

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions containing broken code or asking for advice about code not yet written are off-topic, as the code is not ready for review. After the question has been edited to contain working code, we will consider reopening it." – Jamal
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 6
    \$\begingroup\$ Please look at the formatting and make it look nicer (indendation). You probably want people here to comment on other issues (not formatting). E.g. run your program through the indent program first. \$\endgroup\$ – Brandin Dec 6 '15 at 10:44
  • 3
    \$\begingroup\$ I was going to review the code but I'm putting it off because dealing with the indentation is too much effort to get through. \$\endgroup\$ – Emily L. Dec 6 '15 at 12:25
  • 1
    \$\begingroup\$ Ahh! The repetition! \$\endgroup\$ – Carcigenicate Dec 6 '15 at 18:38
  • 1
    \$\begingroup\$ The second paragraph, which asks about adding additional implementation, is off-topic. Please remove that part to make this on-topic. \$\endgroup\$ – Jamal Dec 7 '15 at 2:01
6
\$\begingroup\$

Other possible cleanups:

  • Avoid declaring function prototype if you can. Having the function prototype plus the actual definition just adds more maintenance overhead. In your program, all functions can simply be declared directly before main().

  • Indenting and spacing are not consistent. This hurts readability a lot. Every scope ({ }) should be properly indented to convey structure/nesting. I strongly suggest installing an auto-formatter plugin into your editor to automate that task for you. Clang-Format is a very good option, for instance.

  • Too many magic numbers. Use enum constants with descriptive names instead.

  • These //------------ ... something comments are distracting. A blank line would suffice.

  • Decide on a naming scheme and be consistent with it. You have a few names LikeThis but most are likeThis. Use one or the other. camelCase is more commonly used for function names and variable names while PascalCase is often used to name types/structures.

  • The bool type is available in modern C, you just have to include <stdbool.h>.


Lastly, I'd like to commend you for not using global variables. You did well by using function parameters to manage your program states. Good job!

\$\endgroup\$
5
\$\begingroup\$

You repeat the following printing pattern many times:

printf("%d-------\n", n);
printf("%c       |\n", cardType);
printf("|       |\n", n);
printf("|       %c\n", cardType);
printf(" -------%d\n", n);

I suggest isolating it into a function.

cardColor

It may be written much more shortly and simply:

void cardColor(){
    puts( rand() % 2 ? "Black" : "Red");
}

Avoid confusing return types

Returning char from cardColor is misleading. It only works at all because printf returns the number of chars printed that being an int may be converted to char. But you do not use that, and why would anyone want to consider the number of characters printed as a char? Just declare that you don't care about the return value (with void) and everything becomes clearer.

Warnings

Compile with gcc -Wall -pedantic yourfile.c and you will see some warnings, the compiler automatically warns you about bad practice, so make it a habit to compile with the -Wall -pedantic flags.

Some of the warnings are:

b.c:26:5: warning: implicit declaration of function ‘time’ [-Wimplicit-function-declaration]
     srand(time(NULL));
     ^


b.c: In function ‘cardSymbolNums’:
b.c:165:5: warning: too many arguments for format [-Wformat-extra-args]
     printf("|       |\n", n);
     ^

But there are also others, I leave to you the learning experience of learning about them and fixing them.

Simplify logic

else if(bankersHand <= uHand){
    return 0;
}
else if (bankersHand == uHand){
    return 0;;
}

Becomes the identical but simpler:

else if(bankersHand <= uHand){
    return 0;
}

That is, you can remove the second else if as the first one already accounts for that.

And use the boolean ||:

if ((userInput == 1))
{
  return userInput;
}
else if (userInput == 2)
{
  return userInput;
}

Becomes:

if (userInput == 1 || userInput == 2)
{
  return userInput;
}
\$\endgroup\$
4
\$\begingroup\$

BTW: please format your code before posting, especially the large amount of whitespace does not much good in the tight boxes here at StackExchange.

Please do not mix declarations and logic code, not everything that is allowed should be done.

Example:

void cardSymbolNums(int n){
  int face;
  face = rand() % 4;
  char cardType;

  if( ...

Better

void cardSymbolNums(int n){
  int face;
  char cardType;

  face = rand() % 4;

  if( ...

Let's take a look at all of the compiler warnings you ignored Yes, I know, you'll probably used a different one with different warnings etc. pp. but code should compile without warnings.

$ gcc --version
gcc-4.8.real (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4

Run as:

cc -std=c11 -W -Wall -O0 blackjack-beginner.c -o blackjack-beginner

If you are not familiar with the GCC: standard C11 with most warnings switched on and no optimization.

blackjack-beginner.c: In function âmainâ:
blackjack-beginner.c:28:3: warning: implicit declaration of function âtimeâ [-Wimplicit-function-declaration]
   srand(time(NULL));

The function time(2) is defined in time.h which you did not include.

blackjack-beginner.c:38:7: warning: variable âbankersHandâ set but not used [-Wunused-but-set-variable]

You set it twice to zero but never use that value, you should delete them all or comment them out.

   int bankersHand;
blackjack-beginner.c:31:7: warning: unused variable âvalueâ [-Wunused-variable]
   int value = 0;

You declared variables which you did not use later. That's ok, but you really should comment things out that you do not use.

blackjack-beginner.c: In function âcardSymbolNumsâ:
blackjack-beginner.c:174:5: warning: too many arguments for format [-Wformat-extra-args]
     printf("|       |\n", n);
// multiple times

You gave printf() two arguments, the string "| |\n" and n but you do not use the second argument. Most compiler will be ok with that but it actually is and error.

Several other unused variables: cardType and userChoice

blackjack-beginner.c: In function âbankersDrawâ:
blackjack-beginner.c:316:21: warning: unused parameter âuserBetâ [-Wunused-parameter]
blackjack-beginner.c:316:34: warning: unused parameter âuserMoneyâ [-Wunused-parameter]
blackjack-beginner.c:316:60: warning: unused parameter âbankersBetâ [-Wunused-parameter]
blackjack-beginner.c:317:7: warning: unused parameter âbankersMoneyâ [-Wunused-parameter]
   int bankersMoney)

You have defined arguments for the function bankersDraw that you never use. Please comment them out or delete them.

blackjack-beginner.c: In function âcardColorâ:
blackjack-beginner.c:142:1: warning: control reaches end of non-void function [-Wreturn-type]

Also in cardFace, checkBet, pick, results, bankersDraw, smartBanker, and gameover.

You declared all of them to return int but they do not always return. The compiler understands a lot, e.g.: the construct in Bankerpick but not the recursion in gameover.

cardColor does not return anything, you can use void

cardFace does not return anything, you can use void

checkBet does something in one branch but nothing in the other Suggestion: merge the functions of checkBet into askBet

pick does something in one branch but nothing in the other Suggestion: either return something like e.g.: 0 or restructure.

results see: pick

bankersDraw it has an if and an else if and no else but the compiler does not know that you need no else. Suggestion: just return a zero after the loop or restructure.

smartBanker the same reason ( no else) as for bankersDraw. Suggestion: just return -1 or restructure.

gameover does return nothing in else branch. Suggestion: just return a zero or restructure.

GCC compiles without warnings now. clang? Not so much. (clang with the same options as GCC)

blackjack-beginner.c:227:7: warning: explicitly assigning a variable of type
  'int' to itself [-Wself-assign]
    v = v;

Mmh...any reason for this?

int cardValue(int v)
{
  if (v >= 10) {
    v = 10;
  } else if ((v >= 2) && (v <= 9)) {
    v = v;
  } else {
    v = pick(v);
  }
  return v;
}

Ah, I see. You can restructure it a bit

int cardValue(int v)
{
  if (v >= 10) {
    return 10;
  } else if ((v >= 2) && (v <= 9)) {
    return v;
  } else {
    return pick(v);
  }
}

Next warning:

blackjack-beginner.c:266:20: warning: equality comparison with extraneous
  parentheses [-Wparentheses-equality]
    if ((userInput == 1)) {

Just too much parentheses, nothing worthwhile in this case.

blackjack-beginner.c:322:7: warning: explicitly assigning a variable of type
  'int' to itself [-Wself-assign]
    v = v;

Same as the old one. restructured version:

int BankercardValue(int v, int bankersHand)
{
  if (v >= 10) {
    return 10;
  } else if ((v >= 2) && (v <= 9)) {
    return v;
  } else {
    return Bankerpick(v, bankersHand);
  }
}

Let's run a linter (here I used splint version 3.1.2, quite an old one)

blackjack-beginner.c: (in function main)
blackjack-beginner.c:36:9: Function srand expects arg 1 to be unsigned int gets
                          time_t: time(NULL)

You should cast here. Please be aware that the size of time_t might not fit into an unsigned integer, although that would be a rare case and it would get, hopefully, caught by the compiler.

blackjack-beginner.c:42:3: Return value (type int) ignored: scanf("%d", &use...

The return of scanf() can be quite useful, let me quote the man-page here

RETURN VALUE These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.

The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs. EOF is also returned if a read error occurs, in which case the error indicator for the stream (see ferror(3)) is set, and errno is set indicate the error.

You also ignore the returns of your own functions. You probably know when to ignore them and when not, but a cast to void might be useful (but not always!) in that cases.

blackjack-beginner.c:7:6: Function exported but not used outside
                         blackjack-beginner: cardSymbolNums

You declare all of you functions at the top. You don't have to, as GLampert explained.

Now, that we got rid of all warnings—actually or "explained away"—we can take a look at the logic.

Others hit a lot of the points I had, but some are still open.

You have a lot of small recursive loops when you communicate with the user. I would put it all in single function, dedicated to communication.

Pseudocode

int askUser(question)
{
   print(question)
   answer = scanAnswer()
   return answer
}

It gets complicated if the answers are free-form and cannot be counted directly, but for know...

A nice structure is good but too many little functions are just that: too many. If you draw a card, you should get everything regarding drawing cards done in one function, not in five.

The question strings are scattered all over the code, put them all in one place where you can find them easily.

Your code is not commented. Nowhere.

Another one re formatting: your function names are not very consistent and, although really a matter of personal taste, camel-case is frowned upon in C.

Has anybody answered your question regarding the symbols? No? It's simple: the font your command-lines use differ from one to one. I have my xterm Unicode-enabled and can probably see your symbols. Only "probably" because you might not use Unicode and I might not have the correct font installed.

You can assume Unicode in these times, even for the command-line, but it is a little bit differently handled in C as can be seen e.g.: here (first find in Google, admitted, there are probably better explanations out there for a beginner)

\$\endgroup\$
3
\$\begingroup\$

Instead of using a bunch of elif statements, you could use a switch.

switch (WinOrLoss)
{
    case 1:
        // some code
        break;

    case 2: 
        // some other code
        break;

    case 3:
        // ...
       break;
 }

But, it's kind of hard to remember what all those numbers represent. I'd define a couple of enums too. Create one for WinOrLoss, userinput, and maybe even create one for your cards too. An enum for your cards isn't the best way to go, but it's a good first step in the right direction.

One last note: WinOrLoss sounds like a Boolean, but has at least 3 distinct states. I'm reminded of What is truth?. Consider finding a better name for that variable.

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

Your card drawing is assuming that there are an infinite number of decks shuffled together, making it possible (albeit not very likely) to for example draw 20 aces in a row.

This is not comparable to how you would play at a real black jack table (you can't count cards for instance).

You'd need to decide a number of decks you want, allocate a buffer with all card values and shuffle it (for example using Fisher-Yates) then have a pointer into the buffer for the next card to draw and increment it. Once you reach the end, you shuffle the buffer again and reset the pointer to the start.

\$\endgroup\$
  • 1
    \$\begingroup\$ I'm fine with an infinite deck, especially for his first try. I think implementing a finite deck could be one of the last things he worries about. \$\endgroup\$ – John Dec 6 '15 at 15:56
  • 1
    \$\begingroup\$ Just a note that if OP wants to limit the deck, most casinos use several decks at once in a shoe to help combat card counting. It's much harder to keep a count on 5 or 10 decks than one. \$\endgroup\$ – RubberDuck Dec 6 '15 at 16:31
  • 1
    \$\begingroup\$ @RubberDuck That's why I said "You need to decide a number of decks you want". :) \$\endgroup\$ – Emily L. Dec 6 '15 at 17:14
  • 1
    \$\begingroup\$ This answer isn't a review of the code, but instead a critique on the business requirements of the code. Unless the user has stated plain-English business requirements which clearly conflict with the implemented code, I think we should generally assume the code meets the business requirements, and critiquing those seems clearly outside the scope of Code Review. \$\endgroup\$ – nhgrif Dec 6 '15 at 18:07
  • \$\begingroup\$ @nhgrif OP explicitly asked for opinions on the program: I was just wondering what every one thinks about my Blackjack game.. I pointed out an issue in the game logic that skews the distribution of cards and may affect the game in a way that OP might not have considered. \$\endgroup\$ – Emily L. Dec 7 '15 at 19:15

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