python实现扫雷小游戏


Posted in Python onApril 24, 2020

前面我们用python实现了贪吃蛇、坦克大战、飞船大战、五子棋等游戏

今天我们用python来实现一下扫雷游戏

本游戏代码量和源文件较多

可以从我的GitHub地址中获取

构建地雷区

import random
from enum import Enum

BLOCK_WIDTH = 30
BLOCK_HEIGHT = 16
SIZE = 20   # 块大小
MINE_COUNT = 99  # 地雷数


class BlockStatus(Enum):
 normal = 1 # 未点击
 opened = 2 # 已点击
 mine = 3 # 地雷
 flag = 4 # 标记为地雷
 ask = 5  # 标记为问号
 bomb = 6 # 踩中地雷
 hint = 7 # 被双击的周围
 double = 8 # 正被鼠标左右键双击


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 = BlockStatus.normal
  self.set_value(value)

 def __repr__(self):
  return str(self._value)
  # return f'({self._x},{self._y})={self._value}, status={self.status}'

 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 = BlockStatus.bomb
   return False

  # 先把状态改为 opened
  self._block[y][x].status = BlockStatus.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 = BlockStatus.double

  around = _get_around(x, y)

  sumflag = 0  # 周围被标记的雷数量
  for i, j in _get_around(x, y):
   if self._block[j][i].status == BlockStatus.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 == BlockStatus.normal:
     if not self.open_mine(i, j):
      result = False
  else:
   for i, j in around:
    if self._block[j][i].status == BlockStatus.normal:
     self._block[j][i].status = BlockStatus.hint
  return result

 def double_mouse_button_up(self, x, y):
  self._block[y][x].status = BlockStatus.opened
  for i, j in _get_around(x, y):
   if self._block[j][i].status == BlockStatus.hint:
    self._block[j][i].status = BlockStatus.normal


def _get_around(x, y):
 """返回(x, y)周围的点的坐标"""
 # 这里注意,range 末尾是开区间,所以要加 1
 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]

主函数

import sys
import time
from enum import Enum
import pygame
from pygame.locals import *
from mineblock import *


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


class GameStatus(Enum):
 readied = 1,
 started = 2,
 over = 3,
 win = 4


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 = GameStatus.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 == GameStatus.started:
     # 鼠标左右键同时按下,如果已经标记了所有雷,则打开周围一圈
     # 如果还未标记完所有雷,则有一个周围一圈被同时按下的效果
     if b1 and b3:
      mine = block.getmine(x, y)
      if mine.status == BlockStatus.opened:
       if not block.double_mouse_button_down(x, y):
        game_status = GameStatus.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 = GameStatus.readied
      block = MineBlock()
      start_time = time.time()
      elapsed_time = 0
      continue

    if game_status == GameStatus.readied:
     game_status = GameStatus.started
     start_time = time.time()
     elapsed_time = 0

    if game_status == GameStatus.started:
     mine = block.getmine(x, y)
     if b1 and not b3:  # 按鼠标左键
      if mine.status == BlockStatus.normal:
       if not block.open_mine(x, y):
        game_status = GameStatus.over
     elif not b1 and b3:  # 按鼠标右键
      if mine.status == BlockStatus.normal:
       mine.status = BlockStatus.flag
      elif mine.status == BlockStatus.flag:
       mine.status = BlockStatus.ask
      elif mine.status == BlockStatus.ask:
       mine.status = BlockStatus.normal
     elif b1 and b3:
      if mine.status == BlockStatus.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 == BlockStatus.opened:
     screen.blit(img_dict[mine.around_mine_count], pos)
     opened_count += 1
    elif mine.status == BlockStatus.double:
     screen.blit(img_dict[mine.around_mine_count], pos)
    elif mine.status == BlockStatus.bomb:
     screen.blit(img_blood, pos)
    elif mine.status == BlockStatus.flag:
     screen.blit(img_flag, pos)
     flag_count += 1
    elif mine.status == BlockStatus.ask:
     screen.blit(img_ask, pos)
    elif mine.status == BlockStatus.hint:
     screen.blit(img0, pos)
    elif game_status == GameStatus.over and mine.value:
     screen.blit(img_mine, pos)
    elif mine.value == 0 and mine.status == BlockStatus.flag:
     screen.blit(img_error, pos)
    elif mine.status == BlockStatus.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 == GameStatus.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 = GameStatus.win

  if game_status == GameStatus.over:
   screen.blit(img_face_fail, (face_pos_x, face_pos_y))
  elif game_status == GameStatus.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实现扫雷小游戏

更多有趣的经典小游戏实现专题,分享给大家:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
用Python给文本创立向量空间模型的教程
Apr 23 Python
python 队列详解及实例代码
Oct 18 Python
微信小程序跳一跳游戏 python脚本跳一跳刷高分技巧
Jan 04 Python
python数据封装json格式数据
Mar 04 Python
numpy实现合并多维矩阵、list的扩展方法
May 08 Python
python中join()方法介绍
Oct 11 Python
对Python之gzip文件读写的方法详解
Feb 08 Python
Python实现决策树并且使用Graphviz可视化的例子
Aug 09 Python
python编写猜数字小游戏
Oct 06 Python
Python类的动态绑定实现原理
Mar 21 Python
Anaconda3中的Jupyter notebook添加目录插件的实现
May 18 Python
彻底解决pip下载pytorch慢的问题方法
Mar 01 Python
jupyter 使用Pillow包显示图像时inline显示方式
Apr 24 #Python
pyspark 随机森林的实现
Apr 24 #Python
Jupyter打开图形界面并画出正弦函数图像实例
Apr 24 #Python
pyspark给dataframe增加新的一列的实现示例
Apr 24 #Python
Pandas将列表(List)转换为数据框(Dataframe)
Apr 24 #Python
python实现飞船大战
Apr 24 #Python
解决安装新版PyQt5、PyQT5-tool后打不开并Designer.exe提示no Qt platform plugin的问题
Apr 24 #Python
You might like
解析php中heredoc的使用方法
2013/06/17 PHP
PHP 裁剪图片
2021/03/09 PHP
使用自定义setTimeout和setInterval使之可以传递参数和对象参数
2009/04/24 Javascript
js滚动条回到顶部的代码
2011/12/06 Javascript
DWZ table的原生分页浅谈
2013/03/01 Javascript
jquery带下拉菜单和焦点图代码分享
2015/08/24 Javascript
JS函数arguments数组获得实际传参数个数的实现方法
2016/05/28 Javascript
JS排序算法之冒泡排序,选择排序与插入排序实例分析
2017/12/13 Javascript
AjaxUpLoad.js实现文件上传功能
2018/03/02 Javascript
webpack4 css打包压缩问题的解决
2018/05/18 Javascript
javascript canvas API内容整理
2020/02/16 Javascript
Python简单的制作图片验证码实例
2017/05/31 Python
Python datetime和unix时间戳之间相互转换的讲解
2019/04/01 Python
Python常用模块os.path之文件及路径操作方法
2019/12/03 Python
pytorch 中的重要模块化接口nn.Module的使用
2020/04/02 Python
python和php哪个容易学
2020/06/19 Python
Python如何实现自带HTTP文件传输服务
2020/07/08 Python
python解包用法详解
2021/02/17 Python
一款简洁的纯css3代码实现的动画导航
2014/10/31 HTML / CSS
HTML5实现简单图片上传所遇到的问题及解决办法
2016/01/20 HTML / CSS
用html5的canvas和JavaScript创建一个绘图程序的简单实例
2016/07/06 HTML / CSS
html5 video全屏播放/自动播放的实现示例
2020/08/06 HTML / CSS
Nili Lotan官网:Nili Lotan同名品牌
2018/01/07 全球购物
接口中的方法可以是abstract的吗
2015/07/23 面试题
Java中会存在内存泄漏吗,请简单描述
2016/12/22 面试题
中文系师范生自荐信
2013/10/01 职场文书
学生会个人自荐书范文
2014/02/12 职场文书
学习雷锋做美德少年寄语大全
2014/04/09 职场文书
工伤赔偿协议书范本
2014/04/15 职场文书
上海世博会口号
2014/06/19 职场文书
旅行社优秀创业计划书
2014/08/16 职场文书
婚宴邀请函
2015/01/30 职场文书
幼师自荐信范文
2015/03/06 职场文书
2015民办小学年度工作总结
2015/05/26 职场文书
Nginx如何配置Http、Https、WS、WSS的方法步骤
2021/05/11 Servers
《Estab Life》4月6日播出 正式PV、主视觉图公开
2022/03/20 日漫