python用tkinter开发的扫雷游戏


Posted in Python onJune 01, 2021

1.实现效果

python用tkinter开发的扫雷游戏

python用tkinter开发的扫雷游戏

2.实现代码

# 导入所需库
from tkinter import *
import random

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 = ['green',	# 绿色
                          'DodgerBlue',	# 浅蓝色
                          'DarkOrange1',# 橙色
                          'blue',	# 蓝色
                          'red',	# 红色
                          'Chocolate4',	# 棕色
                          'grey',	# 灰色
                          'black']	# 黑色
        
        self.setgui()

    def setgui(self):

        # GUI界面

        self.root = Tk()
        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   # 按钮状态设为点开

        # 获取周围八个雷的坐标
        around = [(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 around:
            # 排除掉在雷区之外的雷
            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:'?'}
        
        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='ி',fg='red')
                
main()

3.另一种精致一点的实现

项目地址

需要导入额外的图片和字体资源,在上面的项目地址里可以下载到

代码

import sys
import time
import random
import pygame
from pygame.locals import *

BLOCK_WIDTH = 30
BLOCK_HEIGHT = 16
# 块大小
SIZE = 20
# 地雷数
MINE_COUNT = 66
# 未点击
normal = 1
# 已点击
opened = 2
# 地雷
mine = 3
# 标记为地雷
flag = 4
# 标记为问号
ask = 5
# 踩中地雷
bomb = 6
# 被双击的周围
hint = 7
# 正被鼠标左右键双击
double = 8
readied = 1,
started = 2,
over = 3,
win = 4

class Mine:
    def __init__(self, x, y, value=0):
        self._x = x
        self._y = y
        self._value = 0
        self._around_mine_count = -1
        self._status = normal
        self.set_value(value)
    def __repr__(self):
        return str(self._value)
    def get_x(self):
        return self._x
    def set_x(self, x):
        self._x = x
    x = property(fget=get_x, fset=set_x)
    def get_y(self):
        return self._y
    def set_y(self, y):
        self._y = y
    y = property(fget=get_y, fset=set_y)
    def get_value(self):
        return self._value
    def set_value(self, value):
        if value:
            self._value = 1
        else:
            self._value = 0
    value = property(fget=get_value, fset=set_value, doc='0:非地雷 1:雷')
    def get_around_mine_count(self):
        return self._around_mine_count
    def set_around_mine_count(self, around_mine_count):
        self._around_mine_count = around_mine_count
    around_mine_count = property(fget=get_around_mine_count, fset=set_around_mine_count, doc='四周地雷数量')
    def get_status(self):
        return self._status
    def set_status(self, value):
        self._status = value
    status = property(fget=get_status, fset=set_status, doc='BlockStatus')

class MineBlock:
    def __init__(self):
        self._block = [[Mine(i, j) for i in range(BLOCK_WIDTH)] for j in range(BLOCK_HEIGHT)]
        # 埋雷
        for i in random.sample(range(BLOCK_WIDTH * BLOCK_HEIGHT), MINE_COUNT):
            self._block[i // BLOCK_WIDTH][i % BLOCK_WIDTH].value = 1
    def get_block(self):
        return self._block
    block = property(fget=get_block)
    def getmine(self, x, y):
        return self._block[y][x]
    def open_mine(self, x, y):
        # 踩到雷了
        if self._block[y][x].value:
            self._block[y][x].status = bomb
            return False
        # 先把状态改为 opened
        self._block[y][x].status = opened
        around = _get_around(x, y)
        _sum = 0
        for i, j in around:
            if self._block[j][i].value:
                _sum += 1
        self._block[y][x].around_mine_count = _sum
        # 如果周围没有雷,那么将周围 8 个未中未点开的递归算一遍
        if _sum == 0:
            for i, j in around:
                if self._block[j][i].around_mine_count == -1:
                    self.open_mine(i, j)
        return True
    def double_mouse_button_down(self, x, y):
        if self._block[y][x].around_mine_count == 0:
            return True
        self._block[y][x].status = double
        around = _get_around(x, y)
        # 周围被标记的雷数量
        sumflag = 0
        for i, j in _get_around(x, y):
            if self._block[j][i].status == flag:
                sumflag += 1
        # 周边的雷已经全部被标记
        result = True
        if sumflag == self._block[y][x].around_mine_count:
            for i, j in around:
                if self._block[j][i].status == normal:
                    if not self.open_mine(i, j):
                        result = False
        else:
            for i, j in around:
                if self._block[j][i].status == normal:
                    self._block[j][i].status = hint
        return result
    def double_mouse_button_up(self, x, y):
        self._block[y][x].status = opened
        for i, j in _get_around(x, y):
            if self._block[j][i].status == hint:
                self._block[j][i].status = normal

# 返回 (x, y) 周围的点坐标
def _get_around(x, y):
    return [(i, j) for i in range(max(0, x - 1), min(BLOCK_WIDTH - 1, x + 1) + 1)
            for j in range(max(0, y - 1), min(BLOCK_HEIGHT - 1, y + 1) + 1) if i != x or j != y]

# 游戏屏幕的宽
SCREEN_WIDTH = BLOCK_WIDTH * SIZE
# 游戏屏幕的高
SCREEN_HEIGHT = (BLOCK_HEIGHT + 2) * SIZE

def print_text(screen, font, x, y, text, fcolor=(255, 255, 255)):
    imgText = font.render(text, True, fcolor)
    screen.blit(imgText, (x, y))

def main():
    pygame.init()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption('扫雷')
    # 得分的字体
    font1 = pygame.font.Font('resources/a.TTF', SIZE * 2)
    fwidth, fheight = font1.size('999')
    red = (200, 40, 40)

    # 加载资源图片,因为资源文件大小不一,所以做了统一的缩放处理
    img0 = pygame.image.load('resources/0.bmp').convert()
    img0 = pygame.transform.smoothscale(img0, (SIZE, SIZE))
    img1 = pygame.image.load('resources/1.bmp').convert()
    img1 = pygame.transform.smoothscale(img1, (SIZE, SIZE))
    img2 = pygame.image.load('resources/2.bmp').convert()
    img2 = pygame.transform.smoothscale(img2, (SIZE, SIZE))
    img3 = pygame.image.load('resources/3.bmp').convert()
    img3 = pygame.transform.smoothscale(img3, (SIZE, SIZE))
    img4 = pygame.image.load('resources/4.bmp').convert()
    img4 = pygame.transform.smoothscale(img4, (SIZE, SIZE))
    img5 = pygame.image.load('resources/5.bmp').convert()
    img5 = pygame.transform.smoothscale(img5, (SIZE, SIZE))
    img6 = pygame.image.load('resources/6.bmp').convert()
    img6 = pygame.transform.smoothscale(img6, (SIZE, SIZE))
    img7 = pygame.image.load('resources/7.bmp').convert()
    img7 = pygame.transform.smoothscale(img7, (SIZE, SIZE))
    img8 = pygame.image.load('resources/8.bmp').convert()
    img8 = pygame.transform.smoothscale(img8, (SIZE, SIZE))
    img_blank = pygame.image.load('resources/blank.bmp').convert()
    img_blank = pygame.transform.smoothscale(img_blank, (SIZE, SIZE))
    img_flag = pygame.image.load('resources/flag.bmp').convert()
    img_flag = pygame.transform.smoothscale(img_flag, (SIZE, SIZE))
    img_ask = pygame.image.load('resources/ask.bmp').convert()
    img_ask = pygame.transform.smoothscale(img_ask, (SIZE, SIZE))
    img_mine = pygame.image.load('resources/mine.bmp').convert()
    img_mine = pygame.transform.smoothscale(img_mine, (SIZE, SIZE))
    img_blood = pygame.image.load('resources/blood.bmp').convert()
    img_blood = pygame.transform.smoothscale(img_blood, (SIZE, SIZE))
    img_error = pygame.image.load('resources/error.bmp').convert()
    img_error = pygame.transform.smoothscale(img_error, (SIZE, SIZE))
    face_size = int(SIZE * 1.25)
    img_face_fail = pygame.image.load('resources/face_fail.bmp').convert()
    img_face_fail = pygame.transform.smoothscale(img_face_fail, (face_size, face_size))
    img_face_normal = pygame.image.load('resources/face_normal.bmp').convert()
    img_face_normal = pygame.transform.smoothscale(img_face_normal, (face_size, face_size))
    img_face_success = pygame.image.load('resources/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 = {0: img0, 1: img1, 2: img2, 3: img3, 4: img4, 5: img5, 6: img6, 7: img7, 8: img8}
    bgcolor = (225, 225, 225)
    block = MineBlock()
    game_status = readied
    # 开始时间
    start_time = None
    # 耗时
    elapsed_time = 0
    while True:
        screen.fill(bgcolor)
        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit()
            elif event.type == MOUSEBUTTONDOWN:
                mouse_x, mouse_y = event.pos
                x = mouse_x // SIZE
                y = mouse_y // SIZE - 2
                b1, b2, b3 = pygame.mouse.get_pressed()
                if game_status == started:
                    # 鼠标左右键同时按下,如果已经标记了所有雷,则打开周围一圈;如果还未标记完所有雷,则有一个周围一圈被同时按下的效果
                    if b1 and b3:
                        mine = block.getmine(x, y)
                        if mine.status == opened:
                            if not block.double_mouse_button_down(x, y):
                                game_status = over
            elif event.type == MOUSEBUTTONUP:
                if y < 0:
                    if face_pos_x <= mouse_x <= face_pos_x + face_size \
                            and face_pos_y <= mouse_y <= face_pos_y + face_size:
                        game_status = readied
                        block = MineBlock()
                        start_time = time.time()
                        elapsed_time = 0
                        continue
                if game_status == readied:
                    game_status = started
                    start_time = time.time()
                    elapsed_time = 0
                if game_status == started:
                    mine = block.getmine(x, y)
                    # 按鼠标左键
                    if b1 and not b3:
                        if mine.status == normal:
                            if not block.open_mine(x, y):
                                game_status = over
                    # 按鼠标右键
                    elif not b1 and b3:
                        if mine.status == normal:
                            mine.status = flag
                        elif mine.status == flag:
                            mine.status = ask
                        elif mine.status == ask:
                            mine.status = normal
                    elif b1 and b3:
                        if mine.status == double:
                            block.double_mouse_button_up(x, y)
        flag_count = 0
        opened_count = 0
        for row in block.block:
            for mine in row:
                pos = (mine.x * SIZE, (mine.y + 2) * SIZE)
                if mine.status == opened:
                    screen.blit(img_dict[mine.around_mine_count], pos)
                    opened_count += 1
                elif mine.status == double:
                    screen.blit(img_dict[mine.around_mine_count], pos)
                elif mine.status == bomb:
                    screen.blit(img_blood, pos)
                elif mine.status == flag:
                    screen.blit(img_flag, pos)
                    flag_count += 1
                elif mine.status == ask:
                    screen.blit(img_ask, pos)
                elif mine.status == hint:
                    screen.blit(img0, pos)
                elif game_status == over and mine.value:
                    screen.blit(img_mine, pos)
                elif mine.value == 0 and mine.status == flag:
                    screen.blit(img_error, pos)
                elif mine.status == normal:
                    screen.blit(img_blank, pos)
        print_text(screen, font1, 30, (SIZE * 2 - fheight) // 2 - 2, '%02d' % (MINE_COUNT - flag_count), red)
        if game_status == started:
            elapsed_time = int(time.time() - start_time)
        print_text(screen, font1, SCREEN_WIDTH - fwidth - 30, (SIZE * 2 - fheight) // 2 - 2, '%03d' % elapsed_time, red)
        if flag_count + opened_count == BLOCK_WIDTH * BLOCK_HEIGHT:
            game_status = win
        if game_status == over:
            screen.blit(img_face_fail, (face_pos_x, face_pos_y))
        elif game_status == win:
            screen.blit(img_face_success, (face_pos_x, face_pos_y))
        else:
            screen.blit(img_face_normal, (face_pos_x, face_pos_y))
        pygame.display.update()

if __name__ == '__main__':
    main()

以上就是python用tkinter开发的扫雷游戏的详细内容,更多关于python 扫雷游戏的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
centos下更新Python版本的步骤
Feb 12 Python
Python的Flask框架应用程序实现使用QQ账号登录的方法
Jun 07 Python
Python找出list中最常出现元素的方法
Jun 14 Python
Python字符串通过'+'和join函数拼接新字符串的性能测试比较
Mar 05 Python
Python3从零开始搭建一个语音对话机器人的实现
Aug 23 Python
Python绘制股票移动均线的实例
Aug 24 Python
使用python脚本自动创建pip.ini配置文件代码实例
Sep 20 Python
简述 Python 的类和对象
Aug 21 Python
python 两种方法修改文件的创建时间、修改时间、访问时间
Sep 26 Python
浅析python函数式编程
Sep 26 Python
浅析Python 中的 WSGI 接口和 WSGI 服务的运行
Dec 09 Python
python批量更改目录名/文件名的方法
Apr 18 Python
Pytorch GPU内存占用很高,但是利用率很低如何解决
Python爬取英雄联盟MSI直播间弹幕并生成词云图
如何判断pytorch是否支持GPU加速
Jun 01 #Python
pytorch 两个GPU同时训练的解决方案
Jun 01 #Python
使用Django实现商城验证码模块的方法
Jun 01 #Python
pytorch通过训练结果的复现设置随机种子
Jun 01 #Python
matplotlib画混淆矩阵与正确率曲线的实例代码
Jun 01 #Python
You might like
千呼万唤始出来,DOTA2勇士令状不朽宝藏Ⅱ现已推出
2020/08/25 DOTA
如何给phpadmin一个保护
2006/10/09 PHP
使用无限生命期Session的方法
2006/10/09 PHP
PHP6 中可能会出现的新特性预览
2014/04/04 PHP
javascript生成/解析dom的CDATA类型的字段的代码
2007/04/22 Javascript
javascript表单验证 - Parsley.js使用和配置
2013/01/25 Javascript
js制作简易年历完整实例
2015/01/28 Javascript
AngularJS实现元素显示和隐藏的几个案例
2015/12/09 Javascript
详解JavaScript基于面向对象之继承实例
2015/12/16 Javascript
JS工作中的小贴士之”闭包“与事件委托的”阻止冒泡“
2016/06/16 Javascript
JS实现用户注册时获取短信验证码和倒计时功能
2016/10/27 Javascript
jquery 判断是否支持Placeholder属性的方法
2017/02/07 Javascript
微信开发之企业付款到银行卡接口开发的示例代码
2018/09/18 Javascript
解决Vue-cli npm run build生产环境打包,本地不能打开的问题
2018/09/20 Javascript
koa router 多文件引入的方法示例
2019/05/22 Javascript
浅谈ECMAScript 中的Array类型
2019/06/10 Javascript
layui switch 开关监听 弹出确定状态转换的例子
2019/09/21 Javascript
jQuery使用jsonp实现百度搜索的示例代码
2020/07/08 jQuery
Vue自动构建发布脚本的方法示例
2020/07/24 Javascript
Python字符编码判断方法分析
2016/07/01 Python
python实现发送邮件功能
2017/07/22 Python
Python实现将doc转化pdf格式文档的方法
2018/01/19 Python
浅析python的优势和不足之处
2018/11/20 Python
Python实现程序判断季节的代码示例
2019/01/28 Python
解决pycharm remote deployment 配置的问题
2019/06/27 Python
python接口调用已训练好的caffe模型测试分类方法
2019/08/26 Python
Python Tkinter模块 GUI 可视化实例
2019/11/20 Python
Python操作Excel工作簿的示例代码(\*.xlsx)
2020/03/23 Python
中国领先的汽车保养服务平台:途虎养车
2019/10/18 全球购物
PHP中如何创建和修改数组
2012/05/02 面试题
short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?
2014/09/26 面试题
餐饮业会计岗位职责
2013/12/19 职场文书
致跳高运动员广播稿
2014/01/13 职场文书
2014办公室年度工作总结
2014/12/09 职场文书
药房管理制度范本
2015/08/06 职场文书
JavaScript实现队列结构过程
2021/12/06 Javascript