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

为何跳出函数会导致Oware Abapa游戏棋盘数组异常?

Oware Abapa游戏保存后棋盘意外修改问题

我正在复刻Oware Abapa棋盘游戏,遇到一个问题:saveFile函数能正常将棋盘状态保存到文件,但保存流程结束后,游戏的pit数组(棋盘)被意外修改,原本应该保持原有状态不变。保存后程序还会自动从活跃玩家的最后一个坑位落子,我怀疑和break语句有关,但不确定原因,求解决。


棋盘展示函数

void show(int player, t_Pit p[], t_Pit s[], int win) {
    int a = player, o = !a; // 'a'ctive & 'o'pponent

    int x = a ? 0 : 6;
    int y = a ? 6 : 0;
    char *str = win ? "Wins the game\n\n" : "Select pit (1-6)\nSave board state (0): ";
    printf( "\n"
            "----+-----player%d-----+\n"
            "%2d  |%2d|%2d|%2d|%2d|%2d|%2d|\n"
            "----+-----------------+\n"
            "%2d  |%2d|%2d|%2d|%2d|%2d|%2d|\n"
            "----+-----player%d-----+\t"

            "\nPlayer %d %s",
            o,  s[o], p[y+5], p[y+4], p[y+3], p[y+2], p[y+1], p[y+0], // NB: custom indexing
            s[a], p[x+0], p[x+1], p[x+2], p[x+3], p[x+4], p[x+5], a,
            a, str
    );
}

saveFile函数

void saveFile(int player, t_Pit p[], t_Pit s[]) {
    char fName[26];
    printf("Input a name for the save file:");
    scanf("%s", &fName);
    FILE *save = fopen(fName, "w");
    if (save == NULL) {
        perror("Error opening file for writing");
        return;
    }

    int a = player, o = !a; // 'a'ctive & 'o'pponent

    int x = a ? 0 : 6;
    int y = a ? 6 : 0;

    fprintf(save,
            "%2d\n"
            "%2d %2d %2d %2d %2d %2d\n"
            "%2d\n"
            "%2d %2d %2d %2d %2d %2d\n",
            s[o], p[y+5], p[y+4], p[y+3], p[y+2], p[y+1], p[y+0],
            s[a], p[x+0], p[x+1], p[x+2], p[x+3], p[x+4], p[x+5]);

    fclose(save);
    printf("file '%s' saved.", fName);
}

main函数中相关逻辑

int main(void) {
    enum { nPlayer = 2, nOne = 6, nPit = (2*nOne), nSeed = 4, nWin = 25 }; // ease of code

    t_Pit score[ nPlayer ] = { 0 };
    t_Pit pit[ nPit ] = { 0 };
    int counter = 0;
    int counter1 = 0;
    int i;
    int actv = 0;

    for (int i = 0; i < nPit; ++i) {
        pit[i] = nSeed;
    }

    for ( ; score[0] < nWin && score[1] < nWin; actv = !actv) {
        int from, to;
        while (counter == 0) {
            if (counter1 == 1) {
                if (from == 0) { 
                    saveFile(actv, pit, score);
                    break;
                }
                if (scanf("%d", &from) != 1 || from < 1 || from > 6 || (pit[(from - 1) + fix] <= 1 && temp > 1) || (pit[(from - 1) + fix] == 0 && temp == 1) ) {
                    puts("Out of bounds, select pit (1-6): ");
                } else {
                    counter = 1; 
                }
            }
        }
        counter = 0; 
        counter1 = 0;
        from -= 1; // 0 indexing
        from += fix;
        to = from;

        int nSow = pit[ from ];
        pit[ from ] = 0;

        while (nSow) { //if more than 12 seeds, skip from pit
            if ((to = (to+1) % nPit) != from) {
                ++pit[ to ];
                --nSow;
            }
        }

        while (nSow--) //moving
            pit[ to = (to+1)%nPit ]++;

        // condition true only while in opponent's range of pits
        for ( ; to/nOne != from/nOne; to = (to+(nPit-1))%nPit) // modulo -1
            if (pit[ to ] == 2 || pit[ to ] == 3) {
                score[ actv ] += pit[ to ];
                pit[ to ] = 0;
            } else
                break;
}

问题根源分析

  1. break语句的副作用:输入0触发保存后,break仅跳出内层的while (counter == 0)循环,但外层的落子逻辑(从from -= 1开始的代码)仍会执行。此时from值为0,经过from -=1变成-1,加上fix后会形成非法索引,错误修改pit数组,进而触发自动落子逻辑。
  2. 状态未正确重置:保存操作后没有标记“无需执行落子流程”,程序默认继续执行播种、收割逻辑,导致棋盘被意外修改。

修复方案

  • 添加跳过标记:调用saveFile后设置skipTurn标记,让外层循环直接跳过落子流程,切换到下一轮玩家回合。
  • 修正输入处理顺序:先读取输入再判断是否为0,避免from未初始化的问题。
  • 修复saveFile的scanf错误:scanf("%s", &fName);应改为scanf("%s", fName);,因为fName本身是数组指针,无需取地址,否则可能引发内存越界。

修改后的main函数相关部分示例:

for ( ; score[0] < nWin && score[1] < nWin; actv = !actv) {
    int from, to;
    int skipTurn = 0; // 添加跳过标记
    while (counter == 0) {
        if (counter1 == 1) {
            if (scanf("%d", &from) != 1) {
                puts("Invalid input, try again: ");
                continue;
            }
            if (from == 0) { 
                saveFile(actv, pit, score);
                skipTurn = 1;
                break;
            }
            if (from < 1 || from > 6 || (pit[(from - 1) + fix] <= 1 && temp > 1) || (pit[(from - 1) + fix] == 0 && temp == 1) ) {
                puts("Out of bounds, select pit (1-6): ");
            } else {
                counter = 1; 
            }
        }
    }
    counter = 0; 
    counter1 = 0;
    
    if (skipTurn) {
        continue; // 跳过落子流程,直接切换玩家
    }

    from -= 1; // 0 indexing
    from += fix;
    to = from;

    // 剩余落子逻辑保持不变...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 20:35:04