前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言实现简易三子棋项目

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

作者头像
黎鹤舞
发布2024-03-19 15:11:31
710
发布2024-03-19 15:11:31
举报

逻辑分析过程

1.游戏菜单

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

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

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

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

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

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

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

代码语言:javascript
复制
#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列的二维数组

代码语言:javascript
复制
void boardInit(char arr[ROW][COL],int row,int col)

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

代码语言:javascript
复制
boardInit(arr,ROW,COL);

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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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()

代码语言:javascript
复制
#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()函数返回值)

代码语言:javascript
复制
srand((unsigned int)time(NULL));

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

代码语言:javascript
复制
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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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.电脑胜利

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-09-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 逻辑分析过程
    • 1.游戏菜单
      • 2.三子棋棋盘的设计
        • 模块化编程
        • 二维数组的传递
      • 3.玩家和电脑下棋逻辑
        • 生成随机数
      • 4.游戏结束条件(胜利/平局)
      • 整体代码内容展示
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档