吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 992|回复: 29
收起左侧

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

[复制链接]
Eks6666 发表于 2025-4-5 13:38
[Python] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# 导入所需库
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] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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-5-18 19:19

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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