|
|
# _*_ coding:utf-8 _*_
|
|
|
# @Time:2023/6/29 9:37
|
|
|
# @File:X2.py
|
|
|
# @SoftWare:PyCharm
|
|
|
# @Project:MS
|
|
|
# @author:yzf
|
|
|
from tkinter import scrolledtext, END # 滚动框, 滚动框自动下拉到最后一行
|
|
|
from pymouse import PyMouse # 模拟鼠标自动点击
|
|
|
import tkinter as tk # UI界面
|
|
|
import threading # 多线程任
|
|
|
import pyautogui # 获取窗口位置信息
|
|
|
import random # 随机数
|
|
|
import time # 应用sleep函数睡眠
|
|
|
|
|
|
# 全局变量
|
|
|
TIME_LIMIT = 10 # 扫雷限时
|
|
|
CLOCK = 0 # 扫雷用时记录
|
|
|
TIMER_RUN = False # 计时器是否启动
|
|
|
GAME_OVER = False # 游戏是否结束
|
|
|
curData = [] # 方块状态数组
|
|
|
initData = [] # 初始布雷方案数组
|
|
|
SHOW_BOARD_STATE = [] # 是否计算周围雷数数组
|
|
|
BUTTONS = {} # 方块按钮字典
|
|
|
BOARD_ROWS = 20 # 扫雷方块行数
|
|
|
BOARD_COLS = 20 # 扫雷方块列数
|
|
|
MINES = 96 # 总的雷数
|
|
|
mine_number = 0 # 剩余的雷数
|
|
|
DIGIT_WIDTH = 5 # 数字的大小
|
|
|
DIGIT_HEIGHT = 1
|
|
|
FACE_WIDTH = 40 # 笑脸的大小
|
|
|
FACE_HEIGHT = 40
|
|
|
mine_number_x = 40 # 计雷数器的位置,不管窗口怎么变,计雷数器位置不变
|
|
|
mine_number_y = 20
|
|
|
HEADER_WIDTH = 20 * 40 # 头部栏的大小
|
|
|
HEADER_HEIGHT = 40
|
|
|
RIGHT_WIDTH = 300 # 右侧栏宽度,固定不变
|
|
|
BOTTOM_HEIGHT = 150 # 底部栏高度,固定不变
|
|
|
face_x = 20 * 40 / 2 # 脸图的位置
|
|
|
face_y = 20
|
|
|
clock_x = 20 * 40 - 40 # 计时器的位置
|
|
|
clock_y = 20
|
|
|
MINE_WITH_FLAG = 0 # 是地雷被标旗子的按钮总数
|
|
|
NO_MINE_BUT_FLAG = 0 # 不是地雷但是被标旗子的按钮总数
|
|
|
OPEN_BUTTONS = 0 # 所有被打开的按钮的总数,等于所有按钮数-所有雷数
|
|
|
|
|
|
|
|
|
def set_random_state(): # 设置随机状态curData
|
|
|
global curData
|
|
|
randomList = ['E', 'M', 'B', 'X', 'F', 1, 2, 3, 4, 5, 6, 7, 8] # 随机状态数列
|
|
|
curData = [[0 for j in range(BOARD_COLS)] for i in range(BOARD_ROWS)] # 矩阵初始化
|
|
|
for r in range(BOARD_ROWS):
|
|
|
for c in range(BOARD_COLS):
|
|
|
r_index = random.randint(0, len(randomList) - 1) # 获取随机下标
|
|
|
curData[r][c] = randomList[r_index] # 随机赋予状态
|
|
|
return curData
|
|
|
|
|
|
|
|
|
class Show: # 显示类
|
|
|
def __init__(self):
|
|
|
self.root = tk.Tk() # 初始化窗口
|
|
|
self.root.title('扫雷') # 窗口标题
|
|
|
self.root.resizable(width=False, height=False) # 设置窗口是否可变,宽不可变,高不可变,默认为True
|
|
|
self.root.geometry(f'{BOARD_COLS * 40}x{(BOARD_ROWS + 1) * 40}') # f'{}x{}',该格式{}里面是表达式,方便改变窗口大小
|
|
|
self.top = tk.Frame(self.root, bg='white', relief="sunken", width=HEADER_WIDTH, height=HEADER_HEIGHT) # 头部栏设计
|
|
|
self.label_mine = tk.Label(self.top, text=str(MINES), height=DIGIT_HEIGHT, width=DIGIT_WIDTH, bg='white', fg='red', font=('幼圆', 22)) # 显示雷数
|
|
|
self.label_clock = tk.Label(self.top, text=str(CLOCK), height=DIGIT_HEIGHT, width=DIGIT_WIDTH, bg='white', fg='red', font=('幼圆', 22)) # 显示扫雷用时
|
|
|
self.btn = tk.Button(self.top, bg='white', height=FACE_HEIGHT, width=FACE_WIDTH, relief='raised') # 脸图按钮
|
|
|
# 加载图片资源,图片格式必须是'xx.gif'
|
|
|
self.face1_img = tk.PhotoImage(file='img/face1.gif') # 笑脸
|
|
|
self.face2_img = tk.PhotoImage(file='img/face2.gif') # 耍酷脸
|
|
|
self.face3_img = tk.PhotoImage(file='img/face3.gif') # 哭脸
|
|
|
self.p0 = tk.PhotoImage(file='img/0.gif') # 空白方块
|
|
|
self.p1 = tk.PhotoImage(file='img/1.gif') # 数字1
|
|
|
self.p2 = tk.PhotoImage(file='img/2.gif') # 数字2
|
|
|
self.p3 = tk.PhotoImage(file='img/3.gif') # 数字3
|
|
|
self.p4 = tk.PhotoImage(file='img/4.gif') # 数字4
|
|
|
self.p5 = tk.PhotoImage(file='img/5.gif') # 数字5
|
|
|
self.p6 = tk.PhotoImage(file='img/6.gif') # 数字6
|
|
|
self.p7 = tk.PhotoImage(file='img/7.gif') # 数字7
|
|
|
self.p8 = tk.PhotoImage(file='img/8.gif') # 数字8
|
|
|
self.p9 = tk.PhotoImage(file='img/9.gif') # 爆炸雷
|
|
|
self.p10 = tk.PhotoImage(file='img/10.gif') # 标错雷
|
|
|
self.p11 = tk.PhotoImage(file='img/11.gif') # 旗子
|
|
|
self.p12 = tk.PhotoImage(file='img/12.gif') # 立体方块
|
|
|
self.p13 = tk.PhotoImage(file='img/13.gif') # 未爆炸雷
|
|
|
|
|
|
def create_boards(self): # 初始化BOARD_ROWS*BOARD_COLS个button对象
|
|
|
global BOARD_ROWS, BOARD_COLS, BUTTONS
|
|
|
for row in range(BOARD_ROWS): # row为行,0到BOARD_ROWS-1
|
|
|
for col in range(BOARD_COLS): # col为列,0到BOARD_COLS-1
|
|
|
button = tk.Button(self.root) # 创建button对象
|
|
|
button.place(y=(row + 1) * 40, x=col * 40)
|
|
|
BUTTONS[row, col] = button # 以坐标为键,按钮对象为值,使其唯一确定
|
|
|
|
|
|
def show_game_window(self): # 游戏方块窗口显示
|
|
|
global curData, BOARD_ROWS, BOARD_COLS, BUTTONS
|
|
|
for row in range(BOARD_ROWS): # row为行,0到BOARD_ROWS-1
|
|
|
for col in range(BOARD_COLS): # col为列,0到BOARD_COLS-1
|
|
|
if curData[row][col] == 'M': # 如果是雷
|
|
|
BUTTONS[row, col]['image'] = self.p13
|
|
|
elif curData[row][col] == 'X': # 如果是挖开的雷
|
|
|
BUTTONS[row, col]['image'] = self.p9
|
|
|
elif curData[row][col] == 'E': # 如果是未挖开的方块
|
|
|
BUTTONS[row, col]['image'] = self.p12
|
|
|
elif curData[row][col] == 'F': # 如果是旗子
|
|
|
BUTTONS[row, col]['image'] = self.p11
|
|
|
elif curData[row][col] == 'B': # 如果是挖开的空方块
|
|
|
BUTTONS[row, col]['image'] = self.p0
|
|
|
elif curData[row][col] == 1: # 如果是数字1-8
|
|
|
BUTTONS[row, col]['image'] = self.p1
|
|
|
elif curData[row][col] == 2:
|
|
|
BUTTONS[row, col]['image'] = self.p2
|
|
|
elif curData[row][col] == 3:
|
|
|
BUTTONS[row, col]['image'] = self.p3
|
|
|
elif curData[row][col] == 4:
|
|
|
BUTTONS[row, col]['image'] = self.p4
|
|
|
elif curData[row][col] == 5:
|
|
|
BUTTONS[row, col]['image'] = self.p5
|
|
|
elif curData[row][col] == 6:
|
|
|
BUTTONS[row, col]['image'] = self.p6
|
|
|
elif curData[row][col] == 7:
|
|
|
BUTTONS[row, col]['image'] = self.p7
|
|
|
elif curData[row][col] == 8:
|
|
|
BUTTONS[row, col]['image'] = self.p8
|
|
|
BUTTONS[row, col]['relief'] = 'groove' # 按钮变为平面,不再有立体感
|
|
|
|
|
|
def header_frame(self):
|
|
|
global HEADER_WIDTH, mine_number_x, mine_number_y, clock_x, clock_y, face_x, face_y
|
|
|
self.top['width'] = HEADER_WIDTH
|
|
|
self.top.place(x=0, y=0, anchor=tk.NW) # 头部栏
|
|
|
self.label_mine.place(x=mine_number_x, y=mine_number_y, anchor=tk.CENTER)
|
|
|
self.label_clock.place(x=clock_x, y=clock_y, anchor=tk.CENTER)
|
|
|
self.btn['image'] = self.face1_img # 笑脸
|
|
|
self.btn.place(x=face_x, y=face_y, anchor=tk.CENTER)
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
set_random_state()
|
|
|
test = Show()
|
|
|
test.create_boards()
|
|
|
test.show_game_window()
|
|
|
test.header_frame()
|
|
|
test.root.mainloop()
|