C语言小游戏《三子棋》具体实现

大家好,上节课,我为大家讲解了有关数组的基本知识,那么这节课,我就带着大家来一起实现一个叫《三子棋》的小游戏,会用到我们之前学过的数组,循环等相关知识,也是通过这个游戏,希望大家能够了解C语言实现游戏及其他项目的基本逻辑,那我们话不多说,现在开始吧!

1. 准备工作

在开始写代码之前,我们肯定要在项目中创建一些文件。本次游戏中我们用到的文件有main.c, game.c, game.h。main.c 是主函数,主要是展示给用户看的,包含菜单等这个游戏所用到的一些逻辑,game.h用来声明这些逻辑(函数),game.c中则是对这些函数进行实现。

2. 游戏规则

我们这个三子棋的规则是让用户和电脑进行下棋,用户作为先手,用户下的棋用 * 代替,电脑下的棋用#代替,当某一列,某一行,或者对角线上的三个位置都是相同的字符时,其中一方获胜。

3. main.c的主要代码内容

既然我们想做一个三子棋的小游戏,那么,我们首先就要写一个菜单,菜单中应该包括开始游戏和退出游戏两个选项,我们用1和0代替,又因为用户可能要进行多次游戏,所以我们应如何实现呢?没错,我们应该把这个菜单函数放到循环里,当用户输入0时,代表用户不想继续玩了,这时循环退出,游戏结束,当用户输入为1时,代表用户还想继续玩,那么循环继续,当用户输入其他值时,我们可以提示用户输入错误,请他重新输入一次。

以下就是我们的代码实现

// main.c

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

int main()
{
	int choice = 0; // 选择
	while(1)
	{
		menu();
		printf("请选择 ->");
		scanf("%d", &choice);
		switch (choice)
		{
		case 1:
			printf("开始游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			system("cls");
			break;
		}

	}

	return 0;
}

让我们编译代码,来看一下菜单给用户的展示效果

怎么样,是不是还有美感的?(hhh)

4. 游戏棋盘的实现

有了菜单,我们应该如何展示进行三子棋游戏的过程呢?由于我们用 * 和 # 来代表棋子,那么毋庸置疑,我们应该创建一个长度为3 X 3 的二维字符数组。

那么首先,第一步,我们应该对棋盘进行初始化,将棋盘中的每一个字符都初始化为一个空格。

我们用函数InitBoard实现,在game.h中进行函数声明,在game.c中进行函数实现,在main.c中进行函数调用,下面我们讲解game.c中的具体函数实现过程

用两层循环,直接全部初始化空格就行,很简单

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

5. 打印棋盘

当我们初始化棋盘之后,我们发现,打印出来的棋盘全部都是空格,棋盘与棋盘之间没有分界线,因此,玩家就难以分辨自己在哪个位置下棋。

所以,我们要对我们设计的棋盘进行优化。

说是优化,无非就是在棋盘中间加上若干条分界线,因为我们的游戏名称叫做《三子棋》,所以在这里,我提供一种优化方法。

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

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

这回,让我们再看一次我们设计的棋盘。

怎么样,这次我们设计的棋盘是不是清晰明了了呢?

6. 玩家下棋

设计好棋盘之后,我们接下来应该做什么呢?应该是让玩家进行下棋吧,我们设计一个函数PlayerBoard来进行实现

void PlayerMove(char board[ROW][COL], int row, int col)
{
	printf("玩家下棋>:\n");
	while (1)
	{
		printf("请输入下棋的坐标,中间使用空格>:\n");
		int x, y = 0;
		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("坐标被占用,不能落子,请重新输入\n");
			}

		} else{
			printf("坐标非法,重新输入\n");
		}
	}

}

 在上面的代码中,首先提示玩家输入两个坐标,分别作为棋盘的行和列,然后首先应该判断玩家输入的坐标是否合法,如果不合法,应该让玩家重新输入,所以,我们应该把这段代码放入循环里。如果玩家输入的坐标合法,我们继续分两种情况,如果用户输入的这个坐标之前已经有棋了,那么用户也应该重新输入,如果之前没有放置棋,也就是这个位置的字符为 '0',那么提示用户输入成功,游戏继续。

7. 电脑下棋

同理,在我们写完玩家下棋的代码之后,我们写电脑下棋的代码就更加轻松了。由于电脑下棋我们是让程序随机生成两个1 - 3之间的问题,所以不存在坐标不合法的情况,但是仍然存在生成的坐标位置之前已经放置了棋,所以我们依然要判断生成坐标的合理性,代码如下

void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x, y = 0;
	printf("电脑下棋:>\n");
	
	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

8. 判断输赢

在玩家和电脑你一次我一次的进行博弈的过程中,一定有一个时刻会分出胜负,或者棋盘走满仍未分出胜负的情况,我们称之为平手。

所以,一共有四种情况:

1. 玩家赢  我们让函数返回 *

2. 电脑赢  我们让函数返回 #

3. 打成平手  我们让函数返回 Q

4. 游戏继续  我们让函数返回 C

那么问题来了,什么情况下某一方获胜呢?

其实答案很简单,因为我们设计的是三子棋,所以期盼的某一行,某一列,或者对角线上放置的字符都相同时,相应的哪一方就获胜

由于我们把玩家下的棋用 * 来代替,把电脑下的棋用 # 来代替,而当玩家或者电脑获胜的时候,我们又让函数返回 * 或者 # ,所以我们可以直接返回棋盘上棋子所对应的内容,部分代码如下

for (int i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
		{
			return board[i][0]; // 直接返回
		}
	}

	for (int i = 0; i < row; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][i]; // 直接返回
		}
	}

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];

以上就是某一方获胜的代码,但是我们知道,一局比赛,玩家和电脑还有可能打成平手。

我们上文说了,平手的意思是:棋盘下满了,但是没有某一行,某一列,或者对角线上放置的字符都相同。

所以,我们可以再写一个函数IsFull,来判断棋盘是否已经满了,不能再下棋了

int IsFull(char board[ROW][COL], int row, int col)
{
	for(int i = 0; i < row; i ++)
		for (int j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
			{
				return 0;
				break;
			}
		}

	return 1;
}

所以,平手的代码就很好写了

// 平局
	if (IsFull(board, row, col) == 1) return 'Q';

因为函数的执行是从上到下的,所以如果某一方获胜,平手的情况都没发生,我们直接返回C就好了,让游戏继续进行

所以,判断输赢函数的所有代码如下

char IsWin(char board[ROW][COL], int row, int col)
{
	// 赢
	for (int i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
		{
			return board[i][0]; // 直接返回
		}
	}

	for (int i = 0; i < row; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][i]; // 直接返回
		}
	}

	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) == 1) return 'Q';

	// 继续
	return 'C';

}

10. 完善game代码

最后,让我们来捋一下整个函数的逻辑

1. 初始化棋盘,打印棋盘

2. 玩家下棋,电脑下棋

那么问题来了,我们该在什么时候判断输赢呢,是不是应该在每一个对方下完棋之后,就应该判断一次输赢呢?

所以,我们应该优化一下game代码

1. 初始化棋盘,打印棋盘

2. 玩家下棋,判断输赢

3. 电脑下棋,判断输赢

4. 游戏继续或结束

game的代码如下

void game()
{
	char board[ROW][COL] = { 0 };
	InitBoard(board, ROW, COL); // 有关游戏的操作
	// 打印棋盘
	DisplayBoard(board, ROW, COL);
	// 下棋
	char ret = 0;
	while (1)
	{
		// 1.玩家下棋
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		// 判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C') break;

		// 2.电脑下棋
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		ret = IsWin(board, ROW, COL);
		if (ret != 'C') break;
	}

	// 只判断一次
	if (ret == '*') printf("玩家赢\n");
	else if (ret == '#') printf("电脑赢\n");
	else printf("平局");
}

11. 运行代码

大家可以看到,我们玩家就赢了电脑,因为第一行的棋子全部都是 * 。

那么,我们三子棋的讲解到这里就结束了,同学们学会了吗,快和你们的小伙伴们一起玩起来吧!

希望通过这个三子棋的例子,能让大家更好的理解数组,函数等概念,以及对C语言的逻辑有了初步的了解。

好了,今天的分享到这里就结束了,感谢大家的点赞,分享,与转发~

下期,我会给大家带来另外一个小游戏《扫雷》,有兴趣的小伙伴千万不要错过呦!

我们下期再见,拜拜~ 

  • 11
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值