【C语言】扫雷---全功能版(超详细)

2024-01-28 10:20
文章标签 语言 详细 扫雷 全功能

本文主要是介绍【C语言】扫雷---全功能版(超详细),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:扫雷游戏大家一定不陌生,还记得小时候被这款游戏疯狂折磨。现在,废话不多说,作者带着大家运用C语言的知识来逐步实现一下这款小游戏。

一、雷盘的布置

1.基本布置

扫雷,那我们首先要布置一下雷盘

很简单,我们可以用一个二维数组来存储我们的数据,例如用1代表雷,用0代表无雷。但是,我们在展示的时候就会出现问题,我们不能把我们埋雷的信息给玩家直接展示,也就是说我们需要给这片雷区遮盖起来(再创建一个二维数组专门用来显示)。

此外,用两个数组在以后的处理过程中也很方便,我们处理雷信息时就处理存储雷信息的那个雷盘,展示的时候就处理展示的雷盘,这样就不会对存储雷信息的数组有什么意外的改动。 


2.雷盘改进优化

问题一:(越界问题)

有了棋盘我们就可以开始我们的游戏了,根据游戏的规则我们发现了一个问题,在统计雷的个数的时候,对于情况一来说我们用一个循环直接看周围8个位置是否有雷就可以,但是对于情况二、三、四,周围8个位置中,有的位置是不在我们数组范围里的,我们应该怎么解决这个问题?

解决方法:

1.(最复杂的方法)判断玩家选择排雷的位置,若是情况一,那么周围都排查;若是情况二,那么只排查左侧,左下侧,下侧;若是情况三,那么......,虽然可行,但实现起来比较复杂,且效率不高。

2.(效率不高)在统计周围的雷的数目时,加上一步判断周围的位置是否有效。但是每次都要判断,就会对我们的效率有所降低。

3.(高效方便)(看下图理解)在雷盘外再加上一圈无雷的区域,让原来无效的区域变有效。我们埋雷的时候只埋在中间区域,展示的时候也只展示中间区域,这样无论我们选择哪个位置排查雷,我们都不用担心坐标超出数组范围的问题。这样就牺牲了一小部分的空间,换来了高效和方便。

问题二:(坐标问题)

给你展示下面第一个这个雷盘,你能快速的说出你想要排查的位置吗?

是的,这对玩家太不友好了,没玩两分钟,已经被数坐标折磨了,由于我们不是用鼠标直接点击,而是输入对应的坐标,所以我们需要给玩家一点帮助,就像第二个和第三个这两个图、这样显示就更加方便观察

为什么选择中间而不是我们更熟悉的右边呢?

选择第三个:那么我们在后续的处理中需要对玩家输入的坐标进行处理,比如玩家排查(1,1),实际对应数组的位置是(9,1)不太方便。

选择第二个所输入的坐标恰好对应着数组中间区域的每一个位置,同时中间的方式对于后续的实现不需要再处理一次,十分巧妙。

 


3.代码实现 

提示:后面的代码实现都是各个功能函数的实现,行(ROW、ROWS)列(COL、COLS)雷的数量(_EASY,_MID,_DIF)都用宏定义,方便修改。

//基本信息的展示、方便后面看函数的代码
//行和列
#define ROW 9
#define COL 9//扩充的行和列
#define ROWS ROW+2
#define COLS COL+2//雷的个数
#define _EASY  10
#define _MID   20
#define _DIF   25//两个数组
char base[ROWS][COLS] = { 0 };//布置雷的棋盘
char show[ROWS][COLS] = { 0 };//展示给玩家看的棋盘
 (1)、初始化数组的函数
//创建好数组后对数组初始化
//初始化函数
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{int i = 0;int j = 0;for (i = 0; i < rows; i++){for (j = 0; j < cols; j++){board[i][j] = set;}}
}
 (2)、显示雷盘的函数
//显示雷盘的函数
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{int i = 0;int j = 0;printf("-------------------\n");for (j =0; j <= col; j++){printf("%d ", j);}printf("\n");for (i = 1; i <= row; i++){printf("%d ",i);for (j = 1; j <= col; j++){printf("%c ", board[i][j]);}printf("\n");}printf("-------------------\n");
}
(3)、放置雷的函数 
//放置雷的函数
void SetBase(char board[ROWS][COLS], int row, int col)
{int count = _EASY;while (count){int x = rand() % row+1;   //范围1~rowint y = rand() % col+1;   //范围1~colif (board[x][y] == '0'){board[x][y] = '1';count--;}}
}


 二、整体框架

优化好雷盘后,就正式的开始我们的扫雷游戏

规则:找出所有的雷,游戏胜利;踩雷被炸,游戏结束。

我们先根据游戏的过程,把整体的框架写出来,然后再去一一实现每个具体的函数

这也是我们写代码的一般步骤

注:为了更加美观,我们用了清屏的代码  system("cls")以及 等待代码Sleep(10000);

游戏过程:

1.玩家选择排雷或标记(代替鼠标左键右键)

2.玩家输入坐标(代替鼠标点击)

3.判断坐标的合法性(是否越界、是否重复)

4.判断输赢(有雷则结束游戏,找到所有雷也结束游戏)

5.没有达到输赢的条件,则进一步显示雷盘。(若该位置周围没有雷,就向四周展开;若该位置周围有雷,就在该位置上显示周围雷的个数)

重复上述的过程,直到找出全部的雷或踩雷。

void StartGame(char base[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{int input = 0;int win = 1;//记录输赢while (win){//选择排雷或标记printf("|1.排雷| or |2.标记|\n");printf("-------> ");scanf("%d", &input);switch (input){case 1:{//排雷函数//踩雷函数返回0  退出循环 游戏结束win = FindBase(base, show, row, col);break;}case 2:{//标记函数SignBase(base, show, row, col);break;}default:{printf("-----选择错误-------\n");break;}}//判断输赢//赢的条件是所有的安全格都已经被打开//.......下部分来实现}if (win == 0){printf("踩雷了! Game Over!\n");DisplayBoard(base, ROW, COL);//给玩家展示一下;Sleep(10000);system("cls");}else{printf("获胜! Win Game!\n");DisplayBoard(base, ROW, COL);Sleep(10000);system("cls");}
}


三、具体函数的实现

注:以下默认都是在坐标合法的情况下,自己实现时记得添加判断坐标的合法性

       同时为了更加美观,我们用了清屏的代码  system("cls");

1.标记函数(易实现)

标记功能包括:1.添加标记  2.取消标记

//标记函数//坐标合法后
//玩家选择
int input = 0;
printf("|1.添加标记| or |2.删除标记|\n");
printf("-----> ");
scanf("%d", &input);switch (input)
{
case 1://添加标记
{show[x][y] = '@';system("cls");DisplayBoard(show, ROW, COL);return 1;
}
case 2://删除标记
{show[x][y] = '*';system("cls");DisplayBoard(show, ROW, COL);return 1;
}
default:
{printf("选择错误\n");return 1;
}
}

2.排雷函数(重点看第二个递归展开功能函数)

当满足下面三个条件时,雷盘就会展开一片:

1.选择的位置不是雷

2.选择的位置周围没有雷

3.选择的位置不重复

 对于展开函数,我们发现每一个格子处理的方式都是一样的,所以我们选择递归的方式来实现

   //坐标合法后if (base[x][y] == '1'){return 0;}else//没踩到雷{Extend(base, show, row, col, x, y);system("cls");DisplayBoard(show, ROW, COL);return 1;}

  重点看下面的代码!!!

//实现展开的功能
void Extend(char base[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{int i = 0;int j = 0;if (x >= 1 && x <= row && y >= 1 && y <= col)//坐标是否在范围内{int count = ((base[x - 1][y - 1] +base[x - 1][y] +base[x - 1][y + 1] +base[x][y - 1] +base[x][y + 1] +base[x + 1][y - 1] +base[x + 1][y] +base[x + 1][y + 1]) - (8 * '0'));//计算周围雷的数量show[x][y] = count + '0';if (show[x][y] == '0')//如果周围没有雷,那么符合向外展开的条件{show[x][y] = ' ';//显示为空格//用递归向外扩展for (i = -1; i <= 1; i++){for (j = -1; j <= 1; j++){if (base[x + i][y + j] == '0' && show[x + i][y + j] == '*')//周围不是雷并且没有被排查过{Extend(base, show, row, col, x + i, y + j);}}}}}
}

 具体的效果如下:(很满意)


3.判断输赢的代码 
//判断输赢
//赢的条件是所有的安全格都已经被打开
//我们整体是放在一个循环中,输的话会直接跳出
int i = 0;
int j = 0;
int CountWin = 0;
for (i = 1; i <= row; i++)
{for (j = 1; j <= col; j++){if (show[i][j] != '*' && show[i][j] != '@'){CountWin++;}}
}
if (CountWin == ROW * COL - _EASY)break;//赢游戏之后也会跳出循环

四、结语 

在实现功能的时候,作者发现了鼠标的重要性,哈哈哈。因为没有鼠标,首先是要去找坐标,看的人眼花缭乱;其次是每次在输入坐标的时候,要先选择到底是排雷还是标记,是添加标记还是取消标记。不过这样做,也是为了让功能更加完全。

上面是题外话,总之,曾经的未知已经变成了现在的已知,复杂的东西不过是用最简单的选择、循环语句拼凑而成,所以只要你的思路有条有理,你其实可以实现很多好玩的代码。一起加油!

最后,欢迎大家关注作者,作者会持续更新有趣的代码,在有趣的玩意儿中成长!

这篇关于【C语言】扫雷---全功能版(超详细)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/653346

相关文章

将Mybatis升级为Mybatis-Plus的详细过程

《将Mybatis升级为Mybatis-Plus的详细过程》本文详细介绍了在若依管理系统(v3.8.8)中将MyBatis升级为MyBatis-Plus的过程,旨在提升开发效率,通过本文,开发者可实现... 目录说明流程增加依赖修改配置文件注释掉MyBATisConfig里面的Bean代码生成使用IDEA生

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Java使用Curator进行ZooKeeper操作的详细教程

《Java使用Curator进行ZooKeeper操作的详细教程》ApacheCurator是一个基于ZooKeeper的Java客户端库,它极大地简化了使用ZooKeeper的开发工作,在分布式系统... 目录1、简述2、核心功能2.1 CuratorFramework2.2 Recipes3、示例实践3

通过Docker Compose部署MySQL的详细教程

《通过DockerCompose部署MySQL的详细教程》DockerCompose作为Docker官方的容器编排工具,为MySQL数据库部署带来了显著优势,下面小编就来为大家详细介绍一... 目录一、docker Compose 部署 mysql 的优势二、环境准备与基础配置2.1 项目目录结构2.2 基

C语言中的数据类型强制转换

《C语言中的数据类型强制转换》:本文主要介绍C语言中的数据类型强制转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C语言数据类型强制转换自动转换强制转换类型总结C语言数据类型强制转换强制类型转换:是通过类型转换运算来实现的,主要的数据类型转换分为自动转换

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

C语言实现两个变量值交换的三种方式

《C语言实现两个变量值交换的三种方式》两个变量值的交换是编程中最常见的问题之一,以下将介绍三种变量的交换方式,其中第一种方式是最常用也是最实用的,后两种方式一般只在特殊限制下使用,需要的朋友可以参考下... 目录1.使用临时变量(推荐)2.相加和相减的方式(值较大时可能丢失数据)3.按位异或运算1.使用临时

使用C语言实现交换整数的奇数位和偶数位

《使用C语言实现交换整数的奇数位和偶数位》在C语言中,要交换一个整数的二进制位中的奇数位和偶数位,重点需要理解位操作,当我们谈论二进制位的奇数位和偶数位时,我们是指从右到左数的位置,本文给大家介绍了使... 目录一、问题描述二、解决思路三、函数实现四、宏实现五、总结一、问题描述使用C语言代码实现:将一个整

Linux系统中配置静态IP地址的详细步骤

《Linux系统中配置静态IP地址的详细步骤》本文详细介绍了在Linux系统中配置静态IP地址的五个步骤,包括打开终端、编辑网络配置文件、配置IP地址、保存并重启网络服务,这对于系统管理员和新手都极具... 目录步骤一:打开终端步骤二:编辑网络配置文件步骤三:配置静态IP地址步骤四:保存并关闭文件步骤五:重