你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

C语言实现:从入门的三子棋到N子棋

2021-11-8 0:07:32

今天介绍的内容是关于从三子棋到N子棋的代码实现与优化。
话不多说直入正题

首先,我们要将游戏的外部框架设计好。即为游戏的测试程序和游戏菜单的设计。由于夜已较深我们直接上代码。

void menu()             //菜单
{
	printf("    #####################\n");
	printf("######  1.Play 0.Exit  ######\n");  //这里将 1 设为进入游戏,0 设为退出游戏在下面的条件语句中有妙用
	printf("    #####################\n");
}              
void test()             //这里是游戏的测试程序
{
	srand((unsigned int)time(NULL));       
	 //在用srand()生成随机数种子时不需要进行循环,所以直接放这。并且srand()要用time()函数来获取时间戳来生成随机数种子。time函数声明头文件<time.h>
	
	int input = 0;
	do                     //以do while 循环作为外部框架先执行一次程序再进行循环。
	{
		menu();             //这里是游戏菜单的函数
		printf("请输入:> ");
		scanf("%d", &input);                
		printf("\n");
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出成功\n");
			break;
		default:
			printf("请重新输入:> \n");
			break;
		}
	} while (input);        //上面菜单的设定使得这里巧妙的用input的值来做判断条件
}
int main() 
{
	test();
	return 0;
}

第二布就是将game()函数里的内部逻辑(三子棋的内部逻辑)进行相关的编写了。还是直接上代码

void game()              //游戏的主要框架
{
	char arr[Row][Col] = { 0 };     //创建数组存储下棋信息
	InitBoard(arr, Row, Col);       //初始化棋盘,即将数组内容全部初始化为空格
	DisplayBoard(arr, Row, Col);    //打印棋盘
	char ret = 0;                   //声明变量待会用来接受判断输赢函数的返回值
	while (1)
	{
		PlayerMove(arr, Row, Col);    //玩家下棋 
		DisplayBoard(arr, Row, Col);  //打印棋盘
		ret = Is_Win(arr, Row, Col);  //对游戏过程可能出现的四种情况进行判断,玩家赢返回 '*',电脑赢返回'#',平局返回'p',继续返回'c'(注意这里将玩家、电脑赢的返回值和对应落子形状设定相同将有妙用)
		if (ret != 'c')               //每一次玩家下完棋都要判断一次对局情况
		{                           
			break;
		}
		ComputerMove(arr, Row, Col);  //电脑下棋
		DisplayBoard(arr, Row, Col);  //打印棋盘
		ret = Is_Win(arr, Row, Col);  
		if (ret != 'c')               //同理每次电脑落完子也要判断一次对局情况
		{
			break;
		}
	}
	if (ret == '*')                  
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else if (ret == 'p')
	{
		printf("平局\n");
	}

}

第三步就是对游戏内部需要调用的所有函数进行实现

void InitBoard(char arr[Row][Col], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			arr[i][j] = ' ';
		}
	}
}
void DisplayBoard(char arr[Row][Col], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			printf(" %c ", arr[i][j]);   //将棋盘的每一行每一列都分开来打印,使得棋盘的大小可以随便改变。
			if (j < col - 1)             //这里要判断是因为当打印到最后一列时就不需要打印'|'
			{
				printf("|");
			}
		}
		printf("\n");
		if (i < row - 1)                 //这里的判断作用同上,当打印到最后一行时就不需要打印'---'了。
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		}
		printf("\n");
	}
}
void PlayerMove(char arr[Row][Col], int row, int col)
{
	int a, b;
	while (1)
	{
		printf("请输入:> ");
		scanf("%d%d", &a, &b);
		if (a <= row && a >= 1 && b <= col && b >= 1) //注意此处玩家输入的坐标值可能超出了棋盘大小,或者是这个位置已经有棋子了。所以这里要对这两种情况进行判断。                  
		{
			if (arr[a - 1][b - 1] == ' ')      
			{
				arr[a - 1][b - 1] = '*';
				break;
			}
			else
				printf("此坐标已被占用,请重新输入!\n");
		}
		else
			printf("请重新输入!\n");
	}
}
void ComputerMove(char arr[Row][Col], int row, int col)
{
	printf("电脑走:> \n");
	while (1)
	{                              //在用rand()之前要先调用srand()函数,生成随机数的种子。rand函数要声明头文件<stdlib.h>
		int a = rand() % row;      //此处用生成随机值的方式使得电脑随机落子,并且巧妙的用随机值模上棋盘行列信息使得这个落子范围在棋盘内。                  
		int b = rand() % col;
		if (arr[a][b] == ' ')
		{
			arr[a][b] = '#';
			break;
		}
	}
}
char Is_Win(char arr[Row][Col], int row, int col)
{
	int count = 0;             
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)          //控制行      //横排是否相等的判断                  
	{
		for (j = 0; j < col - 2; j++)  //控制列        
			if (arr[i][j] == arr[i][j + 1] && arr[i][j + 1] == arr[i][j + 2] && arr[i][j] != ' ')
			{
				return arr[i][0];     //这里就是刚刚提到的妙用了,因为玩家下的棋与玩家赢的返回值一致,所以当三个数相等的时候直接返回棋盘信息即可,不需要再对棋盘信息进行判断来判断谁赢
			}
	}
	for (j = 0; j < col; j++)         //控制列       //数列是否相等的判断                            
	{
		for (i = 0; i < row - 2; i++) //控制行
		{
			if (arr[i][j] == arr[i+1][j] && arr[i+1][j ] == arr[i+2][j] && arr[i][j] != ' ')
				return arr[i][j];     
		} 
	}

	for (i = 0; i < col - 2; i++)     //控制列            //从左上至右下的斜列的判断
	{
		for (j = 0; j < row - 2; j++) //控制行                     
		{
			if (arr[j][i] == arr[j + 1][i + 1] && arr[j + 1][i + 1] == arr[j + 2][i + 2] && arr[j][i] != ' ')
			{
				return arr[j][i];                      //   |   |
			}                                          //---|---|---
		}                                              //---|---|---
	}                                                  //   |   |
	for (i = 0; i < row - 2; i++)     //控制行           //从左下至右上的斜列的判断  
	{
		for (j = 2; j <= col; j++)    //控制列
		{
			if (arr[i][j] == arr[i+1][j-1] && arr[i+1][j-1] == arr[i+2][j-2] && arr[i][j] != ' ')
			{
				return arr[i][j];
			}
		}
	}
	for (i = 0; i < row; i++)            //这里判断平局和继续游戏的情况  
	{                                        
		for (j = 0; j < col; j++)   
		{
			if (arr[i][j] == ' ')        //这里引入一个初始化值为0的外界变量count,只要有一个元素为空格,就将他赋值为 1
				count = 1;
		}
	}
	if (count == 1)                     //如果count为1,则证明该棋盘还有空格可以落子,则游戏继续
		return 'c';
	else
		return 'p';                     //当没人赢且棋盘满了,就代表双方平局,所以这里如果count为0,即代表上面的判断语句不成立,棋盘中没有空格,此时平局。
}

emmm因为实在是比较晚了今天就弄得粗糙一点,都是一大坨一大坨的代码与注释。噢噢噢差点忘了还有个自定义函数的点。这里就直接在这做解释了。

#define Row 5     //这里采用宏定义的方式来设置行数与列数,可以通过改变宏定义设置的常量值直接控制棋盘大小。这样的话就能更方便使得棋盘信息可以得到扩充,而不是直接设为常量固定了行数与列数
#define Col 5  

xdm慢慢看哈,阿西吧俺真的要溜去睡觉觉了。如果代码方面有相关问题欢迎评论区进行指正。