-
Blackjack
Hi there.
I am trying to write a blackjack game and I am running into a few teething problems. My main() function currently outputs what values are stored for certain variables, just to see if I'm doing things properly. Here is the code:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 10
typedef struct {
int card;
int suit;
int value;
} card_t;
void generate_card(card_t *crd, int *num, int *score);
int main(void) {
/* current scores for dealer and player */
int p_score = 0;
int d_score = 0;
/* number of cards in their hand */
int p_numofcards = 0;
int d_numofcards = 0;
/* initialise hands */
card_t p_hand[MAX];
card_t d_hand[MAX];
srand((unsigned int)time(NULL));
int i;
for(i=0;i<2;i++) { /*loop twice because at the start of blackjack, each player is given two cards*/
generate_card(&p_hand[i], &p_numofcards, &p_score);
generate_card(&d_hand[i], &d_numofcards, &d_score);
}
/* test */
printf("p_score=%d, p_numofcards=%d\n", p_score, p_numofcards);
printf("d_score=%d, d_numofcards=%d\n", d_score, d_numofcards);
int k;
for(k=0;k<2;k++) {
printf("p_hand[%d].card=%d,"
" p_hand[%d].suit=%d, "
" p_hand[%d].value=%d\n",
k,p_hand[i].card,
k,p_hand[i].suit,
k,p_hand[i].value
);
printf("d_hand[%d].card=%d,"
" d_hand[%d].suit=%d,"
" d_hand[%d].value=%d\n",
k,d_hand[i].card,
k,d_hand[i].suit,
k,d_hand[i].value
);
}
return 0;
}
void generate_card(card_t *crd, int *num, int *score) {
/* crd = (card_t *)malloc(MAX*sizeof(card_t)); */
crd->card = (rand() % 13) + 1;
crd->suit = (rand() % 4) + 1;
int c;
for(c=1;c<11;c++) {
if(crd->card == c) { /*if card is 1-10 (ace- 10) then let the value of the card be whatever it is*/
crd->value = c;
(*score)+=c; /*adds to the score (eg. 10 of hearts would add a 10) */
(*num)++; /*increments the number of cards in the players' hand*/
}
}
switch(crd->card) {
case 11: case 12: case 13: /* if card is jack, queen or king...*/
crd->value = 10;
(*score)+=10;
(*num)++;
break;
}
/*free(crd);*/
}
Let me explain a few things. The card_t type represents with the following attributes: card( 1-13 for ace-king), suit(1-4 for hearts, clubs etc), value( ace=1, jack=10, queen=10 and so on). The generate_card function is supposed to generate a random card to simulate the dealer dealing unkown cards to the participants of the game. The program compiles but outputs rather absurd data:
Code:
$ gcc -o bjack bjack.c
$ ./bjack
p_score=8, p_numofcards=2
d_score=8, d_numofcards=2
p_hand[0].card=0, p_hand[0].suit=0, p_hand[0].value=0
d_hand[0].card=0, d_hand[0].suit=134513052, d_hand[0].value=24641422
p_hand[1].card=0, p_hand[1].suit=0, p_hand[1].value=0
d_hand[1].card=0, d_hand[1].suit=134513052, d_hand[1].value=24641422
Another run to show it is producing random cards correctly:
Code:
$ ./bjack
p_score=11, p_numofcards=2
d_score=6, d_numofcards=2
p_hand[0].card=0, p_hand[0].suit=0, p_hand[0].value=0
d_hand[0].card=0, d_hand[0].suit=134513052, d_hand[0].value=24641422
p_hand[1].card=0, p_hand[1].suit=0, p_hand[1].value=0
d_hand[1].card=0, d_hand[1].suit=134513052, d_hand[1].value=24641422
Any help would be much appreciated, thanks.
-
> k,p_hand[i].card,
Maybe
k,p_hand[k].card,
Also, you need to introduce the idea of a "deck" of cards, which is initially "shuffled" into random order, then "dealt" one card at a time to each player.
Your technique could easily have both players having the same cards.
-
Haha what a ignoramus I am. Thanks for that
Regarding the deck of cards, you're right, it slipped my mind. Should I make an array of size 52, then mark each one null when it has been dealt? Seems kind of resource intensive, what would be a better way?
-
Why not just start with every card, swap random cards a few times until all the cards are shuffled, and then take the top card off of the stack? Then you could set that card to NULL and take the next one, etc. This method wouldn't use too much CPU time, except for during initialization.
-
An array of 52 flags is one method (having "char cards[52]"), but it would require checking the array for unused cards and if used, the randomization is rerun. Using a card number from 1 to 52 in an array of 52 with each randomized is another method. Used cards would be zeros in the deck, unused cards (cards still in the deck) would be 1's in the flag method and the card number using the card number method. For determining the card number, come up with a pattern. I use A to K for 1 to 13 then clubs, diamonds, heats, then spades, for the groups of 13 (note that they are in numerical then alphabetical order). These are just some tips though. There's other ways available.
-
If you don't want it so 'resource intensive' then use a bitset perhaps? (Or a few bitsets...)
You'd need ~7 bytes to store the flags... (52 / 8, 8 bits to a byte)
-
This is actually my second attempt at it, I made a meal of the first one. I actually made provisions for two of the same card being dealt, don't know why it slipped my mind this time. What I did was generate a random card, then see if it was already the the players' hand. If it was, I would generate another one. So something like:
Code:
while( card has already been dealt ) {
generate_card();
}
Does this seem to be a good idea? Sorry, I am just starting out in C, forgive me if this is just as crap as my other idea.
Thanks.
-
Hi, I have decided to redo it, the way dwks stated.
But I'm getting strange output. Basically I have created a deck of cards and have shuffled them. It all seems fine. Then I created a cardtostring function to output '5 of spades' for example. But when testing the function, it produces strange output. Can someone please back me up on this:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define NUMOFCARDS 52
typedef struct {
int card;
int suit;
int value;
} card_t;
int randy(void);
void swapcards(card_t *c1, card_t *c2);
int value(int a);
void cardtostring(card_t crd, char *str);
int main(void) {
srand((unsigned int)time(NULL));
card_t deck[NUMOFCARDS];
/*this intialises the deck giving each card its attributes*/
int i,k,index;
for(i=0;i<4;i++) {
for(k=0;k<13;k++) {
index = (i*13) + k;
deck[index].card = k+1;
deck[index].suit = i+1;
deck[index].value = value(k+1);
}
}
int h=0;
int j;
while(h < 26) { /*loop 26 times swapping a card from the first 26 with a random one from the last 26*/
j = randy();
swapcards(&deck[h],&deck[j]);
h++;
}
/* this is for testing what card is where in the deck after i shuffled them
int p;
for(p=0;p<52;p++) {
printf("Card % d: (card:%d,suit:%d,value:%d)\n",p+1,deck[p].card,deck[p].suit,deck[p].value);
}
*/
/* something is wrong here. this generates output to test the cardtostring function */
char st[30];
int l;
for(l=0;l<52;l++) {
cardtostring(deck[l], st);
printf("The %dth card on the deck is: %s\n",l+1, st);
strcpy(st,"");
}
return 0;
}
int randy(void) {
/* gen random number between 26 and 52 */
return (rand() % (52-26+1))+ 26;
}
void swapcards(card_t *c1, card_t *c2) { /* function to swap two given cards in the deck */
card_t temp;
temp = *c1;
*c1 = *c2;
*c2 = temp;
}
int value(int a) { /* value of card ace=1,jack=10,king=10 etc (will deal with ace=11 later*/
if(a > 0 && a < 11)
return a;
else
return 10;
}
void cardtostring(card_t crd, char *str) {
/*try to create a string of the form "4 of spades" */
if(crd.card > 1 && crd.card < 11) { /* between 2 and 10 then add it to the string */
sprintf(str,"%d",crd.card);
}
switch(crd.card) { /*here add ace for 1, jack for 11 etc */
case 1:
strcat(str, "Ace");
break;
case 11:
strcat(str, "Jack");
break;
case 12:
strcat(str, "Queen");
break;
case 13:
strcat(str, "King");
break;
}
strcat(str, " of "); /* the of part in '4 of hearts' */
switch(crd.suit) {
case 1:
strcat(str, "spades");
break;
case 2:
strcat(str, "clubs");
break;
case 3:
strcat(str, "hearts");
break;
case 4:
strcat(str, "diamonds");
}
}
In my terminal I often see a '4 of <blank space> ' now and again (not all the time). Does anyone else get this? If so, can anyone see what I am doing wrong? Thanks
-
It sounds like none of these case statements are executing:
Code:
switch(crd.suit) {
case 1:
strcat(str, "spades");
break;
case 2:
strcat(str, "clubs");
break;
case 3:
strcat(str, "hearts");
break;
case 4:
strcat(str, "diamonds");
}
Which would mean that crd.suit is invalid. I don't know why, because the rest of your code looks okay. It might give you a starting point.
Rather than using the numeric literal "52" in your program, you should probably be using NUMOFCARDS, and NUMCARDS/2 instead of 26. Just a thought.
Code:
int h=0;
int j;
while(h < 26) { /*loop 26 times swapping a card from the first 26 with a random one from the last 26*/
j = randy();
swapcards(&deck[h],&deck[j]);
h++;
}
That works, I suppose, but it puts a slight bias on the results. You could get away with something as simple as
Code:
int x;
for(x = 0; x < NUMCARDS * 10; x ++) {
swapcards(&deck[rand() % NUMCARDS], &deck[rand() % NUMCARDS]);
}
since swapcards() works perfectly well when passed the same card for both arguments. NUMCARDS*10 swaps might be overkill, but it would give you a very well shuffled deck. :)
-
No the case statements are executing. It is just the odd '4 of <space>' that crops up for no apparent reason. Well no reason that I can see, although there must be one. Here is the output:
Code:
The 1th card on the deck is: Ace of hearts
The 2th card on the deck is: 9 of hearts
The 3th card on the deck is: Ace of diamonds
The 4th card on the deck is: King of hearts
The 5th card on the deck is: 2 of hearts
The 6th card on the deck is: 7 of hearts
The 7th card on the deck is: 5 of spades
The 8th card on the deck is: 3 of spades
The 9th card on the deck is: Jack of diamonds
The 10th card on the deck is: 4 of
The 11th card on the deck is: Ace of spades
The 12th card on the deck is: 3 of diamonds
The 13th card on the deck is: 7 of spades
The 14th card on the deck is: King of spades
The 15th card on the deck is: Queen of diamonds
The 16th card on the deck is: Ace of clubs
The 17th card on the deck is: Queen of hearts
The 18th card on the deck is: 5 of hearts
The 19th card on the deck is: 7 of diamonds
The 20th card on the deck is: 3 of clubs
The 21th card on the deck is: 6 of spades
The 22th card on the deck is: Jack of hearts
The 23th card on the deck is: 7 of clubs
The 24th card on the deck is: 10 of spades
The 25th card on the deck is: 4 of diamonds
The 26th card on the deck is: 4 of spades
The 27th card on the deck is: Jack of spades
The 28th card on the deck is: 10 of clubs
The 29th card on the deck is: 3 of hearts
The 30th card on the deck is: 4 of hearts
The 31th card on the deck is: 5 of clubs
The 32th card on the deck is: 6 of hearts
The 33th card on the deck is: 8 of clubs
The 34th card on the deck is: 8 of hearts
The 35th card on the deck is: 2 of spades
The 36th card on the deck is: 10 of hearts
The 37th card on the deck is: 9 of clubs
The 38th card on the deck is: 4 of clubs
The 39th card on the deck is: King of clubs
The 40th card on the deck is: 8 of spades
The 41th card on the deck is: 2 of diamonds
The 42th card on the deck is: Queen of spades
The 43th card on the deck is: Queen of clubs
The 44th card on the deck is: 5 of diamonds
The 45th card on the deck is: 6 of diamonds
The 46th card on the deck is: 6 of clubs
The 47th card on the deck is: 8 of diamonds
The 48th card on the deck is: 9 of diamonds
The 49th card on the deck is: 10 of diamonds
The 50th card on the deck is: 9 of spades
The 51th card on the deck is: 2 of clubs
The 52th card on the deck is: King of diamonds
Everything apart from that '4 of <space>' is fine.
Regarding the shuffle, you're right, I will give it a thorough shuffle. And yes, I should be using NUMOFCARDS, I was anxious to test it and it just slipped my mind. Thanks
-
I'll elaborate on "none of the case statements are executing".
Code:
switch(crd.suit) {
case 1:
strcat(str, "spades");
break;
case 2:
strcat(str, "clubs");
break;
case 3:
strcat(str, "hearts");
break;
case 4:
strcat(str, "diamonds");
}
}
If crd.suit is not 1, 2, 3, or 4, then nothing will get appended to str. You can see this if you do something like this:
Code:
switch(crd.suit) {
case 1:
strcat(str, "spades");
break;
case 2:
strcat(str, "clubs");
break;
case 3:
strcat(str, "hearts");
break;
case 4:
strcat(str, "diamonds");
break;
default:
strcat(str, "*error*");
}
}
You'd probably get
Code:
The 10th card on the deck is: 4 of *error*
when you ran this modified code.
So, for the 10th card, the suit is not 1, 2, 3, or 4. If you look closely, you'll see that there are already four cards, on of each suit, that have the value 4. So, the 4 in the 10th card is messed up as well. The whole card is probably invalid.
Careful examination shows that there are only three Aces, and so the tenth card should be the Ace of Spades. This is the very first card since spades are represented as 1 in your program.
But wait . . . there are only three Jacks as well. So it's not just the first card that's not being initialized properly.
So there's probably something wrong with this code:
Code:
int i,k,index;
for(i=0;i<4;i++) {
for(k=0;k<13;k++) {
index = (i*13) + k;
deck[index].card = k+1;
deck[index].suit = i+1;
deck[index].value = value(k+1);
}
}
And, you guessed it, I think it's the red line. Multiplying i by 13 doesn't make sense, because i represents the suit and goes from 0 to 3. If you multiplied by 4 instead, it might work.
Anyway, I hope this helps in your debugging. :)