保姆级教学!1.5万字带你理解N子棋!不进来看看吗?

1.前言

(1)知识清单

二维数组
循环和选择语句
系统生成随机数
自定义函数

(2)模块化编写

为什么要分文件来写函数?
一个程序应该有最基本的逻辑和各种不同的功能。
初学时的代码复杂度低,但随着学习的深入,代码会越来越复杂。到了工作的时候,一个程序更要进行分工编写。
所以就把基本逻辑模块和功能模块分开,用函数进行封装。
我们本次用到了三个文件。
在这里插入图片描述

test.c包含程序最基本的运行逻辑,调用其他的功能构成程序。
game.h包含用到的函数、关键常量、以及头文件的声明。

#define ROW 3//game.h
#define COL 3
#define NZIQI 3
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
void menu();
void menu1();
void printqipan(char board[ROW][COL], int row, int col);
void chushihua(char board[ROW][COL], int row, int col);
void playermove(char board[ROW][COL], int row, int col, char player);
void computermove(char board[ROW][COL], int row, int col);
char panduan(char board[ROW][COL], int row, int col, int nziqi);

行数,列数,连棋个数用define声明的常量来表示(目前是三子棋,棋盘为3*3)
game.c实现程序的各种功能。
如何将这三个文件链接起来?
使用包含头文件的方式进行连接。

#include"game1.h"

在另外两个文件前面加上这行代码即可

(3)程序基本要点

n子旗完全采用系统随机数的方式下棋。
棋盘小还好,棋盘一大就成了人工智障,走棋完全没有章法。
于是我做了两个功能:人机对战和玩家对战
同时,n子棋需要有很强的拓展性,只需要修改一个数字,便能改变棋盘规模,游戏规则。

2.程序清单

(1)打印菜单(包括函数menu1,menu2)

游戏开始运行时,我们要打印一个菜单展示交互页面,让玩家输入选项的值和系统交互。创建一个menu函数进行是否开始游戏的选择。

void menu()
{
	printf("**************************************\n");
	printf("************    1.play    ************\n");
	printf("************	0.exit	  ************\n");
	printf("**************************************\n");
	printf("请输入:");
}	

同样,开始游戏后的PVE,pvp两个模式的选择,也需要一个菜单,创建函数menu1

void menu1()
{
	printf("**************************************\n");
	printf("************    1.pvp     ************\n");
	printf("************	0.pve	  ************\n");
	printf("**************************************\n");
	printf("请输入:");
}

这两个函数不需要返回值,用void类型。

(2)基本逻辑

要注意的是,在这个程序中,有些循环的终止条件不能用变量来控制。
我们可以用while(1)达到无限循环,然后用if语句判断是否break达到结束循环的方式。

while(1)
{
if(结束条件)
	{
	break;
	}
}

我们将基本逻辑写在函数test中。
打印菜单1后,我们要使用变量input接受用户输入的值,然后根据值执行逻辑。
(1)开始游戏
此时用户要进行第二次输入来进行模式选择。
(2)退出游戏
让程序结束。
(3)输入错误
需要让用户重新输入。
此时的情景正适合switch函数。

scanf("%d", &input);
	switch (input)
	{
	case 1:
		printf("开始游戏\n");
		printf("请选择模式\n");
		menu1();
		
		break;
	case 0:
		printf("退出游戏\n");
		break;
	default:
		printf("输入错误,请重新输入\n");
		break;
	}

如何在打完一局之后继续进行是否游戏的选择?
使用do while函数,先执行后判断,判断input的值是否为0,值为0,循环结束,刚好对应退出游戏。

int input = 0;
	menu();
	do
	{
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			printf("请选择模式\n");
			//此处进行pve和pvp的选择	
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);

判断pvp和pve的函数也是一样的逻辑
将两次判断逻辑嵌套,就构成了最基本的运行逻辑。

void test()
{
	int input = 0;
	menu();
	do
	{
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			printf("请选择模式\n");
				int input1 = 0;
				menu1();
				do
				{
					char a = '\0';
					scanf("%d", &input1);
					switch (input1)
					{
					case 1:
						printf("pvp\n");
						a=gamepvp();
						shengfu1(a);
						menu();
						break;
					case 0:
						printf("pve\n");
						a=gamepve();
						menu();
						shengfu2(a);
						break;
					default:
						printf("输入错误,请重新输入\n");
						break;
					}
				} while (input1 != 0 && input1 != 1);
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}

这其中有很多函数会在下面的模块讲到。

(3)运行逻辑

在开始游戏后分为两种模式。
分为pvp和pve,要用两个函数去实现。函数名为gamepvp和gamepve。
我们定一个标注,在玩家对战时,玩家一的棋子为*,玩家二棋子为#。人机对战时,玩家棋子为*,电脑棋子为#。

char player1 = '*';//玩家对战
char player2 = '#';

char player1 = '*';//人机对战

在下棋之前,我们要创建好对应规格的棋盘,对其初始化。
如何创建好对应规格的棋盘?
用二维数组正合适。我们用空格表示棋盘中的空位,将数组定义为char类型。

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

之后便进行双方轮流下棋,在每一方下完之后判断胜负,并打印棋盘。

void playermove(char board[ROW][COL], int row, int col, char player);
//玩家下棋
void computermove(char board[ROW][COL], int row, int col);
//电脑下棋

判断胜负用函数panduan去实现

char panduan(char board[ROW][COL], int row, int col, int nziqi);

用while(1)将上述下棋的过程写入循环。
判断胜负之后,用变量ret接受判断函数的返回值(判断函数返回值类型为char),如果不为‘A’,说明游戏还未结束
否则打印棋盘并break跳出循环(结束了总要让你知道结果啊)。

char game()
{
	while (1)
	{
		ret = panduan(board, ROW, COL, NZIQI);
		if (ret != 'A')
		{
			printqipan(board, ROW, COL);
			break;
		}
	}
}	

跳出循环后,将ret的值作为返回值,传递给变量a(a在函数test中),a作为参数函数shengfu的参数,根据a的值来打印游戏结果。

char game()
{	
	while (1)
	{
		ret = panduan(board, ROW, COL, NZIQI);
		if (ret != 'A')
		{
			printqipan(board, ROW, COL);
			break;
		}
	}
	return ret;
}

pvp和pve的函数略有不同,但大体思路一致。
这其中的大部分功能,我们都会以函数的形式来实现。

char gamepve()//函数gamepvp
{	
	char player1 = '*';
	char ret = '\0';
	char board[ROW][COL];
	chushihua(board, ROW, COL);
	while (1)
	{
		printqipan(board, ROW, COL);
		printf("玩家下棋\n");
		playermove(board, ROW, COL, player1);
		ret = panduan(board, ROW, COL, NZIQI);
		if (ret != 'A')
		{
			printqipan(board, ROW, COL);
			break;
		}
		computermove(board, ROW, COL);
		ret = panduan(board, ROW, COL, NZIQI);
		if (ret != 'A')
		{
			printqipan(board, ROW, COL);
			break;
		}
	}
	return ret;
}
char gamepvp()//函数gamepve
{
	char player1 = '*';
	char player2 = '#';
	char ret = '\0';
	char board[ROW][COL];
	chushihua(board, ROW, COL);
	while (1)
	{
		printqipan(board, ROW, COL);
		printf("玩家一下棋\n");
		playermove(board, ROW, COL, player1);
		ret = panduan(board, ROW, COL, NZIQI);
		if (ret != 'A')
		{
			printqipan(board, ROW, COL);
			break;
		}
		printqipan(board, ROW, COL);
		printf("玩家二下棋\n");
		playermove(board, ROW, COL, player2);
		ret = panduan(board, ROW, COL, NZIQI);
		if (ret != 'A')
		{	
			printqipan(board, ROW, COL);
			break;
		}
		return ret;
	}
	return ret;
}

下面就是一一实现这些函数的功能。

(4)初始化(包括函数chushihua)

每次下完棋重新开始游戏,我们都需要一个崭新的棋盘。
这就需要初始化棋盘,将棋盘清空,也就是将二维数组中的每个元素设置为空格字符。
创建一个函数,名字叫chushihua
传递数组,棋盘数据,进行初始化。

void chushihua(char board[ROW][COL],int row,int col)
{
	for (int a = 0; a < row; a++)
	{
		for (int b = 0; b < col; b++)
		{
			board[a][b] = ' ';
		}
	}
}

(5)打印棋盘(包括函数printqipan)

开始游戏前和游戏过程中,我们需要打印棋盘让玩家知道当前棋盘上的状况。
如何打印一个这样的棋盘呢?
为了让棋盘看起来方正,打印数组时在字符的两侧分别加一个空格。

printf("% c ", board[a][b]);

每次打印完一个字符,放置一个字符|,每行的最后不放置字符,打印完一行后换行。

for (int b = 0; b < col; b++)
	{
		printf("% c ", board[a][b]);
		if (b != col - 1)
		{
			printf("|");
		}
	}
printf("\n");

每行打印完,打印分割行。最后一行打印之后不打印分割行。

if (a != row - 1)
	{
		for (int c = 0; c < col - 1; c++)
		{
			printf("---");
				if (c == col - 2)
				{
					printf("--\n");
				}
		}
	}

然后根据行数重复上述两个操作,直到打印完成
我们来看看效果
3乘3
在这里插入图片描述
10乘10
在这里插入图片描述

因为每次下棋都要打印棋盘,我们可以在每次打印之前,清空屏幕。
使用系统命令system(“cls”);来清屏。
需要头文件windows.h
创建一个函数,名字叫printqipan
代码如下。

void printqipan(char board[ROW][COL], int row, int col)
{
	system("cls");
	for (int a = 0; a < row; a++)
	{
		for (int b = 0; b < col; b++)
		{
			printf("% c ", board[a][b]);
			if (b != col - 1)
			{
				printf("|");
			}

		}
		printf("\n");
		
		if (a != row - 1)
		{
			for (int c = 0; c < col - 1; c++)
			{
				printf("---");
					if (c == col - 2)
					{
						printf("--\n");
					}

			}
		}
	}
}

(6)玩家执子(包括函数playermove)

void playermove(char board[ROW][COL], int row, int col, char player)

流程:接受玩家输入的坐标,判断坐标是否合法且未被占用。
要注意的是,在玩家看来,棋盘是从1开始的
但在电脑看来,棋盘是从0开始的(数组元素首下标为0)
所以接收玩家输入的值后应该减一得到数组的位置。
先判断坐标是否合法,如果合法就进入下一步的判断

int a = 0, b = 0;
scanf("%d%d", &a, &b);
	if (a >= 1 && a <= row && b >= 1 && b <= col)
	{
		
	}
	else
	{
		printf("坐标非法");
	}

如果坐标被占用,就让玩家重新输入。
未被占用,则在二维数组放置对应的字符,跳出循环。
player为当前执棋玩家所代表的字符。(在运行逻辑中定义)

if (board[a - 1][b - 1] == ' ')
{
	board[a - 1][b - 1] = player;
	break;
}
else
printf("输入坐标已被占用\n");

切换执子的玩家,只需要改变玩家字符的参数即可。
下面是合并的代码

void playermove(char board[ROW][COL], int row, int col, char player)
{
	int a = 0, b = 0;
	while (1)
	{
		scanf("%d%d", &a, &b);
		if (a >= 1 && a <= row && b >= 1 && b <= col)
		{
			if (board[a - 1][b - 1] == ' ')
			{
				board[a - 1][b - 1] = player;
				break;
			}
			else
				printf("输入坐标已被占用\n");
		}
		else
		{
			printf("坐标非法");
		}
	}
}

(7)电脑执子(包括函数computermove)

之前我们说过,电脑下棋坐标由随机数生成。
下面讲随机数的生成
电脑每次开机都会有一个随机的种子
使用rand函数,程序就会根据种子提供一组数

a = rand()

但是每次种子相同的情况下程序重新运行提供的数列都是一样的。
想改变生成数列,就需要改变种子
除了重启电脑,还可以将一个变化的量设置为种子,这用到srand函数,最好方法是根据系统时间,这用到了函数time。
使用time(NULL)生成系统的时间戳,表示当前时间距离UTC时间 1970-01-01 00:00:00的秒数。
使用time函数需要包含头文件time.h,使用rand,srand函数需要包含头文件stdlib.h
因为srand函数的参数是无符号整形
所以使用强制转换将时间戳的数据类型设置为无符号整形。
这样就在程序每次运行的时候设置了一个随机的种子。
合并起来,设置种子的函数就是

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

设置完了种子,就可以使用rand函数了。
每次程序运行只会设置一个种子,所以将设置种子的函数放在最前面。

int main()
{
	srand((unsigned int)time(NULL));
	test();
	return 0;
}

没错,main函数就这么点内容
如何限制随机数的范围?使用取模运算符。

int a = rand() % col;
int b = rand() % row;

同样,随机数也应该有效。使用和玩家执棋一样的判断方式。
我们也可以加一点有趣的小功能。
使用sleep函数让程序停顿,看起来就像是电脑在思考🤔,后面的参数表示停顿的毫秒数,我们设置为1000。sleep函数同样包含在头文件windows.h头文件中。
代码如下

void computermove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋");
	Sleep(1000);
	while (1)
	{
		int a = rand() % col;
		int b = rand() % row;
		if (board[a][b] == ' ')
		{
			board[a][b] = '#';
			break;
		}
	}
}

(8)胜负判断(包括函数panduan)

这应该是n子棋中关键的部分,我们需要找到一种高效,通用,适应各种棋盘和玩法的判断方式。
判断分为四个模块,行,列和两种方向的对角线。
我们使用一个叫panduan的函数完成这个功能,用这个函数的返回值来决定程序否继续运行。函数将数组,行数,列数和胜负条件作为参数。

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

}

我们先创建四个变量,将他们的初值设为1,作为计数器。

int flag1 = 1, flag2 = 1, flag3 = 1, flag4 = 1;

在一条线上判断相邻的两个位置内容是否相同且不为空格,如果相同且不为空格,则计数器加一,不符合,则计数器重置。

for (int a = 0; a < col - 1; a++)
		{
			if ((board[b][a] == board[b][a+1]) && (board[b][a] != ' '))
			{
				flag2++;
			}
			else
			{
				flag2 = 1;
			}

如果n-1次判断都成立,则说明这条线上有n个相同的棋子连在一起,游戏结束,其中一方获胜。如果有一种棋子胜利,那么函数就返回这个棋子代表的字符。

if (flag2 == nziqi)
			{
				return board[b][a];
			}

一条线判断完之后,转到下一条线,计数器重置为1,重复上述的判断。
下面是完整的代码(行和列之间差距很小)

for (int b = 0; b < row; b++)//判断行
	{
		flag2 = 1;
		for (int a = 0; a < col - 1; a++)
		{
			if ((board[b][a] == board[b][a+1]) && (board[b][a] != ' '))
			{
				flag2++;
			}
			else
			{
				flag2 = 1;
			}
			if (flag2 == nziqi)
			{
				return board[b][a];
			}
		}
	}
for (int a = 0; a < col; a++)//判断列
	{
		flag1 = 1;
		for (int b = 0; b < row-1; b++)
		{
			if ((board[b][a] == board[b + 1][a]) && (board[b][a] != ' '))
			{
				flag1++;
			}
			else
			{
				flag1 = 1;
			}
			if (flag1 == nziqi)
			{
				return board[b][a];
			}
		}
	}

然后就是对角线的判断了,和行和列有所不同。
下面用几张图展示行列和对角线的判断的区别
假设这是棋盘
在这里插入图片描述
在这里插入图片描述

我们将圆圈设为起始点。
此时的只需要判断三条线即可判断完整个棋盘,且起始点都在一条直线上
但是在对角线情况下,情况就不一样
在这里插入图片描述

可以看到起始点不是在一条直线上了。
这个时候就需要多一层循环来确定起始点。
和前面的判断一样,再复述一遍对角线的判断过程
下方函数可以确定判断的起始点(其实起始点多了几个,会增加判断时间,但是不影响判断的结果,代码的优化我会再下一篇写出来)

for (int b = 0; b < row-1; b++)
	{
		flag3 = 1;
		for (int a = 0; a < row - 1; a++)
		{
			
		}
	}

然后进行一条线上的判断

for (; (a < col - 1) && (b < row - 1); a++, b++)
			{
				if ((board[b][a] == board[b + 1][a + 1]) && (board[b][a] != ' '))
				{
					flag3++;
				}
				else
				{
					flag3 = 1;
				}
				if (flag3 == nziqi)
				{
					return board[b][a];
				}
			}

组合起来就是这样

for (int b = 0; b < row-1; b++)//判断斜上方
	{
		flag3 = 1;
		for (int a = 0; a < row - 1; a++)
		{
			for (; (a < col - 1) && (b < row - 1); a++, b++)
			{
				if ((board[b][a] == board[b + 1][a + 1]) && (board[b][a] != ' '))
				{
					flag3++;
				}
				else
				{
					flag3 = 1;
				}
				if (flag3 == nziqi)
				{
					return board[b][a];
				}
			}
		}
	}

斜上方的判断也是如此,只是修改了起始点和判断走向

for (int b = 0; b < row - 1; b++)//判断斜下方
	{
		flag4 = 1;
		for (int a = col - 1; a > 0; a--)
		{
			for (; (a > 0) && (b < row - 1); a--, b++)
			{
				if ((board[b][a] == board[b + 1][a - 1]) && (board[b][a] != ' '))
				{
					flag4++;
				}
				else
				{
					flag4 = 1;
				}
				if (flag4 == nziqi)
				{
					return board[b][a];
				}
			}
		}
	}

如果按这种方式没有找到n个相同的连在一起的棋子,就判断棋盘是否被铺满。
在外部定义函数is_full来判断。

static int is_full(char board[ROW][COL], int row, int col)

对棋盘中每个棋子进行判断,如果存在空位,就返回1
找完棋盘没有空位,就会返回0

static int is_full(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;
}

在判断模块,我们调用函数is_full
如果函数的返回值为1,棋盘满,就返回字符‘Q’
在执行完上述所有判断后,如果都没有成立,说明游戏没有结束
返回字符‘A’

if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}
	return 'A';

(9)打印结果(包括函数shengfu1/2)

之前我们说过
在判断胜负结束之后,将ret的值作为返回值,传递给变量a。
a作为参数函数shengfu的参数,根据a的值来打印游戏结果。
分为pve和pvp两种情况。
使用switch case语句,将ret作为参数。

void shengfu1(char a)
{
	switch (a)
	{
	case '*':printf("玩家一获胜\n"); break;
	case '#':printf("玩家二获胜\n"); break;
	case 'Q':printf("平局\n"); break;
	case 'A':printf("错误\n"); break;
	}
}
void shengfu2(char a)
{
	switch (a)
	{
	case '*':printf("玩家获胜\n"); break;
	case '#':printf("电脑获胜\n"); break;
	case 'Q':printf("平局\n"); break;
	case 'A':printf("错误\n"); break;
	}
}

3。最终程序

将上述部分全部结合在一起!
得到下面的游戏代码
game.h:

#pragma once//game.h
#define ROW 10
#define COL 10
#define NZIQI 3
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>

void menu();

void menu1();

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

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

void playermove(char board[ROW][COL], int row, int col, char player);

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

char panduan(char board[ROW][COL], int row, int col, int nziqi);

test.c:

#include"game1.h"//test.c
void shengfu1(char a)
{
	switch (a)
	{
	case '*':printf("玩家一获胜\n"); break;
	case '#':printf("玩家二获胜\n"); break;
	case 'Q':printf("平局\n"); break;
	case 'A':printf("错误\n"); break;
	}
}
void shengfu2(char a)
{
	switch (a)
	{
	case '*':printf("玩家获胜\n"); break;
	case '#':printf("电脑获胜\n"); break;
	case 'Q':printf("平局\n"); break;
	case 'A':printf("错误\n"); break;
	}
}
char gamepve()
{	
	char player1 = '*';
	char ret = '\0';
	char board[ROW][COL];
	chushihua(board, ROW, COL);
	while (1)
	{
		printqipan(board, ROW, COL);
		printf("玩家下棋\n");
		playermove(board, ROW, COL, player1);
		ret = panduan(board, ROW, COL, NZIQI);
		if (ret != 'A')
		{
			printqipan(board, ROW, COL);
			break;
		}
		computermove(board, ROW, COL);
		ret = panduan(board, ROW, COL, NZIQI);
		if (ret != 'A')
		{
			printqipan(board, ROW, COL);
			break;
		}
	}
	return ret;
}
char gamepvp()
{
	char player1 = '*';
	char player2 = '#';
	char ret = '\0';
	char board[ROW][COL];
	chushihua(board, ROW, COL);
	while (1)
	{
		printqipan(board, ROW, COL);
		printf("玩家一下棋\n");
		playermove(board, ROW, COL, player1);
		ret = panduan(board, ROW, COL, NZIQI);
		if (ret != 'A')
		{
			printqipan(board, ROW, COL);
			break;
		}
		printqipan(board, ROW, COL);
		printf("玩家二下棋\n");
		playermove(board, ROW, COL, player2);
		ret = panduan(board, ROW, COL, NZIQI);
		if (ret != 'A')
		{	
			printqipan(board, ROW, COL);
			break;
		}
		return ret;
	}

	return ret;
}
void test()
{
	int input = 0;
	menu();
	do
	{
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			printf("请选择模式\n");
				int input1 = 0;
				menu1();
				do
				{
					char a = '\0';
					scanf("%d", &input1);
					switch (input1)
					{
					case 1:
						printf("pvp\n");
						a=gamepvp();
						shengfu1(a);
						menu();
						break;
					case 0:
						printf("pve\n");
						a=gamepve();
						menu();
						shengfu2(a);
						break;
					default:
						printf("输入错误,请重新输入\n");
						break;
					}
				} while (input1 != 0 && input1 != 1);
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}
int main()
{
	srand((unsigned int)time(NULL));
	test();
	return 0;
}

game.c:

#include "game1.h"
void menu()
{
	printf("**************************************\n");
	printf("************    1.play    ************\n");
	printf("************	0.exit	  ************\n");
	printf("**************************************\n");
	printf("请输入:");
}	

void menu1()
{
	printf("**************************************\n");
	printf("************    1.pvp     ************\n");
	printf("************	0.pve	  ************\n");
	printf("**************************************\n");
	printf("请输入:");
}
void chushihua(char board[ROW][COL],int row,int col)
{
	for (int a = 0; a < row; a++)
	{
		for (int b = 0; b < col; b++)
		{
			board[a][b] = ' ';
		}
	}
}
void printqipan(char board[ROW][COL], int row, int col)
{
	system("cls");
	for (int a = 0; a < row; a++)
	{
		for (int b = 0; b < col; b++)
		{
			printf("% c ", board[a][b]);
			if (b != col - 1)
			{
				printf("|");
			}

		}
		printf("\n");
		
		if (a != row - 1)
		{
			for (int c = 0; c < col - 1; c++)
			{
				printf("---");
					if (c == col - 2)
					{
						printf("--\n");
					}

			}
		}
	}
}
void playermove(char board[ROW][COL], int row, int col, char player)
{
	int a = 0, b = 0;
	while (1)
	{
		scanf("%d%d", &a, &b);
		if (a >= 1 && a <= row && b >= 1 && b <= col)
		{
			if (board[a - 1][b - 1] == ' ')
			{
				board[a - 1][b - 1] = player;
				break;
			}
			else
				printf("输入坐标已被占用\n");
		}
		else
		{
			printf("坐标非法");
		}
	}
}
void computermove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋");
	Sleep(1000);
	while (1)
	{
		int a = rand() % col;
		int b = rand() % row;
		if (board[a][b] == ' ')
		{
			board[a][b] = '#';
			break;
		}
	}
}
static int is_full(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 panduan(char board[ROW][COL], int row, int col,int nziqi)
{
	int flag1 = 1, flag2 = 1, flag3 = 1, flag4 = 1;
	//判断列
	for (int a = 0; a < col; a++)
	{
		flag1 = 1;
		for (int b = 0; b < row-1; b++)
		{
			if ((board[b][a] == board[b + 1][a]) && (board[b][a] != ' '))
			{
				flag1++;
			}
			else
			{
				flag1 = 1;
			}
			if (flag1 == nziqi)
			{
				return board[b][a];
			}
		}
	}
	//行
	for (int b = 0; b < row; b++)
	{
		flag2 = 1;
		for (int a = 0; a < col - 1; a++)
		{
			if ((board[b][a] == board[b][a+1]) && (board[b][a] != ' '))
			{
				flag2++;
			}
			else
			{
				flag2 = 1;
			}
			if (flag2 == nziqi)
			{
				return board[b][a];
			}
		}
	}
	//对角线
	
	
	for (int b = 0; b < row-1; b++)
	{
		flag3 = 1;
		for (int a = 0; a < row - 1; a++)
		{
			for (; (a < col - 1) && (b < row - 1); a++, b++)
			{
				if ((board[b][a] == board[b + 1][a + 1]) && (board[b][a] != ' '))
				{
					flag3++;
				}
				else
				{
					flag3 = 1;
				}
				if (flag3 == nziqi)
				{
					return board[b][a];
				}
			}
		}
	}
	for (int b = 0; b < row - 1; b++)
	{
		flag4 = 1;
		for (int a = col - 1; a > 0; a--)
		{
			for (; (a > 0) && (b < row - 1); a--, b++)
			{
				if ((board[b][a] == board[b + 1][a - 1]) && (board[b][a] != ' '))
				{
					flag4++;
				}
				else
				{
					flag4 = 1;
				}
				if (flag4 == nziqi)
				{
					return board[b][a];
				}
			}
		}
	}
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}
	return 'A';
}

代码庞大,我只能尽我所能将程序讲得更清晰,但难免有纰漏,难以理解之处。
如果程序有错误,或者文章有改进的地方,请指出,我一定会改正!
下一篇,函数的递归和优化。
大家的关注和点赞是我永远的动力!
在这里插入图片描述

  • 12
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值