C语言入门简单的小游戏三子棋

目录

前言

总体思路

代码整体框架(test.c)

自定义的头文件(game.h)

实现自定义头文件中自定义函数的功能(game.c)

①初始化棋盘函数(InitBoard)

②打印棋盘函数(DisplayBoard)

效果

③玩家下棋(PlayerMove)

1.下棋代码(PlayerMove)

2.判断落子是否合法代码(CheckMove)

④电脑下棋(ComputerMove)

⑤判断输赢(IsWin)

1.IsWin代码(判断胜负信息以及输出胜负信息)

2.CheckWin代码(判断是否分出胜负)

3.WhoWin代码(得出胜负后输出胜负信息)

 整体代码如下

结语


前言

从大一到快大二, 学了这么久的C语言(当然不是一直都在学), 最近无聊重写了一遍自己大一上写的三子棋代码, 涉及的内容都是很简单的语法这样, 懂个基础语法和代码基本逻辑就可以轻松写出一个三子棋小项目, 下面我给出我代码实现的整体思路供参考(源码放在最后).

总体思路

①首先要有一个整体的代码框架逻辑, 写出简单的游戏菜单以及游戏的基本逻辑.

②实现具体的游戏功能, 先初始化棋盘.

③初始化棋盘后写一个能打印棋盘的函数.

④写一个让玩家落子的函数.

⑤写一个让电脑落子的函数.

⑥写一个判断胜负平局的函数.

下面我将会一步一步的实现三子棋, 所有相关的注解注释都打在了代码里.

代码整体框架(test.c)

#include "game.h" // 自定义的游戏函数头文件, 里面包括了stdio.h和自定义函数等等.

// 简易的菜单.
void menu() {
    printf("*****************************\n");
    printf("********  1. play   *********\n");
    printf("********  0. exit   *********\n");
    printf("*****************************\n");
}
// 游戏主体.
void game() {
    char board[ROW][COL] = {0}; // 用二维的char数组模拟棋盘.
    // 初始化.
    InitBoard(board, ROW, COL); // 初始化棋盘每个点位为' '.
    // 显示棋盘.
    DisplayBoard(board, ROW, COL);
    int flag = 0; // 判断有没有分出胜负.
    while (flag == 0) {
        // 玩家先走.
        PlayerMove(board, ROW, COL);
        // 走完显示棋盘.
        DisplayBoard(board, ROW, COL);
        // IsWin是判断有没有分出胜负的函数,分出了的话返回1.
        flag = IsWin(board, ROW, COL);  // 胜负若分则在函数中打印胜负结果.
        if (flag == 1) {                // 已经分胜负了就退出.
            return;
        }
        // 电脑走.
        ComputerMove(board, ROW, COL);
        // 显示棋盘.
        DisplayBoard(board, ROW, COL);
        // 这里不用判断是否退出, 因为下次循环flag为1可以直接判断跳出循环.
        flag = IsWin(board, ROW, COL);
    }
}

int main() {
    int input = 0; // 操作数.
    srand((unsigned)time(NULL)); // 随机时间种子,用于后面电脑走生成的随机数字.

    do {
        // 游戏菜单先显示出来.
        menu();
        
        printf("请选择:>"); // 输入操作1或0决定玩游戏还是退出.
        scanf("%d", &input);

        switch (input) {
            case 0:
                printf("退出游戏成功!\n"); // 此时input为0,下面的while中会跳出.
                system("pause"); // 停一下供玩家确定.
                break;
            case 1:
                printf("正在进入游戏!加载中...\n");
                Sleep(1000); // 闲着没事模拟了加载游戏的效果,将会停留1s.
                game(); //执行游戏.
                break;
            default:
                printf("输入有误!请重新输入!\n"); // 错误的输入将会重新循环输入.
                break;
        }
    } while (input != 0); // input为0就会跳出循环.

    return 0; // 程序结束.
}

这只是主体代码大致的逻辑, 具体的函数实现在后面会提到, srand是为了让后面rand()生成的随机数跟系统时间有关,做到一定意义上的随机. 

自定义的头文件(game.h)

#pragma once // 这个不用管,是防止文件被重复包含,如果用vs2022写他会自动生成.

#include <Windows.h> // system调用的头文件.
#include <stdio.h> // 基本输入输出头文件.
#include <time.h> // time函数要用.

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

// 判断输赢('*'玩家, '#'电脑).
int IsWin(char board[ROW][COL], int row, int col);

// 补充1: 判断落子是否合法(如越界或已经被落子了).
int CheckMove(char board[ROW][COL], int row, int col, int r, int l);

// 补充2: 判断这个位置的棋子能否赢.
int CheckWin(char board[ROW][COL], int row, int col, int r, int l);

// 补充3: 宣布胜利结果或平局结果.
void WhoWin(char winner);

实现自定义头文件中自定义函数的功能(game.c)

这里我想每个函数拆出来一步一步分析, 最后再附上总代码.

①初始化棋盘函数(InitBoard)

很简单的两个for循环嵌套让二维char数组中每个元素都为空格, 模拟没落子的情况.

// 初始化棋盘.
// 简单的两个循环将每个点都赋值空格代表没落子.
void InitBoard(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) {
            board[i][j] = ' ';
        }
    }
}

②打印棋盘函数(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 %c", board[i][j], "|\n"[j == col - 1]);
            // 等价于printf(" %c ", board[i][j]);
            // if(j != col - 1) printf("%c",'|');
            // else printf("%c", '\n');
        }  
    // j如果等于col - 1则表达式返回1访问这个字符串"|\n"下标为1的'\n',否则就是'|'.

        if (i != row - 1) { // 打印横着的线,井子棋三行只打印前两行,最后一行不打印.

            for (j = 0; j < col; ++j) {
                printf("%s%c", "---", "|\n"[j == col - 1]);
            }

        }

    }
}

效果

③玩家下棋(PlayerMove)

这里要注意输入的数字是否合法, 当前位置是否有棋子能否落子, 也就是注意落子是否合法.

1.下棋代码(PlayerMove)

// 玩家下棋.
void PlayerMove(char board[ROW][COL], int row, int col) {
    int flag = 1; // 控制循环退出于否, 由玩家输入数据是否合法来赋值flag.
    while (flag != 0) {
        printf("现在是你的回合!\n");
        printf("请输入你的落子坐标(1~9):>");
        int vis = 0;  // 棋子坐标的和.
        scanf("%d", &vis);              
        // 为了方便设置了1 ~ 9,后面先-1再/3和%3是为了计算棋子坐标.
        int r = (vis - 1) / row;  // 第几行
        int l = (vis - 1) % col;  // 第几列
        if (CheckMove(board, ROW, COL, r, l) == 0) {
            // 非法.
            printf("输入非法!请重新输入!\n");
        } else {
            board[r][l] = '*';  // 赋值代表下子.
            flag = 0;           // 下次循环马上跳出去.
        }
    }
}

2.判断落子是否合法代码(CheckMove)

// 补充1: 判断落子是否合法.
int CheckMove(char board[ROW][COL], int row, int col, int r, int l) {
    if (r < 0 || r >= row || l < 0 || l >= col) // 棋子越界了.
        return 0;
    else if (board[r][l] != ' ') // 该位置已经落子过了.
        return 0;
    else // 落子合法.
        return 1;
}

④电脑下棋(ComputerMove)

这里思路是生成随机数随机落子, CheckMove判断落子合法与玩家下棋中的一致.

// 电脑下棋.
void ComputerMove(char board[ROW][COL], int row, int col) {
    printf("现在是电脑的回合!\n");
    Sleep(500); // 模拟加载.
    int x = rand() % row; // 横坐标.
    int y = rand() % col; // 纵坐标.
    while (CheckMove(board, ROW, COL, x, y) == 0) {
        // 不符合就一直循环.
        x = rand() % row;
        y = rand() % col;
    }
    board[x][y] = '#';  // 电脑落子.
}

⑤判断输赢(IsWin)

总体代码如下, 思路是循环遍历整个board数组.

每遇到一个有下棋的地方就判断这个位置的棋子能不能赢(循环同一行和同一列还有公共的对角线).

如果遇到一个是空的就让flag为1代表棋局还能继续.

如果flag一直为0且没有分出胜负则为平局.

1.IsWin代码(判断胜负信息以及输出胜负信息)

// 判断输赢('*'玩家, '#'电脑)
int IsWin(char board[ROW][COL], int row, int col) {
    int i = 0;
    int flag = 0;  // 如果棋盘满了就是1,否则是0.
    for (i = 0; i < row; ++i) {
        int j = 0;
        for (j = 0; j < col; ++j) {
            if (board[i][j] != ' ') { 
                // 如果有棋子判断这个棋子的类型能不能赢.
                if (CheckWin(board, row, col, i, j) == 1) {
                    // 宣布胜利.
                    WhoWin(board[i][j]); // 传入胜利的棋子的类型(人或者电脑).
                    return 1;
                }
            } else {
                flag = 1;  // 棋盘上有没落子的地方标记棋盘没满.
            }
        }
    }
    if (flag == 1) {
        // 棋盘没满而且没人赢.
        return 0; // 游戏继续.
    } else {
        WhoWin(' ');  // 平局.
        return 1; // 游戏结束.
    }
}

2.CheckWin代码(判断是否分出胜负)

// 补充2: 判断这个位置的棋子能否赢.
int CheckWin(char board[ROW][COL], int row, int col, int r, int l) {
    int i = 0;
    char winner = board[r][l];  // 决定赢的那个棋子的属性.
    for (i = 0; i < 3; ++i) {
        int j = 0;
        // 同一行.
        for (j = 0; j < col; ++j) {
            if (board[r][j] != winner)
                break;
        }
        if (j == col)  // 可以赢了跳出返回1.
            return 1;
        // 同一列.
        for (j = 0; j < row; ++j) {
            if (board[j][l] != winner)
                break;
        }
        if (j == col)  // 可以赢了跳出返回1.
            return 1;
        // 从左上到右下斜对角.
        int k = 0;
        for (j = 0, k = 0; j < row, k < col; ++j, ++k) {
            if (board[j][k] != winner)
                break;
        }
        if (j == row)  // 可以赢了跳出返回1.
            return 1;
        // 从右上到左下斜对角.
        for (j = 0, k = col - 1; j < row, k >= 0; ++j, --k) {
            if (board[j][k] != winner)
                break;
        }
        if (j == row)  // 可以赢了跳出返回1.
            return 1;
    }
    return 0;
}

3.WhoWin代码(得出胜负后输出胜负信息)

// 补充3: 宣布胜利.
void WhoWin(char winner) {
    if (winner == ' ') {
        printf("平局!\n");
        system("pause");
        system("cls"); // 清屏操作, 美观一点.
    } else if (winner == '*') {
        printf("玩家获胜!\n");
        system("pause");
        system("cls");
    } else {
        printf("电脑获胜!\n");
        system("pause");
        system("cls");
    }
}

 整体代码如下

#include "game.h"  // 自定义的头文件.

// 初始化棋盘.
void InitBoard(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) {
            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 %c", board[i][j], "|\n"[j == col - 1]);
        }  // j如果等于col - 1.
           // 则表达式返回1访问这个字符串下标为1的换行,否则就是'|'.
        if (i != row - 1) {
            for (j = 0; j < col; ++j) {
                printf("%s%c", "---", "|\n"[j == col - 1]);
            }
        }
    }
}
// 玩家下棋.
void PlayerMove(char board[ROW][COL], int row, int col) {
    int flag = 1;
    while (flag != 0) {
        printf("现在是你的回合!\n");
        printf("请输入你的落子坐标(1~9):>");
        int vis = 0;              // 棋子坐标的和.
        scanf("%d", &vis);        // 为了方便设置了1 ~ 9,后面-1再/3和%3是为了计算棋子坐标.
        int r = (vis - 1) / row;  // 第几行
        int l = (vis - 1) % col;  // 第几列
        if (CheckMove(board, ROW, COL, r, l) == 0) {
            // 非法.
            printf("输入非法!请重新输入!\n");
        } else {
            board[r][l] = '*';  // 赋值代表下子.
            flag = 0;           // 下次循环马上跳出去.
        }
    }
}
// 电脑下棋.
void ComputerMove(char board[ROW][COL], int row, int col) {
    printf("现在是电脑的回合!\n");
    Sleep(500);
    int x = rand() % row;
    int y = rand() % col;
    while (CheckMove(board, ROW, COL, x, y) == 0) {
        // 不符合就一直循环.
        x = rand() % row;
        y = rand() % col;
    }
    board[x][y] = '#';  // 电脑落子.
}

// 判断输赢('*'玩家, '#'电脑, 'Q'平局, 'C'继续)
int IsWin(char board[ROW][COL], int row, int col) {
    int i = 0;
    int flag = 0;  // 如果棋盘满了就是1,否则是0.
    for (i = 0; i < row; ++i) {
        int j = 0;
        for (j = 0; j < col; ++j) {
            if (board[i][j] != ' ') {
                if (CheckWin(board, row, col, i, j) == 1) {
                    // 宣布胜利.
                    WhoWin(board[i][j]);
                    return 1;
                }
            } else {
                flag = 1;  // 标记棋盘没满.
            }
        }
    }
    if (flag == 1) {
        // 棋盘没满而且没人赢.
        return 0;
    } else {
        WhoWin(' ');  // 平局.
        return 1;
    }
}

// 补充1: 判断落子是否合法.
int CheckMove(char board[ROW][COL], int row, int col, int r, int l) {
    if (r < 0 || r >= row || l < 0 || l >= col)
        return 0;
    else if (board[r][l] != ' ')
        return 0;
    else
        return 1;
}

// 补充2: 判断这个位置的棋子能否赢.
int CheckWin(char board[ROW][COL], int row, int col, int r, int l) {
    int i = 0;
    char winner = board[r][l];  // 决定赢的那个棋子的属性.
    for (i = 0; i < 3; ++i) {
        int j = 0;
        // 同一行.
        for (j = 0; j < col; ++j) {
            if (board[r][j] != winner)
                break;
        }
        if (j == col)  // 可以赢了跳出返回1.
            return 1;
        // 同一列.
        for (j = 0; j < row; ++j) {
            if (board[j][l] != winner)
                break;
        }
        if (j == col)  // 可以赢了跳出返回1.
            return 1;
        // 从左上到右下斜对角.
        int k = 0;
        for (j = 0, k = 0; j < row, k < col; ++j, ++k) {
            if (board[j][k] != winner)
                break;
        }
        if (j == row)  // 可以赢了跳出返回1.
            return 1;
        // 从右上到左下斜对角.
        for (j = 0, k = col - 1; j < row, k >= 0; ++j, --k) {
            if (board[j][k] != winner)
                break;
        }
        if (j == row)  // 可以赢了跳出返回1.
            return 1;
    }
    return 0;
}

// 补充3: 宣布胜利.
void WhoWin(char winner) {
    if (winner == ' ') {
        printf("平局!\n");
        system("pause");
        system("cls");
    } else if (winner == '*') {
        printf("玩家获胜!\n");
        system("pause");
        system("cls");
    } else {
        printf("电脑获胜!\n");
        system("pause");
        system("cls");
    }
}

结语

三子棋到这就结束了, 总体上是一个非常简单适合新手练习基本语法和代码逻辑的小项目. 如果帮助到了你也可以点个赞点个关注, 谢谢支持. 下面附上我的源码地址供参考:

三子棋源码

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值