吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1164|回复: 28
收起左侧

[Python 原创] 用python制作经典扫雷游戏

[复制链接]
Eks6666 发表于 2025-4-5 13:38
[Python] 纯文本查看 复制代码
# 导入所需库
from tkinter import *
import random
import sys
import time


class main:
    # 定义一个类,继承 tkinter 的 Button
    # 用来保存按钮的状态和在网格布局中的位置
    class minebtn(Button):
        def __init__(self, master, xy, **kw):
            Button.__init__(self, master, **kw)
            self.xy = xy
            self._state = 0
            # 状态
            # 0: 未点开
            # 1: 已点开
            # 2: 标记
            # 3: 问号

    def __init__(self):
        # 定义规格及雷数
        self.width = 9
        self.height = 9
        self.minenum = 10
        # Windows 7 默认的三种规格和雷数
        # 9*9,10
        # 16*16,40
        # 16*32,99

        self.rest = self.minenum  # 剩余未标记的雷

        # 雷数的颜色
        self.colorlist = ['blue',  # 蓝色
                          'DodgerBlue',  # 浅蓝色
                          'DarkOrange1',  # 橙色
                          'green',  # 绿色
                          'red',  # 红色
                          'Chocolate4',  # 棕色
                          'grey',  # 灰色
                          'black']  # 黑色

        self.setgui()

    def setgui(self):

        # GUI界面

        self.root = Tk()
        self.root.geometry('200x200+100+100')
        self.root.title('扫雷')

        self.restlabel = Label(self.root, text=f'剩余:{self.minenum}')
        self.restlabel.grid(row=0, column=0, columnspan=3)

        self.mineplace = random.sample(range(self.width * self.height), self.minenum)  # 随机抽取雷
        self.mineplace = [(x % self.width, x // self.height) for x in self.mineplace]  # 将雷的序号转变为坐标

        self.mines = {}

        for y in range(self.height):
            for x in range(self.width):
                self.mines[(x, y)] = self.minebtn(self.root, xy=(x, y), font=('黑体', 8, 'bold'), width=2, bd=1,
                                                  relief='ridge')
                self.mines[(x, y)].bind('<ButtonRelease-1>', lambda event: self._open(event.widget))  # 左键单击点开
                self.mines[(x, y)].bind('<ButtonRelease-3>', lambda event: self.make(event.widget))  # 右键单击事件
                self.mines[(x, y)].grid(row=y + 1, column=x, sticky='nswe')

        self.root.mainloop()

    # 点开
    def _open(self, widget):
        xy = widget.xy
        x = xy[0]
        y = xy[1]  # 获取当前按钮的坐标

        # 如果是雷则显示全部雷的位置
        if widget.xy in self.mineplace:
            self.showmine()
            return

        # 如果已经点开了就什么也不做
        if widget._state == 1:
            return

        widget.configure(relief='flat', bg='white')  # 更改当前按钮的样式

        widget._state = 1  # 按钮状态设为点开

        # 获取周围八个雷的坐标
        near = [(x - 1, y - 1),
                (x, y - 1),
                (x + 1, y - 1),
                (x - 1, y),
                (x + 1, y),
                (x - 1, y + 1),
                (x, y + 1), 
                (x + 1, y + 1)]

        _sum = 0
        around = []

        for o, p in near:
            # 排除掉在雷区之外的雷
            if 0 <= o <= self.width - 1 and 0 <= p <= self.height - 1:
                around.append((o, p))

                # 计算周围的雷数
                if self.mines[(o, p)].xy in self.mineplace:
                    _sum += 1

        # 如果周围没有雷则打开周围未标记的雷,直到有雷为止
        if _sum == 0:
            widget['text'] = ''

            for i, j in around:
                if self.mines[(i, j)]._state == 0:
                    self._open(self.mines[(i, j)])
        else:
            widget['text'] = _sum  # 显示雷数

            # 对应数字设置对应颜色
            widget['fg'] = self.colorlist[_sum - 1]

    # 右键单击设置标记/问号
    def make(self, widget):
        string = {0: '', 2: '♀', 3: '?'}  # 修改为gif

        if widget._state == 0:
            widget._state = 2
            widget['text'] = string[2]
            self.rest -= 1
            self.restlabel['text'] = f'剩余:{self.rest}'

        elif widget._state == 2:
            widget._state = 3
            widget['text'] = string[3]
            self.rest += 1
            self.restlabel['text'] = f'剩余:{self.rest}'

        elif widget._state == 3:
            widget._state = 0
            widget['text'] = string[0]

    # 如果踩到雷,显示所有的雷
    def showmine(self):
        for i, j in self.mineplace:
            self.mines[(i, j)].configure(text='&#128165;', fg='red')  # 修改为gif
            time.sleep(2)
            self.window = Tk()
            self.window.geometry('200x200+100+100')
            self.btshow = Button(self.window, text='你输了', bg='white')
            self.btshow.pack()
            self.window.mainloop()


main()

免费评分

参与人数 3吾爱币 +8 热心值 +3 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
HAPPYCHICK + 1 + 1 谢谢@Thanks!
pyjiujiu + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

wudalang123 发表于 2025-4-6 09:45
改进了一下代码:
[Python] 纯文本查看 复制代码
from tkinter import *
from tkinter import messagebox
import random

class Minesweeper:
    class MineButton(Button):
        def __init__(self, master, xy, **kw):
            super().__init__(master, **kw)
            self.xy = xy
            self.is_mine = False
            self.state = 0  # 0:未开 1:已开 2:旗帜 3:问号
            self.neighbor_mines = 0

    def __init__(self, width=9, height=9, mines=10):
        self.width = width
        self.height = height
        self.total_mines = mines
        self.flags = mines
        self.game_active = True
        
        self.root = Tk()
        self.root.title('扫雷')
        self.setup_ui()
        self.setup_mines()
        self.root.mainloop()

    def setup_ui(self):
        self.flag_label = Label(self.root, text=f'剩余: {self.flags}')
        self.flag_label.grid(row=0, column=0, columnspan=self.width)
        
        self.buttons = {}
        for y in range(self.height):
            for x in range(self.width):
                btn = self.MineButton(
                    self.root, (x, y),
                    width=2, font=('Arial', 10, 'bold'),
                    relief='raised',
                    bg='lightgray'
                )
                btn.bind('<Button-1>', lambda e, b=btn: self.left_click(b))
                btn.bind('<Button-3>', lambda e, b=btn: self.right_click(b))
                btn.grid(row=y+1, column=x)
                self.buttons[(x, y)] = btn

    def setup_mines(self):
        positions = random.sample(
            [pos for pos in self.buttons],
            self.total_mines
        )
        for pos in positions:
            self.buttons[pos].is_mine = True
            
        for pos in self.buttons:
            self.calculate_neighbors(pos)

    def calculate_neighbors(self, pos):
        x, y = pos
        count = 0
        for dx in (-1, 0, 1):
            for dy in (-1, 0, 1):
                if dx == 0 and dy == 0:
                    continue
                nx, ny = x + dx, y + dy
                if (nx, ny) in self.buttons and self.buttons[(nx, ny)].is_mine:
                    count += 1
        self.buttons[pos].neighbor_mines = count

    def left_click(self, button):
        if not self.game_active or button.state != 0:
            return
            
        if button.is_mine:
            self.game_over(False)
            return
            
        self.reveal(button.xy)
        self.check_victory()

    def right_click(self, button):
        if not self.game_active or button.state == 1:
            return
            
        states = {0: ('&#128681;', 2), 2: ('?', 3), 3: ('', 0)}
        current = button.state
        button.config(text=states[current][0])
        button.state = states[current][1]
        
        if current == 0: self.flags -= 1
        elif current == 2: self.flags += 1
        self.flag_label.config(text=f'剩余: {self.flags}')
        self.check_victory()

    def reveal(self, pos, visited=None):
        if visited is None:
            visited = set()
        if pos in visited:
            return
            
        visited.add(pos)
        button = self.buttons[pos]
        button.config(
            relief='sunken',
            bg='white',
            state='disabled'
        )
        button.state = 1
        
        if button.neighbor_mines > 0:
            colors = ['blue', 'green', 'red', 'navy', 'brown', 'cyan', 'black', 'gray']
            button.config(
                text=str(button.neighbor_mines),
                fg=colors[button.neighbor_mines-1]
            )
            return
            
        x, y = pos
        for dx in (-1, 0, 1):
            for dy in (-1, 0, 1):
                nx, ny = x + dx, y + dy
                neighbor = (nx, ny)
                if neighbor in self.buttons and self.buttons[neighbor].state == 0:
                    self.reveal(neighbor, visited)

    def check_victory(self):
        correct_flags = sum(
            1 for pos in self.buttons 
            if self.buttons[pos].state == 2 and self.buttons[pos].is_mine
        )
        if correct_flags == self.total_mines and self.flags == 0:
            self.game_over(True)

    def game_over(self, victory):
        self.game_active = False
        for pos in self.buttons:
            btn = self.buttons[pos]
            if btn.is_mine:
                btn.config(text='&#128163;', bg='orange' if not victory else 'lightgreen')
        
        msg = '恭喜,你赢了!' if victory else '游戏结束,你输了!'
        messagebox.showinfo('游戏结束', msg)
        self.root.destroy()

if __name__ == '__main__':
    Minesweeper()



主要改进点:
  • 游戏逻辑优化
  • 使用面向对象方式重构代码结构
  • 添加胜利条件判断(正确标记所有地雷)
  • 增加游戏状态管理(game_active)
  • 界面优化
  • 使用更直观的旗帜和炸弹图标(&#128681;&#128163;)
  • 添加格子禁用状态
  • 改进颜色方案和视觉效果
  • 功能增强
  • 添加自动展开空白区域功能
  • 支持问号标记功能
  • 正确处理右键标记状态循环
  • 错误修复
  • 修复HTML实体显示问题
  • 移除阻塞式sleep调用
  • 使用Toplevel替代多个Tk实例
  • 正确处理递归展开时的边界情况
  • 用户体验改进
  • 添加游戏结束弹窗提示
  • 胜利时标记正确雷区为绿色
  • 失败时高亮显示错误标记
  • 代码结构优化
  • 分离游戏逻辑与界面代码
  • 使用更合理的变量命名
  • 添加必要的注释说明
这个改进版本实现了经典扫雷游戏的核心功能,包括:
  • 左键点击打开格子
  • 右键循环标记旗帜/问号
  • 自动展开空白区域
  • 雷区显示
  • 胜利/失败判断
  • 实时剩余雷数显示
玩家可以通过以下方式操作:
  • 左键单击:打开格子
  • 右键单击:循环标记旗帜→问号→取消标记
  • 当打开安全格子时自动展开相邻空白区域
  • 正确标记所有地雷后自动判定胜利

ak47u571 发表于 2025-4-5 15:44
Hunter52 发表于 2025-4-5 16:02
dysunb 发表于 2025-4-5 16:12
现在还有人玩扫雷吗,windows都不是标配了
mw26973 发表于 2025-4-5 16:14
牛逼大佬,学习一下
mytomsummer 发表于 2025-4-5 16:41
感谢分享开源
lxj128474 发表于 2025-4-5 17:04
佬,学习一下
Cyam 发表于 2025-4-5 17:18
mac和windows右键事件居然不一样,mac上ButtonRelease-3要改成ButtonRelease-2
joythinker 发表于 2025-4-5 17:19
学习了,哈哈
pyjiujiu 发表于 2025-4-5 17:34
挺有意思,只不过没有赢的状态提示,只有"你输了"...

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-7-8 17:15

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表