import random
import time
class SaoLei:
m: int
n: int
k: int
mode: int
coordinate_list = []
game_space: list[list[int or str]]
def __init__(self, m: int, n: int, k: int, mode: int):
"""
初始化游戏
:param m: m行, m >= 8
:param n: n列, n >= 8
:param k: k个雷, 10 <= k <= m*n*0.3
:param mode: 0:手动模式,1:全自动模式 2:半自动模式
"""
if m >= 8:
self.m = m
else:
print("row Error!")
exit(0)
if n >= 8:
self.n = n
else:
print("col Error!")
exit(0)
if 10 <= k <= m * n * 0.3:
self.k = k
self.flag = self.k
else:
print("k Error!")
exit(0)
if mode in (0, 1, 2):
self.mode = mode
else:
print("Mode Error!")
exit(0)
# 生成区域
self.game_space = [[0 for _ in range(n)] for _ in range(m)]
print("game_space create success!")
# 随机雷的位置
i = 0
while i < self.k:
a = random.randint(0, m - 1)
b = random.randint(0, n - 1)
if self.game_space[a][b] != '*':
i += 1
# 产生数据
self.game_space[a][b] = '*'
c = [-1, 0, 1]
for x in c:
if 0 <= a + x < m:
for y in c:
if 0 <= b + y < n:
if self.game_space[a + x][b + y] != '*':
self.game_space[a + x][b + y] += 1
# 调用游戏进程
self.game_window()
def game_window(self):
show_list = [['■' for _ in range(self.n)] for _ in range(self.m)]
text = ''
while True:
# 输出画面
for i in range(10):
print()
print(text)
print("+++" * self.n, end='+\n')
print(' │ ', end='')
for k in range(1, self.n + 1):
print('%d' % (k % 10), end=' ')
print('\n─┼─', end='')
print('───' * (self.n - 1), end='─\n')
k = 1
for i in range(self.m):
print(k, end='│ ')
for j in range(self.n):
print(show_list[i][j], end=' ')
print()
k = (k + 1) % 10
if text == 'Game Over!':
exit(0)
text = ''
# 检测是否结束
row = self.m
for i in show_list:
if '■' not in i:
row -= 1
if row == 0:
print('Victory!')
exit(1)
# 输入坐标及操作
if self.mode == 0:
a, b, c = self.input_set()
else:
a, b, c = self.automatic_input(show_list)
# 进行处理
if c == 0:
if self.flag > 0:
if show_list[a][b] == '■':
show_list[a][b] = '□'
self.flag -= 1
elif show_list[a][b] == '□':
show_list[a][b] = '■'
self.flag += 1
else:
text = 'Error! Is number'
else:
text = 'Error! No flag'
elif c == 1:
if show_list[a][b] == '■':
if self.game_space[a][b] == '*':
show_list[a][b] = '*'
text = 'Game Over!'
elif self.game_space[a][b] == 0:
show_list[a][b] = self.game_space[a][b]
self.look_zero(a, b, show_list)
else:
show_list[a][b] = self.game_space[a][b]
else:
text = 'Error! No Click'
elif c == 2:
if show_list[a][b] == '■':
show_list[a][b] = '?'
elif show_list[a][b] == '?':
show_list[a][b] = '■'
else:
text = 'Error! Open'
def look_zero(self, a: int, b: int, show_list):
for i in range(a - 1, a + 2):
for j in range(b - 1, b + 2):
if 0 <= i < self.m and 0 <= j < self.n:
if show_list[i][j] == '■':
if self.game_space[i][j] == 0:
show_list[i][j] = 0
self.look_zero(i, j, show_list)
else:
show_list[i][j] = self.game_space[i][j]
def input_set(self):
a = int(input("输入行:"))
b = int(input("输入列:"))
c = int(input("操作(0:插旗; 1:点击; 2:标记):"))
if not (0 < a <= self.m and 0 < b <= self.n and c in (0, 1, 2)):
a, b, c = self.input_set()
a += 1
b += 1
return a - 1, b - 1, c
def automatic_input(self, show_list: list[list]
):
"""
自动化方法
:param show_list:
:return:a,b,c
"""
if self.coordinate_list:
a = self.coordinate_list.pop()
print("选定位置【%d, %d】, 操作为:%d" % (a[0] + 1, a[1] + 1, a[2]))
# time.sleep(0.5)
return a[0], a[1], a[2]
for i in range(self.m):
for j in range(self.n):
if type(show_list[i][j]) is not str and show_list[i][j] > 0:
z = show_list[i][j]
for x in range(i - 1, i + 2):
for y in range(j - 1, j + 2):
if 0 <= x < self.m and 0 <= y < self.n:
if show_list[x][y] == '□':
z -= 1
elif show_list[x][y] == '■':
self.coordinate_list.append([x, y])
if z == 0:
for p in range(len(self.coordinate_list)):
self.coordinate_list[p].append(1)
elif z == len(self.coordinate_list):
for p in range(len(self.coordinate_list)):
self.coordinate_list[p].append(0)
else:
self.coordinate_list.clear()
if self.coordinate_list:
a = self.coordinate_list.pop()
print("选定位置【%d, %d】, 操作为:%d" % (a[0] + 1, a[1] + 1, a[2]))
# time.sleep(0.5)
return a[0], a[1], a[2]
# 进入高级计算
print("调用高级计算处理,请等候...")
self.advanced_automatic_input(show_list)
if self.coordinate_list:
print("计算完成!生成操作:")
for mmm in self.coordinate_list:
print("\t坐标【%d,%d】,操作:%d" % (mmm[0] + 1, mmm[1] + 1, mmm[2]))
print("========================")
a = self.coordinate_list.pop()
print("选定位置【%d, %d】, 操作为:%d" % (a[0] + 1, a[1] + 1, a[2]))
# time.sleep(0.5)
return a[0], a[1], a[2]
# 无法确定,随机选择位置
elif self.mode == 1:
a = random.randint(0, self.m - 1)
b = random.randint(0, self.n - 1)
while show_list[a][b] != '■':
a = random.randint(0, self.m - 1)
b = random.randint(0, self.n - 1)
else:
print("无法确定位置,随机选择【%d, %d】点开" % (a + 1, b + 1))
# time.sleep(2)
return a, b, 1
else:
print("无法确定位置,请手动输入。 提示:还有【%d】个💣" % self.flag)
return self.input_set()
def advanced_automatic_input(self, show_list: list[list]
):
processed_list = []
set_dic = dict()
for i in show_list:
processed_list.append(i.copy())
# 初步处理
for i in range(self.m):
for j in range(self.n):
if type(processed_list[i][j]) is int and processed_list[i][j] > 0:
c = set()
for p in range(i - 1, i + 2):
for q in range(j - 1, j + 2):
if 0 <= p < self.m and 0 <= q < self.n:
if processed_list[p][q] == '□':
processed_list[i][j] -= 1
elif processed_list[p][q] == '■':
c.add((p, q))
if c:
set_dic[tuple(c)] = processed_list[i][j]
# 寻找子集
key_list = list(set_dic.keys())
for i in range(len(key_list)):
for j in range(i + 1, len(key_list)):
if len(key_list[i]) < len(key_list[j]):
p = i
q = j
elif len(key_list[i]) > len(key_list[j]):
p = j
q = i
else:
continue
if set(key_list[p]).issubset(set(key_list[q])):
d = list(set(key_list[q]) - set(key_list[p]))
if set_dic[key_list[q]] - set_dic[key_list[p]] == 0:
for k in d:
e = list(k)
e.append(1)
self.coordinate_list.append(e)
elif set_dic[key_list[q]] - set_dic[key_list[p]] == len(d):
for k in d:
e = list(k)
e.append(0)
self.coordinate_list.append(e)
# 传参【 m行,n列, k个雷, mode:0、手动; 1、全自动; 2、半自动】,可自定义
# 简单 8 8 10
# 一般 16 16 40
# 困难 16 32 99
game = SaoLei(16, 32, 99, 2)
闲来无事,打开虚拟机上的扫雷玩了玩,觉得自己计算很浪费时间,还容易遗漏,就做了个自动扫雷。
简单模式下很容易通关,困难的就看脸了,感兴趣的可以拿去运行一下。
自动化处理核心代码段在 168~273行。
========================
次日,发现自动扫雷算法并不完整,上次的代码仅对单个数字周围进行判断。
但在一些情况下,单个数字无法判断,要综合一片小区域确定某些方块是否一定是炸弹,或者一定安全。
暂且称为高级算法。(并不是算法有多高级,本质上还是取集合(区域),进行大量的判断,难在复杂判断的逻辑关系以及集合、字典的操作上)。
通过半天的归纳总结,找到规律后开始设计代码。
本次修改还优化了输出格式,使得在大区域下更容易确定方块的坐标。
【代码中一些重复的地方可以提取出来作为单独的方法,通过改变参数位置来实现相同的功能,让代码看上去更精简】
【但长时间的修改代码,随着变量、变量类型、数据结构嵌套和逻辑关系的不断增加,有点被搞得头晕了】
【所以既然运行上没有错误,多次试验也没有发现毛病,就不去管它了。说不定也是靠着什么bug运行起来了呢】
【事实上最初写出来的高级算法代码还多了一个子模块,这个模块在一次高级算法结束之后进行进一步处理】
【在设计算法的时候,考虑到这样能减少遍历游戏窗口的次数,加快运行速度,并且可以确定更多的更复杂的坐标及操作】
【虽然写好代码之后第一次运行全自动困难模式顺利通关,但在后来的几次测试中总会错把炸弹点开】
【而且计算出的坐标我实在是看不懂根据什么条件算出来的,修改多次无果,想着是不是算法最初处理的时候就是错误的】
【把这段代码注释掉之后,多次测试,没有任何错误,而且计算出来的坐标和操作都是正确的!计算不出来的时候,我看着也是无法确定哪个方块安全】
【这种情况下只能靠运气随机选择。所以上文说到,说不定是靠着什么bug运行起来的呢。】