• 人与人之间的单机五子棋 —— C语言实现


    人与人之间的单机五子棋

    在这里插入图片描述


    每博一文案

    作家苏曾说,到了现在这个年纪,谁都不想再取悦了,跟谁在一起舒服就和
    谁在一起,包括朋友也是,累了,就躲远一点。人活着总是离不开圈子二字。
    为了扩大自己的社交圈,参见各种活动,每天觥筹交错,忙得不亦乐乎?
    可是,当我们心情低落的时候,把通讯录翻了个遍,也没找到一个能倾诉的人。
    当我们遇到麻烦,想要求助,那些所谓的朋友,愿意帮忙的寥寥无几,以前
    总觉得圈子越大越好,朋友越多越好。后来,慢慢发现,真正的朋友,再好,不再多。
    越简单的人,越难交心,越简单的关系,越难长久。
                                      _________    一禅心灵庙语
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8


    五子棋

    五子棋起源于中国,是全国智力运动会竞技项目之一,是一种两人对弈的纯策略型棋类游戏。双方分别使用黑白两色的棋子,下在棋盘直线与横线的交叉点上,先形成五子连珠者获胜。

    五子棋容易上手,老少皆宜,而且趣味横生,引人入胜。它不仅能增强思维能力,提高智力,而且富含哲理,有助于修身养性。


    而我们做成后的样子是如下: 显示

    在这里插入图片描述


    创建项目的注意事项

    • 首先我们要明白一点就是不存在没有完完全全BUG的项目,就像人无完人一样 我们只能不断地对项目进行优化,减少BUG 的出现,但是我们好像并不能完全的消除所有的BUG
    • 而消除bug 的最好的方式就是运行测试+调试
    • 而我们在运行测试+调试 的时候,需要我们控制一定的量 ,我们需要把握住这个量 ,这一点是十分重要的,这样有助于我们快速的发现问题,也就是bug 的所在,从而提高我们的效率,减少我们的bug 的出现
    • 具体的方法,我个人有以下建议:
    1. 首先我们需要对项目进行合理的分类,那个类,那个文件实现什么样的功能: 比如:一个类是用于定义什么类型,方法的 ,另一个类是用于什么头main 的,再另外一个类是用于对于什么功能上的实现的 ,合理的分类,可以提高我们项目的可读性,让我们自身不会因为自己随着代码量的增加,对应功能上的增加而导致我们越敲越乱,越写越蒙
    2. 对于量上我们可以,每实现了两三个 功能就运行测试看看,是否存在错误,如果存在出现了错误,我们可以在一定的量(范围内),快速的找出哪里出现了问题,从而对它进行调试 ,解决问题,而不是,把项目整体写完了或者是已经实现了好几十个功能,才开始运行测试项目,结果一运行,bug,警告 冒出一大坨,我们先不说,让不让人,抓头发,就是找出问题,恐怕都需要不时间上的浪费吧
    3. 对于代码上的注释,没事多写明白一点,或许有一天,你会感想曾经,那个写了注释的自己或同事
    4. 对于bug 不要害怕,一点一点的调试看看,找出问题的所在,慢慢来,要有耐心,要明白一点现在bug 写的多,以后bug 跟你说拜拜,现在bug ,不解决,bug 天天对你说 明天见

    五子棋实现原理解剖

    我们使用分布式文件系统,编写该项目

    我们这款五子棋游戏是,单机,人与人之间的对战的。角色分为用户1,用户2两位

    • 游戏菜单显示,两种情况,是开始游戏,还是退出游戏
    • 我们定义二维数组 用于五子棋的显示和存放五子棋的信息。显示情况如下:
      • 没有被下过的位置,符号 . 表示
      • 被用户1下过的位置,符号 表示
      • 被用户2下过的位置,符号 表示
    • 下棋方式是,通过输入坐标的形式,下棋
    • 棋盘结果有四种情况分别是:
      • 棋盘未满,未分胜负,继续下棋
      • 棋盘未满,分胜负,用户1 赢了
      • 棋盘未满,分胜负,用户2 赢了
      • 棋盘已满,未分胜负,平局

    功能上简单细节构思

    显示打印数组中以 1 开始,显示行号,以及列号

    下棋中,所下坐标位置,不可超出棋盘大小,不可下在已经被下过的位置,去重,每下一步,都需要打印棋盘

    每下一步棋,都需要判断,下的这一步是否,分出胜负,而不是下了好几步,才判断结果,


    定义相关的变量

    这里我们的棋盘的行列,用户1, 用户2,以及棋盘的结果的标识 使用 定义,方便修改,以及后期的维护

    #define ROW 15    // 棋盘行数
    #define COL 15    // 棋盘列数
    
    
    #define PLAYER1 1     // 定义标识用户 1,
    #define PLAYER2 2     // 定义标识用户 2
    
    
    
    // 棋局中的中情况的定义
    #define NEXT 0        // 表示没有结果,继续下棋
    #define DRAE 3        // 表示棋盘下满了,平局结束
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    进入游戏的选择

    我们把游戏的功能封装写在一个方法中Game() 用于 main 函数调用使用, 使用循环,根据实际情况,我们玩完了一把游戏,可能还会再来一把,所以我们使用循环——> 在循环里头——>再使用分支条件 switch 选择对应的功能, 在循环中定义一个数值,作为循环条件,方便我们更新循环条件,从而达到跳出循环,退出游戏的操作

    
    int main()
    {
    
    	int quit = 0;     // 定义一个标识,用于循环条件的退出
    	int select = 0;   // 对应功能上的选择
    
    	while (!quit)
    	{
    		menc();
    		printf("请输入你的选择: > ");
    		scanf("%d", &select);      // 选择游戏模式
    
    		switch (select)
    		{
    		case 1:
    			Game();     // 进去游戏
    			break;
    		case 0:
    			quit = 1;   // 通过改变循环条件,跳出循环,结束游戏
    			printf("ByeBye!ln\n");
    			break;
    		default:        // 选择错误重新选择
    			printf("Enter Error , Try Again!!! \n");
    			break;
    		}
    		
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    打印菜单的功能的实现

    void menc()   // 游戏菜单
    {
    	printf("#########################\n");
    	printf("### 1.paly     0.Exot ###\n");
    	printf("##### Please Select #####\n");
    	printf("#########################\n");
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    游戏函数 Game( ) 的构建

    1. 首先我们需要创建一个二维数组作为棋盘使用,并使用函数 memset 初始化二维数组(棋盘),同时定义一个变量,用于保存棋盘是否继续,如int result = NEXT ,其中的NEXT就是我们定义的标记:未分胜负,棋盘继续的标识
    2. 游戏是只有在分出胜负,才会结束,所以需要让游戏不要中途停止,所以写成循环的方式,进行游戏。
    3. 定义一个函数用于打印棋盘,如:showBoard(board, ROW, COL) 用于打印棋盘,每下一步都需要打印棋盘,传的参数为(数组,数组的行,数组的列)
    4. 接着定义一个下棋的函数用于下棋,如:playerMove(board, ROW, COL, PLAYER1) ,传的参数是(数组,数组的列,数组的行,谁下的(用户标识符号))
    5. 定义一个判断每下一棋,判断是否出胜负的函数如:result = isOver(board, ROW, COL),并返回结果(用户1赢,用户2赢,平局,继续),根据返回结果,如果返回值,不是继续,说明分出结果了,跳出循环,结束游戏,
    6. 退出循环后,再根据返回判断胜负的返回值,判断是谁赢了,还是平局,使用switch(result) 分支语句

    逻辑思路

    打印棋盘(showBoard( ))——> 用户1下棋(playerMove( ))——>判断胜负结果( isOver( )),(结果上未分胜负)——>打印棋盘(showBoard( ))——>用户2下棋(playerMove( ))——> 判断胜负结果( isOver( )),(结果上未分胜负)——>打印棋盘(showBoard( ))——> 再用户1下棋(playerMove( )),一直这样循环直到分出胜负( isOver( ))

    在这里插入图片描述


    void Game()
    {
    	int board[ROW][COL];                 // 使用二维数组,定义棋盘
    	memset(board, 0, sizeof(board));     // 初始化二维数组
    	int result = NEXT;                   // 游戏继续
    
    	do {
    		showBoard(board, ROW, COL);               // 打印棋盘
    		playerMove(board, ROW, COL, PLAYER1);      // 用户1 下棋
    		result = isOver(board, ROW, COL);      // 判断下了这一手,是否有结果,结果返回
    
    		if (NEXT != result)   // 根据判断后的返回值,判断是否出了结果,是否还需要继续,NEXT 表示继续
    		{
    			break;
    		}
    		showBoard(board, ROW, COL);               // 打印棋盘
    		playerMove(board, ROW, COL, PLAYER2);      // 用户2 下棋
    	    result = isOver(board, ROW, COL);      // 判断下了这一手,是否有结果,结果返回
    
    		if (NEXT != result)    // 根据判断后的返回值,判断是否出了结果,是否还需要继续, NEXT 表示继续
    		{
    			break;
    		}
    
    		
    
    	} while (1);
    
    	 
    	// 跳出循环,根据返回值,判断该局的,结果是,谁赢,谁输,还是平局
    	switch (result)
    	{
    	case PLAYER1:
    		showBoard(board, ROW, COL);               // 打印棋盘
    		printf("恭喜用户1,你赢了\n");
    		break;
    	case PLAYER2:
    		showBoard(board, ROW, COL);               // 打印棋盘
    		printf("恭喜用户2,你赢了\n");
    		break;
    	case DRAE:
    		showBoard(board, ROW, COL);               // 打印棋盘
    		printf("平分秋色,平局\n");
    		break;
    	default:           // 不可能会执行到的,这里设置是为了以防万一
    		break;
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    打印棋盘showBoard() 函数

    1. 首先我们需要清屏,每打印一个新的棋盘,清屏一下,这样体验效果更好,注意我们显示的无论是行号还是列好都是从 1 开始的,所以在打印的过程中需要 +1
    2. 首先打印两个空格,为打印棋行号,空出位置,更加美观,再打印列号
    3. 再使用两层循环打印二维数组,显示棋盘,第一层循环打印行号
    4. 两层循环遍历打印二维数组(显示棋盘),同时判断没有下过的位置以符号==.== 显示,用户1:下过的使用 显示,用户2:下过的使用 显示:

    // 打印二维数组
    void showBoard(const int board[ROW][COL], const int row, const int col)
    {
    //	printf("\e[1;1H\e[2J");  // 清屏
    	system("cls");
    	int i = 0;
    	printf("  ");   // 两个空格
    	for (; i < row; i++)    // 打印行号
    	{
    		printf("%3d", i+1);   // 预留三个位置
    	}
    	printf("\n");
    
    	for (i = 0; i < row; i++)
    	{
    		int j = 0;
    		printf("%2d ",i + 1);  // 预留两个位置
    		for (j; j < col; j++)
    		{
    			if (0 == board[i][j])    // 没有下过的位置打印 为 .
    			{
    				printf(" . ");
    			} 
    			else if (PLAYER1 == board[i][j]) {  // 用户1 的显示符号●
    				printf("● ");
    			}
    			else                          // 不等于,则是用户2 的显示符号 ○
    			{
    				printf("○ ");                 
    			}
    		}
    		printf("\n");
    
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    下棋playerMove( )函数

    1. 使用 static 修饰该函数,封装该函数,让函数只能在该文件内使用,提高代码的可维护性
    2. 定义两个全局变量,用于保存,赋值输入的坐标位置,
    3. 判断输入的坐标是否合理:
      • 是否超出棋盘的大小,下的位置是否被人下过。
      • 注意我们输入的坐标是从 1 下标开始的,而数组是从 0 下标开始的,所以在赋值的过程中需要 -1
    4. 写成循环,因为有可能输入的坐标不合理,需要重新输入,合理时,赋值坐标到数组中,并退出循环,注意赋值到数组中的值,是该用户所下的,标识数值。在宏中就定义好的
    5. #define PLAYER1 1 // 定义标识用户 1
      #define PLAYER2 2 // 定义标识用户 2
    // 下棋
    static void playerMove(int board[ROW][COL], const int row, const int col, const int who)
    {
    	while (1)
    	{
    		printf("player[%d] pleach Enter Your Pos#> ", who);  // 提示打印用户谁的输入
    		scanf("%d %d", &x, &y);       // 输入坐标
    
    
    		// 输入的坐标超出了棋盘,不合法,重新输入
    		if (x < 1 || x > row || y < 1 || y > col)
    		{
    			printf("超过棋盘了,请重新输入\n");
    			continue;    // 跳过本次循环,重新输入
    		}
    
    		// 输入的坐标位置,已经被占用了,不合法,重新输入
    		if (0 != board[x-1][y-1])     
    		{
    			printf("pos IsOccupied!\n");
    			continue;   // 跳过本次循环,重新输入
    		}
    
    
    		// 运行到这,说明该坐标没有问题,可以下入到棋盘中去了
    		board[x - 1][y - 1] = who;
    		break;    // 跳出循环
    
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    判断胜负isOver( )函数

    1. 使用 static 修饰该函数,封装该函数,让函数只能在该文件内使用,提高代码的可维护性

    2. 定义一个枚举结构用于表示各个方位的数值的标识,如typedef enum Dir

    3. 定义一个函数chessCount(board, row, col, LEFT) ,用于计算二维数组中各个方位的上相同的连续出现的数值,返回计算得到的数值,因为在该函数中并没有计算到包含到,本身所下的个数,所以需要额外加上 +1

    4. 判断返回的连续出现的数值,是否存在 >=5 的个数,有则存在,五子连珠的情况说明有人赢了,再根据所走这步的,用户是谁,判断是谁,赢了

    5. 如果没有五子连珠,则再循环遍历二位数组,是否全部下完了,但凡存在没下的,下完了,则表示棋盘未满,未分出胜负,返回继续下棋,返回值为return NEXT

    6. 以上4,5这两种情况都没有出现的话,棋盘满了,未分出胜负,平局,返回值为 return DRAE

    枚举类型:表示方位

    typedef enum Dir  // 枚举 坐标上的方向,注意,枚举以逗号分割开来
    {
    	LEFT,       // 左边
    	RIGHT,      // 右边
    	UP,         // 上边
    	DOWN,       // 下边
    	LEFT_UP,    // 左上边
    	LEFT_DOWN,  // 左下边
    	RIGHT_UP,   // 右上边
    	RIGHT_DOWN  // 右下边
    	// 注意 枚举中的最后一个属性,不要加逗号分隔开,不然会报错的
    }Dir;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    // 走一步,判断结果
    static int isOver(const int board[ROW][COL], const int row, const int col)
    {
    	// 从四个位置上判断,可能五子连珠的,可能性,当连续的相同的字符,数量个数为 5 时,表示有一方赢了
    
    	// 在此基础上都需要加上 1 ,因为本身的位置上的,没有包含计算上,是需要计算上的
    	// 左右横方向上的个数
    	int count1 = chessCount(board, row, col, LEFT) + chessCount(board, row, col, RIGHT) + 1;
    	// 上下纵方向上的个数
    	int count2 = chessCount(board, row, col, UP) + chessCount(board, row, col, DOWN) + 1;
    	// 左斜上方向上的个数
    	int count3 = chessCount(board, row, col, LEFT_DOWN) + chessCount(board, row, col, RIGHT_UP) + 1;
    	// 右斜下方向上的个数
    	int count4 = chessCount(board, row, col, LEFT_UP) + chessCount(board, row, col, RIGHT_DOWN) + 1;
    
    
    	// 只要在任意位置上,个数上 >= 5 就,实现了五子连珠,就有获胜了
    	if (count1 >= 5 || count2 >= 5 || count3 >= 5 || count4 >= 5)
    	{
    		if (board[x - 1][y - 1] == PLAYER1)
    		{
    			return PLAYER1;   // 表示用户1 赢了
    		}
    		else
    		{
    			return PLAYER2;   //否则就是 表示用户2 赢了
    		}
    	}
    
    
    	// 循环遍历二维数组(棋盘),判断棋盘是否存在满了,如果有一个棋位置,还可以下,就表示还可以继续下棋,
    	for (int i = 0; i < row; i++)
    	{
    		for (int j = 0; j < col; j++)
    		{
    			if (board[i][j] == 0)
    			{
    				return NEXT;   // 继续,棋盘没有满
    			}
    		}
    	}
    
    	return DRAE;        // 走到这表示棋盘满了,平局
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    计算坐标周围连续的个数chessCount( )函数

    在这里插入图片描述


    根据所下的位置判断,是否存在五子连珠 的情况:一共存在四种情况分别是

    • 横方向上的 五子连珠

    在这里插入图片描述

    • 纵方向上的五子连珠

    在这里插入图片描述

    • 左斜向下的五子连珠

    在这里插入图片描述

    • 右斜上的五子连珠

    在这里插入图片描述


    注意 :编程中的图形界面上的坐标是与,数学上的坐标是相反的,数学上的,横为 X轴,纵为 Y轴 ,而编程中的是横为 Y轴,纵为 X 轴 ,如下图所示:

    在这里插入图片描述

    根据所传参数中的方位,定义多条分支选择switch() ,作出相应的坐标上的移动,我们需要保留原来的坐标位置用于判断,所以我们需要拷贝复制一份,坐标用于移动,防止丢失原来的坐标位置,因为我们需要计算的是连续的,所以需要写在循环中,

    方位上的移动坐标

    左边 :y–;

    右边:y++;

    上边:x++;

    下边:x–;

    左下边:y–;x–;

    左上边:x++;y–;

    右上边:y++;x++;

    右下边:y++;x–;


    1. 每移动一个坐标位置,都需要判断该坐标是否,越界了,是否超出了棋盘的范围,超出了,不用计算了,跳出循环
    2. 在确定移动坐标合理时,再判断该移动的坐标位置上的数值,是否与你所下的位置上的坐标上的数值相等,相等说明连续,计数,不相等,说明不连续,不计数,退出循环
    3. 退出循环后,返回所计数的结果
    4. 最后,因为该函数计数时,是没有计算到,包含到,所下位置的本身个数,所以需要额外 + 1
    
    // 计算下的点位置上的,4个方向上的连续字符的 个数,
    // 注意编程上的图形界面的坐标是,与数学上的坐标 x,y 是相反的,横坐标为 y, 纵坐标为 x 的
    int chessCount(const int board[ROW][COL], const int row, const int col, Dir d)
    {
    	int count = 0;
    	int _x = x-1;
    	int _y = y-1;   // 拷贝一份坐标,用于移动,防止改变原来的,坐标,
    	                // 注意是在数组上的实际坐标,不是你输入的,数组从零开始
    
    	while (1)
    	{
    		switch (d)
    		{
    		case LEFT:        // 左边
    			_y--;
    			break;
    		case UP:          // 上边
    			_x++;
    			break;
    		case DOWN:        // 下边
    			_x--;
    			break;
    		case LEFT_DOWN:   // 左下
    			_x--;
    			_y--;
    			break;
    		case LEFT_UP:     // 左上
    			_x++;
    			_y--;
    			break;
    		case RIGHT:       // 右边
    			_y++;
    			break;
    		case RIGHT_DOWN:  // 右下
    			_x--;
    			_y++;
    			break;
    		case RIGHT_UP:    // 右上
    			_x++;
    			_y++;
    			break;
    		default:
    			break;
    		}
    
    
    		// 坐标越界了,跳出循环
    		if (_x < 0 || _x > row - 1 || _y < 0 || -y > col - 1)
    		{
    			break;
    		}
    
    		//if (board[_x][_y] != board[x - 1][y - 1])
    		//{
    		//	break;      // 表示数组上的数值也就是棋盘上的点,不连续了,不用加加了,跳出循环
    		//}
    
    		if (board[_x][_y] == board[x - 1][y - 1])
    		{
    			count++;     // 说明数组上的数值也就是棋盘上的点,连续,加加计数
    		} 
    		else  // 否则表示不相等,不连续,跳出循环
    		{
    			break; // 表示数组上的数值也就是棋盘上的点,不连续了,不用加加了,跳出循环
    		}
    
    
    	}
    
    	return count;    // 跳出循环,返回在连续的个数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    所有功能都实现了,可以运行该项目程序了,判断是否存在错误

    在这里插入图片描述


    五子棋完整代码实现

    这里采用了分布式文件系统,其中的代码大家只需要,根据对应的文件,复制粘贴内容,就可以了,使用 VS 运行了,注意:不要缺失了,不然,可能无法运行的

    game.h

    用于存放 :头文件的引入,宏定义,变量,函数的声明,结构体,枚举

    #pragma once
    #define  _CRT_SECURE_NO_WARNINGS  1
    
    #include
    #include
    #include
    
    
    #define ROW 15    // 棋盘行数
    #define COL 15    // 棋盘列数
    
    
    #define PLAYER1 1     // 定义标识用户 1,
    #define PLAYER2 2     // 定义标识用户 2
    
    
    
    // 棋局中的中情况的定义
    #define NEXT 0        // 表示没有结果,继续下棋
    #define DRAE 3        // 表示棋盘下满了,平局结束
    
    
    
    typedef enum Dir  // 枚举 坐标上的方向,注意,枚举以逗号分割开来
    {
    	LEFT,       // 左边
    	RIGHT,      // 右边
    	UP,         // 上边
    	DOWN,       // 下边
    	LEFT_UP,    // 左上边
    	LEFT_DOWN,  // 左下边
    	RIGHT_UP,   // 右上边
    	RIGHT_DOWN  // 右下边
    	// 注意 枚举中的最后一个属性,不要加逗号分隔开,不然会报错的
    }Dir;
    
    extern void menc();      // 打印游戏菜单
    extern void Game();      // 进入游戏
    extern void playerMove(int board[ROW][COL], const int row, const int col, const int who);    // 下棋
    extern int isOver(const int board[ROW][COL], const int row, const int col);        // 判断下一步后的,结果
    extern int chessCount(const int board[ROW][COL], const int row, const int col, Dir d);     // 计算,连续出现相同的个数,从而判断是否有五子连珠
    extern void showBoard(const int board[ROW][COL], const int row, const int col);    // 打印二维数组,棋盘
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    game.c

    存放五子棋中对应功能实现的函数

    #include "game.h"
    
    
    
    int x = 0;   // 定义全局变量,X 坐标
    int y = 0;   // 定义全局变量,y 坐标
    
    void menc()   // 游戏菜单
    {
    	printf("#########################\n");
    	printf("### 1.paly     0.Exot ###\n");
    	printf("##### Please Select #####\n");
    	printf("#########################\n");
    
    }
    
    void Game()
    {
    	int board[ROW][COL];                 // 使用二维数组,定义棋盘
    	memset(board, 0, sizeof(board));     // 初始化二维数组
    	int result = NEXT;                   // 游戏继续
    
    	do {
    		showBoard(board, ROW, COL);               // 打印棋盘
    		playerMove(board, ROW, COL, PLAYER1);      // 用户1 下棋
    		result = isOver(board, ROW, COL);      // 判断下了这一手,是否有结果,结果返回
    
    		if (NEXT != result)   // 根据判断后的返回值,判断是否出了结果,是否还需要继续,NEXT 表示继续
    		{
    			break;
    		}
    		showBoard(board, ROW, COL);               // 打印棋盘
    		playerMove(board, ROW, COL, PLAYER2);      // 用户2 下棋
    	    result = isOver(board, ROW, COL);      // 判断下了这一手,是否有结果,结果返回
    
    		if (NEXT != result)    // 根据判断后的返回值,判断是否出了结果,是否还需要继续, NEXT 表示继续
    		{
    			break;
    		}
    
    		
    
    	} while (1);
    
    	 
    	// 跳出循环,根据返回值,判断该局的,结果是,谁赢,谁输,还是平局
    	switch (result)
    	{
    	case PLAYER1:
    		showBoard(board, ROW, COL);               // 打印棋盘
    		printf("恭喜用户1,你赢了\n");
    		break;
    	case PLAYER2:
    		showBoard(board, ROW, COL);               // 打印棋盘
    		printf("恭喜用户2,你赢了\n");
    		break;
    	case DRAE:
    		showBoard(board, ROW, COL);               // 打印棋盘
    		printf("平分秋色,平局\n");
    		break;
    	default:           // 不可能会执行到的,这里设置是为了以防万一
    		break;
    	}
    	
    }
    
    
    
    // 下棋
    static void playerMove(int board[ROW][COL], const int row, const int col, const int who)
    {
    	while (1)
    	{
    		printf("player[%d] pleach Enter Your Pos#> ", who);  // 提示打印用户谁的输入
    		scanf("%d %d", &x, &y);       // 输入坐标
    
    
    		// 输入的坐标超出了棋盘,不合法,重新输入
    		if (x < 1 || x > row || y < 1 || y > col)
    		{
    			printf("超过棋盘了,请重新输入\n");
    			continue;    // 跳过本次循环,重新输入
    		}
    
    		// 输入的坐标位置,已经被占用了,不合法,重新输入
    		if (0 != board[x-1][y-1])     
    		{
    			printf("pos IsOccupied!\n");
    			continue;   // 跳过本次循环,重新输入
    		}
    
    
    		// 运行到这,说明该坐标没有问题,可以下入到棋盘中去了
    		board[x - 1][y - 1] = who;
    		break;    // 跳出循环
    
    	}
    
    }
    
    
    
    // 走一步,判断结果
    static int isOver(const int board[ROW][COL], const int row, const int col)
    {
    	// 从四个位置上判断,可能五子连珠的,可能性,当连续的相同的字符,数量个数为 5 时,表示有一方赢了
    
    	// 在此基础上都需要加上 1 ,因为本身的位置上的,没有包含计算上,是需要计算上的
    	// 左右横方向上的个数
    	int count1 = chessCount(board, row, col, LEFT) + chessCount(board, row, col, RIGHT) + 1;
    	// 上下纵方向上的个数
    	int count2 = chessCount(board, row, col, UP) + chessCount(board, row, col, DOWN) + 1;
    	// 左斜上方向上的个数
    	int count3 = chessCount(board, row, col, LEFT_DOWN) + chessCount(board, row, col, RIGHT_UP) + 1;
    	// 右斜下方向上的个数
    	int count4 = chessCount(board, row, col, LEFT_UP) + chessCount(board, row, col, RIGHT_DOWN) + 1;
    
    
    	// 只要在任意位置上,个数上 >= 5 就,实现了五子连珠,就有获胜了
    	if (count1 >= 5 || count2 >= 5 || count3 >= 5 || count4 >= 5)
    	{
    		if (board[x - 1][y - 1] == PLAYER1)
    		{
    			return PLAYER1;   // 表示用户1 赢了
    		}
    		else
    		{
    			return PLAYER2;   //否则就是 表示用户2 赢了
    		}
    	}
    
    
    	// 循环遍历二维数组(棋盘),判断棋盘是否存在满了,如果有一个棋位置,还可以下,就表示还可以继续下棋,
    	for (int i = 0; i < row; i++)
    	{
    		for (int j = 0; j < col; j++)
    		{
    			if (board[i][j] == 0)
    			{
    				return NEXT;   // 继续,棋盘没有满
    			}
    		}
    	}
    
    	return DRAE;        // 走到这表示棋盘满了,平局
    
    }
    
    
    
    // 计算下的点位置上的,4个方向上的连续字符的 个数,
    // 注意编程上的图形界面的坐标是,与数学上的坐标 x,y 是相反的,横坐标为 y, 纵坐标为 x 的
    int chessCount(const int board[ROW][COL], const int row, const int col, Dir d)
    {
    	int count = 0;
    	int _x = x-1;
    	int _y = y-1;   // 拷贝一份坐标,用于移动,防止改变原来的,坐标,
    	                // 注意是在数组上的实际坐标,不是你输入的,数组从零开始
    
    	while (1)
    	{
    		switch (d)
    		{
    		case LEFT:        // 左边
    			_y--;
    			break;
    		case UP:          // 上边
    			_x++;
    			break;
    		case DOWN:        // 下边
    			_x--;
    			break;
    		case LEFT_DOWN:   // 左下
    			_x--;
    			_y--;
    			break;
    		case LEFT_UP:     // 左上
    			_x++;
    			_y--;
    			break;
    		case RIGHT:       // 右边
    			_y++;
    			break;
    		case RIGHT_DOWN:  // 右下
    			_x--;
    			_y++;
    			break;
    		case RIGHT_UP:    // 右上
    			_x++;
    			_y++;
    			break;
    		default:
    			break;
    		}
    
    
    		// 坐标越界了,跳出循环
    		if (_x < 0 || _x > row - 1 || _y < 0 || -y > col - 1)
    		{
    			break;
    		}
    
    		//if (board[_x][_y] != board[x - 1][y - 1])
    		//{
    		//	break;      // 表示数组上的数值也就是棋盘上的点,不连续了,不用加加了,跳出循环
    		//}
    
    		if (board[_x][_y] == board[x - 1][y - 1])
    		{
    			count++;     // 说明数组上的数值也就是棋盘上的点,连续,加加计数
    		} 
    		else  // 否则表示不相等,不连续,跳出循环
    		{
    			break; // 表示数组上的数值也就是棋盘上的点,不连续了,不用加加了,跳出循环
    		}
    
    		
    
    	}
    
    	return count;    // 跳出循环,返回在连续的个数
    }
    
    
    
    
    // 打印二维数组
    void showBoard(const int board[ROW][COL], const int row, const int col)
    {
    //	printf("\e[1;1H\e[2J");  // 清屏
    	system("cls");
    	int i = 0;
    	printf("  ");   // 两个空格
    	for (; i < row; i++)    // 打印行号
    	{
    		printf("%3d", i+1);   // 预留三个位置
    	}
    	printf("\n");
    
    	for (i = 0; i < row; i++)
    	{
    		int j = 0;
    		printf("%2d ",i + 1);  // 预留两个位置
    		for (j; j < col; j++)
    		{
    			if (0 == board[i][j])    // 没有下过的位置打印 为 .
    			{
    				printf(" . ");
    			} 
    			else if (PLAYER1 == board[i][j]) {  // 用户1 的显示符号●
    				printf("● ");
    			}
    			else                          // 不等于,则是用户2 的显示符号 ○
    			{
    				printf("○ ");                 
    			}
    		}
    		printf("\n");
    
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263

    test.c

    main 函数的所在文件,用于测试五子棋,是否存在错误,因为有关代码封装的比较好,所以在该main 函数中代码量并不多。

    // #define  _CRT_SECURE_NO_WARNINGS  1
    
    #include"game.h"
    
    
    int main()
    {
    
    	int quit = 0;     // 定义一个标识,用于循环条件的退出
    	int select = 0;   // 对应功能上的选择
    
    	while (!quit)
    	{
    		menc();
    		printf("请输入你的选择: > ");
    		scanf("%d", &select);      // 选择游戏模式
    
    		switch (select)
    		{
    		case 1:
    			Game();     // 进去游戏
    			break;
    		case 0:
    			quit = 1;   // 通过改变循环条件,跳出循环,结束游戏
    			printf("ByeBye!ln\n");
    			break;
    		default:        // 选择错误重新选择
    			printf("Enter Error , Try Again!!! \n");
    			break;
    		}
    		
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    这便是五子棋实现的所有代码,运行看看

    在这里插入图片描述


    最后:

    限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见!

  • 相关阅读:
    关于Gson的TypeToken
    2022浙江省网络安全技能赛决赛
    【目标检测】超分重建对小目标检测有效性探究
    力扣176. 第二高的薪水
    C++ 【继承】
    SpringMVC处理Ajax请求及处理和响应json格式的数据
    Python字典,元组与集合
    计算机毕业设计Java校园闲置物品交换平台系统(源码+系统+mysql数据库+Lw文档)
    【蓝桥杯选拔赛真题48】Scratch跳舞机游戏 少儿编程scratch蓝桥杯选拔赛真题讲解
    【知识分享】Java获取当前周的开始时间结束时间
  • 原文地址:https://blog.csdn.net/weixin_61635597/article/details/126040277