C语言编写井字棋游戏

目录

1. 概述

2. 代码实现

InitBoard 函数

DisplayBoard 函数

PlayMove 函数

ComputerMove 函数

IsFull 函数

IsWin 函数

game 函数

main 函数

3. 总结


1. 概述

该项目为使用 C 语言编写基于控制台的井字棋游戏。

玩家使用“*”作为棋子,电脑使用“#”作为棋子,两个实体轮流下棋,第一个完成三个棋子连成一线的玩家获胜。若棋盘已满且没有玩家获胜,游戏成平局。

该游戏为基础井字棋游戏,AI 算法为随机下棋。

2. 代码实现

#define _CRT_SECURE_NO_WARNINGS 1

#include 
#include 
#include 

#define ROW 3
#define COL 3
#define PLAYER_PIECE '*'
#define COMPUTER_PIECE '#'

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

void DisplayBoard(char board[ROW][COL], int row, int col)
{
    int i = 0;
    for (i = 0; i < row; i++)
    {
        int j = 0;
        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");
        }
    }
}

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

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][y] == ' ')
        {
            board[x][y] = COMPUTER_PIECE;
            break;
        }
    }
}

int IsFull(char board[ROW][COL], int row, int col)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            if (board[i][j] == ' ')
            {
                return 0;//棋盘没满
            }
        }
    }
    return 1;//棋盘满了
}

char IsWin(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];
        }
    }

    //判断对角线1
    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];
    }

    int ret = IsFull(board, row, col);
    if (ret == 1)
    {
        return 'Q';
    }
    return 'C';
}

void menu()
{
    printf("****************************\n");
    printf("******  输入1开始游戏 ******\n");
    printf("******  输入0结束游戏 ******\n");
    printf("****************************\n");
}

void game()
{
    //存储数据 二维数组
    char board[ROW][COL];
    //初始化棋盘,初始化空格
    InitBoard(board, ROW, COL);
    //打印一下棋盘  本质是打印数组内容
    DisplayBoard(board, ROW, COL);
    //接受游戏转状态
    char ret = 0;

    while (1)
    {
        //玩家走
        PlayMove(board, ROW, COL);
        //玩家走完打印
        DisplayBoard(board, ROW, COL);
        printf("\n");
        //判断玩家是否赢了游戏
        ret = IsWin(board, ROW, COL);
        if (ret != 'C')
            break;
        //电脑走
        ComputerMove(board, ROW, COL);
        //电脑走完打印
        DisplayBoard(board, ROW, COL);
        printf("\n");
        //判断电脑是否赢了游戏
        ret = IsWin(board, ROW, COL);
        if (ret != 'C')
            break;
    }
    if (ret == PLAYER_PIECE)
    {
        printf("玩家胜利!\n\n");
    }
    else if (ret == COMPUTER_PIECE)
    {
        printf("电脑胜利!\n\n");
    }
    else
    {
        printf("平局!\n\n");
    }
    DisplayBoard(board, ROW, COL);
    printf("\n");
}

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

该井字棋游戏使用 C 语言编写,主要由以下几个函数组成:

- InitBoard:初始化棋盘,并将所有位置设置为空格
- DisplayBoard:打印当前棋盘状态
- PlayMove:玩家下棋
- ComputerMove:随机 AI 下棋
- IsFull:判断棋盘是否已满
- IsWin:判断是否有玩家获胜
- menu:打印游戏菜单
- game:控制游戏的流程,游戏结束后打印胜者,并展示最终的棋盘状态
- main:执行前两个函数,并允许用户选择是否开始游戏或结束游戏

InitBoard 函数

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

该函数用于初始化棋盘,并将所有位置设置为空格。其中,`board` 为一个二维字符数组,存储了棋盘的状态;`row` 和 `col` 分别表示棋盘的行数和列数。

函数实现过程中,使用了嵌套循环对所有的位置进行遍历,将其设置为空格。

DisplayBoard 函数

void DisplayBoard(char board[ROW][COL], int row, int col)
{
    int i = 0;
    for (i = 0; i < row; i++)
    {
        int j = 0;
        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");
        }
    }
}

该函数用于打印当前棋盘状态。与 `InitBoard` 函数类似,它也使用了 `board` 二维数组表示棋盘状态,同时也使用了 `row` 和 `col` 表示棋盘的行数和列数。

函数实现过程中,使用了嵌套循环对所有的位置进行遍历,使用 `printf` 函数打印每个棋子,并在其后添加竖线分隔符。在打印完一行后,还会添加水平分隔符。通过判断行和列的位置,函数可以控制是否打印分隔符。

PlayMove 函数

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

该函数用于让玩家下棋。同样使用了 `board` 二维数组表示棋盘状态,`row` 和 `col` 表示棋盘的行数和列数。

函数实现过程中,使用 `scanf` 函数获取用户输入的坐标,并判断输入坐标的合法性和位置是否已被占用。如果输入的坐标非法或者该位置已被占用,将提示用户重新输入。如果输入的坐标合法并且该位置未被占用,就将棋子放置在该位置。

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][y] == ' ')
        {
            board[x][y] = COMPUTER_PIECE;
            break;
        }
    }
}

该函数用于让电脑下棋。同样使用了 `board` 二维数组表示棋盘状态,`row` 和 `col` 表示棋盘的行数和列数。

函数实现过程中,使用 `rand` 函数生成随机的坐标,判断该位置是否已被占用。如果该位置未被占用,就将棋子放置在该位置。

IsFull 函数

int IsFull(char board[ROW][COL], int row, int col)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            if (board[i][j] == ' ')
            {
                return 0;//棋盘没满
            }
        }
    }
    return 1;//棋盘满了
}

该函数用于判断棋盘是否已满。同样使用了 `board` 二维数组表示棋盘状态,`row` 和 `col` 表示棋盘的行数和列数。

函数实现过程中,使用了嵌套循环对所有的位置进行遍历,判断是否有空格。如果有空格,说明棋盘未满,返回 0;否则,返回 1,表示棋盘已满。

IsWin 函数

char IsWin(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];
        }
    }

    //判断对角线1
    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];
    }

    int ret = IsFull(board, row, col);
    if (ret == 1)
    {
        return 'Q';
    }
    return 'C';
}

该函数用于判断是否有玩家获胜。同样使用了 `board` 二维数组表示棋盘状态,`row` 和 `col` 表示棋盘的行数和列数。

函数实现过程中,使用了嵌套循环判断各行、各列、对角线是否已有一方下满三个棋子。如果存在一方下满三个棋子并连成一线,就返回该方棋子类型。

如果棋盘已满且没有任何一方获胜,就返回 ‘Q’,表示平局。

void menu()
{
    printf("****************************\n");
    printf("******  输入1开始游戏 ******\n");
    printf("******  输入0结束游戏 ******\n");
    printf("****************************\n");
}

该函数用于打印游戏菜单,提示用户输入 1 开始游戏或输入 0 结束游戏。

game 函数

void game()
{
    //存储数据 二维数组
    char board[ROW][COL];
    //初始化棋盘,初始化空格
    InitBoard(board, ROW, COL);
    //打印一下棋盘  本质是打印数组内容
    DisplayBoard(board, ROW, COL);
    //接受游戏转状态
    char ret = 0;

    while (1)
    {
        //玩家走
        PlayMove(board, ROW, COL);
        //玩家走完打印
        DisplayBoard(board, ROW, COL);
        printf("\n");
        //判断玩家是否赢了游戏
        ret = IsWin(board, ROW, COL);
        if (ret != 'C')
            break;
        //电脑走
        ComputerMove(board, ROW, COL);
        //电脑走完打印
        DisplayBoard(board, ROW, COL);
        printf("\n");
        //判断电脑是否赢了游戏
        ret = IsWin(board, ROW, COL);
        if (ret != 'C')
            break;
    }
    if (ret == PLAYER_PIECE)
    {
        printf("玩家胜利!\n\n");
    }
    else if (ret == COMPUTER_PIECE)
    {
        printf("电脑胜利!\n\n");
    }
    else
    {
        printf("平局!\n\n");
    }
    DisplayBoard(board, ROW, COL);
    printf("\n");
}

该函数用于控制游戏流程,实现玩家和电脑轮流下棋,直至游戏结束。在每次玩家和电脑下完棋后,都会打印当前的棋盘状态以及提示信息。

如果某一方下满三个棋子连成一线,游戏结束,并打印胜利方。如果棋盘已满且没有任何一方获胜,游戏结束,并打印平局。

最后,该函数还将展示最终的棋盘状态。

main 函数

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

该函数是整个程序的入口,执行该函数会根据用户的选择打印游戏菜单或直接退出游戏。

在函数实现过程中,使用了 `srand` 函数生成一个基于时间的随机数种子。此外,在执行菜单选项时,使用了 `switch` 语句根据用户输入执行相应的代码实现。

整个程序的结构非常清晰,游戏流程没有使用递归,主要是使用循环来控制。

其中,比较有意思的部分是 `DisplayBoard` 函数,它使用了循环嵌套来打印棋盘的状态,通过判断行列数来控制是否打印框线。

另一个有趣的部分是 `ComputerMove` 函数,它使用 `rand()` 函数随机下棋。

3. 总结

该井字棋游戏是一个基础版,但可以通过该参考代码了解到一些基本的 C 语言操作,如循环嵌套、判断语句和数组等。

当然,该游戏也有一些可以改进的地方。比如,可以考虑添加更加智能的 AI,提高游戏难度和趣味性。

总之,该项目对于初学者来说是一个不错的练手项目,希望该项目能够帮助你熟悉 C 语言的操作和基本编程概念。

你可能感兴趣的:(c语言,游戏,c++)