C语言实现简易三子棋项目

逻辑分析过程

1.游戏菜单

一个游戏最基础的部分就说选择菜单,玩家可以选择玩或者退出这个游戏。当然设计的游戏不能只玩一次就得重新打开,所以我们利用循环的方式让玩家选择玩或者不玩。
如果选择玩 -->: 进入游戏->游戏结束->重新进行选择;
如果选择不玩–>:结束循环,跳出选择菜单。


那么如果通过代码进行实现呢?
首先是玩家通过输入来进行选择,我们设置一个变量 input 来接受玩家的选择信息。并且用do{}while;循环 来进行控制。用do{}while;循环的好处是能让循环体至少循环一次。


那么循环体内部又该怎么实现呢?
既然是通过玩家输入,对玩或者不玩进行选择;那么我们直接启动 选择语句switch(); 来进行控制。


我们作为开发者知道可以通过输入进行选择,但是玩家并不知道呀,我们得通过打印对玩家进行提示,并且应该有一个美化的菜单设计,便于玩家进行操作。所以我们设计一个menu()函数 作为我们的菜单。
由于其只需要打印菜单内容,并不需要返回数值,所以函数类型是void。


由于我们是循环体,处于设计考虑我们提示玩家输入1开始游戏,输入0结束游戏,并且让input作为循环条件,正好while(0) 为假,循环结束。

当然在switch语句中,我们也要考虑玩家错误输入的情况,不能因为玩家的一次错误输入程序便彻底崩溃了。我们在default 中应该也进行相对应的提示,让玩家可以重新进入选择菜单进行输入。

我们写道这里已经可以展现出一个逻辑合理的主菜单内容了,先进行测试一下:
下面是代码部分:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

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

int main()
{
	int input = 0;
	do {
		menu();
		printf("请输入:->");
		scanf("%d",&input);

		switch (input)
		{
		case 1:
			printf("游戏进入成功!!\n");
			break;
		case 0:
			printf("游戏已成功退出!!\n");
			break;
		default:
			printf("您输入有误,请重新进行选择\n");
			break;

		}
			
	} while (input);

	return 0;
}

运行结果如图所示:
在这里插入图片描述

2.三子棋棋盘的设计

写到这里的时候,我们的函数就已经开始逐渐变得冗杂了,出于可读性的考虑,我们采用模块化编程。我在这里解释一下模块化编程。


模块化编程

将一个项目中的各个模块的代码放在不同的.c文件里,在.h文件(头文件)里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要 #include “XXX.h”注:自定义的头文件并不是<>,而是" "双引号引用的)。
模块化编程的优点:
使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等。


在这里,我们的游戏逻辑代码还没有编写,所以我们新建一个 game.c文件和 game.h文件 ,game.h用于存放函数的声明。 — 别忘记在原本的.c文件中进行引用
我们现在原.c文件中设置一个 game()函数 用来展示游戏功能。
并且这个game()函数 需要连接在switch case 1:之后。

我们在设置game函数时,首先要设计一个棋盘。那这个棋盘上的应该是由数组进行存储的,并且这个数组的大小是3*3。我们先设置一个char 类型数组,其大小是3 * 3。 这里不推荐直接写arr[3][3],而是建议利用宏定义 #define,这样便于后续代码信息的修改和和统一
我在这里宏定义了ROW 3和COL 3,代表了行为3和列为3
有了数组对棋盘信息进行存储了,我们还要对其初始化,让一开始棋盘的内容均为空格。我们再设置一个boardInit()函数来初始化数组信息。
注:这里的函数传递需要传递二维数组,特此补充一下知识点


二维数组的传递

二维数组的传递形式是(int* parr [ ][ ]),但是数组名通常就指代数组第一个元素的地址;并且通常我们需要利用到二维数组的行数和列数,也要进行传递。
所以我们在建立函数进行接收参数时代码应该如下格式:
char arr[ROW][COL]表面接受的是ROW 行 COL列的二维数组

void boardInit(char arr[ROW][COL],int row,int col)

而传递二维数组时,只需要传递地址即可

boardInit(arr,ROW,COL);

到这里,我们棋盘展示的部分已经做完了,运行检测
代码如下:
test.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"

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


void game()
{
	char arr[ROW][COL] = { 0 };
	boardInit(arr,ROW,COL);
	display(arr, ROW, COL);

}

int main()
{
	int input = 0;
	do {
		menu();
		printf("请输入->:");
		scanf("%d",&input);

		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("游戏已成功退出!!\n");
			break;
		default:
			printf("您输入有误,请重新进行选择\n");
			break;

		}
			
	} while (input);

	return 0;
}

game.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"

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

void display(char arr[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (j != col - 1)
			{
				printf(" %c |", arr[i][j]);
			}
			else
				printf(" %c \n",arr[i][j]);
		}

		
		if (i != row - 1)
		{
			for (int j = 0; j < col; j++)
			{
				if (j != col - 1)
					printf("---|");
				else
					printf("---\n");
			}
		}

	}
}

game.h()

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

#define ROW 3
#define COL 3

//将二维数组里的数据初始化为空格
void boardInit(char arr[ROW][COL], int row, int col);

//通过打印展现出棋盘边界,便于区分
void display(char arr[ROW][COL], int row, int col);

3.玩家和电脑下棋逻辑

根据上面的逻辑,我们再设置两个函数:一个是playerMove();代表玩家进行下棋操作;另一个是computerMove();代表电脑进行下棋操作。当然,下棋操作是一个循环操作,先是玩家下棋,然后电脑下棋直到一方胜利或者是平局。 我们先不考虑胜利条件,先用死循环while(1)把下棋操作写入。


这里的玩家下棋函数需要接受玩家输入的坐标信息,但是玩家并不是程序员,还是会按照常理判断坐标,即1~3的范围进行输入。
当然这里也一个循环输入过程,
1.如果玩家输入的数不在棋盘范围之内,需要返回重新输入
2.如果玩家输入的数已经被占用,需要返回重新输入
这里还是利用while(1)死循环来输入,如果输入正确则对齐赋值并break跳出。

电脑下棋同理,只不过落子由玩家输入变成了电脑随机生成,这里我们调用 生成随机数的函数。

生成随机数

先在主函数中引用一个srand();函数,表示生成一个随机数种子,让生成随机数变成真随机数 需要调用<stdlib.h>头文件
再利用time();函数使srand始终发生变化:
代码如下:(这里的unsigned int是为了强制类型转换time()函数返回值)

srand((unsigned int)time(NULL));

这样我们使用rand();函数生成的随机数就是真随机数了。
当然我们需要控制随机数的范围,让他在0~2之间,所以用

x = rand() % row;
y = rand() % col;

注:这里判断是否已被占用是字符比较,可以用==,字符串比较则不能用== 而是用strcmp()函数


我们在调用完下棋函数后,记得重新展示一下棋盘,来向玩家进行展示。

运行结果如下:

在这里插入图片描述到这里,我们的下棋操作就已经编写完成了,但是我们还无法判断胜利或者平局,所以我们需要编写判断条件。

4.游戏结束条件(胜利/平局)

当我们在进行下棋循环操作时,棋盘有四种状态
1.玩家胜利
2.电脑胜利
3.平局
4.游戏仍在进行中


由于有这四种状态,所以我们需要调用一个函数,在任一方下棋操作进行后进行判断,并返回相应值来判断棋盘状态。
我们设置一个Iswin();函数,并且我们需要其返回值来判断状态,设其为char 类型函数。
由于只有第四种状态是不结束游戏的,我们在下棋循环中先对状态4进行判断,如果是状态4,我们返回c。如果Iswin()的返回值 != c,那么就说明游戏结束了。如果 == c,下棋循环仍进行。


对于其他三种结束状态,我们通过判Iswin()函数的返回值来判断属于那种结束条件,我们设置以下的返回值:
1.玩家胜利 -> ‘’ * "
2.电脑胜利 -> " # "
3.平局 -> ‘q’


对于胜利结局,无论是电脑还是玩家,都是判断如下三种情况
1.遍历每一行,看哪一行是相同且不等于空格
2.遍历每一列,看那一列是相同的且不等于空格
3.遍历两个对角线,看对角线上是否均相等且不等同于空格

如果有相同情况,则直接返回相同的元素(对照前文设置判断条件)

注意:这里一定要注意还有全为空格的情况,必须要排除


对于平局结局:
1.先判断棋盘是否已经为满
这里可以再设置一个函数isFull()来检测棋盘是否已经满了,遍历棋盘,如果有一个格子 == 空格 return 0; 如果遍历完都没有空格,return -1
2.如果棋盘已经满且没有胜利,判定为平局。


写到这里,整个三子棋的项目逻辑基本完成,下面是代码展示

整体代码内容展示

test.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"

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


void game()
{
	char ret = 0;

	char arr[ROW][COL] = { 0 };
	//初始化棋盘数据
	boardInit(arr,ROW,COL);
	//打印棋盘布局
	display(arr, ROW, COL);
	//电脑和玩家下棋
	while (1)
	{
		//玩家下棋
		playerMove(arr, ROW, COL);
		//展示棋盘
		display(arr, ROW, COL);
		//判断游戏是否结束
		ret = Iswin(arr, ROW, COL);
		if (ret != 'c')
			break;
		//电脑下棋
		computerMove(arr, ROW, COL);
		//展示棋盘
		display(arr, ROW, COL);
		//判断游戏是否结束
		ret = Iswin(arr, ROW, COL);
		if (ret != 'c')
			break;	
	}
	
	if (ret == '*')
	{
		printf("玩家胜利!!!\n");
	}
	else if (ret == '#')
		printf("电脑胜利!!!\n");
	else
		printf("平局了!!!!");
		

}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do {
		menu();
		printf("请输入->:");
		scanf("%d",&input);

		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("游戏已成功退出!!\n");
			break;
		default:
			printf("您输入有误,请重新进行选择\n");
			break;

		}
			
	} while (input);

	return 0;
}

game.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"

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

void display(char arr[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (j != col - 1)
			{
				printf(" %c |", arr[i][j]);
			}
			else
				printf(" %c \n",arr[i][j]);
		}

		
		if (i != row - 1)
		{
			for (int j = 0; j < col; j++)
			{	
				if (j != col - 1)
					printf("---|");
				else
					printf("---\n");
			}
		}

	}
}

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

	while (1)
	{
		printf("现在是玩家回合\n");
		printf("请输入下棋的坐标信息->:");
		scanf("%d %d", &x, &y);

		if ((x > 0 && x <= row) && (y > 0 && y <= col))
		{
			if (arr[x - 1][y - 1] == ' ')
			{
				arr[x - 1][y - 1] = '*';
				break;
			}
			else
				printf("你输入的坐标已被落子,请重新输入\n");
		}
		else
			printf("这是一个错误的坐标,请重新下棋\n");
	}
}

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

	printf("现在是电脑下棋:\n");
	while (1)
	{
		
		x = rand() % row;
		y = rand() % col;
		if (arr[x][y] == ' ')
		{
			arr[x][y] = '#';
			break;
		}
	}
	
}

char Iswin(char arr[ROW][COL], int row, int col)
{
	int i = 0;

	//遍历每一行,看有没有行相同
	for (i = 0; i < row; i++)
	{
		if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')
			return arr[i][0];
	}

	//遍历每一列,看看有没有列相同
	for (i = 0; i < col; i++)
	{
		if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ')
			return arr[0][i];
	}

	//遍历对角线
	if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[1][1] != ' ')
		return arr[1][1];
	if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
		return arr[1][1];


	//判断平局
	if (isFull(arr, ROW, COL) == -1)
		return 'q';

	else
		return 'c';
}

int isFull(char arr[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (arr[i][j] == ' ')
				return 0;
		}
	}
	return -1;
}

game.h

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

#define ROW 3
#define COL 3

//将二维数组里的数据初始化为空格
void boardInit(char arr[ROW][COL], int row, int col);

//通过打印展现出棋盘边界,便于区分
void display(char arr[ROW][COL], int row, int col);

//玩家进行下棋:
void playerMove(char arr[ROW][COL], int row, int col);

//电脑进行下棋
void computerMove(char arr[ROW][COL], int row, int col);

//判断输赢
char Iswin(char arr[ROW][COL], int row, int col);

//判断棋盘是否为满
int isFull(char arr[ROW][COL], int row, int col);

运行结果:
1.玩家胜利
在这里插入图片描述
2.电脑胜利
在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
五子棋是一种相对简单的棋类游戏,可以用C语言实现人机对战的功能。 首先,我们需要定义一个棋盘的二维数组来表示游戏界面,可以选择一个15×15的大小。用1代表玩家落子的地方,用2代表电脑落子的地方,用0代表空白的地方。 接下来,需要编写一个函数来打印当前的棋盘状态。可以使用循环来遍历棋盘数组,并根据不同的值输出对应的符号,比如打印X表示玩家落子,打印O表示电脑落子,打印空格表示空白的位置。 然后,需要编写一个函数来实现玩家的落子功能。可以通过向控制台读取玩家的输入来确定落子的位置,然后将相应的棋盘数组位置修改为1。 接着,需要编写一个函数来实现电脑的落子功能。可以使用一种简单的算法来确定落子的位置,比如随机选择一个空白位置来落子,并将对应的棋盘数组位置修改为2。 最后,还需要编写一个函数来检查游戏是否结束。可以通过遍历棋盘数组来检查是否有五个相连的棋子。可以编写多个检查函数,分别检查横向、纵向、左上到右下、右上到左下四个方向是否存在五连子。 在主函数中,可以通过循环来控制游戏的进行,每个循环中先打印当前的棋盘状态,然后让玩家落子,再检查游戏是否结束,如果没有结束则让电脑落子,再次检查游戏是否结束。 以上就是用C语言实现简易五子棋人机对战的基本思路,可以根据需要进行进一步的功能扩展和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值