449 lines
15 KiB
Python
449 lines
15 KiB
Python
import copy
|
||
from collections import deque
|
||
|
||
class GreedyResourceCollector:
|
||
"""
|
||
基于贪心算法的资源收集器
|
||
每次移动时选择3x3视野范围内最高价值的资源
|
||
只能进行上下左右移动
|
||
"""
|
||
|
||
def __init__(self, maze, start_pos=None, end_pos=None):
|
||
"""
|
||
初始化贪心资源收集器
|
||
|
||
Args:
|
||
maze: 迷宫地图,2D列表
|
||
start_pos: 起始位置 (row, col),如果为None则自动寻找
|
||
end_pos: 目标位置 (row, col),如果为None则自动寻找
|
||
"""
|
||
self.original_maze = copy.deepcopy(maze)
|
||
self.maze = copy.deepcopy(maze)
|
||
self.rows = len(maze)
|
||
self.cols = len(maze[0]) if self.rows > 0 else 0
|
||
|
||
# 寻找起始位置和目标位置
|
||
self.start_pos = start_pos or self._find_position('s')
|
||
self.end_pos = end_pos or self._find_position('e')
|
||
|
||
if not self.start_pos:
|
||
raise ValueError("无法找到起始位置 's'")
|
||
if not self.end_pos:
|
||
raise ValueError("无法找到目标位置 'e'")
|
||
|
||
self.current_pos = self.start_pos
|
||
self.path = [self.start_pos]
|
||
self.collected_resources = []
|
||
self.total_value = 0
|
||
self.visited_resources = set()
|
||
|
||
print(f"起始位置: {self.start_pos}")
|
||
print(f"目标位置: {self.end_pos}")
|
||
|
||
def _find_position(self, target):
|
||
"""寻找地图中指定字符的位置"""
|
||
for i in range(self.rows):
|
||
for j in range(self.cols):
|
||
if self.maze[i][j].lower() == target.lower():
|
||
return (i, j)
|
||
return None
|
||
|
||
def get_3x3_vision(self, pos):
|
||
"""
|
||
获取以pos为中心的3x3视野范围内的所有单元格
|
||
|
||
Args:
|
||
pos: 当前位置 (row, col)
|
||
|
||
Returns:
|
||
dict: {(row, col): cell_value} 形式的字典
|
||
"""
|
||
row, col = pos
|
||
vision = {}
|
||
|
||
# 遍历3x3范围
|
||
for dr in range(-1, 2):
|
||
for dc in range(-1, 2):
|
||
new_row, new_col = row + dr, col + dc
|
||
|
||
# 检查边界
|
||
if 0 <= new_row < self.rows and 0 <= new_col < self.cols:
|
||
vision[(new_row, new_col)] = self.maze[new_row][new_col]
|
||
|
||
return vision
|
||
|
||
def get_adjacent_cells(self, pos):
|
||
"""
|
||
获取当前位置的上下左右四个相邻位置
|
||
|
||
Args:
|
||
pos: 当前位置 (row, col)
|
||
|
||
Returns:
|
||
list: 可移动的相邻位置列表
|
||
"""
|
||
row, col = pos
|
||
adjacent = []
|
||
|
||
# 上下左右四个方向
|
||
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
|
||
|
||
for dr, dc in directions:
|
||
new_row, new_col = row + dr, col + dc
|
||
|
||
# 检查边界和可移动性
|
||
if (0 <= new_row < self.rows and
|
||
0 <= new_col < self.cols and
|
||
self.can_move_to((new_row, new_col))):
|
||
adjacent.append((new_row, new_col))
|
||
|
||
return adjacent
|
||
|
||
def can_move_to(self, pos):
|
||
"""
|
||
检查是否可以移动到指定位置
|
||
|
||
Args:
|
||
pos: 目标位置 (row, col)
|
||
|
||
Returns:
|
||
bool: 是否可以移动
|
||
"""
|
||
row, col = pos
|
||
cell = self.maze[row][col]
|
||
|
||
# 不能移动到墙壁
|
||
if cell == '1':
|
||
return False
|
||
|
||
return True
|
||
|
||
def evaluate_resource_value(self, cell):
|
||
"""
|
||
评估资源的价值
|
||
|
||
Args:
|
||
cell: 单元格内容
|
||
|
||
Returns:
|
||
int: 资源价值,正数表示收益,负数表示损失
|
||
"""
|
||
if cell.startswith('g'):
|
||
# 金币资源,提取数值
|
||
try:
|
||
return int(cell[1:])
|
||
except ValueError:
|
||
return 0
|
||
elif cell.startswith('t'):
|
||
# 陷阱资源,提取数值并取负
|
||
try:
|
||
return -int(cell[1:])
|
||
except ValueError:
|
||
return 0
|
||
else:
|
||
# 其他类型单元格没有资源价值
|
||
return 0
|
||
|
||
def find_best_resource_in_vision(self):
|
||
"""
|
||
在3x3视野范围内找到价值最高的可到达资源
|
||
如果视野内没有正价值资源,则考虑移动到更有利的位置
|
||
|
||
Returns:
|
||
tuple: (最佳资源位置, 资源价值) 或 (None, 0)
|
||
"""
|
||
vision = self.get_3x3_vision(self.current_pos)
|
||
|
||
best_pos = None
|
||
best_value = float('-inf')
|
||
|
||
# 首先只考虑正价值资源
|
||
for pos, cell in vision.items():
|
||
# 跳过已访问的资源
|
||
if pos in self.visited_resources:
|
||
continue
|
||
|
||
# 跳过当前位置
|
||
if pos == self.current_pos:
|
||
continue
|
||
|
||
# 跳过不可移动的位置
|
||
if not self.can_move_to(pos):
|
||
continue
|
||
|
||
# 检查是否为资源
|
||
value = self.evaluate_resource_value(cell)
|
||
if value > 0 and value > best_value: # 只考虑正价值资源
|
||
# 确保可以到达这个位置
|
||
path_to_resource = self.find_path_to_target(pos)
|
||
if path_to_resource: # 可以到达
|
||
best_value = value
|
||
best_pos = pos
|
||
|
||
# 如果视野内没有正价值资源,考虑负价值资源(陷阱)
|
||
if best_pos is None:
|
||
for pos, cell in vision.items():
|
||
if pos in self.visited_resources or pos == self.current_pos:
|
||
continue
|
||
if not self.can_move_to(pos):
|
||
continue
|
||
|
||
value = self.evaluate_resource_value(cell)
|
||
if value < 0 and value > best_value: # 选择损失最小的陷阱
|
||
path_to_resource = self.find_path_to_target(pos)
|
||
if path_to_resource:
|
||
best_value = value
|
||
best_pos = pos
|
||
|
||
# 如果视野内完全没有资源,寻找最近的正价值资源
|
||
if best_pos is None:
|
||
best_pos, best_value = self.find_nearest_valuable_resource()
|
||
|
||
return best_pos, best_value if best_pos else 0
|
||
|
||
def find_nearest_valuable_resource(self):
|
||
"""
|
||
在整个地图上寻找最近的高价值正资源
|
||
|
||
Returns:
|
||
tuple: (最佳资源位置, 资源价值) 或 (None, 0)
|
||
"""
|
||
best_pos = None
|
||
best_score = float('-inf')
|
||
|
||
for i in range(self.rows):
|
||
for j in range(self.cols):
|
||
pos = (i, j)
|
||
cell = self.maze[i][j]
|
||
|
||
# 跳过已访问的资源
|
||
if pos in self.visited_resources:
|
||
continue
|
||
|
||
# 跳过当前位置
|
||
if pos == self.current_pos:
|
||
continue
|
||
|
||
# 检查是否为正价值资源
|
||
value = self.evaluate_resource_value(cell)
|
||
if value > 0: # 只考虑正价值资源
|
||
# 计算到达该资源的路径
|
||
path_to_resource = self.find_path_to_target(pos)
|
||
if path_to_resource:
|
||
# 计算性价比:价值/距离
|
||
distance = len(path_to_resource)
|
||
score = value / distance
|
||
|
||
if score > best_score:
|
||
best_score = score
|
||
best_pos = pos
|
||
|
||
if best_pos:
|
||
value = self.evaluate_resource_value(self.maze[best_pos[0]][best_pos[1]])
|
||
return best_pos, value
|
||
|
||
return None, 0
|
||
|
||
def find_path_to_target(self, target_pos):
|
||
"""
|
||
使用BFS找到到目标位置的最短路径
|
||
|
||
Args:
|
||
target_pos: 目标位置 (row, col)
|
||
|
||
Returns:
|
||
list: 从当前位置到目标位置的路径(不包含当前位置)
|
||
"""
|
||
if self.current_pos == target_pos:
|
||
return []
|
||
|
||
queue = deque([(self.current_pos, [])])
|
||
visited = {self.current_pos}
|
||
|
||
while queue:
|
||
pos, path = queue.popleft()
|
||
|
||
# 获取相邻位置
|
||
for next_pos in self.get_adjacent_cells(pos):
|
||
if next_pos in visited:
|
||
continue
|
||
|
||
new_path = path + [next_pos]
|
||
|
||
# 找到目标
|
||
if next_pos == target_pos:
|
||
return new_path
|
||
|
||
visited.add(next_pos)
|
||
queue.append((next_pos, new_path))
|
||
|
||
return [] # 无法到达
|
||
|
||
def collect_resource(self, pos):
|
||
"""
|
||
收集指定位置的资源
|
||
|
||
Args:
|
||
pos: 资源位置 (row, col)
|
||
"""
|
||
row, col = pos
|
||
cell = self.maze[row][col]
|
||
value = self.evaluate_resource_value(cell)
|
||
|
||
if value != 0:
|
||
self.collected_resources.append({
|
||
'position': pos,
|
||
'type': cell,
|
||
'value': value
|
||
})
|
||
self.total_value += value
|
||
self.visited_resources.add(pos)
|
||
|
||
print(f"收集资源: 位置{pos}, 类型{cell}, 价值{value}, 总价值{self.total_value}")
|
||
|
||
def run_greedy_collection(self):
|
||
"""
|
||
运行贪心资源收集算法
|
||
|
||
Returns:
|
||
dict: 包含路径、收集的资源等信息
|
||
"""
|
||
print("开始贪心资源收集...")
|
||
|
||
while True:
|
||
# 在3x3视野内寻找最佳资源
|
||
best_resource_pos, best_value = self.find_best_resource_in_vision()
|
||
|
||
if best_resource_pos is None:
|
||
print("视野内没有更多资源可收集")
|
||
break
|
||
|
||
print(f"发现最佳资源: 位置{best_resource_pos}, 价值{best_value}")
|
||
|
||
# 计算到最佳资源的路径
|
||
path_to_resource = self.find_path_to_target(best_resource_pos)
|
||
|
||
if not path_to_resource:
|
||
print(f"无法到达资源位置{best_resource_pos}")
|
||
self.visited_resources.add(best_resource_pos) # 标记为无法到达
|
||
continue
|
||
|
||
# 移动到资源位置
|
||
for next_pos in path_to_resource:
|
||
self.current_pos = next_pos
|
||
self.path.append(next_pos)
|
||
print(f"移动到: {next_pos}")
|
||
|
||
# 收集资源
|
||
self.collect_resource(best_resource_pos)
|
||
|
||
print("资源收集完成!")
|
||
return self.get_collection_result()
|
||
|
||
def get_collection_result(self):
|
||
"""
|
||
获取收集结果
|
||
|
||
Returns:
|
||
dict: 包含路径、资源、统计信息的字典
|
||
"""
|
||
return {
|
||
'path': self.path.copy(),
|
||
'collected_resources': self.collected_resources.copy(),
|
||
'total_value': self.total_value,
|
||
'total_moves': len(self.path) - 1,
|
||
'resources_count': len(self.collected_resources),
|
||
'start_pos': self.start_pos,
|
||
'end_pos': self.end_pos,
|
||
'final_pos': self.current_pos
|
||
}
|
||
|
||
def print_result_summary(self):
|
||
"""打印收集结果摘要"""
|
||
result = self.get_collection_result()
|
||
|
||
print("\n=== 贪心资源收集结果摘要 ===")
|
||
print(f"起始位置: {result['start_pos']}")
|
||
print(f"最终位置: {result['final_pos']}")
|
||
print(f"总移动步数: {result['total_moves']}")
|
||
print(f"收集资源数量: {result['resources_count']}")
|
||
print(f"资源总价值: {result['total_value']}")
|
||
|
||
print("\n收集的资源详情:")
|
||
for i, resource in enumerate(result['collected_resources'], 1):
|
||
print(f" {i}. 位置{resource['position']}: {resource['type']} (价值: {resource['value']})")
|
||
|
||
print(f"\n完整移动路径: {' -> '.join(map(str, result['path']))}")
|
||
|
||
def visualize_path_on_maze(self):
|
||
"""
|
||
在迷宫上可视化移动路径
|
||
|
||
Returns:
|
||
list: 标记了路径的迷宫副本
|
||
"""
|
||
visual_maze = copy.deepcopy(self.original_maze)
|
||
|
||
# 标记路径
|
||
for i, pos in enumerate(self.path):
|
||
row, col = pos
|
||
if pos == self.start_pos:
|
||
visual_maze[row][col] = 'S' # 起点
|
||
elif pos == self.end_pos:
|
||
visual_maze[row][col] = 'E' # 终点
|
||
elif pos in [r['position'] for r in self.collected_resources]:
|
||
# 已收集的资源位置
|
||
visual_maze[row][col] = '*'
|
||
else:
|
||
# 路径点
|
||
visual_maze[row][col] = str(i % 10)
|
||
|
||
return visual_maze
|
||
|
||
def print_visual_maze(self):
|
||
"""打印可视化的迷宫"""
|
||
visual_maze = self.visualize_path_on_maze()
|
||
|
||
print("\n=== 路径可视化迷宫 ===")
|
||
print("S: 起点, E: 终点, *: 已收集资源, 数字: 路径步骤")
|
||
for row in visual_maze:
|
||
print(' '.join(f"{cell:>2}" for cell in row))
|
||
|
||
|
||
def demo_greedy_resource_collection():
|
||
"""演示贪心资源收集算法"""
|
||
|
||
# 创建一个示例迷宫
|
||
demo_maze = [
|
||
['s', '0', 'g5', '1', 't3'],
|
||
['0', '1', '0', '0', 'g2'],
|
||
['g3', '0', '1', 't2', '0'],
|
||
['0', 't1', '0', '0', 'g4'],
|
||
['1', '0', 'g1', '0', 'e']
|
||
]
|
||
|
||
print("=== 贪心资源收集算法演示 ===")
|
||
print("迷宫说明:")
|
||
print(" s: 起点, e: 终点")
|
||
print(" g数字: 金币资源 (正收益)")
|
||
print(" t数字: 陷阱资源 (负收益)")
|
||
print(" 0: 可通行路径, 1: 墙壁")
|
||
print("\n原始迷宫:")
|
||
for row in demo_maze:
|
||
print(' '.join(f"{cell:>2}" for cell in row))
|
||
|
||
# 创建贪心收集器并运行
|
||
collector = GreedyResourceCollector(demo_maze)
|
||
result = collector.run_greedy_collection()
|
||
|
||
# 打印结果
|
||
collector.print_result_summary()
|
||
collector.print_visual_maze()
|
||
|
||
return collector, result
|
||
|
||
|
||
if __name__ == "__main__":
|
||
# 运行演示
|
||
collector, result = demo_greedy_resource_collection()
|