三子棋程序的讲解(全)

2023-12-20 07:14:07

前期回顾

????????这里给大家提供往期的传送门:

三子棋程序的讲解(1)-CSDN博客

三子棋程序的讲解(2)-CSDN博客

三子棋程序的讲解(3)-CSDN博客

三子棋程序的讲解(4)-CSDN博客

三子棋程序的讲解(5)-CSDN博客

????????这是将其拆开来讲的,有兴趣的可以看一下。

实现思路

1.棋盘的设计

????????相信我们大部分人都玩过或者见过三子棋,就是在3*3的棋盘内,两方对弈,当一方有三颗棋子在任意方向连着三颗就赢了(横、竖、斜都可以)。当我说到3*3,是否能让你想起什么?正是二维数组,假定一个数组arr,我们给他确定大小arr[3][3],这不正是棋盘的轮廓就出来了吗?3*3棋盘和3*3的二维数组;我们还应该有一个初始化棋盘的程序,因为每次下完棋之后,想要再次进行游戏,棋盘不能是满的,所以说应该每次下完一盘都应该清除棋盘上面的东西,才能让游戏反复多次。

2.玩家如何下棋

????????玩家想要下棋,我们应该将其与数组的知识结合起来,我们让玩家输入行号和列号,然后对应在数组的行号和列号-1的位置,将其元素改为玩家的棋子(我们随便给玩家的棋子给一个符号),当玩家每次输入一个行号列号的时候,都可以将棋盘的对应位置改为那个符号,就相当于玩家在这个棋盘上面下棋了。

3.电脑如何下棋

????????人机大战相信一直是很多人所希望看到的,但毕竟能力有限,无法复刻像alphago那样的AI,但是我们也可以创造出一个人工“智障”来和我们对弈。依稀记得我在猜数字那篇博客中提到过rand函数产生随机值(简单程序“猜数字”的实现(1)-CSDN博客),我们可以用rand函数,让电脑随机在数组中填入元素(符号),和玩家的符号不一样,就可以区分并且下棋了。

4.如何判断胜负

?????????判断输赢就相对而言简单一些了:只要在3*3的棋盘(数组)中,有三个在同一行、同一列、或者同一斜边的元素(数组),都是一样的,那么那个元素(数组)所属于的玩家就赢了,但值得一提的是:应该注意一种情况,就是当棋盘全满的时候,双方没有一人赢,那么就应该是平局。

棋盘的初始化

????????三子棋,就是在3*3的棋盘内,两方对弈,当一方有三颗棋子在任意方向连着三颗就赢了(横、竖、斜都可以)。当我说到3*3,是否能让你想起什么?正是二维数组,假定一个数组arr,我们给他确定大小arr[3][3],这不正是棋盘的轮廓就出来了吗?3*3棋盘和3*3的二维数组;我们还应该有一个初始化棋盘的程序,因为每次下完棋之后,想要再次进行游戏,棋盘不能是满的,所以说应该每次下完一盘都应该清除棋盘上面的东西,才能让游戏反复多次。那么我们怎么让它初始化呢,且看代码:

void intboard(char board[ROW][COL], int row, int col)//这里用函数的形式写,传的参数是
数组,和数组的行列大小
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';//将数组全部初始化为空格
		}
	}
}

????????我们需要用两个循环变量来控制数组的行列,这样之后,我们就可以将数组内的所有元素初始化为空格,我们只需要在每次游戏结束之后调用这个函数,就可以实现棋盘的初始化了。

棋盘的打印

????????我们先想一想棋盘的轮廓,是不是类似于一个“井”字形,所以说我们应该填充(__and |)来构成这个井字形:

void Displayboard(char board[ROW][COL], int row, int col)
{
	int i;
	for (i = 0; i < row; i++)
	{
		//打印数据
		int j;
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			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");
		}
	}
}

?????????这样,我们每一行都会打印(___|___|___),然后每一列都会打印(|),将二者用两个循环变量所控制,行列分别控制并且将其写出来,然后打印出来。

玩家移动

????????我们已经知道了,实际上棋盘是一个二维数组,那么我们就可以通过改变数组的元素,通过人和电脑的摆入的不同的元素,就可以实现人和电脑的下棋,并判断到底是谁胜是负。理论可行,且看代码:

void playermove(char board[ROW][COL], int row, int col)
{
	printf("请输入坐标:(行在前,列在后,请用空格或回车隔开)\n");//让玩家能清楚,自己该怎么下棋
	printf("AlphaGO有1s的思考时间,不要急躁\n");
	printf("玩家请下棋>\n");
	int x, y;
	scanf("%d%d", &x, &y);//让玩家输入行号和列号
	while (1)
	{
		if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//玩家得合法的输入,所以说输入必须大于1,但小于行数和列数
		{
			if (board[x - 1][y - 1] == ' ')//并且在不是乱输的请况下,要确保玩家想要的那个格子没被其他元素占用
			{
				board[x - 1][y - 1] = '*';//都满足,则为玩家下一颗棋子
				break;
			}
			else//如果玩家选了一个非空格(已经被占用的格子),就进行提示
			{
				printf("\n");
				printf("已经被占用了\n");
				printf("\n");
				break;
			}
		}
		else//如果玩家乱输,也对其提示
		{
			printf("\n");
			printf("坐标非法\n");
			printf("\n");
			break;
		}
	}
}

????????这个代码主要是用于玩家的移动,大部分内容其实看注释内容也可以看明白,因为其实不难,但需要注意一些事情:实际上这个数组是0,1,2的三行三列,学过代码的就知道这件事情,但其实在棋盘的表示上面,或者说其他不懂这个的玩家,是认为是1,2,3这样的三行三列,所以说我们在代码的实现中,应该是对行(row)和列(col)减一的元素进行操作的(row-1,col-1)。?

电脑移动

void AlphaGO(char board[ROW][COL], int row, int col)
{
	printf("Alpha go->\n");//提示,现在是电脑在走
 
	while (1)//只要电脑没找到合适的地方落子,就会一直循环,直到找到地方来落子
	{
		int x, y;
		x = rand() % row;//产生行的随机值
		y = rand() & col;//产生列的随机值
		if (board[x][y] == ' ')//只要棋盘上面的某行某列是空的就
		{
			board[x][y] = '$';//修改那里的元素,然后相当于电脑落子了
			break;//电脑落子之后就结束循环
		}
	}
}

????????这里就必须给大家讲一个东西了,就是关于rand函数来产生随机值的这一点,rand函数产生的是“伪随机数”,其并非真正的随机数。为了解决rand函数生成的“伪随机数”的问题,我们可以引入srand函数和time函数,rand和srand函数的头文件都是<stdlib.h>。

????????通过rand()%row和rand()%col,我们就可以产生一些随机的数字,这些数字都是介于输入的行列的值的(其实就是数组的行列),所以说电脑会随机在这3*3的棋盘内生成数字,然后我们用一个if来控制(只要电脑产生的随机数在数组中的位置,没有被占用(就是那里的元素正好是空格),那么就修改为不同于玩家的元素),这样之后,每次调用,电脑都能在棋盘上面随机产生一个“棋子”,然后后面就可以通过玩家和电脑的棋子来判断胜负了。

? ? ? ? 但是这样明显电脑是十分弱智的,没有什么挑战性,但先不管,我们先让电脑弱智,当整个程序能够跑起来之后,再对电脑进行优化。

判断胜负

????????胜利的方式实际上非常的简单,只需要任意一行(或者任意一列,或者任意对角线),有三颗相同的棋子就算那一方赢了,所以说我们在我们这个行为0,1,2和列为0,1,2的数组中,只需要确保当行号一样的时候,列号(0,1,2)都相同(同理,当列号一样的时候,确保行号0,1,2都相同),或者确保0,0和1,1和2,2这几个元素;或者0,2和1,1,和2,0这几个元素相同,就可以判断胜利了,先看代码:

char result(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][1] != ' ')//当有一行全部为同一个元素的时候,就返回那个元素给游戏控制函数
 
		{
			return board[i][1];//随便返回一个数组下标(注意是要把行写成i),因为当有一行元素全相同的时候,那一行随便哪个元素都是相同的
		}
	}
	int j;
	for (j = 0; j < col; j++)//和上面判断行相同一样,一样的思路,只是这里是判断是否存在一列有全部相同的
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
		{
			return board[1][j];//返回那个元素(胜利的元素)
		}
	}
	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 (Full(board, row, col))//这个函数是判断棋盘是否是满的,这里下面会给大家提到
	{
		return 'Q';
	}
	return 'c';
}

????????这里还有一件事,这样写,我们还差了一点对于游戏平局的判断——只要上述情况都没满足,但棋盘(二维数组)又已经被填满了元素的时候,就可以判断为平局了,然后将平局的结果返回给游戏控制函数:

int Full(char board[ROW][COL], int row, int col)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')//检查数组中的每一个元素,只要有一处还是空格,就返回0
				return 0;
		}
	}
	return 1;//返回1就说明数组中没有元素了
}

?

游戏主体实现

?游戏控制函数

void board_game()
{
	char board[ROW][COL];//创造二维数组
	intboard(board, ROW, COL);//初始化棋盘
	Displayboard(board, ROW, COL);//打印棋盘
	char ret;//创造变量,用来接受胜负的返回值
	while (1)//只要胜负未分,就一直循环
	{
		playermove(board, ROW, COL);//玩家先移动
		Displayboard(board, ROW, COL);//电脑再移动
		ret = result(board, ROW, COL);//判断胜负
		if (ret != 'c')
		{
			break;
		}
		AlphaGO(board, ROW, COL);
		Sleep(1000);
		Displayboard(board, ROW, COL);
		ret = result(board, ROW, COL);
		if (ret != 'c')//当返回c时,游戏继续
		{
			break;
		}
	}
	if (ret == '*')//返回为*则玩家赢了
	{
		printf("你赢了\n");
		Displayboard(board, ROW, COL);
		Sleep(2000);
		system("cls");
 
	}
	else if (ret == '$')//返回为$则电脑赢了
	{
		system("cls");
		printf("you loss\n");
		printf("fvv\n");
		Sleep(2000);
		Displayboard(board, ROW, COL);
		system("cls");
 
	}
	else//其他情况就是平局了
		printf("平局\n");
	Displayboard(board, ROW, COL);
	Sleep(2000);
	system("cls");
}

游戏主体函数

int main()
{
	srand((unsigned int)time(NULL));//产生随机数的函数
	int input;
	do
	{
		menu();//首先打印游戏菜单
		printf("do you want to play it?");//提示玩家
		printf("\n");
		scanf("%d", &input);//让玩家选择玩还是不玩
		switch (input)//用玩家的输入来进行选择
		{
		case 1://选1,则游戏开始,调用游戏控制函数
		{
			system("cls");
			printf("你将挑战AlphaGO,庆幸它现在还处于幼年\n");
			board_game();
			break;
		}
		case 0://输入0则游戏退出
		{
			system("cls");
			printf("game over\n");
			Sleep(1000);
			system("cls");
			printf("fvv\n");
			Sleep(1000);
			break;
		}
		default://其他的输入则为错误
		{
			system("cls");
			printf("error\n");
			Sleep(2000);
			system("cls");
			break;
		}
		}
	} while (input);//用do-while控制循环,至少游戏进入一次
	return 0;
}

全部代码?

????????将以上部分全部结合起来,就可以完成我们想要的三子棋了,但代码很多,放在同一个源文件中可能有一些紊乱,建议将函数放一个源文件,实现又放在另外一个源文件,这是所有的代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>
#include<windows.h>
#include<stdlib.h>
#define ROW 3
#define COL 3
void menu()
{
	printf("*****************************\n");
	printf("*****************************\n");
	printf("********1.play 0.exit********\n");
	printf("*****************************\n");
	printf("\n");
}
void intboard(char board[ROW][COL], int row, int col)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}
//因为将行列定死了,所以说是相对垃圾的写法,只能写3*3
/*void Displayboard(char board[ROW][COL], int row, int col)
{
	int i;
	for (i = 0; i < row; i++)
	{
		//打印数据
		printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
		//打印分割
		if (i < row - 1)
		printf("---|---|---\n");
	}
}*/
void Displayboard(char board[ROW][COL], int row, int col)
{
	int i;
	for (i = 0; i < row; i++)
	{
		//打印数据
		int j;
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			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");
		}
	}
}
void playermove(char board[ROW][COL], int row, int col)
{
	printf("请输入坐标:(行在前,列在后,请用空格或回车隔开)\n");
	printf("AlphaGO有1s的思考时间,不要急躁\n");
	printf("玩家请下棋>\n");
	int x, y;
	scanf("%d%d", &x, &y);
	while (1)
	{
		if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("\n");
				printf("已经被占用了\n");
				printf("\n");
				break;
			}
		}
		else
		{
			printf("\n");
			printf("坐标非法\n");
			printf("\n");
			break;
		}
	}
}
void AlphaGO(char board[ROW][COL], int row, int col)
{
	printf("Alpha go->\n");
 
	while (1)
	{
		int x, y;
		x = rand() % row;
		y = rand() & col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '$';
			break;
		}
	}
}
int Full(char board[ROW][COL], int row, int col)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}
char result(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][1] != ' ')
		{
			return board[i][1];
		}
	}
	int j;
	for (j = 0; j < col; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
		{
			return board[1][j];
		}
	}
	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 (Full(board, row, col))
	{
		return 'Q';
	}
	return 'c';
}
void board_game()
{
	char board[ROW][COL];
	intboard(board, ROW, COL);
	Displayboard(board, ROW, COL);
	char ret;
	while (1)
	{
		playermove(board, ROW, COL);
		Displayboard(board, ROW, COL);
		ret = result(board, ROW, COL);
		if (ret != 'c')
		{
			break;
		}
		AlphaGO(board, ROW, COL);
		Sleep(1000);
		Displayboard(board, ROW, COL);
		ret = result(board, ROW, COL);
		if (ret != 'c')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("你赢了\n");
		Displayboard(board, ROW, COL);
		Sleep(2000);
		system("cls");
 
	}
	else if (ret == '$')
	{
		system("cls");
		printf("you loss\n");
		printf("fvv\n");
		Sleep(2000);
		Displayboard(board, ROW, COL);
		system("cls");
 
	}
	else
		printf("平局\n");
	Displayboard(board, ROW, COL);
	Sleep(2000);
	system("cls");
}
int main()
{
	srand((unsigned int)time(NULL));
	int input;
	do
	{
		menu();
		printf("do you want to play it?");
		printf("\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
		{
			system("cls");
			printf("你将挑战AlphaGO,庆幸它现在还处于幼年\n");
			board_game();
			break;
		}
		case 0:
		{
			system("cls");
			printf("game over\n");
			Sleep(1000);
			system("cls");
			printf("fvv\n");
			Sleep(1000);
			break;
		}
		default:
		{
			system("cls");
			printf("error\n");
			Sleep(2000);
			system("cls");
			break;
		}
		}
	} while (input);
	return 0;
}

?

?

文章来源:https://blog.csdn.net/Aishangyuwen/article/details/135082811
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。