# -*- coding: utf-8 -*-
# __/author__by:Kevin_F/__
import tkinter.messagebox
import random
import time
import pygame
import tkinter
import tkinter.messagebox
num_w = 30 # 横向格子数
num_h = 16 # 竖向格子数
# 界面初始化
def window_init():
pygame.init()
global window
# 格子尺寸30*30,状态栏高度100,上下左右边界20
window = pygame.display.set_mode((30 * num_w + 20 * 2, 100 + 30 * num_h + 2 * 40))
pygame.display.set_caption('扫雷xp')
window.fill((128, 138, 135)) # 背景色:冷灰
pygame.draw.rect(window, (192, 192, 192), (0, 0, 30 * num_w + 20 * 2, 100)) # 最上面状态框
for j in range(num_h):
for i in range(num_w):
# 格子坐标
block_x = 20 + i * 30
block_y = 100 + 20 + j * 30
list_block.append([block_x, block_y, 0, 0])
list_block_pos.append((block_x, block_y))
"""
list_temp生成存放每一行中格子的坐标信息
block_x:格子的x坐标
block_y:格子的y坐标 (坐标信息是固定不可修改的,所以用元组表示)
0:定义此格子是否是雷 是雷:-1 不是雷:表示周边相邻非雷格子的数量,先写入默认值为0
0:定义格子的标记状态 0-未标记,即初始值 1-标记是雷 2-标记问号 3-左击点开
"""
pygame.draw.rect(window, (192, 192, 192), (block_x, block_y, 30, 30), 0) # 实心格子
pygame.draw.rect(window, (220, 220, 220), (block_x, block_y, 30, 30), 2) # 格子边线
pygame.display.flip()
# 状态栏 雷剩余数
def bomb_les():
global count_bomb
if count_bomb < 0:
count_bomb = 0
# 不存在标记雷超过99的可能,因为未标记的状态只有先标记成雷-1,再右键取消标记雷+1
pygame.draw.rect(window, (192, 192, 192), (0, 0, 150, 100), 0)
font_bomb_num = pygame.font.SysFont('Microsoft YaHei', 60, bold=True)
bomb_num = font_bomb_num.render(str(count_bomb), True, (255, 0, 0))
window.blit(bomb_num, (20, 10))
pygame.display.update()
# 状态栏 状态显示
def game_status(status):
img_working = pygame.image.load('me.png')
img_bad = pygame.image.load('yc.png')
img_win = pygame.image.load('yes.png')
if status == 'working': # 游戏开始时的图片
window.blit(img_working, (15 * num_w + 20 - 45, 5))
elif status == 'loose': # 游戏失败的图片
window.blit(img_bad, (15 * num_w + 20 - 45, 5))
elif status == 'win': # 游戏胜利的图片
window.blit(img_win, (15 * num_w + 20 - 45, 5))
else:
pass
pygame.display.update()
# 游戏初始化(随机生成a颗雷,初始化每个格子的位置信息,状态信息)
def game_init():
# 随机生成99颗地雷
a = random.randint(10, 100)
global list_bomb_pos
list_bomb_pos = random.sample(list_block_pos, a)
# 从所有格子中取不重复的99个,即得到99个位置坐标(x,y),组成的列表即是所有雷的坐标列表
# 改写list_block中格子的第三个值 从0改成-1 即表示是雷
for i in range(len(list_bomb_pos)):
for j in range(len(list_block)):
if list_block[j][0] == list_bomb_pos[i][0] and list_block[j][1] == list_bomb_pos[i][1]:
list_block[j][2] = -1
# 不是雷的格子,要计算出格子周边的雷数,并写入list_block
for index in range(len(list_block)):
# index为list_block的下标
x = index % 30
y = index // 30
if list_block[index][2] == -1:
continue
else:
list_beside = list_side(x, y)
mark_num = 0 # 定义一个变量,用于存放此格子周边是雷的格子的个数
for element in list_beside:
if element[2] == -1:
mark_num += 1
list_block[index][2] = mark_num # 将周边雷的个数写入list_block中的第三个值
# 初始化一个列表,用于存放不是雷的格子的坐标
for i in list_block_pos:
if i not in list_bomb_pos:
list_not_bomb.append(i)
# 获得一个存放此格子周边相邻格子组成的列表(定义此函数返回值就是这个列表)
def list_side(x, y):
if 1 <= x <= 28 and 1 <= y <= 14:
list_side = [
list_block[x - 1 + (y - 1) * 30],
list_block[x + (y - 1) * 30],
list_block[x + 1 + (y - 1) * 30],
list_block[x - 1 + y * 30],
list_block[x + 1 + y * 30],
list_block[x - 1 + (y + 1) * 30],
list_block[x + (y + 1) * 30],
list_block[x + 1 + (y + 1) * 30]
]
elif x == 0 and 1 <= y <= 14:
list_side = [
list_block[(y - 1) * 30],
list_block[1 + (y - 1) * 30],
list_block[1 + y * 30],
list_block[(y + 1) * 30],
list_block[1 + (y + 1) * 30]
]
elif x == 29 and 1 <= y <= 14:
list_side = [
list_block[28 + (y - 1) * 30],
list_block[29 + (y - 1) * 30],
list_block[28 + y * 30],
list_block[28 + (y + 1) * 30],
list_block[29 + (y + 1) * 30]
]
elif 1 <= x <= 28 and y == 0:
list_side = [
list_block[x - 1 + y * 30],
list_block[x + 1 + y * 30],
list_block[x - 1 + (y + 1) * 30],
list_block[x + (y + 1) * 30],
list_block[x + 1 + (y + 1) * 30]
]
elif 1 <= x <= 28 and y == 15:
list_side = [
list_block[x - 1 + (y - 1) * 30],
list_block[x + (y - 1) * 30],
list_block[x + 1 + (y - 1) * 30],
list_block[x - 1 + y * 30],
list_block[x + 1 + y * 30]
]
elif x == 0 and y == 0:
list_side = [list_block[1], list_block[30], list_block[31]]
elif x == 0 and y == 15:
list_side = [list_block[420], list_block[421], list_block[451]]
elif x == 29 and y == 0:
list_side = [list_block[28], list_block[58], list_block[59]]
else:
list_side = [list_block[448], list_block[449], list_block[478]]
return list_side
# 绘制鼠标点击后的格子
def draw_block(para, x, y):
if para == 0: # 此格子周围格子都不是雷,此格子直接画个实心方块
pygame.draw.rect(window, (180, 180, 180), (x * 30 + 20, y * 30 + 120, 30, 30), 0)
elif para in [1, 2, 3, 4, 5, 6, 7, 8]: # 此格子周围有雷,显示周围雷的数量
colors = {
1: (0, 0, 255),
2: (34, 139, 34),
3: (25, 25, 112),
4: (160, 32, 240),
5: (94, 38, 18),
6: (199, 97, 20),
7: (240, 230, 140),
8: (240, 230, 140)
}
num_font = pygame.font.SysFont('SimHei', 24, bold=True)
text_num = num_font.render(str(para), True, colors[para])
w1, h1 = text_num.get_size()
window.blit(text_num, (x * 30 + 35 - w1 / 2, y * 30 + 135 - h1 / 2))
elif para == 'bomb': # 左键点击到雷的时候,地雷爆炸(变红色)
pygame.draw.circle(window, (0, 0, 0), (x * 30 + 35, y * 30 + 135), 12)
elif para == 'mark': # 右键标记为雷时,画个红色实心圆表示地雷
pygame.draw.rect(window, (192, 192, 192), (x * 30 + 20, y * 30 + 120, 30, 30), 0) # 实心格子
pygame.draw.rect(window, (220, 220, 220), (x * 30 + 20, y * 30 + 120, 30, 30), 2) # 格子边线
pygame.draw.circle(window, (255, 0, 0), (x * 30 + 35, y * 30 + 135), 11)
elif para == '?': # 右键标记?,格子上显示?
num_font = pygame.font.SysFont('SimHei', 24)
text_num = num_font.render('?', True, (255, 0, 0))
w1, h1 = text_num.get_size()
pygame.draw.rect(window, (192, 192, 192), (x * 30 + 20, y * 30 + 120, 30, 30), 0) # 实心格子
pygame.draw.rect(window, (220, 220, 220), (x * 30 + 20, y * 30 + 120, 30, 30), 2) # 格子边线
window.blit(text_num, (x * 30 + 35 - w1 / 2, y * 30 + 135 - h1 / 2))
elif para == 'blank': # 右键切换成未标记状态时,画为初始化时的格子
pygame.draw.rect(window, (192, 192, 192), (x * 30 + 20, y * 30 + 120, 30, 30), 0) # 实心格子
pygame.draw.rect(window, (220, 220, 220), (x * 30 + 20, y * 30 + 120, 30, 30), 2) # 格子边线
else:
pass
pygame.display.update()
# 扫雷区鼠标左击事件
def mouse_click_left(mouse_x, mouse_y):
global list_block, is_loose, count_click
pos_x = (mouse_x - 20) // 30
pos_y = (mouse_y - 120) // 30
index = pos_x + pos_y * 30 # list_block的下标index
# 触发了此位置的左击事件,要先把此位置的左击事件表达出来
if list_block[index][3] == 3: # 如果这个格子已经被左击点开过,则不能重复点击
pass
else:
# 先判断点击是否是雷 ,如果是雷,游戏失败
if list_block[index][2] == -1: # 鼠标左击 是雷 游戏失败
is_loose = True
for x, y in list_bomb_pos:
draw_block('bomb', (x - 20) // 30, (y - 120) // 30)
# 把当前点击的这个雷标成红色
pygame.draw.rect(window, (255, 0, 0), (20 + pos_x * 30, 120 + pos_y * 30, 30, 30), 0)
draw_block('bomb', pos_x, pos_y)
game_status('loose')
# 游戏已经失败,这个时候再点击扫雷区,无效
# 计时停止
else:
# 不是雷的情况下,再判断这个格子是否是周边雷数为0 ,如果为0 ,则要继续触发左击点开该格子周围8个格子
# 除了画出当前位置的左击事件,还需要判断当前周边雷数是否为0
# 如果为0,自动触发此格子周边8个相邻格子的左击事件(因为0 代表周边8个格子都不是雷,直接自动左击显示出这8个格子每一个的周边雷数)
# 如果这8个格子中还有周边雷数为0的,则递归调用函数
if list_block[index][2] == 0:
draw_block(list_block[index][2], pos_x, pos_y)
list_block[index][3] = 3
list_beside = list_side(pos_x, pos_y)
for i in list_beside:
x = (i[0] - 20) // 30
y = (i[1] - 120) // 30
if list_block[x + 30 * y][3] == 0: # 判断此位置的标记状态(list_block里的最后一个参数),0-未标记
mouse_click_left(x * 30 + 20, y * 30 + 120) # 调用函数
draw_block(list_block[x + 30 * y][2], x, y) # 画出左击后的图形
list_block[x + 30 * y][3] = 3 # 调用函数,即说明发生左击(即使是电脑自动触发,而非人为操作),更改标记状态为3
else: # 该格子周边雷数不为0,直接显示该格子的周边雷数
draw_block(list_block[index][2], pos_x, pos_y)
list_block[index][3] = 3
count_click = 0
for i in list_block:
if i[3] == 3:
count_click += 1
# 每次触发鼠标左击事件后,都重新统计已经左击点开的格子个数
# 为了后面判断如果点开的格子个数=非雷的格子总数,则游戏胜利
# 扫雷区鼠标右击事件
def mouse_click_right(mouse_x, mouse_y):
global count_bomb
pos_x = (mouse_x - 20) // 30
pos_y = (mouse_y - 120) // 30
index = pos_x + pos_y * 30 # list_block的下标index
status = list_block[index][3]
# 游戏结束,不能再右击
if is_loose:
import tkinter.messagebox
tkinter.messagebox.showinfo('扫雷xp','你死了')
# -*- conding:utf-8 -*-
while True:
# 检查音乐流播放,有返回True,没有返回False
# 如果没有音乐流则选择播放
if pygame.mixer.music.get_busy() == False:
pygame.mixer.music.play()
else:
# 右击,格子显示状态在0-未标记 1-标记雷 2-标记? 三种状态之间循环切换
if status == 0: # 如果此格子当前处于未标记状态
draw_block('mark', pos_x, pos_y)
list_block[index][3] = 1
count_bomb -= 1
elif status == 1:
draw_block('?', pos_x, pos_y)
list_block[index][3] = 2
count_bomb += 1
elif status == 2:
draw_block('blank', pos_x, pos_y)
list_block[index][3] = 0
else:
# 已经左击点开的位置 不能在右击(即status=3的情况)
pass
# 扫雷区鼠标中键事件
def mouse_click_mid(mouse_x, mouse_y):
# 鼠标中键该格子,表示扫描该格子周边相邻格子
# 该格子只有被左击点开(list_block[index][3]==3),且该格子周边雷数(list_block[index][2])=标记雷的总个数
# 中键触发自动左键点开周边非雷的格子(之前没有左击点开过 ),如果之前左击点开过,则跳过
pos_x = (mouse_x - 20) // 30
pos_y = (mouse_y - 120) // 30
index = pos_x + pos_y * 30 # list_block的下标index
status = list_block[index][3]
list_beside = list_side(pos_x, pos_y)
mark_bomb_sum = 0
for item in list_beside:
if item[3] == 1: # item[3]即相当于list_block[index][3] 如果该值为1 表示标记是雷
mark_bomb_sum += 1
if list_block[index][3] == 3 and list_block[index][2] == mark_bomb_sum and list_block[index][2] != 0:
# 该格子只有被左击点开(list_block[index][3]==3),且该格子周边雷数(list_block[index][2])=标记雷的总个数,且不等于0
for item in list_beside:
# 中键触发自动左键点开周边非雷的格子(之前没有左击点开过 ),如果之前左击点开过,则跳过
if item[3] == 3 or item[3] == 1 or item[3] == 2:
# 状态是3,表示左击点开过,状态为1 ,表示标记是雷,状态为2,表示疑问,这三种情况都不能触发鼠标左击事件
continue
else:
mouse_click_left(item[0], item[1])
def main():
global is_loose, list_block, list_block_pos, list_bomb_pos, list_not_bomb, count_click, count_bomb, num_not_bomb, t_start
list_block_pos = [] # 用于存放每个格子的位置坐标信息(x,y)
list_block = [] # 用于存放每个格子的坐标和状态信息,即每个元素=[x,y,0,0]
list_bomb_pos = [] # 用于存放雷的元素的坐标
list_not_bomb = [] # 用于存放不是雷的格子的坐标
count_click = 0 # 用于记录左击点开的格子数量
window_init()
game_status('working')
game_init()
is_loose = False
num_not_bomb = len(list_block_pos) - len(list_bomb_pos)
count_bomb = len(list_bomb_pos)
while True:
bomb_les()
if 0 <= count_click < num_not_bomb and not is_loose:
if count_click == 0:
t_start = int(time.time())
# 在while True循环内,如果count_click=0,表示没有鼠标左击,此时不计时
# 此时,因为while True一直循环,所以会一直刷新t_start的值,不断获取当前时间
# 当count_click从1开始,即一旦发生左击点击,上面if条件 count_click==0不成立,则不会再刷新t_start的值
# t_start的值固定在count_click=1前上一次循环的值,即左击点击之前的最后的一次循环的值,从而实现左击开始计时
t_show = int(time.time()) - t_start
pygame.draw.rect(window, (192, 192, 192), (720, 0, 200, 100), 0)
font_time = pygame.font.SysFont('Microsoft YaHei', 60, bold=True)
text_time = font_time.render(str(t_show), True, (255, 0, 0))
window.blit(text_time, (920 - text_time.get_size()[0], 10)) # 时间靠右显示
pygame.display.update()
else: # 如果游戏成功(count_click = num_not_bomb) 或者游戏失败(is_loose=True) 都停止时间显示
# !/usr/bin/python
# -*- coding: UTF-8 -*-
# Python2.x 导入方法
# Python3.x 导入方法
# from tkinter import *
pass
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
if is_loose:
game_status('loose')
continue
elif count_click < num_not_bomb:
mouse_x, mouse_y = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if 20 <= mouse_x <= 920 and 120 <= mouse_y <= 600:
if click == (True, False, False): # 鼠标左击
mouse_click_left(mouse_x, mouse_y)
elif click == (False, True, False): # 鼠标中键
mouse_click_mid(mouse_x, mouse_y)
elif click == (False, False, True): # 鼠标右键
mouse_click_right(mouse_x, mouse_y)
else: # 当count_click == num_not_bomb,则说明所有非雷的格子全部被点开,游戏胜利
game_status('win')
elif event.type == pygame.MOUSEBUTTONUP:
mouse_x1, mouse_y1 = pygame.mouse.get_pos()
if 0 <= mouse_x1 - (15 * num_w + 20 - 45) <= 90 and 10 <= mouse_y1 <= 100:
# 鼠标在状态图标位置左击点击时,表示游戏开始
return main() # 游戏开始或者游戏重新开始
if __name__ == '__main__':
import tkinter
import PySimpleGUI as sg
count = range(100)
for i, item in enumerate(count):
sg.one_line_progress_meter('扫雷xp', i + 1, len(count), '扫雷xp进度')
# 假设这代码部分需要0.05s
time.sleep(0.000001)
main()
有图和pygame就行
图片:
运行截图: