Begin
扫雷是多少人儿时的经典,如今windows不在自带了,大多数人玩的都是网页版扫雷.
这两天不知咋就特别喜欢扫雷,但是这个界面:
这可能是普通人的界面,但这绝不是我的界面!
我的界面应该是这样的:
这才是程序员的扫雷!
那么,开始吧
Second
理清这个程序的原理,主要是以下几点:
- 一个雷盘,里面有的是雷,有的不是雷.
- 获取输入,包含3个输入:要不要标记旗帜,行数,列数
- 如果不标记旗帜,我们要判断行列数对应的方块是不是雷,不是就递归展开周围方块,是雷就结束游戏.标记旗帜就在行列数对应的方块显示旗帜
First
实现第一个比较简单
首先创建一个Mines类,表示方块,有两个属性,是显示在外面给玩家看的,一个是背地里进行处理的.
class Mines:
def __init__(self):
self.show = ''
self.inside = ''
然后创建一个二维列表,每个元素是一个Mines实例(刚开始觉得可以用numpy,还来想了想还是用原生列表吧)
# 使用列表解析式创建,快速且偷懒
m = [[Mines() for i in range(10)] for j in range(10)]
之后再用random库随机在雷盘中放10个雷
ed = [] # 历史记录列表
for i in range(10):
a = random.randint(0, 9)
b = random.randint(0, 9)
if (a, b) not in ed: # 如果这个不是雷
m[a][b].inside = 'x'
ed.append((a, b)) # 添加历史记录
else:
i-=1 # 如果这个是雷,这次循环就不算,再来一次
还有一个非常重要的地方
在雷盘中,如果一个格子边八个格子中有雷,那么这个格子就会显示这八个格子有几个雷
这个好办我们只需要加一个遍历整个二维列表代码,如果这个格子不是雷,就判断这个格子身边有多少雷,然后放在inside属性里就可以了
另外还要判断格子在外面一圈的特殊情况,不然会出发index out of list
具体代码较为啰嗦,所以我用了一点AI:
for i in range(10):
for j in range(10):
if m[i][j].inside == 'x':
continue
elif i == 0 and j == 0:
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif i == 0 and j == 9:
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif i == 9 and j == 0:
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif i == 9 and j == 9:
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif i == 0:
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif i == 9:
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif j == 0:
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif j == 9:
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
else:
if m[i - 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
Second
现在雷盘已经布置好了,接下来就是玩法部分
首先一个主循环:
def display():
'''
打印雷盘
'''
print("\033c") # 清空控制台
print(' \033[32m1 2 3 4 5 6 7 8 9 10\033[0m') # 列号
for i in range(10):
if i == 9:
# 行号10会占两格,为了使界面美观,所以行号为10时,只安排一个空格
print('\033[32m10\033[0m', end = ' ')
else:
# 剩余的俩空格
print('\033[32m' + str(i + 1) + '\033[0m', end = ' ')
for j in range(10):
print(m[i][j].show, end = ' ') #打印雷盘元素
print()
while True:
# 啥也不输入就是False,否则就是True,然后翻转一下供下面if使用
flag = not (bool(input('Do you want to flag a mine? ')))
pos1 = int(input('Enter the row: '))
pos2 = int(input('Enter the column: '))
if flag:
if m[pos1 - 1][pos2 - 1].inside == 'x':
# 失败检测
m[pos1 - 1][pos2 - 1].show = m[pos1 - 1][pos2 - 1].inside
display()
print('You lose!')
break
else:
# 自定义的config函数在后面介绍,作用是将范围内雷盘挖开
config(pos1 - 1, pos2 - 1)
display()
else:
# 如果要标旗,那么将显示出的show属性改为旗帜
m[pos1 - 1][pos2 - 1].show = '▶'
display()
到此,我们离成品仅剩最后一步
在点击时,雷盘会自动展开
展开到有数字显示时停止
这个我们可以看做将show属性改为inside属性,如果inside为0,那么将inside设为空格,再检查更改身边的格子的属性,整个是一个递归的过程,递归边界是inside属性不为0.
这里还有个小坑,我就卡在这里折腾了好久(递归是真的不好调试),当时每一次运行都会出发递归错误,即递归运行的次数超过了限制,然后我不停的更改限制,但是始终不行,最终发现了问题:
我没考虑到会重复递归,直接陷入死循环直至崩溃.
比如下面
这个方块1先自己被挖开,然后让另外一个方块2被挖开
然后另外一个方块2在判断范围里又迫不得已的再判断一遍前面的方块1
然后方块1又判断方块2
方块2接着又判断方块1
…
然后就死循环了
那怎么让他可以不死循环呢?
有办法
我们前面提到,被判断过并且具有可以判断别的方块能力的方块都会被转换为空格
那么我们多加一层判断,看他是不是空格,是空格就不判断不就行了
于是我二次加工代码,这个代码同样啰嗦,我也用了工作区内联AI
def config(pos1, pos2):
if m[pos1][pos2].inside != '0' and m[pos1][pos2].inside != '▶':
m[pos1][pos2].show = m[pos1][pos2].inside
elif m[pos1][pos2].inside == '▶':
pass
else:
m[pos1][pos2].inside = ' '
if pos1 == 0 and pos2 == 0:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 + 1)
config(pos1 + 1, pos2)
config(pos1 + 1, pos2 + 1)
elif pos1 == 0 and pos2 == 9:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 - 1)
config(pos1 + 1, pos2)
config(pos1 + 1, pos2 - 1)
elif pos1 == 9 and pos2 == 0:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 + 1)
config(pos1 - 1, pos2)
config(pos1 - 1, pos2 + 1)
elif pos1 == 9 and pos2 == 9:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 - 1)
config(pos1 - 1, pos2)
config(pos1 - 1, pos2 - 1)
elif pos1 == 0:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 - 1)
config(pos1, pos2 + 1)
config(pos1 + 1, pos2 - 1)
config(pos1 + 1, pos2)
config(pos1 + 1, pos2 + 1)
elif pos1 == 9:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 - 1)
config(pos1, pos2 + 1)
config(pos1 - 1, pos2 - 1)
config(pos1 - 1, pos2)
config(pos1 - 1, pos2 + 1)
elif pos2 == 0:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1 - 1, pos2)
config(pos1 + 1, pos2)
config(pos1 - 1, pos2 + 1)
config(pos1, pos2 + 1)
config(pos1 + 1, pos2 + 1)
elif pos2 == 9:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1 - 1, pos2)
config(pos1 + 1, pos2)
config(pos1 - 1, pos2 - 1)
config(pos1, pos2 - 1)
config(pos1 + 1, pos2 - 1)
else:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1 - 1, pos2 - 1)
config(pos1 - 1, pos2)
config(pos1 - 1, pos2 + 1)
config(pos1, pos2 - 1)
config(pos1, pos2 + 1)
config(pos1 + 1, pos2 - 1)
config(pos1 + 1, pos2)
config(pos1 + 1, pos2 + 1)
齐活了!
Third
我们在这一阶段给他加上一点小细节
比如说输赢检测,开始的时候问候一声之类的
输赢检测
我们定义一个chick()函数
def check():
ret = True # 刚开始是True,以后如果碰到了不符合赢的条件就False
for i in range(10):
for j in range(10):
# 开始看有没有不符合赢的条件
if m[i][j] != 'x':
if m[i][j].show != ' ' and m[i][j].show != '1' and m[i][j].show != '2' and m[i][j].show != '3' and m[i][j].show != '4' and m[i][j].show != '5' and m[i][j].show != '6' and m[i][j].show != '7' and m[i][j].show != '8':
ret = False
break # 一有就退出循环,加快程序步伐,有点类似于短路求值
else:
if m[i][j].show != '▶':
ret = False
break # 同上
return ret # 返回True就是赢了,不然就是False
然后再主循环里加上检测就行
最后在主循环前打印一句话:
print('Welcome to Minesweeper!')
OK!
整体就编好了,你们闲的话可以给这个程序加上GUI
源码如下:
import numpy as np
import random
import sys
sys.setrecursionlimit(2000) # set the maximum depth as 2000
class Mines:
def __init__(self) -> None:
self.show = ' '
self.inside = ' '
def check():
ret = True
for i in range(10):
for j in range(10):
if m[i][j] != 'x':
if m[i][j].show != ' ' and m[i][j].show != '1' and m[i][j].show != '2' and m[i][j].show != '3' and m[i][j].show != '4' and m[i][j].show != '5' and m[i][j].show != '6' and m[i][j].show != '7' and m[i][j].show != '8':
ret = False
break
else:
if m[i][j].show != '▶':
ret = False
break
return ret
def display():
print("\033c")
print(' \033[32m1 2 3 4 5 6 7 8 9 10\033[0m')
for i in range(10):
if i == 9:
print('\033[32m10\033[0m', end = ' ')
else:
print('\033[32m' + str(i + 1) + '\033[0m', end = ' ')
for j in range(10):
print(m[i][j].show, end = ' ')
print()
def config(pos1, pos2):
if m[pos1][pos2].inside != '0' and m[pos1][pos2].inside != '▶':
m[pos1][pos2].show = m[pos1][pos2].inside
elif m[pos1][pos2].inside == '▶':
pass
else:
m[pos1][pos2].inside = ' '
if pos1 == 0 and pos2 == 0:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 + 1)
config(pos1 + 1, pos2)
config(pos1 + 1, pos2 + 1)
elif pos1 == 0 and pos2 == 9:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 - 1)
config(pos1 + 1, pos2)
config(pos1 + 1, pos2 - 1)
elif pos1 == 9 and pos2 == 0:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 + 1)
config(pos1 - 1, pos2)
config(pos1 - 1, pos2 + 1)
elif pos1 == 9 and pos2 == 9:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 - 1)
config(pos1 - 1, pos2)
config(pos1 - 1, pos2 - 1)
elif pos1 == 0:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 - 1)
config(pos1, pos2 + 1)
config(pos1 + 1, pos2 - 1)
config(pos1 + 1, pos2)
config(pos1 + 1, pos2 + 1)
elif pos1 == 9:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1, pos2 - 1)
config(pos1, pos2 + 1)
config(pos1 - 1, pos2 - 1)
config(pos1 - 1, pos2)
config(pos1 - 1, pos2 + 1)
elif pos2 == 0:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1 - 1, pos2)
config(pos1 + 1, pos2)
config(pos1 - 1, pos2 + 1)
config(pos1, pos2 + 1)
config(pos1 + 1, pos2 + 1)
elif pos2 == 9:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1 - 1, pos2)
config(pos1 + 1, pos2)
config(pos1 - 1, pos2 - 1)
config(pos1, pos2 - 1)
config(pos1 + 1, pos2 - 1)
else:
m[pos1][pos2].show = m[pos1][pos2].inside
config(pos1 - 1, pos2 - 1)
config(pos1 - 1, pos2)
config(pos1 - 1, pos2 + 1)
config(pos1, pos2 - 1)
config(pos1, pos2 + 1)
config(pos1 + 1, pos2 - 1)
config(pos1 + 1, pos2)
config(pos1 + 1, pos2 + 1)
def debug():
print(' \033[32m1 2 3 4 5 6 7 8 9 10\033[0m')
for i in range(10):
if i == 9:
print('\033[32m10\033[0m', end = ' ')
else:
print('\033[32m' + str(i + 1) + '\033[0m', end = ' ')
for j in range(10):
print(m[i][j].inside, end = ' ')
print()
ed = []
m = [[Mines() for i in range(10)] for j in range(10)]
for i in range(10):
for j in range(10):
m[i][j].show = '■'
m[i][j].inside = '0'
for i in range(10):
a = random.randint(0, 9)
b = random.randint(0, 9)
if (a, b) not in ed:
m[a][b].inside = 'x'
ed.append((a, b))
else:
i-=1
for i in range(10):
for j in range(10):
if m[i][j].inside == 'x':
continue
elif i == 0 and j == 0:
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif i == 0 and j == 9:
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif i == 9 and j == 0:
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif i == 9 and j == 9:
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif i == 0:
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif i == 9:
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif j == 0:
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
elif j == 9:
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
else:
if m[i - 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i - 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j - 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
if m[i + 1][j + 1].inside == 'x':
m[i][j].inside = str(int(m[i][j].inside) + 1)
print('Welcome to Minesweeper!')
display()
while True:
flag = not (bool(input('Do you want to flag a mine? ')))
pos1 = int(input('Enter the row: '))
pos2 = int(input('Enter the column: '))
if flag:
if m[pos1 - 1][pos2 - 1].inside == 'x':
m[pos1 - 1][pos2 - 1].show = m[pos1 - 1][pos2 - 1].inside
display()
print('You lose!')
quit()
else:
config(pos1 - 1, pos2 - 1)
display()
if check():
break
else:
m[pos1 - 1][pos2 - 1].show = '▶'
display()
if check():
break
print("You win!")
现代码已开源:https://github.com/yuan-deve/PythonGit/blob/main/minesweeper.py
玩玩看怎么样吧: