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 相关文章推荐
Django1.3添加app提示模块不存在的解决方法
Aug 26 Python
wxPython定时器wx.Timer简单应用实例
Jun 03 Python
python利用sklearn包编写决策树源代码
Dec 21 Python
Python开启线程,在函数中开线程的实例
Feb 22 Python
python多线程抽象编程模型详解
Mar 20 Python
Pandas中Series和DataFrame的索引实现
Jun 27 Python
Python实现变声器功能(萝莉音御姐音)
Dec 05 Python
Python迭代器模块itertools使用原理解析
Dec 11 Python
服务器端jupyter notebook映射到本地浏览器的操作
Apr 14 Python
django跳转页面传参的实现
Sep 17 Python
Python descriptor(描述符)的实现
Nov 15 Python
只用Python就可以制作的简单词云
Jun 07 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
php获取某个目录大小的代码
2008/09/10 PHP
PHP把数字转成人民币大写的函数分享
2014/06/30 PHP
thinkPHP导出csv文件及用表格输出excel的方法
2015/12/30 PHP
php求数组全排列,元素所有组合的方法
2016/05/05 PHP
PHP实现获取第一个中文首字母并进行排序的方法
2017/05/09 PHP
详解如何在云服务器上部署Laravel
2017/06/30 PHP
Yii框架操作cookie与session的方法实例详解
2019/09/04 PHP
javascript json2 使用方法
2010/03/16 Javascript
jquery.tmpl JQuery模板插件
2011/10/10 Javascript
JS截取字符串常用方法整理及使用示例
2013/10/18 Javascript
解析jquery中的ajax缓存问题
2013/12/19 Javascript
JQuery实现Ajax加载图片的方法
2015/12/24 Javascript
JavaScript中动态向表格添加数据
2017/01/24 Javascript
JavaScript中的this陷阱的最全收集并整理(没有之一)
2017/02/21 Javascript
canvas实现环形进度条效果
2017/03/23 Javascript
用angular实现多选按钮的全选与反选实例代码
2017/05/23 Javascript
spirngmvc js传递复杂json参数到controller的实例
2018/03/29 Javascript
浅谈Redux中间件的实践
2018/07/27 Javascript
[46:44]DOTA2-DPC中国联赛 正赛 Ehome vs PSG.LGD BO3 第二场 3月7日
2021/03/11 DOTA
python 算法 排序实现快速排序
2012/06/05 Python
使用IronPython把Python脚本集成到.NET程序中的教程
2015/03/31 Python
Python实现读取txt文件并转换为excel的方法示例
2018/05/17 Python
将pandas.dataframe的数据写入到文件中的方法
2018/12/07 Python
Python根据欧拉角求旋转矩阵的实例
2019/01/28 Python
Python使用socket_TCP实现小文件下载功能
2020/10/09 Python
Ivory Isle Designs美国/加拿大:婚礼和活动文具公司
2018/08/21 全球购物
StubHub希腊:购买体育赛事、音乐会和剧院门票
2019/08/03 全球购物
意大利奢侈品多品牌集合店:TheDoubleF
2019/08/24 全球购物
大学生关于奋斗的演讲稿
2014/01/09 职场文书
车贷收入证明范本
2014/09/14 职场文书
2014幼儿园中班工作总结
2014/11/10 职场文书
中考学习决心书
2015/02/04 职场文书
感恩母亲节活动总结
2015/02/10 职场文书
体检通知范文
2015/04/21 职场文书
三严三实·严以修身心得体会
2016/01/15 职场文书
演讲开头怎么书写?
2019/08/06 职场文书