maze_python/SourceCollector.py
2025-06-30 21:05:34 +08:00

248 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import csv
from collections import deque
class TreeNode:
def __init__(self):
self.fa = None
self.children = []
self.pos = None
self.final_pos = None
self.val = 0
self.id = 0
self.dp = 0
class SourceCollector:
def __init__(self, filename=None, maze=None):
self.filename = filename
self.maze = maze
self.start_pos = None
self.end_pos = None
self.path = []
self.node_path = []
self.boss_pos = None
self.lock_pos = None
if self.filename:
self.maze = []
with open(f"{self.filename}",'r') as f:
reader = csv.reader(f)
for idx,row in enumerate(reader):
t = []
for idy,i in enumerate(row):
if i.startswith('b'):
t.append('0')
self.boss_pos = (idx,idy)
elif i.startswith('l'):
t.append('0')
self.lock_pos = (idx,idy)
else:
t.append(i)
self.maze.append(t)
else:
self.maze = maze
self.rowNums = len(self.maze)
self.colNums = len(self.maze[0])
for i in range(self.rowNums):
for j in range(self.colNums):
if self.maze[i][j] =='s':
self.start_pos = (i,j)
if self.maze[i][j] =='e':
self.end_pos = (i,j)
def dfs_show(self,u):
if u.id != 0:
print(f"id: {u.id} , fa:{u.fa.id} , val:{u.val} , pos:{u.pos}")
else:
print(f"id: {u.id} , val:{u.val} , pos:{u.pos}")
for child in u.children:
self.dfs_show(child)
def build_a_tree(self):
cnt = 0
root = TreeNode()
root.pos = self.start_pos
root.id = 0
root.val = 0
root.fa = None
queue = deque([(self.start_pos[0], self.start_pos[1], root)])
st = [[False] * self.colNums for _ in range(self.rowNums)]
st[self.start_pos[0]][self.start_pos[1]] = True
dx = [-1, 0, 1, 0]
dy = [0, -1, 0, 1]
while queue:
x, y, parent = queue.popleft()
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if self.outofmap(nx, ny) or st[nx][ny]:
continue
if self.maze[nx][ny] != '1':
st[nx][ny] = True
new_node = TreeNode()
new_node.pos = (nx, ny)
new_node.fa = parent
cnt+=1
new_node.id = cnt
if self.maze[nx][ny].startswith('g'):
new_node.val = int(self.maze[nx][ny][1:])
elif self.maze[nx][ny].startswith('t'):
new_node.val =-1 *int(self.maze[nx][ny][1:])
parent.children.append(new_node)
queue.append((nx, ny, new_node))
return root
def outofmap(self,x,y):
return x < 0 or y < 0 or x >= self.rowNums or y >= self.colNums
def getlca(self,u, v):
def get_path_to_root(node):
path = []
while node:
path.append(node)
node = node.fa
return path
path_u = get_path_to_root(u)
path_v = get_path_to_root(v)
path_u.reverse()
path_v.reverse()
lca = None
for i in range(min(len(path_u),len(path_v))):
if path_u[i] == path_v[i]:
lca = path_u[i]
else:
break
if lca is None:
return []
u_to_lca = []
node = u
while node != lca:
u_to_lca.append(node)
node = node.fa
lca_to_v = []
node_list = []
node = v
while node != lca:
node_list.append(node)
node = node.fa
node_list.append(lca)
node_list.reverse()
for node in node_list:
lca_to_v.append(node)
full_path = u_to_lca + lca_to_v[:-1]
return full_path
def dfs(self, sn):
sn.dp = sn.val
sn.final_pos = sn.pos
sn.path = [sn]
# 先对子节点全部深搜一遍,收集路径长度
children = sn.children[:]
for child in children:
self.dfs(child)
if self.colNums < 11:
children.sort(key=lambda c: len(c.path))
cur = None
for idx, child in enumerate(children):
if child.dp > 0:
sn.dp += child.dp
if cur is not None:
sn.path.extend(self.getlca(sn.path[-1], child))
sn.path.extend(child.path)
cur = child
sn.final_pos = cur.final_pos
def get_path(self):
return self.path
def bfs_path(self, start, end):
"""从start到end的最短路径含首尾"""
from collections import deque
n, m = self.rowNums, self.colNums
visited = [[False]*m for _ in range(n)]
prev = [[None]*m for _ in range(n)]
q = deque([start])
visited[start[0]][start[1]] = True
dx = [-1, 0, 1, 0]
dy = [0, -1, 0, 1]
while q:
x, y = q.popleft()
if (x, y) == end:
break
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if 0 <= nx < n and 0 <= ny < m and not visited[nx][ny]:
if self.maze[nx][ny] != '1':
visited[nx][ny] = True
prev[nx][ny] = (x, y)
q.append((nx, ny))
# 回溯路径
path = []
cur = end
while cur and cur != start:
path.append(cur)
cur = prev[cur[0]][cur[1]]
if cur == start:
path.append(start)
path.reverse()
return path
return []
def run(self):
sn = self.build_a_tree()
# self.dfs_show(sn)
self.dfs(sn)
self.path =[_.pos for _ in sn.path]
for idx,item in enumerate(self.path):
if idx > 0:
if item == self.path[idx-1]:
del self.path[idx]
self.path.extend(self.bfs_path(self.path[-1],self.end_pos))
def output_list(self):
copy_maze = self.maze
for idx, (y, x) in enumerate(self.path):
if copy_maze[y][x].startswith('s') | copy_maze[y][x].startswith('e'):
continue
if copy_maze[y][x].startswith('g') | copy_maze[y][x].startswith('t'):
copy_maze[y][x] = f"{copy_maze[y][x]}p{idx}"
continue
copy_maze[y][x] = f"p{idx}"
return copy_maze
if __name__ == '__main__':
obj = SourceCollector(filename="maze.csv")
obj.run()
path = obj.get_path()
for i in path:
print(i)
# print(sn.pos)
# pre = sn.pos
# for _ in sn.dp_path:
# dx,dy = _[0] - pre[0],_[1]-pre[1]
# if dx > 0:
# print("down")
# elif dx < 0:
# print("up")
# elif dy > 0:
# print("right")
# elif dy < 0:
# print("left")
# pre = _