三子棋中碰到的问题小结

42 阅读7分钟

三子棋的问题小结

在最近的编程中,我本人遇到了一些小问题,所以在此来对这些问题进行一次小结

1.最常见的是链接型错误

当我在写三子棋的时候,写到末尾的时候,总是容易将函数名的大小写弄错,一开始自己也没有注意到这些问题,当程序运行报错的时候才发现了函数写错了,要么是大小写写错,要么是写的过快导致函数名中间有几个反过来的,或是漏掉字母

就像这样:

屏幕截图 2024-10-25 161047.png

屏幕截图 2024-10-25 161102.png

或是这样:

屏幕截图 2024-10-25 161200.png

屏幕截图 2024-10-25 161214.png

还有就是这样子:

屏幕截图 2024-10-25 161242.png

屏幕截图 2024-10-25 161251.png

这一开始没注意到就导致一直卡住,后面通过查阅资料知道了:当下面报错类型是LNK时,那就属于链接型错误,而链接型错误无非是没有对函数声明,或是将函数名写错了

2.第二种就是运行时错误

这种错误令我感到十分的头疼,每看到电脑的棋下的位置总跟自己预想的不一样时,就得调试一次,而且可能这种错误不是一下就能找到

1.就像下面这串电脑运行逻辑的代码:

void Compuetrmove(char board[ROW][COL], int row, int col)
{
    int x = rand() % row;
    int y = rand() % col;
    int i = 0,j = 0;
    int a, b;
    while (1)
    {
        int count1 = 0;
        int count2 = 0;
        printf("Computermove:>\n");
        if (board[1][1] == ' ')
        {
            printf("2 2\n");
            board[1][1] = '*';
            goto end;
        }
        else
        {
            for (i = 0, j = 0, count1 = 0, count2 = 0; i < row, j < col; i++, j++)//判断斜下线
        {
            if (board[i][j] == '#')
            {
                count1++;
            }
            else if (board[i][j] == '*')
            {
                count2++;
            }
            else
            {
                //当board[i][j]==' '时,直接将i,j赋值给a,b,然后输出
                a = j;
                b = i;
            }
            if ((count1 == 2 || count2 == 2) && count1 + count2 != row)
            {
                //当count1==2时,说明玩家的子已经连成2个,此时拦截
                //当count2==2时,说明电脑的子已经连成2个,此时下这直接获胜
                //count1+count2!=3,保证此时斜下这条线有空位
                board[a][b] = '*';
                goto end;
            }
        }
        for (i = 0, j = col, count1 = 0, count2 = 0; i < row, j > 0; i++, j--)//判断斜上线
        {
            if (board[i][j] == '#')
            {
                count1++;
            }
            else if (board[i][j] == '*')
            {
                count2++;
            }
            else
            {
                //当board[i][j]==' '时,直接将i,j赋值给a,b,然后输出
                a = j;
                b = i;
            }
            if ((count1 == 2 || count2 == 2) && count1 + count2 != row)
            {
                //当count1==2时,说明玩家的子已经连成2个,此时拦截
                //当count2==2时,说明电脑的子已经连成2个,此时下这直接获胜
                //count1+count2!=3,保证此时斜上这条线有空位
                board[a][b] = '*';
                goto end;
            }
        }
        for (i = 0, count1 = 0, count2 = 0; i < row;i++)//判断列
        {
            for (j = 0; j < col; j++)
            {
                if (board[j][i] == '#')
                {
                    count1++;
                }
                else if (board[j][i] == '*')
                {
                    count2++;
                }
                else
                {
                    //当board[i][j]==' '时,直接将i,j赋值给a,b,然后输出
                    a = i;
                    b = j;
                }
                if ((count1 == 2 || count2 == 2) && count1 + count2 != row - 1)
                {
                    //当count1==2时,说明玩家的子已经连成2个,此时拦截
                    //当count2==2时,说明电脑的子已经连成2个,此时下这直接获胜
                    //count1+count2!=3,保证此时列这条线有空位
                    board[b][a] = '*';
                    goto end;
                }
            }
        }
        for (i = 0, count1 = 0, count2 = 0; i < row; i++)//判断行
        {
            for (j = 0; j < col; j++)
            {
                if (board[i][j] == '#')
                {
                    count1++;
                }
                else if (board[i][j] == '*')
                {
                    count2++;
                }
                else
                {
                    //当board[i][j]==' '时,直接将i,j赋值给a,b,然后输出
                    a = i;
                    b = j;
                }
                if ((count1 == 2 || count2 == 2) && count1 + count2 != row - 1)
                {
                    //当count1==2时,说明玩家的子已经连成2个,此时拦截
                    //当count2==2时,说明电脑的子已经连成2个,此时下这直接获胜
                    //count1+count2!=3,保证此时行这条线有空位
                    board[i][j] = '*';
                    goto end;
                }
            }
        }
            if (board[x][y] == ' ')
        {
            board[x][y] = '*';
        }
    end:
        break;
    }
}
​

这段代码中藏了好多个错误,下面一个一个讲

1.a,b的赋值错误

最开始的判断斜下和斜上的a,b的值被赋反了,这导致了有时电脑会重复落子,落到原本已经有子的位置上

屏幕截图 2024-10-25 162229.png

而正确的方法是a=i,b=j

2.初始化只进行了一次

在后面的判断行与列的代码中,count1count2的初始化只进行了一次,这也导致了重复落子的问题

屏幕截图 2024-10-25 162501.png

而正确的方法是将这个初始化放到下面,这样每判断一次行或列的时候,会重新计数,这样就可以达到原本的拦截或取胜目的

3.随机值只取一次

最开始,我们为x,y进行取随机值,这目的是为了当前面的方法都不适用的时候,可以进行随机落点,但若board[x][y] != ' ' ,这时候就应该重新取一次随机值,但经过测试,就算没有用到goto的语法,只要不对代码进行干涉,那么代码最终还是会自动运行goto后面的语句块,

屏幕截图 2024-10-25 163156.png

所以,这时候正确的做法是:

    if(board[x][y]==' ')
    {
        board[x][y]='*';
        end:
            break;
    }

这样就可以做到当我们的board[x][y]即使不是空的,也可以重新进行一次循环

2.下面这串代码也是

int main()
{
    int choice = 0;
    int choice2 = 0;
    srand((unsigned int)time(NULL));//创建随机值
    do
    {
        menu();//游戏菜单
        scanf("%d", &choice);
​
        switch (choice)
        {
                while(1)
                {
                    menudiff();
                    scanf("%d",&choice2);
                    if(choice2!=1||choice2!=2||choice2!=3)
                    {
                        printf("输入错误,请重新输入\n");
                    }
                    else
                    {
                        break;
                    }
                }
            game(choice2);   
            continue;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("输入错误,请重新输入\n");
        }
    } while (choice);
    return 0;
}

这上面这串代码存在一个巨大的问题,就是逻辑非:只要一个一个是真,那么就会执行该语句块,其不能代表既的意思

所以这段代码有严重的错误:

屏幕截图 2024-10-25 164947.png

所以这段代码应该这样改:

            do
            {
                menudiff();
                scanf("%d", &choice2);
                switch (choice2)
                {
                case 1:
                case 2:
                case 3:
                    goto out;
                default:
                    printf("无此选项,请重新选择\n");
                }
            } while (1);
            out:
                game(choice2);
            
            continue;

这样就能够达到我们的目的

3.还有这也是

char Iswin(char Board[ROW][COL], int row, int col)
{
    for (int i = 0, j = 0, count = 0; i < COL - 1, j < ROW - 1; i++, j++)//判断斜下
    {
        if (Board[i][j] == Board[i + 1][j + 1] && Board[i][j] != ' ')
        {
            count++;
        }
        if (count == 2)
        {
            if (Board[i][j] == '*')
                return '*';
            else
                return '#';
        }
    }
    for (int i = COL - 1, j = 0, count = 0; i >= 1, j < ROW - 1; i--, j++)//判断斜上
    {
        if (Board[i][j] == Board[i - 1][j + 1] && Board[i][j] != ' ')
        {
            count++;
        }
        if (count == 2)
        {
            if (Board[i][j] == '*')
                return '*';
            else
                return '#';
        }
    }
    for (int i = 0,count = 0; i < COL; i++)//判断行
    {
        for (int j = 0; j < ROW - 1; j++)
        {
            if (Board[i][j] == Board[i][j + 1]&&Board[i][j]!=' ')
            {
                count++;
            }
            if (count == 2)
            {
                if (Board[i][j] == '*')
                    return '*';
                else
                    return '#';
            }
        }
        for (int i = 0,count = 0; i < COL; i++)//判断列
        {
            for (int j = 0; j < ROW - 1; j++)
            {
                if (Board[j][i] == Board[j + 1][i]&&Board[j][i]!=' ')
                {
                    count++;
                }
                if (count == 2)
                {
                    if (Board[j][i] == '*')
                        return '*';
                    else
                        return '#';
                }
            }
        }
        int count1 = 0;
        for (int i = 0; i < COL; i++)//判断棋盘是否满了,满了则平局
        {
            for (int j = 0; j < ROW; j++)
            {
                if (Board[i][j] != ' ')
                {
                    count1++;
                }
                else
                    //当Board[i][j]==' '时,说明棋盘至少有一个空位,可以直接跳出循环
                    break;
                if (count1 == (ROW) * (COL))
                {
                    return 'p';
                }
            }
        }
        //当以上一次都没有完成return,说明游戏还没有结束,所以继续
        return 'c';
    }
}

这段代码中这段也存在初始化只进行一次的问题

屏幕截图 2024-10-25 165713.png

通过上面的代码,我们现在能很好的看到这里的count只初始化了一次

所以更正后应该是这样的:

    for (int i = 0; i < COL; i++)//判断行
    {
        int count=0;
        for (int j = 0; j < ROW - 1; j++)
        {
            if (Board[i][j] == Board[i][j + 1]&&Board[i][j]!=' ')
            {
                count++;
            }
            if (count == 2)
            {
                if (Board[i][j] == '*')
                    return '*';
                else
                    return '#';
            }
        }
        for (int i = 0; i < COL; i++)//判断列
        {
            int count=0;
            for (int j = 0; j < ROW - 1; j++)
            {
                if (Board[j][i] == Board[j + 1][i]&&Board[j][i]!=' ')
                {
                    count++;
                }
                if (count == 2)
                {
                    if (Board[j][i] == '*')
                        return '*';
                    else
                        return '#';
                }
            }
        }

总结

这些大概就是我在三子棋中遇见的一些错误,这些问题第一次碰见的时候真的花费了我大量时间去寻找,一直在慢慢的反复的调试,最终才把这些问题揪了出来