C语言(扫雷游戏)

news/2024/5/19 14:53:35

                     Hi~!这里是奋斗的小羊,很荣幸各位能阅读我的文章,诚请评论指点,关注+收藏,欢迎欢迎~~     

                        💥个人主页:小羊在奋斗

                        💥所属专栏:C语言   

        本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为同样是初学者的学友展示一些我的学习过程及心得。文笔、排版拙劣,望见谅。

                                一、扫雷游戏

                                                1、扫雷游戏规则

                                                2、扫雷游戏的实现

                                                        2.1打印游戏菜单

                                                        2.2游戏分析

                                                        2.3打印游戏棋盘

                                                        2.4埋雷

                                                        2.5扫雷

一、扫雷游戏

1、扫雷游戏的规则

        首先我们来介绍一下扫雷游戏的玩法,扫雷游戏的常规界面(9*9)如下:

         上面游戏板上有许多个格子,有些格子里面埋有雷,玩家需要点击格子揭开它们,如果揭开的格子里是雷则被炸死游戏结束,若果揭开的格子不是雷则显示点开的格子周围有多少个雷,玩家需要通过给出的信息进行逻辑判断和猜测来排除所有的雷。

2、扫雷游戏的实现

        2.1打印游戏界面

        了解完游戏的玩法后,我们就要来好好想想要怎么通过代码来实现这个小游戏。

        首先,我们需要新建一个 main.c 文件来存放函数的主体代码,新建一个 game.c 文件用来游戏实现代码,新建一个 game.h 来包含其中会用到的一些头文件和相关函数的声明。

        跟其他游戏一样,我们得有个游戏菜单吧,在 —> 猜数字小游戏 这篇文章中我们已经有了一种打印游戏菜单的方法,不妨我们就继续延用这种办法吧。

main.c 

#define  _CRT_SECURE_NO_WARNINGS#include "game.h"//这里我将头文件<stdio.h>包含到game.h,再在main.c和//game.c文件中包含game.h,避免重复引用void menu()
{printf("##########################\n");printf("########  1.play  ########\n");printf("########  0.exit  ########\n");printf("##########################\n");
}int main()
{int input = 0;do{menu();printf("请选择:");scanf("%d", &input);switch (input)//通过输入的input来选择开始游戏还是退出游戏{case 1:printf("开始游戏!\n");break;case 0:printf("退出游戏!");break;default:  //如果不小心输入错误的值,还要提示重新选择printf("选择错误,请重新选择!\n");break;}} while (input);//do—while循环可以帮助我们实现重复玩游戏return 0;
}

        代码运行先打印一个简易的游戏菜单,提示我们选择1开始游戏,选择0退出游戏,选择其他值则提示选择错误,重新选择。当我们选择1开始游戏,游戏结束后通过 break 跳出来到 while 判断,input 的值为1继续循环开始游戏;当我们选择0则退出游戏,通过 break 跳出来到 while 判断,input 的值为0退出循环;当我们选择其他非0的值通过 break 跳出来到 while 判断,非0继续循环。

        通过代码执行可以试验出我们当前的逻辑是正确的。我们在写工程量比较大的代码时,写完一段程序最好运行试验一下是否符合我们的想法。

        2.2游戏分析

        接下来我们就要分析一下该如何实现这个游戏了。首先我们得有一个矩形棋盘吧,这里我们就先设计一个简单的 9*9 游戏棋盘。提到矩形 9*9 棋盘我们就很容易联想到之前学习过的二维数组,二维数组就能很好的帮我们实现这个事情,并且二维数组还能通过坐标唯一确定一个小格子。有了游戏棋盘在玩游戏之前还应该由系统自动布置好雷的位置,并且位置是随机的,影藏在棋盘下我们看不到。

        我们设计的这个棋盘不仅要事先随机布置隐藏好雷,还要在我们玩的时候显示周围雷的个数,是不是对这个棋盘要求太高了,我们实现起来也比较复杂。这里我们有一个还不错的解决办法,我们可以定义两个二维数组,一个用来随机产生并且隐藏雷,在我们玩游戏的时候并不打印;另一个在我们玩的时候打印显示排雷的信息也就是周围雷的个数。

        提到定义两个二维数组就不得不想清楚我们究竟要定义两个什么类型的二维数组呢?在这之前,我们需要考虑一下怎么区分雷和非雷。其实这一步有很多种方法,想要怎么设计完全由你自己决定,这里我们不妨就定义字符 ‘0’ 为雷,字符 ‘1’ 为非雷吧,至于为什么要定义为字符而不是我们常见的数字1和0,其实是有原因的。

        我们前面说过,如果揭开的格子下不是雷,就要将这个格子周围的雷的数目加起来并在我们揭开的这个格子上显示,要显示的话当然显示的是数字,如果这个格子周围恰好是一个雷就要在这个格子上显示数字1,这就非常容易与我们定义的雷(0)或非雷(1)冲突,我们定义为字符的话就可以很好的避免这个问题。

 

         还有一个隐藏的问题,如果我们想排查(8,7)这个坐标,很明显越界了,那我们要判断这个坐标是不是雷之前还要先判断数组是否越界,因为数组越界是比较危险的事情,谁也不知道越界访问到的是什么数据,严重还会导致程序崩溃,所以我们要想办法避免这个问题。

        我们可以把之前定义的两个字符型二维数组大小改为 11 行 11 列,而不是用 9 行 9 列,在操作的时候外面一圈不操作,只在 9*9 的棋盘内排雷,这样就不会有越界的问题。

        2.3打印游戏棋盘

       接上所述,我们定义了两个 11 行 11 列的字符型二维数组,定义好后我们先将埋雷的二维数组初始化为 ‘1’,将显示排雷信息的二维数组初始化为 * ,因为埋雷的二维数组并不打印,所以我们就实现了用一个棋盘覆盖另一个棋盘的效果。

        到这里我们先来看一下效果:

         测试效果跟我们预期的一样,当然,在真正玩的时候上面埋雷的棋盘是不打印的,这里我们只是测试一下棋盘是否初始化成功。

        相关代码如下:

        main.c 

#define  _CRT_SECURE_NO_WARNINGS#include "game.h"//这里我将头文件<stdio.h>包含到game.h,再在main.c和//game.c文件中包含game.h,避免重复引用void menu()
{printf("##########################\n");printf("########  1.play  ########\n");printf("########  0.exit  ########\n");printf("##########################\n");
}void game()
{//定义两个二维数组作棋盘char mine[ROWS][COLS] = { 0 };//存放雷char show[ROWS][COLS] = { 0 };//存放排雷的信息//初始化二维数组Init_Board(mine, ROWS, COLS, '1');Init_Board(show, ROWS, COLS, '*');//打印棋盘Display_Board(mine, ROW, COL);Display_Board(show, ROW, COL);}int main()
{int input = 0;do{menu();printf("请选择:");scanf("%d", &input);switch (input)//通过输入的input来选择开始游戏还是退出游戏{case 1:game();break;case 0:printf("退出游戏!");break;default:  //如果不小心输入错误的值,还要提示重新选择printf("选择错误,请重新选择!\n");break;}} while (input);//do—while循环可以帮助我们实现重复玩游戏return 0;
}

         game.c

#define  _CRT_SECURE_NO_WARNINGS#include "game.h"void Init_Board(char board[ROWS][COLS], int rows, int cols, char set)
{int i = 0;for (i = 0; i < rows; i++){int j = 0;for (j = 0; j < cols; j++){board[i][j] = set;}}
}void Display_Board(char board[ROWS][COLS], int row, int col)
{int i = 0;printf("————————扫雷————————\n");for (i = 0; i <= row; i++){printf("%d ", i);}printf("\n");for (i = 1; i <= row; i++){printf("%d ", i);int j = 0;for (j = 1; j <= col; j++){printf("%c ", board[i][j]);}printf("\n");}
}

        game.h 

#pragma once#include <stdio.h>#define ROW 9  //定义二维数组的行和列方便修改大小
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2//初始化两个二维数组
void Init_board(char board[ROWS][COLS], int rows, int cols, char set);//打印棋盘
void Display_Board(char board[ROWS][COLS], int row, int col);

        注意: 虽然我们有些函数操作的是 ROW 和 COL , 但我们函数传参的时候传的还是 ROWS 和 COLS , 因此我们形参接收的时候一定要用 ROWS 和 COLS 接收。

        2.4埋雷

        我们之前确定了定义 ‘0’ 为雷,下面我们就来探讨如何埋雷。

        首先,我们需要确定埋多少个雷,可以定义一个符号常量来选择埋多少个雷,这里就先埋10个雷。其次,埋雷的话肯定是要随机的埋10个雷,那就要产生10个随机的坐标,产生随机数的函数我们在之前的猜数字小游戏中已经使用过,这里就不过多介绍了。(猜数字小游戏)

        我们在埋雷的过程中还需要判断这个坐标是否已经埋了雷,这个不难实现,只需要加一个 if 语句即可。我们来看代码实现:

        main.c

#define  _CRT_SECURE_NO_WARNINGS#include "game.h"//这里我将头文件<stdio.h>包含到game.h,再在main.c和//game.c文件中包含game.h,避免重复引用void menu()
{printf("##########################\n");printf("########  1.play  ########\n");printf("########  0.exit  ########\n");printf("##########################\n");
}void game()
{//定义两个二维数组作棋盘char mine[ROWS][COLS] = { 0 };//存放雷char show[ROWS][COLS] = { 0 };//存放排雷的信息//初始化二维数组Init_Board(mine, ROWS, COLS, '1');Init_Board(show, ROWS, COLS, '*');//打印棋盘//Display_Board(mine, ROW, COL);Display_Board(show, ROW, COL);//埋雷Set_Mine(mine, ROW, COL);Display_Board(mine, ROW, COL);//打印看一下埋雷是否成功}int main()
{int input = 0;srand((unsigned int)time(NULL));//调用rand函数之前先要调用srand函数do{menu();printf("请选择:");scanf("%d", &input);switch (input)//通过输入的input来选择开始游戏还是退出游戏{case 1:game();break;case 0:printf("退出游戏!");break;default:  //如果不小心输入错误的值,还要提示重新选择printf("选择错误,请重新选择!\n");break;}} while (input);//do—while循环可以帮助我们实现重复玩游戏return 0;
}

        game.c 

#define  _CRT_SECURE_NO_WARNINGS#include "game.h"void Init_Board(char board[ROWS][COLS], int rows, int cols, char set)
{int i = 0;for (i = 0; i < rows; i++){int j = 0;for (j = 0; j < cols; j++){board[i][j] = set;}}
}void Display_Board(char board[ROWS][COLS], int row, int col)
{int i = 0;printf("————————扫雷————————\n");for (i = 0; i <= row; i++){printf("%d ", i);}printf("\n");for (i = 1; i <= row; i++){printf("%d ", i);int j = 0;for (j = 1; j <= col; j++){printf("%c ", board[i][j]);}printf("\n");}
}void Set_Mine(char mine[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int count = 0;while (count < EASY_COUNT){x = rand() % row + 1;//产生范围为1-9的随机数y = rand() % col + 1;if (mine[x][y] != '0'){mine[x][y] = '0';count++;}}
}

        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 Init_board(char board[ROWS][COLS], int rows, int cols, char set);//打印棋盘
void Display_Board(char board[ROWS][COLS], int row, int col);//埋雷
void Set_Mine(char mine[ROWS][COLS], int row, int col);

        运行结果为: 

 

         可以看到,我们已经实现了埋10个雷。

        2.5扫雷

        接下来就到了最后一步,实现扫雷。

        我们需要输入一个坐标,判断此坐标下是否埋着雷,如果是雷则打印 “你踩雷了,游戏失败!”,并且打印出所有雷的位置;如果不是雷则需要在这个坐标处显示周围8个坐标内雷的个数,继续输入坐标扫雷。

        怎么获得排查过的坐标周围雷的个数呢?我们不难发现,(x,y)周围8个坐标分别可以表示为x或y加-1、0、1得到的9个坐标,然后把这9个坐标的值分别进去字符 ‘0’,使其转换为整型再加起来,经过处理就能得到雷的个数。

        最终的代码为:

        main.c

#define  _CRT_SECURE_NO_WARNINGS#include "game.h"//这里我将头文件<stdio.h>包含到game.h,再在main.c和//game.c文件中包含game.h,避免重复引用void menu()
{printf("##########################\n");printf("########  1.play  ########\n");printf("########  0.exit  ########\n");printf("##########################\n");
}void game()
{//定义两个二维数组作棋盘char mine[ROWS][COLS] = { 0 };//存放雷char show[ROWS][COLS] = { 0 };//存放排雷的信息//初始化二维数组Init_Board(mine, ROWS, COLS, '1');Init_Board(show, ROWS, COLS, '*');//打印棋盘//Display_Board(mine, ROW, COL);Display_Board(show, ROW, COL);//埋雷Set_Mine(mine, ROW, COL);//Display_Board(mine, ROW, COL);//打印看一下埋雷是否成功//扫雷Find_Mine(mine, show, ROW, COL);
}int main()
{int input = 0;srand((unsigned int)time(NULL));//调用rand函数之前先要调用srand函数do{menu();printf("请选择:");scanf("%d", &input);switch (input)//通过输入的input来选择开始游戏还是退出游戏{case 1:game();break;case 0:printf("退出游戏!");break;default:  //如果不小心输入错误的值,还要提示重新选择printf("选择错误,请重新选择!\n");break;}} while (input);//do—while循环可以帮助我们实现重复玩游戏return 0;
}

        game.c

#define  _CRT_SECURE_NO_WARNINGS#include "game.h"void Init_Board(char board[ROWS][COLS], int rows, int cols, char set)
{int i = 0;for (i = 0; i < rows; i++){int j = 0;for (j = 0; j < cols; j++){board[i][j] = set;}}
}void Display_Board(char board[ROWS][COLS], int row, int col)
{int i = 0;printf("————————扫雷————————\n");for (i = 0; i <= row; i++){printf("%d ", i);}printf("\n");for (i = 1; i <= row; i++){printf("%d ", i);int j = 0;for (j = 1; j <= col; j++){printf("%c ", board[i][j]);}printf("\n");}
}void Set_Mine(char mine[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int count = 0;while (count < EASY_COUNT){x = rand() % row + 1;//产生范围为1-9的随机数y = rand() % col + 1;if (mine[x][y] != '0'){mine[x][y] = '0';count++;}}
}int Get_Mine_Count(char mine[ROWS][COLS], int x, int y)
{int i = 0;int count = 0;for (i = -1; i <= 1; i++){int j = 0;for (j = -1; j <= 1; j++){count += (mine[x + i][y + j] - '0');}}return (9 - count);
}void Find_Mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int count = 0;while (count < (row * col - EASY_COUNT))//排查出所有的非雷坐标后退出循环{printf("请输入想要排查的坐标:");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col)//必须输入范围内的坐标{if (mine[x][y] == '0'){printf("你踩雷了,游戏结束!\n");Display_Board(mine, ROW, COL);printf("\n\n\n");break;}else{int count = Get_Mine_Count(mine, x, y);show[x][y] = count + '0';//整型count需要加上字符‘0’才能赋值给字符型数组showDisplay_Board(show, ROW, COL);}}else{printf("输入的坐标有误,x和y的范围为1-%d\n", row);}}if (count == EASY_COUNT){printf("恭喜你,扫雷成功!\n");Display_Board(mine, ROW, COL);}
}

        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 Init_board(char board[ROWS][COLS], int rows, int cols, char set);//打印棋盘
void Display_Board(char board[ROWS][COLS], int row, int col);//埋雷
void Set_Mine(char mine[ROWS][COLS], int row, int col);//扫雷
void Find_Mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

            至此,我们就完成了整个游戏的实现。

                                    点击跳转主页—> 💥个人主页小羊在奋斗


http://www.mrgr.cn/p/87327183

相关文章

R语言随机森林RandomForest、逻辑回归Logisitc预测心脏病数据和可视化分析|附代码数据

全文链接:http://tecdat.cn/?p=22596 最近我们被客户要求撰写关于预测心脏病的研究报告,包括一些图形和统计输出。 本报告是对心脏研究的机器学习/数据科学调查分析。更具体地说,我们的目标是在心脏研究的数据集上建立一些预测模型,并建立探索性和建模方法。但什么是心脏研…

数据结构基础第3讲

数据结构基础第3讲 栈及其应用 内容考点一:栈的概念 1.顺序栈的定义:出栈顺序情况计算 给定n个元素,出栈顺序的情形满足卡特兰数,计算公式: \[\frac{C_{2n}^{n}}{n+1} \]例题: 确定第一个出栈的谁。有两种可能: 找带头大哥。栈的顺序存储结构 顺序栈操作顺序栈4要素栈空条…

数据结构基础第4讲

数据结构基础第4讲 队列 内容考点一: 队列概念 代码不考 1.队列的定义考点二:顺序队列的定义考点三顺序队列的性质与操作 4要素:考点四:循环队列的定义 由于顺序队列会存在假溢出问题,引入循环队列。 假溢出:描述:考点五:循环队列的操作判断空满:性质: 考频75%元素个…

深入探索GDB:Linux下强大的调试神器

目录 一、GDB简介&#xff1a;源码级调试的基石 二、GDB基础操作&#xff1a;从入门到熟练 启动与基本命令 三、GDB进阶功能&#xff1a;解锁更深层次的调试能力 1. 回溯追踪&#xff1a;洞察调用栈 2. 动态内存检测&#xff1a;揪出内存问题 3. 条件断点与观察点&#…

【视频】N-Gram、逻辑回归反欺诈模型文本分析招聘网站欺诈可视化|附数据代码

原文链接:https://tecdat.cn/?p=36028 原文出处:拓端数据部落公众号 随着互联网的快速发展,招聘网站已成为求职者与雇主之间的重要桥梁。然而,随之而来的欺诈行为也日益猖獗,给求职者带来了极大的困扰和风险。因此,如何帮助客户有效地识别和防范招聘网站上的欺诈行为,已…

02 IO口的操作

目录前言一、IO的概念1.IO接口2.IO端口二、CPU和外设进行数据传输的方法1.程序控制方式1.1 无条件1.2 查询方式2.中断方式3.DMA方式一、方法介绍和代码编写1.前置知识2.程序方式1.1 无条件方式1.1.1 打开对应的GPIO口1.1.2 初始化对应的GPIO引脚1.1.2.1 推挽输出1.1.2.2 开漏输…

vmstat命令详解

一、参数信息 vmstat 命令是用于报告虚拟内存统计信息的工具&#xff0c;常用于 Unix/Linux 系统上。它可以提供关于系统资源使用情况的详细信息&#xff0c;包括 CPU、内存、虚拟内存、磁盘、系统调用等方面的统计数据。以下是常见的 vmstat 命令参数的详解&#xff1a; vms…

题解 UOJ577【[ULR #1] 打击复读】

别学基本子串结构(这篇没写完)题解 UOJ577【[ULR #1] 打击复读 reference https://www.cnblogs.com/crashed/p/17382894.html https://www.cnblogs.com/sizeof127/articles/17579027.html 字符串——黄建恒,广东实验中学 题目描述 为了提升搜索引擎的关键词匹配度以加大访问…

VUE识别图片文字OCR(tesseract.js)

效果:1&#xff1a; 效果图2&#xff1a; 一、安装tesseract.js npm i tesseract.js 二、静态页面实现 <template><div><div style"marginTop:100px"><input change"handleChage" type"file" id"image-input"…

吴恩达机器学习-第二课-第四周

吴恩达机器学习 学习视频参考b站:吴恩达机器学习 本文是参照视频学习的随手笔记,便于后续回顾。 决策树 决策树模型(Decision Tree Model) 猫分类示例通过决策树模型判断是否为猫 一些术语:根结点,决策节点(包括根结点),叶子结点决策树算法是在所有的决策树模型中选一…

【EI会议征稿】2024年先进机械电子、电气工程与自动化国际学术会议(ICAMEEA 2024)

2024 International Conference on Advanced Mechatronic, Electrical Engineering and Automation ●会议简介 2024年先进机械电子、电气工程与自动化国际学术会议&#xff08;ICAMEEA 2024&#xff09;将汇聚全球机械电子、电气工程与自动化领域的专家学者&#xff0c;共同…

【QT进阶】Qt Web混合编程之使用ECharts显示各类折线图等

往期回顾 【QT进阶】Qt Web混合编程之QWebEngineView基本用法-CSDN博客 【QT进阶】Qt Web混合编程之CMake VS2019编译并使用QCefView&#xff08;图文并茂超详细版本&#xff09;-CSDN博客【QT进阶】Qt Web混合编程之html、 js的简单交互-CSDN博客 【QT进阶】Qt Web混合编程之使…

编译用于Qt的opencv问题解决

CMake was unable to find a build program corresponding to "MinGW Makefiles"解释: 这个错误表明CMake无法找到用于生成Makefiles的构建程序。在使用CMake生成项目文件时,如果指定了"MinGW Makefiles",CMake需要一个Make工具来构建项目,而这个工具通…

破解生产瓶颈,提升时效性——蓝鹏测控推进效率革新

在日益激烈的市场竞争中&#xff0c;蓝鹏公司近日宣布采取一系列措施&#xff0c;旨在解决生产过程中的关键短板问题&#xff0c;特别是设计定稿延迟、原料采购不及时等问题&#xff0c;以确保生产部门能够按时完成订单&#xff0c;提高整体运营效率。 蓝鹏公司位于经济发展活…

操作系统八股

操作系统八股 1. 你了解IO多路复用么? 我们熟悉的 select/poll/epoll 内核提供给用户态的多路复用系统调用,进程可以通过一个系统调用函数从内核中获取多个事件。 select/poll/epoll 是如何获取网络事件的呢?在获取事件时,先把所有连接(文件描述符)传给内核,再由内核返回…

chakra-ui学习笔记(一)

前言:发现chakra-ui也不错,虽然比起antd功能稍少一点。1,Stack与Flex区别 Notes on Stack vs Flex#The Stack component and the Flex component have their children spaced out evenly but the key difference is that the Stack wont span the entire width of the conta…

云原生Kubernetes: K8S 1.29版本 部署Jenkins

目录 一、实验 1.环境 2.K8S 1.29版本 部署Jenkins 服务 3.jenkins安装Kubernetes插件 二、问题 1.创建pod失败 2.journalctl如何查看日志信息 2.容器内如何查询jenkins初始密码 3.jenkins离线安装中文包报错 4.jenkins插件报错 一、实验 1.环境 &#xff08;1&…

深度解析 Spring 源码:三级缓存机制探究

文章目录 一、 三级缓存的概述二、 三级缓存的实现原理2.1 创建Bean流程图2.2 getBean()2.3 doGetBean()2.4 createBean()2.5 doCreateBean()2.4 getSingleton() 三、 三级缓存的使用场景与注意事项3.1 在实际开发中如何使用三级缓存3.2 三级缓存可能出现的问题及解决方法 一、…

股票数据爬虫

东方财富网-数据中心 —— 爬虫项目 0x00 起因 MaMa 看到别人有个软件,可以直接把一个网站上的数据全部爬进一个 Excel 里边,但是那个人不给这个软件,所以她怂恿我写一个。。。 0x01 需求 千股千评 _ 数据中心 _ 东方财富网 (eastmoney.com) 对于里边的00~60开头的股票,把股…

Linux多进程(一)创建进程与进程控制

一、进程状态 进程一共有五种状态分别为&#xff1a;创建态&#xff0c;就绪态&#xff0c;运行态&#xff0c;阻塞态(挂起态)&#xff0c;退出态(终止态)其中创建态和退出态维持的时间是非常短的&#xff0c;稍纵即逝。主要是就绪态, 运行态, 挂起态三者之间的状态切换。 就绪…