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;
}
相关推荐
TeYiToKu1 小时前
笔记整理—linux驱动开发部分(1)驱动梗概
linux·c语言·arm开发·驱动开发·嵌入式硬件
DdddJMs__1352 小时前
C语言 | Leetcode C语言题解之第517题超级洗衣机
c语言·leetcode·题解
边疆.2 小时前
C++类和对象 (中)
c语言·开发语言·c++·算法
林浔09062 小时前
C语言部分输入输出(printf函数与scanf函数,getchar与putchar详解,使用Linux ubuntu)
c语言·开发语言
blessing。。3 小时前
__attribute__ ((__packed__))
linux·c语言·arm开发
长潇若雪4 小时前
结构体(C 语言)
c语言·开发语言·经验分享·1024程序员节
DARLING Zero two♡5 小时前
关于我、重生到500年前凭借C语言改变世界科技vlog.12——深入理解指针(2)
c语言·开发语言·科技·1024程序员节
环能jvav大师6 小时前
使用Ubuntu系统+VS Code开发STC51单片机
linux·c语言·开发语言·单片机·嵌入式硬件·ubuntu
黄交大彭于晏6 小时前
第五天学习总结:C语言学习笔记 - 数组篇
c语言·笔记·学习
何曾参静谧7 小时前
「C/C++」C++20 之 #include<ranges> 范围
c语言·c++·c++20