我是学习 C 的新手,因此作为练习,我尝试创建一个基于文本的纸牌游戏,其中每个人/CPU 将一张随机卡片放在他们的牌组顶部,拥有较高卡片的人将拿走卡片并将其添加到他们的牌组中。在平局时,两张牌都将被丢弃。当一个人达到零牌时,在这个游戏中他们会输。它基本上是战争纸牌游戏的简化版。
我开始开发后不久就遇到了一个问题。这是我遇到错误之前的代码:
#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
}
(我为代码转储道歉;我想确保可以重现该问题)
代码中的问题
我已经测试了将随机卡片属性分配给每副卡片的代码,并发现分配的值是 ok。我试图通过在确定卡片字符串时基本上循环两次来避免创建重复代码,并且我试图创建一个指向卡片结构指针的指针。这是一个测试运行示例,说明会发生什么:
输入您希望封印给每位玩家的卡片数量 战争游戏(0到100之间) 50 再次按回车键继续... 请稍等...我们正在分发您的卡片 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡号:3 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:12 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡号:11 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:11 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:1 卡号:13 是否红色:1 请稍等...我们正在分发您的卡片 (0%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:4 卡号:4 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡号:13 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡号:2 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:8 是否红色:1 请稍等...我们正在分发您的卡片 (8%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡号:12 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:12 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:1 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:1 卡号:3 是否红色:0 请稍等...我们正在分发您的卡片 (16%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:4 卡号:7 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:4 卡号:8 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡数:10 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:1 是否红色:0 请稍等...我们正在分发您的卡片 (24%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡号:5 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:4 卡号:4 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:9 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:1 卡号:9 是否红色:1 请稍等...我们正在分发您的卡片 (32%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:4 卡号:13 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:1 卡号:13 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:5 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:4 是否红色:1 请稍等...我们正在分发您的卡片 (40%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡号:8 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡号:9 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:1 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:1 卡号:12 是否红色:0 请稍等...我们正在分发您的卡片 (48%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:4 卡号:3 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:5 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:13 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡数:10 是否红色:0 请稍等...我们正在分发您的卡片 (56%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡号:1 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:4 卡号:1 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:9 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:1 卡号:5 是否红色:1 请稍等...我们正在分发您的卡片 (64%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:4 卡号:9 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:1 卡数:10 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:5 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:13 是否红色:1 请稍等...我们正在分发您的卡片 (72%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡号:4 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡数:6 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:4 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:1 卡号:8 是否红色:0 请稍等...我们正在分发您的卡片 (80%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:4 卡号:12 是否红色:0 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:2 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡号:13 是否红色:1 仅出于调试目的,以下是生成的玩家卡片: 套装编号:3 卡数:6 是否红色:0 请稍等...我们正在分发您的卡片 (88%) 仅出于调试目的,以下是生成的玩家卡片: 套装编号:2 卡数:10 是否红色:1 请稍等...我们正在给你的对手一手好牌 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:11 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:3 卡号:9 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:1 卡号:12 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:13 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:5 是否红色:1 请稍等...我们正在给你的对手一手好牌 (0%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:3 卡号:9 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:3 卡号:13 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:11 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:2 卡号:13 是否红色:0 请稍等...我们正在给你的对手一手好牌 (8%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:1 卡号:4 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:9 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡数:10 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:11 是否红色:0 请稍等...我们正在给你的对手一手好牌 (16%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:3 卡号:2 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:1 卡号:5 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡数:6 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:2 卡数:6 是否红色:0 请稍等...我们正在给你的对手一手好牌 (24%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:1 卡数:10 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:2 卡号:1 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:5 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:1 是否红色:1 请稍等...我们正在给你的对手一手好牌 (32%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:3 卡号:5 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:3 卡数:10 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:1 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:2 卡号:9 是否红色:1 请稍等...我们正在给你的对手一手好牌 (40%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:1 卡号:13 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡数:6 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡数:10 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:7 是否红色:0 请稍等...我们正在给你的对手一手好牌 (48%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:3 卡号:11 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:1 卡号:2 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:9 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:2 卡号:2 是否红色:0 请稍等...我们正在给你的对手一手好牌 (56%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:1 卡数:6 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:2 卡号:11 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:5 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡数:10 是否红色:1 请稍等...我们正在给你的对手一手好牌 (64%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:3 卡号:1 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:3 卡号:7 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:1 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:2 卡号:5 是否红色:1 请稍等...我们正在给你的对手一手好牌 (72%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:1 卡号:12 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:3 卡号:3 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:13 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:3 是否红色:0 请稍等...我们正在给你的对手一手好牌 (80%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:3 卡号:7 是否红色:0 仅用于调试目的,这里是生成的 CPU 卡 套装编号:4 卡号:12 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:1 卡号:9 是否红色:1 仅用于调试目的,这里是生成的 CPU 卡 套装编号:2 卡号:11 是否红色:0 请稍等...我们正在给你的对手一手好牌 (88%) 仅用于调试目的,这里是生成的 CPU 卡 套装编号:1 卡号:2 是否红色:1 哇!我的手已经厌倦了处理所有这些牌。让我们玩! 3、2、1,去,去,去!!!
[PROGRAM STOPS UNEXPECTEDLY BEFORE PRINTING OUT RESULTS]
中止陷阱:6
当我尝试将随机生成的数字转换为单词时,让我相信我正在访问我不拥有的内存。我认为当我尝试创建指向卡片结构数组的指针并在此处决定的指针时会出现问题:
struct card **pCurrentSideCards
当我根据 for 循环迭代做出决定时:
if(i == 0) {
pCurrentSideCards = &pPlayerCards;
} else {
pCurrentSideCards = &pCpuCards;
}
最后,当我尝试使用 switch 语句访问值时
switch ( ((*pCurrentSideCards)+i)->current_card_suit)
这种尝试删除重复代码的方法是否存在问题?这是我代码中的错误所在吗?