C语言(扫雷游戏)

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

                        💥个人主页:小羊在奋斗

                        💥所属专栏:C语言   

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

                                一、扫雷游戏

                                                1、扫雷游戏规则

                                                2、扫雷游戏的实现

                                                3、

一、扫雷游戏

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.hqwc.cn/news/640203.html

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

相关文章

CSS基础:浮动(float)的3种方式,清除浮动3种方式的详解

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

Splashtop Cloud RADIUS 解决方案入围教育技术奖

2024年4月17日 加利福尼亚州库比蒂诺 Splashtop 在简化随处办公的远程解决方案领域处于领先地位&#xff0c;该公司自豪地宣布最近入围了《教育技术文摘》&#xff08;EdTech Digest&#xff09;的“2024年教育技术奖”的“炫酷工具”决赛。教育科技奖成立于2010年&#xff0c…

vue的学习之用vue写一个hello,vue

根据以下步骤下载vue.js 介绍 — Vue.js 创建一个damo.html &#xff0c;引入vue.js即可 <body><div id"app">{{ message }}</div><!-- Vue --><!-- 开发环境版本&#xff0c;包含了有帮助的命令行警告 --><script src"js/vu…

mysql的DDL语言和DML语言

DDL语言&#xff1a; 操作数据库&#xff0c;表等&#xff08;创建&#xff0c;删除&#xff0c;修改&#xff09;&#xff1b; 操作数据库 1&#xff1a;查询 show databases 2:创建 创建数据库 create database 数据库名称 创建数据库&#xff0c;如果不存在就创建 crea…

opencv_5_图像像素的算术操作

方法1&#xff1a;调用库函数 void ColorInvert::mat_operator(Mat& image) { Mat dst; Mat m Mat::zeros(image.size(), image.type()); m Scalar(2, 2, 2); multiply(image, m, dst); m1 Scalar(50,50, 50); //divide(image, m, dst); //add(im…

Hdu1150 Machine Schedule【二分图最大匹配、最小点覆盖】

Machine Schedule 题意 有两台机器&#xff0c;机器 A A A 有 n n n 个模式&#xff0c;机器 B B B 有 m m m 个模式 有 k k k 个任务&#xff0c;第 i i i 个任务只能由机器 A A A 的模式 x x x 或机器 B B B 的模式 y y y 完成 问最少需要多少个模式才能完成所有任…

67条tips实战案例渗透测试大佬的技巧总结

67条tips实战案例渗透测试大佬的技巧总结。 Tips 1. 手动端口探测 nmap的-sV可以探测出服务版本&#xff0c;但有些情况下必须手动探测去验证 使用Wireshark获取响应包未免大材小用&#xff0c;可通过nc简单判断 eg. 对于8001端口&#xff0c;nc连接上去&#xff0c;随便输…

如何判别三角形和求10 个整数中最大值?

分享每日小题&#xff0c;不断进步&#xff0c;今天的你也要加油哦&#xff01;接下来请看题------> 一、已知三条边a&#xff0c;b&#xff0c;c能否构成三角形&#xff0c;如果能构成三角形&#xff0c;判断三角形的类型&#xff08;等边三角形、等腰三角形或普通三角形 …

AB5 点击消除

AB5 点击消除 可以用栈来解决。 当栈为空的时候&#xff0c;直接将字符入栈当栈非空的时候 当前字符与栈顶字符相同 出栈 当前字符与栈顶字符不同 入栈 重复上述2步即可。 栈在输出的时候不能从栈底开始输出&#xff0c;需要先把栈顶元素弹出并保存下来&#xff0c;在进行输…

Windows 的常用命令(不分大小写)

Net user &#xff08;查看当前系统所有的账户&#xff09; net user yourname password /add 添加新用户 net localgroup administrators yourname /add 添加管理员权限 net user yourname /delete 删除用户 net user 命令 [colorred]说明&#xff1a;以下命令仅限持管理员…

PLC_博图系列☞N=:在信号下降沿置位操作数

、 PLC_博图系列☞N&#xff1a;在信号下降沿置位操作数 文章目录 PLC_博图系列☞N&#xff1a;在信号下降沿置位操作数背景介绍N&#xff1a; 在信号下降沿置位操作数说明参数示例 关键字&#xff1a; PLC、 西门子、 博图、 Siemens 、 N 背景介绍 这是一篇关于PLC编程的…

Linux 安装 Docker +Docker Compose + cucker/get_command_4_run_container

TIP&#xff1a;下面演示的 Linux 系统为 CentOS 7.9。 Docker 更新你的系统并安装必要的依赖项&#xff1a; sudo yum update -y sudo yum install -y yum-utils device-mapper-persistent-data lvm2添加 Docker 的官方仓库&#xff1a; sudo yum-config-manager --add-rep…