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()