I am new to learning about C, so as an exercise I attempted to create a text-based card game where each person/CPU places down a random card at the top of their deck, and the person with the higher card will take the card and add it to their deck. During a tie, both cards would be discarded. When a person reaches zero cards, in this game they will lose. It is basically a simplified version of the war card game.
I came across a problem soon after I started developing. Here is the code I had until I ran across my error:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
enum suit {
heart = 0,
spade = 1,
club = 2,
diamond = 3
};
struct card {
enum suit current_card_suit;
int card_num;
int is_red_card;
};
int cardDeckSize = 0;
int numOpponentCardsLeft;
int numPlayerCardsLeft;
void deal_cards_to_player (struct card * pPlayerCards, int cardDeckSize);
void deal_cards_to_cpu (struct card * pCpuCards, int cardDeckSize);
int get_random_number_above_zero(int max);
void waitFor(unsigned int seconds);
void flush_input() {
int ch;
while ((ch = getchar()) != '\n' && ch != EOF)
continue;
}
int main (int argc, char **argv) {
//Get Random number sequence for later
srand(time(NULL));
int random_suit;
char userInput[10];
//Get deck size for war game
int invalidSize = 1;
while (invalidSize == 1) {
printf("\nEnter the amount of cards you would like to be dealed to each player for the war game (between 0 and 100)\n");
fgets(userInput, 9, stdin);
if(sscanf(userInput, "%i", &cardDeckSize) != 1) {
printf("\nPlease enter an integer. You will have to enter the number twice because you did not follow directions.\n");
flush_input();
continue;
}
if(cardDeckSize > 0 && cardDeckSize < 101) {
invalidSize = 0;
break;
} else {
printf("\nPlease type a number between 0 and 100. You will have to enter the number twice because you did not follow directions.\n");
flush_input();
continue;
}
}
printf("\nPress enter once more to proceed...");
flush_input();
//Create arrays to hold the cards of the player and CPU
struct card *pPlayerCards;
struct card *pCpuCards;
pPlayerCards = (struct card *) malloc (cardDeckSize * sizeof(struct card));
pCpuCards = (struct card *) malloc (cardDeckSize * sizeof(struct card));
//You will see duplication in the two functions called below, but I could not figure out how to remove it; it works for now
deal_cards_to_player(pPlayerCards, cardDeckSize);
deal_cards_to_cpu(pCpuCards, cardDeckSize);
printf("\nWow! My hand is SO tired of dealing all those cards. Let's play!\n\n");
struct card **pCurrentSideCards;
//"parse" the properties of each card...and right after show the game round results to the player
/* I will have to change "i < cardDeckSize" eventually becuase the size of the deck will not necessarily determine
the # of rounds; I'll get through one round first before worrying about this */
for(int i = 0; i < cardDeckSize; i++) { //Loop through each round
//Countdown for the player
printf("3, ");
waitFor(1);
printf("2, ");
waitFor(1);
printf("1, ");
waitFor(1);
printf("GO, GO, GO!!!");
waitFor(1);
char playerCardSuit[6];
char cpuCardSuit[6];
char playerStringCardNum[5];
char cpuStringCardNum[5];
char playerCardColor[5];
char cpuCardColor[5];
//Determine card terms set for each player
for (int i = 0; i < 2; i++) {
/* To eliminate duplication, based on the current iteration I tried to
create a pointer that points to a pointer to the array of card structures
for each player */
if(i == 0) {
//Start with "parsing" the player cards, then the CPU cards
pCurrentSideCards = &pPlayerCards;
} else {
pCurrentSideCards = &pCpuCards;
}
char cardSuit[6];
//EXPLANATION:
/* Accessing the card properties: I first tried to dereference the pointer
to find a pointer to the card deck structure array. Then I accessed
the correct card member in the array with "+i". I finally tried
to access the value with the "->" symbol, which dereferences again
before accessing the member
*/
switch ( ((*pCurrentSideCards)+i)->current_card_suit) {
case heart:
strcpy(cardSuit, "hearts");
break;
case spade:
strcpy(cardSuit, "spades");
break;
case club:
strcpy(cardSuit, "clubs");
break;
case diamond:
strcpy(cardSuit, "diamonds");
break;
default:
printf("\nThere was a fatal error determining the card suit of some dealt out cards.\n");
}
if(i == 0) { //If i = 0 we are working with the player cards, otherwise we are working with the CPU cards
strcpy(playerCardSuit, cardSuit);
} else {
strcpy(cpuCardSuit, cardSuit);
}
char stringCardNum[5];
switch ( ((*pCurrentSideCards)+i) ->card_num) {
case 1:
strcpy(stringCardNum, "ace");
break;
case 11:
strcpy(stringCardNum, "jack");
break;
case 12:
strcpy(stringCardNum, "queen");
break;
case 13:
strcpy(stringCardNum, "king");
break;
default: {
int cardAsNumber = ((*pCurrentSideCards)+i) -> card_num;
char cardAsString[5];
sprintf(cardAsString, "%i", cardAsNumber);
strcpy(stringCardNum,cardAsString);
}
}
if(i == 0) {
strcpy(playerStringCardNum, stringCardNum);
} else {
strcpy(cpuStringCardNum, stringCardNum);
}
char cardColor[5];
switch ( ((*pCurrentSideCards)+i )->is_red_card) {
case 0:
strcpy(cardColor, "black");
break;
case 1:
strcpy(cardColor, "red");
}
if(i == 0) {
strcpy(playerCardColor, cardColor);
} else {
strcpy(cpuCardColor, cardColor);
}
}
//The error comes right here before printing out the results somewhere
printf(" RESULTS!!! (DUN DUN, DUN)");
printf("\n Card Color Card Number Card Suit");
printf("\n YOU: A %s %s of %s ", playerCardColor, playerStringCardNum, playerCardSuit);
printf("\n CPU: A %s %s of %s " , cpuCardColor, cpuStringCardNum, cpuCardSuit);
}
free(pPlayerCards);
return 0;
}
void deal_cards_to_cpu(struct card *pCpuCards, int cardsToDeal) {
printf("\nPlease Wait...We are giving your opponent a good hand\n");
waitFor(1);
int numbersDealedSinceResponse = 0;
float randNumsNeeded = 3 * cardsToDeal; //3 values to assign per card in the deck
float totalNumsDealed = 0.0;
for (int i = 0; i < cardsToDeal; i++) {
//Get suit for card
int suitNum = get_random_number_above_zero(4);
//Get card number
int cardNum = get_random_number_above_zero(13);
//Tell if the card is red
int isRed = (get_random_number_above_zero(2)) - 1;
//Find the current card and assign the correct values
(pCpuCards+i)->current_card_suit = suitNum;
(pCpuCards+i)->card_num = cardNum;
(pCpuCards+i)->is_red_card = isRed;
printf("\n\nFor debugging purposes only, here are the CPU cards generated");
printf("\nSuit Num: %i", suitNum);
printf("\nCardNum: %i", cardNum);
printf("\nIs it red: %i\n", isRed);
if(numbersDealedSinceResponse > 6) {
//delay and then change seed; see comment in deal_cards_to_player() for explanation
waitFor(1);
srand(time(NULL));
}
if(numbersDealedSinceResponse == 12) {
//After 12 cards, give the user feedback on our progress by % done
float percent_done = 100 * (totalNumsDealed / randNumsNeeded);;
int percent_rounded = (int) percent_done;
printf("Please Wait...We are giving your opponent a good hand (%i%%)\n", percent_rounded);
numbersDealedSinceResponse = 0;
totalNumsDealed += 12;
}
numbersDealedSinceResponse+=3;
}
}
void deal_cards_to_player(struct card *pPlayerCard, int cardsToDeal) {
printf("\nPlease Wait...We are dealing out your cards\n");
waitFor(1);
int numbersDealedSinceResponse = 0;
float randNumsNeeded = 3 * cardsToDeal; //You need three random values shown in the struct for each card
float totalNumsDealed = 0.0;
for (int i = 0; i < cardsToDeal; i++) {
//Get suit for card
int suitNum = get_random_number_above_zero(4);
//Get card number
int cardNum = get_random_number_above_zero(13);
//Tell if the card is red
int isRed = (get_random_number_above_zero(2)) - 1;
//Assign the values after getting the current card in the array
(pPlayerCard+i)->current_card_suit = suitNum;
(pPlayerCard+i)->card_num = cardNum;
(pPlayerCard+i)->is_red_card = isRed;
printf("\n\nFor debugging purposes only, here are the player cards generated:");
printf("\nSuit Num: %i", suitNum);
printf("\nCardNum: %i", cardNum);
printf("\nIs it red: %i", isRed);
if(numbersDealedSinceResponse > 6) {
/* In order for the random numbers to stay unique and not be
generated in a pattern, wait for one second and then call srand(time(NULL));
to change the seed again based on time every 6 numbers */
waitFor(1);
srand(time(NULL));
}
if(numbersDealedSinceResponse == 12) {
//Every 12 numbers provide a response showing the percent completed
float percent_done = 100 * (totalNumsDealed / randNumsNeeded);;
int percent_rounded = (int) percent_done;
printf("Please Wait...We are dealing out your cards (%i%%)\n", percent_rounded);
numbersDealedSinceResponse = 0;
totalNumsDealed += 12;
}
numbersDealedSinceResponse+=3;
}
}
int get_random_number_above_zero(int max_num) {
int randomNumber;
randomNumber = (rand() % max_num) + 1;
return randomNumber;
}
void waitFor(unsigned int seconds) {
unsigned int stopTime = time(0) + seconds;
while (time(0) < stopTime); //it will wait until the calculated stop time
}
(I apologize for the code dump; I wanted to make sure it was possible to reproduce the problem)
The Problem in the Code
I have tested the code that assigns random card properties to each deck of cards, and have found the values assigned to be ok. I attempted to avoid creating duplicate code by basically looping two times when determining the card strings, and I tried to create a pointer to the pointer to the structure of cards. Here is a test run example of what happens:
Enter the amount of cards you would like to be sealed to each player for the war game (between 0 and 100) 50 Press enter once more to proceed... Please Wait...We are dealing out your cards For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 3 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 12 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 11 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 11 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 1 CardNum: 13 Is it red: 1 Please Wait...We are dealing out your cards (0%) For debugging purposes only, here are the player cards generated: Suit Num: 4 CardNum: 4 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 13 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 2 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 8 Is it red: 1 Please Wait...We are dealing out your cards (8%) For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 12 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 12 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 1 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 1 CardNum: 3 Is it red: 0 Please Wait...We are dealing out your cards (16%) For debugging purposes only, here are the player cards generated: Suit Num: 4 CardNum: 7 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 4 CardNum: 8 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 10 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 1 Is it red: 0 Please Wait...We are dealing out your cards (24%) For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 5 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 4 CardNum: 4 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 9 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 1 CardNum: 9 Is it red: 1 Please Wait...We are dealing out your cards (32%) For debugging purposes only, here are the player cards generated: Suit Num: 4 CardNum: 13 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 1 CardNum: 13 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 5 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 4 Is it red: 1 Please Wait...We are dealing out your cards (40%) For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 8 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 9 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 1 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 1 CardNum: 12 Is it red: 0 Please Wait...We are dealing out your cards (48%) For debugging purposes only, here are the player cards generated: Suit Num: 4 CardNum: 3 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 5 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 13 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 10 Is it red: 0 Please Wait...We are dealing out your cards (56%) For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 1 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 4 CardNum: 1 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 9 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 1 CardNum: 5 Is it red: 1 Please Wait...We are dealing out your cards (64%) For debugging purposes only, here are the player cards generated: Suit Num: 4 CardNum: 9 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 1 CardNum: 10 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 5 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 13 Is it red: 1 Please Wait...We are dealing out your cards (72%) For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 4 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 6 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 4 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 1 CardNum: 8 Is it red: 0 Please Wait...We are dealing out your cards (80%) For debugging purposes only, here are the player cards generated: Suit Num: 4 CardNum: 12 Is it red: 0 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 2 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 13 Is it red: 1 For debugging purposes only, here are the player cards generated: Suit Num: 3 CardNum: 6 Is it red: 0 Please Wait...We are dealing out your cards (88%) For debugging purposes only, here are the player cards generated: Suit Num: 2 CardNum: 10 Is it red: 1 Please Wait...We are giving your opponent a good hand For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 11 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 3 CardNum: 9 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 1 CardNum: 12 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 13 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 5 Is it red: 1 Please Wait...We are giving your opponent a good hand (0%) For debugging purposes only, here are the CPU cards generated Suit Num: 3 CardNum: 9 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 3 CardNum: 13 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 11 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 2 CardNum: 13 Is it red: 0 Please Wait...We are giving your opponent a good hand (8%) For debugging purposes only, here are the CPU cards generated Suit Num: 1 CardNum: 4 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 9 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 10 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 11 Is it red: 0 Please Wait...We are giving your opponent a good hand (16%) For debugging purposes only, here are the CPU cards generated Suit Num: 3 CardNum: 2 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 1 CardNum: 5 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 6 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 2 CardNum: 6 Is it red: 0 Please Wait...We are giving your opponent a good hand (24%) For debugging purposes only, here are the CPU cards generated Suit Num: 1 CardNum: 10 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 2 CardNum: 1 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 5 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 1 Is it red: 1 Please Wait...We are giving your opponent a good hand (32%) For debugging purposes only, here are the CPU cards generated Suit Num: 3 CardNum: 5 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 3 CardNum: 10 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 1 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 2 CardNum: 9 Is it red: 1 Please Wait...We are giving your opponent a good hand (40%) For debugging purposes only, here are the CPU cards generated Suit Num: 1 CardNum: 13 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 6 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 10 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 7 Is it red: 0 Please Wait...We are giving your opponent a good hand (48%) For debugging purposes only, here are the CPU cards generated Suit Num: 3 CardNum: 11 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 1 CardNum: 2 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 9 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 2 CardNum: 2 Is it red: 0 Please Wait...We are giving your opponent a good hand (56%) For debugging purposes only, here are the CPU cards generated Suit Num: 1 CardNum: 6 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 2 CardNum: 11 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 5 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 10 Is it red: 1 Please Wait...We are giving your opponent a good hand (64%) For debugging purposes only, here are the CPU cards generated Suit Num: 3 CardNum: 1 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 3 CardNum: 7 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 1 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 2 CardNum: 5 Is it red: 1 Please Wait...We are giving your opponent a good hand (72%) For debugging purposes only, here are the CPU cards generated Suit Num: 1 CardNum: 12 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 3 CardNum: 3 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 13 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 3 Is it red: 0 Please Wait...We are giving your opponent a good hand (80%) For debugging purposes only, here are the CPU cards generated Suit Num: 3 CardNum: 7 Is it red: 0 For debugging purposes only, here are the CPU cards generated Suit Num: 4 CardNum: 12 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 1 CardNum: 9 Is it red: 1 For debugging purposes only, here are the CPU cards generated Suit Num: 2 CardNum: 11 Is it red: 0 Please Wait...We are giving your opponent a good hand (88%) For debugging purposes only, here are the CPU cards generated Suit Num: 1 CardNum: 2 Is it red: 1 Wow! My hand is SO tired of dealing all those cards. Let's play! 3, 2, 1, GO, GO, GO!!!
[PROGRAM STOPS UNEXPECTEDLY BEFORE PRINTING OUT RESULTS]
Abort trap: 6
Leading me to believe I am accessing memory I do not own when I tried to convert the randomly generated numbers into words. I think my problem happens when I try to create my pointer that points to the pointer to the array of the card structure and decide here:
struct card **pCurrentSideCards
And when I decide based on the for loop iteration:
if(i == 0) {
pCurrentSideCards = &pPlayerCards;
} else {
pCurrentSideCards = &pCpuCards;
}
Finally, when I try to access the values with the switch statements
switch ( ((*pCurrentSideCards)+i)->current_card_suit)
Is there a problem with this approach to try to remove the duplicate code? Is this where the error in my code is?