扫雷小游戏TK+Pygame结合

起初没看老师要求用Pygame写扫雷,后来发现要用tk写,就简单加了一个tk菜单,设置了三个级别难度,希望有所帮助

其中关于tk和pygame的下载仅对于VS

Ctrl+Alt+L打开解决方案资源管理器+右击Python环境+点击查看所有Python环境+下划线换成PYPI搜索pygame或tkinter,点击运行命令:pip install pygame就可以了

 可参照下图:

                

运行截图:

            

源代码如下:

import sys
import random
import time
from tkinter import  *
import tkinter as tk
import tkinter.messagebox 
import pygame

SIZE = 20
def do_job():
    s='左击方块为雷游戏结束,否则显示相邻8块地雷数,为空相邻无雷\n'+\
    '右击方块标记红旗表示有雷,再右击变?,再右击无标记\n'+\
    '方块显示为n,相邻8块有n块为雷'+\
    '\n标记红旗排除所有雷即胜利。'
    tkinter.messagebox.showinfo(title="帮助",message=s)


def create_random_board(row, col, mine_num):
    """
    得到一个随机的棋盘
    """
    # 随机布雷
    nums = [{"opened": False, "opened_num": 0, 'closed_num': "空"} for _ in range(row * col - mine_num)]  # 16x30-99 表示的是生成381个0
    nums += [{"opened": False, "opened_num": "雷", 'closed_num': "空"} for _ in range(mine_num)]  # 99颗地雷
    random.shuffle(nums)  # 乱序,此时nums是乱的
    return [list(x) for x in zip(*[iter(nums)] * col)]

def click_block(x, y, board_list):
    """
    检测点击的是哪个方格(即第x行,第y列)
    """
    # 计算出点击的空格的行、列
    for row, line in enumerate(board_list):
        for col, _ in enumerate(line):
            if col * SIZE <= x <= (col + 1) * SIZE and (row + 2) * SIZE <= y <= (row + 2 + 1) * SIZE:
                print("点击的空格的位置是:", row, col)
                return row, col

def left_click_block(row, col, board_list,BLOCK_ROW_NUM ,BLOCK_COL_NUM):
    """
    左击空格后的处理
    """
    if board_list[row][col].get("opened") is False and board_list[row][col].get("opened_num") != "雷":
        # 如果不是雷,那么就计算当前位置数字
        mine_num = get_mine_num(row, col, board_list,BLOCK_ROW_NUM,BLOCK_COL_NUM)
        print("地雷数:", mine_num)
        board_list[row][col]["opened_num"] = mine_num
        board_list[row][col]["opened"] = True  # 标记为"打开"状态
        board_list[row][col]["closed_num"] = "空"  # 标记为"未打开时的状态为空格",防止显示剩余雷数错误
        if mine_num == 0:
            # 如果方格周边没有雷此时,判断是否有连续空位置
            set_nums_blank(row, col, board_list,BLOCK_ROW_NUM ,BLOCK_COL_NUM)
    elif board_list[row][col].get("opened") is False and board_list[row][col].get("opened_num") == "雷":
        board_list[row][col]["opened_num"] = "踩雷"  # 标记为"踩雷"图片
        board_list[row][col]["opened"] = True  # 标记为"打开"状态
        board_list[row][col]["closed_num"] = "空"  # 标记为"未打开时的状态为空格",防止显示剩余雷数错误
        return True

def right_click_block(row, col, board_list):
    """
    右击方格后更新其状态(标记为雷、问号?、取消标记)
    """
    if board_list[row][col].get("opened") is False:
        if board_list[row][col]["closed_num"] == "空":
            board_list[row][col]["closed_num"] = "雷标记"
            # return 1  # 表示找到了一颗雷,需要+1
        elif board_list[row][col]["closed_num"] == "雷标记":
            board_list[row][col]["closed_num"] = "疑问标记"
            # return -1  # 表示取消标记雷,需要-1
        elif board_list[row][col]["closed_num"] == "疑问标记":
            board_list[row][col]["closed_num"] = "空"

def get_mine_num(row, col, board_list,BLOCK_ROW_NUM ,BLOCK_COL_NUM):
    """
    计算点击的空格周围的雷的数量
    """
    # 生成起始位置、终止位置
    row_start = row - 1 if row - 1 >= 0 else row
    row_stop = row + 2 if row + 1 <= BLOCK_ROW_NUM - 1 else row + 1
    col_start = col - 1 if col - 1 >= 0 else col
    col_stop = col + 2 if col + 1 <= BLOCK_COL_NUM - 1 else col + 1

    # 循环遍历当前方格周围的雷的数量
    mine_num = 0
    for i in range(row_start, row_stop):
        for j in range(col_start, col_stop):
            if board_list[i][j].get("opened_num") == "雷":
                mine_num += 1
    return mine_num


def set_nums_blank(row, col, board_list,BLOCK_ROW_NUM ,BLOCK_COL_NUM):
    """
    判断当前位置的周边位置是否为空,如果是则继续判断,
    最终能够实现点击一个空位置后连续的空位置都能够显示出来
    """
    mine_num = get_mine_num(row, col, board_list,BLOCK_ROW_NUM ,BLOCK_COL_NUM)
    print("row=%d, col=%d, mine_num=%d" % (row, col, mine_num))
    if mine_num == 0:
        board_list[row][col]['opened'] = True
        board_list[row][col]["opened_num"] = 0
        board_list[row][col]["closed_num"] = "空"
        # 判断对角是否是数字
        for i, j in [(-1, -1), (1, 1), (1, -1), (-1, 1)]:
            if 0 <= row + i <= BLOCK_ROW_NUM-1 and 0 <= col + j <= BLOCK_COL_NUM-1:
                mine_num = get_mine_num(row + i, col + j, board_list,BLOCK_ROW_NUM ,BLOCK_COL_NUM)
                if mine_num:
                    board_list[row + i][col + j]['opened'] = True
                    board_list[row + i][col + j]["opened_num"] = mine_num
                    board_list[row + i][col + j]["closed_num"] = "空"

        # 判断剩下4个位置是否是也是0,即空
        for i, j in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
            if 0 <= row + i <= BLOCK_ROW_NUM-1 and 0 <= col + j <= BLOCK_COL_NUM-1:
                if not board_list[row + i][col + j].get("opened"):
                    set_nums_blank(row + i, col + j, board_list,BLOCK_ROW_NUM ,BLOCK_COL_NUM)
    else:
        board_list[row][col]['opened'] = True
        board_list[row][col]["opened_num"] = mine_num
        board_list[row][col]["closed_num"] = "空"

def get_mine_flag_num(board_list):
    """
    计算还剩多少颗雷
    """
    num = 0
    for line in board_list:
        for num_dict in line:
            if num_dict.get("closed_num") == "雷标记":
                num += 1

    return num

def open_all_mine(board_list):
    """
    显示所有的雷
    """
    for row, line in enumerate(board_list):
        for col, num_dict in enumerate(line):
            if num_dict.get("opened_num") == "雷":
                num_dict["opened"] = True

def judge_win(board_list):
    """
    判断是否获胜
    只要有1个标记为地雷但实际不是地雷的方格,就表示未获胜,否则获胜
    """
    for line in board_list:
        for num_dict in line:
            if num_dict.get("opened_num") == "雷" and num_dict.get("closed_num") != "雷标记":
                return False
    else:
        return True

def run(MINE_COUNT,BLOCK_ROW_NUM,BLOCK_COL_NUM):
    SCREEN_WIDTH, SCREEN_HEIGHT = BLOCK_COL_NUM * SIZE, (BLOCK_ROW_NUM + 2) * SIZE
    a,b,c=MINE_COUNT,BLOCK_ROW_NUM,BLOCK_COL_NUM
    pygame.init()
    pygame.display.set_caption('扫雷')
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    bgcolor = (225, 225, 225)  # 背景色
    
    # 要显示的棋盘
    board_list = create_random_board(BLOCK_ROW_NUM, BLOCK_COL_NUM, MINE_COUNT)

    # 默认的方格图片
    img_blank = pygame.image.load('resource/blank.bmp').convert()
    img_blank = pygame.transform.smoothscale(img_blank, (SIZE, SIZE))
    # "雷标记"图片
    img_mine_flag = pygame.image.load('resource/flag.bmp').convert()
    img_mine_flag = pygame.transform.smoothscale(img_mine_flag, (SIZE, SIZE))
    # "雷"图片
    img_mine = pygame.image.load('resource/mine.bmp').convert()
    img_mine = pygame.transform.smoothscale(img_mine, (SIZE, SIZE))
    # "疑问标记"图片
    img_ask = pygame.image.load('resource/ask.bmp').convert()
    img_ask = pygame.transform.smoothscale(img_ask, (SIZE, SIZE))
    # "踩雷"图片
    img_blood = pygame.image.load('resource/blood.bmp').convert()
    img_blood = pygame.transform.smoothscale(img_blood, (SIZE, SIZE))
    # "表情"图片
    face_size = int(SIZE * 1.25)
    img_face_fail = pygame.image.load('resource/face_fail.bmp').convert()
    img_face_fail = pygame.transform.smoothscale(img_face_fail, (face_size, face_size))
    img_face_normal = pygame.image.load('resource/face_normal.bmp').convert()
    img_face_normal = pygame.transform.smoothscale(img_face_normal, (face_size, face_size))
    img_face_success = pygame.image.load('resource/face_success.bmp').convert()
    img_face_success = pygame.transform.smoothscale(img_face_success, (face_size, face_size))
    # "表情"位置
    face_pos_x = (SCREEN_WIDTH - face_size) // 2
    face_pos_y = (SIZE * 2 - face_size) // 2
    # "雷"的数量图片
    img_dict = {
        '雷标记': img_mine_flag,
        '雷': img_mine,
        '空': img_blank,
        '疑问标记': img_ask,
        '踩雷': img_blood,
    }
    for i in range(9):
        img_temp = pygame.image.load('resource/{}.bmp'.format(i)).convert()
        img_temp = pygame.transform.smoothscale(img_temp, (SIZE, SIZE))
        img_dict[i] = img_temp

    # 游戏状态
    game_status = "normal"
    # 显示雷的数量、耗时用到的资源
    font = pygame.font.Font('resource/a.TTF', SIZE * 2)  # 字体
    f_width, f_height = font.size('999')
    red = (200, 40, 40)
    # 标记出雷的个数
    flag_count = 0
    # 记录耗时
    elapsed_time = 0
    last_time = time.time()
    start_record_time = False

    # 创建计时器(防止while循环过快,占用太多CPU的问题)
    clock = pygame.time.Clock()
    while True:
        # 事件检测(鼠标点击、键盘按下等)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN and event.button:
                b1, b2, b3 = pygame.mouse.get_pressed()
                mouse_click_type = None
                if b1 and not b2 and not b3:  # 左击
                    mouse_click_type = "left"
                elif not b1 and not b2 and b3:  # 右击
                    mouse_click_type = "right"
                print("点击了鼠标的[%s]键" % mouse_click_type)
                x, y = pygame.mouse.get_pos()
                if game_status == "normal" and 2 * SIZE <= y <= SCREEN_HEIGHT:
                    # 计算点击的是哪个空
                    position = click_block(x, y, board_list)
                    if position:
                        if mouse_click_type == "right":
                            # 开始记录耗时
                            start_record_time = True
                            # 如果右击方格,那么就更新其状态
                            right_click_block(*position, board_list)
                            # 更新标记的雷的数量
                            flag_count = get_mine_flag_num(board_list)
                        elif mouse_click_type == "left":
                            # 开始记录耗时
                            start_record_time = True
                            # 点击空格的处理
                            game_over = left_click_block(*position, board_list,BLOCK_ROW_NUM ,BLOCK_COL_NUM)
                            # 更新标记的雷的数量
                            flag_count = get_mine_flag_num(board_list)
                            if game_over:
                                # 将所有雷的位置,标记出来
                                open_all_mine(board_list)
                                # 更改游戏状态
                                game_status = "fail"
                                # 停止记录耗时
                                start_record_time = False
                        # 判断是否获胜
                        win_flag = judge_win(board_list)
                        if win_flag:
                            game_status = "win"
                            # 停止记录耗时
                            start_record_time = False
                elif face_pos_x <= x <= face_pos_x + face_size and face_pos_y <= y <= face_pos_y + face_size:
                     # 重来一局
                        run(a,b,c)

        # 填充背景色
        screen.fill(bgcolor)

        # 显示方格
        for i, line in enumerate(board_list):
            for j, num_dict in enumerate(line):
                if num_dict.get("opened"):
                    screen.blit(img_dict[num_dict.get("opened_num")], (j * SIZE, (i + 2) * SIZE))
                else:
                    screen.blit(img_dict[num_dict.get("closed_num")], (j * SIZE, (i + 2) * SIZE))

        # 显示表情
        if game_status == "win":
            screen.blit(img_face_success, (face_pos_x, face_pos_y))
        elif game_status == "fail":
            screen.blit(img_face_fail, (face_pos_x, face_pos_y))
            
        else:
            screen.blit(img_face_normal, (face_pos_x, face_pos_y))

        # 显示剩余雷的数量
        mine_text = font.render('%02d' % (MINE_COUNT - flag_count), True, red)
        screen.blit(mine_text, (15, (SIZE * 2 - f_height) // 2 - 2))

        # 显示耗时
        if start_record_time and time.time() - last_time >= 1:
            elapsed_time += 1
            last_time = time.time()
        mine_text = font.render('%03d' % elapsed_time, True, red)
        screen.blit(mine_text, (SCREEN_WIDTH - f_width - 10, (SIZE * 2 - f_height) // 2 - 2))

        # 刷新显示(此时窗口才会真正的显示)
        pygame.display.update()
        # FPS(每秒钟显示画面的次数)
        clock.tick(60)  # 通过一定的延时,实现1秒钟能够循环60次
def main1():
    root.destroy()
    run(70,16,30)
def main2():
    root.destroy()
    run(40,16,16)
def main3():
    root.destroy()
    run(10,9,9)

root = tk.Tk()
root.title('扫雷')
root.geometry('160x180+300+250')
   
text = "菜单"
text =tk.Label(root,text=text,font=('微软雅黑',14),padx=10).pack()
playButton1 = tk.Button(root, text='初级难度',command=lambda: main3())
playButton1.pack(fill=X,pady='0px')
playButton2 = tk.Button(root, text='中级难度',command=lambda: main2())
playButton2.pack(fill=X,pady='0px')
playButton3 = tk.Button(root, text='高级难度',command=lambda: main1())
playButton3.pack(fill=X,pady='0px')
playButton3 = tk.Button(root, text='帮助',command=do_job)
playButton3.pack(fill=X,pady='0px')
playButton4 = tk.Button(root, text='退出',command=root.destroy)
playButton4.pack(fill=X,pady='0px')
root.mainloop()

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值