Skip to content

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)