PyQt挖地雷游戏学习笔记(7)

1、游戏规则

地雷随机埋设在“棋盘”方格里,挖到地雷为败,挖光全部无雷方格为胜。

2、游戏的空间表示

游戏发生在棋盘上,游戏的场景、规则,都体现在棋盘上。

棋盘,由“场景盘”和“逻辑盘”共同组成。

“场景盘”是玩家挖雷面对的棋盘。

“逻辑盘”是实现游戏规则所需的“雷区盘”、“空区盘”和“提示盘”。

“逻辑盘”由数组表示。

棋盘、游戏规则之类,由模块game_scene.py进行设置、表示等处理。

3、游戏的起点开端

挖雷游戏,始于game_scene.py的class GameScene的start()。

每一盘新游戏的开始,都是通过调用这个函数。其中包括:

整个程序启动之初,main.py的MainWindow的init()中的调用;

菜单选择新开一局时,on_action_New_triggered()中的调用;

菜单设置游戏时,on_action_Setup_triggered()中的调用;

棋盘上端按钮按下后,槽函数me()中的调用。

def start(self):
    self.setStatus(RUNNING) #设置棋局状态
    self.mine_map = random_map(self.map_size, self.mines)  # 布置地雷
    self.hint_map = hint_map(self.mine_map)                # 提示雷情
    self.flag_map = np.zeros(self.map_size, dtype=np.bool) # bug 删除
    self.open_map = np.zeros(self.map_size, dtype=np.bool) # 挖开的空白区
    self.update() # 场景盘更新

注意!其中一句,如下所示,是有害无益的bug。

self.flag_map = np.zeros(self.map_size, dtype=np.bool)

程序中,所有与self.flag_map相关的语句,都应删除。

第一句,设置棋局状态,和最后一句,“场景盘”更新,放在后面说。

除去删除的bug,中间3句,分别是在“逻辑盘”上布雷、标志雷情、布置空白区域。

4、在逻辑盘上随机埋设地雷

def random_map(size, count):
    mine_map = np.zeros(size, dtype=np.bool)
    x, y = size
    while count > 0:
        rx = random.randint(0, x-1)
        ry = random.randint(0, y-1)
        if not mine_map[rx, ry]:
            count -= 1
            mine_map[rx, ry] = True
    return mine_map

random_map是模块级全局函数。size是表示棋盘长宽的元组,count表示地雷数目。

从lib.py导入的np,即numpy。这里的np.zeros返回一个二维数组,

数组大小由size确定,如(3,3);数组的9个成员都是False。例如:

>>> mine_map = np.zeros((3,3), dtype=np.bool)
>>> mine_map
array([[False, False, False],
       [False, False, False],
       [False, False, False]], dtype=bool)
>>> mine_map[1,1]
False
>>>

rx和ry是随机产生的整数。0 <= rx <= x-1,ry类似。

while循环的意思是说:如果地雷数目count > 0,

随机生成rx和ry作为坐标,并检测地雷逻辑盘mine_map[rx,ry]处是否有雷;

如果没有地雷,即该处值等于False,则将其值赋予True,并将地雷计数器减1;

如果此处有雷,即该处值等于True,则继续循环,生成新坐标布雷,直至全部完成。

5、在逻辑盘上标注提示

def hint_map(mine_map):
    x, y = mine_map.shape
    hint_map = np.zeros((x+2, y+2), dtype=np.uint8)
    for dx in range(x):
        for dy in range(y):
            if mine_map[dx, dy]:
                for tx in range(3):
                    for ty in range(3):
                        hint_map[dx+tx, dy+ty] += 1
    return hint_map[1:x+1,1:y+1]

逻辑盘上的标注提示,就是在空白格上标注,其四周共埋着多少地雷。

这个函数的算法,比较巧妙,值得学习参考。

注意!mine_map是numpy建立的数组,具有shape属性。Python的列表,没有该属性。

提示盘hint_map比布雷盘mine_map整整大一圈。若mine_map = (10,10),则hint_map = (12,12)

这相当于把mine_map置放在hint_map的中间,避免了检测判断数组边界的麻烦,简化了具体算法。

外层的2个for循环,是为了遍历整个逻辑盘mine_map;

如果在盘格mine_map[dx,dy]处发现地雷,即值为True,则开始内层循环,

以dx,dy为原点,将它自身所处行列,及其右2列,其下2行,共计9个盘格中标注的地雷数目加1。

这句return hint_map[1:x+1,1:y+1],通过数据剪切,巧妙地把hint_map上移一行,左移一列,

与mine_map的实际情况完全一致了。

6、逻辑盘空白区域的标注

这是不能事先设置的。因为它是玩家博弈时,鼠标点击产生的结果。

时间: 2024-12-10 00:43:42

PyQt挖地雷游戏学习笔记(7)的相关文章

PyQt挖地雷游戏学习笔记(2)

分析多个文件组成的源代码,最好使用辅助工具. 我试过Source Navigator 和 Source Insight,感觉后者功能多些,比较好用. 一.程序的组成模块等文件 这个挖地雷游戏,主要由2种文件组成: 1.程序模块 主程序main.py:负责主窗口物件,按钮和菜单的设置.事件处理.程序初始化.结束等等. 游戏逻辑模块game_scene.py:负责游戏规则.棋盘显示.胜负裁判.记录得分等等. "导入"文件lib.py:导入一些通用的模块,其他文件只需导入lib.py. 游戏

PyQt挖地雷游戏学习笔记(6)

学习别人的作品,有一大好处,可以反观自己的不足. 自己的不足,往往是基础知识有欠缺,基本功不扎实. 今天,再补一课:星号表达式(*expression). 挖地雷程序中,有2处用到星号表达式.一是在main.py,另一处在game_scene.py. 先看第一处的情况: @pyqtSlot() def on_action_Setup_triggered(self): result = setup.getSetup() if not result: return self.scene.setMap

PyQt挖地雷游戏学习笔记(1)

想学会PyQt.按习惯做法,从分析学习编程实例着手. 从网上找了个"挖地雷"的源码,大卸八块,仔细解剖,力图学到些东西. 原程序中有bug,如:"棋盘"没有下边界线:挖出全部地雷后,程序没反应,不认定获胜. 我已将其铲掉,可以正常运行了. 程序源码可在此下载 所做修改,都在game_scene.py.共有2处: 一是在函数checkWin()中,原文是: if ((self.mine_map == self.flag_map).all() and (self.min

PyQt挖地雷游戏学习笔记(4)

又遇到一个有意思的问题,它提醒了查阅文档和相关源码,与测试的重要性. 直接上代码,setup.py """ module: setup """ from lib import * from config import * form, base = loadUiType("setup.ui") class SetupDlg(QDialog, form): def __init__(self): super(SetupDlg, s

PyQt挖地雷游戏学习笔记(3)

这个程序使用了Python有特色的语法套路.先看第一个. main.py里的class MainWindow的函数init()中,有这样的语句: self.scene.setMap((conf.w, conf.h), conf.mines) 这里的变量conf,定义在config.py中,由以下语句引入的: from config import * 这里的conf.w和conf.h是什么呢?由此,转入config.py,一探究竟. #module: config import json DEFA

组合游戏学习笔记 [补档]

基础 满足以下条件的游戏是组合游戏: 有两人参与游戏, 轮流作出决策 无法作出决策的人失败, 然后游戏结束 游戏总能在有限次决策后结束 游戏的同一个状态不会多次到达 任意一个参与者在某一确定状态下可以作出的决策集合只与当前状态有关 定义先手必败状态为必败态, 先手必胜状态为必胜态, 则我们有 无法进行任何移动的状态是必败态. 可以移动到必败态的状态的是必胜态. 所有移动都只能得到必胜态的状态是必败态. 考虑如何判断一个游戏中, 先手是否必胜: 根据定义, 由于游戏中任意状态都不可能重复出现, 因

关于扫雷游戏学习笔记(二)

用户控件与窗体之间的传值:要实现当MineField中扫雷成功或失败,笑脸图标做出相应的改变. 1.通过构造函数传参 //MineField类中 public PictureBox smile; public MineField(PictureBox pb) { smile=pb; InitializeComponent(); } //在Form.Designer中的InitializeComponent里加入 this.mineField1 = new Mine.MineField(pictu

Unity3D学习笔记之七创建自己的游戏场景

到现在为止我们已经拥有了比较完备的Prefab,已经可以创建宏大的游戏场景,并以第一人称视角在场景中漫游了.这里给大家做个小的示范,建一个小场景大家在创建场景的时候需要自由发挥,做个尽量大的场景出来. 这一系列教程以及素材均参考自人人素材翻译组出品的翻译教程<Unity游戏引擎的基础入门视频教程>,下载链接附在第二篇学习笔记中. 我们以最初的添加了First Person Controller的PFB_Straight为整个场景的中心点来展开.我们先从Project中Prefabs文件夹拖出来

【Unity 3D】学习笔记三十五:游戏实例——摄像机切换镜头

摄像机切换镜头 在游戏中常常会切换摄像机来观察某一个游戏对象,能够说.在3D游戏开发中,摄像头的切换是不可或缺的. 这次我们学习总结下摄像机怎么切换镜头. 代码: private var Camera0: GameObject; private var Camera1: GameObject; private var Camera2: GameObject; private var Camera: GameObject; function Start() { //获取摄像机对象 Camera =