游戏|看完这个即便你是菜鸟你也会用C语言实现三子棋


文章目录

      • 1. 三子棋实现的基本思路
      • 2. 主函数
      • 3. menu函数和game函数
      • 4. game.c中的具体实现函数
        • 4.1 初始化函数 InitBoard()
        • 4.2 打印棋盘函数 DispayBoard()
        • 4.3 玩家操作函数 PlayerMove()
        • 4.4 电脑下棋函数 ComputerMove()
        • 4.5 判断胜负函数 IsWin()
        • 4.6 判断棋盘是否已满函数 IsFull()
      • 5. 头文件
      • 6. 实现效果

1. 三子棋实现的基本思路
三子棋是我们在儿时玩过的最经典的游戏之一,游戏的规则很简单,在一个 3×3 的棋盘中,两人交替下棋,当某一方的三个棋子连成三点一线,那么即为胜利。
那么问题来了,我们怎样巧妙的用编程的思维去完成这样一个小项目呢?首先,我们清楚,如果我们在一个源文件中将所有相关的代码全部挤进去,那么写到一半的时候你可能会窒息,“我写到哪了?”“我在写什么?”,一连串发自灵魂的自问可能会让你明白这世界并不美好。既然这样我们何不把写的代码按功能分类,分别放到不同的源文件中去呢?因此我们就可以在主函数 main.c 、功能函数 game.c 、以及头文件 game.h 中进行编译啦。
有了这样的一个大前提,我们知道游戏一般都会有菜单,让你选择玩游戏还是退出,当你进入了游戏,棋盘会出现在你的面前,等待你下的第一步棋,你下完了,电脑会自动下完第二步,这样一直循环,一直等到决出胜负。
这个时候你不会天真的以为事情就这么的顺风顺水吧,让我们来看一看我们到底要实现几个函数:初始化棋盘、打印棋盘、玩家下棋、电脑下棋、判断胜负,况且我们如何存储这个棋盘的棋子的数据呢?显然我们可以用一个二维数组来存储每一个格子的信息,接下来让我们一步一步地来实现。
2. 主函数
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; }

有了以上的主函数作为参考,接下来的实现就是要我们一步一个脚印写出来了。首先我们观察上述代码是否有未知的需求,显然 menu() 函数和 game() 函数我们都是未知的,接下来我们来实现。
3. menu函数和game函数
就像在第一点说的那样,游戏的实现主要在这一部分,我们需要将各种功能都封装成函数。我们设棋盘有 ROW 行,COL 列,而不是死板的将行和列都设置为 3 ,这样我们把ROW和COL定义在头文件中 ,就优化了代码的可更改性。另外,在所有要封装的函数中,判断胜负的函数是需要返回值的,根据返回值的不同,通过 if 语句的判断从而打印出不同的结果。我们假设返回字符 * 代表玩家胜利,返回 # 代表电脑胜利,返回 C 代表游戏继续(没有分出胜负),返回 Q 代表平局。
void menu()//打印游戏菜单界面 { printf("*********************\n"); printf("****1.PLAY*****\n"); printf("****0.EXIT*****\n"); printf("*********************\n"); } void game() { char ret; //初始化返回变量 char board[ROW][COL]; //定义棋盘数组 InitBoard(board, ROW, COL); //将棋盘数组内的元素都初始化为空格 DisplayBoard(board, ROW, COL); //打印出棋盘,让棋盘可视化 while (1)//进入游戏循环 {PlayerMove(board, ROW, COL); //玩家下棋 DisplayBoard(board, ROW, COL); //打印出棋盘,让棋盘可视化 ret = IsWin(board, ROW, COL); //判断是否分出了胜负 if (ret != 'C')//只有返回C时游戏才没有分出胜负 {break; } ComputerMove(board, ROW, COL); //电脑下棋 DisplayBoard(board, ROW, COL); //打印出棋盘,让棋盘可视化 ret = IsWin(board, ROW, COL); //判断是否分出了胜负 if (ret != 'C')//只有返回C时游戏才没有分出胜负 {break; } } //退出循环则是已经分出胜负的情况 if (ret == '*') {printf("玩家赢!\n"); } else if (ret == '#') {printf("电脑赢!\n"); } else {printf("平局!\n"); }

4. game.c中的具体实现函数
4.1 初始化函数 InitBoard() 在游戏开始之前,我们需要将表示棋盘的数组里面的元素全部初始化,大家想一想,在什么都没有操作的棋盘上是不是什么都没有,但是我们不能把数组初始化为没有呀,什么都没有只是视觉上的表现,我们不妨将数组每个元素初始化为空格。
//char board[ROW][COL]用于接收棋盘数据的数组 //row用于接收棋盘的行数 //col用于接收棋盘的列数 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] = ' '; //将每一个数组元素初始化为空格 } } }

4.2 打印棋盘函数 DispayBoard() 虽然我们已经将棋盘的每个元素初始化为了空格,但只是在计算机内存完成的操作,我们的肉眼是无法很好的观察到的,既然要下棋,我们总要将整个棋盘可视化吧,我们计划将棋盘设计成以下格式:
游戏|看完这个即便你是菜鸟你也会用C语言实现三子棋
文章图片

我们观察到棋盘一共有五行,第一三五行放置了数组元素与分割线,二四行放置了分隔线,这是一个 3×3 的棋盘,我们试想,如果我们仅仅将棋盘打印出只符合 3×3 棋盘的分隔线,你那未必也太挫了吧,我们想要修改头文件中的数据,而棋盘的大小也跟着改变,这样也就方便了以后程序的优化升级。
我们观察这个棋盘的规律:第一行是由九个空格两条竖线组成的,我们不妨将三个空格和一条竖线看作一组,循环打印,当打印到第三组的时候利用条件语句不打印不就可以了吗?再继续往下观察,可见第一行和第二行的布局与第三行和第四行的布局完全一样,我们也不妨将这两行也看做一组循环打印,至于最后一行,利用相同的方法,即打印到最后一次时不打印下面横竖线分隔线的那一部分。
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]); // "空格%c空格" 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"); } } }

打印出的结果如图所示
游戏|看完这个即便你是菜鸟你也会用C语言实现三子棋
文章图片

4.3 玩家操作函数 PlayerMove() 玩家的操作无非就是将棋子放入想要放入的位置中,我们设玩家棋子为字符 * ,我们的目的就是将这颗星号放入数组中,这就需要用到二维数组的坐标作为传参,让玩家输入坐标,从而将对应的二维数组元素置为星号。此时我们就得考虑了,输入的坐标大于了二维数组本身的范围怎么办?下过棋子的地方还能不能继续下呢?对应的输入坐标和二维数组的坐标时相同的吗?
显然我们需要给定一个输入坐标的范围;下过棋子的地方再下棋子就会报错,利用循环重新输入;输入的坐标减一才是对应的二维数组的坐标。
void PlayerMove(char board[ROW][COL], int row, int col) { printf("玩家走:"); while (1) {printf("请输入坐标:\n"); int x, y; scanf("%d%d", &x, &y); //给定输入坐标的合法范围 if (x >= 1 && x <= row && y >= 1 && y <= col) {//该坐标下元素不是空格,说明已经被占用 if (board[x - 1][y - 1] != ' ') {printf("该格子被占用,请重新输入:\n"); } else {//没有被占用,将其置为 * board[x - 1][y - 1] = '*'; break; } } else //其他不合法的输入 {printf("输入错误,请重新输入\n"); } } }

4.4 电脑下棋函数 ComputerMove() 不会吧不会吧,不会有人真的以为电脑会动脑子吧,这里的电脑下棋只不过是利用计算机产生两个随机数作为坐标,然后将电脑的棋子放置到棋盘中,除了要生成随机的坐标,其他的操作都与玩家下棋相似。这时的随机数就成问题了,随机数那么大,坐标那么小怎么办?我们将随机数模上行数或者列数,假如行列都是 3 ,我们不就得到 0,1,2 了吗?当然了,随机数函数 rand() 是需要 srand进行配置的,将时间戳作为变量,可以得到不同的随机数, srand 的配置在主函数中。
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] = '#'; break; } } }

4.5 判断胜负函数 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][0] == board[i][2] && board[i][0] != ' ')//当行上出现三点一线 {return board[i][0]; //谁赢就返回什么 } } for (i = 0; i < col; i++) {if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')//当列上出现三点一线 {return board[0][i]; } } if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ')//当对角线上出现三点一线 {return board[0][0]; } if (board[0][2] == board[1][1] && board[2][0] == board[1][1] && board[0][2] != ' ')//当斜对角线上出现三点一线 {return board[0][2]; } if (IsFull(board, ROW, COL) == 1)//棋盘满了,平局 return 'Q'; if (IsFull(board, ROW, COL) == 0)//继续 return 'C'; }

4.6 判断棋盘是否已满函数 IsFull() 我们遍历整个数组,如果出现了空格,则棋盘没满。用 0 和 1 作为返回值,0代表没满,1代表已满。
static 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; }

5. 头文件
我们实现完了所有的具体功能函数,需要在头文件中添加函数声明,并且在 game.c 和 main.c 中引入 头文件 game.h
#include #include #include#define ROW 3 #define COL 3void 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); //电脑下棋 char IsWin(char board[ROW][COL], int row, int col); //判断输赢

6. 实现效果
【游戏|看完这个即便你是菜鸟你也会用C语言实现三子棋】我们来看一看玩家赢得效果
游戏|看完这个即便你是菜鸟你也会用C语言实现三子棋
文章图片
游戏|看完这个即便你是菜鸟你也会用C语言实现三子棋
文章图片

试一试退出游戏
游戏|看完这个即便你是菜鸟你也会用C语言实现三子棋
文章图片
平局
游戏|看完这个即便你是菜鸟你也会用C语言实现三子棋
文章图片

电脑赢
游戏|看完这个即便你是菜鸟你也会用C语言实现三子棋
文章图片

以上就是本篇博客得全部内容,感谢老爷们得观看,记得赞赞点一手哦~

    推荐阅读