基于C语言扫雷游戏的设计与实现
作者:Code_Cao 发布时间:2023-06-03 01:46:49
1 引言
伴随着信息技术的快速发展,近年来,人们的生活已经离不开计算机。生活娱乐几乎都是在计算机上进行的。其中的扫雷游戏就是之一。扫雷游戏是微软公司在1992年在windows系统上发布的一款益智类小游戏,直到今天这款小游戏依然存在,可见此款游戏的成功。本文将用Visual Studio 2019作为开发工具来模拟实现扫雷游戏。经过大一第一学期的学习,我们对C语言的理论及其相关知识有了一定的认识和了解。本文可以把我们所学的理论知识和实践动手能力相结合,另外也可以对我所学过的知识进行复习巩固。通过探索windows的扫雷小游戏,我们可以巧妙地和这学期学过的C语言相结合起来模拟实现 * 面化简易版的扫雷小游戏。
2 相关工作
准备Visual Studio 2019开发工具,了解数据类型,会使用选择语句、循环语句、函数、数组等内容。
了解ASCII码值、随机数的生成。了解二维坐标相关的数学知识。
3 本文方法
3.1玩家游戏思路
进入菜单,进行选择是否进入,或退出游戏。
进入游戏模块,输入坐标进行扫雷。扫到雷,结束游戏,没扫到雷,进行标记该位置周围相邻八个位置有多少个雷,直到玩家失败或者赢掉此游戏。
3.2游戏构思细节
由于本游戏玩家看到的界面是由9* 9大小的方格构成并用字符* 对B雷盘进行覆盖的画面,但当我们计算一个非雷的位置周围八个相邻位置是否有雷时,这样就会导致数组越界,为了防止越界,我们将雷盘设置为11乘11。因为是字符* 覆盖,所以对B雷盘创建用的是字符类型的二维数组,同时为了方便实现一次声明,两次调用等操作。我们把a雷盘也用字符类型的二维数组进行创建。
3.3 游戏设计实现
3.3.1 游戏分三个模块,test.c,game.c和game.h。
(1)以模块化的函数思想进行设计,使游戏整体思路更加清晰。首先打开Visual Studio 2019,创建扫雷游戏的空项目,创建test.c源文件,和game.c源文件,一个game.h头文件。c语言中头文件中一般定义了函数的声明、结构体的定义、宏定义。(常量和全局变量最好放到源文件中)。C语言源文件中我们放置一些函数。来将游戏的具体实现。 源文件(test.c)里面放主函数和游戏的整体功能。
3.3.2 在头文件中进行准备工作
(1)#define进行宏定义,进行 InitMine,SetMine,DisplayMine,FindMine等函数的声明。
//宏定义便于后期的更改
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROWS ROW + 2
//设置11*11的格子防止数组越界。
#define COLS COL + 2
#define ROW 9
#define COL 9
//以下是对函数的声明
void InitMine(char mine[ROWS][COLS], int row, int col, char set);
void SetMine(char mine[ROWS][COLS], int row, int col);
void DisplayMine(char show[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//设置雷的个数
#define minecount 10
3.3.3 test.c中游戏大致功能的实现
(1)生成菜单栏
对于玩家来说,游戏的开始之前需要一个菜单栏进行选择游戏的开始或者退出,这时候就要用do语句生成菜单栏,并在其中使用switch语句进行选择进入或退出游戏。
test()
{
//用rand()函数之前需要先用srand函数。
srand((unsigned int)time(NULL));
int n = 0;
//游戏进去之后需要先有个菜单所以用上do语句
do
{
//打印菜单
menu();
scanf("%d", &n);
switch (n)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("请重新选择\n");
break;
}
} while (n);
}
//程序的入口
int main()
{
test();
return 0;
}
(2)调用game函数
我们将A盘有mine数组表示,B盘用show数组表示。
在switch中调用game函数进行char mine[ROWS][COLS] = { 0 };char show[ROWS][COLS] = { 0 };等数组的创建。用InitMine(mine, ROWS, COLS, ‘0');InitMine(show, ROWS, COLS, ' * );进行雷盘的初始化。我们设置雷为字符1,非雷为字符0。首先,把A盘初始化为非0,即全都不是雷,把B盘初始化为*号。接着用SetMine(mine, ROW, COL);DisplayMine(show, ROW, COL)运用随机数生成函数,向A盘随机布置雷;//用B盘进行展示雷的信息和结果,接着用FindMine(mine, show, ROW, COL);进行排雷
game()
{
system("cls");//对画面进行清屏,使页面简洁。
char mine[ROWS][COLS] = { 0 };//创建11*11数组
char show[ROWS][COLS] = { 0 };
//第一次传mine数组名
InitMine(mine, ROWS, COLS, '0');
//第二次传show数组名
InitMine(show, ROWS, COLS, '*');
//在A雷盘布置雷,所以需要传mine数组名
SetMine(mine, ROW, COL);
//用B盘进行展示雷的信息和结果
DisplayMine(show, ROW, COL);
//进行排雷
FindMine(mine, show, ROW, COL);
}
3.3.4game.c * 能具体实现
(1)InitMine运用双重for循环,初始化棋盘。
(2)SetMine运用随机数的生成,rand函数()函数,布置雷。
(3)DisplayMine用双重for循环遍历,展示棋盘
(4)FindMine用while循环和if的嵌套,期间关键在于调用SpreadMine函数进行递归,再调用is_win函数进行判断输赢。
关键部分代码(递归)
void SpreadMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
//调用CountMine函数进行查找周围八个位置雷的个数。
int count = CountMine(mine, x, y);
if (0 == count)
{
show[x][y] = ' ';
if (show[x][y + 1] == '*')
SpreadMine(mine, show, x, y + 1);
if (show[x][y - 1] == '*')
SpreadMine(mine, show, x, y - 1);
if (show[x + 1][y] == '*')
SpreadMine(mine, show, x + 1, y);
if (show[x + 1][y - 1] == '*')
SpreadMine(mine, show, x + 1, y - 1);
if (show[x + 1][y + 1] == '*')
SpreadMine(mine, show, x + 1, y + 1);
if (show[x - 1][y] == '*')
SpreadMine(mine, show, x - 1, y);
if (show[x - 1][y + 1] == '*')
SpreadMine(mine, show, x - 1, y + 1);
if (show[x - 1][y - 1] == '*')
SpreadMine(mine, show, x - 1, y - 1);
}
else
{
//如果该位置是雷,则将雷的个数显示在此位置
show[x][y] = count + '0';
//由于‘0'的的ASCII码值为48,‘1'的ASCII码值是49.
// 所以
//1 + '0' = '1'
//2 + '0' = '2'
}
}
4 结果与分析
本文实现了扫雷游戏的基本功能,根据提示,输入坐标从而实现扫雷。如果踩到雷就会退出游戏。如果剩下的*号等于雷数,则赢得游戏。如果输入坐标不对,则重新输入。最后通过递归实现了扫雷一片展开的功能。待有提高的是实现图形界面化和用鼠标操作界面可以学习EasyX来进一步优化。
5 总结
通过对此扫雷游戏的设计和实现,我们回顾了C语言的大半部分知识并对其中的细节进行了剖析。学习了C语言编程风格,了解了一些较常用的知识。我们对二维数组的应用,函数的声明,定义,和调用,以及if,switch选择语句和for,do,while循环语句有了更深刻的理解,提升了我们的逻辑思维能力。同时对ASCII码,随机数生成的应用也有了一些了解。另外,也提升了我们的模块化思维能力,比如我们用三个模块来构建此游戏。并且游戏中每个功能的实现都是由函数模块构成的。
GitHub源码链接:
https://github.com/CaoCoder/C_code
整体代码
头文件
//宏定义便于后期的更改
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROWS ROW + 2
//设置11*11的格子防止数组越界。
#define COLS COL + 2
#define ROW 9
#define COL 9
//以下是对函数的声明
void InitMine(char mine[ROWS][COLS], int row, int col, char set);
void SetMine(char mine[ROWS][COLS], int row, int col);
void DisplayMine(char show[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//设置雷的个数
#define minecount 10
源文件
#define _CRT_SECURE_NO_WARNINGS
#include "12_9game.h"//引用自己创建的头文件用双引号
//对9*9雷区遍历查*的数量和minecount进行比较。
int is_win(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int c = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if ('*' == show[i][j])
{
c++;
}
}
}
return c;
}
//用递归,进行实现一片展开的功能
void SpreadMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
//调用CountMine函数进行查找周围八个位置雷的个数。
int count = CountMine(mine, x, y);
if (0 == count)
{
show[x][y] = ' ';
if (show[x][y + 1] == '*')
SpreadMine(mine, show, x, y + 1);
if (show[x][y - 1] == '*')
SpreadMine(mine, show, x, y - 1);
if (show[x + 1][y] == '*')
SpreadMine(mine, show, x + 1, y);
if (show[x + 1][y - 1] == '*')
SpreadMine(mine, show, x + 1, y - 1);
if (show[x + 1][y + 1] == '*')
SpreadMine(mine, show, x + 1, y + 1);
if (show[x - 1][y] == '*')
SpreadMine(mine, show, x - 1, y);
if (show[x - 1][y + 1] == '*')
SpreadMine(mine, show, x - 1, y + 1);
if (show[x - 1][y - 1] == '*')
SpreadMine(mine, show, x - 1, y - 1);
}
else
{
//如果该位置是雷,则将雷的个数显示在此位置
show[x][y] = count + '0';
//由于‘0'的的ASCII码值为48,‘1'的ASCII码值是49.
// 所以
//1 + '0' = '1'
//2 + '0' = '2'
}
}
//查找非雷位置周围八个位置有多少雷
int CountMine(char mine[ROWS][COLS], int x, int y)
{
return (mine[x - 1][y + 1] + mine[x][y + 1]
+ mine[x + 1][y + 1] + mine[x - 1][y]
+ 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 c = row * col - minecount;
while (1)
{
printf("请输入坐标(x,y):");
scanf("%d%d", &x, &y);
//将输入的坐标限制在9*9的雷区
if (x <= row && x >= 1 && y <= col && y >= 1)
{
if (mine[x][y] == '0')
{
SpreadMine(mine, show, x, y);
system("cls");
DisplayMine(show, ROW, COL);
if (is_win(show, ROW, COL) == minecount)
{
printf("恭喜你赢了!\n");
DisplayMine(mine, ROW, COL);
break;
}
}
else
{
printf("很遗憾你被炸死了~\n");
DisplayMine(mine, ROW, COL);
break;
}
}
else
{
printf("输入坐标越界,请重新输入:");
}
}
}
//初始化棋盘,一个函数,两次调用。
void InitMine(char mine[ROWS][COLS], int row, int col, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
mine[i][j] = set;
}
}
}
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int c = 0;
while (c != minecount)
{
int x = rand() % row + 1;
//用rand()函数对9求余得到0~9这十个数,再加一就得到1~10.
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
//如果此位置为‘0',即非雷
//则设置为‘1',即雷
mine[x][y] = '1';
c++;
}
}
}
//运用双重循环打印9*9的B雷区
void DisplayMine(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
if (0 == i)
{
printf("%d ", j);
}
else
{
printf("%c ", show[i][j]);
}
}
printf("\n");
}
}
源文件
#define _CRT_SECURE_NO_WARNINGS
#include "12_9game.h"
game()
{
system("cls");//对画面进行清屏,使页面简洁。
char mine[ROWS][COLS] = { 0 };//创建11*11数组
char show[ROWS][COLS] = { 0 };
//第一次传mine数组名
InitMine(mine, ROWS, COLS, '0');
//第二次传show数组名
InitMine(show, ROWS, COLS, '*');
//在A雷盘布置雷,所以需要传mine数组名
SetMine(mine, ROW, COL);
//用B盘进行展示雷的信息和结果
DisplayMine(show, ROW, COL);
//进行排雷
FindMine(mine, show, ROW, COL);
}
menu()
{
printf("┏━━━━━━┓━━━━━━━━━━━━━━━━━━━━┓━━━━━━━━━━━┓\n");
printf("┣━━━━━━┫━━━━━━1.PLAY━━━━━━━━┫━━━━━━━━━━━┫\n");
printf("┣━━━━━━┫━━━━━━1.EXIT━━━━━━━━┫━━━━━━━━━━━┫\n");
printf("┗━━━━━━┛━━━━━━━━━━━━━━━━━━━━┛━━━━━━━━━━━┛\n");
}
test()
{
//用rand()函数之前需要先用srand函数。
srand((unsigned int)time(NULL));
int n = 0;
//游戏进去之后需要先有个菜单所以用上do语句
do
{
//打印菜单
menu();
scanf("%d", &n);
switch (n)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("请重新选择\n");
break;
}
} while (n);
}
//程序的入口
int main()
{
test();
return 0;
}
来源:https://blog.csdn.net/qq2466200050/article/details/121803433
猜你喜欢
- 一、使用无参构造方法创建二、使用静态工厂创建三、使用实例工厂创建来源:https://www.cnblogs.com/jock766/p/1
- 1. 理解abstract:抽象的2. 作用abstract可以用来修饰类、方法。不能用abstract修饰变量、代码块、构造器。不能用ab
- 一、题目描述题目实现:获取远程服务器和客户机的IP地址和端口号。二、解题思路创建一个服务器类:ServerSocketFrame,继承JFr
- 概述现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。J
- 这篇文章主要介绍了Spring ApplicationListener * 用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具
- FeignClient发送post请求异常这个问题其实很基础。但是却难倒了我。记录一下在发送post请求的时候要指定消息格式正确的写法是这样
- Java super关键字super 关键字与 this 类似,this 用来表示当前类的实例,super 用来表示父类。super 可以用
- 一、什么是深拷贝和浅拷贝对于所有面向对象的语言,复制永远是一个容易引发讨论的题目,C#中也不例外。此类问题在面试中极其容易被问到,我们应该在
- 俗话说没有规矩就没有方圆,java作为一门严谨的面向对象的高级编程语言,自然对权限整个重要的问题有严格的控制。Java中,可以通过一些Jav
- 有的时候,我们需要对一堆数据进行统计分析后生成HTML或Excel格式报表。本来这并不是一件很难的事,但确是件比较麻烦的事情。最令人头痛的是
- 看过阿里巴巴开发手册的同学应该都会对Integer临界值127有点印象。原文中写的是:【强制】所有整型包装类对象之间值的比较,全部使用 eq
- 本文实例讲述了Java日期操作类常见用法。分享给大家供大家参考,具体如下:一 取出当前日期时间1 代码import java.time.*;
- 在拼接绝对路径的网址时,经常需要从Request.Url中获取根网址(比如https://git.oschina.net),然后与相对路径一
- Java中的StringUtils引入及使用pom.xml中引入依赖<!-- https://mvnrepository.com/ar
- 简介方案对比本处列举表示类型或状态的常用方法的对比。法1:使用数字表示(不推荐)//1:支付宝支付;2:微信支付;3:银行卡支付privat
- 本文实例讲述了C#获取路径的几种方式。分享给大家供大家参考。具体如下:string str1 =Process.GetCurrentProc
- 向shell提供命令非常简单,需要学习的注解很少。该命令的实现风格与使用依赖注入的应用程序的开发类相同,您可以利用Spring容器的所有特性
- 在日常开发中,可能会遇到同一份代码,需要根据运营需求打出不同包名、不同图标、不同名称的Apk,发布到不同的渠道中。Android Studi
- 一、C#和JS互相调用 1、js调用C# C#代码如下: webView.CoreWebView2.AddHo
- 本文为大家分享了Java实现班级管理系统的具体代码,供大家参考,具体内容如下需求:班级管理系统功能:对学生的信息进行管理1 登录系统 &nb