如今的游戏让人眼花缭乱,还有谁记得当时的扫雷(Minesweeper)游戏?它虽然页面简陋,玩法单一,但却和数独、幻方一样,是一种益智类游戏,因为当时的windows xp只有扫雷游戏,我就一直研究它,琢磨它。久而久之,我越来越喜欢这款游戏,所以很久之前,我就想要用自己的技术,创造属于我自己的扫雷。今天,有了mind+平台举办的比赛,点燃了我的好胜心。而我也终于能用我自己的python技术,重现这款1992年发布的扫雷,找回经典的味道。
我把我的扫雷游戏命名为Bigsweeper(笑)
使用软件:
mind+
sublime Text3
Qdesigner(pyqt5包含)
使用语言:
python
使用库:
pygame
pyqt5
代码结构如上图
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自动生成的
下面是程序截图:
下面是我的代码
从上到下依次是:
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()
伦**2024.03.27
666