618 lines
20 KiB
Python
618 lines
20 KiB
Python
import math
|
||
from maze import *
|
||
import math
|
||
import copy
|
||
from collections import deque
|
||
|
||
|
||
class GreedyPlayer:
|
||
def __init__(self, map_data, start=None, end=None):
|
||
"""初始化GreedyPlayer对象"""
|
||
self.map_data = map_data
|
||
self.rows = len(map_data)
|
||
self.cols = len(map_data[0]) if self.rows > 0 else 0
|
||
self.start = start
|
||
self.end = end
|
||
self.path = []
|
||
self.total_reward = 0
|
||
self.visited = set()
|
||
self.marked_map = []
|
||
|
||
# 如果未指定起点和终点,自动查找
|
||
if not self.start or not self.end:
|
||
self._find_start_end()
|
||
|
||
def _find_start_end(self):
|
||
"""自动查找地图中的起点(s)和终点(e)"""
|
||
for y in range(self.rows):
|
||
for x in range(self.cols):
|
||
if self.map_data[y][x] == 's' or self.map_data[y][x] == 'S':
|
||
self.start = (x, y)
|
||
elif self.map_data[y][x] == 'e' or self.map_data[y][x] == 'E':
|
||
self.end = (x, y)
|
||
print(f"起点: {self.start}, 终点: {self.end}")
|
||
|
||
def get_visible_cells(self, x, y, visibility=1):
|
||
"""获取以(x,y)为中心的上下左右四个方向的单元格信息"""
|
||
visible = {}
|
||
# 只考虑上下左右四个方向(dx或dy为±1,另一个为0)
|
||
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
|
||
for dx, dy in directions:
|
||
nx, ny = x + dx, y + dy
|
||
if 0 <= nx < self.cols and 0 <= ny < self.rows:
|
||
cell = self.map_data[ny][nx]
|
||
distance = 1 # 上下左右移动距离为1
|
||
visible[(nx, ny)] = (cell, distance)
|
||
return visible
|
||
|
||
def evaluate_cell(self, cell, distance):
|
||
"""评估单元格的价值,返回奖励/路径的比值"""
|
||
if cell == 's' or cell == 'e':
|
||
return 0 # 起点和终点不参与资源评估
|
||
|
||
if cell.startswith('t'):
|
||
try:
|
||
value = -int(cell[1:]) # t表示损失,转为负值
|
||
return value / distance
|
||
except ValueError:
|
||
return 0
|
||
elif cell.startswith('g'):
|
||
try:
|
||
value = int(cell[1:]) # g表示收益,转为正值
|
||
return value / distance
|
||
except ValueError:
|
||
return 0
|
||
|
||
return 0 # 0、l、b等不产生资源价值
|
||
|
||
def find_path(self):
|
||
"""基于贪心策略的路径规划(只能上下左右移动)"""
|
||
if not self.start or not self.end:
|
||
raise ValueError("地图中未找到起点或终点")
|
||
|
||
current = self.start
|
||
self.path = [current]
|
||
self.visited = {current}
|
||
self.total_reward = 0
|
||
|
||
while current != self.end:
|
||
x, y = current
|
||
visible = self.get_visible_cells(x, y)
|
||
|
||
best_cell = None
|
||
best_value = -float('inf')
|
||
|
||
for (nx, ny), (cell, distance) in visible.items():
|
||
# 跳过已访问的位置
|
||
if (nx, ny) in self.visited:
|
||
continue
|
||
|
||
# 只允许在0、t、g、l、b上行走
|
||
if cell not in ['0'] and not cell.startswith(('t', 'g', 'l', 'b')):
|
||
continue
|
||
|
||
# 评估单元格价值
|
||
value = self.evaluate_cell(cell, distance)
|
||
|
||
# 终点具有最高优先级
|
||
if cell == 'e':
|
||
value = float('inf')
|
||
|
||
# 选择贪心值最大的单元格
|
||
if value > best_value:
|
||
best_value = value
|
||
best_cell = (nx, ny)
|
||
|
||
# 无法找到可行路径
|
||
if best_cell is None:
|
||
print("无法找到通往终点的路径!")
|
||
break
|
||
|
||
# 更新当前位置和路径
|
||
current = best_cell
|
||
self.path.append(current)
|
||
self.visited.add(current)
|
||
|
||
# 更新总收益(跳过起点和终点)
|
||
if len(self.path) > 1 and len(self.path) < len(self.path) + 1:
|
||
cell = self.map_data[current[1]][current[0]]
|
||
if cell.startswith('t'):
|
||
self.total_reward -= int(cell[1:])
|
||
elif cell.startswith('g'):
|
||
self.total_reward += int(cell[1:])
|
||
self.add_path_to_map()
|
||
return self.path
|
||
|
||
def add_path_to_map(self):
|
||
"""在地图上标记路径,上下移动用|,左右移动用-"""
|
||
if not self.path:
|
||
print("没有路径可标记")
|
||
return
|
||
|
||
# 创建地图副本,避免修改原始地图
|
||
marked_map = [row.copy() for row in self.map_data]
|
||
|
||
# 标记路径点
|
||
for i, (x, y) in enumerate(self.path):
|
||
if marked_map[y][x] == 's':
|
||
marked_map[y][x] = 'S' # 标记起点
|
||
elif marked_map[y][x] == 'e':
|
||
marked_map[y][x] = 'E' # 标记终点
|
||
else:
|
||
marked_map[y][x] = '*' # 标记路径点
|
||
|
||
# 标记路径线(上下左右)
|
||
for i in range(len(self.path) - 1):
|
||
x1, y1 = self.path[i]
|
||
x2, y2 = self.path[i + 1]
|
||
|
||
# 左右移动
|
||
if x1 != x2 and y1 == y2:
|
||
start, end = (x1, x2) if x1 < x2 else (x2, x1)
|
||
for x in range(start, end + 1):
|
||
if marked_map[y1][x] not in ['S', 'E']:
|
||
marked_map[y1][x] = '-'
|
||
|
||
# 上下移动
|
||
elif y1 != y2 and x1 == x2:
|
||
start, end = (y1, y2) if y1 < y2 else (y2, y1)
|
||
for y in range(start, end + 1):
|
||
if marked_map[y][x1] not in ['S', 'E']:
|
||
marked_map[y][x1] = '|'
|
||
|
||
# 保存标记后的地图
|
||
self.marked_map = marked_map
|
||
return marked_map
|
||
|
||
def get_path(self):
|
||
"""返回找到的路径"""
|
||
return self.path
|
||
|
||
def get_total_reward(self):
|
||
"""返回总收益"""
|
||
return self.total_reward
|
||
|
||
|
||
class Greedy3x3ResourceCollector:
|
||
"""
|
||
基于3x3视野的贪心资源收集器
|
||
每次移动时选择3x3视野范围内最高价值的资源
|
||
只能进行上下左右移动
|
||
"""
|
||
|
||
def __init__(self, map_data, start=None, end=None):
|
||
"""
|
||
初始化3x3视野贪心资源收集器
|
||
|
||
Args:
|
||
map_data: 迷宫地图,2D列表 (注意:这里是[y][x]格式)
|
||
start: 起始位置 (x, y),如果为None则自动寻找
|
||
end: 目标位置 (x, y),如果为None则自动寻找
|
||
"""
|
||
self.original_map = copy.deepcopy(map_data)
|
||
self.map_data = copy.deepcopy(map_data)
|
||
self.rows = len(map_data)
|
||
self.cols = len(map_data[0]) if self.rows > 0 else 0
|
||
|
||
# 寻找起始位置和目标位置
|
||
self.start = start or self._find_position('s')
|
||
self.end = end or self._find_position('e')
|
||
|
||
if not self.start:
|
||
raise ValueError("无法找到起始位置 's'")
|
||
if not self.end:
|
||
raise ValueError("无法找到目标位置 'e'")
|
||
|
||
self.current_pos = self.start
|
||
self.path = [self.start]
|
||
self.collected_resources = []
|
||
self.total_value = 0
|
||
self.visited_resources = set()
|
||
self.explored_positions = set([self.start])
|
||
|
||
print(f"3x3视野贪心算法初始化")
|
||
print(f"起始位置: {self.start}")
|
||
print(f"目标位置: {self.end}")
|
||
|
||
def _find_position(self, target):
|
||
"""寻找地图中指定字符的位置,返回(x, y)格式"""
|
||
for y in range(self.rows):
|
||
for x in range(self.cols):
|
||
if self.map_data[y][x].lower() == target.lower():
|
||
return (x, y)
|
||
return None
|
||
|
||
def get_3x3_vision(self, pos):
|
||
"""
|
||
获取以pos为中心的3x3视野范围内的所有单元格
|
||
|
||
Args:
|
||
pos: 当前位置 (x, y)
|
||
|
||
Returns:
|
||
dict: {(x, y): cell_value} 形式的字典
|
||
"""
|
||
x, y = pos
|
||
vision = {}
|
||
|
||
# 遍历3x3范围
|
||
for dx in range(-1, 2):
|
||
for dy in range(-1, 2):
|
||
new_x, new_y = x + dx, y + dy
|
||
|
||
# 检查边界
|
||
if 0 <= new_x < self.cols and 0 <= new_y < self.rows:
|
||
vision[(new_x, new_y)] = self.map_data[new_y][new_x]
|
||
|
||
return vision
|
||
|
||
def get_adjacent_cells(self, pos):
|
||
"""
|
||
获取当前位置的上下左右四个相邻位置
|
||
|
||
Args:
|
||
pos: 当前位置 (x, y)
|
||
|
||
Returns:
|
||
list: 可移动的相邻位置列表
|
||
"""
|
||
x, y = pos
|
||
adjacent = []
|
||
|
||
# 上下左右四个方向
|
||
directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] # 上、下、左、右
|
||
|
||
for dx, dy in directions:
|
||
new_x, new_y = x + dx, y + dy
|
||
|
||
# 检查边界和可移动性
|
||
if (0 <= new_x < self.cols and
|
||
0 <= new_y < self.rows and
|
||
self.can_move_to((new_x, new_y))):
|
||
adjacent.append((new_x, new_y))
|
||
|
||
return adjacent
|
||
|
||
def can_move_to(self, pos):
|
||
"""
|
||
检查是否可以移动到指定位置
|
||
|
||
Args:
|
||
pos: 目标位置 (x, y)
|
||
|
||
Returns:
|
||
bool: 是否可以移动
|
||
"""
|
||
x, y = pos
|
||
cell = self.map_data[y][x]
|
||
|
||
# 不能移动到墙壁
|
||
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_3x3_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
|
||
|
||
# 检查是否可以直接到达(相邻位置)
|
||
if pos not in self.get_adjacent_cells(self.current_pos):
|
||
continue
|
||
|
||
# 检查是否为资源
|
||
value = self.evaluate_resource_value(cell)
|
||
if value > 0 and value > best_value: # 优先选择正价值资源
|
||
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
|
||
if pos not in self.get_adjacent_cells(self.current_pos):
|
||
continue
|
||
|
||
value = self.evaluate_resource_value(cell)
|
||
if value < 0 and value > best_value: # 选择损失最小的陷阱
|
||
best_value = value
|
||
best_pos = pos
|
||
|
||
return best_pos, best_value if best_pos else 0
|
||
|
||
def find_exploration_target(self):
|
||
"""
|
||
当视野内没有资源时,寻找探索目标
|
||
优先选择未探索过的位置
|
||
"""
|
||
adjacent = self.get_adjacent_cells(self.current_pos)
|
||
|
||
# 优先选择未探索的位置
|
||
unexplored = [pos for pos in adjacent if pos not in self.explored_positions]
|
||
if unexplored:
|
||
return unexplored[0] # 选择第一个未探索的位置
|
||
|
||
# 如果所有相邻位置都探索过,选择任意一个
|
||
if adjacent:
|
||
return adjacent[0]
|
||
|
||
return None
|
||
|
||
def collect_resource(self, pos):
|
||
"""
|
||
收集指定位置的资源
|
||
|
||
Args:
|
||
pos: 资源位置 (x, y)
|
||
"""
|
||
x, y = pos
|
||
cell = self.map_data[y][x]
|
||
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_3x3_greedy_collection(self, max_moves=1000):
|
||
"""
|
||
运行3x3视野贪心资源收集算法
|
||
|
||
Args:
|
||
max_moves: 最大移动步数,防止无限循环
|
||
|
||
Returns:
|
||
dict: 包含路径、收集的资源等信息
|
||
"""
|
||
print("\\n开始3x3视野贪心资源收集...")
|
||
|
||
moves = 0
|
||
stuck_count = 0 # 连续无法找到资源的次数
|
||
max_stuck = 20 # 最大连续无资源次数
|
||
|
||
while moves < max_moves and stuck_count < max_stuck:
|
||
moves += 1
|
||
|
||
# 在3x3视野内寻找最佳资源
|
||
best_resource_pos, best_value = self.find_best_resource_in_3x3_vision()
|
||
|
||
if best_resource_pos is not None:
|
||
print(f"第{moves}步: 发现视野内资源 位置{best_resource_pos}, 价值{best_value}")
|
||
|
||
# 移动到资源位置并收集
|
||
self.current_pos = best_resource_pos
|
||
self.path.append(best_resource_pos)
|
||
self.explored_positions.add(best_resource_pos)
|
||
self.collect_resource(best_resource_pos)
|
||
|
||
stuck_count = 0 # 重置无资源计数
|
||
else:
|
||
# 视野内没有资源,进行探索性移动
|
||
exploration_target = self.find_exploration_target()
|
||
|
||
if exploration_target:
|
||
print(f"第{moves}步: 视野内无资源,探索移动到 {exploration_target}")
|
||
self.current_pos = exploration_target
|
||
self.path.append(exploration_target)
|
||
self.explored_positions.add(exploration_target)
|
||
stuck_count += 1
|
||
else:
|
||
print(f"第{moves}步: 无法进行任何移动,结束收集")
|
||
break
|
||
|
||
if moves >= max_moves:
|
||
print(f"达到最大移动步数 {max_moves},结束收集")
|
||
elif stuck_count >= max_stuck:
|
||
print(f"连续 {max_stuck} 步未找到资源,结束收集")
|
||
|
||
print("3x3视野资源收集完成!")
|
||
return self.get_collection_result()
|
||
|
||
def get_collection_result(self):
|
||
"""获取收集结果"""
|
||
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,
|
||
'end_pos': self.end,
|
||
'final_pos': self.current_pos,
|
||
'explored_positions': len(self.explored_positions)
|
||
}
|
||
|
||
def get_path(self):
|
||
"""返回路径,转换为(y, x)格式以兼容现有代码"""
|
||
# 将(x, y)格式的路径转换为(y, x)格式
|
||
return [(y, x) for (x, y) in self.path]
|
||
|
||
def get_total_reward(self):
|
||
"""返回总收益"""
|
||
return self.total_value
|
||
|
||
def add_path_to_map(self):
|
||
"""在地图上标记路径"""
|
||
marked_map = [row.copy() for row in self.map_data]
|
||
|
||
# 标记路径点
|
||
for i, (x, y) in enumerate(self.path):
|
||
if marked_map[y][x] == 's':
|
||
marked_map[y][x] = 'S' # 标记起点
|
||
elif marked_map[y][x] == 'e':
|
||
marked_map[y][x] = 'E' # 标记终点
|
||
elif (x, y) in [r['position'] for r in self.collected_resources]:
|
||
marked_map[y][x] = '*' # 标记已收集资源
|
||
else:
|
||
marked_map[y][x] = '.' # 标记路径点
|
||
|
||
self.marked_map = marked_map
|
||
return marked_map
|
||
|
||
|
||
# 使用示例
|
||
def main():
|
||
obj = MazeGenerator(20,'demo.csv',name="龙脊峡谷迷宫")
|
||
obj.generate(
|
||
seed=123,
|
||
boss_count=2,
|
||
traps_range=(5, 10),
|
||
mechanisms_range=(3, 7),
|
||
skill_traps=8
|
||
)
|
||
obj.export_to_csv()
|
||
|
||
map_data = obj.read_csv()
|
||
see = MazeGenerator(1,filename ='demo.csv')
|
||
see.read_from_csv()
|
||
print("=== 原始迷宫 ===")
|
||
see.print_maze()
|
||
|
||
print("\\n" + "="*60)
|
||
print("使用传统贪心算法:")
|
||
print("="*60)
|
||
player = GreedyPlayer(map_data)
|
||
player.find_path()
|
||
see.maze = player.marked_map
|
||
see.print_maze()
|
||
print(f"传统贪心算法总收益: {player.get_total_reward()}")
|
||
|
||
print("\\n" + "="*60)
|
||
print("使用3x3视野贪心算法:")
|
||
print("="*60)
|
||
# 使用新的3x3视野算法
|
||
greedy_3x3 = Greedy3x3ResourceCollector(map_data)
|
||
result = greedy_3x3.run_3x3_greedy_collection()
|
||
|
||
# 显示结果
|
||
see.maze = greedy_3x3.add_path_to_map()
|
||
see.print_maze()
|
||
|
||
print(f"\\n3x3视野算法结果:")
|
||
print(f" 总移动步数: {result['total_moves']}")
|
||
print(f" 收集资源数量: {result['resources_count']}")
|
||
print(f" 资源总价值: {result['total_value']}")
|
||
print(f" 探索位置数: {result['explored_positions']}")
|
||
|
||
print("\\n收集的资源详情:")
|
||
for i, resource in enumerate(result['collected_resources'], 1):
|
||
print(f" {i}. 位置{resource['position']}: {resource['type']} (价值: {resource['value']})")
|
||
|
||
|
||
def demo_3x3_greedy():
|
||
"""演示3x3视野贪心算法"""
|
||
|
||
# 创建一个示例迷宫
|
||
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("=== 3x3视野贪心算法演示 ===")
|
||
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))
|
||
|
||
# 使用3x3视野贪心算法
|
||
collector = Greedy3x3ResourceCollector(demo_maze)
|
||
result = collector.run_3x3_greedy_collection()
|
||
|
||
# 显示标记后的迷宫
|
||
marked_maze = collector.add_path_to_map()
|
||
print("\\n标记路径后的迷宫:")
|
||
print("S: 起点, E: 终点, *: 已收集资源, .: 路径")
|
||
for row in marked_maze:
|
||
print(' '.join(f"{cell:>2}" for cell in row))
|
||
|
||
print(f"\\n算法结果:")
|
||
print(f" 总移动步数: {result['total_moves']}")
|
||
print(f" 收集资源数量: {result['resources_count']}")
|
||
print(f" 资源总价值: {result['total_value']}")
|
||
|
||
return collector, result
|
||
|
||
|
||
|
||
|
||
if __name__ == "__main__":
|
||
# 运行演示
|
||
print("选择运行模式:")
|
||
print("1. 完整迷宫生成和算法比较")
|
||
print("2. 简单3x3视野算法演示")
|
||
|
||
choice = input("请输入选择 (1 或 2,默认为 2): ").strip()
|
||
|
||
if choice == "1":
|
||
main()
|
||
else:
|
||
demo_3x3_greedy()
|
||
|
||
|
||
|
||
|