0


C语言一个小时实现简易三子棋,看看你能不能让愚蠢的电脑获胜,还不快上手试试吗(无AI算法)

今天利用空余时间,便写一写三子棋的实现,可能很多人第一次看到这些东西,心里就会有一点害怕,可能会说:天呐,这得多难啊,我真的能写吗?那么回答是肯定的:能写,并且还很简单。那就让我们来看一看吧!

我们首先要分析整个三子棋的需要,因为我们不是做人机对战(过段时间博主会发一篇五子棋人机对战的实现),主要实现人机对战需要考虑的情况比较多,比较复杂,在这里既然是新手向,大家有一个制作的思路就可以了。

三子棋下棋,首先我们需要做到能看到棋盘,之后是下棋,电脑会在我们之后。每一步我们都想屏幕能清空,同时保留一次棋盘。然后是判断输赢的问题。(如果书写对战AI,还需要考虑棋盘情况来书写对应的算法)。

为了实现功能,我们将其拆解成三部分:一个test.c,一个game.c和一个game.h。

其中test.c用来书写实现逻辑,game.c用来书写函数定义,game.h则用来声明。

第一部分:

测试文件test.c,这个文件我们需要一个目录指引,也就是玩还是不玩,这个很容易实现,之后是定制棋盘,我们在头文件里会声明这些东西(ROW和COL一类的量)。首先肯定是初始化一个棋盘,这个很简单,直接给棋盘赋值空格就好(InitBoard),之后我们需要让它变成我们想要的样子(也就是DisplayBoard),这需要一点点的功夫,比如我们期望如下:

那么我们就需要在书写定义的时候用点心了,不过我们现在看的是主函数,所以我们先关注主函数:

有了棋盘我们就还需要能下子,就需要玩家下子(PlayerMove)和电脑下子(ComputerMove),之后我们需要判断输赢平局的情况(IfWin),这时候就基本上思路齐全了。

那么我们的主函数的逻辑已经比较清晰了,就直接上代码吧:

#include"game.h"
void menu()
{
    printf("===========================\n");
    printf("=======   1.play    =======\n");
    printf("=======   0.exit    =======\n");
    printf("===========================\n");
}

void game()
{
    //存储数据-二维数组
    char board[ROW][COL];
    //初始化棋盘-初始化空格
    InitBoard(board, ROW, COL);
    //打印棋盘-本质打印数组的内容
    DisplayBoard(board, ROW, COL);
    char ret = 0;//用来获取游戏状态
    while (1)
    {
        //玩家走
        PlayerMove(board, ROW, COL);
        DisplayBoard(board, ROW, COL);
        //判断输赢
        ret = IfWin(board, ROW, COL);
        if (ret != 'C')
            break;
        //电脑走
        ComputerMove(board, ROW, COL);
        system("cls");
        DisplayBoard(board, ROW, COL);
        //判断输赢
        ret = IfWin(board, ROW, COL);
        if (ret != 'C')
            break;
    }
    if (ret == '*')
    {
        printf("玩家胜利\n");
    }
    else if (ret == '#')
    {
        printf("电脑胜利\n");
    }
    else
    {
        printf("平局\n");
    }
    DisplayBoard(board, ROW, COL);
}

int main()
{
    int input = 0;
    srand((unsigned int)time(NULL));
    do
    {
        menu();
        printf("请选择:\n");
        scanf_s("%d", &input);
        system("cls");
        switch (input)
        {
        case 1:
            printf("三子棋游戏:\n");
            game();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,请重新选择:\n");
            break;
        }
    } while (input != 0);
}

在这里需要注意IfWin返回值的情况,这个是需要提前考虑的,由于玩家使用 “ * ”我们就认为返回了“ * ”就是玩家胜利,返回“ # ”就是电脑胜利,返回“ C ”(continue)就继续,返回 “ Q "就是平局

这样会使代码更加简单,至于为什么,我们马上就能知道。

第二步,书写头文件,主函数已经有了需求,接下来在头文件里书写一下声明就OK了:

#pragma once
//头文件的包含
#include<time.h>
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
//符号的定义
#define ROW 3
#define COL 3
//函数的声明
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘的函数
void DisplayBoard(char board[ROW][COL], int row, int col);
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);

//判断输赢
//1.玩家赢 '*'  2.电脑赢  '#' 3.平局  'Q' 4.游戏继续  'C'
char IfWin(char board[ROW][COL], int row, int col);

第三步就是很关键的一步了,这一步决定了程序能否正常运行,因为它书写的是我们的函数定义,

我们已经知道,我们需要初始化一个棋盘(InitBoard),展示棋盘情况(DisplayBoard)玩家下子(PlayerMove)和电脑下子(ComputerMove),之后我们需要判断输赢平局的情况(IfWin)

那么就分开看看这些函数模块(不要忘记包含头文件#include"game.h"):

1、InitBoard

void InitBoard(char board[ROW][COL], int row, int col)
{
    int i = 0, j = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            board[i][j] = ' ';
        }
    }
}

这个就是妥妥的二维数组赋值,全赋值空格,没什么好说的。

2、DisplayBoard

void DisplayBoard(char board[ROW][COL], int row, int col)
{
    int i = 0, j = 0;
    for (i = 0; i < row; i++)
    {
        //打印每一行的数据 空格+数据+空格+竖杠作为一个大元素
        for (j = 0; j < col; j++)
        {
            printf(" %c ", board[i][j]);
            if (j < col - 1)
                printf("|");
        }
        printf("\n");
        //打印每一行的分割线
        if (i < row - 1)
        {
            int j = 0;
            for (j = 0; j < col; j++)
            {
                printf("---");
                if (j < col - 1)
                    printf("|");
            }
            printf("\n");
        }
    }
}

因为我们有所期望,就是上图的那个棋盘,所以需要思考怎么制作棋盘,也很简单,打印每一行的数据,用 空格+数据+空格+竖杠 作为一个大元素 ,同时最后一个大元素没有竖杠。用代码解释的话,就是上面所示。

之后是每一行的区别,我们需要隔离棋盘的行,那么打印分隔线即可,道理如同打印“大元素”。

这个期望,我们就解决了。

3、PlayerMove

玩家落子,我们需要思考合法性,并给出反馈,所以需要需要if条件,首先判断输入的数据是否合理,之后是判断所输入的空间是否被占用。(由于我们习惯性的把横坐标在前,纵坐标灾后,同时注意数组的下标从0开始数,所以接收的时候为了符合判断,我们颠倒一下x和y,同时3-y,x-1)

注意思考为什么是3-y(或者说是ROW - y),因为我们打印出来的列是从上向下数的,而我们看棋盘是从下向上看,所以只能倒过来接受数据才符合我们的思路。

void PlayerMove(char board[ROW][COL], int row, int col)
{
    int x = 0, y = 0;
    printf("玩家走:\n");
    printf("请输入下棋的坐标:");
    //判断坐标合法性
    while (1)
    {
        scanf_s("%d %d", &x, &y);
        if (x >= 1 && x <= row && y >= 1 && y <= col)
        {
            //下棋
            //判断坐标是否呗被占用
            if (board[3 - y][x - 1] == ' ')
            {
                board[3 - y][x - 1] = '*';
                break;
            }
            else
            {
                printf("坐标被占用,请重新输入\n");
            }
        }
        else
        {
            printf("坐标非法,请重新输入\n");
        }
    }
}

如果二者都没有,那么我们就给原来的空格赋值为 “ * ”

4、ComputerMove

电脑下子就简单的多了,只需要随机赋值就可以,同时判断一下占用情况。

void ComputerMove(char board[ROW][COL], int row, int col)
{
    printf("电脑走:\n");
    while (1)
    {
        int x = rand() % row;
        int y = rand() % col;
        //判断占用
        if (board[x - 1][y - 1] == ' ')
        {
            board[x - 1][y - 1] = '#';
            break;
        }
    }
}

5、IfWin

整个代码,唯一有一点难度的应该就是这里了,这里笔者写的比较简陋,我们首先判断每一行是否相等,再每一列,再对角线,如果都没有,就进入棋盘是否满了的判断,如果棋盘满了,那么就是平局,没满,就继续下棋,这里我们需要返回值。同时注意,我们的返回值就是棋盘存储的数据,这一点简化了我们的代码。

//判断游戏是否有输赢
char IfWin(char board[ROW][COL], int row, int col)
{
    int i = 0;
    //判断行
    for (i = 0; i < row; i++)
    {
        if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
        {
            return board[i][1];
        }
    }
    //判断列
    for (i = 0; i < col; i++)
    {
        if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
        {
            return board[1][i];
        }
    }
    //判断对角线
    if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
    {
        return board[1][1];
    }
    if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
    {
        return board[1][1];
    }
    //判断平局
    //满了返回1,没满返回0
    if (IsFull(board, ROW, COL) == 1)
    {
        return 'Q';
    }
    //都没有,继续游戏
    return 'C';
}

那么我们自然需要一个判断棋盘是否已满的函数,由于笔者的电脑出了一点小问题,只能书写这种非常原始的判断方式,有能力的伙伴可以自行改善代码哦

int IsFull(char board[ROW][COL], int row, int col)
{
    for (int i = 0; i < row; i++)
    {
        if (board[i][0] == ' ')
            return 0;
        else if (board[i][1] == ' ')
            return 0;
        else if (board[i][2] == ' ')
            return 0;
        else if (board[0][i] == ' ')
            return 0;
        else if (board[1][i] == ' ')
            return 0;
        else if (board[2][i] == ' ')
            return 0;
        else if (board[i][i] == ' ')
            return 0;
    }
    return 1;
}

到这里,我们的简化版三子棋就实现了,想要书写AI算法的也可以根据棋盘判断的情况进行思考,思考电脑应该如何围堵,落子,然后代码实现。

这里就不多说了,那么感谢看到这里啦!

标签: c语言 算法

本文转载自: https://blog.csdn.net/awaitxm/article/details/122628275
版权归原作者 芝士工具猿 所有, 如有侵权,请联系我们删除。

“C语言一个小时实现简易三子棋,看看你能不能让愚蠢的电脑获胜,还不快上手试试吗(无AI算法)”的评论:

还没有评论