You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
7.6 KiB

5 months ago
# _*_ coding:utf-8 _*_
# @Time2023/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() # 输出显示计时器效果