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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# _*_ 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() # 输出显示计时器效果