399 lines
14 KiB
Python
399 lines
14 KiB
Python
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
|
||
# 添加资源收集统计
|
||
self.collected_resources = [] # 收集的资源详情
|
||
self.total_coins = 0 # 金币总价值
|
||
self.total_traps = 0 # 陷阱总损失
|
||
self.total_value = 0 # 总价值(金币-陷阱)
|
||
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)
|
||
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))
|
||
|
||
# 计算并打印资源总结
|
||
self.print_resource_summary()
|
||
|
||
|
||
|
||
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
|
||
|
||
def calculate_resources(self):
|
||
"""计算路径上收集到的资源总量"""
|
||
self.collected_resources = []
|
||
self.total_coins = 0
|
||
self.total_traps = 0
|
||
self.total_value = 0
|
||
|
||
visited_positions = set() # 避免重复计算同一位置的资源
|
||
|
||
for step, (y, x) in enumerate(self.path):
|
||
if (y, x) in visited_positions:
|
||
continue
|
||
|
||
visited_positions.add((y, x))
|
||
cell = self.maze[y][x]
|
||
|
||
if cell.startswith('g'): # 金币
|
||
try:
|
||
coin_value = int(cell[1:])
|
||
self.total_coins += coin_value
|
||
self.total_value += coin_value
|
||
self.collected_resources.append({
|
||
'type': 'coin',
|
||
'position': (y, x),
|
||
'value': coin_value,
|
||
'step': step
|
||
})
|
||
except ValueError:
|
||
# 如果没有数值,默认为1
|
||
self.total_coins += 1
|
||
self.total_value += 1
|
||
self.collected_resources.append({
|
||
'type': 'coin',
|
||
'position': (y, x),
|
||
'value': 1,
|
||
'step': step
|
||
})
|
||
|
||
elif cell.startswith('t'): # 陷阱
|
||
try:
|
||
trap_value = int(cell[1:])
|
||
self.total_traps += trap_value
|
||
self.total_value -= trap_value
|
||
self.collected_resources.append({
|
||
'type': 'trap',
|
||
'position': (y, x),
|
||
'value': -trap_value,
|
||
'step': step
|
||
})
|
||
except ValueError:
|
||
# 如果没有数值,默认为1
|
||
self.total_traps += 1
|
||
self.total_value -= 1
|
||
self.collected_resources.append({
|
||
'type': 'trap',
|
||
'position': (y, x),
|
||
'value': -1,
|
||
'step': step
|
||
})
|
||
|
||
elif cell.startswith('b'): # Boss (记录但不计入总价值)
|
||
self.collected_resources.append({
|
||
'type': 'boss',
|
||
'position': (y, x),
|
||
'value': 0,
|
||
'step': step
|
||
})
|
||
|
||
elif cell.startswith('l'): # 机关 (记录但不计入总价值)
|
||
self.collected_resources.append({
|
||
'type': 'mechanism',
|
||
'position': (y, x),
|
||
'value': 0,
|
||
'step': step
|
||
})
|
||
|
||
def print_resource_summary(self):
|
||
"""打印资源收集总结"""
|
||
print("\n" + "="*50)
|
||
print("SourceCollector 资源收集总结")
|
||
print("="*50)
|
||
|
||
if not self.path:
|
||
print("没有生成路径,无法统计资源")
|
||
return
|
||
|
||
# 先计算资源
|
||
self.calculate_resources()
|
||
|
||
print(f"路径总长度: {len(self.path)} 步")
|
||
print(f"收集资源总数: {len(self.collected_resources)} 个")
|
||
print()
|
||
|
||
# 按类型分组统计
|
||
coins = [r for r in self.collected_resources if r['type'] == 'coin']
|
||
traps = [r for r in self.collected_resources if r['type'] == 'trap']
|
||
bosses = [r for r in self.collected_resources if r['type'] == 'boss']
|
||
mechanisms = [r for r in self.collected_resources if r['type'] == 'mechanism']
|
||
|
||
print(f"金币收集: {len(coins)} 个,总价值: +{self.total_coins}")
|
||
if coins:
|
||
for coin in coins:
|
||
print(f" 步骤{coin['step']}: 位置{coin['position']}, 价值+{coin['value']}")
|
||
|
||
print(f"\n陷阱触发: {len(traps)} 个,总损失: -{self.total_traps}")
|
||
if traps:
|
||
for trap in traps:
|
||
print(f" 步骤{trap['step']}: 位置{trap['position']}, 损失{trap['value']}")
|
||
|
||
if bosses:
|
||
print(f"\nBoss遭遇: {len(bosses)} 个")
|
||
for boss in bosses:
|
||
print(f" 步骤{boss['step']}: 位置{boss['position']}")
|
||
|
||
if mechanisms:
|
||
print(f"\n机关触发: {len(mechanisms)} 个")
|
||
for mech in mechanisms:
|
||
print(f" 步骤{mech['step']}: 位置{mech['position']}")
|
||
|
||
print()
|
||
print("-"*30)
|
||
print(f"最终总价值: {self.total_value} (金币: +{self.total_coins}, 陷阱: -{self.total_traps})")
|
||
print("-"*30)
|
||
print("="*50)
|
||
|
||
def get_resource_summary(self):
|
||
"""获取资源收集摘要(用于其他模块调用)"""
|
||
if not hasattr(self, 'collected_resources') or not self.collected_resources:
|
||
self.calculate_resources()
|
||
|
||
return {
|
||
'path_length': len(self.path),
|
||
'total_resources': len(self.collected_resources),
|
||
'total_coins': self.total_coins,
|
||
'total_traps': self.total_traps,
|
||
'total_value': self.total_value,
|
||
'collected_resources': self.collected_resources.copy()
|
||
}
|
||
|
||
if __name__ == '__main__':
|
||
obj = SourceCollector(filename="maze.csv")
|
||
obj.run()
|
||
path = obj.get_path()
|
||
|
||
print("\n路径坐标序列:")
|
||
for i, pos in enumerate(path):
|
||
print(f"步骤{i}: {pos}")
|
||
|
||
# 获取资源总结
|
||
summary = obj.get_resource_summary()
|
||
print(f"\n最终资源总价值: {summary['total_value']}")
|
||
# 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 = _ |