Code feedback on blackjack game

Started by
20 comments, last by popcorn 19 years, 10 months ago
That''s a good idea Magmai Kai Holmlor, I''ll probably try to implement your idea for learning purposes but I feel that the idea that Zahlman put forward is probably more appropriate to the game that I am trying to make at the moment.

I would just like to say that it has been really interesting to put this post as well as others forward as I feel that I have really learnt something new all the time - string tables, bitmasks etc. I have read about these concepts before but it is only after attempting to write this game and putting posts on these forums that I feel that I really understand how to use them properly.


How about them apples?
How about them apples?
Advertisement
You can print characters for clubs, diamonds, hearts, & spades using the extented acsii characters 3-6.

"\5",//"Clubs ",
"\4",//"Diamonds",
"\3",//"Hearts ",
"\6",//"Spades ",
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
They actually mean something don''t they. i.e 3 is a heart symbol but it represents end of text in ascii. I''ll put it in though as I think it will be nicer to look at. Thanks for that.

How about them apples?
How about them apples?
UPDATE:

Well I carried out most of the improvements to the code and changed some of the layout. Its been successful and I feel quite happy with the results. The only thing left to do is write a better shuffle function.

Anything else that others can think of would be great.

Does anyone know how to post a screenshot?

The source is below:
#include <stdio.h>#include <stdlib.h>#define MAXCARDS 52#define MAXHAND 5#define MAXSTRING 20#define TRUE 1#define FALSE 0//bitmask defines#define FIRSTCARD 1#define SECONDCARD 2#define THIRDCARD 4#define FOURTHCARD 8#define FIFTHCARD 16typedef int bool;enum SUIT {diamond, club, heart, spade};enum TYPE {ace, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king};//card structuretypedef struct card{  enum SUIT suit;  enum TYPE type;}card;//deck structuretypedef struct deck{  card cards[MAXCARDS];  int dealt;}deck;//player structuretypedef struct player{  card hand[MAXHAND];  int numofcards;  int playertotal;  bool stick;}player;//function prototypesvoid flush();//card functionsvoid createDeck(deck *);void shuffleCards(deck *);int dealCard(player *, deck *);int getCardValue(player, int);void displayHand(player, bool);//void displayHand(player, int);//bitmask version//game functionsvoid initialise(player *, player *, deck *);void userMenu(player *, deck *);void dealerAI(player *, deck *);void calculateHandTotal(player *);void decideWinner(player, player);int main(int argc, char *argv[]){    //seed the randam number function  srand((unsigned)time(NULL));  char play;    deck deck = {0};//create a deck  player dealer = {0};  player player = {0};    createDeck(&deck);//create a deck of cards     do  {    initialise(&dealer, &player, &deck);    do    {      printf("Dealers Hand\n");      displayHand(dealer, TRUE);      printf("Your Hand\n");      displayHand(player, FALSE);          if(player.stick == FALSE)      {        printf("Player Menu\n");        userMenu(&player, &deck);      }      if(dealer.stick == FALSE)      {        calculateHandTotal(&dealer);        dealerAI(&dealer, &deck);      }    }while(dealer.stick == FALSE || player.stick == FALSE);        //calculate the dealers total    calculateHandTotal(&dealer);    //calculate the players total     calculateHandTotal(&player);     printf("Dealers Hand\n");    displayHand(dealer, FALSE);    printf("Your Hand\n");    displayHand(player, FALSE);     //decide a winner    decideWinner(dealer, player);        printf("Deal again(y/n): ");    scanf("%c", &play);    flush();        }while(play != 'n');        #ifndef NDEBUG  system("pause");  #endif  return 0;}//function definitions//function for carraige return problemvoid flush(){  char ch;  while((ch = getchar()) != '\n');}//card function definitions//create a fresh deck of cardsvoid createDeck(deck *deck){  enum SUIT i;//looping variable  enum TYPE j;//looping variable  int num = 0;  for(i = diamond; i <= spade; i++)  {    for(j = ace; j <= king; j++)    {      deck->cards[num].suit = i;      deck->cards[num].type = j;      num++;    }  }}//shuffle cardsvoid shuffleCards(deck *deck){   int i;//looping variable  for(i = 0; i < 52; i++)  {    int num1 = rand() % MAXCARDS;    int num2 = rand() % MAXCARDS;    card temp;//swapping variable          temp = deck->cards[num1];    deck->cards[num1] = deck->cards[num2];    deck->cards[num2] = temp;  }}//deal a cardint dealCard(player *player, deck *deck){  if(player->numofcards == 5)  {    player->stick = TRUE;    return 1;//warning  }  else  {    player->hand[player->numofcards] = deck->cards[deck->dealt];     (player->numofcards)++;//increment the number of cards the player has    (deck->dealt)++;//increment the number of cards dealt  }  return 0;//ok}int getCardValue(player player, int handindex){  int value;    if(player.hand[handindex].type == ace || player.hand[handindex].type < ten)    value = player.hand[handindex].type + 1;  else    value = 10;         return value;   }//Create the hand for the player to seevoid displayHand(player player, bool hidecard)//void displayHand(player player, int hidecard)//bitmask version{  char *pStringSuit[MAXHAND];//array of pointers to strings to hold the suit strings  char *pStringType[MAXHAND];//array of pointers to strings to hold the type strings  //suit string table                       char *pSuitName[] = {"\4", "\5", "\3", "\6"};  //type string table  char *pTypeName[] = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};  //unknown string  char *pUnknown = "?";    int i;//looping variable    //create the strings using an index into the appropriate string table  for(i = 0; i < player.numofcards; i++)  {                              //index to string table    pStringSuit[i] = pSuitName[player.hand[i].suit];    pStringType[i] = pTypeName[player.hand[i].type];  }    if(hidecard)  //if((hidecard & FIRSTCARD) > 0)bitmask version  {    pStringSuit[0] = pUnknown;    pStringType[0] = pUnknown;  }    //bitmask stuff  /*if((hidecard & SECONDCARD) > 0)  {    pStringSuit[1] = pUnknown;    pStringType[1] = pUnknown;  }*/    //display the cards to the player  for(i = 0; i < player.numofcards; i++)  {    printf("%s%s ", pStringSuit[i], pStringType[i]);  }  printf("\n");}//game function definitions//initialise the gamevoid initialise(player *dealer, player *player, deck *deck){  shuffleCards(deck);//shuffle the cards  deck->dealt = 0;  dealer->numofcards = 0;  player->numofcards = 0;   dealer->playertotal = 0;  player->playertotal = 0;    dealer->stick = FALSE;  player->stick = FALSE;    //deal first two cards to player and dealer    dealCard(player, deck);  dealCard(dealer, deck);  dealCard(player, deck);  dealCard(dealer, deck);}//twist or stickvoid userMenu(player *player, deck *deck){  char userchoice;  bool menu = TRUE;    printf("1. Twist\n");  printf("2. Stick\n");  printf("Enter choice: ");    while(menu)  {    if(kbhit())    {      userchoice = getch();      printf("%c\n", userchoice);      switch(userchoice)      {        case '1': if(dealCard(player, deck) == 1)                    printf("Max hand reached\n");                  else                  {                    menu = FALSE;                    break;                  }        case '2': player->stick = TRUE;                  menu = FALSE;                  break;        default: printf("Please select 1 or 2\n");      }    }  }}//dealer AIvoid dealerAI(player *dealer, deck *deck){  if(dealer->playertotal <=16)  {    if(dealCard(dealer, deck) == 1)      printf("Max hand reached\n");    else      printf("Dealer takes another card\n");     }  else  {    printf("Dealer sticks\n");    dealer->stick = TRUE;  }}//work out the total of a players handvoid calculateHandTotal(player *player){  int i;//looping variable  int hasAce = 0;    player->playertotal = 0;//set player total back to zero    for(i = 0; i < player->numofcards; i++)  {    if(player->hand[i].type == ace)      hasAce = 1;    player->playertotal = player->playertotal + getCardValue(*player, i);  }    //if the player has an ace and the conditions are right add another 10 to the playertotal  if(hasAce && player->playertotal <= 11)    player->playertotal = player->playertotal + 10;}//find a winnervoid decideWinner(player dealer, player player){  //display the total for each player  printf("Dealers total is: %d\n", dealer.playertotal);  printf("Your total is: %d\n", player.playertotal);   //if both players are bust   if(dealer.playertotal > 21 && player.playertotal > 21)    printf("You are both bust. The game is tied.\n");     //check to see if dealer is bust  if(dealer.playertotal > 21 && player.playertotal <= 21)    printf("Dealer is bust. You win.\n");    //check to see if player is bust  if(player.playertotal > 21 && dealer.playertotal <= 21)    printf("Dealer wins. You are bust.\n");    //if both players total are the same  if(dealer.playertotal == player.playertotal)    printf("The score is tied. Dealer wins.\n");    //if players are not bust, work out the winner    if((dealer.playertotal > player.playertotal)  && dealer.playertotal <= 21)    printf("Dealer wins.\n");  else  if((player.playertotal > dealer.playertotal) && player.playertotal <= 21)    printf("You win.\n");}[\source]


How about them apples?

[edited by - popcorn on May 31, 2004 12:22:18 PM]
How about them apples?
- Merge calculateHandValue into dealCard; these two make a single action, really. The hand''s value should always be the sum of the cards (notwithstanding the business with aces), so it should get changed any time the hand contents are changed. The easiest way to do that is to call the value-updating from the routines that do the hand-modifying; but there''s only one of those, so just inline it. (And at that point you should be able to figure out how to do it a lot more cleanly. Hint: make use of the information you already have about the previous total and ace content.)

- Implement the rules properly: do one while loop to ask the user for input (and they lose if they bust, even if dealer would too), and then a loop where the dealer accumulates cards. Actually, you might consider doing that looping within dealerAI() and only show the end result (and of course the beginning 2-card configuration) of dealer''s hand to the user.

- Possibly add another flag (another bool, or a value in the bitmask?) to displayHand() to enable outputting the total value of a hand along with the cards.

- Inline decideWinner() in the main loop, since it''s called from only the one spot *and* it produces output. (Of course, you''ll have to rearrange some of that logic and distribute it through the main loop, once you have proper rules.)

- Try implementing an on-demand shuffling technique: initialize your deck with values 1..52 in order, and each time a card is needed:

- pick a random number from 0..numcards-1.
- select deck[that number] and save that as the return value.
- put deck[numcards - 1] into deck[that number].
- decrement numcards.
- translate the return value into a card and return it (put it into a hand, whatever).

Basically, it works by taking a random card out of the remaining undealt cards, and then collapsing the cards back into a contiguous array (to make it easier to do the random selection next time). Since you''re picking randomly anyway, the order of cards in the remaining deck doesn''t matter, so instead of shifting everything to fill the space, you just lift up the card from the end of the deck and put it in the missing space. Now you don''t need a shuffleCards(), but instead just add this logic to dealCard().
Thanks for that Zahlman. Theres a lot of good advice there and I''ll try to implement most of those ideas.
How about them apples?
OK, I've implemented all of the suggestions that Zahlman has suggested. Again, I am mostly satisfied with the results. There are less functions and the program works fine. I did feel though that the dealcard function is now doing quite a lot. i.e dealing a card, shuffling and also working out the hand total.

quote:
The easiest way to do that is to call the value-updating from the routines that do the hand-modifying; but there's only one of those, so just inline it. (And at that point you should be able to figure out how to do it a lot more cleanly. Hint: make use of the information you already have about the previous total and ace content.)


Could you elaborate on this please.
I have implemented the solution as follows:

//deal a card/shuffle and work out hand totalint dealCard(player *player, deck *deck){  int randomnumber;  int i;  bool hasace = FALSE;  card tmpcard;    if(player->numofcards == MAXHAND)  {    player->stick = TRUE;    return 1;//warning  }  else  {    //deal and shuffle    randomnumber = rand() % (MAXCARDS - deck->dealt - 1);    tmpcard = deck->cards[randomnumber];//store the card    deck->cards[randomnumber] = deck->cards[MAXCARDS - deck->dealt - 1] ;//put this card in position so it can't be picked again    deck->cards[MAXCARDS - deck->dealt - 1] = tmpcard;//complete the swap    (deck->dealt)++;//increment the number of cards dealt out from the deck    player->hand[player->numofcards] = tmpcard;        (player->numofcards)++;//increment the number of cards the player has        player->playertotal = 0;         for(i = 0; i < player->numofcards; i++)    {      //calculate hand total      if(player->hand[i].type <= ten)        player->playertotal = player->playertotal + player->hand[i].type;      else        player->playertotal = player->playertotal + 10;              if(player->hand[i].type == ace)        hasace = TRUE;    }    //if the player has an ace    if(hasace && player->playertotal <= 11)      player->playertotal = player->playertotal + 10;       return 0;  }}


      #include <stdio.h>#include <stdlib.h>#define MAXCARDS 52#define MAXHAND 5#define MAXSTRING 20#define TRUE 1#define FALSE 0typedef int bool;enum SUIT {diamond = 1, club, heart, spade};enum TYPE {ace = 1, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king};//card structuretypedef struct card{  enum SUIT suit;  enum TYPE type;}card;//deck structuretypedef struct deck{  card cards[MAXCARDS];  int dealt;}deck;//player structuretypedef struct player{  card hand[MAXHAND];  int numofcards;  int playertotal;  bool stick;}player;//function prototypesvoid flush();//card functionsvoid createDeck(deck *);int dealCard(player *, deck *);void displayHand(player, bool, bool);//game functionsvoid initialise(player *, player *, deck *);void userMenu(player *, deck *);void dealerAI(player *, deck *);inline void decideWinner(player, player);void displaydeck(deck *);int main(int argc, char *argv[]){    //seed the randam number function  srand((unsigned)time(NULL));  char play;     deck deck = {0};//create a deck  player dealer = {0};  player player = {0};    createDeck(&deck);//create a deck of cards    do  {    initialise(&dealer, &player, &deck);    //deal first two cards to player and dealer      dealCard(&player, &deck);    dealCard(&dealer, &deck);    dealCard(&player, &deck);    dealCard(&dealer, &deck);         do    {      printf("Dealers Hand\n");      displayHand(dealer, TRUE, FALSE);      printf("Your Hand\n");      displayHand(player, FALSE, FALSE);          printf("Player Menu\n");      userMenu(&player, &deck);             if(player.playertotal > 21)      {        printf("Dealer wins. You are bust.\n");         player.stick = TRUE;      }          }while(player.stick == FALSE);        if(player.playertotal <= 21)       dealerAI(&dealer, &deck);        printf("Dealers Hand\n");    displayHand(dealer, FALSE, TRUE);    printf("Your Hand\n");    displayHand(player, FALSE, TRUE);        //decide a winner    if(dealer.playertotal <= 21 && player.playertotal <= 21)      decideWinner(dealer, player);        printf("Deal again(y/n): ");    scanf("%c", &play);    flush();          }while(play != 'n');          #ifndef NDEBUG  system("pause");  #endif  return 0;}//function definitions//function for carraige return problemvoid flush(){  char ch;  while((ch = getchar()) != '\n');}//card function definitions//create a fresh deck of cardsvoid createDeck(deck *deck){  enum SUIT i;//looping variable  enum TYPE j;//looping variable  int num = 0;  for(i = diamond; i <= spade; i++)  {    for(j = ace; j <= king; j++)    {      deck->cards[num].suit = i;      deck->cards[num].type = j;      num++;    }  }}//deal a card/shuffle and work out hand totalint dealCard(player *player, deck *deck){  int randomnumber;  int i;  bool hasace = FALSE;  card tmpcard;    if(player->numofcards == MAXHAND)  {    player->stick = TRUE;    return 1;//warning  }  else  {    //deal and shuffle    randomnumber = rand() % (MAXCARDS - deck->dealt - 1);    tmpcard = deck->cards[randomnumber];//store the card    deck->cards[randomnumber] = deck->cards[MAXCARDS - deck->dealt - 1] ;//put this card in position so it can't be picked again    deck->cards[MAXCARDS - deck->dealt - 1] = tmpcard;//complete the swap    (deck->dealt)++;//increment the number of cards dealt out from the deck    player->hand[player->numofcards] = tmpcard;        (player->numofcards)++;//increment the number of cards the player has        player->playertotal = 0;         for(i = 0; i < player->numofcards; i++)    {      //calculate hand total      if(player->hand[i].type <= ten)        player->playertotal = player->playertotal + player->hand[i].type;      else        player->playertotal = player->playertotal + 10;              if(player->hand[i].type == ace)        hasace = TRUE;    }        if(hasace && player->playertotal <= 11)      player->playertotal = player->playertotal + 10;       return 0;  }}//Create the hand for the player to seevoid displayHand(player player, bool hidecard, bool displaytotal){  char *pStringSuit[MAXHAND];//array of pointers to strings to hold the suit strings  char *pStringType[MAXHAND];//array of pointers to strings to hold the type strings  //suit string table                       char *pSuitName[] = {"\4", "\5", "\3", "\6"};  //type string table  char *pTypeName[] = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};  //unknown string  char *pUnknown = "?";    int i;//looping variable    //create the strings using an index into the appropriate string table  for(i = 0; i < player.numofcards; i++)  {                              //index to string table    pStringSuit[i] = pSuitName[player.hand[i].suit - 1];//minus one to get the correct element in the string table    pStringType[i] = pTypeName[player.hand[i].type - 1];//minus one to get the correct element in the string table  }    if(hidecard)  {    pStringSuit[0] = pUnknown;    pStringType[0] = pUnknown;  }    //display the cards to the player  for(i = 0; i < player.numofcards; i++)  {    printf("%s%s ", pStringSuit[i], pStringType[i]);  }    if(displaytotal)    printf("\nTotal is: %d", player.playertotal);    printf("\n");}//game function definitions//initialise the gamevoid initialise(player *dealer, player *player, deck *deck){  deck->dealt = 0;    dealer->numofcards = 0;  player->numofcards = 0;   dealer->playertotal = 0;  player->playertotal = 0;    dealer->stick = FALSE;  player->stick = FALSE;}//twist or stickvoid userMenu(player *player, deck *deck){  char userchoice;  bool menu = TRUE;    printf("1. Twist\n");  printf("2. Stick\n");  printf("Enter choice: ");    while(menu)  {    if(kbhit())    {      userchoice = getch();      printf("%c\n", userchoice);      switch(userchoice)      {        case '1': if(dealCard(player, deck) == 1)                    printf("Max hand reached\n");                  else                  {                    menu = FALSE;                    break;                  }        case '2': player->stick = TRUE;                  menu = FALSE;                  break;        default: printf("Please select 1 or 2\n");      }    }  }}//dealer AIvoid dealerAI(player *dealer, deck *deck){  do  {    if(dealer->playertotal > 21)    {      printf("Dealer is bust. You win.\n");      dealer->stick = TRUE;    }    else    {      if(dealer->playertotal <=16)      {        if(dealCard(dealer, deck) == 1)          printf("Max hand reached\n");        else          printf("Dealer takes another card\n");         }      else      {        printf("Dealer sticks\n");        dealer->stick = TRUE;      }    }  }while(dealer->stick == FALSE);}//find a winnervoid decideWinner(player dealer, player player){  //if both players total are the same  if(dealer.playertotal == player.playertotal)    printf("The score is tied. Dealer wins.\n");    //if players are not bust, work out the winner    if(dealer.playertotal > player.playertotal)    printf("Dealer wins.\n");  else  if(player.playertotal > dealer.playertotal)    printf("You win.\n");}void displaydeck(deck *deck){  char *pStringSuit[MAXCARDS];//array of pointers to strings to hold the suit strings  char *pStringType[MAXCARDS];//array of pointers to strings to hold the type strings  char *pSuitName[] = {"\4", "\5", "\3", "\6"};  char *pTypeName[] = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};   int i;    for(i = 0; i < MAXCARDS; i++)  {                              //index to string table    pStringSuit[i] = pSuitName[deck->cards[i].suit - 1];    pStringType[i] = pTypeName[deck->cards[i].type - 1];  }    for(i=0; i<MAXCARDS; i++)  {    printf("Suit: %s\n", pStringSuit[i]);    printf("Type: %s\n", pStringType[i]);  }}






[edited by - popcorn on June 6, 2004 8:59:54 AM]

[edited by - popcorn on June 6, 2004 9:00:23 AM]
How about them apples?
quote:Original post by popcorn
OK, I''ve implemented all of the suggestions that Zahlman has suggested. Again, I am mostly satisfied with the results. There are less functions and the program works fine. I did feel though that the dealcard function is now doing quite a lot. i.e dealing a card, shuffling and also working out the hand total.


The thing is that it doesn''t really "shuffle"; it takes a random card out of the remaining set. If you do want to separate it out, then just write a separate "int getCard(deck *deck)" function returning the code for the dealt card. Though honestly I wouldn''t bother for a program like this.

And "working out the hand total" is just preserving an invariant - "playertotal == sum of values of cards".

As for how that''s done simply - see my comments in the code:

int dealCard(player *player, deck *deck){  int randomnumber;  int i;  bool hasace = FALSE; // You''ll need to put this in the player  // structure instead to make the simplification work.  card tmpcard;    if(player->numofcards == MAXHAND)  {    // You can probably arrange it so that this never happens,    // by setting player->stick when that last card is    // actually dealt.    player->stick = TRUE;    return 1;//warning  }  else  {    // declare randomnumber here; even in C you can declare at    // the top of a scope, not just at tops of functions. Keep    // stuff local to where it''s used.    randomnumber = rand() % (MAXCARDS - deck->dealt - 1);    tmpcard = deck->cards[randomnumber];    deck->cards[randomnumber] = deck->cards[MAXCARDS - deck->dealt - 1] ;//put the last card into this position so that we won''t pick the same one again    // deck->cards[MAXCARDS - deck->dealt - 1] = tmpcard;     // -- don''t need to do this; all cards beyond the end are invalid anyway.    (deck->dealt)++;    player->hand[player->numofcards] = tmpcard;        (player->numofcards)++;        // **** At this point, the "playertotal" already stores    // **** the total value of the previous cards. So the new    // **** value will just be that value, plus the value of the    // **** new card. So don''t reset playertotal here, just add    // **** the value of the tmpcard.    // **** Well, not quite. There''s still that tricky issue with    // **** aces. So you can add some logic to cover that, but    // **** it''s probably harder than you think. In any case,    // **** you''ll need to track "hasAce" as part of the Player    // **** struct. (Or else go searching for aces again every    // **** time, which rather defeats the purpose.)    // **** Here''s my recommendation: Have "playertotal" store    // **** the "raw" total, counting aces as 1. Then write an    // **** accessor function, like "int getValue(player *hand)"    // **** which will return either hand->playertotal or    // **** hand->playertotal + 10, according to the value of    // **** hand->hasAce.}


For your next project, I''d suggest converting this all to C++ (or even Java), to see how all this pointer-passing gets abstracted away for you. I think you''ve got a pretty good handle on the whole OO idea actually
Thanks again Zahlman. I think I have a better idea of what to do to handle the aces now.
How about them apples?
them apples is pretty tasty.

This topic is closed to new replies.

Advertisement