【C语言】扫雷小游戏

文章目录

  • 前言
  • 一、游戏玩法
  • 二、创建文件
    • test.c文件
      • menu()——打印菜单
      • game()——调用功能函数,游戏的实现
      • main()主函数
    • game.c文件
      • 初始化棋盘
      • 打印棋盘
      • 随机布置雷的位置
      • 统计周围雷的个数
      • 展开周围一片没有雷的区域
      • 计算已排查位置的个数
      • 排查雷(包括检测输赢):
    • game.h文件
      • 头文件
      • 棋盘的大小以及雷的个数
      • 主要功能函数的声明
  • 三、完整代码

前言

扫雷游戏与前文的三子棋小游戏有些类似,都是在二维数组的基础上进行的,但扫雷需要考虑的东西更多,也更难一点。今天我们就一起来学习如何实现扫雷小游戏,找回童年的回忆。(最后附有完整代码)

一、游戏玩法

没有玩过扫雷的小伙伴可以打开电脑自带的扫雷小游戏玩两把,很上头有没有,哈哈哈。

游戏规则:

1.首先,扫雷是在一个N*N的棋盘上进行的游戏,这些方格中随机暗藏着一定数量的雷。
2.揭开一个方格,如果没有地雷,则会显示周围8个方格的地雷的数量,如果周围8个方格都没有地雷,则会翻开一片区域。
3.如果揭开的是地雷,那么游戏失败。
4.可以选择标记未揭开的方格为雷,也可以取消标记,方便玩家记忆雷的位置

游戏胜利条件: 不触发地雷,找出所有不是雷的位置。

程序试玩:

初始菜单

在这里插入图片描述

上方是玩家棋盘,下方是布置好的雷盘

在这里插入图片描述

选择1排查雷,点开坐标(4,3)的方格,显示3,说明该坐标周围有3颗地雷

在这里插入图片描述
选择2,标记坐标(4,1)为雷,显示 " ! "

在这里插入图片描述

游戏失败!

在这里插入图片描述

二、创建文件

像三子棋一样,我们同样采用分模块的编程思想,创建三个文件来分别存放对应功能的代码。

test.c文件:主程序,功能的调用
game.c文件:扫雷游戏的具体功能实现
game.h文件:工程需要的头文件和函数声明以及宏定义

这样做的好处有:提高代码的可读性,方便后续调试,条理清晰,使主程序看起来简洁

test.c文件

menu()——打印菜单

void menu()
{printf("*******************\n");printf("***** 0.exit ******\n");printf("***** 1.play ******\n");printf("*******************\n");
}

game()——调用功能函数,游戏的实现

1.创建并初始化棋盘
2.随机布置雷的位置
3.打印棋盘信息
4.排查雷(包括判断游戏输赢)

这些是game.h中的宏定义信息,后面都会用到的,这里先声明一下

#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define count 10
void game()
{char mine[ROWS][COLS] = { 0 };//布置好的雷盘char show[ROWS][COLS] = { 0 };//排查出的雷盘init_board(mine, ROWS, COLS, '0');//mine棋盘全部初始化为0init_board(show, ROWS, COLS, '*');//show棋盘全部初始化为*set_mine(mine, ROW, COL);	//随机布置雷的位置print_board(show, ROW, COL);//打印玩家棋盘find_mine(mine, show, ROW, COL);//排查雷
}

之所以创建两个棋盘数组,是因为一个棋盘用来存放系统布置的雷的位置,一个棋盘用来存放玩家手中的棋盘信息。相当于一份是有答案的卷子,一份是玩家需要做的白卷。

创建的棋盘多两行两列是为了 方便后面排查周围8个格子时不会产生越界行为,不然要分情况讨论,很麻烦。显然,直接在创建数组时多开辟一圈更省事。

main()主函数

int main()
{srand((unsigned int)time(NULL));int input = 0;do{menu();printf("请选择->:");scanf("%d", &input);switch (input){case 0:printf("退出游戏\n");break;case 1:system("cls");game();break;default:printf("选择错误,请重新选择\n");break;}} while (input);return 0;
}

game.c文件

初始化棋盘

初始化棋盘为ch字符,可以自己定义初始化字符
我们将存放答案的那张棋盘全部初始化为字符0,后面布置有雷则置1
将玩家手中的游戏棋盘全部初始化为 * ,表示未揭开状态

void init_board(char board[ROWS][COLS], int rows, int cols, char ch)
{for (int i = 0; i < rows; i++){for (int j = 0; j < cols; j++){board[i][j] = ch;}}
}

打印棋盘

我们每写完一个功能模块,最好调用打印函数来验证是否正确

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

随机布置雷的位置

使用rand()产生随机数,但rand()每次只会产生固定的随机数,所以要在主函数中加入srand((unsigned int)time(NULL));将时间作为seed产生不断变化的随机数

rand()和srand()的使用需要包括头文件#include<stdlib.h>
time()函数的使用需要包括头文件#include<time.h>

void set_mine(char board[ROWS][COLS], int row, int col)
{int cnt = count;while (cnt){int x = rand() % row + 1; //得到1~row的随机数int y = rand() % col + 1; //得到1~col的随机数if (board[x][y] == '0'){board[x][y] = '1';//表示该位置布置了雷cnt--;}}
}

统计周围雷的个数

int get_mine_count(char mine[ROWS][COLS], int x, int y)
{int ret = 0;for (int i = x - 1; i <= x + 1; i++){for (int j = y - 1; j <= y + 1; j++){if(i != x || j != y)//周围8个位置,不包括自己ret = ret + mine[i][j] - '0';}}return ret;
}

展开周围一片没有雷的区域

如果该位置周围一个雷也没有,则继续打开周围的空白格子直到遇到周围有雷的格子。

void open_area(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{int cnt = get_mine_count(mine, x, y);if (cnt == 0){show[x][y] = '0';for (int i = x - 1; i <= x + 1; i++){for (int j = y - 1; j <= y + 1; j++){if (show[i][j] == '*' && x >= 1 && x <= row && y >= 1 && y <= col){open_area(mine, show, ROW, COL, i, j);//递归}}}}else//直到递归到周围8个位置存在雷的区域{show[x][y] = cnt + '0';}
}

计算已排查位置的个数

int get_win(char board[ROWS][COLS], int row, int col)
{int win = 0;for (int i = 1; i <= row; i++){for (int j = 1; j <= col; j++){if (board[i][j] != '*' && board[i][j] != '!')win++;}}return win;
}

排查雷(包括检测输赢):

首先根据选择,走向不同分支
选择1: 排查雷,首先判断坐标,若不在棋盘范围则重新输入,否则进入下一步判断;该位置若已被排查则重新输入坐标,没有排查过则进行排查,并显示周围雷的个数,同时计算已排查位置的个数
选择2: 标记雷,若该坐标已排查或已标记则重新输入,否则将该坐标置为" ! ",表示已被标记,并打印棋盘信息。
选择3: 取消标记,若该坐标已排查或未被标记则重新输入,否则将“ !”重新置为“ * ”。
其他选择则重新输入。

每选择一次,对win进行判断,若win等于棋盘上非雷个数的总和,则排雷成功,否则继续游戏直到游戏成功或失败。

void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int win = 0;int input = 0;while (win < (row * col - count)){printf("请选择->:1.排查雷 2.标记雷 3.取消标记\n");scanf("%d", &input);if (input == 1){printf("请输入要排查的坐标->:");scanf("%d%d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){if (show[x][y] == '*' || show[x][y] == '!'){if (mine[x][y] == '1'){printf("很遗憾,你被炸死了!!!\n");print_board(mine, ROW, COL);break;}else{open_area(mine, show, ROW, COL, x, y);print_board(show, ROW, COL);win = get_win(show, ROW, COL);}}else{printf("该坐标已被排查,请重新输入\n");}}else{printf("坐标非法,请重新输入\n");}}else if (input == 2){printf("请输入要标记的坐标->:");scanf("%d %d", &x, &y);//判断坐标合法性if (x >= 1 && x <= row && y >= 1 && y <= col){if (show[x][y] == '*'){show[x][y] = '!';print_board(show, ROW, COL);}else if (show[x][y] == '!'){printf("该位置已被标记,请重新选择!\n");}else{printf("该位置已被排查,不能被标记,请重新选择!\n");}}else{printf("坐标不合法,请重新输入!\n");}}else if (input == 3){printf("请输入要取消标记的坐标->:");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){if (show[x][y] == '!'){show[x][y] = '*';print_board(show, ROW, COL);}else{printf("该位置不能取消标记,请重新选择!\n");}}else{printf("坐标不合法,请重新输入!\n");}}else{printf("输入有误,请重新输入!\n");}}if (win == row * col - count){printf("\n恭喜你,排雷成功!\n");printf("雷的分布情况:\n");print_board(mine, ROW, COL);}
}

game.h文件

头文件

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

棋盘的大小以及雷的个数

使用宏定义,方便随时修改棋盘规格以及雷的个数

#pragma once//防止头文件重复调用
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define count 10

主要功能函数的声明

//初始化雷盘
void init_board(char board[ROWS][COLS], int row, int  col);
//打印雷盘
void print_board(char board[ROWS][COLS], int row, int col);
//布置雷
void set_mine(char board[ROWS][COLS], int row, int col);
//排查雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

三、完整代码

完整代码上传在gitee

点此跳转扫雷gitee源码

以上就是扫雷游戏的简单实现,细心的小伙伴发现错误欢迎指正。最后感谢您的观看和支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/802704.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【剪映专业版】04全局设置

视频课程&#xff1a;B站有知公开课【剪映电脑版教程】 设置-全局设置 草稿 草稿位置&#xff1a;非系统盘&#xff08;C盘&#xff09; 素材下载位置与 缓存管理&#xff1a;如果下载素材较多&#xff0c;需要定期删除缓存 预设保存位置&#xff1a;非系统盘&#xff08;C盘&a…

基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba并采用前后端分离的企业级微服务多租户系统架构

简介 基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba并采用前后端分离的企业级微服务多租户系统架构。并引入组件化的思想实现高内聚低耦合并且高度可配置化&#xff0c;适合学习和企业中使用。 真正实现了基于RBAC、jwt和oauth2的无状态统一权限认证的解决方案&#x…

C语言操作符详解(二)

一、位操作符 & 按位与 | 按位或 ^ 按位异或 ~ 按位取反 注意&#xff1a;它们的操作数必须是整数。 下面的码我都只取了后八位 1.1、按位与 使用补码进行按位与 规则:对应二进制位有0就是0,两个同时为1才为1. 1.2、按位或 使用补码进行按位或 规则:对应二进…

【攻防世界】Web_python_template_injection

{{}}是变量包裹标识符&#xff0c;里面存放的是一个变量&#xff0c;当你输入 http://61.147.171.105:55121/{{8*8}} 执行成功&#xff0c;说明存在模版注入。接下来&#xff0c;开始想办法编代码拿到服务器的控制台权限 。 首先&#xff0c;题目告诉我们这是一个 python 注入…

SysTick滴答定时器 - 延时函数

SysTick定时器 Systick定时器&#xff0c;是一个简单的定时器&#xff0c;对于CM3,CM4内核芯片&#xff0c;都有Systick定时器。Systick定时器常用来做延时&#xff0c;或者实时系统的心跳时钟。这样可以节省MCU资源&#xff0c;不用浪费一个定时器。比如UCOS中&#xff0c;分…

每日一题:矩阵置零

给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]使用两个标记变量。 class Sol…

全球IP数据库:多维度的数据收集与应用

随着互联网的普及和信息技术的飞速发展&#xff0c;全球IP数据库作为一种重要的数据资源&#xff0c;正在被广泛应用于各个领域。全球IP数据库不仅包含了庞大的IP地址信息&#xff0c;还涵盖了丰富的多维度数据&#xff0c;这些数据可以帮助企业、政府和研究机构更好地了解用户…

并发学习26--多线程 异步模式之工作线程

定义&#xff1a;让有限的工作线程&#xff08;Worker Thread&#xff09;来轮流异步处理无限多的任务。线程池便是这种模式。 饥饿&#xff1a; 固定大小线程池也会有饥饿现象。 若一个线程池有两个线程&#xff0c;能够处理两种任务。但是两种任务间有先后顺序。若来一个任…

用户画像——集群搭建

用户画像——集群搭建 1.设计一个规模合适的集群 1.1.1资源预估 1.1.2 选择服务器 2.部署和管理集群的工具 2.1 Hadoop的发展历程 2.2 部署和管理Hadoop的集群并不简单 三种工具的部署方式 3.自动创建虚拟机 3.1 什么是Vagrant&#xff1f; 3.2 安装Vagrant和概念介绍 3.3 使用…

c++——sort()函数

一、代码和效果 #include<bits/stdc.h> using namespace std;int main() {int a[6]{1,45,2,5,456,7};sort(a,a6);for(int i0; i<6; i){cout<<a[i]<<" "<<endl;}return 0; } 二、sort函数解析 &#xff08;从小到大&#xff09; std::so…

深入理解k8s kube-proxy

1、概述 我觉得只要大家知道kube-proxy是用来配置网络规则的而不是转发流量的&#xff0c;真正的流量由iptables/ipvs来转发就可以了。 网络是k8s的一个关键部分。理解k8s中网络组件如何工作可以帮助更好的设计和配置我们的应用。 kube-proxy就是K8s网络的核心组件。它把我们…

从“危”到“机”:HubSpot如何助企业转化出海营销CRM风险?

在全球化的大背景下&#xff0c;越来越多的企业选择出海拓展业务&#xff0c;以寻求更大的发展空间。然而&#xff0c;随着市场的扩大&#xff0c;企业在出海营销过程中也面临着各种风险。为了有效规避这些风险&#xff0c;许多企业选择借助HubSpot这样的专业营销软件。今天运营…

国际数字影像产业园构建成都文创产业园新地标!

国际数字影像产业园区&#xff0c;位于成都市金牛区的核心地带&#xff0c;不仅地理位置得天独厚&#xff0c;而且周边配套设施完善&#xff0c;交通便捷&#xff0c;不止成为北二环的新地标&#xff0c;正在构建成都文创产业园新地标&#xff01; 国际数字影像产业园这一片区…

专利年费缴纳后电子票据

专利年费缴纳后的电子票据 随着信息技术的快速发展&#xff0c;电子票据作为一种新型的支付与记录方式&#xff0c;已经深入到了我们日常生活的各个方面。特别是在知识产权领域&#xff0c;专利年费的缴纳也开始逐步实现电子化。 一、专利年费缴纳的重要性 专利年费是专利权…

5【PS让图片动起来】系列3-【时间轴 花瓣飘落】

【问题介绍】上章将花瓣拆分为一片片花瓣&#xff0c;现在让这片花瓣【动态】飘落吧~ PS选择“窗口”→ “时间轴” 下方弹出时间轴&#xff0c;其中01:00f就是这个位置为1秒的。中间的0 10f 20f等&#xff0c;为帧速率&#xff0c;也就是一秒的动画时间里有几帧的意思 下图…

Linux进阶篇:磁盘管理(二):LVM的创建、格式化和使用

Linux磁盘管理&#xff08;二&#xff09;&#xff1a;LVM的创建、格式化和使用 一、LVM原理回顾 LVM的工作原理进行一个总结&#xff1a; (1)物理磁盘被格式化为PV&#xff0c;空间被划分为一个个的PE (2)不同的PV加入到同一个VG中&#xff0c;不同PV的PE全部进入到了VG的PE…

2024年【R1快开门式压力容器操作】最新解析及R1快开门式压力容器操作试题及解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【R1快开门式压力容器操作】最新解析及R1快开门式压力容器操作试题及解析&#xff0c;包含R1快开门式压力容器操作最新解析答案和解析及R1快开门式压力容器操作试题及解析练习。安全生产模拟考试一点通结合国家…

比特币革命:刚刚开始

作者&#xff1a;Marius Farashi Tasooji 编译&#xff1a;秦晋 要充分理解比特币及其含义&#xff0c;首先必须理解什么是价值&#xff0c;什么是货币。以及是什么赋予资产价值&#xff1f; 这个问题看似愚蠢&#xff0c;但实际上非常有趣。我们的生活是由我们消费或出售的物品…

scrapy 爬取m3u8视频

scrapy 爬取m3u8视频 【一】效果展示 爬取ts文件样式 合成的MP4文件 【二】分析m3u8文件路径 视频地址&#xff1a;[在线播放我独自升级 第03集 - 高清资源](https://www.physkan.com/ph/175552-8-3.html) 【1】找到m3u8文件 这里任务目标很明确 就是找m3u8文件 打开浏览器…

CPU和GPU分别是什么?主要区别是什么?

CPU与GPU的区别 定义性能特点应用领域结构与体积控制与协同工作特殊用途与限制脑图 定义 CPU: 中央处理器&#xff0c;负责解释并执行指令GPU: 图形处理器&#xff0c;专用于图形和视频处理 性能特点 CPU 计算量小&#xff0c;适合复杂运算结构组成复杂&#xff0c;控制逻辑…