# -*- 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()
运行截图: