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?
Code feedback on blackjack game
You can print characters for clubs, diamonds, hearts, & spades using the extented acsii characters 3-6.
"\5",//"Clubs ",
"\4",//"Diamonds",
"\3",//"Hearts ",
"\6",//"Spades ",
"\5",//"Clubs ",
"\4",//"Diamonds",
"\3",//"Hearts ",
"\6",//"Spades ",
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:
How about them apples?
[edited by - popcorn on May 31, 2004 12:22:18 PM]
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]
- 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().
- 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.
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.
Could you elaborate on this please.
I have implemented the solution as follows:
[edited by - popcorn on June 6, 2004 8:59:54 AM]
[edited by - popcorn on June 6, 2004 9:00:23 AM]
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]
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
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement