回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

[mind+ python] pygame实现扫雷游戏 简单

头像 BL_hongzhong 2021.09.05 823 1

    如今的游戏让人眼花缭乱,还有谁记得当时的扫雷(Minesweeper)游戏?它虽然页面简陋,玩法单一,但却和数独、幻方一样,是一种益智类游戏,因为当时的windows xp只有扫雷游戏,我就一直研究它,琢磨它。久而久之,我越来越喜欢这款游戏,所以很久之前,我就想要用自己的技术,创造属于我自己的扫雷。今天,有了mind+平台举办的比赛,点燃了我的好胜心。而我也终于能用我自己的python技术,重现这款1992年发布的扫雷,找回经典的味道。

    我把我的扫雷游戏命名为Bigsweeper(笑)

    

project-image

使用软件:

    mind+

    sublime Text3

    Qdesigner(pyqt5包含)

使用语言:

    python

使用库:

    pygame

    pyqt5

project-image

代码结构如上图

GAME_player.py 负责游戏的逻辑

GAME_game_ui.py 负责游戏的ui

GAME_menu_ui.py 负责游戏菜单的ui

GAME_main.py 则是把上述所有文件整合起来,并判断、回应用户的操作

 

当用户操作可用并被GAME_main.py采集到时,它先调用GAME_player.py里的接口,即“在逻辑内反应用户操作”,当收到GAME_player.py的回应后,调用GAME_game.ui.py,把动作结果返回给用户

而GAME_menu.py则是显示、设置游戏信息的,同样被GAME_mian.py随时调用

菜单是我拖拽开发的,GAME_menu.py里的代码是Qdesigner自动生成的

 

下面是程序截图:

project-image
project-image

下面是我的代码

从上到下依次是:

GAME_main.py

GAME_player.py

GAME_menu_ui.py

GAME_game_ui.py

代码
# -*- coding: utf-8 -*-

# 2021.8.16开始编程
# 扫雷游戏 主程序
# GAME_main.py

# 作者:张绍翊
# 机构:麦咭编程特训营广西来宾校区

import sys
import pygame
from pygame.locals import *
from PyQt5.QtWidgets import QMessageBox

""" 自建模块 """
from GAME_menu_ui import *
import GAME_player as player
import GAME_game_ui as ui

pygame.init()

MINEFIELD_LENGTH = 8 #雷区的长
MINEFIELD_WIDTH = 8 #雷区的宽
""" 因为pygame窗口长、宽最小值为128像素,则雷区长、宽不可低于8个格子 """

win_number = int() #胜利局数
game_number = int() #总局数

game = False #游戏打开状态

FPS = 30 #游戏帧率

def game_main():

    global screen
    global this_game

    global game_number
    global win_number

    global game

    player.player_init(MINEFIELD_LENGTH,MINEFIELD_WIDTH,mine_number) #初始化游戏逻辑自建库
    ui.window_init(MINEFIELD_LENGTH,MINEFIELD_WIDTH) #初始化游戏页面自建库

    game_number += 1
    myWin.game_run.setText("游戏正在运行中……")

    this_game = player.Game() #新游戏类(全局变量)
    screen = ui.new_window() #新页面(全局变量)

    myWin.mine_density.setValue(this_game.mine_number/size*100) #随机雷模式的雷密度的显示
    ui.set_plaid(screen,ui.NONE_STYLE) #给页面平铺未知格

    myWin.action_show.append(\
        "—"*11 + "\n 新 游 戏 \n 雷数:%d\n方块数:%d\n"\
        %(this_game.mine_number,this_game.size)\
    )

    start_time = pygame.time.get_ticks()
    fpsClock = pygame.time.Clock()

    while True: #消息循环

        myWin.mark_number.display(this_game.marked_number)
        myWin.open_number.display(this_game.opened_number)
        myWin.size_mark_number.display(this_game.mine_number - this_game.marked_number)

        end_time = pygame.time.get_ticks()
        myWin.time_show.display(format((end_time - start_time)/1000,'.2f'))
        """ 这里format返回的是str,按照理解,应该要转化类型。但是这个控件可以正常显示,所以没有改 """
        
        for event in pygame.event.get():

            if event.type == pygame.QUIT:
                game = False
                game_number -= 1

                myWin.game_run.setText("游戏被退出")

                myWin.action_show.append(\
                    "—"*11 + " 强 制 退 出 \n 不计入总数中\n"\
                )

                pygame.quit()
                return

            elif event.type == pygame.MOUSEBUTTONDOWN: #点击
                click_index = pygame.mouse.get_pos()
                if ui.judging_legitimate_click(click_index): #点击合法性判断

                    this_game.action_number += 1

                    if event.button == 1: #响应this_plaid.mark左键单击(打开方块)
                        open_it(ui.get_plaid_index(click_index))
                    elif event.button == 3: #响应右键单击(标记方块)
                        mark_it(ui.get_plaid_index(click_index))

                if this_game.winlost == player.GAME_FAIL:
                    game = False #只需要在每一次点击结束后判断输赢即可

                    myWin.action_show.append(\
                        "—"*11 + " 游 戏 失 败 \n用时 %f 秒\n"\
                        %((end_time - start_time)/1000)\
                    )

                    myWin.game_run.setText("游戏失败")

                    pygame.quit()
                    return False

                elif this_game.winlost == player.GAME_WIN:
                    game = False

                    myWin.action_show.append(\
                        "—"*11 + " 游 戏 胜 利 \n用时 %f 秒\n"\
                        %((end_time - start_time)/1000)\
                    )

                    myWin.game_run.setText("游戏胜利")

                    win_number += 1
                    pygame.quit()
                    return True
                    
        fpsClock.tick(FPS) #刷新屏幕
        pygame.display.update()


def open_it(plaid_index):
    """
    鼠标左键响应(打开格子)
    逻辑内打开方块、并进行页面刷新
    """
    open_state = this_game.open_plaid(plaid_index) #逻辑内执行打开方块动作

    if this_game.opened_number < 3:#坏运气拯救计划:当前2次踩到雷或是踩不到0(不能连锁)后,重新开始游戏
        if this_game.get_plaid_show_number(plaid_index) != 0:

            myWin.action_show.append(\
                "—"*11 + " 操作%d 开局失利 \n  重新开始游戏\n"\
                %(this_game.action_number)\
            )

            try:
                this_game.anew_game() 
                open_it(plaid_index) #在原来的方块上重新点击

            except RecursionError: #若超出递归深度,则雷数减去2
                this_game.mine_number -= 2

    if open_state == player.OPEN_NOW:

        myWin.action_show.append(\
            "—"*11 + " 操作%d 点击 \n 方块坐标 (%d,%d)\n"\
            %(this_game.action_number,plaid_index[0],plaid_index[1])\
        )

        ui.new_plaid_style(screen,plaid_index,ui.OPEN_STYLE) #更改方块样式
        ui.new_show_number(screen,plaid_index,this_game.get_plaid_show_number(plaid_index)) #显示方块数字

        if this_game.get_plaid_show_number(plaid_index) == 0:#连锁打开方块
            """ 若点击的方块为0,则把周围不是雷的方块都打开 """
            for index in player.get_around_index(plaid_index):
                if this_game.get_plaid_show_number(index) != 9:
                    open_it(index) #递归打开周围方块


def mark_it(plaid_index):
    """
    鼠标右键响应(标记/取消标记 格子)
    逻辑内标记格子、并进行页面刷新
    """
    mark_state = this_game.mark_plaid(plaid_index)#逻辑内执行标记方块动作

    if mark_state == player.MARK_NOW: #标记方块

        myWin.action_show.append(\
            "—"*11 + " 操作%d 标记 \n 方块坐标 (%d,%d)\n"\
            %(this_game.action_number,plaid_index[0],plaid_index[1])\
        )

        ui.new_plaid_style(screen,plaid_index,ui.MARK_STYLE)#更改方块样式,下同

    elif mark_state == player.MARK_DONT: #取消标记

        myWin.action_show.append(\
            "—"*11 + " 操作%d 取消标记 \n 方块坐标 (%d,%d)\n"\
            %(this_game.action_number,plaid_index[0],plaid_index[1])\
        )

        ui.new_plaid_style(screen,plaid_index,ui.NONE_STYLE)


def menu_length():
    """
    雷区长度刷新展示
    """
    global MINEFIELD_LENGTH

    MINEFIELD_LENGTH = myWin.game_window_l.value()
    myWin._l_number.display(MINEFIELD_LENGTH) #刷新雷区长
    menu_size()


def menu_width():
    """
    雷区宽度刷新展示
    """
    global MINEFIELD_WIDTH

    MINEFIELD_WIDTH = myWin.game_window_w.value()
    myWin._w_number.display(MINEFIELD_LENGTH) #刷新雷区长
    menu_size()


def menu_size():
    """
    雷区面积刷新展示
    """
    global size
    size = MINEFIELD_LENGTH * MINEFIELD_WIDTH
    myWin.size.display(size) #刷新雷区面积

    myWin.input_mine_number.setMaximum(size//3)


def menu_mine_number():
    """
    雷数量及地雷密度刷新展示
    """
    global mine_number

    mine_number = 0 if myWin.use_random_mine_number.isChecked() else myWin.input_mine_number.value()
    myWin.mine_number.display("PA.." if mine_number == 0 else mine_number)#刷新雷数量,随机雷数量显示"PA.."表示"RAND"

    myWin.mine_density.setValue(mine_number/size*100) #显示雷密度


def game_begin():
    """
    游戏的开始与局数的统计
    按钮game_begin的槽函数
    """
    global game

    if game == True:

        QMessageBox.critical(\
            myWin,u"出现错误",\
            u"你已经打开了游戏,不可以重复打开!",\
            QMessageBox.Yes|QMessageBox.No,\
            QMessageBox.Yes\
        )

        return

    else:
        game = True
        try:
            game_main()
            myWin.game_number.display(game_number)
            myWin.win_number.display(win_number)
            myWin.winbai.setValue(win_number/game_number*100 if game_number != 0 else 0)

        except pygame.error: #pygame报错可以忽视
            pygame.quit()
            return

        except RecursionError: #递归报错说明玩家选项实在过于天使(天使:形容词)

            QMessageBox.critical(\
                myWin,u"出现错误",\
                u"游戏遇到一些问题,请尝试把雷密度调高或把雷区面积调小!",\
                QMessageBox.Yes|QMessageBox.No,\
                QMessageBox.Yes\
            )

            pygame.quit()
            return

if __name__ == '__main__':
    app = QApplication(sys.argv)
    myWin = MyWindow()
    myWin.winbai.setValue(0)

    myWin.closeEvent = lambda event:pygame.quit() or event.accept() #重写窗口closeEvent方法
    """
    当关闭窗口时,pyqt5会触发一个QCloseEvent的事件,此事件发生后触发方法closeEvent()
    这里自定义一个closeEvent(),加入pygame.quit(),让玩家点击菜单关闭键时,让游戏窗口也成功退出
    """

    sys.setrecursionlimit(3500) #重新设置递归深度为3500层(默认1000层左右)
    """
    连锁打开是用递归实现的,设置深度是为了面对某些天使选项(天使:形容词)
    """

    menu_width()
    menu_length()
    menu_size()
    menu_mine_number()

    myWin.show()

    
    """ 槽函数绑定 """
    myWin.input_mine_number.valueChanged.connect(menu_mine_number)
    myWin.use_random_mine_number.toggled.connect(menu_mine_number)

    myWin.game_window_l.valueChanged.connect(menu_length)
    myWin.game_window_w.valueChanged.connect(menu_width)

    myWin.game_begin.clicked.connect(game_begin)
    
    sys.exit(app.exec_())
代码
# -*- coding: utf-8 -*-

# GAME_player.py
# 扫雷游戏 游戏逻辑接口
# 2021.8.18 开始编程

# 作者:张绍翊
# 机构:麦咭编程特训营广西来宾校区


from random import randint

""" 接口信号 """
GAME_FAIL = -933 #游戏失败值
GAME_WIN = 1412 #游戏成功值

OPEN_NOW = 5132 #打开值
MARK_NOW = 946 #标记值
MARK_DONT = -124 #取消标记值


def player_init(Mlength,Mwidth,Mmine = 0):
    """
    游戏逻辑模块初始化
    传入雷区长宽、设置为全局变量
    """
    global length
    global width
    global mine_number

    length = Mlength
    width = Mwidth
    mine_number = Mmine


class Game(object):
    """
    游戏类,属性如下:
    mine_number = 雷的数量
    size = 游戏雷区面积
    opened_number = 打开的方块个数
    marked_number = 标记的方块个数
    plaid_number = 没打开的方块个数
    action_number = 操作数量
    winlost = 游戏胜负情况(用数字表示,判断用常量,初始是0)
    """
    def __init__(self):
        """
        新建一个游戏对象
        """
        minefield = Minefield()
        minefield.new_game_minefield_matrix()
        self.minefield_matrix = minefield.minefield_matrix
        self.mine_number = minefield.mine_number
        self.size = minefield.size

        self.plaid_number = self.size
        self.opened_number = int()
        self.action_number = int()
        self.marked_number = int()

        self.winlost = int() #游戏的输赢情况是表示为int类型的


    def open_plaid(self,index):
        """
        打开游戏对象的某个方块
        传入参数:方块坐标元组(x,y)
        """
        this_plaid = self.minefield_matrix[index[0]][index[1]]

        if this_plaid.mark == True or this_plaid.open_ == True:
            return 

        this_plaid.open_ = True
        self.opened_number += 1
        self.plaid_number -= 1

        if self.get_plaid_show_number(index) == 9: #若踩到了雷
            self.winlost = GAME_FAIL #游戏失败
            return
        elif self.plaid_number == self.mine_number: #若剩下的方块数量等于雷的数量
            self.winlost = GAME_WIN #游戏胜利

        self.minefield_matrix[index[0]][index[1]] = this_plaid
        return OPEN_NOW
                    

    def mark_plaid(self,index):
        """
        标记(或是取消标记)游戏对象的某个方块
        传入参数:方块坐标(x,y)
        """
        this_plaid = self.minefield_matrix[index[0]][index[1]]

        if this_plaid.open_ != True:
            return_code = MARK_DONT if this_plaid.mark else MARK_NOW
            this_plaid.mark = False if this_plaid.mark else True
            #已经标记了的,取消标记。反之同理
            self.marked_number += 1 if this_plaid.mark else -1
            self.minefield_matrix[index[0]][index[1]] = this_plaid
            return return_code
    

    def get_plaid_show_number(self,index):
        """
        获取某个方块的数字
        """
        return self.minefield_matrix[index[0]][index[1]].show_number


    def anew_game(self):
        """
        按同样的游戏属性重新生成一局游戏
        """
        player_init(length,width,self.mine_number)
        self.__init__() 


    def good_plaid(self,index):
        """
        获取一个方块的操作情况
        若方块是未知块,返回True,若方块已被标记或打开,返回False
        """
        this_plaid =  self.minefield_matrix[index[0]][index[1]]
        return this_plaid.open_ == False and this_plaid.mark == False


class Plaid(object):
    """
    方块类,属性如下:
    show_number = 此方块上的数字(9表示雷)
    mark = 是否被标记(true or false)
    open_ = 是否被打开(true or false)
    """

    show_number = int()
    mark = False
    open_ = False


class Minefield(object):
    """
    雷区类,属性如下:
    minefield_matrix = 表示雷区的二维数组
    mine_number = 此雷区的雷数量
    size = 此雷区的面积
    """
    def new_game_minefield_matrix(self): 
        """
        在类内创建一片完善的雷区
        传入参数:雷区边长
        """
        self.minefield_matrix = [[Plaid() for y in range(width)]for x in range(length)]#定义二维数组,表示实际雷区
        self.random_bray() if mine_number == 0 else self.precise_bray(mine_number)#雷区布雷
        self.minefield_matrix_object_init()#雷区对象初始化
        

    def minefield_matrix_object_init(self): 
        """
        确定雷区中每个方块的显示
        确定该雷区的雷数量
        """
        self.mine_number = int() #雷区雷数量
        self.size = length*width #雷区大小(面积)

        for x in range(length):
            for y in range(width): #遍历雷区

                if self.minefield_matrix[x][y].show_number != 9: #若遍历到的格子不是雷,检测周围所有可用方块的布雷情况
                    for index in get_around_index((x,y)):

                        if self.minefield_matrix[index[0]][index[1]].show_number == 9:
                            self.minefield_matrix[x][y].show_number += 1 #确定该格子显示
                else:
                    self.mine_number += 1 #若遍历到的这个格子是雷,则雷数量加1


    def random_bray(self):
        """
        给雷区随机布雷
        循环次数始终是雷区面积的四分之一,保证了雷的密度
        而random_bray()的代码有可能会对雷区的某个位置重复布雷,实现随机雷数量
        """
        for i in range(length*width//3): #雷数量不超过雷区格子总数的1/3
            mine_index = get_random_index()
            self.minefield_matrix[mine_index[0]][mine_index[1]].show_number = 9 #雷在数组中表示为9


    def precise_bray(self,mine_number):
        """
        按雷数量给雷区布雷,是random_bray()的表弟
        """
        mine_index_list = list()
        for i in range(mine_number):
            """ 如果python可以使用do -- while,接下来的代码将不会那么重复 """
            mine_index = get_random_index()

            while mine_index in mine_index_list:
                mine_index = get_random_index()

            self.minefield_matrix[mine_index[0]][mine_index[1]].show_number = 9
            mine_index_list.append((mine_index[0],mine_index[1]))


def get_random_index():
    """
    获取一个随机的下标
    """
    return (randint(0,length-1),randint(0,width-1))


def get_around_tilted_index(index):
    """
    获取某个位置的左上、右上、左下、右下的方块的所有可用下标
    """
    around_index_list = list()
    x,y = index[0],index[1]

    for index in [(x-1,y-1),(x-1,y+1),(x+1,y-1),(x+1,y+1)]:

        if index[0]>=0 and index[1]>=0:
            if index[0]<length and index[1]<width:
                around_index_list.append(index)

    return around_index_list


def get_around_adjacent_index(index):
    """
    获取某个位置上、下、左、右四个方位的方块的所有可用坐标
    """
    around_index_list = list()
    x,y = index[0],index[1]

    for index in [(x,y-1),(x,y+1),(x-1,y),(x+1,y)]:

        if index[0]>=0 and index[1]>=0:
            if index[0]<length and index[1]<width:
                around_index_list.append(index)

    return around_index_list


def get_around_index(index):
    """
    获取某个位置周围一圈方块的所有可用坐标
    """
    return get_around_tilted_index(index) + get_around_adjacent_index(index)
代码
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'z_Game_menu_ui.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.

# 2021.9.1开始编程
# 扫雷游戏 菜单ui接口

#菜单ui为拖拽开发,并由Qdesigner自动生成代码

# 作者:张绍翊
# 机构:麦咭编程特训营广西来宾校区

from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QIcon


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(549, 309)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.game_begin = QtWidgets.QPushButton(self.centralwidget)
        self.game_begin.setGeometry(QtCore.QRect(200, 240, 171, 41))
        self.game_begin.setObjectName("game_begin")
        self.line = QtWidgets.QFrame(self.centralwidget)
        self.line.setGeometry(QtCore.QRect(180, -30, 20, 401))
        self.line.setFrameShape(QtWidgets.QFrame.VLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")
        self.action_show = QtWidgets.QTextBrowser(self.centralwidget)
        self.action_show.setGeometry(QtCore.QRect(380, 10, 161, 271))
        self.action_show.setObjectName("action_show")
        self.line_3 = QtWidgets.QFrame(self.centralwidget)
        self.line_3.setGeometry(QtCore.QRect(-10, 140, 421, 20))
        self.line_3.setFrameShape(QtWidgets.QFrame.HLine)
        self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_3.setObjectName("line_3")
        self.widget = QtWidgets.QWidget(self.centralwidget)
        self.widget.setGeometry(QtCore.QRect(200, 10, 171, 131))
        self.widget.setObjectName("widget")
        self.gridLayout_7 = QtWidgets.QGridLayout(self.widget)
        self.gridLayout_7.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_7.setObjectName("gridLayout_7")
        self.game_run = QtWidgets.QLabel(self.widget)
        self.game_run.setObjectName("game_run")
        self.gridLayout_7.addWidget(self.game_run, 0, 0, 1, 2)
        self.label_15 = QtWidgets.QLabel(self.widget)
        self.label_15.setObjectName("label_15")
        self.gridLayout_7.addWidget(self.label_15, 1, 0, 1, 1)
        self.mark_number = QtWidgets.QLCDNumber(self.widget)
        self.mark_number.setObjectName("mark_number")
        self.gridLayout_7.addWidget(self.mark_number, 1, 1, 1, 2)
        self.label_16 = QtWidgets.QLabel(self.widget)
        self.label_16.setObjectName("label_16")
        self.gridLayout_7.addWidget(self.label_16, 2, 0, 1, 1)
        self.open_number = QtWidgets.QLCDNumber(self.widget)
        self.open_number.setObjectName("open_number")
        self.gridLayout_7.addWidget(self.open_number, 2, 1, 1, 2)
        self.label_17 = QtWidgets.QLabel(self.widget)
        self.label_17.setObjectName("label_17")
        self.gridLayout_7.addWidget(self.label_17, 3, 0, 1, 1)
        self.size_mark_number = QtWidgets.QLCDNumber(self.widget)
        self.size_mark_number.setObjectName("size_mark_number")
        self.gridLayout_7.addWidget(self.size_mark_number, 3, 1, 1, 2)
        self.label_18 = QtWidgets.QLabel(self.widget)
        self.label_18.setObjectName("label_18")
        self.gridLayout_7.addWidget(self.label_18, 4, 0, 1, 2)
        self.time_show = QtWidgets.QLCDNumber(self.widget)
        self.time_show.setObjectName("time_show")
        self.gridLayout_7.addWidget(self.time_show, 4, 2, 1, 1)
        self.widget1 = QtWidgets.QWidget(self.centralwidget)
        self.widget1.setGeometry(QtCore.QRect(10, 160, 171, 121))
        self.widget1.setObjectName("widget1")
        self.gridLayout_5 = QtWidgets.QGridLayout(self.widget1)
        self.gridLayout_5.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_5.setObjectName("gridLayout_5")
        self.gridLayout_4 = QtWidgets.QGridLayout()
        self.gridLayout_4.setObjectName("gridLayout_4")
        self.label_3 = QtWidgets.QLabel(self.widget1)
        self.label_3.setObjectName("label_3")
        self.gridLayout_4.addWidget(self.label_3, 0, 0, 1, 1)
        self.game_window_l = QtWidgets.QSpinBox(self.widget1)
        self.game_window_l.setMinimum(8)
        self.game_window_l.setMaximum(30)
        self.game_window_l.setObjectName("game_window_l")
        self.gridLayout_4.addWidget(self.game_window_l, 0, 1, 1, 1)
        self.label_4 = QtWidgets.QLabel(self.widget1)
        self.label_4.setObjectName("label_4")
        self.gridLayout_4.addWidget(self.label_4, 0, 2, 1, 1)
        self.label_2 = QtWidgets.QLabel(self.widget1)
        self.label_2.setObjectName("label_2")
        self.gridLayout_4.addWidget(self.label_2, 1, 0, 1, 1)
        self.game_window_w = QtWidgets.QSpinBox(self.widget1)
        self.game_window_w.setMinimum(8)
        self.game_window_w.setMaximum(30)
        self.game_window_w.setObjectName("game_window_w")
        self.gridLayout_4.addWidget(self.game_window_w, 1, 1, 1, 1)
        self.label_5 = QtWidgets.QLabel(self.widget1)
        self.label_5.setObjectName("label_5")
        self.gridLayout_4.addWidget(self.label_5, 1, 2, 1, 1)
        self.gridLayout_5.addLayout(self.gridLayout_4, 0, 0, 1, 1)
        self.gridLayout_3 = QtWidgets.QGridLayout()
        self.gridLayout_3.setObjectName("gridLayout_3")
        self.gridLayout_2 = QtWidgets.QGridLayout()
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.label = QtWidgets.QLabel(self.widget1)
        self.label.setObjectName("label")
        self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1)
        self.input_mine_number = QtWidgets.QSpinBox(self.widget1)
        self.input_mine_number.setMinimum(10)
        self.input_mine_number.setObjectName("input_mine_number")
        self.gridLayout_2.addWidget(self.input_mine_number, 0, 1, 1, 1)
        self.gridLayout_3.addLayout(self.gridLayout_2, 0, 0, 1, 1)
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.use_input_mine_number = QtWidgets.QRadioButton(self.widget1)
        self.use_input_mine_number.setChecked(True)
        self.use_input_mine_number.setObjectName("use_input_mine_number")
        self.gridLayout.addWidget(self.use_input_mine_number, 2, 0, 1, 1)
        self.use_random_mine_number = QtWidgets.QRadioButton(self.widget1)
        self.use_random_mine_number.setObjectName("use_random_mine_number")
        self.gridLayout.addWidget(self.use_random_mine_number, 1, 0, 1, 1)
        self.gridLayout_3.addLayout(self.gridLayout, 1, 0, 1, 1)
        self.gridLayout_5.addLayout(self.gridLayout_3, 1, 0, 1, 1)
        self.widget2 = QtWidgets.QWidget(self.centralwidget)
        self.widget2.setGeometry(QtCore.QRect(15, 9, 171, 131))
        self.widget2.setObjectName("widget2")
        self.gridLayout_8 = QtWidgets.QGridLayout(self.widget2)
        self.gridLayout_8.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_8.setObjectName("gridLayout_8")
        self.label_24 = QtWidgets.QLabel(self.widget2)
        self.label_24.setObjectName("label_24")
        self.gridLayout_8.addWidget(self.label_24, 0, 0, 1, 1)
        self._l_number = QtWidgets.QLCDNumber(self.widget2)
        self._l_number.setObjectName("_l_number")
        self.gridLayout_8.addWidget(self._l_number, 0, 1, 1, 1)
        self.label_25 = QtWidgets.QLabel(self.widget2)
        self.label_25.setObjectName("label_25")
        self.gridLayout_8.addWidget(self.label_25, 0, 2, 1, 1)
        self.label_20 = QtWidgets.QLabel(self.widget2)
        self.label_20.setObjectName("label_20")
        self.gridLayout_8.addWidget(self.label_20, 1, 0, 1, 1)
        self._w_number = QtWidgets.QLCDNumber(self.widget2)
        self._w_number.setObjectName("_w_number")
        self.gridLayout_8.addWidget(self._w_number, 1, 1, 1, 1)
        self.label_21 = QtWidgets.QLabel(self.widget2)
        self.label_21.setObjectName("label_21")
        self.gridLayout_8.addWidget(self.label_21, 1, 2, 1, 1)
        self.label_9 = QtWidgets.QLabel(self.widget2)
        self.label_9.setObjectName("label_9")
        self.gridLayout_8.addWidget(self.label_9, 2, 0, 1, 1)
        self.size = QtWidgets.QLCDNumber(self.widget2)
        self.size.setObjectName("size")
        self.gridLayout_8.addWidget(self.size, 2, 1, 1, 1)
        self.label_10 = QtWidgets.QLabel(self.widget2)
        self.label_10.setObjectName("label_10")
        self.gridLayout_8.addWidget(self.label_10, 2, 2, 1, 1)
        self.label_12 = QtWidgets.QLabel(self.widget2)
        self.label_12.setObjectName("label_12")
        self.gridLayout_8.addWidget(self.label_12, 3, 0, 1, 1)
        self.mine_number = QtWidgets.QLCDNumber(self.widget2)
        self.mine_number.setObjectName("mine_number")
        self.gridLayout_8.addWidget(self.mine_number, 3, 1, 1, 1)
        self.label_13 = QtWidgets.QLabel(self.widget2)
        self.label_13.setObjectName("label_13")
        self.gridLayout_8.addWidget(self.label_13, 3, 2, 1, 1)
        self.label_14 = QtWidgets.QLabel(self.widget2)
        self.label_14.setObjectName("label_14")
        self.gridLayout_8.addWidget(self.label_14, 4, 0, 1, 1)
        self.mine_density = QtWidgets.QProgressBar(self.widget2)
        self.mine_density.setProperty("value", 24)
        self.mine_density.setObjectName("mine_density")
        self.gridLayout_8.addWidget(self.mine_density, 4, 1, 1, 2)
        self.widget3 = QtWidgets.QWidget(self.centralwidget)
        self.widget3.setGeometry(QtCore.QRect(201, 161, 171, 71))
        self.widget3.setObjectName("widget3")
        self.gridLayout_6 = QtWidgets.QGridLayout(self.widget3)
        self.gridLayout_6.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_6.setObjectName("gridLayout_6")
        self.label_23 = QtWidgets.QLabel(self.widget3)
        self.label_23.setObjectName("label_23")
        self.gridLayout_6.addWidget(self.label_23, 0, 0, 1, 1)
        self.game_number = QtWidgets.QLCDNumber(self.widget3)
        self.game_number.setObjectName("game_number")
        self.gridLayout_6.addWidget(self.game_number, 0, 1, 1, 1)
        self.label_22 = QtWidgets.QLabel(self.widget3)
        self.label_22.setObjectName("label_22")
        self.gridLayout_6.addWidget(self.label_22, 0, 2, 1, 1)
        self.label_11 = QtWidgets.QLabel(self.widget3)
        self.label_11.setObjectName("label_11")
        self.gridLayout_6.addWidget(self.label_11, 1, 0, 1, 1)
        self.win_number = QtWidgets.QLCDNumber(self.widget3)
        self.win_number.setObjectName("win_number")
        self.gridLayout_6.addWidget(self.win_number, 1, 1, 1, 1)
        self.label_8 = QtWidgets.QLabel(self.widget3)
        self.label_8.setObjectName("label_8")
        self.gridLayout_6.addWidget(self.label_8, 1, 2, 1, 1)
        self.label_19 = QtWidgets.QLabel(self.widget3)
        self.label_19.setObjectName("label_19")
        self.gridLayout_6.addWidget(self.label_19, 2, 0, 1, 1)
        self.winbai = QtWidgets.QProgressBar(self.widget3)
        self.winbai.setProperty("value", 24)
        self.winbai.setObjectName("winbai")
        self.gridLayout_6.addWidget(self.winbai, 2, 1, 1, 2)
        self.game_begin.raise_()
        self.line.raise_()
        self.mine_number.raise_()
        self.label_10.raise_()
        self.size.raise_()
        self.label_9.raise_()
        self.mine_density.raise_()
        self.label_12.raise_()
        self.label_14.raise_()
        self.label_13.raise_()
        self.line_3.raise_()
        self.label_8.raise_()
        self.label_11.raise_()
        self.win_number.raise_()
        self.label_19.raise_()
        self.winbai.raise_()
        self.label_22.raise_()
        self.label_23.raise_()
        self.game_number.raise_()
        self.label_20.raise_()
        self._w_number.raise_()
        self.label_21.raise_()
        self.label_24.raise_()
        self._l_number.raise_()
        self.label_25.raise_()
        self.action_show.raise_()
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "扫雷"))
        self.game_begin.setText(_translate("MainWindow", "开始扫雷"))
        self.action_show.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">———————————</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">游戏限制:</p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">雷区最大面积30x30</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">雷区最小面积8x8</p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">雷密度不得超过1/3</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">雷数量最少10个</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">———————————</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">地雷密度 = 雷数/面积</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">胜率 = 胜利/总局数</p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">———————————</p></body></html>"))
        self.game_run.setText(_translate("MainWindow", "游戏未在运行"))
        self.label_15.setText(_translate("MainWindow", "标记格数"))
        self.label_16.setText(_translate("MainWindow", "打开格数"))
        self.label_17.setText(_translate("MainWindow", "剩余雷数"))
        self.label_18.setText(_translate("MainWindow", "累计用时(秒)"))
        self.label_3.setText(_translate("MainWindow", "窗口长"))
        self.label_4.setText(_translate("MainWindow", "格"))
        self.label_2.setText(_translate("MainWindow", "窗口宽"))
        self.label_5.setText(_translate("MainWindow", "格"))
        self.label.setText(_translate("MainWindow", "设置雷数量"))
        self.use_input_mine_number.setText(_translate("MainWindow", "使用设置的雷数量"))
        self.use_random_mine_number.setText(_translate("MainWindow", "使用随机雷数量"))
        self.label_24.setText(_translate("MainWindow", "雷区长"))
        self.label_25.setText(_translate("MainWindow", "格"))
        self.label_20.setText(_translate("MainWindow", "雷区宽"))
        self.label_21.setText(_translate("MainWindow", "格"))
        self.label_9.setText(_translate("MainWindow", "雷区面积"))
        self.label_10.setText(_translate("MainWindow", "格"))
        self.label_12.setText(_translate("MainWindow", "总雷数量"))
        self.label_13.setText(_translate("MainWindow", "个"))
        self.label_14.setText(_translate("MainWindow", "地雷密度"))
        self.label_23.setText(_translate("MainWindow", "累计游戏"))
        self.label_22.setText(_translate("MainWindow", "局"))
        self.label_11.setText(_translate("MainWindow", "胜利"))
        self.label_8.setText(_translate("MainWindow", "局"))
        self.label_19.setText(_translate("MainWindow", "胜率"))

class MyWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MyWindow, self).__init__(parent)
        self.setWindowIcon(QIcon('.\\GAME_ui_icon\\mine.png'))
        self.setFixedSize(549,309)
        self.setupUi(self)
代码
# -*- coding: utf-8 -*-

# GAME_game_ui.py
# 扫雷游戏 游戏ui接口
# 2021.8.18 开始编程

# 作者:张绍翊
# 机构:麦咭编程特训营广西来宾校区


import pygame


pygame.init()

UI_ERROR = -913 #ui报错代码

PLAID_SIZE = 15 #每个格子的边长(单位:像素)
PLAID_CLOLUR = "#6699CC" #格子的颜色

WINDOW_TITLE = "扫雷" #窗口标题
WINDOW_CLOLUR = "#E0E0E0" #窗口背景色

NONE_STYLE = pygame.image.load(".\\GAME_plaid_style\\GAME_none_plaid_style.png") #未知方块样式
OPEN_STYLE = pygame.image.load(".\\GAME_plaid_style\\GAME_open_plaid_style.png") #打开方块样式
MARK_STYLE = pygame.image.load(".\\GAME_plaid_style\\GAME_mark_style.png") #红旗标记样式

NUMBER_FONT = pygame.font.SysFont('comicsansms',17) #标记数字样式 Comic Sans MS 字体 17号 抗锯齿

#每个数字的样式在下面定义
NUMBER_1_STYLE = NUMBER_FONT.render(u" 1",True,"#54FF9F")
NUMBER_2_STYLE = NUMBER_FONT.render(u" 2",True,"#FFDEAD")
NUMBER_3_STYLE = NUMBER_FONT.render(u" 3",True,"#00868B")
NUMBER_4_STYLE = NUMBER_FONT.render(u" 4",True,"#E6E6FA")
NUMBER_5_STYLE = NUMBER_FONT.render(u" 5",True,"#CFCFCF")
NUMBER_6_STYLE = NUMBER_FONT.render(u" 6",True,"#FF69B4")
NUMBER_7_STYLE = NUMBER_FONT.render(u" 7",True,"#D2B48C")
NUMBER_8_STYLE = NUMBER_FONT.render(u" 8",True,"#CD5C5C")

def window_init(length,width):
    """
    游戏窗口模块初始化
    根据传入的长宽确定窗口长宽
    """
    global WINDOW_LENGTH,WINDOW_WIDTH

    WINDOW_LENGTH =  (PLAID_SIZE+1)*length#窗口的长(单位:像素)
    WINDOW_WIDTH = (PLAID_SIZE+1)*width #窗口的宽(单位:像素)


def new_window():
    """
    新建一个窗口
    """
    screen = pygame.display.set_mode((WINDOW_LENGTH,WINDOW_WIDTH)) #显示窗口
    screen.fill(WINDOW_CLOLUR) #填充颜色
    pygame.display.set_caption(WINDOW_TITLE) #窗口标题

    pygame.display.update()
    return screen


def set_plaid(screen,style):
    """
    给窗口平铺某个格子
    """
    for x in range(0,WINDOW_LENGTH,PLAID_SIZE+1):
        for y in range(0,WINDOW_WIDTH,PLAID_SIZE+1):

            screen.blit(style,(x,y)) #显示传入style
    pygame.display.update()


def judging_legitimate_click(index):
    """
    判断一个点击坐标的合法性
    """
    return get_plaid_index(index) != UI_ERROR


def get_plaid_index(index):
    """
    根据传入坐标判断此坐标上的格子的坐标
    传入的一般是点击坐标
    """
    for x in range(0,WINDOW_LENGTH,PLAID_SIZE+1):
        for y in range(0,WINDOW_WIDTH,PLAID_SIZE+1):

            if index[0]>x and index[1]>y:
                if index[0]<x+PLAID_SIZE and index[1]<y+PLAID_SIZE:
                    return (x//(PLAID_SIZE+1),y//(PLAID_SIZE+1))     

    return UI_ERROR


def get_draw_plaid_index(plaid_index):
    """
    根据传入坐标判断此坐标上的格子的左上角的坐标(在窗口内)
    传入方块坐标
    """
    return (plaid_index[0]*(PLAID_SIZE+1),plaid_index[1]*(PLAID_SIZE+1))


def new_plaid_style(screen,plaid_index,style):
    """
    给某个方块改变样式
    """
    index = get_draw_plaid_index(plaid_index)
    screen.blit(style,index)
    pygame.display.update()


def new_show_number(screen,plaid_index,number):
    """
    给某个方块显示数字
    """
    index = get_draw_plaid_index(plaid_index)

    if number != 0 and number != 9:
        screen.blit(eval("NUMBER_%d_STYLE"%(number)),(index[0]-1,index[1]-5))
    #当方块上的数字为0时,不需要显示数字“0”

    pygame.display.update()

评论

user-avatar
  • 伦**

    伦**2024.03.27

    666

    0