我看你骨骼惊奇,送你本武林秘籍--《三子棋至多子棋的扩展》

 前言:

这篇文章将会手把手带你从程序设计需求出发,用代码思维来实现三子棋游戏。

每一步都会按照这个流程进行

1.要实现什么(程序设计需求)

例如:三子棋的游戏规则 

           用户的游玩方式

2.怎么实现

代码设计

首先我们要用到模块化编程

 我们初学者一开始的编程都是将代码放在一个.c文件里面 这样不利于代码后续的维护

以及不利于工程的合作(如果要合作完成项目就都得在一个文件里面写),既不方便代码的交流,又拖慢了工程进度,效率和产能都不高。

模块化编程

而把代码分装到.h和.c文件 并且把代码实现的功能分装到不同的.c文件  

.h文件放函数的声明

.c文件放函数的定义

在多人合作的时候,各自写的功能不同的函数就可以分装到不同的.c文件里,并在最后整合到一个工程下面,这样提高了效率,又提高了产能,且在后续也极容易维护代码。当要优化某个功能或修改某个功能实现的bug,就从对应的.c文件下更改即可

我们创建三个项目 分别是test.c 用来放游戏的启动代码  game.c用来放实现游戏的函数 game.h用来放函数的声明

Ⅰ编写游戏启动代码

1.实现什么--->玩家先看到游戏菜单--->   菜单中显示    选择进入或退出游戏 

--->  主程序做到完成一局游戏后可以继续游玩

2.程序设计

#include <stdio.h>
void menu()
{
	printf("**********************\n");
	printf("**1.play     0.exit **\n");
	printf("**********************\n");
}

int main()
{
	int input = 0;   
	
	do
	{
		menu(); //打印菜单
		printf("请输入-->> ");
		scanf("%d", &input);
		switch (input)
		{
		case 1: //注意case 1作为分支入口 case 和数字之间是有空格的,如果没有,编译器无法解析
			game();  //调用游戏函数,开始游戏
			break;
		case 0:
			printf("exit\n");
			break;
		default:
			printf("please try again\n");
		}
	} while (input);    //注意while后有分号
	return 0;
}

do while语句来实现玩完一局游戏不过瘾可以继续玩

且实现在代码一运行起来时直接执行一次循环体中的内容而不是先判断。

而do while的循环体内就放整个游戏的启动代码

menu()函数实现菜单的打印

玩家选择----》用switch分支语句实现

这里同时将input(玩家的输入也即选择)作为while循环的入口和switch分支语句的入口

好处是当玩家选择1时可以进入游戏(  进到case 1  调用game()函数)

而当玩家输入非0的数字时仍可以进入循环中,即仍停留在菜单界面。

 Ⅱ游戏函数的实现

一.

实现棋盘的打印

1.实现什么---》 棋盘在最开始只有分割线 里面存放空格 

2.怎么实现  ---》 从最简单的三子棋开始 就是打印一个3x3的九宫格  对于棋盘的存储功能,我们用二维数组来实现,对于棋盘的格子,线条界面,我们用循环来编写

#include <stdio.h>

#define ROW 3     //应输入表达式E0029报错  一般情况是宏定义常量加了分号,去掉就好
#define COL 3

void InitBoard(char board[ROW][COL], int row, int col);
void DisplayBoard(char board[ROW][COL], int row, int col);

在game.h声明函数

因为我们后续要扩展为多子棋,所以我们用宏定义常量来表示棋盘的行和列

在传参时,我们要访问到二维数组上的元素,并且要通过行和列来访问(数组遍历)

所以既要传数组名 又要传行和列

因为在test.c 和game.c文件中都会用到printf 函数 都要引用<stdio.h>头文件

且因为宏定义常量的使用也需要引用头文件

引用自己的头文件的格式是

#include "game.h"    

用双引号引头文件 ,结尾没有分号

所以为了简洁

我们将所有需要调用的库函数的头文件都放在game.h中 这样一来在test.c 和game.c中只要引用一个game.h文件即可

在game.c中 实现函数的定义和调用

先在game函数中创建数组,然后调用初始化棋盘和打印棋盘函数


void game()
{
	
	char board[ROW][COL] = { 0 };
	InitBoard(board, ROW, COL);//初始化棋盘
	DisplayBoard(board, ROW, COL);//打印棋盘
	
}

在game.h文件声明

 

棋盘的初始化
 

两个for循环遍历行和列  并给数组赋值' '  字符空格,注意字符要加单引号

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] = ' ';  //初始化棋盘,将棋盘的位置都设为空格
		}
	}
}

棋盘的打印

要打印的棋盘理论上张这样  

我们用循环来实现打印

因此要对这个棋盘进行拆分

现在我们先将一行(对ROW行的循环)棋盘分成两个部分

一个是数据行(顾名思义用来存放下的棋子的数据)

一个是分割线 

这里还要注意分割线在最后一行是不打印的,所以在循环中可以通过加if()语句来判断

接下来进一步对列(COL)的循环进行拆分

对于数据行我们将 空空空竖杠 即(   |) 看作一组 但是在最后一组中竖杠不打印(if语句来实现)

对于分割行 将杠杠杠竖杠 即(---|)看作一组  同样在最后一组不打印竖杠(if语句来实现)

下面进行代码编写

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函数打印字符串直接用""引用
			     
		}
		printf("\n");  //注意一行完成打印要进行分行
		
//分割线的打印
		for (j = 0; j < COL; j++)
		{
			if (i < ROW - 1)    //分割线在最后一行不打印
			{
				printf("---");
				if (j < COL - 1)    //竖杠最后一列不打印
					printf("|");
			}
		}
		printf("\n");   //一行分割线打印完成也要进行分行
	}
}

这里分享个我在编写时的错误

就是在打印字符的时候可以

1.     printf("%c", '|');

这样以单字符形式打印

也可以直接按字符串的打印形式

2.      printf("|");

这样子打印

但是在1.这种打印的方式中 一定要注意竖杠要加上单引号 表示字符  不然编译器会报错

二.游戏过程的实现

1.要实现什么---》 先玩家输入 ---》 棋盘显示玩家下的棋子   ---》

电脑随机输入 ---》显示电脑下的棋子

在玩家和电脑下完每一步棋子之后都判断游戏是继续还是有一方胜利还是平局

如果是继续 就继续重复上面的步骤

如果是哪一方赢了就打印相应的结果(平局同理)

2.怎么实现

我们继续在game()函数中完善代码,进行整个游戏过程的代码实现

要实现不断下棋,我们就把所有函数都放进while(1)的循环中,当游戏结束就用break跳出循环

跳出循环后我们再判断是哪一种情况

我们用字符类型 ret 来接受 判断输赢的函数 IsWin()的返回值

这样就可以根据返回值来判断结果

这里我们暂时不设置返回值是什么

void game()
{
	char ret = 0;
	char board[ROW][COL] = { 0 };
	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 !=    )
			break;
			//电脑下棋
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret !=    )
			break;
	}
if (ret ==    )
		printf("player win!\n");
	else if (ret ==    )
		printf("computer win!\n");
	else
		printf("平局\n");

先在game.h函数声明全部的函数

 这里面的<stdlib.h>和<time.h>函数是生成随机数要引用的头文件

我们后续会详说

接着继续在game.c文件完善游戏函数

玩家移动函数

理清思路:

首先提示输入几行几列

我们要考虑到,玩家不知道数组元素下标从0开始,所以在对数组进行访问的时候要做相应的变化

比如玩家输入x=1  y = 2 (一行二列) 对应到实际的二维数组,访问的其实是board[0][1]

上的元素,也即board[x-1][y-1]


判断输入的合法性
1 选择的位置是空格,没有被占用 2.选择的位置是棋盘中的位置
 因为存在玩家乱输入值的情况,所以要在合法输入值的情况下才能继续程序
否则就继续循环

而当玩家输入的坐标是违规的时候就要给相应提示,并且再次进入到玩家输入的循环中

所以我们依旧用while(1)循环来包含玩家输入的代码

当玩家输入成功就break跳出来
 

我们将玩家下的棋子定作字符*  也即 '*'

下面是代码实现

void PlayerMove(char board[ROW][COL], int row, int col)
{
	
	int x = 0;
	int y = 0;
	printf("请玩家输入-->  \n");
	while (1)
	{
		scanf("%d %d", &x, &y);  //sacnf函数输入几个数的时候要用逗号隔开
		if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//输入坐标在棋盘内
		{
			//在玩家输入到棋盘内的位置时判断位置是否被占用
		    if (board[x - 1][y - 1] == ' ')  //可落子
			{
				board[x - 1][y - 1] = '*';  
				break;
			}
			else
				printf("此处被占用 \n");//选择位置被占用
			
		}
		else
			printf("输入坐标非法,请重试 \n");
	}
}

电脑移动

理清思路:

要电脑随机下子,我们就要生成随机数
生成随机数在0~ROW-1  0~COL-1 (符合数组访问元素下标)这时就要用到取余%    

对随机数取ROW 的余 就会得到 0 ~ROW-1 这些数 
电脑也要判断落子处是否为空格

这是因为生成的随机数坐标是可能恰好在已经下过的地方的

当这一次生成的随机坐标不符号要求时,继续生成随机数坐标

所以将生成随机数坐标放进while(1)循环,当生成合法坐标时落子并break跳出循环

我们将电脑的棋子定作字符#    也即  '#'

随机数的生成

rand 函数会随机生成  0--32767之间的数字

在使用rand()生成随机数函数的时候要先对函数进行初始化,即给生成的随机数定一个起点(这是规定)

也就是在调用rand()函数之前要先用srand()完成随机数的初始化

而srand()的初始化也要接受一个随机数

而要使生成的随机数每次都不同,我们用时间戳来给这个随机数赋值

时间戳就是

调用计算机时间需引用头文件<time.h>

因为我们只需要一个随机数的起点 所以srand只使用一次即可,将其放在test.c文件中的函数启动main函数里面

srand((unsigned int) time(NULL));

这个就是对随机数进行起点的初始化代码

因为time函数的返回值类型是time_t

unsigned int给time强制转换类型;time指针为空指针类型

至于为什么这么设置我们暂时不用深究

对时间戳的解释

在计算机中,「时间戳」一般是指 Unix 时间戳,即自从 Unix 纪元(格林威治时间 1970 年 1 月 1 日 00:00:00)到当前时间的秒数

通俗来讲就是一个当前时间的标志

下面是代码实现

void ComputerMove(char board[ROW][COL], int row, int col)
{
	
	while (1)
	{
		int x = rand() % row;   //电脑生成的坐标要在循环里面
		int y = rand() % col;   //不然生成一次不成功又不会继续生成的话,游戏就没法继续下去了
		if (board[x][y] == ' ')//是空的才能下
		{
			board[x][y] = '#';
			break;
		}
	}
	
}

最后就是判断输赢以及游戏是否继续的函数的实现

IsWin函数的实现

 

我们先设计IsWin()函数的返回值。

玩家赢返回'*'
 电脑赢返回'#'
平局返回'Q'
 游戏继续返回'C'
  将玩家和电脑输出的标志作为返回值的好处是在判断的时候可以直接返回用于判断的任意一个元素

完善game()函数的代码逻辑

void game()
{
	char ret = 0;
	char board[ROW][COL] = { 0 };
	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')
			break;
			//电脑下棋
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*')
		printf("player win!\n");
	else if (ret == '#')
		printf("computer win!\n");
	else
		printf("平局\n");

}

实现IsWin()函数

我们要判断行 列 和 两个斜对角线上的三个(多个)字符是否是相同的

前两个可以用循环来实现,后两个可直接用if函数判断

这里要注意的是在满足三个连成直线的字符相等的时候还要同时满足这个字符不是空格

最后要实现的是平局和继续游戏的判断

对于平局 ,我们可以对整个数组进行遍历 当发现没有一个格子是空格时,就是平局

我们可以额外编写个Is_Full()函数来进行判断

而继续游戏的返回值只要放在最后面即可 因为当前面的任意一个if函数成功返回了相应字符

都会直接结束这个IsWin函数,也就轮不到'c'这个继续游戏标志字符的返回了

反之,当前面的if函数都没有成功返回,代码执行到最后只剩return 'c';

游戏也就自然继续进行了

Is_Full() 函数的实现

int Is_Full(char board[ROW][COL], int row, int col)
{
	//满了返回1
	//没满返回0
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')  //只要找到有一个空格就返回0,没满,结束函数
			    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][0] != ' ')
		{
			return board[i][0];  
		}
	}

	//判断列
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][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[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];
	          

	if (Is_Full(board, row, col) == 1)    
		return 'Q';
	return 'C';
}

当然,这样子写判断,其实把代码写死了,只能判断三子棋

接下来我们进行扩展

三子棋扩展为多子棋

思维实现:

多子棋的判断
拆分判断元素  实质为判断相邻的三个(n个)棋子是否相等
拆分为判断相邻的两个棋子是否相等,并且将这个判断放入循环 循环次数比判断的个数小1
我们可以用拼积木的方式去理解
假设有三块相同的积木 而现在要把他们拼成一个整体(一条直线)
每两块积木间需要一个用来连接的积木
而三块积木的拼接就需要两个连接积木、
再转化为三子棋 就是判断胜利的情况就是找到两个等号(==)将数组上三个元素的大小关系建立起来
于是就可以推广到多子棋 无非就是找拼接用的积木数量够不够多而已
在循环体中加入计数器count即可实现

这里我们在前面的基础上只要修改IsWin()函数即可实现多子棋

char IsWin(char board[ROW][COL], int  row, int col)
{

	//判断行
	int i = 0;
	
	for (i = 0; i < row ; i++)  //但是行的遍历判断总次数没少,因为要看完所有行嘛
	{
		int j = 0;
		int count = 0;  //count 放在这里初始化,在每遍历一行后都会重置
		for(j = 0; j < col-1;j++ )//判断条件比总数少一,是因为判断三个数相等其实只要两个等号
		{
			if (board[i][j] == board[i][j + 1] && board[i][j] != ' ')
			{
				
				count++;
			}
			if (count == row-1)  //count 就是用来计算累积了几个等号的
				return board[i][j];
		}
		
	}

	//遍历列
	//逻辑与判断行相同

	for (i = 0; i < col ; i++)  
	{
		int j = 0;
		int count = 0;  
		for (j = 0; j < row - 1; j++)  //注意此时i是列 j是行
		{
			if (board[j][i] == board[j + 1][i] && board[j][i] != ' ')
			{
				count++;
			}
			if (count == row - 1)  
				return board[j][i];
		}

	}

	//判断对角线
	//   \对角线
	int count = 0;
	int j = 0;
	for (i = 0; i < row - 1; i++)
	{
		
		
		if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' ')
		{
			count++;
			if(count < row - 1)
			   j++;
		}
		if (count == row - 1)
			return board[i][j];
		
	}


	//   /对角线

	int countb = 0;
	j = col - 1;
	for (i = 0; i < row - 1; i++) //三子棋时进行2次循环
	{
		
	
		if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ')
		{
			countb++;
			if(countb < row -1)  //这样在判断第二个等号完后就不会改变board[i][j]的数
			   j--;
		}
		if (countb == row - 1)
			return board[i][j];
	}



	if (Is_Full(board, row, col) == 1)    
		return 'Q';
	return 'C';
}

改进后的代码在判断行与列的时候还是比较简单的

但是在判断对角线的时候就复杂许多

以其中一个对角线的判断循环为例

因为对角线的每一组等号的判断都涉及两行两列(这点和行,列的判断不同)

所以count(计数器)的初始化不能放在循环体内,否则在下一个循环就会被重置。

其次因为计数器放在for循环外部的原因,且两个对角线的判断都涉及计数器的使用,

所以我们创建count  和countb来分别用于两个对角线的判断

然后在对角线的判断中,我们不能再使用两个for循环来遍历数组

因为有从右上到左下的斜对角线,行和列的坐标变换是不一样(不同步)的,如果单纯用两个循环,很难实现这种复杂的特殊情况的判断

而是只用一个循环,并在if()判断语句中实现下标位置的改变

所以我们的数组列下标 j 也要初始化在for循环外部

而如果要实现一组等号的判定完成后,进行下一组的判定,我们就可以将 j 的变化放进和count/countb 一起的if函数内部

并且为了在完成最后一组等号的计算后,不改变原来的数组下标,即为了不影响最终返回值的结果,我们还要给下标 j 的改变再加个if判断 

总结:

  三子棋虽然简单,但是运用到的知识非常多,在进行整个游戏的实现时,要时刻保持目的性,即时刻想着我要实现什么,我要怎么实现,可以运用哪些知识。

   最后眼看不如手动,许多知识你看似懂了,但是只有自己动手做起来才知道自己的分量。

且不要害怕犯错,多运用调试功能自己解决问题,实在找不到问题再去请教别人。

   真不要怕自己会被别人嘲笑,比如我在调试代码的时候就发现我的if函数进不去,而是直接跳过了,自己排了两个小时,没发现问题,就去问老师。最后发现就是if()后面多加了个分号。这是因为现在编译器比较灵敏,当你代码没写完整的时候就会报错,可能当时我看编译器报错了,然后又看到括号后面少了个分号,就完全没在意if()后面是不能加分号的。这两个小时也算是血的教训吧,编写代码的时候真的不能心急,要确保每一步都符合语法规则且符合逻辑。虽然两个小时都没找出bug,但是在这两个小时里面,我重复过了很多遍代码,确定了每一个代码块实现的功能,谁又能说这些“浪费”的时间是完全没用的呢。在调试代码的过程中你会学到很多很多东西,这些都是被人没法教你的。

  希望这篇文章对你有所帮助,感谢您的阅读。

                                                      可不可以给个点赞呢qwq

 

下面是完整代码

game.h文件

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 3     //应输入表达式E0029报错  一般情况是宏定义常量加了分号,去掉就好
#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);
char IsWin(char board[ROW][COL], int  row, int col);
int Is_Full(char board[ROW][COL], int row, int col);

test.c文件

#include "game.h"


void menu()
{
	printf("**********************\n");
	printf("**1.play     0.exit **\n");
	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("exit\n");
			break;
		default:
			printf("please try again\n");
		}
	} while (input);
	return 0;
}

game.c文件

#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 ", board[i][j]);
			if(j < COL - 1)
				printf("|");
			     
		}
		printf("\n");
		//打印分割线
		for (j = 0; j < COL; j++)
		{
			if (i < ROW - 1)
			{
				printf("---");
				if (j < COL - 1)
					printf("|");
			}
		}
		printf("\n");
	}
}


void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("请玩家输入-->  \n");
	while (1)
	{
		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] = '*';
				break;
			}
			else
				printf("此处被占用 \n");//选择位置被占用
			
		}
		else
			printf("输入坐标非法,请重试 \n");
	}
}

void ComputerMove(char board[ROW][COL], int row, int col)
{
	
	while (1)
	{
		int x = rand() % row;   //电脑生成的坐标要在循环里面
		int y = rand() % col;   //不然生成一次不成功又不会继续生成的话,游戏就没法继续下去了
		if (board[x][y] == ' ')//是空的才能下
		{
			board[x][y] = '#';
			break;
		}
	}
	
}

int Is_Full(char board[ROW][COL], int row, int col)
{
	//满了返回1
	//没满返回0
	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++)  //但是行的遍历判断总次数没少,因为要看完所有行嘛
	{
		int j = 0;
		int count = 0;  //count 放在这里初始化,在每遍历一行后都会重置
		for(j = 0; j < col-1;j++ )//判断条件比总数少一,是因为判断三个数相等其实只要两个等号
		{
			if (board[i][j] == board[i][j + 1] && board[i][j] != ' ')
			{
				
				count++;
			}
			if (count == row-1)  //count 就是用来计算累积了几个等号的
				return board[i][j];
		}
		
	}

	//遍历列
	//逻辑与判断行相同

	for (i = 0; i < col ; i++)  
	{
		int j = 0;
		int count = 0;  
		for (j = 0; j < row - 1; j++)  //注意此时i是列 j是行
		{
			if (board[j][i] == board[j + 1][i] && board[j][i] != ' ')
			{
				count++;
			}
			if (count == row - 1)  
				return board[j][i];
		}

	}

	//判断对角线
	//   \对角线
	int count = 0;
	int j = 0;
	for (i = 0; i < row - 1; i++)
	{
		
		
		if (board[i][j] == board[i + 1][j + 1] && board[i][j] != ' ')
		{
			count++;
			if(count < row - 1)
			   j++;
		}
		if (count == row - 1)
			return board[i][j];
		
	}


	//   /对角线

	int countb = 0;
	j = col - 1;
	for (i = 0; i < row - 1; i++) //三子棋时进行2次循环
	{
		
	
		if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ')
		{
			countb++;
			if(countb < row -1)  //这样在判断第二个等号完后就不会改变board[i][j]的数
			   j--;
		}
		if (countb == row - 1)
			return board[i][j];
	}




	if (Is_Full(board, row, col) == 1)    
		return 'Q';
	return 'C';
}



//game函数包含数组的定义初始化,因为数组的使用与棋盘的打印是联系在一起的
void game()
{
	char ret = 0;
	char board[ROW][COL] = { 0 };
	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')
			break;
			//电脑下棋
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*')
		printf("player win!\n");
	else if (ret == '#')
		printf("computer win!\n");
	else
		printf("平局\n");

}

  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值