C语言小游戏3——扫雷

扫雷游戏

1. 扫雷游戏的功能说明

  • 使用控制台实现经典的扫雷游戏
  • 游戏可以通过菜单实现继续玩或者退出游戏
  • 扫雷的棋盘是 9*9的格子
  • 默认随机布置 10个雷
  • 可以排查雷
  • 如果位置不是雷,就显示周围有几个雷
  • 如果位置是雷,就炸死游戏结束
  • 把除10个雷之外的所有雷都找出来,排雷成功,游戏结束

2. 游戏的分析和设计

扫雷的过程中,布置的雷和排查出的雷的信息都需要存储 ,所以我们需要一定的数据结构来存储这些信息。因为我们需要在 9*9的棋盘上布置雷的信息和排查雷,我们首先想到的就是创建一个 9*9的数组来存放信息。

此外,我们也需要将排查出的雷的数量信息记录存储,并打印出来,作为排雷的重要参考信息的

**问题一:**我们在棋盘上布置了雷,棋盘上雷的信息 1和非雷的信息 0,假设我们排查了某一个位置后,这个坐标处是雷 d 的 1和这个坐标的周围有1个雷的 1会产生歧义。

此处有多种办法解决,比如:雷和非雷的信息不要使用数字,使用某些字符就行,这样就避免冲突了,但是这样做棋盘上有雷和非雷的信息,还有排查出的雷的个数信息,就比较混杂,不够方便。

**解决方案:**专门给一个棋盘(对应一个数组 mine)存放布置好的雷的信息,再给另外一个棋盘(对应另外一个数组 show)存放排查出的雷的信息。这样就互不干扰了,把雷布置到 mine数组,在 mine数组中排查雷,排查出的数据存放在 show数组,并且打印 show数组的信息给后期排查参考。

show数组开始时初始化为字符 '*',为了保持两个数组的类型一致 ,可以使用同一套函数处理, mine数组最开始也初始化为字符 '0',布置雷改成 '1'


**问题二:**在排查的坐标位于边界处时,周围的部分坐标可能存在越界。

**解决方案:**为了防止越界,我们在设计的时候,给数组扩大一圈,雷还是布置在中间的 9*9的坐标上,周围一圈不去布置雷就行,这样就解决了越界的问题。所以我们将存放数据的数组创建成 11*11是比较合适。

3. 扫雷游戏的代码实现

game.h

c 复制代码
#pragma once

//减少源文件中引用头文件的个数
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

//便于修改棋盘的大小
#define ROW 9
#define COL 9

#define ROWS ROW + 2
#define COLS COL + 2

//便于修改雷的个数
#define MINE_NUM 10

void init_board(char arr[ROWS][COLS], int row, int col, char set);

void display_board(char arr[ROWS][COLS], int row, int col);

void set_mine(char arr[ROWS][COLS], int row, int col);

void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

game.cpp

c 复制代码
#include"game.h"

//初始化棋盘
void init_board(char arr[ROWS][COLS],int row, int col, char set)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			//放置一个set参数,使得初始化的值由我们设定
			arr[i][j] = set;
		}
	}
}

//打印棋盘
void display_board(char arr[ROWS][COLS], int row, int col)
{
	int i = 0, j = 0;
	//打印棋盘周围的坐标
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
}

//布置雷
void set_mine(char arr[ROWS][COLS], int row, int col)
{
	int i = 0, j = 0;
	int count = MINE_NUM;
	while (count)
	{
		i = rand() % row + 1;
		j = rand() % col + 1;
		if (arr[i][j] == '0')
		{
			arr[i][j] = '1';
			count--;
		}
	}
}

//检测周围雷的个数
//原理:'0' + a(移动范围内的自然数) = 'a'
int get_mine_count(char arr[ROWS][COLS], int i, int j)
{
	return (arr[i - 1][j - 1] + arr[i][j - 1] + arr[i + 1][j - 1] + 
		arr[i - 1][j] + arr[i + 1][j] + arr[i - 1][j + 1] + arr[i][j + 1] + 
		arr[i + 1][j + 1] - 8 * '0');
}

//排查雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int i = 0, j = 0;
	//用于判断游戏是否成功结束的标志
	int win = 0;
	while (win < ROW * COL - MINE_NUM)
	{
		printf("请输入要排查的坐标:>\n");
		scanf("%d %d", &i, &j);
		//框定坐标的范围
		if (i > 0 && i <= row && j > 0 && j <= col)
		{
			//避免重复排查
			if (show[i][j] == '*')
			{
				if (mine[i][j] == '1')
				{
					printf("很遗憾,你被炸死了\n");
					pirntf("游戏失败");
					display_board(mine, row, col);
					break;
				}
				else
				{
					//输出周围雷的个数
					show[i][j] = '0' + get_mine_count(mine, i, j);
					display_board(show, row, col);
					win++;
				}
			}
			else
				printf("该坐标已经被排查过,请重新输入\n");
		}
		else
			printf("输入错误,请重新输入进行排查\n");
	}
	if (win == ROW * COL - MINE_NUM)
	{
		printf("恭喜你,排雷成功");
		display_board(mine, ROW, COL);
	}
}

test.cpp

c 复制代码
#include"game.h"
void menu()
{
	printf("***************************\n");
	printf("*****   输入1:play   *****\n");
	printf("*****   输入0:exit   *****\n");
	printf("***************************\n");
}
void game()
{
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	init_board(mine, ROWS, COLS, '0');
	init_board(show, ROWS, COLS, '*');
	set_mine(mine, ROW, COL);
	display_board(show, ROW, COL);
	find_mine(mine, show, ROW, COL);
}
void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出游戏\n");
			break;
		case 1:
			printf("开始游戏\n");
			printf("----------  扫雷!  ---------\n");
			game();
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}
相关推荐
Uu_05kkq1 小时前
【C语言1】C语言常见概念(总结复习篇)——库函数、ASCII码、转义字符
c语言·数据结构·算法
嵌入式科普3 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
A懿轩A3 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列
1 9 J5 小时前
数据结构 C/C++(实验五:图)
c语言·数据结构·c++·学习·算法
仍然探索未知中6 小时前
C语言经典100例
c语言
爱吃西瓜的小菜鸡6 小时前
【C语言】矩阵乘法
c语言·学习·算法
Stark、7 小时前
【Linux】文件IO--fcntl/lseek/阻塞与非阻塞/文件偏移
linux·运维·服务器·c语言·后端
deja vu水中芭蕾8 小时前
嵌入式C面试
c语言·开发语言
stm 学习ing9 小时前
HDLBits训练3
c语言·经验分享·笔记·算法·fpga·eda·verilog hdl
CSND74013 小时前
Ubuntu vi(vim)编辑器配置一键补全main函数
linux·c语言·ubuntu·编辑器·vim