C语言实现单牌游戏偶发无限循环问题求助
单牌(One-Card)游戏无限循环问题定位与修复
问题描述
用C语言实现的单牌游戏,运行约5次中至少2次会陷入无限循环,除偶发循环外功能符合预期,原实现代码如下:
#include <stdio.h> #include <stdlib.h> #include <time.h> #define NUM_CARDS 52 #define NUM_SUITS 4 #define NUM_RANKS 13 #define INITIAL_HAND_SIZE 5 #define MAX_HAND_SIZE 16 typedef struct { char *suit; char *rank; int value; } Card; typedef struct { Card hand[MAX_HAND_SIZE]; int handSize; } Player; const char *suits[] = {"Spades", "Hearts", "Clubs", "Diamonds"}; const char *ranks[] = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"}; Card deck[NUM_CARDS]; Player player, computer; Card openCard; int deckIndex = 0; int drawCount = 0; // Function prototypes void initialize_deck(); void shuffle_deck(); void deal_cards(); void print_hand(Player *p); void take_turn(Player *current, Player *opponent, int *turn); void play_card(Player *p, int cardIndex); void draw_card(Player *p, int count); int check_victory(Player *p); int check_defeat(Player *p); void apply_attack_card(Player *attacker, Player *defender, int attackValue); void start_game(); int canPlayCard(Card c); int isAttackCard(Card c); int main() { srand(time(NULL)); start_game(); return 0; } void initialize_deck() { int k = 0; for (int i = 0; i < NUM_SUITS; i++) { for (int j = 0; j < NUM_RANKS; j++) { deck[k].suit = (char *)suits[i]; deck[k].rank = (char *)ranks[j]; deck[k].value = j + 2; k++; } } shuffle_deck(); } void shuffle_deck() { for (int i = 0; i < NUM_CARDS; i++) { int r = i + rand() / (RAND_MAX / (NUM_CARDS - i) + 1); Card temp = deck[i]; deck[i] = deck[r]; deck[r] = temp; } } void deal_cards() { player.handSize = INITIAL_HAND_SIZE; computer.handSize = INITIAL_HAND_SIZE; for (int i = 0; i < INITIAL_HAND_SIZE; i++) { player.hand[i] = deck[deckIndex++]; computer.hand[i] = deck[deckIndex++]; } openCard = deck[deckIndex++]; } void print_hand(Player *p) { for (int i = 0; i < p->handSize; i++) { printf("%s of %s\n", p->hand[i].rank, p->hand[i].suit); } } void take_turn(Player *current, Player *opponent, int *turn) { printf("%s's turn!\n", current == &player ? "Player" : "Computer"); printf("Open card: %s of %s\n", openCard.rank, openCard.suit); print_hand(current); int played = 0; for (int i = 0; i < current->handSize; i++) { if (canPlayCard(current->hand[i])) { play_card(current, i); played = 1; if (isAttackCard(openCard)) { apply_attack_card(current, opponent, openCard.value == 2 ? 2 : 4); } break; } } if (!played) { printf("No playable card. Drawing a card...\n"); draw_card(current, 1); } if (check_defeat(current)) { printf("%s loses!\n", current == &player ? "Player" : "Computer"); exit(0); } else if (check_victory(current)) { printf("%s wins!\n", current == &player ? "Player" : "Computer"); exit(0); } *turn = 1 - *turn; } void play_card(Player *p, int cardIndex) { openCard = p->hand[cardIndex]; p->handSize--; for (int i = cardIndex; i < p->handSize; i++) { p->hand[i] = p->hand[i + 1]; } printf("%s played: %s of %s\n", p == &player ? "Player" : "Computer", openCard.rank, openCard.suit); } void draw_card(Player *p, int count) { for (int i = 0; i < count; i++) { if (deckIndex < NUM_CARDS) { p->hand[p->handSize++] = deck[deckIndex++]; } } } int check_victory(Player *p) { return p->handSize == 0; } int check_defeat(Player *p) { return p->handSize >= MAX_HAND_SIZE; } int canPlayCard(Card c) { return c.value == openCard.value || c.suit == openCard.suit; } int isAttackCard(Card c) { return c.value == 2 || c.value == 14; } void apply_attack_card(Player *attacker, Player *defender, int attackValue) { int hasDefense = 0; for (int i = 0; i < defender->handSize; i++) { if (defender->hand[i].value == 2 || defender->hand[i].value == 14) { hasDefense = 1; break; } } if (!hasDefense) { printf("%s draws %d cards.\n", defender == &player ? "Player" : "Computer", attackValue); draw_card(defender, attackValue); } else { printf("%s has an attack card to defend.\n", defender == &player ? "Player" : "Computer"); } } void start_game() { initialize_deck(); deal_cards(); int turn = 0; while (1) { if (turn == 0) { take_turn(&player, &computer, &turn); if (check_victory(&player) || check_defeat(&computer)) { break; } } else { take_turn(&computer, &player, &turn); if (check_victory(&computer) || check_defeat(&player)) { break; } } } }
问题根源
- 牌库耗尽后的无终止逻辑:当牌库所有卡牌被抽完(
deckIndex >= NUM_CARDS),若玩家/电脑无牌可出,draw_card不执行任何操作,回合直接切换,双方陷入"无牌可出→抽不到牌→切换回合"的死循环。 - 抽牌后未检查新卡牌可用性:原代码中抽牌后直接结束回合,未判断新抽到的牌是否可立即打出,导致不必要的回合切换,加剧循环风险。
- 结束条件覆盖不全:仅通过手牌为空(胜利)或手牌满(失败)判断游戏结束,未处理牌库耗尽且双方均无有效牌可出的场景。
修复后的完整代码
#include <stdio.h> #include <stdlib.h> #include <time.h> #define NUM_CARDS 52 #define NUM_SUITS 4 #define NUM_RANKS 13 #define INITIAL_HAND_SIZE 5 #define MAX_HAND_SIZE 16 typedef struct { char *suit; char *rank; int value; } Card; typedef struct { Card hand[MAX_HAND_SIZE]; int handSize; } Player; const char *suits[] = {"Spades", "Hearts", "Clubs", "Diamonds"}; const char *ranks[] = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"}; Card deck[NUM_CARDS]; Player player, computer; Card openCard; int deckIndex = 0; int drawCount = 0; // Function prototypes void initialize_deck(); void shuffle_deck(); void deal_cards(); void print_hand(Player *p); void take_turn(Player *current, Player *opponent, int *turn); void play_card(Player *p, int cardIndex); void draw_card(Player *p, int count); int check_victory(Player *p); int check_defeat(Player *p); void apply_attack_card(Player *attacker, Player *defender, int attackValue); void start_game(); int canPlayCard(Card c); int isAttackCard(Card c); int hasPlayableCard(Player *p); int main() { srand(time(NULL)); start_game(); return 0; } void initialize_deck() { int k = 0; for (int i = 0; i < NUM_SUITS; i++) { for (int j = 0; j < NUM_RANKS; j++) { deck[k].suit = (char *)suits[i]; deck[k].rank = (char *)ranks[j]; deck[k].value = j + 2; k++; } } shuffle_deck(); } void shuffle_deck() { for (int i = 0; i < NUM_CARDS; i++) { int r = i + rand() / (RAND_MAX / (NUM_CARDS - i) + 1); Card temp = deck[i]; deck[i] = deck[r]; deck[r] = temp; } } void deal_cards() { player.handSize = INITIAL_HAND_SIZE; computer.handSize = INITIAL_HAND_SIZE; for (int i = 0; i < INITIAL_HAND_SIZE; i++) { player.hand[i] = deck[deckIndex++]; computer.hand[i] = deck[deckIndex++]; } openCard = deck[deckIndex++]; } void print_hand(Player *p) { for (int i = 0; i < p->handSize; i++) { printf("%s of %s\n", p->hand[i].rank, p->hand[i].suit); } } // 检查玩家是否有可出牌 int hasPlayableCard(Player *p) { for (int i = 0; i < p->handSize; i++) { if (canPlayCard(p->hand[i])) { return 1; } } return 0; } void take_turn(Player *current, Player *opponent, int *turn) { printf("%s's turn!\n", current == &player ? "Player" : "Computer"); printf("Open card: %s of %s\n", openCard.rank, openCard.suit); print_hand(current); int played = 0; // 优先出可玩的牌 if (hasPlayableCard(current)) { for (int i = 0; i < current->handSize; i++) { if (canPlayCard(current->hand[i])) { play_card(current, i); played = 1; if (isAttackCard(openCard)) { apply_attack_card(current, opponent, openCard.value == 2 ? 2 : 4); } break; } } } else { // 无牌可出时尝试抽牌 if (deckIndex < NUM_CARDS) { printf("No playable card. Drawing a card...\n"); draw_card(current, 1); // 抽牌后检查是否可以出牌 if (canPlayCard(current->hand[current->handSize - 1])) { printf("Drew a playable card! Playing it...\n"); play_card(current, current->handSize - 1); played = 1; if (isAttackCard(openCard)) { apply_attack_card(current, opponent, openCard.value == 2 ? 2 : 4); } } } else { // 牌库已空且无牌可出,直接判定失败 printf("No playable cards and deck is empty. %s loses!\n", current == &player ? "Player" : "Computer"); exit(0); } } if (check_defeat(current)) { printf("%s loses!\n", current == &player ? "Player" : "Computer"); exit(0); } else if (check_victory(current)) { printf("%s wins!\n", current == &player ? "Player" : "Computer"); exit(0); } // 额外检查:牌库空且双方都无牌可出,判定手牌少的获胜 if (deckIndex >= NUM_CARDS && !hasPlayableCard(&player) && !hasPlayableCard(&computer)) { if (player.handSize < computer.handSize) { printf("Deck empty, Player has fewer cards. Player wins!\n"); } else if (computer.handSize < player.handSize) { printf("Deck empty, Computer has fewer cards. Computer wins!\n"); } else { printf("Deck empty, both have same number of cards. Tie!\n"); } exit(0); } *turn = 1 - *turn; } void play_card(Player *p, int cardIndex) { openCard = p->hand[cardIndex]; p->handSize--; for (int i = cardIndex; i < p->handSize; i++) { p->hand[i] = p->hand[i + 1]; } printf("%s played: %s of %s\n", p == &player ? "Player" : "Computer", openCard.rank, openCard.suit); } void draw_card(Player *p, int count) { for (int i = 0; i < count; i++) { if (deckIndex < NUM_CARDS) { p->hand[p->handSize++] = deck[deckIndex++]; } } } int check_victory(Player *p) { return p->handSize == 0; } int check_defeat(Player *p) { return p->handSize >= MAX_HAND_SIZE; } int canPlayCard(Card c) { return c.value == openCard.value || c.suit == openCard.suit; } int isAttackCard(Card c) { return c.value == 2 || c.value == 14; } void apply_attack_card(Player *attacker, Player *defender, int attackValue) { int hasDefense = 0; for (int i = 0; i < defender->handSize; i++) { if (defender->hand[i].value == 2 || defender->hand[i].value == 14) { hasDefense = 1; break; } } if (!hasDefense) { printf("%s draws %d cards.\n", defender == &player ? "Player" : "Computer", attackValue); draw_card(defender, attackValue); } else { printf("%s has an attack card to defend.\n", defender == &player ? "Player" : "Computer"); } } void start_game() { initialize_deck(); deal_cards(); int turn = 0; while (1) { if (turn == 0) { take_turn(&player, &computer, &turn); } else { take_turn(&computer, &player, &turn); } } }
修复说明
- 新增
hasPlayableCard函数:统一检查玩家是否有可出牌,避免重复逻辑。 - 完善抽牌后逻辑:抽牌后立即检查新牌是否可出,若可出则直接打出,减少无效回合。
- 增加牌库耗尽终止条件:
- 当牌库空且当前玩家无牌可出时,直接判定该玩家失败。
- 每次回合后检查牌库是否为空且双方均无牌可出,此时根据手牌数量判定胜负或平局。
- 简化
start_game循环:移除冗余的胜利/失败检查,因为take_turn内已通过exit(0)处理结束逻辑。
内容的提问来源于stack exchange,提问作者user25529716
相关产品推荐
相关产品推荐

