C语言实现扫雷小游戏(排雷时可展开)

本文详细介绍了如何使用C语言编写扫雷游戏,包括地图设计、初始化、打印、设置雷、查找雷、展开非雷区域等核心逻辑,并提供了源代码进行解析。虽然没有图形界面,但游戏的基本玩法得以实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

游戏介绍

扫雷想必大家都听说过吧?我们今天写的小游戏就是扫雷,只不过我们只使用C语言写。我们能够做到的就是实现扫雷的基本逻辑,没有图形化界面。

源代码

这次游戏程序的写法和上一次的三子棋的模式很像,也是三个文件。

1.game.h(游戏头文件)

//头文件引用
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//常量声明
#define ROW 9//地图大小
#define COL 9//地图大小
#define ROWS ROW+2
#define COLS COL+2
#define EAZY_COUNT 10

//函数声明

//打印菜单
void menu();
//游戏函数
void game();
//初始化地图
void init(char board[ROWS][COLS], char set);
//打印地图
void display_board(char board[ROWS][COLS]);
//埋雷
void set_mine(char board[ROWS][COLS]);
//排雷
void find_mine(char show[ROWS][COLS], char mine[ROWS][COLS]);
//数雷
int mine_count(char board[ROWS][COLS], int r, int c);
//弹开
void ExpandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);
//判断游戏状态
int is_win(char board[ROWS][COLS]);

2.test.c(游戏测试)

#include "game.h"
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	//打印菜单,实现选择功能
	do
	{
		menu();
		printf("请选择:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:/*printf("play!\n");*/
			game();
			break;
		case 0:printf("游戏结束!\n");
			break;
		default:printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	system("pause");
	return 0;
}

game.c(游戏实现)

#include "game.h"

void menu()
{
	printf("*********************\n");
	printf("*****  1. play  *****\n");
	printf("*****  0. exit  *****\n");
	printf("*********************\n");
}
void game()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	init(mine, '0');
	init(show, '*');
	set_mine(mine);
	//display_board(mine);
	display_board(show);
	find_mine(show, mine);
}
void init(char board[ROWS][COLS], char set)
{
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			board[i][j] = set;
		}
	}
}
void display_board(char board[ROWS][COLS])
{
	for (int j = 0; j <= COL; j++)
	{
		printf("%d ", j);
	}
	printf("\n");
	for (int i = 1; i <= ROW; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= COL; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}
void set_mine(char board[ROWS][COLS])
{
	int count = EAZY_COUNT;
	while (count)
	{
		int r = rand() % ROW + 1;
		int c = rand() % COL + 1;
		if (board[r][c] == '0')
		{
			board[r][c] = '1';
			count--;
		}
	}
	void find_mine(char show[ROWS][COLS], char mine[ROWS][COLS]);
}
void find_mine(char show[ROWS][COLS], char mine[ROWS][COLS])
{
	int r = 0, c = 0;
	while (1)
	{
		printf("请输入排雷的坐标:\n");
		scanf("%d %d", &r, &c);
		if (r >= 1 && r <= ROW && c >= 1 && c <= COL)
		{
			if (show[r][c] == '*')
			{
				if (mine[r][c] == '0')
				{
					//show[r][c] = mine_count(mine, r, c) + '0';
					ExpandBoard(mine, show, r, c);
					if (is_win(show))
					{
						break;
					}
					display_board(show);
					//display_board(mine);
				}
				else
				{
					printf("踩雷了!游戏结束!\n");
					display_board(mine);
					break;
				}
			}
			else
			{
				printf("此位置已经排查过了!\n");
			}
		}
		else
		{
			printf("坐标不合法,请重新输入!\n");
		}
	}
	if (is_win(show))
	{
		printf("扫雷成功!\n");
		display_board(mine);
	}
}
int mine_count(char board[ROWS][COLS], int r, int c)
{
	return (board[r + 1][c - 1] +
		board[r][c - 1] +
		board[r - 1][c - 1] +
		board[r + 1][c] +
		board[r - 1][c] +
		board[r + 1][c + 1] +
		board[r][c + 1] +
		board[r - 1][c + 1] - (8 * '0'));
}
void ExpandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c)
{
	int ret = mine_count(mine, r, c);

	if (ret == 0)
	{
		show[r][c] = ' ';
		int i = 0;
		int j = 0;

		for (i = -1; i <= 1; i++)
		{
			for (j = -1; j <= 1; j++)
			{
				if ((r + i) > 0 && (c + i) > 0 && (r + i < ROWS) && (c + j < COLS) && show[r + i][c + j] == '*')
				{
					ExpandBoard(mine, show, r + i, c + j);
				}
			}
		}
		/*if (show[r - 1][c - 1] == '*' && r - 1 > 0 && r - 1 < ROWS && c - 1 > 0 && c - 1 < COLS)
			ExpandBoard(mine, show, r - 1, c - 1, win);
		if (show[r - 1][c] == '*' && r - 1 > 0 && r - 1 < ROWS && c > 0 && c < COLS)
			ExpandBoard(mine, show, r - 1, c, win);
		if (show[r - 1][c + 1] == '*' && r - 1 > 0 && r - 1 < ROWS && c + 1 > 0 && c + 1 < COLS)
			ExpandBoard(mine, show, r - 1, c + 1, win);
		if (show[r][c - 1] == '*' && r > 0 && r < ROWS && c - 1 > 0 && c - 1 < COLS)
			ExpandBoard(mine, show, r, c - 1, win);
		if (show[r][c + 1] == '*' && r > 0 && r < ROWS && c + 1 > 0 && c + 1 < COLS)
			ExpandBoard(mine, show, r, c + 1, win);
		if (show[r + 1][c - 1] == '*' && r + 1 > 0 && r + 1 < ROWS && c - 1 > 0 && c - 1 < COLS)
			ExpandBoard(mine, show, r + 1, c - 1, win);
		if (show[r + 1][c] == '*' && r + 1 > 0 && r + 1 < ROWS && c > 0 && c < COLS)
			ExpandBoard(mine, show, r + 1, c, win);
		if (show[r + 1][c + 1] == '*' && r + 1 > 0 && r + 1 < ROWS && c + 1 > 0 && c + 1 < COLS)
			ExpandBoard(mine, show, r + 1, c + 1, win);
	}*/
	}
	else
	{
		show[r][c] = ret + '0';
	}
}
int is_win(char board[ROWS][COLS])
{
	int count = 0;
	for (int i = 1; i <= ROW; i++)
	{
		for (int j = 1; j <= COL; j++)
		{
			if (board[i][j] == '*')
			{
				count++;
			}
		}
	}
	if (count == EAZY_COUNT)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

详解

接下来是游戏实现的一些详细的解释,由于很多地方是和上一期的三子棋相似的,我挑一些主要的地方讲解。

1.地图的设计(为什么地图的大小是(9+2)*(9+2)?)

在这里插入图片描述

白色的是我们玩家看到的地图。我们之所以要将地图扩大一圈,是因为我们后面的数坐标周围一圈的雷时,可能会导致越界访问的问题,所以我们这样设计。这一点其实和我们上一期的三子棋的棋盘设计是一个原理。我们以最右下角的个子为例,我们数它周围的雷数,需要访问这些棕色的格子,所以如果数组不够的话,就会导致越界访问。
在这里插入图片描述
还有一点就是我们这次定义地图时,设计了两个棋盘,他们大小一样,但是作用不同,一个是用来存放雷的信息的mine数组,另一个是用来展示给玩家的游戏地图show数组

2.初始化与打印地图

需要注意的地方有两点,第一点是我们给数组存数据时是char类型,所以记得加上'',并且最好是给整个数组都初始化一下。第二点是打印数组时,为了方便玩家输入坐标,我们最好是打印一下行号和列号,具体实现方式请看源代码。
效果展示:
在这里插入图片描述

3.设置雷(随机数的使用)

设置雷主要知识点就是随机数的使用,我们需要生成1-9的随机数。所以对rand生成的随机数采取以下措施:

int r = rand() % ROW + 1;
int c = rand() % COL + 1;

记得要设置随机数生成器哟。

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

采用循环的方式设置雷是为了保证能够设置多个并且有效的雷,没设置成功一个雷,就离出口更近一步。具体实现请看源代码。

4.寻找雷

玩家游玩的部分,主要逻辑如下:玩家输入坐标,如果坐标合法的话,判断该坐标是否已经排查过了,如果该坐标没有排查过,再判断是否为雷,如果不是雷,就显示该坐标周围雷的个数。
在这里插入图片描述

需要注意的地方就是数该坐标周围雷的个数这里,我们需要用到字符的加减法,如果你想要得到整型变量时,需要加减一个'0',如果需要将整型转换成字符时,也是同理。

5.将不是雷的地方展开(递归实现)

逻辑:数该坐标周围的雷数,如果为0.则将该位置变成' ',再依次对该坐标周围的八个格子采用同样的方式,递归出口:坐标周围的雷数不是0。
有两种代码,实现原理一样,但是递归次数不一样,代码量也不一样:
1.

void ExpandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c)
{
	int ret = mine_count(mine, r, c);

	if (ret == 0)
	{
		show[r][c] = ' ';
		int i = 0;
		int j = 0;

		for (i = -1; i <= 1; i++)
		{
			for (j = -1; j <= 1; j++)
			{
				if ((r + i) > 0 && (c + i) > 0 && (r + i < ROWS) && (c + j < COLS) && show[r + i][c + j] == '*')
				{
				    //递归
					ExpandBoard(mine, show, r + i, c + j);
				}
			}
		}
	}
	else
	{
		show[r][c] = ret + '0';
	}
}
void ExpandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c)
{
	int ret = mine_count(mine, r, c);

	if (ret == 0)
	{
		show[r][c] = ' ';
		int i = 0;
		int j = 0;
		if (show[r - 1][c - 1] == '*' && r - 1 > 0 && r - 1 < ROWS && c - 1 > 0 && c - 1 < COLS)
			ExpandBoard(mine, show, r - 1, c - 1);
		if (show[r - 1][c] == '*' && r - 1 > 0 && r - 1 < ROWS && c > 0 && c < COLS)
			ExpandBoard(mine, show, r - 1, c);
		if (show[r - 1][c + 1] == '*' && r - 1 > 0 && r - 1 < ROWS && c + 1 > 0 && c + 1 < COLS)
			ExpandBoard(mine, show, r - 1, c + 1);
		if (show[r][c - 1] == '*' && r > 0 && r < ROWS && c - 1 > 0 && c - 1 < COLS)
			ExpandBoard(mine, show, r, c - 1);
		if (show[r][c + 1] == '*' && r > 0 && r < ROWS && c + 1 > 0 && c + 1 < COLS)
			ExpandBoard(mine, show, r, c + 1);
		if (show[r + 1][c - 1] == '*' && r + 1 > 0 && r + 1 < ROWS && c - 1 > 0 && c - 1 < COLS)
			ExpandBoard(mine, show, r + 1, c - 1);
		if (show[r + 1][c] == '*' && r + 1 > 0 && r + 1 < ROWS && c > 0 && c < COLS)
			ExpandBoard(mine, show, r + 1, c);
		if (show[r + 1][c + 1] == '*' && r + 1 > 0 && r + 1 < ROWS && c + 1 > 0 && c + 1 < COLS)
			ExpandBoard(mine, show, r + 1, c + 1);
	}
}
	else
	{
	show[r][c] = ret + '0';
	}
}

总结

这个扫雷小游戏还有需要改进的地方,比如放标记功能,调整难度功能,感兴趣的小伙伴可以继续研究。如果内容有什么错误的话,请大家提出来,一起讨论,一起进步!
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值