Skip to content

Commit 8ea5260

Browse files
committed
add code
1 parent 707fb3e commit 8ea5260

File tree

2 files changed

+224
-1
lines changed

2 files changed

+224
-1
lines changed

doudou/2020-06-12-maze/maze.py

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import random
2+
import pyxel
3+
4+
5+
# Python 3.6.9+
6+
#
7+
# Win:
8+
# pip install -U pyxel
9+
# ************************
10+
# Mac
11+
# 1、brew install python3 sdl2 sdl2_image
12+
# 2、restart the terminal
13+
# 3、pip3 install -U pyxel
14+
# ************************
15+
# 「S」键开启游戏
16+
# https://github.com/kitao/pyxel/blob/master/README.cn.md
17+
18+
class Maze:
19+
def __init__(self, width, height):
20+
self.width = width
21+
self.height = height
22+
self.map = [[0 if x % 2 == 1 and y % 2 == 1 else 1 for x in range(width)] for y in range(height)]
23+
self.map[1][0] = 0 # 入口
24+
self.map[height - 2][width - 1] = 0 # 出口
25+
self.visited = []
26+
# right up left down
27+
self.dx = [1, 0, -1, 0]
28+
self.dy = [0, -1, 0, 1]
29+
30+
def set_value(self, point, value):
31+
self.map[point[1]][point[0]] = value
32+
33+
def get_value(self, point):
34+
return self.map[point[1]][point[0]]
35+
36+
# 获取坐标(x,y) 的邻居 返回数据结构为:二维数组
37+
def get_neighbor(self, x, y, value):
38+
res = []
39+
for i in range(4):
40+
if 0 < x + self.dx[i] < self.width - 1 and 0 < y + self.dy[i] < self.height - 1 and \
41+
self.get_value([x + self.dx[i], y + self.dy[i]]) == value:
42+
res.append([x + self.dx[i], y + self.dy[i]])
43+
return res
44+
45+
# 获取坐标(x,y) 的邻墙
46+
def get_neighbor_wall(self, point):
47+
return self.get_neighbor(point[0], point[1], 1)
48+
49+
# 获取坐标(x,y) 的邻路
50+
def get_neighbor_road(self, point):
51+
return self.get_neighbor(point[0], point[1], 0)
52+
53+
def deal_with_not_visited(self, point, wall_position, wall_list):
54+
if not [point[0], point[1]] in self.visited:
55+
self.set_value(wall_position, 0)
56+
self.visited.append(point)
57+
wall_list += self.get_neighbor_wall(point)
58+
59+
# generate maze
60+
# https://en.wikipedia.org/wiki/Maze_generation_algorithm
61+
#
62+
# 1、迷宫行和列必须为奇数。
63+
# 2、奇数行和奇数列的交叉点为路,其余点为墙。迷宫四周全是墙。
64+
# 3、选定一个为路的单元格(本例选 [1,1]),然后把它的邻墙放入列表 wall。
65+
# 4、当列表 wall 里还有墙时:
66+
# 4.1、从列表里随机选一面墙,如果这面墙分隔的两个单元格只有一个单元格被访问过
67+
# 3.1.1、那就从列表里移除这面墙,同时把墙打通
68+
# 3.1.2、将单元格标记为已访问
69+
# 3.1.3、将未访问的单元格的的邻墙加入列表 wall
70+
# 4.2、如果这面墙两面的单元格都已经被访问过,那就从列表里移除这面墙
71+
def generate(self):
72+
start = [1, 1]
73+
self.visited.append(start)
74+
wall_list = self.get_neighbor_wall(start)
75+
while wall_list:
76+
wall_position = random.choice(wall_list)
77+
neighbor_road = self.get_neighbor_road(wall_position)
78+
wall_list.remove(wall_position)
79+
self.deal_with_not_visited(neighbor_road[0], wall_position, wall_list)
80+
self.deal_with_not_visited(neighbor_road[1], wall_position, wall_list)
81+
82+
def is_out_of_index(self, x, y):
83+
return x == 0 or x == self.width - 1 or y == 0 or y == self.height - 1
84+
85+
# dfs
86+
def dfs(self, x, y, path, visited=[]):
87+
# 越界
88+
if self.is_out_of_index(x, y):
89+
return False
90+
91+
# 访问过 or 撞墙
92+
if [x, y] in visited or self.get_value([x, y]) == 1:
93+
return False
94+
95+
visited.append([x, y])
96+
path.append([x, y])
97+
98+
# over
99+
if x == self.width - 2 and y == self.height - 2:
100+
return True
101+
102+
# recursive
103+
for i in range(4):
104+
if 0 < x + self.dx[i] < self.width - 1 and 0 < y + self.dy[i] < self.height - 1 and \
105+
self.get_value([x + self.dx[i], y + self.dy[i]]) == 0:
106+
if self.dfs(x + self.dx[i], y + self.dy[i], path, visited):
107+
return True
108+
elif not self.is_out_of_index(x, y) and path[-1] != [x, y]:
109+
path.append([x, y])
110+
111+
# dfs
112+
def dfs_route(self):
113+
path = []
114+
self.dfs(1, 1, path)
115+
116+
ans = [[0, 1]]
117+
for i in range(len(path)):
118+
ans.append(path[i])
119+
if 0 < i < len(path) - 1 and path[i - 1] == path[i + 1]:
120+
ans.append(path[i])
121+
ans.append([width - 1, height - 2])
122+
return ans
123+
124+
# bfs
125+
def bfs_route(self):
126+
start = {'x': 0, 'y': 1, 'prev': None}
127+
now = start
128+
q = [start]
129+
visited = [[start['x'], start['y']]]
130+
# 1、从起点出发,获取起点周围所有连通的路
131+
# 2、如果该路没有走过,则加入队列 Q,否则跳过 同时记录其前驱节点
132+
while q:
133+
now = q.pop(0)
134+
# 结束
135+
if now['x'] == self.width - 2 and now['y'] == self.height - 2:
136+
break
137+
roads = my_maze.get_neighbor_road([now['x'], now['y']])
138+
for road in roads:
139+
if not road in visited:
140+
visited.append(road)
141+
q.append({'x': road[0], 'y': road[1], 'prev': now})
142+
143+
ans = []
144+
while now:
145+
ans.insert(0, [now['x'], now['y']])
146+
now = now['prev']
147+
ans.append([width - 1, height - 2])
148+
return ans
149+
150+
151+
pixel = 5
152+
width, height = 37, 21
153+
road_color, wall_color = 7, 13
154+
start_point_color, end_point_color, = 11, 11
155+
head_color, route_color, backtrack_color = 9, 11, 8
156+
157+
my_maze = Maze(width, height)
158+
my_maze.generate()
159+
160+
161+
class App:
162+
def __init__(self):
163+
#pyxel.init(width * pixel, height * pixel, caption='maze', border_width=10, border_color=0xFFFFFF)
164+
pyxel.init(width * pixel, height * pixel)
165+
self.death = True
166+
self.index = 0
167+
self.route = []
168+
self.step = 1 # 步长,数值越小速度越快,1:每次一格; 10:每次 1/10 格
169+
self.color = start_point_color
170+
self.bfs_route = my_maze.bfs_route()
171+
self.dfs_route = my_maze.dfs_route()
172+
self.dfs_model = True
173+
pyxel.run(self.update, self.draw)
174+
175+
def update(self):
176+
if pyxel.btn(pyxel.KEY_Q):
177+
pyxel.quit()
178+
179+
if pyxel.btn(pyxel.KEY_S):
180+
self.death = False
181+
182+
if not self.death:
183+
self.check_death()
184+
self.update_route()
185+
186+
def draw(self):
187+
# draw maze
188+
for x in range(height):
189+
for y in range(width):
190+
color = road_color if my_maze.map[x][y] is 0 else wall_color
191+
pyxel.rect(y * pixel, x * pixel, pixel, pixel, color)
192+
pyxel.rect(0, pixel, pixel, pixel, start_point_color)
193+
pyxel.rect((width - 1) * pixel, (height - 2) * pixel, pixel, pixel, end_point_color)
194+
195+
if self.index > 0:
196+
# draw route
197+
offset = pixel / 2
198+
for i in range(len(self.route) - 1):
199+
curr = self.route[i]
200+
next = self.route[i + 1]
201+
self.color = backtrack_color if curr in self.route[:i] and next in self.route[:i] else route_color
202+
pyxel.line(curr[0] + offset, (curr[1] + offset), next[0] + offset, next[1] + offset, self.color)
203+
pyxel.circ(self.route[-1][0] + 2, self.route[-1][1] + 2, 1, head_color)
204+
205+
def check_death(self):
206+
if self.dfs_model and len(self.route) == len(self.dfs_route) - 1:
207+
self.death = True
208+
elif not self.dfs_model and len(self.route) == len(self.bfs_route) - 1:
209+
self.death = True
210+
211+
def update_route(self):
212+
index = int(self.index / self.step)
213+
self.index += 1
214+
if index == len(self.route): # move
215+
if self.dfs_model:
216+
self.route.append([pixel * self.dfs_route[index][0], pixel * self.dfs_route[index][1]])
217+
else:
218+
self.route.append([pixel * self.bfs_route[index][0], pixel * self.bfs_route[index][1]])
219+
220+
221+
App()

doudou/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ Python技术 公众号文章代码库
1717
+ [520](https://github.com/JustDoPython/python-examples/tree/master/doudou/2020-05-17-520):Python 教你花式表白小姐姐
1818

1919
+ [字符画](https://github.com/JustDoPython/python-examples/tree/master/doudou/2020-05-17-character-drawing):字符画
20+
21+
+ [迷宫](https://github.com/JustDoPython/python-examples/tree/master/doudou/2020-06-12-maze):迷宫
2022
---
2123

2224
从小白到工程师的学习之路。
2325

24-
关注公众号:python 技术,回复"python"一起学习交流。
26+
关注公众号:python 技术,回复python一起学习交流。
2527

2628
![](http://favorites.ren/assets/images/python.jpg)

0 commit comments

Comments
 (0)