基于:C语言,GTK+2.0,Sqlite3
使用工具:VMware,MobaXterm,Sublime Text3
一丶下载及运行
下载链接:https://pan.baidu.com/s/1L5Z-S6Zy08dUVv6txAhB0g?pwd=qi5h
提取码:qi5h
若只进行游玩,只需下载“扫雷”文件,并安装mysql运行库
打开Linux系统,打开终端,输入 sudo apt-get install libmysqlclient-dev 回车
然后将下载的“扫雷”文件于Linux内打开即可
若无法将文件从Windows拖入Linux,则输入以下指令
sudo apt-get autoremove open-vm-tools
sudo apt-get install open-vm-tools-desktop
然后重启虚拟机,即可将文件拖入Ubuntu中
二丶功能说明
1.游戏主界面:

2.登陆/注册模块:

登陆功能:
输入用户名和密码,点击登陆,将于数据库查询用户名是否存在
若不存在,则弹窗消息(该用户不存在!)
若存在,则检查输入框密码与该用户密码是否相同,
若相同则登陆成功
否则,弹窗消息(登陆失败!)
注册功能:
输入用户名和密码,点击注册,将于数据库查询用户名是否存在
若存在,则弹窗消息(该用户已存在!)
若不在,则将输入框用户名和密码写入数据库
登陆成功后,主界面:
“登陆”变为“注销”,按下注销后回到未登录状态
“游客模式”更改为“开始游戏”


2.游戏模块:
点击“游客模式/开始游戏”,弹出游戏界面

若未点击到地雷,则显示该处3×3包围范围内的地雷数量:

若点击到空白处:向周围扩散触发按钮,直至遇到数字
若所有非地雷单元均被点开,则游戏胜利,弹出胜利消息框

上传按钮:
若未登陆,则为灰,不可触发,
若已登陆,则记录(用户名,本局游戏地雷数,完成游戏所用时间,地图大小,地雷量%)
并上传至数据库,表gamerank
重玩按钮:重新载入游戏界面
返回按钮:返回主菜单
若点中地雷,则显示所有地雷位置,并弹窗(游戏结束!)

点击确定退出游戏界面,返回主菜单
3.游戏设置界面
可更改“游戏地图大小”“炸弹数量百分比(非精确)”

原理与登陆注册相同,不过并不写入数据库
而是更改两个加载游戏界面时会读取的全局变量mapsize,boomnumber。
如:10×10:1%(左) 50×50:50%(右)


4.排行榜:
点击后弹出游戏胜利排行榜

读取数据库中,由已登录的胜利界面上传的游戏数据,按照排雷数量排序
(注:为防止数据过多仅有完成游戏的游戏数据可上传,即仅胜利数据)
(另:图中负数时间与0时间,均为测试数据)
5.退出游戏
点击后结束进程。
三丶实现思路
登陆/注册模块:
在登陆按钮的信号函数中调用此函数,参数为两个输入框中的内容

注册同理,注册按钮的信号函数中调用此函数,参数为两个输入框中的内容

游戏模块:
1.地雷数:
首先,定义两个整数变量mapsize和gamelevel
其中,mapsize代表地图大小,gamelevel代表游戏难度
再定义一个二维数组A,遍历A并赋值A中的每一个元素一个随机数
该随机数以当前系统时间为种子,并余以100,即被限制在(0~99)中
由于扫雷游戏中,一个非雷方框周围最多有8个雷,即下图(红色代表有雷)

则可能的数字有0(空白),1,2,3,4,5,6,7,8
没有9,则可使用9代表地雷
如果数组A中,元素随机出的数小于gamelevel,则将该元素置为9,否则,置为0

如图,代表gamelevel的数越大,则有越多的元素被置为9(地雷)
即实现了游戏难度等级越大,游戏中的地雷越多
输出该数组A,并适当地排列,即可得到一张仅有0(非雷)和9(地雷)组成的游戏地图

2. 显示周围地雷数量:
很简单,遍历数组A中的所有元素,判断该元素周围所有元素是否为9
若是,则该元素的值+1

输出数组A:

则地雷地图的底层功能已经实现
3. 图形界面的实现:
3.1地雷按钮
创建一个GtkWidget的二维数组B

使用gtk_toggle_button控件实现按下按钮后不回弹的效果
遍历数组B,并给每一个元素赋值gtk_toggle_button

使用gtk_widget_set_sensitive防止再次点击回弹按钮

所有非空按钮信号函数赋为onclick,如下
点击按钮,遍历数组B,取得该被点按钮在数组中的坐标(n,m),传入change()函数中

Change函数的功能为:
当点击按钮时,获取数组A表上坐标为(n,m)元素的值
若该值不为9,则改变数组B中(n,m)位置按钮的文本为该值
其中该值为0时,文本应为空(“”)
若该值为9,则代表点击到地雷,改变该位置按钮文本为“X”,弹出窗口,结束游戏
如此,则可实现数组B与数组A一一对应,用数组A操控数组B。具体代码如下:

4. 空白展开功能的实现:
当点击空白时,理应将周围的空白一起显露
思路为:当点击空白时,遍历该空白按钮周围上下左右4个位置的值
若为0,则递归那个值为0的位置,如此递归下去,直至所有空白都递归
另外,虽只递归周围的0位,但也要将本0位周围的非0位按钮触发,又0位周围必无地雷
即可实现以数字为空白区域边缘的效果
如下图:

函数名为void blank(int n,int m),(n,m)由按键的信号函数提供,同上change()
若周围有值为0,则再次调用函数
如点击位为(n,m),传入blank(n,m),判断出(n+1,m)位为0
则再次调用blank(n+1,m),以此类推,即可实现空白展开
代码如下:

5. 判断胜利:
当场上所有非地雷按钮均被触发时,则为胜利
思路:标记每一个被点击或被递归的按钮,以其值置为10标记为已点击,如上图blank中

其中,mapboom即为本文中的数组A
然后,每次点击或递归时,执行update()函数
效果为,遍历当前数组A中的值,若数组A中不存在小于9的数(0-8)
则代表所有非地雷按钮均已触发,则弹出“胜利窗口”
6. 规避递归错误
需要注意,当递归到数组A的边缘时,如(0,0)位,明显(0-1,0)位不存在
此时无法判断,程序会崩溃,则需要在数组外围预置一组数据,将该数据置为10

此时输出如图:

不规则是因为10位两位,与个位无法对齐
大于10是因为该位置参与地雷数的判断,加上了周围的地雷数,不影响判剩条件
7. 游戏选项的实现
在最开始定义了两个整型参数mapsize和gamelevel;
每次生成游戏界面会调用这两个参数,只需将其设置为全局变量
再通过游戏界面和交互按钮,更改这两个参数
即可实现自定义“地图大小”和“难度等级”
由于在规避递归错误时,预留了2行和2列
则用户更改的mapsize和实际的mapsize须有2的差值,来实现图形上的游戏地图大小控制
8. 胜利界面/游戏结束界面
游戏胜利时,弹出胜利界面
若已登录,即iflogin ==1时,则上传按钮为可用,否则不可用,原理同游戏按钮

重新游玩则是销毁游戏界面并重新初始化并显示游戏界面,略
返回主菜单,略
游戏结束界面,略
9. 游戏地图按钮的排列
说完基本逻辑,说说图形的实现:
首先,游戏界面窗口随地图大小改变而改变

同时,表格布局与按钮数相同

批量对按钮进行布局和信号函数的赋值

如此,即可实现按钮的自动排列
排行榜的实现:
胜利界面的上传有

其中,
时间为:“弹出胜利界面时的系统时间”减去“打开游戏界面时系统时间”
即为所消耗的时间
gamepoint为排雷数量,在弹出胜利界面时,遍历数组A中值为9的元素个数即可得到
Name,mapsize,gamelevel等前文已有说明,不再赘述
排行榜:
数据库中所有数据按照排雷数降序排列,则有

将读取的RankList赋给一个字符指针数组P,其长度为行r乘以列c
排除无效第一行无效数据,则i从5开始依次赋给P

列出数据:
每一次gtk_clist_append,只能赋p[0]-p[5]的数据
则每一次赋值后,p内数据前移5,如下图,即可实现数据更新

需注意,需在数组尾部预留5个有数据的空间,否则将超出数组空间,程序将崩溃
四丶待优化
- 图形界面过于简陋,未美化
- 胜利界面信息过少,可添加排雷数,使用时间等信息
- 技术原因,暂无法实现右键标记地雷
- 代码性能低,算法未优化,许多遍历,应该时可以优化的
- 上传数据只能本地,不可云同步,后面有机会我将数据库上传至我的云服务器并连接,实现云同步排行榜
更新日志:
2023/9/14
更换数据库为mysql,重写登陆/注册,上传,排行榜部分,现在可以云同步用户账号和用户游戏数据
发表回复