最近在抖音看到有意思的小游戏,如图:
当然,这很简单。但我想的是用python来解决这个问题,且用最暴力的穷举法来解决。
思路如下:
- 先将上图转化为一个列表。黑点用1表示,白点用0表示。并一行一行的取黑点坐标。
dots = [ [1, 1, 1, 1], [1, 1, 0, 1], [0, 1, 1, 1], [1, 1, 1, 1], [0, 1, 1, 0] ] black_dots = [] # 黑点坐标列表 for i in range(5): for j in range(4): point = dots[i][j] if point == 1: black_dots.append((i, j))
- 根据游戏规则,一笔连完所有黑子,不能连白子,不能斜连,其实就是当选定一个起点后,从这个起点的坐标来看,后一个黑子的位置就是只能是x坐标相差1或者y坐标相差1的黑子。即设起点坐标为
,第二个黑子的坐标为
,因此有:
或者:
后面第三个黑子又和第二个满足如上条件,一直到最后一颗黑子,表示全部黑子被一笔连完。
运行结果为:# 先定义判断两坐标代表的黑子有没有斜连的函数 def isdirect(_p1, _p2): _x = abs(_p1[0] - _p2[0]) _y = abs(_p1[1] - _p2[1]) if (_x == 1 and _y == 0) or (_x == 0 and _y == 1): return True else: return False """ 1.根据规则,先从黑点里选一个当作起点. 2.再在剩下的选第二个点,判断第一,第二个点是否满足要求。 3.再在剩下里选第三个点,判断第二,第三个点是否满足要求。 4.重复第二步和第三步,一直到最后一个。 """ """ for d1 in black_dots: # 选择起点 ds1 = [_ for _ in black_dots if _ != d1] # 去掉起点 for d2 in ds1: # 选择第二个点 ds2 = [_ for _ in ds1 if _ != d2] # 去掉第二个点 if isdirect(d1, d2): # 判断起点和第二个点是否斜连 for d3 in ds2: # 选择第三个点 ds3 = [_ for _ in ds2 if _ != d3] # 去掉第三个点 if isdirect(d2, d3): # 判断第二个点和第三个点是否斜连 ... # 重复 """ # 通过观察发现,用递归函数的方法要更清晰一些。 def link(_ds, _d, _r, *args): """ _ds: 点列表。 _d: 上一个点坐标。 _r: 结果列表。 args: _p的记录。 """ args = list(args) args.append(_d) for __d in _ds: __ds = [_ for _ in _ds if _ != __d] if isdirect(_d, __d): if __ds: link(__ds, __d, _r, args) else: args.append(__d) _r.append(args) else: continue # 获取递归结果 result = [] for d1 in black_dots: ds1 = [_ for _ in black_dots if _ != d1] link(ds1, d1, result) for i in result: print(i)
[[[[[[[[[[[[[[[(1, 3)], (0, 3)], (0, 2)], (0, 1)], (0, 0)], (1, 0)], (1, 1)], (2, 1)], (2, 2)], (2, 3)], (3, 3)], (3, 2)], (4, 2)], (4, 1)], (3, 1), (3, 0)] [[[[[[[[[[[[[[[(2, 2)], (2, 1)], (1, 1)], (1, 0)], (0, 0)], (0, 1)], (0, 2)], (0, 3)], (1, 3)], (2, 3)], (3, 3)], (3, 2)], (4, 2)], (4, 1)], (3, 1), (3, 0)] [[[[[[[[[[[[[[[(3, 0)], (3, 1)], (4, 1)], (4, 2)], (3, 2)], (2, 2)], (2, 1)], (1, 1)], (1, 0)], (0, 0)], (0, 1)], (0, 2)], (0, 3)], (1, 3)], (2, 3), (3, 3)] [[[[[[[[[[[[[[[(3, 0)], (3, 1)], (4, 1)], (4, 2)], (3, 2)], (3, 3)], (2, 3)], (1, 3)], (0, 3)], (0, 2)], (0, 1)], (0, 0)], (1, 0)], (1, 1)], (2, 1), (2, 2)] [[[[[[[[[[[[[[[(3, 0)], (3, 1)], (4, 1)], (4, 2)], (3, 2)], (3, 3)], (2, 3)], (2, 2)], (2, 1)], (1, 1)], (1, 0)], (0, 0)], (0, 1)], (0, 2)], (0, 3), (1, 3)] [[[[[[[[[[[[[[[(3, 3)], (2, 3)], (1, 3)], (0, 3)], (0, 2)], (0, 1)], (0, 0)], (1, 0)], (1, 1)], (2, 1)], (2, 2)], (3, 2)], (4, 2)], (4, 1)], (3, 1), (3, 0)]
- 发现结果是多维列表,因此对结果进行清理。
# 对结果进行清理的函数 def process(_r): _rs = [] for _d in _r: if isinstance(_d, list): _rs += process(_d) else: _rs.append(_d) return _rs result = [] for d1 in black_dots: ds1 = [_ for _ in black_dots if _ != d1] link(ds1, d1, result) for i in result: print(process(i))
此时运行结果为:
[(1, 3), (0, 3), (0, 2), (0, 1), (0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (2, 3), (3, 3), (3, 2), (4, 2), (4, 1), (3, 1), (3, 0)]
[(2, 2), (2, 1), (1, 1), (1, 0), (0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (3, 2), (4, 2), (4, 1), (3, 1), (3, 0)]
[(3, 0), (3, 1), (4, 1), (4, 2), (3, 2), (2, 2), (2, 1), (1, 1), (1, 0), (0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3)]
[(3, 0), (3, 1), (4, 1), (4, 2), (3, 2), (3, 3), (2, 3), (1, 3), (0, 3), (0, 2), (0, 1), (0, 0), (1, 0), (1, 1), (2, 1), (2, 2)]
[(3, 0), (3, 1), (4, 1), (4, 2), (3, 2), (3, 3), (2, 3), (2, 2), (2, 1), (1, 1), (1, 0), (0, 0), (0, 1), (0, 2), (0, 3), (1, 3)]
[(3, 3), (2, 3), (1, 3), (0, 3), (0, 2), (0, 1), (0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (3, 2), (4, 2), (4, 1), (3, 1), (3, 0)]
到此,完成一笔连黑子的画法计算。现在利用turtle库来进行可视化。
import turtle as te
row = len(dots) # 行
column = len(dots[0]) # 列
te.setup(1.0, 1.0) # 画布大小
te.colormode(255) # 颜色模式
te.bgcolor((245, 230, 205)) # 画布背景色
te.hideturtle() # 隐藏箭头
dotSize = 20 # 点大小
penSize = 3 # 画笔大小
x_scale = 50 # x缩放比例
y_scale = 50 # y缩放比例
x_bias = -x_scale * column / 2 # x偏置
y_bias = row * y_scale / 2 # y偏置
dot0Color = (255, 255, 255) # 白点
dot1Color = (0, 0, 0) # 黑点
penColor = (255, 0, 0) # 画笔颜色(红色)
for i in range(row):
for j in range(column):
x = x_scale * j + x_bias
y = -y_scale * i + y_bias
if dots[i][j] == 1:
te.penup()
te.setpos(x, y)
te.pendown()
te.dot(dotSize, dot1Color)
elif dots[i][j] == 0:
te.penup()
te.setpos(x, y)
te.pendown()
te.dot(dotSize, dot0Color)
# 画出第一条路径
n = 0
x = result[n][0][1] * x_scale + x_bias
y = -result[n][0][0] * y_scale + y_bias
te.penup()
te.setpos(x, y)
te.pendown()
for p1 in result[n]:
x = p1[1] * x_scale + x_bias
y = -p1[0] * y_scale + y_bias
te.pencolor(penColor)
te.pensize(penSize)
te.goto(x, y)
te.done()
绘图结果:
源代码:
import turtle as te
dots = [
[1, 1, 1, 1],
[1, 1, 0, 1],
[0, 1, 1, 1],
[1, 1, 1, 1],
[0, 1, 1, 0]
]
black_dots = [] # 黑点坐标列表
for i in range(5):
for j in range(4):
point = dots[i][j]
if point == 1:
black_dots.append((i, j))
# 先定义判断两坐标代表的黑子有没有斜连的函数
def isdirect(_p1, _p2):
_x = abs(_p1[0] - _p2[0])
_y = abs(_p1[1] - _p2[1])
if (_x == 1 and _y == 0) or (_x == 0 and _y == 1):
return True
else:
return False
"""
1.根据规则,先从黑点里选一个当作起点.
2.再在剩下的选第二个点,判断第一,第二个点是否满足要求。
3.再在剩下里选第三个点,判断第二,第三个点是否满足要求。
4.重复第二步和第三步,一直到最后一个。
"""
"""
for d1 in black_dots: # 选择起点
ds1 = [_ for _ in black_dots if _ != d1] # 去掉起点
for d2 in ds1: # 选择第二个点
ds2 = [_ for _ in ds1 if _ != d2] # 去掉第二个点
if isdirect(d1, d2): # 判断起点和第二个点是否斜连
for d3 in ds2: # 选择第三个点
ds3 = [_ for _ in ds2 if _ != d3] # 去掉第三个点
if isdirect(d2, d3): # 判断第二个点和第三个点是否斜连
... # 重复
"""
# 通过观察发现,用递归函数的方法要更清晰一些。
def link(_ds, _d, _r, *args):
"""
_ds: 点列表。
_d: 上一个点坐标。
_r: 结果列表。
args: _p的记录。
"""
args = list(args)
args.append(_d)
for __d in _ds:
__ds = [_ for _ in _ds if _ != __d]
if isdirect(_d, __d):
if __ds:
link(__ds, __d, _r, args)
else:
args.append(__d)
_r.append(args)
else:
continue
def process(_r):
_rs = []
for _d in _r:
if isinstance(_d, list):
_rs += process(_d)
else:
_rs.append(_d)
return _rs
result = []
for d1 in black_dots:
ds1 = [_ for _ in black_dots if _ != d1]
link(ds1, d1, result)
for i in result:
result = [process(r) for r in result]
row = len(dots) # 行
column = len(dots[0]) # 列
te.setup(1.0, 1.0) # 画布大小
te.colormode(255) # 颜色模式
te.bgcolor((245, 230, 205)) # 画布背景色
te.hideturtle() # 隐藏箭头
dotSize = 20 # 点大小
penSize = 3 # 画笔大小
x_scale = 50 # x缩放比例
y_scale = 50 # y缩放比例
x_bias = -x_scale * column / 2 # x偏置
y_bias = row * y_scale / 2 # y偏置
dot0Color = (255, 255, 255) # 白点
dot1Color = (0, 0, 0) # 黑点
penColor = (255, 0, 0) # 画笔颜色(红色)
for i in range(row):
for j in range(column):
x = x_scale * j + x_bias
y = -y_scale * i + y_bias
if dots[i][j] == 1:
te.penup()
te.setpos(x, y)
te.pendown()
te.dot(dotSize, dot1Color)
elif dots[i][j] == 0:
te.penup()
te.setpos(x, y)
te.pendown()
te.dot(dotSize, dot0Color)
# 画出第一条路径
n = 0
x = result[n][0][1] * x_scale + x_bias
y = -result[n][0][0] * y_scale + y_bias
te.penup()
te.setpos(x, y)
te.pendown()
for p1 in result[n]:
x = p1[1] * x_scale + x_bias
y = -p1[0] * y_scale + y_bias
te.pencolor(penColor)
te.pensize(penSize)
te.goto(x, y)
te.done()