三子棋(双人对决+人机对战)

在这里插入图片描述

test.c部分

main函数

int main()
{
	test();     //test可以测试该游戏的全部功能
	return 0;
}

一个程序的运行是从main函数开始,main函数结尾的,在写程序代码的时候尽量减少main函数中的代码,这样做的目的是使代码封装在函数内部,这样会使程序更加简洁,更清晰,容易修改,这样写代码是一个好的代码习惯。

test函数

//定义一个输入,代表游戏的开始或者退出
int input = 0;

//测试三子棋的函数
void test()
{
	srand((unsigned int)time(NULL));
	do
	{
		menu();  //打印一个菜单,提示 1开始 0退出
	again:   //防止输入错误时打印多份菜单
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)   //使用switch分几种不同输入时进入不同的函数
		{
		case 0:
			printf("退出游戏\n");
			break;
		case 1:
		case 2:
			system("cls");   //当进入一次游戏时,清一次屏幕
			game();
			break;
		default:
			printf("输入错误,重新输入!\n");
			goto again;
			break;
		}
	} while (input);    //当input=0时刚好退出,放在这里很合理
}

当程序执行时,我们要先设计一个菜单,给用户提供选择,我们要将用户选择的值存放起来,调用相对应的函数,在这里我用了一个switch语句来进行选择,把input设置为全局变量是因为我这里有三个选择,双人版,单人版,和退出。方便后面再双人版和单人版的逻辑实现。

game.h部分

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


#define ROW 3    //棋盘的行
#define COL 3    //棋盘的列

//游戏菜单
void menu();

//游戏实现
void game();

在这里引入整个工程所需要的头文件,其它的.c文件包含这个头文件,就可以实现该头文件里面包含的内容。
ROW 是游戏棋盘的行,COL 是列,这里使用宏定义是为了方便后期改成五子棋,四子棋之类的。
在这个头文件中同时也包含了game.c里面的部分函数声明。

game.c部分

游戏菜单

//游戏菜单
void menu()
{
	printf("**********************\n");
	printf("****  三  子  棋  ****\n");
	printf("****   1.双人对决 ****\n");
	printf("****   2.人机对决 ****\n");
	printf("****   0.退出     ****\n");
	printf("**********************\n");
}

在这里插入图片描述
在主函数里面会调用此游戏菜单,供用户选择。

棋盘的初始化

void Initmap(char arr[ROW][COL], int row, int col)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			arr[i][j] = ' ';
		}
	}
}

游戏的数据我们这里选择一个字符型二维数组来进行存放,开始初始化数组内容全部为’ '空格,这样设置后面棋盘打印的比较美观。

棋盘的打印

void Print(char arr[ROW][COL], int row, int col)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			printf(" %c |", arr[i][j]);
		}
		printf("\b \n");    //注意退格符要与空格搭配使用
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---|");
			}
			printf("\b \n");
		}
	}
}

这里我设置的为三子棋,所以只打印三行三列,但是在这里我们打印了5行,第一行打印数组的第一行内容,每打印一个符号后面跟一个|当分隔符,但是这样会多出来一个|,在这里我们就要使用\b退格符要搭配空格使用才起作用,就可以消除最后面的那个|,第二行打印一行下划线,用于区分每一行的,以此类推,但是这样也会多打印一行下滑线,这里我们用if语句进行判断,当打印到最后一行时,不打印下划线。
在这里插入图片描述

玩家下棋

void Input(char map[ROW][COL], int row, int col, char ret, char* gamer)
{
	int x, y;
	printf("%s输入:>", gamer);
	while (1)
	{
		scanf("%d %d", &x, &y);
		if (x > row || y > col || x <= 0 || y <= 0)
		{
			printf("非法输入,重新输入:>");
		}
		else
		{
			if (map[x - 1][y - 1] != ' ')
			{
				printf("位置重复占用,重新输入:>");
			}
			else
			{
				map[x - 1][y - 1] = ret;
				break;
			}
		}
	}
}

void Input(char map[ROW][COL], int row, int col, char ret, char* gamer)
该函数有四个参数第一个是我们传进去的棋盘,第二个是行数,第三个是列数,第四个是要存的符号,就是二维数组中保存的符号,方便后面双人版的实现。
1.首先我们要提示玩家下棋;
2.当玩家下完棋之后,我们要判断位置的合法性,有没有越界或者已经下过棋子;
3.当以上条件都成立时,我们将玩家输入的坐标,在二维数组中存放数据,在这里玩家下棋输入的下标,认为都是从1开始的,但是数组的下标是从0开始的,所以要-1。

电脑下棋

void ComperInput(char map[ROW][COL], int row, int col)
{
	// *
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;
		if (map[x][y] == ' ')
		{
			map[x][y] = '*';
			break;
		}
	}
}

void ComperInput(char map[ROW][COL], int row, int col);
该函数有三个参数;
第一个是二维数组,第二个是行数,第三个是列数;
1.电脑下棋不用提示;
2.电脑下棋的坐标我们用随机数函数生成,控制取值范围在:0~2之内;
3.这里只需用判断该坐标是否有棋子;

判断输赢

//‘D’表示平局,'A'表示玩家1胜,'B'表示玩家2胜,
// ‘C’表示游戏继续
//输入坐标为(1,1)时,实际在数组中的坐标是(0,0)
char is_win(char map[ROW][COL], int row, int col)
{
	
	int i, j;
	//A和B只能在进入函数时初始化一次
	int A = 1, B = 1;

	//记录下棋的次数
	count++;

	//行判断
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col - 1; j++)
		{
			if (map[i][j] != map[i][j + 1])
			{
				A = 0;
				B = 0;
				break;

				/*if (map[i][j] == '#' && map[i][j + 1] == '#')
				{
					A = 1;
				}

				if (map[i][j] == '*' && map[i][j + 1] == '*')
				{
					B = 1;
				}

				if (map[i][j] == ' ' || map[i][j + 1] == ' ')
				{
					A = 0, B = 0;
					break;
				}*/
			}
			else
			{
				if (map[i][j] == '#' && map[i][j + 1] == '#')
				{
					A = 1;
				}

				if (map[i][j] == '*' && map[i][j + 1] == '*')
				{
					B = 1;
				}

				if (map[i][j] == ' ' || map[i][j + 1] == ' ')
				{
					A = 0, B = 0;
					break;
				}

			}
		}

		if (A == 1)
		{
			return 'A';
		}
		else if (B == 1)
		{
			return 'B';
		}
	}
	//列判断
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col - 1; j++)
		{
			if (map[j][i] != map[j + 1][i])
			{
				A = 0;
				B = 0;
				break;
				/*if (map[j][i] == '#' && map[j][i + 1] == '#')
				{
					A = 1;
				}

				if (map[j][i] == '*' && map[j][i + 1] == '*')
				{
					B = 1;
				}

				if (map[j][i] == ' ' || map[j][i + 1] == ' ')
				{
					A = 0, B = 0;
					break;
				}*/

			}
			else
			{
				if (map[j][i] == '#' && map[j + 1][i] == '#')
				{
					A = 1;
				}

				if (map[j][i] == '*' && map[j + 1][i] == '*')
				{
					B = 1;
				}

				if (map[j][i] == ' ' || map[j + 1][i] == ' ')
				{
					A = 0, B = 0;
					break;
				}

			}

		}

		if (A == 1)
		{
			return 'A';
		}
		else if (B == 1)
		{
			return 'B';
		}

	}
	//左对角线判断
	for (i = 0,j = 0; i < row - 1,j < col - 1; i++, j++)
	{
		if (map[i][j] != map[i + 1][j + 1])
		{

			A = 0;
			B = 0;
			break;

			/*if (map[j][i] == '#' && map[j + 1][i + 1] == '#')
			{
				A = 1;
			}

			if (map[j][i] == '*' && map[j + 1][i + 1] == '*')
			{
				B = 1;
			}

			if (map[i][j] == ' ' || map[i + 1][j + 1] == ' ')
			{
				A = 0, B = 0;
			}*/

		}
		else
		{
			if (map[i][j] == '#' && map[i + 1][j + 1] == '#')
			{
				A = 1;
			}

			if (map[i][j] == '*' && map[i + 1][j + 1] == '*')
			{
				B = 1;
			}

			if (map[i][j] == ' ' || map[i + 1][j + 1] == ' ')
			{
				A = 0, B = 0;
				break;
			}
		}

	}
	if (A == 1)
	{
		return 'A';
	}
	else if (B == 1)
	{
		return 'B';
	}

	//右对角线判断
	for (i = 0, j = col - 1; (j >= 1 && i < row - 1); j--, i++)
	{
		if (map[i][j] != map[i + 1][j - 1])
		{

			A = 0;
			B = 0;
			break;

			/*if (map[i][j] == '#' && map[i + 1][j - 1] == '#')
			{
				A = 1;
			}

			if (map[i][j] == '*' && map[i + 1][j - 1] == '*')
			{
				B = 1;
			}

			if (map[i][j] == ' ' || map[i + 1][j - 1] == ' ')
			{
				A = 0, B = 0;
			}*/

		}
		else
		{
			if (map[i][j] == '#' && map[i + 1][j - 1] == '#')
			{
				A = 1;
			}

			if (map[i][j] == '*' && map[i + 1][j - 1] == '*')
			{
				B = 1;
			}

			if (map[i][j] == ' ' || map[i + 1][j - 1] == ' ')
			{
				A = 0, B = 0;
				break;
			}
		}

	}
	if (A == 1)
	{
		return 'A';
	}
	else if (B == 1)
	{
		return 'B';
	}

	//判断棋盘是否已满
	if (count == row * col)
	{
		//这里要将A和B置为0,否则出去A和B都等于1
		A = 0, B = 0;
		return 'D';
	}

	return 'C';
}

1.行判断,判断一行是否都是一个符号;
2.列判断,判断一列是否都是一个符号;
3.左对角线判断,判断对角线上的符号是否相同;
4.右对角线判断,判断对角线上的符号是否相同;
5.用返回的参数决定游戏谁赢谁输或者游戏结束,
//‘D’表示平局,'A’表示玩家1胜,'B’表示玩家2胜,
// ‘C’表示游戏继续

游戏引擎

void game()
{
	char ret = 0;   //用于记录输赢的符号
	char map[ROW][COL];  //用一个二维数组存储输入的数据
	Initmap(map, ROW, COL);    //初始化棋盘
	Print(map, ROW, COL);   //打印游戏棋盘

	count = 0;

	//下棋过程
	while (1)
	{
		//玩家1下棋
		Input(map, ROW, COL, '#', "玩家1");
		//清除上一次的棋盘
		system("cls");
		//下一次棋,打印一次棋盘
		Print(map, ROW, COL);   //打印游戏棋盘
		//判断游戏是否结束
		//返回值是C时游戏继续
		if ((ret = is_win(map, ROW, COL)) != 'C')
		{
			break;
		}
		//玩家2下棋
		switch (input)
		{
		case 1:
			Input(map, ROW, COL, '*', "玩家2");
			break;
		case 2:
			ComperInput(map, ROW, COL);
			break;
		}
		system("cls");
		Print(map, ROW, COL);   //打印游戏棋盘
		//判断游戏是否结束
		//返回值是C时游戏继续
		if ((ret = is_win(map, ROW, COL)) != 'C')
		{
			break;
		}
	}
	if (ret == 'A')
	{
		printf("玩家1胜!\n");
	}
	else if (ret == 'B')
	{
		printf("玩家2胜!\n");
	}
	else if (ret == 'D')
	{
		printf("平局\n");
	}
}

该函数是整合了上面的函数功能来实现我们想要的功能。
调用此函数就可以驱动游戏的核心。

完整代码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


#define ROW 3    //棋盘的行
#define COL 3    //棋盘的列

//定义一个输入,代表游戏的开始或者退出
int input = 0;

//记录判断的次数,等于下棋的个数
int count = 0;

//游戏菜单
void menu()
{
	printf("**********************\n");
	printf("****  三  子  棋  ****\n");
	printf("****   1.双人对决 ****\n");
	printf("****   2.人机对决 ****\n");
	printf("****   0.退出     ****\n");
	printf("**********************\n");
}

void Print(char arr[ROW][COL], int row, int col)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			printf(" %c |", arr[i][j]);
		}
		printf("\b \n");    //注意退格符要与空格搭配使用
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---|");
			}
			printf("\b \n");
		}
	}
}

void Initmap(char arr[ROW][COL], int row, int col)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			arr[i][j] = ' ';
		}
	}
}


//玩家下棋用#,电脑下棋用*
void Input(char map[ROW][COL], int row, int col, char ret, char* gamer)
{
	int x, y;
	printf("%s输入:>", gamer);
	while (1)
	{
		scanf("%d %d", &x, &y);
		if (x > row || y > col || x <= 0 || y <= 0)
		{
			printf("非法输入,重新输入:>");
		}
		else
		{
			if (map[x - 1][y - 1] != ' ')
			{
				printf("位置重复占用,重新输入:>");
			}
			else
			{
				map[x - 1][y - 1] = ret;
				break;
			}
		}
	}
}

//电脑下棋
void ComperInput(char map[ROW][COL], int row, int col)
{
	// *
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;
		if (map[x][y] == ' ')
		{
			map[x][y] = '*';
			break;
		}
	}
}

//‘D’表示平局,'A'表示玩家1胜,'B'表示玩家2胜,
// ‘C’表示游戏继续
//输入坐标为(1,1)时,实际在数组中的坐标是(0,0)
char is_win(char map[ROW][COL], int row, int col)
{
	
	int i, j;
	//A和B只能在进入函数时初始化一次
	int A = 1, B = 1;

	//记录下棋的次数
	count++;

	//行判断
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col - 1; j++)
		{
			if (map[i][j] != map[i][j + 1])
			{
				A = 0;
				B = 0;
				break;

				/*if (map[i][j] == '#' && map[i][j + 1] == '#')
				{
					A = 1;
				}

				if (map[i][j] == '*' && map[i][j + 1] == '*')
				{
					B = 1;
				}

				if (map[i][j] == ' ' || map[i][j + 1] == ' ')
				{
					A = 0, B = 0;
					break;
				}*/
			}
			else
			{
				if (map[i][j] == '#' && map[i][j + 1] == '#')
				{
					A = 1;
				}

				if (map[i][j] == '*' && map[i][j + 1] == '*')
				{
					B = 1;
				}

				if (map[i][j] == ' ' || map[i][j + 1] == ' ')
				{
					A = 0, B = 0;
					break;
				}

			}
		}

		if (A == 1)
		{
			return 'A';
		}
		else if (B == 1)
		{
			return 'B';
		}
	}
	//列判断
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col - 1; j++)
		{
			if (map[j][i] != map[j + 1][i])
			{
				A = 0;
				B = 0;
				break;
				/*if (map[j][i] == '#' && map[j][i + 1] == '#')
				{
					A = 1;
				}

				if (map[j][i] == '*' && map[j][i + 1] == '*')
				{
					B = 1;
				}

				if (map[j][i] == ' ' || map[j][i + 1] == ' ')
				{
					A = 0, B = 0;
					break;
				}*/

			}
			else
			{
				if (map[j][i] == '#' && map[j + 1][i] == '#')
				{
					A = 1;
				}

				if (map[j][i] == '*' && map[j + 1][i] == '*')
				{
					B = 1;
				}

				if (map[j][i] == ' ' || map[j + 1][i] == ' ')
				{
					A = 0, B = 0;
					break;
				}

			}

		}

		if (A == 1)
		{
			return 'A';
		}
		else if (B == 1)
		{
			return 'B';
		}

	}
	//左对角线判断
	for (i = 0,j = 0; i < row - 1,j < col - 1; i++, j++)
	{
		if (map[i][j] != map[i + 1][j + 1])
		{

			A = 0;
			B = 0;
			break;

			/*if (map[j][i] == '#' && map[j + 1][i + 1] == '#')
			{
				A = 1;
			}

			if (map[j][i] == '*' && map[j + 1][i + 1] == '*')
			{
				B = 1;
			}

			if (map[i][j] == ' ' || map[i + 1][j + 1] == ' ')
			{
				A = 0, B = 0;
			}*/

		}
		else
		{
			if (map[i][j] == '#' && map[i + 1][j + 1] == '#')
			{
				A = 1;
			}

			if (map[i][j] == '*' && map[i + 1][j + 1] == '*')
			{
				B = 1;
			}

			if (map[i][j] == ' ' || map[i + 1][j + 1] == ' ')
			{
				A = 0, B = 0;
				break;
			}
		}

	}
	if (A == 1)
	{
		return 'A';
	}
	else if (B == 1)
	{
		return 'B';
	}

	//右对角线判断
	for (i = 0, j = col - 1; (j >= 1 && i < row - 1); j--, i++)
	{
		if (map[i][j] != map[i + 1][j - 1])
		{

			A = 0;
			B = 0;
			break;

			/*if (map[i][j] == '#' && map[i + 1][j - 1] == '#')
			{
				A = 1;
			}

			if (map[i][j] == '*' && map[i + 1][j - 1] == '*')
			{
				B = 1;
			}

			if (map[i][j] == ' ' || map[i + 1][j - 1] == ' ')
			{
				A = 0, B = 0;
			}*/

		}
		else
		{
			if (map[i][j] == '#' && map[i + 1][j - 1] == '#')
			{
				A = 1;
			}

			if (map[i][j] == '*' && map[i + 1][j - 1] == '*')
			{
				B = 1;
			}

			if (map[i][j] == ' ' || map[i + 1][j - 1] == ' ')
			{
				A = 0, B = 0;
				break;
			}
		}

	}
	if (A == 1)
	{
		return 'A';
	}
	else if (B == 1)
	{
		return 'B';
	}

	//判断棋盘是否已满
	if (count == row * col)
	{
		//这里要将A和B置为0,否则出去A和B都等于1
		A = 0, B = 0;
		return 'D';
	}

	return 'C';
}

//游戏实现
void game()
{
	char ret = 0;   //用于记录输赢的符号
	char map[ROW][COL];  //用一个二维数组存储输入的数据
	Initmap(map, ROW, COL);    //初始化棋盘
	Print(map, ROW, COL);   //打印游戏棋盘

	count = 0;

	//下棋过程
	while (1)
	{
		//玩家1下棋
		Input(map, ROW, COL, '#', "玩家1");
		//清除上一次的棋盘
		system("cls");
		//下一次棋,打印一次棋盘
		Print(map, ROW, COL);   //打印游戏棋盘
		//判断游戏是否结束
		//返回值是C时游戏继续
		if ((ret = is_win(map, ROW, COL)) != 'C')
		{
			break;
		}
		//玩家2下棋
		switch (input)
		{
		case 1:
			Input(map, ROW, COL, '*', "玩家2");
			break;
		case 2:
			ComperInput(map, ROW, COL);
			break;
		}
		system("cls");
		Print(map, ROW, COL);   //打印游戏棋盘
		//判断游戏是否结束
		//返回值是C时游戏继续
		if ((ret = is_win(map, ROW, COL)) != 'C')
		{
			break;
		}
	}
	if (ret == 'A')
	{
		printf("玩家1胜!\n");
	}
	else if (ret == 'B')
	{
		printf("玩家2胜!\n");
	}
	else if (ret == 'D')
	{
		printf("平局\n");
	}
}



//测试三子棋的函数
void test()
{
	srand((unsigned int)time(NULL));
	do
	{
		menu();  //打印一个菜单,提示 1开始 0退出
	again:   //防止输入错误时打印多份菜单
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)   //使用switch分几种不同输入时进入不同的函数
		{
		case 0:
			printf("退出游戏\n");
			break;
		case 1:
		case 2:
			system("cls");   //当进入一次游戏时,清一次屏幕
			game();
			break;
		default:
			printf("输入错误,重新输入!\n");
			goto again;
			break;
		}
	} while (input);    //当input=0时刚好退出,放在这里很合理
}



int main()
{
	test();
	return 0;
}

结语

到这里这篇博客已经结束啦。
这份博客👍如果对你有帮助,给博主一个免费的点赞以示鼓励欢迎各位🔎点赞👍评论收藏⭐️,谢谢!!!
如果有什么疑问或不同的见解,欢迎评论区留言欧👀

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值