# _*_ coding:utf-8 _*_ # @Time:2023/6/29 9:37 # @File:X3.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 # 所有被打开的按钮的总数,等于所有按钮数-所有雷数 class Data: def init_mine_map(self, mines): # 初始化布雷方案 global BOARD_ROWS, BOARD_COLS, initData # 全局变量行数,列数,布雷方案 initData = [[0 for i in range(BOARD_COLS)] for j in range(BOARD_ROWS)] # 无雷初始化,先列后行 for i in random.sample(range(BOARD_COLS * BOARD_ROWS), mines): initData[i // BOARD_COLS][i % BOARD_COLS] = 'M' # 在BOARD_COLS*BOARD_ROWS范围中随机生成mines个雷 # 雷行下标为随机数除以列数取整,雷列下标为随机数对列数取模 return initData def init_board_state(self): # 初始化方块状态 global curData, SHOW_BOARD_STATE, BOARD_ROWS, BOARD_COLS curData = [['E' for i in range(BOARD_COLS)] for j in range(BOARD_ROWS)] # 立体方块 SHOW_BOARD_STATE = [[0 for i in range(BOARD_COLS)] for j in range(BOARD_ROWS)] # 显示状态 def get_around_xy(self, x, y): # 返回对应坐标周围的坐标列表 global BOARD_ROWS, BOARD_COLS return [(i, j) for i in range(max(0, x - 1), min(BOARD_ROWS - 1, x + 1) + 1) # 行号最小为0不为负,最大BOARD_ROWS-1 for j in range(max(0, y - 1), min(BOARD_COLS - 1, y + 1) + 1) if i != x or j != y] # 不包括自己,即x行y列方块 def get_around_mine_num(self, x, y): # 递归获取周围雷数 global curData, initData # 雷的状态,布雷方案,是否计算过 if initData[x][y] == 'M': # 挖开的是雷,游戏结束 curData[x][y] = 'X' # 更新状态为翻开的雷 return 0 # 返回结果,是雷 around_xy = self.get_around_xy(x, y) # 周围按钮的坐标 num = self.num_of_mine(x, y) # 记录周围的总雷数 if num == 0: # 如果雷数为0,更新状态为B且递归调用函数进行雷数计算 curData[x][y] = 'B' for i, j in around_xy: if curData[i][j] == 'E': # 把周围未打开的方块都检查一遍 self.get_around_mine_num(i, j) return 1 # 返回结果不是雷,且方块已经打开,显示结果 def show_flag(self, x, y): # 标志旗子 global curData if curData[x][y] == 'E': # 如果按钮未打开,且未标记为旗子则显示旗子标志 curData[x][y] = 'F' elif curData[x][y] == 'F': # 如果按钮未打开,且已经标记为旗子,则取消显示 curData[x][y] = 'E' else: return # 如果按钮已经打开,则不做任何操作 def game_timer(self): # 计时器 global GAME_OVER, TIMER_RUN # TIMER_RUN为计时器是否运行 if GAME_OVER: # 如果游戏结束,不再响应 return def count(): # 该函数完成读秒功能,将运行在子线程中 global TIMER_RUN, CLOCK, GAME_OVER, TIME_LIMIT while TIMER_RUN and CLOCK <= TIME_LIMIT: # 开始计时条件 CLOCK += 1 print(CLOCK) # 测试代码 time.sleep(1) # 休眠1秒。模拟读秒 if CLOCK > TIME_LIMIT: # 如果超时,游戏失败 GAME_OVER = True if not TIMER_RUN: # 子线程状态为未启动,则启动子进程,并把状态设为True TIMER_RUN = True count() # 开始计时 elif TIMER_RUN: # 子线程状态为启动,则暂停子进程,并把状态设为False TIMER_RUN = False # 暂停计时 def remaining_mine_num(self): # 得到剩余雷数的同时,判断是否胜利 global MINES, BOARD_ROWS, BOARD_COLS, GAME_OVER, TIMER_RUN, initData, curData, mine_number, \ MINE_WITH_FLAG, NO_MINE_BUT_FLAG, OPEN_BUTTONS MINE_WITH_FLAG = 0 # 所有按钮下有地雷被标旗子的总数 NO_MINE_BUT_FLAG = 0 # 所有按钮下无地雷被标旗子的总数 OPEN_BUTTONS = 0 # 所有被打开的按钮的总数,等于所有按钮数-所有雷数 for i in range(BOARD_ROWS): # i为行,0到BOARD_ROWS-1 for j in range(BOARD_COLS): # j为列,0到BOARD_COLS-1 if initData[i][j] == 'M' and curData[i][j] == 'F': # 如果该按钮下地雷被标旗子 MINE_WITH_FLAG += 1 elif initData[i][j] != 'M' and curData[i][j] == 'F': # 如果该按钮下无地雷被标旗子 NO_MINE_BUT_FLAG += 1 elif initData[i][j] != 'M' and curData[i][j] != 'E': # 如果该按钮不是雷且已被打开 OPEN_BUTTONS += 1 mine_number = MINES - (MINE_WITH_FLAG + NO_MINE_BUT_FLAG) # mine_number为剩余的地雷数 if mine_number < 0: # 如无雷也被标记红旗,可能出现标记红旗的按钮数大于地雷数,雷数不能为负 mine_number = 0 # 标记为旗子的所有块>实际雷数,仍显示0个雷 return mine_number def num_of_mine(self, x, y): # 获取(x,y)处周围的雷数 global initData, curData minenum = 0 # 保存雷数 if initData[x][y] != 'M': # 如果不是雷 for i, j in self.get_around_xy(x, y): # 遍历周围的方块 if initData[i][j] == 'M': minenum += 1 # 是雷则雷数加1 curData[x][y] = minenum # 更新改方块的状态 return minenum if __name__ == '__main__': test = Data() test.init_mine_map(MINES) # 初始化initData test.init_board_state() # 初始化curData、SHOW_BOARD_STATE for i in range(BOARD_ROWS): # 输出initData print(initData[i]) print('\n') test.get_around_mine_num(0, 0) for i in range(BOARD_ROWS): # 获取(0,0)周围雷数,并输出查看结果 print(curData[i]) print('\n') test.show_flag(5, 5) for i in range(BOARD_ROWS): # 标记(5,5)为雷,并输出查看结果 print(curData[i]) print('\n') print(test.remaining_mine_num(), '\n') # 输出剩余雷数 print(test.num_of_mine(1, 1), '\n') # 获取(1,1)周围雷数,并输出。为0表示(1,1)为雷 test.game_timer() # 输出显示计时器效果