首页 > 代码库 > PyQt挖地雷游戏学习笔记(7)

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、逻辑盘空白区域的标注

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

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