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基于动态规划算法计算单词距离
Jul 25 Python
Python 搭建Web站点之Web服务器网关接口
Nov 06 Python
Python常见工厂函数用法示例
Mar 21 Python
Win8下python3.5.1安装教程
Jul 29 Python
python 通过 socket 发送文件的实例代码
Aug 14 Python
对python xlrd读取datetime类型数据的方法详解
Dec 26 Python
numpy和pandas中数组的合并、拉直和重塑实例
Jun 28 Python
Python实现初始化不同的变量类型为空值
Jun 02 Python
Python如何对齐字符串
Jul 30 Python
解决PyCharm IDE环境下,执行unittest不生成测试报告的问题
Sep 03 Python
Python 3.9的到来到底是意味着什么
Oct 14 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
simplehtmldom Doc api帮助文档
2012/03/26 PHP
PHP中CURL方法curl_setopt()函数的参数分享
2013/01/19 PHP
有关PHP性能优化的介绍
2013/06/20 PHP
php gethostbyname获取域名ip地址函数详解
2010/01/24 Javascript
js修改input的type属性及浏览器兼容问题探讨与解决
2013/01/23 Javascript
jQuery层级选择器用法分析
2015/02/10 Javascript
比较常见的javascript中定义函数的区别
2015/11/09 Javascript
基于jQuery实现左右图片轮播(原理通用)
2015/12/24 Javascript
原生JavaScript制作计算器
2016/10/16 Javascript
Javascript中的getter和setter初识
2017/08/17 Javascript
vue.js实现的绑定class操作示例
2018/07/06 Javascript
使用ECharts实现状态区间图
2018/10/25 Javascript
关于NodeJS中的循环引用详解
2019/07/23 NodeJs
ES6之Proxy的get方法详解
2019/10/11 Javascript
antd Select下拉菜单动态添加option里的内容操作
2020/11/02 Javascript
微信小程序弹窗禁止页面滚动的实现代码
2020/12/30 Javascript
python实现class对象转换成json/字典的方法
2016/03/11 Python
举例讲解Python面向对象编程中类的继承
2016/06/17 Python
Python代码缩进和测试模块示例详解
2018/05/07 Python
python使用Matplotlib画饼图
2018/09/25 Python
在PyCharm中实现关闭一个死循环程序的方法
2018/11/29 Python
python无限生成不重复(字母,数字,字符)组合的方法
2018/12/04 Python
解决在pycharm中显示额外的 figure 窗口问题
2019/01/15 Python
使用Python操作FTP实现上传和下载的方法
2019/04/01 Python
PyQt5的安装配置过程,将ui文件转为py文件后显示窗口的实例
2019/06/19 Python
Python学习笔记之函数的定义和作用域实例详解
2019/08/13 Python
如何使用Pytorch搭建模型
2020/10/26 Python
Farfetch台湾官网:奢侈品牌时尚购物平台
2019/06/17 全球购物
MIKI HOUSE美国官方网上商店:日本领先的婴儿和儿童高级时装品牌
2020/06/21 全球购物
请解释接口的显式实现有什么意义
2012/05/26 面试题
英文版网络工程师求职信
2013/10/28 职场文书
物流专业大学生的自我鉴定
2013/11/13 职场文书
行政部岗位职责范本
2014/03/13 职场文书
教师工作决心书
2015/02/04 职场文书
还在手动盖楼抽奖?教你用Python实现自动评论盖楼抽奖(一)
2021/06/07 Python
MySQL 1130异常,无法远程登录解决方案详解
2021/08/23 MySQL