首页 > 其他分享 >初识扫雷!

初识扫雷!

时间:2024-09-23 22:48:02浏览次数:11  
标签:ROWS int mine COLS char 初识 扫雷 printf

                                      前言

  今天我们来简单了解一下一款经典的游戏--扫雷!文末附上完整代码。

扫雷作为微软开发并发行的一款大众类的益智小游戏,于1992年发行。玩家的目标是在最短时间内识别出所有非雷区的格子,同时避免触碰到地雷。游戏区域由多个隐藏格子组成,每个格子可能隐藏着地雷或数字,数字表示周围八个格子中地雷的数量。玩家通过点击格子揭示内容,使用逻辑推理逐步排除雷区。游戏考验玩家的观察力和推理能力,任何一次误点地雷都会导致游戏失败。

扫雷分为初级,中级,高级等难度,玩家也可以自行定义雷的数量与方格的大小。免费在线扫雷 (minesweeper.cn)这是网页版的扫雷游戏,大家感兴趣可以点击进入。

                                    游戏分析与设计:

1.数据结构分析

布置的雷和排查出的雷的信息都需要存储,所以我们需要⼀定的数据结构来存储这些信息。初阶的扫雷(如上图)大小是9*9, 因为我们需要在9*9的棋盘上布置雷的信息和排查雷,我们⾸先想到的就是创建⼀个9*9的数组来存放信息

• 使⽤控制台实现经典的扫雷游戏

• 游戏可以通过菜单实现继续玩或者退出游戏

• 扫雷的棋盘是9*9的格⼦ • 默认随机布置10个雷

• 可以排查雷 ◦ 如果位置不是雷,就周围有⼏个雷

         ◦  如果位置是雷,就炸死游戏结束

         ◦ 把除10个雷之外的所有⾮雷都找出来,排雷成功,游戏结束

2.⽂件结构设计

为了便于代码的创建,我们分成三个文件来实现。

                                        代码实现

1.菜单的创建

void menu()
{
	
	printf("***********************\n");
	printf("*****   1.play   ******\n");
	printf("*****   0.exit   ******\n");
	printf("***********************\n");
}
int main()
{ 
	srand( (unsigned int) time ( NULL ) );
	int input = 0 ;
	do
	{
		menu();
		printf("请选择:");
		
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("\n");
			game();
			break;

		case 0:
			printf("\n");
			printf("退出\n");
				break;
		default:
			printf("\n");
			printf("输入有误,请重新输入\n");
			break;
		}

	} while (input);

	return 0;
}

细节分析:

创建menu函数来简单的放置我们的菜单栏,并添加选项1,0。1是开始游戏,而0是退出游戏!   使用do while循环,这样我们就能至少进行一次菜单栏操作,输入选项进入switch函数,为了让我们玩过一次后还能玩,当input等于0时,我们才结束循环退出!

2.初始化数组

game.h

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//ROW 行  COL  列

test.c

char mine[ROWS][COLS] = { 0 };//放布置好的雷‘0’
char show[ROWS][COLS] = { 0 };//放排查出的雷‘*’
//初始化数组
InitBoard(mine, ROWS, COLS,'0');
InitBoard(show, ROWS, COLS, '*');

game.c

#include "game.h"
void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = set;
		}
	}
}

细节分析:

在开始前,我们需要知道若要引用game.h的函数与声明,我们要在文件头加入#include "game.h", 这样我们就只需要在game.h中放置函数声明与定义!

board有寄存的意思。

为什么在game.h中声明行列的数目?其实是为了自定义方格大小时便于修改

相信大家注意到了,ROWS和COLS,为什么要创建这两个常量?下面我通过画图为大家分析

                                  

我们知道当选择A时,格子如果没有雷,则会显示周围八格的地雷数,如果没有雷则显示空格。那么如果选择C时,则显示周围五格,这将会大大提高我们的代码难度,难不成特地为边缘格子开发一个判断循环结构?所以我们就在9*9格子的基础上增加两行两列,也就是周围补上一圈!

如下图,D点也判断周围八格的地雷数!这样便于我们构造判断语句,即如果周围八格有地雷,则地雷数依次++

所以在test.c中把数组mine和show传入函数InitBoard,当然了,我们在展示格子和雷的时候只需要9*9的范围。

3.打印棋盘

game.h

void DisplayBoard(char board[ROWS][COLS], int row, int col);

test.c

DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);

game.c

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("-------扫雷-------\n");
	printf("\n");
	printf("  ");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
	}
	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");
}

细节分析:

前面我们讲到构造了一个11*11的二维数组,且提到了在打印棋盘时,我们其实只需要9*9的空间

所以在test.c中我们传入ROW,COL,就是9。那么打印数组时,我们就从下标1开始,依次打印九个内容。那么为了美观,我们在头行头列加入数字便于我们知道这是哪行哪列,这也为我们后续的代码实现奠定基础!也可以加入扫雷字样

4.布置地雷

game.h

#include<stdlib.h>
#include<time.h>
void SetMine(char board[ROWS][COLS], int row, int col);

test.c

//布置雷
SetMine(mine, ROW, COL);

game.c

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		//随机生成下标
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] != '1')
		{
			board[x][y] = '1';
			count--;
		}
	}		
}

细节分析:

在这里我们用到了rand函数,这是一个生成伪随机数的函数,就是生成的数并不是真正随机,生成后再次打开,我们会发现数字与上次的一模一样,细心的你一定发现了,在菜单创建的主函数,有这么一串代码:srand( (unsigned int) time ( NULL ) );这便是生成随机数的关键。我们知道,在计算机中,时间是不断变化的,那么我们利用时间戳(大家可以搜看看)。time的参数如果是NULL,就能只返回时间的差值,这就是前面说的时间戳。srand函数能生成种子,而time让srand生成的是随机种子!

定义r=rand()的返回值,(由公式可知,如果要生成0-n的随机数,就让r%(n+1),即生成0-99的随机数,是r%100)现在我们要生成0-9的随机数,就是r%9+1.

rand函数与time函数需要头文件。

一切安排妥当后,我们就在数组中放入地雷,当这个位置不是雷时,我们就放入雷,并且雷的总数--。雷的数量在game.h中定义。

5.排查雷

关键的一步!

game.h

#define EASY_COUNT 10
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);

test.c

//排查雷 
FindMine(mine, show, ROW, COL);

game.c

int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x + 1][y] + 
		mine[x + 1][y + 1] +mine[x][y + 1] + mine[x - 1][y + 1]-8*'0';
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;

	int win = 0;
	while (win<row*col-EASY_COUNT)
	{
		printf("请输入要排查的坐标(行 列): ");
		scanf("%d%d", &x, &y);
		if (x>0 && x<=row && y>0 && y<=col)
		{
			if (mine[x][y] == '1')
			{
				printf("炸了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				win++;
				printf("还需排查%d个位置\n", row * col - EASY_COUNT-win);
				//统计x,y坐标周围的8个坐标中雷的个数
				int c=GetMineCount(mine,x,y);
				show[x][y] = c + '0';
				DisplayBoard(show, ROW, COL);
			}
		}
		else
		{
			printf("输入的坐标有误,请重新输入");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功!!!\n");
		DisplayBoard(mine, ROW, COL);
	}
	

}

细节分析:

根据代码,我们容易知道,当我们输入坐标后,若该位置恰好有雷,那么游戏失败,退出。

否则,排查出附近八个位置雷的数目!

int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x + 1][y] + 
		mine[x + 1][y + 1] +mine[x][y + 1] + mine[x - 1][y + 1]-8*'0';
}

因为相同字符相减为 0 ,所以方法就是让周围八个位置的相加再减去8* ' 0 ' ,下图是进阶算法。

循环的终止条件就是当win等于除了雷以外的格子数,而每次排查没踩到雷,win++。

每次排查后,都生成下一次的棋盘!

下面是效果图与源码!

效果图:

全部源码:

game.h

#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 EASY_COUNT 10
void InitBoard(char board[ROWS][COLS], int row, int col, char set);

void DisplayBoard(char board[ROWS][COLS], int row, int col);

void SetMine(char board[ROWS][COLS], int row, int col);

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

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = set;
		}
	}
}

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("-------扫雷-------\n");
	printf("\n");
	printf("  ");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
	}
	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 SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		//随机生成下标
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] != '1')
		{
			board[x][y] = '1';
			count--;
		}
	}		
}

int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x + 1][y] + 
		mine[x + 1][y + 1] +mine[x][y + 1] + mine[x - 1][y + 1]-8*'0';
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;

	int win = 0;
	while (win<row*col-EASY_COUNT)
	{
		printf("请输入要排查的坐标(行 列): ");
		scanf("%d%d", &x, &y);
		if (x>0 && x<=row && y>0 && y<=col)
		{
			if (mine[x][y] == '1')
			{
				printf("炸了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				win++;
				printf("还需排查%d个位置\n", row * col - EASY_COUNT-win);
				//统计x,y坐标周围的8个坐标中雷的个数
				int c=GetMineCount(mine,x,y);
				show[x][y] = c + '0';
				DisplayBoard(show, ROW, COL);
			}
		}
		else
		{
			printf("输入的坐标有误,请重新输入");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功!!!\n");
		DisplayBoard(mine, ROW, COL);
	}
	

}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include "game.h"


void menu()
{
	
	printf("***********************\n");
	printf("*****   1.play   ******\n");
	printf("*****   0.exit   ******\n");
	printf("***********************\n");
}
void game()
{
	char mine[ROWS][COLS] = { 0 };//放布置好的雷‘0’
	char show[ROWS][COLS] = { 0 };//放排查出的雷‘*’
	//初始化面板
	InitBoard(mine, ROWS, COLS,'0');
	InitBoard(show, ROWS, COLS, '*');
	
	//打印棋盘
	//DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);

	//布置雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);

	//排查雷 
	FindMine(mine, show, ROW, COL);
}
int main()
{ 
	srand( (unsigned int) time ( NULL ) );
	int input = 0 ;
	do
	{
		menu();
		printf("请选择:");
		
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("\n");
			game();
			break;

		case 0:
			printf("\n");
			printf("退出\n");
				break;
		default:
			printf("\n");
			printf("输入有误,请重新输入\n");
			break;
		}

	} while (input);

	return 0;
}

拓展:

作为一个经典的游戏,我们的代码还是不完善的,许多功能并没有实现,如:

1.在判断为雷的格子上插旗子标记

2.加入排雷的时间显示

3.如果排查位置不是雷,周围也没有雷,可以展开周围的一片

4.选择游戏的难度

       。简单    9*9  10个雷

       。中等    16*16   40个雷

       。困难    30*16  99个雷

       。自定义

小结:

  本次分享就到此结束了,如果你有关于扫雷拓展与优化的想法,或者本文章中的错误与不足,欢迎在评论区中留言!!如果觉得小编写的不错,记得一键三连支持一下!!非常感谢!

标签:ROWS,int,mine,COLS,char,初识,扫雷,printf
From: https://blog.csdn.net/2401_83779763/article/details/142431409

相关文章

  • 初识DPDK
    初识DPDK背景DPDK(DataPlaneDevelopmentKit)DPDK最初是为了证明intel处理器能够应对高性能的网络数据包而提出的以Linux为例子,网卡收包流程概述如下1、网络数据包通过网线到达网卡2、网卡通过DMA将数据写入内存3、网卡发起中断通知CPU数据到达内存4、驱动读取数......
  • 小爱·扫雷游戏
    c164-扫雷题目描述小爱正在编写一个扫雷游戏的出题模块。这个模块的输入是一张地图,标记了每个地雷的位置,地雷以*表示,空地以.表示。这个模块的输出是一个表格,统计了每个空地周围八个方格内的地雷数量。例如,给定一个初始地图:*.*...*..则应输出:*2*231*10请你帮助小爱......
  • [Mysql]基础入门篇_关于数据库的简介_初识DQL语句
    @[TOC]文章目录何为数据库数据库类型数据库管理系统(本博主使用的是Mysql数据库)何为SQL准备工作及select查询操作的简单使用何为数据库数据库是一个有组织的数据集合,允许高效存储、管理和检索信息。它通过结构化的方式存储数据,通常使用数据库管理系统(DBMS)来执行操作,如......
  • 初识算法
    持续更新数据结构与算法专题,欢迎关注.......1.1什么是算法?定义在数学和计算机科学领域,算法是一系列有限的严谨指令,通常用于解决一类特定问题或执行计算Inmathematicsandcomputerscience,analgorithm(/ˈælɡərɪðəm/)isafinitesequenceofrigorousinstru......
  • 九宫格(html css实现)---初识flex布局
    记录flex属性并实现一个九宫格flex属性Flex容器:需要注意的是:当时设置flex布局之后,子元素的float、clear、vertical-align的属性将会失效.container{display:flex;}//块状元素.container{inline-flex;}//行内元素块状元素1.***独占一行:块元素会自动......
  • 初识数据结构和算法
    说在前面:⭐看到这篇文章的友友你好啊,在学习的路途中欢迎你的私信、留言,交流互动啊,我们一起学习、一起进步呀!⭐目录数据和结构解释含义数据的属性划分数据和算法的关系算法中复杂度时间复杂度空间复杂度数据和结构解释含义......
  • 如何在 ASP.NET Core Web API 方法执行前后 “偷偷“ 作一些 “坏“ 事?初识 ActionFil
    前言:什么是ActionFilterAttribute?ActionFilterAttribute是一种作用于控制器Action方法的特性(Attribute),通过它,你可以在操作执行前后、异常处理时等不同的阶段插入自定义逻辑。比如在执行操作方法之前修改请求参数、记录日志、进行权限验证等操作,在执行操作方法之后发送邮件......
  • shell编程初识
    1.Shell概述,编程语言.1.1编程概述应用广泛:Shell占据的Linux半壁江山.企业要求:面试要求运维人员必会.目标:1.能够熟练书写超过多行的脚本:检查类:系统巡检,系统加固,优化:(检查,配置优化)系统优化(初始化)(第2阶段开头).......
  • 业务初识-思考问题-分析数据-输出结论
    思考问题:确认问题(目的(明确程度,原因是解决还是什么),背景,思路)检测数据完善性拆解问题-经典分析框架-搭建自己的分析框架sg:拆解问题总结:一个原则四个方法MECE法则:拆解部分要相互独立、完全穷尽时间流程法、模型框架法、量化公式法、穷尽要素法时间流程法:最常用,根据时间......
  • 深度学习 初识学习 9.16
    什么是SVMSVM(SupportVectorMachine,支持向量机)是一种监督学习模型,用于分类和回归分析。其基本思想是找到一个超平面,使得两类样本在该超平面上的间隔最大化。这个间隔被称为“最大间隔”,而位于最大间隔边界上的样本点则被称为“支持向量”。SVM的关键概念:超平面:在n维空间中,一......