三滑稽甲苯 发表于 2020-8-30 13:04

闲暇时刻写了一个python模拟扫雷脚本

本帖最后由 三滑稽甲苯 于 2020-9-30 17:00 编辑

首先上代码:
# 导入库
from random import randint as rnd
from os import system
from time import sleep
# 定义MineField类
class MineField():
    def __init__(self, x, y, n):# 初始化
      if x <= 36 and y <=36 and n <x*y:self.x = x; self.y = y; self.n = n# 判断行列数是否满足要求并记录
      else: raise ValueError('Invalid input.')
      self.field = []# 游戏数据存储
      for i in range(x*y): self.field.append({'type': 0, 'status': 0})# 初始化数据
      self.flagged = 0; self.known = 0; self.status = 0
      self.theme = (10*('-'), (' ', '1', '2', '3', '4', '5', '6', '7', '8', '*'), 10*('!'))# 使用theme以便修改呈现效果
      '''
      type                status               self.status
      0   nothing   0       unknown   -1      defeat
      1-8 numbers1       known       0       unknown
      9   mine          2       flagged   1       success
      '''
   
    def surroundings(self, n):# 获得给点方格附近方格
      result = set()
      y = n // self.x; x = n % self.x
      possible = {(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)}
      for d in possible:
            tx = d + x; ty = d + y
            if 0 <= tx < self.x and 0 <= ty < self.y: result.add(ty * self.x + tx)
      return result

    def generate(self):# 生成雷
      mines = set(); count = 0; self.known = 0
      x = self.x; y = self.y
      while count < self.n:
            tmp = rnd(0, x*y)
            while tmp in mines: tmp = rnd(0, x*y)
            mines.add(tmp); count += 1
      # print('Field_gen:', self.field)# debug
      print(mines)
      for mine in mines:
            self.field['type'] = 9
            for surrounding in self.surroundings(mine):
                if self.field['type'] != 9: self.field['type'] += 1# ; print(surrounding,'+= 1')# debug
      
    def show(self):# 呈现
      s = f'{self.flagged}/{self.n}\n '
      index = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
      t = 0
      for i in range(self.x): s += index
      for y in range(self.y):
            s += '\n'; s += index
            for x in range(self.x):
                info = self.field
                s += self.theme]]
                t += 1
      return s
   
    def (self, n):# 打开某未知方格
      t = self.field['type']
      if t == 0:
            self.field['status'] = 1; self.known += 1
            for surrounding in self.surroundings(n):
                if self.field['status'] == 0:
                  self.open_unknown(surrounding)
                  # print('Open #', surrounding)# debug
      elif t == 9: self.field['status'] = 1; self.known += 1; self.status = -1
      else: self.field['status'] = 1; self.known += 1

    def open_known(self, n):# 打开某已知方格附近的方格
      surroundings = self.surroundings(n)
      flagged = 0
      for surrounding in surroundings:
            if self.field['status'] == 2: flagged += 1
      if flagged == self.field['type']:
            for surrounding in surroundings:
                if self.field['status'] == 0: self.open_unknown(surrounding)

    def open(self, n):# 判断情况,调用open_known抑或open_unknown
      t = self.field['status']
      if t == 0: self.open_unknown(n)
      elif t == 1: self.open_known(n)
      else: pass
      if self.known + self.n == self.x * self.y: self.status = 1
    def flag(self, n):# 标记雷
      self.flagged += 1 - self.field['status']
      self.field['status'] = 2 - self.field['status']

def convert(s):# 行列数为字母时转化为数字
    if '0' <= s <= '9': return int(s)
    elif 'a' <= s <= 'z': return ord(s) - ord('a') + 10
    elif 'A' <= s <= 'Z': return ord(s) - ord('A') + 10
    else: raise ValueError('Invalid input.')

m = MineField(15,15,6)# 生成MineField实例(15*15,6颗雷)
m.generate()# 生成雷
while m.status == 0:# 主循环
    system('cls')# 清空界面
    print(m.show())# 显示
    i = input('>')# 输入指令
    cmd = i.split()
    # 处理指令
    if cmd == 'open' or cmd == 'o':
      target = convert(cmd) + convert(cmd) * m.x
      m.open(target)
    elif cmd == 'flag' or cmd == 'f':
      target = convert(cmd) + convert(cmd) * m.x
      m.flag(target)
    elif cmd == 'exit': break
    elif cmd == 'help':
      system('cls')
      print('Help:\nopen: open/o x y\nflag: flag/f x y\nexit: exit')
      input('Press enter to continue...')
    else:
      system('cls')
      print('Invalid!')
      input('Press enter to continue...')
if m.status == 1:# 扫雷成功
    system('cls')
    print(m.show())
    for c in 'You ': print(c, end='', flush=True); sleep(0.15)
    for c in 'WON!': print(c, end='', flush=True); sleep(0.5)
    print()
    input('Press enter to exit...')
elif m.status == -1:# 扫雷失败
    system('cls')
    print(m.show())
    for c in 'You ': print(c, end='', flush=True); sleep(0.15)
    for c in 'LOST!': print(c, end='', flush=True); sleep(0.5)
    print()
    if input('Inspect?(y)') == 'y':
      m.theme = ((' ', '1', '2', '3', '4', '5', '6', '7', '8', '*'), (' ', '1', '2', '3', '4', '5', '6', '7', '8', '*'), (' ', '1', '2', '3', '4', '5', '6', '7', '8', '*'))
      print(m.show())
      input('Press enter to exit...')
注意:行、列不可超过36。
首次使用输入'help'来获取帮助。
欢迎学习,转载请注明出处。
截图:



.py文件(包含视频演示/历史版本):https://www.lanzoux.com/b00zs1ehg   密码:ei8c
github:https://github.com/PRO-2684/MineSweeping
更新日志
8.30 首版
9.12 支持更多的行、列(10->36)
9.30 更详尽的注释

三滑稽甲苯 发表于 2020-8-30 13:57

2079898548 发表于 2020-8-30 13:21
看的我人傻了,我觉得吧,竖排唉太近了,还有-换成*会不会更好看点

*默认是雷{:1_896:}
你可以自行修改p12, 121行theme变量来实现自定义图标显示:victory:

从零学习的歪经小 发表于 2020-8-30 13:21

看的我人傻了,我觉得吧,竖排唉太近了,还有-换成*会不会更好看点{:301_999:}

yimn 发表于 2020-8-30 15:50

感觉好难啊

biaxiaoliong 发表于 2020-8-31 08:42

来学习一下!

最乖小宝宝 发表于 2020-8-31 10:37

前来学习

c03xp 发表于 2020-8-31 12:12

有点意思

灵武 发表于 2020-8-31 16:41

有Python交流群吗?我是新手刚入门,想找一群志同道合的朋友一起学习

luxingyu329 发表于 2020-8-31 21:53

正在学习,还一头雾水的样子

lichen218 发表于 2020-9-29 16:41

感谢分享
页: [1] 2
查看完整版本: 闲暇时刻写了一个python模拟扫雷脚本