Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 78737e2

Browse files
committed
python mazegame code
1 parent 9d9a50e commit 78737e2

File tree

3 files changed

+333
-1
lines changed

3 files changed

+333
-1
lines changed

‎taiyangxue/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Python 代码实例
2-
2+
-[mazegame](https://github.com/JustDoPython/python-examples/tree/master/taiyangxue/mazegame) : 程序员陪孩子,你还可以这么干......
33
- [meta](https://github.com/JustDoPython/python-examples/tree/master/taiyangxue/meta) : 几行代码,撸了个 元宇宙?!
44
- [fake-thread](https://github.com/JustDoPython/python-examples/tree/master/taiyangxue/fake-thread) : Python 多线程居然是 —— 假的?
55
- [logging-train](https://github.com/JustDoPython/python-examples/tree/master/taiyangxue/logging-train) : 神器 logging,你真的了解吗?

‎taiyangxue/mazegame/maze.py‎

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import random
2+
3+
class MazeGen:
4+
def __init__(self, width, height):
5+
self.width = width
6+
self.height = height
7+
self.map = [[0 if x % 2 == 1 and y % 2 == 1 else 1 for x in range(width)] for y in range(height)]
8+
# random.choice([0, height -1]), random.randint(1, width - 2)
9+
# random.randint(1, height -2), random.choice(0, width - 1)
10+
# random.choice([0, 3])
11+
# self.map[1][0] = 0 # 入口
12+
self.entrance = (random.choice([0, height -1]), random.randint(1, width - 2))
13+
self.exit = (random.randint(1, height -2), random.choice([0, width - 1]))
14+
self.map[self.entrance[0]][self.entrance[1]] = 0
15+
self.map[self.exit[0]][self.exit[1]] = 0
16+
self.visited = []
17+
# right up left down
18+
self.dx = [1, 0, -1, 0]
19+
self.dy = [0, -1, 0, 1]
20+
21+
def set_value(self, point, value):
22+
self.map[point[1]][point[0]] = value
23+
24+
def get_value(self, point):
25+
return self.map[point[1]][point[0]]
26+
27+
# 获取坐标(x,y) 的邻居 返回数据结构为:二维数组
28+
def get_neighbor(self, x, y, value):
29+
res = []
30+
for i in range(4):
31+
if 0 < x + self.dx[i] < self.width - 1 and 0 < y + self.dy[i] < self.height - 1 and \
32+
self.get_value([x + self.dx[i], y + self.dy[i]]) == value:
33+
res.append([x + self.dx[i], y + self.dy[i]])
34+
return res
35+
36+
# 获取坐标(x,y) 的邻墙
37+
def get_neighbor_wall(self, point):
38+
return self.get_neighbor(point[0], point[1], 1)
39+
40+
# 获取坐标(x,y) 的邻路
41+
def get_neighbor_road(self, point):
42+
return self.get_neighbor(point[0], point[1], 0)
43+
44+
def deal_with_not_visited(self, point, wall_position, wall_list):
45+
if not [point[0], point[1]] in self.visited:
46+
self.set_value(wall_position, 0)
47+
self.visited.append(point)
48+
wall_list += self.get_neighbor_wall(point)
49+
50+
# generate maze
51+
# https://en.wikipedia.org/wiki/Maze_generation_algorithm
52+
#
53+
# 1、迷宫行和列必须为奇数。
54+
# 2、奇数行和奇数列的交叉点为路,其余点为墙。迷宫四周全是墙。
55+
# 3、选定一个为路的单元格(本例选 [1,1]),然后把它的邻墙放入列表 wall。
56+
# 4、当列表 wall 里还有墙时:
57+
# 4.1、从列表里随机选一面墙,如果这面墙分隔的两个单元格只有一个单元格被访问过
58+
# 3.1.1、那就从列表里移除这面墙,同时把墙打通
59+
# 3.1.2、将单元格标记为已访问
60+
# 3.1.3、将未访问的单元格的的邻墙加入列表 wall
61+
# 4.2、如果这面墙两面的单元格都已经被访问过,那就从列表里移除这面墙
62+
def generate(self):
63+
start = [1, 1]
64+
self.visited.append(start)
65+
wall_list = self.get_neighbor_wall(start)
66+
while wall_list:
67+
wall_position = random.choice(wall_list)
68+
neighbor_road = self.get_neighbor_road(wall_position)
69+
wall_list.remove(wall_position)
70+
self.deal_with_not_visited(neighbor_road[0], wall_position, wall_list)
71+
self.deal_with_not_visited(neighbor_road[1], wall_position, wall_list)
72+
# self.map[self.entrance[0]][self.entrance[1]] = 1
73+
# while True:
74+
# x = random.randint(1, self.height-2)
75+
# y = random.randint(1, self.width-2)
76+
# if self.map[x][y] == 0:
77+
# self.map[x][y] = 2
78+
# break
79+
80+
def is_out_of_index(self, x, y):
81+
return x == 0 or x == self.width - 1 or y == 0 or y == self.height - 1
82+
83+
# dfs
84+
def dfs(self, x, y, path, visited=[]):
85+
# 越界
86+
if self.is_out_of_index(x, y):
87+
return False
88+
89+
# 访问过 or 撞墙
90+
if [x, y] in visited or self.get_value([x, y]) == 1:
91+
return False
92+
93+
visited.append([x, y])
94+
path.append([x, y])
95+
96+
# over
97+
if x == self.width - 2 and y == self.height - 2:
98+
return True
99+
100+
# recursive
101+
for i in range(4):
102+
if 0 < x + self.dx[i] < self.width - 1 and 0 < y + self.dy[i] < self.height - 1 and \
103+
self.get_value([x + self.dx[i], y + self.dy[i]]) == 0:
104+
if self.dfs(x + self.dx[i], y + self.dy[i], path, visited):
105+
return True
106+
elif not self.is_out_of_index(x, y) and path[-1] != [x, y]:
107+
path.append([x, y])
108+
109+
# dfs
110+
def dfs_route(self):
111+
path = []
112+
self.dfs(1, 1, path)
113+
114+
ans = [[0, 1]]
115+
for i in range(len(path)):
116+
ans.append(path[i])
117+
if 0 < i < len(path) - 1 and path[i - 1] == path[i + 1]:
118+
ans.append(path[i])
119+
ans.append([self.width - 1, self.height - 2])
120+
return ans
121+
122+
# bfs
123+
def bfs_route(self):
124+
start = {'x': 0, 'y': 1, 'prev': None}
125+
now = start
126+
q = [start]
127+
visited = [[start['x'], start['y']]]
128+
# 1、从起点出发,获取起点周围所有连通的路
129+
# 2、如果该路没有走过,则加入队列 Q,否则跳过 同时记录其前驱节点
130+
while q:
131+
now = q.pop(0)
132+
# 结束
133+
if now['x'] == self.width - 2 and now['y'] == self.height - 2:
134+
break
135+
roads = self.get_neighbor_road([now['x'], now['y']])
136+
for road in roads:
137+
if not road in visited:
138+
visited.append(road)
139+
q.append({'x': road[0], 'y': road[1], 'prev': now})
140+
141+
ans = []
142+
while now:
143+
ans.insert(0, [now['x'], now['y']])
144+
now = now['prev']
145+
ans.append([width - 1, height - 2])
146+
return ans
147+
148+
# width, height = 37, 21
149+
# my_maze = Maze(width, height)
150+
# my_maze.generate()
151+
# print(my_maze.map)

‎taiyangxue/mazegame/mazegame.py‎

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import time
2+
from turtle import *
3+
4+
from maze import MazeGen
5+
6+
# ENTER = 2
7+
# EXIT = 5
8+
PART_OF_PATH = 0
9+
OBSTACLE = 1
10+
TRIED = 3
11+
DEAD_END = 4
12+
13+
class Maze:
14+
def __init__(self, mazedata, enter, exit) -> None:
15+
rowsInMaze = len(mazedata)
16+
columnsInMaze = len(mazedata[0])
17+
self.enter = enter
18+
self.exit = exit
19+
self.startRow = enter[0]
20+
self.startCol = enter[1]
21+
self.mazelist = mazedata
22+
23+
self.rowsInMaze = rowsInMaze
24+
self.columnsInMaze = columnsInMaze
25+
self.xTranslate = -columnsInMaze/2
26+
self.yTranslate = rowsInMaze/2
27+
self.t = Turtle(shape='turtle')
28+
setup(width=800, height=650)
29+
setworldcoordinates(-(columnsInMaze-1)/2 - 0.5, -(rowsInMaze-1)/2 - 0.5,
30+
(columnsInMaze-1)/2 + 0.5, (rowsInMaze-1)/2 + 0.5)
31+
pass
32+
33+
def drawMaze(self):
34+
tracer(0)
35+
for y in range(self.rowsInMaze):
36+
for x in range(self.columnsInMaze):
37+
if self.mazelist[y][x] == OBSTACLE:
38+
self.drawCenteredBox(x + self.xTranslate, -y + self.yTranslate, 'tan')
39+
40+
self.t.color('black', 'blue')
41+
self.updatePosition(self.startRow, self.startCol)
42+
tracer(1)
43+
44+
def drawCenteredBox(self, x, y, color):
45+
self.t.up()
46+
self.t.goto(x - 0.5, y - 0.5)
47+
self.t.color('black', color)
48+
self.t.setheading(90)
49+
self.t.down()
50+
self.t.begin_fill()
51+
for _ in range(4):
52+
self.t.forward(1)
53+
self.t.right(90)
54+
self.t.end_fill()
55+
update()
56+
57+
58+
def moveTurtle(self, x, y):
59+
self.t.up()
60+
self.t.setheading(self.t.towards(x+self.xTranslate, -y+self.yTranslate))
61+
self.t.goto(x+self.xTranslate, -y+self.yTranslate)
62+
63+
def dropBreadcrumb(self, color):
64+
self.t.dot(color)
65+
66+
def updatePosition(self, row, col, val=None):
67+
if val:
68+
self.mazelist[row][col] = val
69+
self.moveTurtle(col, row)
70+
71+
if val == PART_OF_PATH:
72+
color = 'green'
73+
elif val == OBSTACLE:
74+
color = 'red'
75+
elif val == TRIED:
76+
color = 'black'
77+
elif val == DEAD_END:
78+
color = 'red'
79+
else:
80+
color = None
81+
82+
if color:
83+
self.dropBreadcrumb(color)
84+
85+
def isExit(self, row, col):
86+
return (row, col) == self.exit
87+
# return (row == 0 or row == self.rowsInMaze-1 or
88+
# col == 0 or col == self.columnsInMaze-1)
89+
90+
def __getitem__(self, idx):
91+
try:
92+
return self.mazelist[idx]
93+
except:
94+
return [int(i) for i in '1'*self.columnsInMaze]
95+
96+
def find(maze, startRow, startColumn, searchType):
97+
if searchType == 'es' or searchType == 'e':
98+
return east(maze, startRow, startColumn, searchType) or south(maze, startRow, startColumn, searchType) or \
99+
west(maze, startRow, startColumn, searchType) or north(maze, startRow, startColumn, searchType)
100+
elif searchType == 'en':
101+
return east(maze, startRow, startColumn, searchType) or north(maze, startRow, startColumn, searchType) or \
102+
west(maze, startRow, startColumn, searchType) or south(maze, startRow, startColumn, searchType)
103+
elif searchType == 'wn' or searchType == 'w':
104+
return west(maze, startRow, startColumn, searchType) or north(maze, startRow, startColumn, searchType) or \
105+
east(maze, startRow, startColumn, searchType) or south(maze, startRow, startColumn, searchType)
106+
elif searchType == 'ws':
107+
return west(maze, startRow, startColumn, searchType) or south(maze, startRow, startColumn, searchType) or \
108+
east(maze, startRow, startColumn, searchType) or north(maze, startRow, startColumn, searchType)
109+
elif searchType == 'n':
110+
return north(maze, startRow, startColumn, searchType) or east(maze, startRow, startColumn, searchType) or \
111+
west(maze, startRow, startColumn, searchType) or south(maze, startRow, startColumn, searchType)
112+
elif searchType == 's':
113+
return south(maze, startRow, startColumn, searchType) or east(maze, startRow, startColumn, searchType) or \
114+
west(maze, startRow, startColumn, searchType) or north(maze, startRow, startColumn, searchType)
115+
pass
116+
117+
def east(maze, startRow, startColumn, searchType):
118+
return search(maze, startRow, startColumn+1, searchType)
119+
120+
def south(maze, startRow, startColumn, searchType):
121+
return search(maze, startRow+1, startColumn, searchType)
122+
123+
def west(maze, startRow, startColumn, searchType):
124+
return search(maze, startRow, startColumn-1, searchType)
125+
126+
def north(maze, startRow, startColumn, searchType):
127+
return search(maze, startRow-1, startColumn, searchType)
128+
129+
130+
def search(maze, startRow, startColumn, searchType): # 从指定的点开始搜索
131+
if maze[startRow][startColumn] == OBSTACLE:
132+
return False
133+
if maze[startRow][startColumn] == TRIED:
134+
return False
135+
if maze.isExit(startRow, startColumn):
136+
maze.updatePosition(startRow, startColumn, PART_OF_PATH)
137+
return True
138+
139+
maze.updatePosition(startRow, startColumn, TRIED)
140+
141+
found = find(maze, startRow, startColumn, searchType)
142+
# found = search(maze, startRow, startColumn+1) or \
143+
# search(maze, startRow+1, startColumn) or \
144+
# search(maze, startRow-1, startColumn) or \
145+
# search(maze, startRow, startColumn-1)
146+
147+
148+
if found:
149+
maze.updatePosition(startRow, startColumn, PART_OF_PATH)
150+
else:
151+
maze.updatePosition(startRow, startColumn, DEAD_END)
152+
153+
return found
154+
155+
156+
if __name__ == '__main__':
157+
mg = MazeGen(31, 21)
158+
mg.generate()
159+
mazedata = mg.map
160+
m = Maze(mg.map, mg.entrance, mg.exit)
161+
myWin = m.t.getscreen()
162+
m.drawMaze()
163+
164+
# 计算最近探索方向
165+
searchType = 'es'
166+
if mg.entrance[0]<mg.exit[0] and mg.entrance[1] < mg.exit[1]:
167+
searchType = 'es'
168+
elif mg.entrance[0]<mg.exit[0] and mg.entrance[1] > mg.exit[1]:
169+
searchType = 'ws'
170+
elif mg.entrance[0]>mg.exit[0] and mg.entrance[1] > mg.exit[1]:
171+
searchType = 'wn'
172+
elif mg.entrance[0]>mg.exit[0] and mg.entrance[1] < mg.exit[1]:
173+
searchType = 'en'
174+
elif mg.entrance[0] == mg.exit[0]:
175+
searchType = 'n'
176+
elif mg.entrance[1] == mg.exit[1]:
177+
searchType = 's'
178+
179+
search(m, m.startRow, m.startCol, searchType)
180+
181+
myWin.exitonclick()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /