【C语言】 三子棋小游戏 (超详解+游戏优化+原代码)

文章目录

前言

一、设计思路

二、游戏实现

1.菜单

2.初始化棋盘

3.打印棋盘

4.玩家下棋

5.电脑下棋

6.判断输赢

三、游戏演示

四、原代码

1.game.h

2.game.c

3.test.c

拓展:游戏优化

总结


前言

 三子棋的实现其实并不难,只要大家理解游戏是如何进行的就可以通过简单的代码来实现。本文会教大家如何使用C语言实现最简单的三子棋游戏,并且会说一点让电脑变“聪明”的方法。 


一、设计思路

100df4417f4ae2aa7887b3e4e16d242e.jpeg

1.设计一个游戏应该有个开始的选择菜单;

2.三子棋是个3×3的棋盘,创建一个二维数组作为棋盘;

3.棋盘初始为空的,先初始化棋盘;

4.打印出棋盘给玩家看,应打印出如图有线条的棋盘;

5.实现玩家下棋;

6.实现电脑下棋;

7.设计个判断输赢的函数,每次下棋后都进行一次判断。


二、游戏实现

  我们创建三个文件来进行编程,test.c,game.c,game.h,test.c用来放main函数进行游戏的测试运行,game.h和game.c分别放游戏的声明定义

1.菜单

  game.c中定义:

//打印菜单
void meun()
{
	printf("**********************\n");
	printf("**      1.play     ***\n");
	printf("**      0.exit     ***\n");
	printf("**********************\n");
}

  我们定义菜单有两个选项,输入“1”进入游戏函数,输入“0”退出游戏,其他则“输入错误”。我们考虑玩家玩完一局还想再玩的话菜单就要重复打印,那么菜单就应该放入循环里,而菜单必先打印一次,所以我们使用do while循环。

  test.c中实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

//三子棋游戏
void game()
{
    printf("游戏开始\n");
}

int main()
{
	int input = 0;
	do
	{
		meun();
		printf("请选择:>");
		scanf("%d", &input);
		if (1 == input)
		{
			game();
		}
		else if (0 == input)
		{
			printf("退出游戏\n");
		}
		else
			printf("输入错误,请重新输入\n");

	} while (input);

	return 0;
}

 测试:

f4b6fc30f8884356b49582fe36614e8e.png

2.初始化棋盘

  我们在头文件game.h中定义行ROW为3,列COL为3。这样子行和列就可以轻易更换。

#define ROW 3
#define COL 3

  game.c中定义:

我们定义创建的二维数组棋盘初始都为空格。

//初始化棋盘
void init_board(char board[ROW][COL])
{
	int i = 0;
	int j = 0;
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			board[i][j] = ' ';
		}
	}
}

3.打印棋盘

 2dd7ef137e294af0818f462ec7b1efc3.png

 但是我们发现循环打印的最后一次会少个“ | ”,所以我们在打印“ | ”前可以先做个判断来解决。

  game.c中定义:

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

 test.c中测试:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

//三子棋游戏
void game()
{
	char board[ROW][COL] = { '0' };

	printf("游戏开始:\n");
	//初始化棋盘
	init_board(board);
	//打印棋盘
	print(board,ROW,COL);
}

int main()
{
	int input = 0;
	do
	{
		meun();
		printf("请选择:>");
		scanf("%d", &input);
		if (1 == input)
		{
			game();
		}
		else if (0 == input)
		{
			printf("退出游戏\n");
		}
		else
			printf("输入错误,请重新输入\n");

	} while (input);

	return 0;
}

 47981a34709c47139e02dbaab4b2afae.png

4.玩家下棋

设计玩家下棋时要注意的是玩家要下的坐标有三种情况:

1.该坐标已有棋子;

2.该坐标非法(不在棋盘里);

3.改坐标为“ ”,可以下棋。

   所以我们要分别判断三种情况,只有第三种才允许玩家下棋(我定义玩家棋子为“ * ”),否则将重新进入循环让玩家再输入一遍。

小tip:玩家看到棋盘会下意识觉得棋盘的行列是从1开始的,但其实数组的下标是从0开始访问的,所以我们在获取玩家输入的数字时要进行减1的操作才能对应上。

  game.c中定义:

//玩家下棋
void player(char board[ROW][COL])
{
	int x = 0;
	int y = 0;
	printf("请输入要下的坐标:>");
	while (1)
	{
		scanf("%d %d", &x, &y);
		if (x>0 && x <= ROW&&y>0 && y <= COL)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
				printf("已有棋子,请重新输入:>");
		}
		else
			printf("坐标非法,请重新输入:>");
	}
}

  test.c中测试:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

//三子棋游戏
void game()
{
	char board[ROW][COL] = { '0' };

	printf("游戏开始:\n");
	//初始化棋盘
	init_board(board);
	//打印棋盘
	print(board,ROW,COL);
	while (1)
	{
        printf("玩家下棋:\n");
		//玩家下棋
		player(board);
		print(board, ROW, COL);
    }
}

int main()
{
	int input = 0;
	do
	{
		meun();
		printf("请选择:>");
		scanf("%d", &input);
		if (1 == input)
		{
			game();
		}
		else if (0 == input)
		{
			printf("退出游戏\n");
		}
		else
			printf("输入错误,请重新输入\n");

	} while (input);

	return 0;
}

 95801170a4eb479a9612689d6cdfc013.png

5.电脑下棋

   我们制作最简单的三字棋时考虑电脑是进行随机下棋的,所以我们用上rand()函数来生成随机数(使用rand函数生成随机数要在主函数先使用srand函数),%行或者列就可以得到0~2的数。只要棋盘中为“  ”就下棋。(我定义电脑棋子为“#”)

  game.c中定义:

//电脑下棋
void computer(char board[ROW][COL])
{
	while (1)
	{
		int x = rand() % ROW;
		int y = rand() % COL;

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

  test.c中测试 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#include<stdlib.h>

//三子棋游戏
void game()
{
	char board[ROW][COL] = { '0' };
	printf("游戏开始:\n");
	//初始化棋盘
	init_board(board);
	//打印棋盘
	print(board,ROW,COL);
	while (1)
	{
		printf("玩家下棋:\n");
        //玩家下棋
		player(board);
		printf("电脑下棋:\n");
        //电脑下棋
		computer(board);
		print(board, ROW, COL);
	}
}

//三子棋游戏
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		meun();
		printf("请选择:>");
		scanf("%d", &input);
		if (1 == input)
		{
			game();
		}
		else if (0 == input)
		{
			printf("退出游戏\n");
		}
		else
			printf("输入错误,请重新输入\n");

	} while (input);

	return 0;
}

e2afa8f018514c0fa2e0385cbb52f86e.png

6.判断输赢

我们要先知道判断时有几种情况:

1.玩家三子连在一起获胜:对角线,横,竖;

2.电脑三子连在一起获胜;

3.平局;

4.未分胜负,游戏继续;

我们不妨设judge函数结束时会返回一个字符类型:

1.玩家获胜返回“*”;

2.电脑获胜返回“#”;

3.平局返回“D”;

4.游戏继续返回“C”。

小tip:我们用循环的方式一个个访问数组里的元素,判断是第几种情况。我们发现玩家获胜返回的字符和玩家下的棋子都是“*”,所以可以直接return数组中的元素以此简化代码。(电脑获胜同上)

  game.c中定义:

//判断输赢
char judge(char board[ROW][COL])
{
	int i = 0;
	int j = 0;
	int flag = 1;

	//斜对角判断
	for (i = 0, j = 0; i < ROW-1, j < COL-1; i++, j++)
	{
		if (board[i][j] != board[i + 1][j + 1] || board[i][j] == ' ')
		{
			break;
		}
		if (i == ROW - 2)
			return board[i][j];
	}

	for (i = 0, j = ROW-1; i < ROW - 1, j >0; i++, j--)
	{
		if (board[i][j] != board[i + 1][j - 1] || board[i][j] == ' ')
		{
			break;
		}
		if (i == ROW - 2)
			return board[i][j];
	}

	//行判断
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL-1; j++)
		{
			if (board[i][j] != board[i][j + 1] || board[i][j] == ' ')
				break;
			if (j == COL - 2)
				return board[i][j];
		}
	}

	//列判断
	for (j = 0; j < COL; j++)
	{
		for (i = 0; i < ROW - 1; i++)
		{
			if (board[i][j] != board[i + 1][j] || board[i][j] == ' ')
				break;
			if (i == ROW - 2)
				return board[i][j];
		}
	}

	//平局判断
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] == ' ')
			{
				flag = 0;
				break;
			}
		}
	}
	if (flag == 1)
		return 'D';
	else
		return 'C';
}

 test.c中测试:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#include<stdlib.h>

//三子棋游戏
void game()
{
	char jud = 0;
	char board[ROW][COL] = { '0' };
	printf("游戏开始:\n");
	//初始化棋盘
	init_board(board);
	//打印棋盘
	print(board,ROW,COL);
	while (1)
	{
		printf("玩家下棋:\n");
		player(board);
		//print(board, ROW, COL);
		//判断输赢
		jud = judge(board);
		if (jud != 'C')
		{
			break;
		}
		printf("电脑下棋:\n");
		computer(board);
		jud = judge(board);
		if (jud != 'C')
		{
			break;
		}
		print(board, ROW, COL);
	}
	if (jud == '*')
	{
		print(board, ROW, COL);
		printf("恭喜你,获胜了\n");
	}
	if (jud == '#')
	{
		print(board, ROW, COL);
		printf("你输了,电脑获胜\n");
	}
	if (jud == 'D')
	{
		print(board, ROW, COL);
		printf("游戏结束,平局\n");
	}
}

int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		meun();
		printf("请选择:>");
		scanf("%d", &input);
		if (1 == input)
		{
			game();
		}
		else if (0 == input)
		{
			printf("退出游戏\n");
		}
		else
			printf("输入错误,请重新输入\n");

	} while (input);

	return 0;
}

b2818d8ddc84479ba076f36893f85eef.png


三、游戏演示

 3848622b8ff34b39bcf6713d77e2cb29.png


四、原代码

1.game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ROW 3
#define COL 3

//打印菜单
void meun();

//初始化棋盘
void init_board(char board[ROW][COL]);

//打印棋盘
void print(char board[ROW][COL], int row, int col);

//玩家下棋
void player(char board[ROW][COL]);

//电脑下棋
void computer(char board[ROW][COL]);

//判断输赢
char judge(char board[ROW][COL]);

2.game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

//打印菜单
void meun()
{
	printf("**********************\n");
	printf("**      1.play     ***\n");
	printf("**      0.exit     ***\n");
	printf("**********************\n");
}

//初始化棋盘
void init_board(char board[ROW][COL])
{
	int i = 0;
	int j = 0;
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			board[i][j] = ' ';
		}
	}
}

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

//玩家下棋
void player(char board[ROW][COL])
{
	int x = 0;
	int y = 0;
	printf("请输入要下的坐标:>");
	while (1)
	{
		scanf("%d %d", &x, &y);
		if (x>0 && x <= ROW&&y>0 && y <= COL)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
				printf("已有棋子,请重新输入:>");
		}
		else
			printf("坐标非法,请重新输入:>");
	}
}

//电脑下棋
void computer(char board[ROW][COL])
{
	while (1)
	{
		int x = rand() % ROW;
		int y = rand() % COL;

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

//判断输赢
char judge(char board[ROW][COL])
{
	int i = 0;
	int j = 0;
	int flag = 1;

	//斜对角判断
	for (i = 0, j = 0; i < ROW-1, j < COL-1; i++, j++)
	{
		if (board[i][j] != board[i + 1][j + 1] || board[i][j] == ' ')
		{
			break;
		}
		if (i == ROW - 2)
			return board[i][j];
	}

	for (i = 0, j = ROW-1; i < ROW - 1, j >0; i++, j--)
	{
		if (board[i][j] != board[i + 1][j - 1] || board[i][j] == ' ')
		{
			break;
		}
		if (i == ROW - 2)
			return board[i][j];
	}

	//行判断
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL-1; j++)
		{
			if (board[i][j] != board[i][j + 1] || board[i][j] == ' ')
				break;
			if (j == COL - 2)
				return board[i][j];
		}
	}

	//列判断
	for (j = 0; j < COL; j++)
	{
		for (i = 0; i < ROW - 1; i++)
		{
			if (board[i][j] != board[i + 1][j] || board[i][j] == ' ')
				break;
			if (i == ROW - 2)
				return board[i][j];
		}
	}

	//平局判断
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] == ' ')
			{
				flag = 0;
				break;
			}
		}
	}
	if (flag == 1)
		return 'D';
	else
		return 'C';
}

3.test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

//三子棋游戏
void game()
{
	char jud = 0;
	char board[ROW][COL] = { '0' };
	printf("游戏开始:\n");
	//初始化棋盘
	init_board(board);
	//打印棋盘
	print(board,ROW,COL);
	while (1)
	{
		printf("玩家下棋:\n");
		player(board);
		//print(board, ROW, COL);
		//判断输赢
		jud = judge(board);
		if (jud != 'C')
		{
			break;
		}
		printf("电脑下棋:\n");
		computer(board);
		jud = judge(board);
		if (jud != 'C')
		{
			break;
		}
		print(board, ROW, COL);
	}
	if (jud == '*')
	{
		print(board, ROW, COL);
		printf("恭喜你,获胜了\n");
	}
	if (jud == '#')
	{
		print(board, ROW, COL);
		printf("你输了,电脑获胜\n");
	}
	if (jud == 'D')
	{
		print(board, ROW, COL);
		printf("游戏结束,平局\n");
	}
}

int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		meun();
		printf("请选择:>");
		scanf("%d", &input);
		if (1 == input)
		{
			game();
		}
		else if (0 == input)
		{
			printf("退出游戏\n");
		}
		else
			printf("输入错误,请重新输入\n");

	} while (input);

	return 0;
}

拓展:游戏优化

我们很容易发现上面这样编出来的三子棋人机就像人工智障一样,明明玩家就要获胜了也不会阻止一下玩家,或者自己就快要赢了也还是乱下。这是因为电脑始终都是下的随机棋,那么我们就可以通过更改电脑下棋函数让电脑稍微聪明一点点:让电脑输赢判断时发现出现了两个相同的字符且第三个位置为“  ”时,就抢先下棋到“  ”上。

computer函数代码优化:

//电脑下棋
void computer(char board[ROW][COL])
{
	while (1)
	{
		int x = rand() % ROW;
		int y = rand() % COL;
		int i = 0;
		int j = 0;
		int a = 0;
		int b = 0;
		int count1 = 0;
		int count2 = 0;

		//抢中轴
		if (board[ROW / 2][COL / 2] == ' ')
		{
			board[ROW / 2][COL / 2] = '#';
			break;
		}

		//斜对角判断
		for (i = 0, j = 0, count1 = 0, count2 = 0; i < ROW, j < COL; i++, j++)
		{
			if (board[i][j] == '#')
				count1++;
			else if (board[i][j] == '*')
				count2++;
			else
			{
				a = i;
				b = j;
			}
		}

		if ((count1 == ROW - 1 || count2 == ROW - 1 )&& (count1 + count2) != ROW)
		{
			board[a][b] = '#';
			goto end;
		}


		for (i = 0, j = COL-1, count1 = 0, count2 = 0; i < ROW, j >=0; i++, j--)
		{
			if (board[i][j] == '#')
				count1++;
			else if (board[i][j] == '*')
				count2++;
			else
			{
				a = i;
				b = j;
			}
		}
		if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
		{
			board[a][b] = '#';
			goto end;
		}

		//行判断
		for (i = 0 ; i < ROW; i++)
		{
			count1 = 0;
			count2 = 0;
			for (j = 0; j < COL ; j++)
			{
				if (board[i][j] == '#')
					count1++;
				else if (board[i][j] == '*')
					count2++;
				else
				{
					a = i;
					b = j;
				}
			}
			if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
			{
				board[a][b] = '#';
				goto end;
			}
		}

		//列判断
		for (j = 0; j < COL; j++)
		{
			count1 = 0;
			count2 = 0;
			for (i = 0; i < ROW; i++)
			{
				if (board[i][j] == '#')
					count1++;
				else if (board[i][j] == '*')
					count2++;
				else
				{
					a = i;
					b = j;
				}
			}
			if ((count1 == ROW - 1 || count2 == ROW - 1) && (count1 + count2) != ROW)
			{
				board[a][b] = '#';
				goto end;
			}
		}

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			end:
			break;
		}
	}
}

 优化后游戏演示:

ea8ee7094c6f473587f3a90234da6ec3.png

 (虽然它已经变聪明了许多,但还是斗不过我们的计谋哈哈)


总结

  游戏的设计其实就是把游戏拆分成一个个板块来进行逐一实现,本文介绍了最基本的三子棋的设计,还有小小的优化方法。但是这个三子棋还有很大很大的优化空间,感兴趣的小伙伴可以自己好好研究一下。探究出更厉害的优化方案欢迎各位大佬留言私信,最后,感谢大家阅读到这~

猜你喜欢

转载自blog.csdn.net/weixin_60804760/article/details/127847385