吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8490|回复: 24
收起左侧

[Python 原创] 【Python】扫雷小游戏(PyQt5)

  [复制链接]
lovingxiaobing 发表于 2019-3-14 20:52


也没啥可介绍哒,扫雷大家都玩过。

雷的分布算法也很简单,就是在雷地图(map:二维数组)中,随机放雷,然后这个雷的8个方位(上下左右、四个对角)的数字(非雷的标记、加一后不为雷的标记)都加一。


如何判断是否踩到雷有很多种方法,只要判断某个控件对应map中的值是否为雷的标记即可,其余的都是PyQt5的控件操作,没啦~~~


雷的分布数据看起来是这样子的:
minedata.png


放上几张截图,还有源代码吧。附件在文章末尾放上.
mine0.png
mine1.png
mine2.png

所有源代码(有点儿多~~~~):
[Python] 纯文本查看 复制代码
#coding: utf-8

__author__ = "小冰|lovingxiaobing"
__email__ = "865741184@qq.com|[email]lovingxiaobing@qq.com[/email]"
__note__ = """
* 扫雷小游戏
* 需要python3.x以上
* 需要安装PyQt5
* pip install PyQt5
"""

import sys

try:
    import PyQt5
except ImportError:
    import tkinter
    from tkinter import messagebox
    err_str = "请安装PyQt5后再打开: pip install PyQt5"
    messagebox.showerror("模块错误!", err_str)
    raise ImportError(err_str)
    sys.exit()


from random import randint
from PyQt5.QtWidgets import \
    QApplication,           \
    QWidget,                \
    QPushButton,            \
    QLCDNumber,             \
    QDesktopWidget,         \
    QMessageBox
from PyQt5.QtCore import Qt


class Mine(object):
    mine = 9
    no_mine = 0
    n_mine = 10
    width = 10
    height = 10

    def __init__(self, width=10, height=10, nMines=10):
        self.map = []
        for _ in range(height):
            t_line = []
            for _ in range(width):
                t_line.append(self.no_mine)
            self.map.append(t_line)
        
        self.width = width
        self.height = height
        self.n_mine = nMines

        self.remix()
    
    # 打乱布局重新随机编排
    def remix(self):

        for y in range(self.height):
            for x in range(self.width):
                self.map[y][x] = self.no_mine

        def add_mark(x, y):
            # 如果不是雷的标记就+1
            if self.map[y][x]+1 < self.mine:
                self.map[y][x] += 1
        
        mine_count = 0

        while mine_count < self.n_mine:
            x = randint(0, self.width-1)
            y = randint(0, self.height-1)

            if self.map[y][x] != self.mine:
                self.map[y][x] = self.mine
                
                mine_count += 1

                # 雷所在的位置的8个方位的数值+1
                ## 上下左右
                if y-1 >= 0: add_mark(x, y-1)
                if y+1 < self.height: add_mark(x, y+1)
                if x-1 >= 0: add_mark(x-1, y)
                if x+1 < self.width: add_mark(x+1, y)
                ## 四个角: 左上角、左下角、右上角、右下角
                if x-1 >= 0 and y-1 >=1: add_mark(x-1, y-1)
                if x-1 >= 0 and y+1 < self.height: add_mark(x-1, y+1)
                if x+1 < self.width and y-1 >= 1: add_mark(x+1, y-1)
                if x+1 < self.width and y+1 < self.height: add_mark(x+1, y+1)
    
    def __getitem__(self, key):
        return self.map[key]

    def __str__(self):
        format_str = ""
        for y in range(self.height):
            format_str += str(self[y]) + "\n"
        return format_str
    __repr__ = __str__

class LCDCounter(QLCDNumber):
    __counter = 0
    def __init__(self, start=0, parent=None):
        super().__init__(4, parent)
        self.setSegmentStyle(QLCDNumber.Flat)
        self.setStyleSheet("background: black; color: red")
        self.counter = start
    
    @property
    def counter(self):
        return self.__counter
    @counter.setter
    def counter(self, value):
        self.__counter = value
        self.display(str(self.__counter))
    
    def inc(self):
        self.counter += 1
    def dec(self):
        self.counter -= 1

class MineButton(QPushButton):
    # 按钮类型
    MINE = Mine.mine        # 雷
    NOTMINE = Mine.no_mine  # 不是雷
    m_type = None

    # 按钮状态
    mark = False    # 是否是标记状态(默认: 未被标记)

    s_flag = '&#9873;'   # 标记
    s_mine = '&#9760;'  # 雷
    s_success = '&#128076;'

    # 按钮是否按下(默认False: 未按下)
    __pushed = False

    # 按钮对应map的位置
    m_x = 0
    m_y = 0

    def __init__(self, map_pos, m_type, parent):
        super().__init__(parent)
        self.m_type = m_type
        self.pushed = False
        self.m_x = map_pos[0]
        self.m_y = map_pos[1]
    
    @property
    def pushed(self):
        return not self.__pushed
    @pushed.setter
    def pushed(self, value):
        self.__pushed = not value
        self.setEnabled(self.__pushed)

    ## 按钮上的鼠标按下事件
    def mousePressEvent(self, e):
        #print("m_x:%d"%self.m_x, "m_y:%d"%self.m_y, "m_type:%d"%self.m_type)

        p = self.parent()
        # 记录鼠标单击次数
        p.nwap_lcd_clicked.counter += 1

        # 左键扫雷
        if e.buttons() == Qt.LeftButton:
            # 踩中雷, 全部雷都翻起来
            if self.m_type == self.MINE:
                for t_line_btn in p.btn_map:
                    for btn in t_line_btn:
                        if btn.m_type == btn.MINE:
                            btn.setText(btn.s_mine)
                        else:
                            if btn.mark != True:
                                if btn.m_type != btn.NOTMINE:
                                    btn.setText(str(btn.m_type))
                        btn.pushed = True
                # 苦逼脸
                p.RestartBtn.setText('&#128547;')
                QMessageBox.critical(self, "失败!", "您不小心踩到了雷! " + self.s_mine)
                return None
            elif self.m_type == self.NOTMINE:
                self.AutoSwap(self.m_x, self.m_y)
            else:
                self.setText(str(self.m_type))
            
            p.mine_counter -= 1
            self.pushed = True
        # 右键添加标记
        elif e.buttons() == Qt.RightButton:
            if self.mark == False:
                self.setText(self.s_flag)
                self.mark = True
            else:
                self.setText("")
                self.mark = False
        
        self.setFocus(False)
    

    ## 当按下的位置是NOTMINE时自动扫雷
    def AutoSwap(self, x, y):
        p = self.parent()
        map_btn = p.btn_map
        
        def lookup(t_line, index):
            # 向左扫描
            i = index
            while i >= 0 and not t_line[i].pushed and t_line[i].m_type != MineButton.MINE:
                if t_line[i].m_type != MineButton.NOTMINE:
                    t_line[i].setText(str(t_line[i].m_type))
                t_line[i].pushed = True
                p.mine_counter -= 1
                p.nwap_lcd_counter.counter = p.mine_counter
                i -= 1
                if t_line[i].m_type != MineButton.NOTMINE:
                    break
            # 向右扫描
            i = index + 1
            while i < p.mine_map.width and not t_line[i].pushed and t_line[i].m_type != MineButton.MINE:
                if t_line[i].m_type != MineButton.NOTMINE:
                    t_line[i].setText(str(t_line[i].m_type))
                t_line[i].pushed = True
                p.mine_counter -= 1
                p.nwap_lcd_counter.counter = p.mine_counter
                i += 1
                if t_line[i].m_type != MineButton.NOTMINE:
                    break
        
        # 向上扫描
        j = y
        while j >= 0:
            lookup(map_btn[j], x)
            j -= 1
        # 向下扫描
        j = y + 1
        while j < p.mine_map.height:
            lookup(map_btn[j], x)
            j += 1
        

        

class MineWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.mine_map = Mine(nMines=16)
        self.InitGUI()
        #print(self.mine_map)
        
    def InitGUI(self):
        
        w_width = 304
        w_height = 344

        self.resize(w_width, w_height)
        self.setFixedSize(self.width(), self.height())
        self.setWindowTitle("扫雷")

        ## 窗口居中于屏幕
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.x(), qr.y())


        l_start_x = 2
        l_start_y = 40
        l_x = l_start_x
        l_y = l_start_y
        l_width = 30
        l_height = 30

        # 雷区按钮
        self.btn_map = []
        for h in range(self.mine_map.height):
            l_x = l_start_x
            self.btn_map.append(list())
            for w in range(self.mine_map.width):
                self.btn_map[h].append(MineButton([w, h], self.mine_map[h][w], self))
                self.btn_map[h][w].resize(l_width, l_height)
                self.btn_map[h][w].move(l_x, l_y)
                self.btn_map[h][w].show()
                l_x += l_width
            l_y += l_height

        r_width = 30
        r_height = 30

        # 恢复按钮
        self.RestartBtn = QPushButton('&#128522;', self)
        self.RestartBtn.clicked.connect(self.restart_btn_event)
        self.RestartBtn.resize(r_width, r_height)
        self.RestartBtn.move((w_width-r_width)//2, 6)

        ## 计数器
        self.__mine_counter = self.mine_map.width * self.mine_map.height - self.mine_map.n_mine

        ## 两个LCD显示控件
        # 操作次数
        self.nwap_lcd_clicked = LCDCounter(0, self)
        self.nwap_lcd_clicked.move(44, 8)

        # 无雷块个数
        self.nwap_lcd_counter = LCDCounter(self.mine_counter, self)
        self.nwap_lcd_counter.move(204, 8)
        
    def restart_btn_event(self):
        self.mine_map.remix()
        #QMessageBox.information(self, "look up", str(self.mine_map))
        for y in range(len(self.btn_map)):
            for x in range(len(self.btn_map[y])):
                self.btn_map[y][x].pushed = False
                self.btn_map[y][x].setText("")
                self.btn_map[y][x].m_type = self.mine_map[y][x]
        
        self.mine_counter = self.mine_map.width * self.mine_map.height - self.mine_map.n_mine
        self.RestartBtn.setText('&#128522;')
        self.nwap_lcd_clicked.counter = 0
        self.nwap_lcd_counter.counter = self.mine_counter
    
    ### 计数器
    @property
    def mine_counter(self):
        return self.__mine_counter
    @mine_counter.setter
    def mine_counter(self, value):
        self.__mine_counter = value
        self.nwap_lcd_counter.dec()
        if self.mine_counter == 0:
            for t_line_btn in self.btn_map:
                for btn in t_line_btn:
                    if btn.m_type == btn.MINE:
                        btn.setText(btn.s_success)
                        btn.pushed = True
            QMessageBox.information(self, "恭喜!", "您成功扫雷! " + MineButton.s_success)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MineWindow()
    w.show()
    sys.exit(app.exec_())




倾国倾城的小姐姐,小哥哥点个赞呗(暗示!!!!!然后再.....)


扫雷.zip (3.08 KB, 下载次数: 127)

免费评分

参与人数 5吾爱币 +9 热心值 +4 收起 理由
Mini小新 + 1 + 1 谢谢@Thanks!
钢铁不败 + 1 + 1 谢谢@Thanks!
时空之外 + 1 + 1 用心讨论,共获提升!
292138 + 1 谢谢@Thanks!
苏紫方璇 + 5 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

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

resu 发表于 2020-3-14 13:04
源码好评!
Traceback (most recent call last):
  File "MineSweeper.pyqt.utf8.py", line 184, in mousePressEvent
    self.AutoSwap(self.m_x, self.m_y)
  File "MineSweeper.pyqt.utf8.py", line 234, in AutoSwap
    lookup(map_btn[j], x)
  File "MineSweeper.pyqt.utf8.py", line 228, in lookup
    if t_line[i].m_type != MineButton.NOTMINE:
IndexError: list index out of range
 楼主| lovingxiaobing 发表于 2019-3-14 21:21
本帖最后由 lovingxiaobing 于 2019-3-14 21:25 编辑
33911628 发表于 2019-3-14 21:10
扫雷在第一次点击时必定不会踩到雷,不知道代码里那一块实现了这个功能

这个(第一次)可能不会踩到雷,你人品非常棒!
在MineButton.mousePressEvent中处理按下"扫雷"按钮的事件,判断处理也在其中
小2b 发表于 2019-3-14 21:10
33911628 发表于 2019-3-14 21:10
扫雷在第一次点击时必定不会踩到雷,不知道代码里那一块实现了这个功能
chaselove 发表于 2019-3-14 21:13
刚刚入门,学习一下
zimao 发表于 2019-3-14 21:17
支持一下。
jiaming123 发表于 2019-3-14 21:24 来自手机
支持一下
xsdwl 发表于 2019-3-14 21:48
支持,虽然刚入门python,继续学习
yyltt 发表于 2019-3-14 21:54
python好像很流行,看来可以了解了解
e23135645631 发表于 2019-3-14 22:15 来自手机
给美化一下看得就舒服了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-17 02:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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