您现在的位置是:首页 >技术杂谈 >【C语言】三子棋小游戏的思路及实现(内附代码)网站首页技术杂谈

【C语言】三子棋小游戏的思路及实现(内附代码)

李昕泽的小仓库 2024-06-17 10:19:58
简介【C语言】三子棋小游戏的思路及实现(内附代码)

简单不先于复杂,而是在复杂之后。

89efcc89ac61428db4d5b6639b2bd948.jpeg

目录

1. 分文件实现

2.分步骤实现 

2.1 游戏菜单 

2.2 创建棋盘 

2.3 初始化棋盘 

2.4 打印棋盘 

2.5 玩家下棋 

2.6 电脑下棋 

2.7 判断输赢 

3. 附完整代码

3.1 test.c 

3.2 game.h 

3.2 game.c 


 

1. 分文件实现

当我们实现一个有较多功能的三子棋小游戏时,我们要用到许多库函数,需要包含头文件,还有符号的定义,函数的声明等等,还要写很多“高内聚、低耦合”的函数来实现具体的一些功能,所以代码量会非常庞大。

我们就需要在一个工程里创建多个文件,赋予其不同的分工,来使我们的程序可读性提高,清晰易懂。

我在vs2019中创建了3个文件来实现三子棋小游戏:

test.c  //测试游戏的逻辑

game.c  //游戏代码的实现
game.h  //游戏代码的声明(函数声明,符号定义)

 

2.分步骤实现 

在写一个完整的大型的程序之前,我们需要先梳理好实现程序的逻辑:

1. 我们需要实现一个游戏菜单,来让用户选择游戏或者退出游戏,打印在屏幕上。

2. 既然要下棋,我们需要提供一个棋盘来承载棋子,所以下一步我们要创建一个棋盘。

3. 创建了棋盘之后,我们需要将棋盘初始化,将所有的格子都初始化为空格。

4. 上一步仅仅初始化了棋盘,并没有打印到屏幕上,我们需要让用户看到棋盘才可以正常下棋,所以要打印棋盘。

5. 棋盘已就位,我们要开始下棋,由我们来和电脑对弈,所以这部分要实现的功能是玩家在棋盘上下棋。我们还需要在其中实现一个检验合法性的功能,来检验棋盘是否已经下满了,如果已经下满了,当然不能继续在棋盘上下棋。

6. 玩家下棋之后轮到电脑下棋。

7.当双方达到一定条件之后,一方或输或赢或双方平局,我们需要实现判断输赢的功能。 

1. 游戏菜单

2. 创建棋盘

3. 初始化棋盘

4. 显示棋盘

5. 玩家下棋

6. 电脑下棋

7. 判断输赢

2.1 游戏菜单 

我们在 game.c 和 test.c 里面均要用到库函数以及函数,为了更加便捷,我们把所有库函数头文件的包含、宏定义以及函数声明都放到 game.h 中,然后在  game.c 和 test.c 中包含上game.h。

首先在 main 函数中实现一个菜单,我们选择用do while循环来实现,因为无论游戏如何进行,都必须先让用户看到菜单界面选择是否进入游戏。do while 循环的特点就是先去做,再去判断条件,至少执行一次。

我们用 printf 函数和 scanf 函数来提醒用户进行选择并且输入选择。

用户选择之后我们需要对用户的选择做出相应的反馈,选择进入游戏,就进入游戏逻辑的函数,或者选择退出,选错了就要重新选择。

 接下来测试一下功能:

2.2 创建棋盘 

接下来创建一个行列都是3的棋盘

此时我们用到符号定义,用 ROW 和 COL 分别代表行和列的值,这样做的好处是我们在之后维护或者迭代代码的时候,不必修改所有行列的值,直接在头文件里更改就可以·,同时也增加了代码的可读性。

2.3 初始化棋盘 

接下来我们需要写一个函数来初始化棋盘,并且将每一个格子都设置为空格。

在 test.c 文件中调用函数,game.h 中声明函数,game.c中定义函数。

函数调用: 

函数声明:

 函数定义:

2.4 打印棋盘 

接下来我们需要写一个打印棋盘函数,用来打印当前棋盘的状态。

函数调用:

 函数声明:

函数定义:

测试一下:

但是如果这么写的话不便于维护,如果我们后期要迭代成五子棋,需要打印5*5的棋盘,当ROW 和 COL 的值更改了之后,打印的行没有问题,但是列还是不变,会变成这个样子:

为了适应更多情况,更改代码如下:

 

行列改为5的情况也适用:

 

2.5 玩家下棋 

接下来我们要实现让玩家能够输入坐标并在棋盘上下棋。

函数调用:

玩家下棋实际上还是对表示棋盘的数组进行操作,而且要检验下棋位置是否合法,所以函数传参的时候需要把数组和行列都传过去。

而且玩家下棋和电脑下棋是一个不断交互的过程,双方轮次下棋,直到一方获胜或者平局为止。

我们暂时不考虑游戏终止的条件,用一个 while 循环里面置玩家和电脑下棋的函数。

注:验证两点是否合法

1. 落子范围是否合法

2. 落子位置是否已被占用

函数声明:

 

函数定义:

2.6 电脑下棋 

要实现电脑下棋,我们可以用生成随机数的方法,然后通过对生成随机数的限制控制随机数生成的范围,随机数代表 board 这个二维数组的坐标。

然后通过合法性验证:

如果随机数生成的坐标处没有下棋,则电脑落子。

如果已经下棋,则继续循环生成随机数,直至电脑落子后跳出循环。

函数调用:

 

函数声明:

 

函数定义:

 

包含头文件:

srand 函数需要包含<stdlib.h>

time 函数需要包含<time.h>

 

设置随机数生成起点:

 

2.7 判断输赢 

我们要实现的这个函数需要判断四种状态:

1. 玩家赢 - ’*‘

2. 电脑赢 - ’#‘

3. 平局 - 'Q'

4. 继续 - 'C'

我们可以约定不同状态返回不同的符号如上

在每一次下棋后都要判断一次输赢,且判断输赢之后显示最终的棋盘。

 

函数声明:

 

函数定义:

 

3. 附完整代码

3.1 test.c 

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void game()
{
	char ret = 0;
	char board[ROW][COL] = { 0 };//创建棋盘

	InitBoard(board, ROW,	COL);//初始化棋盘
	DisplayBoard(board, ROW, COL);//打印棋盘
	while (1)
	{
		PlayerMove(board, ROW, COL);//玩家下棋
		ret = IsWin(board, ROW, COL);//判断输赢
		if (ret != 'C')
		{
			break;
		}
		DisplayBoard(board, ROW, COL);

		ComputerMove(board, ROW, COL);//电脑下棋
		ret = IsWin(board, ROW, COL);//判断输赢
		if (ret != 'C')
		{
			break;
		}
		DisplayBoard(board, ROW, COL);

	}
	if (ret == '*')
	{
		printf("
玩家获胜!!!!!!!!!!!!!!!!
");
	}
	else if (ret == '#')
	{
		printf("
电脑获胜!!!!!!!!!!!!!!!!
");
	}
	else
	{
		printf("
平局
");
	}
	DisplayBoard(board, ROW, COL);//打印棋盘

}

void menu()
{
	printf("********************
");
	printf("***1.play  0.exit***
");
	printf("********************
");
}

int main()
{
	srand((unsigned int) time(NULL));//设置随机数生成的起点

	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("
已退出");
			break;
		default:
			printf("
选错了,请重新选择");
			break;
		}
		
	} while (input);

	return 0;
}#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void game()
{
	char ret = 0;
	char board[ROW][COL] = { 0 };//创建棋盘

	InitBoard(board, ROW,	COL);//初始化棋盘
	DisplayBoard(board, ROW, COL);//打印棋盘
	while (1)
	{
		PlayerMove(board, ROW, COL);//玩家下棋
		ret = IsWin(board, ROW, COL);//判断输赢
		if (ret != 'C')
		{
			break;
		}
		DisplayBoard(board, ROW, COL);

		ComputerMove(board, ROW, COL);//电脑下棋
		ret = IsWin(board, ROW, COL);//判断输赢
		if (ret != 'C')
		{
			break;
		}
		DisplayBoard(board, ROW, COL);

	}
	if (ret == '*')
	{
		printf("
玩家获胜!!!!!!!!!!!!!!!!
");
	}
	else if (ret == '#')
	{
		printf("
电脑获胜!!!!!!!!!!!!!!!!
");
	}
	else
	{
		printf("
平局
");
	}
	DisplayBoard(board, ROW, COL);//打印棋盘

}

void menu()
{
	printf("********************
");
	printf("***1.play  0.exit***
");
	printf("********************
");
}

int main()
{
	srand((unsigned int) time(NULL));//设置随机数生成的起点

	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("
已退出");
			break;
		default:
			printf("
选错了,请重新选择");
			break;
		}
		
	} while (input);

	return 0;
}

 

3.2 game.h 

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3

//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);

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

//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);

//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);

//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局 - 'Q'
//继续 - 'C'

char IsWin(char board[ROW][COL], int row, int col);

 

3.2 game.c 

 

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

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

//第一个版本
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//	int i = 0;
//	for (i = 0; i < row; i++)
//	{
//		//打印数据
//		printf(" %c | %c | %c 
", board[i][0], board[i][1], board[i][2]);
//		//打印分割行
//		if(i<row-1)
//		{
//			printf("---|---|---
");
//
//		}
//	}
//}

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		//打印数据
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
			{
				printf("|");
			}
		}
		printf("
");

		//打印分割行
		if (i < row - 1)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
			printf("
");
		}
	}
}

void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;

	printf("
玩家下棋:
");

	while (1)
	{
		printf("
请输入坐标:
");
		scanf("%d %d", &x, &y);
		//坐标范围合法的判断
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
		}
		else
		{
			printf("
坐标被占用,请选择其他位置
");
		}
	}
}

void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("
电脑下棋:
");

	int x = 0;
	int y = 0;

	while (1)
	{
		x = rand() % row;//0~2
		y = rand() % col;//0~2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

//棋盘被下满了返回1
//棋盘没下满返回0
int IsFull(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (board[i][j] != '0')
			{
				return 0;//不满
			}
		}
	}
	return 1;
}

char IsWin(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 = 0;
	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 (IsFull(board, row, col))
	{
		return 'Q';
	}

	//游戏继续
	return 'C';

}

 

 

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。