【日常篇】007_利用pygame实现经典扫雷

  曾经在大二和大三的时候分别尝试用MATLAB和VB.NET去实现扫雷,但分别都因为没搞清扫雷的逻辑和不清楚VB.NET的特性(主要是没有区分开图形界面与游戏逻辑)而放弃了

  直到2021年的年底,在写完《飞机大战》后准备找一个新的选题时,才想起了这个一直没写出来的游戏,于是便很快就将本次的主题定了下来。在经过两三年的“锻炼”后,写出扫雷应该也不是一件难事了

基本思路

地图场景

  扫雷这个游戏是在一个由m × \times ×n个grid的区域内进行的,因此能够直接想到的自然是使用二维数组来表示。每一个grid所包含的信息无非是以下几种:

  1、是否被翻开

  2、是否为地雷

  3、周围一圈8个grid中的雷数,介于0-8之间

  由此设计两个二维数组,其中一个用于存储是否为地雷或周围地雷的数量,另外一个用于存储grid是否被翻开。前者使用字符’*’、‘0’——'8’表示,后者mask则采用bool类型,当值为真时表示未翻开:

def generateField(self):
    """
        初始化二维数组
    """
    self.mineField = np.zeros(shape=(self.height, self.width), dtype=str)

def generateMine(self):
    """
        生成地雷
    """
    indexList = list(np.arange(self.width * self.height, dtype=int))
    mineList = []
    for mineID in range(self.mineNumber):
        thisMineIdx = indexList[int(np.random.rand() * len(indexList))]
        mineList.append(thisMineIdx)
        indexList.remove(thisMineIdx)
    for eachMinePosIdx in mineList:
        i = eachMinePosIdx // self.width
        j = eachMinePosIdx % self.width
        self.mineField[i][j] = '*'

def generateMarkNumber(self):
    """
        生成数字标记
    """
    for i in range(self.height):
        for j in range(self.width):
            if(self.mineField[i][j] == '*'):
                continue
            number = 0
            for di in [-1, 0, 1]:
                for dj in [-1, 0, 1]:
                    if((di == 0 and dj == 0) or (not (0 <= i + di < self.height)) or (not (0 <= j + dj < self.width))):
                        continue
                    if(self.mineField[i+di][j+dj] == '*'):
                        number += 1
            self.mineField[i][j] = str(number)

def generateMaskField(self):
    """
        生成Mask数组
    """
    self.maskField = np.ones(shape=(self.height, self.width), dtype=bool)

右键:标记flag

  因为雷的数量是相对较少的,所以对应的flag数量也是一样,如果使用二维数组来表示,这个数组将会是非常稀疏的,这样就占用了很多不必要占用的空间。因此,flag的存储方式使用简单的列表形式,每一个元素都表示这个flag所存放的相应坐标位置。与此同时,还要记录当前flag的数量以及flag的数量上限,如果数量已满,则不能再继续进行标记flag的操作:

def initFlagInfo(self):
    """
        初始化Flag信息,包括容器和flag数
    """
    self.flagContainer = []
    self.flagLimit = self.mineNumber
    self.currentFlagNumber = 0

def flagGrid(self, i, j):
    """
        右键点击i行j列的grid时执行,综合判断flag操作

        Parameters
        ----------
        i : int
            i行
        j : int
            j列 
    """
    if(not self.maskField[i][j]): # 如果已经被点开了,自然不能插旗
        return
    self.updateFlag(i, j)

def updateFlag(self, i, j):
    """
        对i行j列进行Flag操作

        Parameters
        ----------
        i : int
            i行
        j : int
            j列 
    """
    if([i, j] in self.flagContainer):
        self.flagContainer.remove([i, j])
        self.currentFlagNumber -= 1
    else:
        if(not self.isFlagReachMax()):
            self.flagContainer.append([i, j])
            self.currentFlagNumber += 1

左键:翻开一个grid

  使用鼠标左键点击时,相应的grid会被翻开。随后,周围的所有无雷连续区域也会被自动翻开,这个区域是以数字介于1-9之间的grid为边界的:

在这里插入图片描述

  点击这个区域中的任何一个grid,都应该翻开这样的同一片连续的区域

  对于这类翻开一片连续区域的情形而言,使用队列遍历的算法再合适不过了。首先将点击的那个grid放进队列,然后取出并将mask标记为False,把周围满足条件的grid全部加入到队列中,再开始从队列中取出第一个元素,对其周围的grid进行判断……以此类推,直到最后队列为空时,结束遍历。这样一来,就可以把整片区域都遍历到了:

def flipGrid(self, i, j):
    """
        翻开位于i行j列处的方块,如果踩雷则游戏结束,否则更新相应的状态后继续游戏

        Parameters
        ----------
        i : int
            i行
        j : int
            j列
    """
    if(not self.isMasked(i, j)): # 已经被点过的话,当然就不点了
        return
    if([i, j] in self.flagContainer): # 插了flag的不能点
        return
    if(self.mineField[i][j] == '*'): # 踩雷,di了!
        self.gameover()
    self.updateMask(i, j)

def updateMask(self, i, j):
    """
        根据点击的位置,更新Mask的情况

        Parameters
        ----------
        i : int
            i行
        j : int
            j列
    """
    # 使用队列的方法,判断该解除哪一块的mask
    queue = [[i, j]]
    while(queue):
        p = queue[0]
        queue.remove(queue[0])
        for di in [-1, 0, 1]:
            for dj in [-1, 0, 1]:
                i = p[0]
                j = p[1]
                if((di == 0 and dj == 0) or (not (0 <= i + di < self.height)) or (not (0 <= j + dj < self.width))):
                    continue
                if(di and dj): # 这里之前是个bug,在debug的时候很草率地换了个pass上去,就没有再修改了,所以看起来很冗余
                    pass
                if(self.maskField[i+di, j+dj]):
                    if(self.mineField[i, j] not in ['0', '*'] and self.mineField[i+di, j+dj] == '0'):
                        queue.append([i+di, j+dj])
                    if(self.mineField[i, j] == '0' and self.mineField[i+di, j+dj] not in ['*']):
                        queue.append([i+di, j+dj])
        # 如果上面本来有flag,则拔掉
        if([i, j] in self.flagContainer):
            self.flagGrid(i, j)
        self.maskField[i, j] = False

左右键同时点击:对周围一圈的区域进行进一步的探索

  对着已经翻开的区域,同时点击左右键时,就会对周围一圈的区域进行检查。如果发现周围一圈的所有地雷都被flag标记的话,则会将剩下的几个没有被标记的地方全部翻开(效果和点击鼠标左键一致);如果没有标记周围一圈的所有地雷,则不会进行任何操作,并且如果此时flag已用光,则会直接触发地雷的爆炸:

def swampGrid(self, i, j):
    """
        对着i, j的四周一圈扫雷

        Parameters
        ----------
        i : int
            i行
        j : int
            j列 
    """
    # 如果扫的是mask区,则直接跳过
    if(self.maskField[i][j]):
        return
    # 如果检查到flag下面没有雷(或有雷却没被flag),则不翻开
    for di in [-1, 0, 1]:
        for dj in [-1, 0, 1]:
            if((di == 0 and dj == 0) or (not (0 <= i + di < self.height)) or (not (0 <= j + dj < self.width))):
                continue
            if([i+di, j+dj] in self.flagContainer and self.mineField[i+di][j+dj] != "*"):
                return
            if([i+di, j+dj] not in self.flagContainer and self.mineField[i+di][j+dj] == "*"):
                # 如果有雷没有flag,并且已经flag用完了,则直接点击爆炸
                if(self.isFlagReachMax()):
                    self.flipGrid(i+di, j+dj)
                return
    # 扫雷成功后,翻开周围一圈的地盘
    for di in [-1, 0, 1]:
        for dj in [-1, 0, 1]:
            if((di == 0 and dj == 0) or (not (0 <= i + di < self.height)) or (not (0 <= j + dj < self.width))):
                continue
            if(self.maskField[i+di][j+dj] and [i+di, j+dj] not in self.flagContainer):
                self.flipGrid(i+di, j+dj)

游戏场景与图形界面相连接

  在完成了游戏逻辑的编写后,就要考虑如何使用图形界面将这个游戏场景展现给用户看了。游戏中发生的各种事件,全都由游戏逻辑这个程序来执行,而图形界面的作用就相当于游戏与用户之间的互动窗口:用户通过点击鼠标和敲击键盘的方式将信号通过图形界面传递给游戏逻辑,而游戏逻辑则通过图形化的方式将当前状况展现给用户看

  在这里,用户使用不同的方式点击鼠标以及按下相应的键盘时,就会对游戏逻辑程序发出不同值的信号,以执行不同的操作:

def clickOperation(self, event, mousePos):
    """
        点击鼠标时触发事件
    """
    # 在点击grid的时候触发的事件
    j = mousePos[0] // self.gridSize
    i = mousePos[1] // self.gridSize
    # 游戏结束时不能继续操作
    if(not self.stage.isGameover()):
        # 左右同时:swamp
        if(pygame.mouse.get_pressed()[0] and pygame.mouse.get_pressed()[2]):
            self.stage.action(3, i, j)
        # 左击:翻grid
        elif(event.button == 1):
            self.stage.action(1, i, j)
        # 右击:flag
        elif(event.button == 3):
            self.stage.action(2, i, j)
        # 点完后,判断是否游戏胜利
        if(self.stage.isGamewin()):
            self.stage.gamewin()

def gameStage(self):
    """
        游戏图形界面
    """
    while True:
        events = pygame.event.get()
        for event in events:
            if(event.type == pygame.QUIT):
                exit()
            if(event.type == pygame.MOUSEBUTTONDOWN):
                self.clickOperation(event, pygame.mouse.get_pos())
            if(event.type == pygame.KEYDOWN):
                # 重开快捷键
                if(pygame.key.get_pressed()[pygame.K_r]):
                    self.stage = Stage()
        self.gameStageDraw()
        pygame.display.update()

  游戏逻辑程序在接收到信号后,就会根据信号的值来执行不同的动作:

def action(self, signal, i, j):
    """
        采取动作,与图像界面直接连接
        信号1:翻雷
        信号2:flag
        信号3:扫雷

        Parameters
        ----------
        signal : int
            信号
        i : int
            第i行
        j : int
            第j列
    """
    if(signal == 1):
        self.flipGrid(i, j)
    elif(signal == 2):
        self.flagGrid(i, j)
    elif(signal == 3):
        self.swampGrid(i, j)

  以上便是核心算法和思路的简述,其他还有很多细节,将会在文末的源代码中全部贴出,可以直接复制或使用git clone进行下载

效果展示

  注:此时还有一个bug没有移除,所以看起来会有那么一点不对劲……

在这里插入图片描述

mine

源代码展示

  代码下载链接:https://github.com/VtaSkywalker/20211223_001_mines

stage.py

import numpy as np
import sys

class StageConfig:
    height = 10
    width = 10
    mineNumber = 10
    doFirstFlip = False

class Stage:
    """
        场景类

        Attributes
        ----------
        height : int
            雷区的高度
        width : int
            雷区的宽度
        mineNumber : int
            雷的数量
        mineField : char[][]
            雷区
        maskField : bool[][]
            雷区的Mask,True为挡住
        flagContainer : int[2][]
            雷区的flag,存储了哪些地方插旗的信息
        flagLimit : int
            插旗的上限
        currentFlagNumber : int
            当前插旗的数量
    """
    def __init__(self):
        self.height = StageConfig.height
        self.width = StageConfig.width
        self.mineNumber = StageConfig.mineNumber
        self.initStage()

    def initStage(self):
        """
            初始化游戏
        """
        if(not self.isMineNumberLegal()):
            raise Exception("雷的数量不合理!")
        self.generateField()
        self.generateMine()
        self.generateMarkNumber()
        self.generateMaskField()
        self.initFlagInfo()
        if(StageConfig.doFirstFlip):
            safeIdx = np.where(self.mineField != '*')
            randomSelectIdx = int(np.random.rand() * len(safeIdx[0]))
            self.flipGrid(safeIdx[0][randomSelectIdx], safeIdx[1][randomSelectIdx])
        self.gameoverState = False

    def isMineNumberLegal(self):
        """
            雷的数量是否合理

            Returns
            -------
            合理 / 不合理 : True / False
        """
        if(self.width * self.height <= self.mineNumber):
            return False
        if(self.mineNumber <= 0):
            return False
        return True

    def generateField(self):
        """
            初始化二维数组
        """
        self.mineField = np.zeros(shape=(self.height, self.width), dtype=str)

    def generateMine(self):
        """
            生成地雷
        """
        indexList = list(np.arange(self.width * self.height, dtype=int))
        mineList = []
        for mineID in range(self.mineNumber):
            thisMineIdx = indexList[int(np.random.rand() * len(indexList))]
            mineList.append(thisMineIdx)
            indexList.remove(thisMineIdx)
        for eachMinePosIdx in mineList:
            i = eachMinePosIdx // self.width
            j = eachMinePosIdx % self.width
            self.mineField[i][j] = '*'

    def generateMarkNumber(self):
        """
            生成数字标记
        """
        for i in range(self.height):
            for j in range(self.width):
                if(self.mineField[i][j] == '*'):
                    continue
                number = 0
                for di in [-1, 0, 1]:
                    for dj in [-1, 0, 1]:
                        if((di == 0 and dj == 0) or (not (0 <= i + di < self.height)) or (not (0 <= j + dj < self.width))):
                            continue
                        if(self.mineField[i+di][j+dj] == '*'):
                            number += 1
                self.mineField[i][j] = str(number)

    def generateMaskField(self):
        """
            生成Mask数组
        """
        self.maskField = np.ones(shape=(self.height, self.width), dtype=bool)
    
    def initFlagInfo(self):
        """
            初始化Flag信息,包括容器和flag数
        """
        self.flagContainer = []
        self.flagLimit = self.mineNumber
        self.currentFlagNumber = 0

    def flipGrid(self, i, j):
        """
            翻开位于i行j列处的方块,如果踩雷则游戏结束,否则更新相应的状态后继续游戏

            Parameters
            ----------
            i : int
                i行
            j : int
                j列
        """
        if(not self.isMasked(i, j)): # 已经被点过的话,当然就不点了
            return
        if([i, j] in self.flagContainer): # 插了flag的不能点
            return
        if(self.mineField[i][j] == '*'): # 踩雷,di了!
            self.gameover()
        self.updateMask(i, j)

    def isGameover(self):
        if(self.gameoverState):
            return True
        return False

    def gameover(self):
        """
            游戏结束时执行
        """
        self.gameoverState = True
        print("gameover")

    def updateMask(self, i, j):
        """
            根据点击的位置,更新Mask的情况

            Parameters
            ----------
            i : int
                i行
            j : int
                j列
        """
        # 使用队列的方法,判断该解除哪一块的mask
        queue = [[i, j]]
        while(queue):
            p = queue[0]
            queue.remove(queue[0])
            for di in [-1, 0, 1]:
                for dj in [-1, 0, 1]:
                    i = p[0]
                    j = p[1]
                    if((di == 0 and dj == 0) or (not (0 <= i + di < self.height)) or (not (0 <= j + dj < self.width))):
                        continue
                    if(di and dj):
                        pass
                    if(self.maskField[i+di, j+dj]):
                        if(self.mineField[i, j] not in ['0', '*'] and self.mineField[i+di, j+dj] == '0'):
                            queue.append([i+di, j+dj])
                        if(self.mineField[i, j] == '0' and self.mineField[i+di, j+dj] not in ['*']):
                            queue.append([i+di, j+dj])
            # 如果上面本来有flag,则拔掉
            if([i, j] in self.flagContainer):
                self.flagGrid(i, j)
            self.maskField[i, j] = False

    def isMasked(self, i, j):
        """
            检查i行j列是否被Mask

            Parameters
            ----------
            i : int
                i行
            j : int
                j列            
        """
        if(self.maskField[i][j]):
            return True
        return False

    def flagGrid(self, i, j):
        """
            右键点击i行j列的grid时执行,综合判断flag操作

            Parameters
            ----------
            i : int
                i行
            j : int
                j列 
        """
        if(not self.maskField[i][j]): # 如果已经被点开了,自然不能插旗
            return
        self.updateFlag(i, j)

    def isFlagReachMax(self):
        """
            检查Flag数是否达到上限
        """
        if(self.currentFlagNumber >= self.flagLimit):
            return True
        return False

    def updateFlag(self, i, j):
        """
            对i行j列进行Flag操作

            Parameters
            ----------
            i : int
                i行
            j : int
                j列 
        """
        if([i, j] in self.flagContainer):
            self.flagContainer.remove([i, j])
            self.currentFlagNumber -= 1
        else:
            if(not self.isFlagReachMax()):
                self.flagContainer.append([i, j])
                self.currentFlagNumber += 1

    def cmdShow(self):
        """
            在cmd中打印当前状态
        """
        print("X\t", end="")
        for j in range(self.width):
            print("\033[33m%s\033[0m\t" % j, end="")
        print("")
        for i in range(self.height):
            print("\033[33m%s\033[0m\t" % i, end="")
            for j in range(self.width):
                if(self.maskField[i][j]):
                    if([i, j] in self.flagContainer):
                        print("F\t", end="")
                    else:
                        print(".\t", end="")
                else:
                    print("%s\t" % self.mineField[i][j], end="")
            print("")

    def swampGrid(self, i, j):
        """
            对着i, j的四周一圈扫雷

            Parameters
            ----------
            i : int
                i行
            j : int
                j列 
        """
        # 如果扫的是mask区,则直接跳过
        if(self.maskField[i][j]):
            return
        # 如果检查到flag下面没有雷(或有雷却没被flag),则不翻开
        for di in [-1, 0, 1]:
            for dj in [-1, 0, 1]:
                if((di == 0 and dj == 0) or (not (0 <= i + di < self.height)) or (not (0 <= j + dj < self.width))):
                    continue
                if([i+di, j+dj] in self.flagContainer and self.mineField[i+di][j+dj] != "*"):
                    return
                if([i+di, j+dj] not in self.flagContainer and self.mineField[i+di][j+dj] == "*"):
                    # 如果有雷没有flag,并且已经flag用完了,则直接点击爆炸
                    if(self.isFlagReachMax()):
                        self.flipGrid(i+di, j+dj)
                    return
        # 扫雷成功后,翻开周围一圈的地盘
        for di in [-1, 0, 1]:
            for dj in [-1, 0, 1]:
                if((di == 0 and dj == 0) or (not (0 <= i + di < self.height)) or (not (0 <= j + dj < self.width))):
                    continue
                if(self.maskField[i+di][j+dj] and [i+di, j+dj] not in self.flagContainer):
                    self.flipGrid(i+di, j+dj)

    def isGamewin(self):
        """
            判断是否游戏胜利
        """
        if(sum(sum(self.maskField)) == self.mineNumber):
            return True
        return False

    def gamewin(self):
        """
            游戏胜利
        """
        for i in range(self.height):
            for j in range(self.width):
                if(self.maskField[i][j] and [i, j] not in self.flagContainer):
                    self.flagContainer.append([i, j])
        print("game win!")

    def action(self, signal, i, j):
        """
            采取动作,与图像界面直接连接
            信号1:翻雷
            信号2:flag
            信号3:扫雷

            Parameters
            ----------
            signal : int
                信号
            i : int
                第i行
            j : int
                第j列
        """
        if(signal == 1):
            self.flipGrid(i, j)
        elif(signal == 2):
            self.flagGrid(i, j)
        elif(signal == 3):
            self.swampGrid(i, j)

display.py

from pygame import event
from stage import *
import pygame_menu
import pygame

class Display:
    """
        显示界面
    """

    def __init__(self):
        self.stage = Stage()

    def clickOperation(self, event, mousePos):
        """
            点击鼠标时触发事件
        """
        # 在点击grid的时候触发的事件
        j = mousePos[0] // self.gridSize
        i = mousePos[1] // self.gridSize
        # 游戏结束时不能继续操作
        if(not self.stage.isGameover()):
            # 左右同时:swamp
            if(pygame.mouse.get_pressed()[0] and pygame.mouse.get_pressed()[2]):
                self.stage.action(3, i, j)
            # 左击:翻grid
            elif(event.button == 1):
                self.stage.action(1, i, j)
            # 右击:flag
            elif(event.button == 3):
                self.stage.action(2, i, j)
            # 点完后,判断是否游戏胜利
            if(self.stage.isGamewin()):
                self.stage.gamewin()

    def gameStage(self):
        """
            游戏图形界面
        """
        while True:
            events = pygame.event.get()
            for event in events:
                if(event.type == pygame.QUIT):
                    exit()
                if(event.type == pygame.MOUSEBUTTONDOWN):
                    self.clickOperation(event, pygame.mouse.get_pos())
                if(event.type == pygame.KEYDOWN):
                    # 重开快捷键
                    if(pygame.key.get_pressed()[pygame.K_r]):
                        self.stage = Stage()
            self.gameStageDraw()
            pygame.display.update()

    def gameStageDraw(self):
        """
            对gameStage进行绘制
        """
        # 初始化-黑屏
        self.screen.fill((0,0,0))

        maskedGrid_img = pygame.image.load("./img/maskedGrid.png")
        maskedGrid_rect = maskedGrid_img.get_rect()
        unmaskedGrid_img = pygame.image.load("./img/unmaskedGrid.png")
        unmaskedGrid_rect = unmaskedGrid_img.get_rect()
        flag_img = pygame.image.load("./img/flag.png")
        flag_rect = flag_img.get_rect()
        mine_img = pygame.image.load("./img/mine.png")
        mine_rect = mine_img.get_rect()
        for i in range(StageConfig.height):
            for j in range(StageConfig.width):
                centerx = self.gridSize * (0.5 + j)
                centery = self.gridSize * (0.5 + i)
                # mask / unmask
                if(self.stage.maskField[i][j]):
                    maskedGrid_rect.centerx = centerx
                    maskedGrid_rect.centery = centery
                    self.screen.blit(maskedGrid_img, maskedGrid_rect)
                else:
                    unmaskedGrid_rect.centerx = centerx
                    unmaskedGrid_rect.centery = centery
                    self.screen.blit(unmaskedGrid_img, unmaskedGrid_rect)
                    # 1-9 / mine
                    if('1' <= self.stage.mineField[i][j] <= '9'):
                        number_img = pygame.image.load("./img/%s.png" % self.stage.mineField[i][j])
                        number_rect = number_img.get_rect()
                        number_img = pygame.transform.scale(number_img, (int(number_rect.size[0] * 0.6), int(number_rect.size[1] * 0.6)))
                        number_rect = number_img.get_rect()
                        number_rect.centerx = centerx
                        number_rect.centery = centery
                        self.screen.blit(number_img, number_rect)
                # game over时显示所有地雷
                if(self.stage.isGameover() and self.stage.mineField[i][j] == '*'):
                    mine_rect.centerx = centerx
                    mine_rect.centery = centery
                    self.screen.blit(mine_img, mine_rect)
        # flag显示
        for eachFlagPoint in self.stage.flagContainer:
            flag_rect.centerx = self.gridSize * (0.5 + eachFlagPoint[1])
            flag_rect.centery = self.gridSize * (0.5 + eachFlagPoint[0])
            self.screen.blit(flag_img, flag_rect)
        # 剩余flag数显示
        flag_rect.centerx = 25
        flag_rect.centery = self.gridSize * self.stage.height + 25
        self.screen.blit(flag_img, flag_rect)
        font = pygame.font.SysFont("arial", 35)
        img = font.render('x %s' % str(self.stage.flagLimit - len(self.stage.flagContainer)), True, (255, 255, 255))
        rect = img.get_rect()
        rect.left = 50
        rect.centery = self.gridSize * self.stage.height + 25
        self.screen.blit(img, rect)
        # 鼠标所在处的那个grid高光
        mousePos = pygame.mouse.get_pos()
        j = mousePos[0] // self.gridSize
        i = mousePos[1] // self.gridSize
        if(0 <= j < self.stage.width and 0 <= i < self.stage.height):
            highLightGrid = pygame.Surface((50, 50))
            highLightGrid.set_alpha(128)
            highLightGrid.fill((255, 255, 255))
            self.screen.blit(highLightGrid, (j * self.gridSize, i * self.gridSize))
        # gameover / gamewin文字显示
        font = pygame.font.SysFont("arial", 35)
        if(self.stage.isGameover()):
            img = font.render('Game Over', True, (255, 0, 0))
            rect = img.get_rect()
            rect.centerx = self.width / 2
            rect.centery = self.gridSize * self.stage.height + 25
            self.screen.blit(img, rect)
        elif(self.stage.isGamewin()):
            img = font.render('Game Win', True, (0, 255, 0))
            rect = img.get_rect()
            rect.centerx = self.width / 2
            rect.centery = self.gridSize * self.stage.height + 25
            self.screen.blit(img, rect)

    def initForm(self):
        """
            初始化图形界面
        """
        pygame.init()
        self.gridSize = 50
        self.width = StageConfig.width * self.gridSize
        self.height = StageConfig.height * self.gridSize + 50
        self.screen = pygame.display.set_mode((self.width, self.height))

    def mainLoop(self):
        """
            主循环
        """
        self.initForm()
        self.gameStage()

start.py

from display import *

d = Display()
d.mainLoop()

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值