You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
            }
        }
    }
}

问题根源

  1. 牌库耗尽后的无终止逻辑:当牌库所有卡牌被抽完(deckIndex >= NUM_CARDS),若玩家/电脑无牌可出,draw_card不执行任何操作,回合直接切换,双方陷入"无牌可出→抽不到牌→切换回合"的死循环。
  2. 抽牌后未检查新卡牌可用性:原代码中抽牌后直接结束回合,未判断新抽到的牌是否可立即打出,导致不必要的回合切换,加剧循环风险。
  3. 结束条件覆盖不全:仅通过手牌为空(胜利)或手牌满(失败)判断游戏结束,未处理牌库耗尽且双方均无有效牌可出的场景。

修复后的完整代码

#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);
        }
    }
}

修复说明

  1. 新增hasPlayableCard函数:统一检查玩家是否有可出牌,避免重复逻辑。
  2. 完善抽牌后逻辑:抽牌后立即检查新牌是否可出,若可出则直接打出,减少无效回合。
  3. 增加牌库耗尽终止条件:
    • 当牌库空且当前玩家无牌可出时,直接判定该玩家失败。
    • 每次回合后检查牌库是否为空且双方均无牌可出,此时根据手牌数量判定胜负或平局。
  4. 简化start_game循环:移除冗余的胜利/失败检查,因为take_turn内已通过exit(0)处理结束逻辑。

内容的提问来源于stack exchange,提问作者user25529716

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 11:11:08