C语言实现三子棋(超级细节)

  各位读者贵安,这篇博客我发表一下我对用C语言实现三子棋游戏的理解与看法,各位可以参考一下,也欢迎提出建议。(最后附上整个三子棋代码)

  首先我们先讲解一下三子棋的游戏规则,顾名思义,三子棋就是在一个棋盘中,玩家和电脑对弈,哪一方的三个棋子连成一条直线即获胜。

  那么我们就可以获得三个条件:首先需要一个棋盘,然后需要玩家能与电脑对弈,接着还要判断输赢,有了这三个条件,我们就可以开始写代码。

  首先我们创建一个源文件,命名为text.cpp。

  先整理好函数的调用,我们先创建一个main函数,在里面创建一个text()测试函数,将所有的游戏代码实现与函数调用放在这个text()函数内,这样可以使main函数更加简洁美观和方便后期管理。

 

  接下来,我们就要在text()函数内开始实现游戏的选择菜单,就是我们进入游戏时第一眼看到的东西,让我们选择游戏模式之类的。这里我们需要让用户选择,1是开始游戏,0是退出,do...while函数是为了无论如何都能先打印出菜单让用户选择。

void text()
{
	int input = 0;
	do
	{
		menu();
		cout << "请输入" << endl;
		cin >> input;
		switch (input)
		{
		case 1:
		{
			game();//游戏的实现
			break;
		}
		case 0:
		{
			cout << "退出";
			break;
		}
		default:
		{
			cout << "输入错误,请从新输入";
			break;
		}
		}
	} while (input);
}

  这里我们就需要创建一个名为input的整形来接收用户输入的数字,并把它放在swich和while循环里判断,这里的while判断放input是一个小技巧,因为用户选择1是开始,0是结束,同时0也为真,这时while就会退出循环。

  接下来就是game()函数,也就是游戏的主体实现。

  首先我们知道,三子棋的棋盘是由行和列组成的,最少也是三行三列,那么我们就可以用一个二维数组来储存三子棋的数据,之后我们要用到这个行和列的数据的地方会很多,所以这里我们不能写死,我们可以创建一个头文件,在里面定义一个宏,名字可以自己取,数字也可以自定义,这里我们定义为3,

//注意,这里的红定义是在创建的头文件定义的
#define HANG 3//行数
#define LIE 3//列数

  接下来我们只要在text.cpp的源文件中包含一下这个头文件就可以使用宏定义常量了。

  那么现在我们用这些宏定义常量来定义一个二维数组,就像这样。

char qipan[HANG][LIE] = { 0 };

   接下来我们要开始打印棋盘,首先我们要打印一个空的棋盘出来,那么我们就要先对这个二维数组进行初始化,我们可以写一个函数对这个二维数组初始化。

int_ti(qipan, HANG, LIE);//初始化棋盘

在使用该函数前,我们需要先在头文件中对此函数进行声明,然后再创建一个.cpp文件来实现这个以及之后会使用到的函数,这里我们给这个头文件命名为game.cpp。

然后是初始化棋盘函数的实现,整体使用for循环对二维数组进行遍历并将每一个下标赋值为空格。因为要打印的是空白的棋盘,所以赋值为空格,具体函数如下

void int_ti(char qipan[HANG][LIE], int hang, int lie)
{
	//初始化全是空格
	for (int i = 0; i < hang; i++)
	{
		for (int j = 0; j < lie; j++)
		{
			qipan[i][j] = ' ';
		}
	}
}

  接下来是棋盘的分割行和列,这里就需要一些逻辑分析,这里我们要打印的是一个3*3的棋盘,那么我们的打印棋盘函数传参就可以将初始化好的数组以及定义好的宏定义常量传进去。

	print_qipan(qipan, HANG, LIE);//打印棋盘

  照样是头文件声明,在game.cpp文件中实现。

  三行三列,那么每一列就有三个空格供玩家和电脑落子,我们需要打印出来的效果如图所示

为了让玩家的每一个子都落在方格的正中间,我们可以在每一个落子位置两边加上空格,使其美观。既然有三行,那么我们就可以分三次打印,第一次打印第一行,每行有多少个小方格,我们由列数决定,打印顺序为“空格,数据,空格,‘分隔符|’,有几列就打印几次。

void print_qipan(char qipan[HANG][LIE], int hang, int lie)
//每一次循环都只为了打印一个小方格
{
	for (int i = 0; i < hang; i++)//一共要打印几行
	{
		for (int j = 0; j < lie; j++)//决定每行打印多少小方格
		{
			cout << " " << qipan[i][j] << " ";
			if (j<lie-1)//判断是否到最后一列,最后一列不打印
			{
				cout << "|";
			}
		}
		cout << endl;//打印完一行的小方格要换行
		//打印分割行
		if (i<hang-1)//判断是否为最后一行,最后一行不打印
		{
			for (int k = 0; k < lie; k++)//打印一行的分割行
			{
				cout << "---";
				if (k < lie - 1)
				{
					cout << "|";
				}

			}
		}
		cout << endl;//打印完一行要换行;
	}
}

  接下来是玩家落子,这个就只需要让玩家输入坐标,然后判断坐标是否被占用,若坐标未被占用,则将当前输入坐标的x和y值各减一,再将二维数组中该位置的数据替换成“*”即可(注:这里x和y各减一的原因是因为玩家不是程序员,并不认为数组下标会从0开始)代码实现如下

void play_move(char qipan[HANG][LIE], int hang, int lie)
{
	cout << "玩家下棋";
	while (1)
	{
		cout << "请输入坐标" << endl;
		int x, y = 0;
		cin >> x >> y;
		if (x >= 1 && x <= hang && y >= 1 && y <= lie)//先判断输入坐标是否正确
		{
			if (qipan[x - 1][y - 1] == ' ')//再判断该坐标是否被调用
			{
				qipan[x - 1][y - 1] = '*';
				break;
			}
		}
		else
		{
			cout << "坐标非法,请重新输入";
		}
	}
}

  接下来是电脑下棋,电脑下棋则需要生成两个随机数x和y,然后判断二维数组对应的坐标是否被占用,若未被占用,就将对应的坐标的数据改成“#”。

void computer_move(char qipan[HANG][LIE], int hang, int lie)
{
	cout << "电脑下棋" << endl;
	while (1)
	{

		int x = rand() % HANG;
		int y = rand() % LIE;
		if (qipan[x][y] == ' ')
		{
			qipan[x][y] = '#';
			break;
		}
	}
}

然后我们把玩家下棋和电脑下棋在text.cpp中写成循环,这里玩家下棋需要打印一次棋盘,电脑下棋需要打印一次棋盘

	while (1)
	{
		
		play_move(qipan, HANG, LIE);
		print_qipan(qipan, HANG, LIE);
		computer_move(qipan, HANG, LIE);
		print_qipan(qipan, HANG, LIE);
	}

  那么下棋总得有个输赢吧?我们得封装个函数判断是玩家赢了还是电脑赢了,或者是平局,按照规则,三条线无论横纵或者对角线,只要能连在一起,就算赢,那么我们封装的函数就只需要判断当前是“*”连在了一起还是“#”连在了一起,返回一个值,交给text.cpp来判断,返回值为了方便,我们可以之间返回连成一条线的符号,如果棋盘满了,就是平局,还需要在判断输赢的函数里再调用一个函数用来遍历棋盘是否还有空格,如果没有空格了,那么,就返回一个Q告诉text.cpp里的判断,表示平局,具体实现如下

int is_full(char qipan[HANG][LIE], int hang, int lie)//遍历数组是否有空格
{
	for (int i = 0; i < hang; i++)
	{
		for (int j = 0; i < lie; j++)
		{
			if (qipan[i][j] == ' ')
			{
				return 1;

			}
		}
	}
	return 0;
}
char is_win(char qipan[HANG][LIE], int hang, int lie)
{
	//判断三行
	for (int i = 0; i < hang; i++)
	{
		if (qipan[i][0]==qipan[i][1]&&qipan[i][1]==qipan[i][2]&&qipan[i][0]!=' ')
		{
			return qipan[i][0];
			
		}
	}
	//判断三列
	for (int j = 0; j < lie; j++)
	{
		if (qipan[0][j]==qipan[1][j]&&qipan[1][j]==qipan[2][j]&&qipan[0][j]!=' ')
		{
			return qipan[0][j];
		}
	}
	//判断对角线
	if (qipan[0][0]==qipan[1][1]&&qipan[1][1]==qipan[2][2]&&qipan[1][1]!=' ')
	{
		return qipan[1][1];
	}
	if (qipan[0][2]==qipan[1][1]&&qipan[1][1]==qipan[2][0]&&qipan[0][2]!=' ')
	{
		return qipan[0][2];
	}


	//判断平局,就是判断棋盘内是否还有空格
	if (is_full(qipan,hang,lie) == 0)
	{
		return 'Q';
	}
	else
	{
		return 'c';
	}


}

  那么我们三子棋的所有函数都写出来了,接下来只要在game()函数里调用即可,下面附上全部代码图(ps:我总觉的之间看全部代码里我写的注释比看我讲解更清楚明了)

void game()//
{
	//创建一个二维数组
	char qipan[HANG][LIE] = { 0 };
	//传参初始化棋盘,这里传的是实参,也就是首元素地址
	int_ti(qipan, HANG, LIE);//初始化棋盘
	print_qipan(qipan, HANG, LIE);//打印棋盘
	int ret = 0;
	while (1)
	{
		
		play_move(qipan, HANG, LIE);
		print_qipan(qipan, HANG, LIE);
		ret =is_win(qipan, HANG, LIE);
		if (ret!='c')
		{
			break;
		}
		computer_move(qipan, HANG, LIE);
		ret = is_win(qipan, HANG, LIE);
		if (ret!='c')
		{
			break;
		}
		print_qipan(qipan, HANG, LIE);
	}
	//判断返回值
	if (ret == 'Q')
	{
		cout << "平局";
	}
	else if (ret == '#')
	{
		cout << "电脑赢";
	}
	else if(ret == '*')
	{
		cout << "玩家赢";
	}
	else if(ret == 'Q')
	{
		cout << "平局";
	}
}

//玩家赢返回’*‘
//电脑赢返回“#”
//平局返回“Q”
//没有平局返回“C"

void text()
{
	srand((unsigned int)time(NULL));//通过时间戳设置随机数,只需要调用一次
	int input = 0;
	do
	{
		menu();
		cout << "请输入" << endl;
		cin >> input;
		switch (input)
		{
		case 1:
		{
			game();
			break;
		}
		case 0:
		{
			cout << "退出";
			break;
		}
		default:
		{
			cout << "输入错误,请从新输入";
			break;
		}
		}
	} while (input);
}
int main()
{
	text();
	return 0;
}

  下图是函数内部实现

#include"game.h"
void menu()
{
	cout << "*****************" << endl;
	cout << "**** 1.开始 *****" << endl;
	cout << "**** 0.退出 *****" << endl;
	cout << "*****************" << endl;
}




void int_ti(char qipan[HANG][LIE], int hang, int lie)
{
	//初始化全是空格
	for (int i = 0; i < hang; i++)
	{
		for (int j = 0; j < lie; j++)
		{
			qipan[i][j] = ' ';
		}
	}
}

void print_qipan(char qipan[HANG][LIE], int hang, int lie)
//每一次循环都只为了打印一个小方格
{
	for (int i = 0; i < hang; i++)//一共要打印几行
	{
		for (int j = 0; j < lie; j++)//决定每行打印多少小方格
		{
			cout << " " << qipan[i][j] << " ";
			if (j<lie-1)//判断是否到最后一列,最后一列不打印
			{
				cout << "|";
			}
		}
		cout << endl;//打印完一行的小方格要换行
		//打印分割行
		if (i<hang-1)//判断是否为最后一行,最后一行不打印
		{
			for (int k = 0; k < lie; k++)//打印一行的分割行
			{
				cout << "---";
				if (k < lie - 1)
				{
					cout << "|";
				}

			}
		}
		cout << endl;//打印完一行要换行;
	}
}

void play_move(char qipan[HANG][LIE], int hang, int lie)
{
	cout << "玩家下棋";
	while (1)
	{
		cout << "请输入坐标" << endl;
		int x, y = 0;
		cin >> x >> y;
		if (x >= 1 && x <= hang && y >= 1 && y <= lie)//先判断输入坐标是否正确
		{
			if (qipan[x - 1][y - 1] == ' ')//再判断该坐标是否被调用
			{
				qipan[x - 1][y - 1] = '*';
				break;
			}
		}
		else
		{
			cout << "坐标非法,请重新输入";
		}
	}
}

void computer_move(char qipan[HANG][LIE], int hang, int lie)
{
	cout << "电脑下棋" << endl;
	while (1)
	{

		int x = rand() % HANG;
		int y = rand() % LIE;
		if (qipan[x][y] == ' ')
		{
			qipan[x][y] = '#';
			break;
		}
	}
}

int is_full(char qipan[HANG][LIE], int hang, int lie)//遍历数组是否有空格
{
	for (int i = 0; i < hang; i++)
	{
		for (int j = 0; i < lie; j++)
		{
			if (qipan[i][j] == ' ')
			{
				return 1;

			}
		}
	}
	return 0;
}
char is_win(char qipan[HANG][LIE], int hang, int lie)
{
	//判断三行
	for (int i = 0; i < hang; i++)
	{
		if (qipan[i][0]==qipan[i][1]&&qipan[i][1]==qipan[i][2]&&qipan[i][0]!=' ')
		{
			return qipan[i][0];
			
		}
	}
	//判断三列
	for (int j = 0; j < lie; j++)
	{
		if (qipan[0][j]==qipan[1][j]&&qipan[1][j]==qipan[2][j]&&qipan[0][j]!=' ')
		{
			return qipan[0][j];
		}
	}
	//判断对角线
	if (qipan[0][0]==qipan[1][1]&&qipan[1][1]==qipan[2][2]&&qipan[1][1]!=' ')
	{
		return qipan[1][1];
	}
	if (qipan[0][2]==qipan[1][1]&&qipan[1][1]==qipan[2][0]&&qipan[0][2]!=' ')
	{
		return qipan[0][2];
	}


	//判断平局,就是判断棋盘内是否还有空格
	if (is_full(qipan,hang,lie) == 0)
	{
		return 'Q';
	}
	else
	{
		return 'c';
	}


}

  最后感谢各位的阅读,因为是第一次写技术类的博客,不知道怎么讲解,若有错误地点,讲的不好的地方,还请多多指教,祝各位读者万事顺利,谢谢~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值