Archived

This topic is now archived and is closed to further replies.

Code feedback on blackjack game

This topic is 4943 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

IDE: Dev-C++ - 4.9.8.7 Language: C I have finished writing a game of blackjack and would like some general feedback on the code - where I can improve it etc. One of the things I would like to do is to improve the output. I would like to be able to get more control over how things are displayed on the screen but I only have experience of using standard C library functions like printf and puts etc. So any advice here would be great.
#include <stdio.h>
#include <stdlib.h>

#define MAXCARDS 52
#define MAXHAND 5
#define MAXSTRING 20

enum SUIT {diamond, club, heart, spade};
enum TYPE {ace, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king};

//card structure

struct CARD
{
  enum SUIT suit;
  enum TYPE type;
};

struct CARD deck[MAXCARDS];//global variable


//player structure

struct PLAYER
{
  struct CARD hand[MAXHAND];
  int numofcards;
  int playertotal;
  int stick;
};

//function prototypes

void flush();

//card functions

void createDeck();
void shuffleCards();
void dealCard(struct PLAYER *, int *);
int getCardValue(struct PLAYER, int);
void displayHand(struct PLAYER);
//game functions

void initialise(struct PLAYER *, struct PLAYER *, int *);
void userMenu(struct PLAYER *, int *);
void dealerAI(struct PLAYER *, int *);
void calculateHandTotal(struct PLAYER *);
void decideWinner(struct PLAYER, struct PLAYER);

int main(int argc, char *argv[])
{  
  //seed the randam number function

  srand((unsigned)time(NULL));
  int index = 0;//index into deck of cards

  char play;
  
  struct PLAYER dealer = {0};
  struct PLAYER player = {0};
  
  createDeck();//create a deck of cards

  initialise(&dealer, &player, &index);//first time initialisation

  
  do
  {
    do
    {
      if(player.stick == 0)
      {
        printf("Player Menu\n");
        userMenu(&player, &index);
        printf("players hand\n");
        displayHand(player);
      }
      if(dealer.stick == 0)
      {
        calculateHandTotal(&dealer);
        dealerAI(&dealer, &index);
        printf("dealers hand\n");
        displayHand(dealer);
      }
    }while(dealer.stick == 0 || player.stick == 0);
    
    //calculate the dealers total

    calculateHandTotal(&dealer);
    //calculate the players total 

    calculateHandTotal(&player);
 
    //decide a winner

    decideWinner(dealer, player);
    
    flush();//to get rid of any characters in the buffer left over from menu select

    
    printf("Deal again(y/n): ");
    scanf("%c", &play);
    flush();
    
    if(play == ''y'')
      initialise(&dealer, &player, &index);   
  }while(play != ''n'');
   
  system("PAUSE");	
  return 0;
}

//function definitions


//function for carraige return problem

void flush()
{
  char ch;
  while((ch = getchar()) != ''\n'');
}

//card function definitions


//create a fresh deck of cards

void createDeck()
{
  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[num].suit = i;
      deck[num].type = j;
      num++;
    }
  }
}

//shuffle cards

void shuffleCards()
{ 
  int i;//looping variable


  for(i = 0; i < 30; i++)
  {
    int num1 = rand() % MAXCARDS;
    int num2 = rand() % MAXCARDS;
    struct CARD temp;//swapping variable

      
    temp = deck[num1];
    deck[num1] = deck[num2];
    deck[num2] = temp;
  }
}

//deal a card

void dealCard(struct PLAYER *player, int *index)
{
  if(player->numofcards == 5)
  {
    printf("Maximum hand reached. Stick has been selected\n");
    player->stick = 1;
  }
  else
  {
    player->hand[player->numofcards] = deck[*index];
 
    (player->numofcards)++;//increment the number of cards the player has

    (*index)++;//increment the index

  }
}

int getCardValue(struct PLAYER player, int index)
{
  int value;
  
  if(player.hand[index].type == ace || player.hand[index].type < ten)
    value = player.hand[index].type + 1;
  else
    value = 10;
       
  return value;   
}

//Create the hand for the player to see

void displayHand(struct PLAYER player)
{
  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[] = {"Diamonds", "Clubs", "Hearts", "Spades"};
  //type string table

  char *pTypeName[] = {"Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"};
  
  int i;//looping variable

  
  //allocate memory for pointers to point to

  for(i = 0; i < player.numofcards; i++)
  {
    pStringSuit[i] = (char *) malloc(MAXSTRING);
    pStringType[i] = (char *) malloc(MAXSTRING);
    if( pStringSuit[i] == NULL )
      printf("Could not allocate memory for pStringSuit\n");
    if( pStringType[i] == NULL )
      printf("Could not allocate memory for pStringType\n");
  }
  
  //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];
  }
  
  //display the cards to the player

  //display all the suits first

  for(i = 0; i < player.numofcards; i++)
  {
    printf("Suit:%s\t", pStringSuit[i]);
  }
  printf("\n");
  //display the type of card

  for(i = 0; i < player.numofcards; i++)
  {
    printf("Type:%s\t", pStringType[i]);
  }
  printf("\n");
  
  //free the memory

  for(i = 0; i < player.numofcards; i++)
  {
    free(pStringSuit[i]);
      printf("Memory freed for %d\n", i);
    free(pStringType[i]); 
      printf("Memory freed for %d\n", i);
  }
}

//game function definitions


//initialise the game

void initialise(struct PLAYER *dealer, struct PLAYER *player, int *index)
{
  shuffleCards();//shuffle the cards


  *index = 0;

  dealer->numofcards = 0;
  player->numofcards = 0;
 
  dealer->playertotal = 0;
  player->playertotal = 0;
  
  dealer->stick = 0;
  player->stick = 0;
  
  //deal first two cards to player and dealer  

  dealCard(player, index);
  dealCard(dealer, index);
  dealCard(player, index);
  dealCard(dealer, index);
  
  //show the players hand

  displayHand(*player);  
}

//twist or stick

void userMenu(struct PLAYER *player, int *index)
{
  int userchoice;
  int menu = 1;
  
  while(menu)
  {
    printf("1. Twist\n");
    printf("2. Stick\n");
    printf("Enter choice: ");
    scanf("%d", &userchoice);
    switch(userchoice)
    {
      case 1: dealCard(player, index);
            menu = 0;
            break;
      case 2: player->stick = 1;
            menu = 0;
            break;
      default: printf("Please select 1 or 2\n");
    }
  }
}

//dealer AI

void dealerAI(struct PLAYER *dealer, int *index)
{
  if(dealer->playertotal <=16)
  {
    printf("Dealer takes another card\n");
    dealCard(dealer, index);
  }
  else
  {
    printf("Dealer sticks\n");
    dealer->stick = 1;
  }
}

//work out the total of a players hand

void calculateHandTotal(struct 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 winner

void decideWinner(struct PLAYER dealer, struct PLAYER player)
{
  //display the total for each player

  printf("Dealers total is: %d\n", dealer.playertotal);
  printf("Your total is: %d\n", player.playertotal);
 
  //check to see if dealer is bust

  if(dealer.playertotal > 21)
    printf("Dealer is bust. You win.\n");
  
  //check to see if player is bust

  if(player.playertotal > 21)
    printf("Dealer wins. You are bust.\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");
  else
  if(dealer.playertotal == dealer.playertotal)
    printf("Dealer wins.\n");
}
[\source]
How about them apples?

Share this post


Link to post
Share on other sites
A suitable graphics API for a card game would be either Java, Flash, DirectX or Opengl.

regards

ace

PS u can start in this forum for Opengl and DirectX, although id say go with DirectX, tis what im learning.

Share this post


Link to post
Share on other sites
Some critiques:
1) try and get rid of that global for a more modular design
2) it''d probably be a good idea to declare those card names in displayHand const char*[] instead of char*[], although it''s not neccessary and your compiler might even notice that it''s never modified
Other than that it''s a very nice piece of code, good job

Share this post


Link to post
Share on other sites
ace_lovegrove, I'm not really trying for graphics with this game. What I meant was that I am trying to improve the screen layout of text and menus etc in this game. If you compile and run the code you will probably see what I mean. I want the screen layout to be a bit cleaner and I wanted to ask if others knew how to get more control of how things are displayed on a console. Like I said I only know how to do things with the printf function and use things like the \t, \n etc to control layout of text.

Thanks for the comments bytecoder.

How about them apples?

[edited by - popcorn on May 17, 2004 8:52:41 PM]

Share this post


Link to post
Share on other sites
Anyone else got any opinions. Can I also ask if the beginner section of the forums is the best place to ask for code feedback or are there better places?

How about them apples?

Share this post


Link to post
Share on other sites
I wouldn''t be completely opposed to posting this in software engineering; code-review is peer-review which is a sound engineering practice.

If you want better alignment when printing the hand, pad the strings with spaces to make them the same length.

char *pSuitName[] = {
"Diamonds",
"Clubs ",
"Hearts ",
"Spades "};



ALL_CAPS is supposed to be reserved for #define macros. C and C++ convention is lower_case for variables and structures. CamelCase is also popular in C++ becuase it''s common with object-oriented code.

MAXCARDS, MACHAND, MAXSTRING are ok, but the enum''s and the CARD struct really shouldn''t be all caps.

You can specify values in enum, so you could do this:

enum card_type {
ace = 1,
two,
three,
four,
};


That way the enum for two would actually have the value two.

Technically those are not function prototypes though even I still call them that. They are forware declarations (prototypes didn''t have to have the parameter list).

It''s common in C to incorporate a typedef into the struct definition so you don''t have to specify struct every time.

typedef struct card_t {
card_suit suit;
card_face face_value;
} card;

void shuffle_cards(card*, int cards);


stick really is a boolean, and in C it''s common to make this explicit. It''s still an int, you just declare it using BOOL (which is a Win32 #define). You could make your own typdef to be portable. I think they added a distinct bool type to C99.

You can surround system("PAUSE") with
#ifndef NDEBUG
system("pause");
#endif
That way a release build won''t pause.

It''s bad practice to dump messages from sub-routines such as dealCards. This makes it much less modulure. dealCards should return an error code that indicates either a warning (positive value), an error (negative value), or everything''s ok (zero). stdio is your current user-interface, but you may want to replace it with graphics later (and will need to visually indicate problems that you current write to the stdout for).

The hard part about blackjack is that the ace can be either 1 or 11. getCardValue doesn''t seem to handle this.

In displayHand you don''t need to reallocate any memory.

char *pSuitName[] = {"Diamonds", "Clubs", "Hearts", "Spades"};
char *pStringSuit[MAXHAND] = {0};

//This works just fine, you can just point to the string in pSuitName
pStringSuit[0] = pSuitName[0];



Initialized is spelled with a z, oh are you from England?
Must be from England, if you want to internationalize your game, you need to use a string table and replace Twist with Hit and Stick with Stand for the US

In userMenu, you can use kbhit and getch so they player doesn''t have to hit enter to take action.

In dealerAI, delaer->stick = TRUE would make more sense to me.

I don''t think the player wins if both of them bust.

Part of BlackJack is seeing the first card the dealer has before you decide to hit or stand. This is an important part of the game!

In main, you can move initialize inside the first do, and eliminate the test for ''y''/initialise at the end of the loops.

Initialise shouldn''t display the hand, it''s job is to initiallize it. You should call the display hand function in main after calling initialise.

Share this post


Link to post
Share on other sites
Thanks for that Magmai Kai Holmlor, your post was very insightful. You''ve made lots of good points that I probably wouldn''t have thought about. The point about allocating memory was actually especially good since I was having some problems freeing memory occasionally.

I always thought that function prototype and forward declarations were the same thing but I guess not now.

About the issue of seeing the dealers first card - at the moment I am using the displayhand function which shows all the cards in a hand. Should I write another function specifically to show the dealers hand so that it would display something like:

suit: unknown suit: spades
type: unknown type: king

I am reluctant to do this since most of the code would be just the same as the code in the displayhand function. So I was wondering if there was a better solution.

Lots to think about, oh you guessed right I am from the UK - Scotland rather than England. I will keep in mind the software engineering section of the forums from now on.

Thanks again.

Share this post


Link to post
Share on other sites
You could instead make DisplayHand() more powerful - add a boolean "hideFirstCard" parameter and check it appropriately in the loop over the cards.

Share this post


Link to post
Share on other sites
Have DisplayHand take a bitmask and only display the cards whos'' bits are on. Then you could use the same function for 3, 5, or 7 card stud (Poker games).

const first_card = 1;
const second_card = 2;
displayHand(dealer, first_card);
displayHand(player, first_card|second_card);

Now I want to write a Poker game

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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 16

typedef int bool;

enum SUIT {diamond, club, heart, spade};
enum TYPE {ace, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king};

//card structure

typedef struct card
{
enum SUIT suit;
enum TYPE type;
}card;

//deck structure

typedef struct deck
{
card cards[MAXCARDS];
int dealt;
}deck;

//player structure

typedef struct player
{
card hand[MAXHAND];
int numofcards;
int playertotal;
bool stick;
}player;

//function prototypes

void flush();

//card functions

void createDeck(deck *);
void shuffleCards(deck *);
int dealCard(player *, deck *);
int getCardValue(player, int);
void displayHand(player, bool);
//void displayHand(player, int);//bitmask version

//game functions

void 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 problem

void flush()
{
char ch;
while((ch = getchar()) != '\n');
}

//card function definitions


//create a fresh deck of cards

void 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 cards

void 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 card

int 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 see

void 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 game

void 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 stick

void 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 AI

void 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 hand

void 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 winner

void 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]

Share this post


Link to post
Share on other sites
- 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().

Share this post


Link to post
Share on other sites
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 total

int 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 0

typedef 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 structure

typedef struct card
{
enum SUIT suit;
enum TYPE type;
}card;

//deck structure

typedef struct deck
{
card cards[MAXCARDS];
int dealt;
}deck;

//player structure

typedef struct player
{
card hand[MAXHAND];
int numofcards;
int playertotal;
bool stick;
}player;

//function prototypes

void flush();

//card functions

void createDeck(deck *);
int dealCard(player *, deck *);
void displayHand(player, bool, bool);
//game functions

void 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 problem

void flush()
{
char ch;
while((ch = getchar()) != '\n');
}

//card function definitions


//create a fresh deck of cards

void 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 total

int 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 see

void 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 game

void 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 stick

void 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 AI

void 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 winner

void 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]

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
quote:
Could you use the tag or something in your signature so it doesn't look like it's part of your post?


Yeah I think I will change it. I've actually had people reply to my signature thinking that I was talking about apple macs or something.

[edited by - popcorn on June 7, 2004 6:10:37 AM]

Share this post


Link to post
Share on other sites