commit f877c2a6784fd66e1b3a9bf1f6c3997f737f2e5c Author: Gary Gan Date: Thu Jun 26 20:15:59 2025 +0800 初始化 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba0430d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ \ No newline at end of file diff --git a/assets/coin.png b/assets/coin.png new file mode 100644 index 0000000..7097fbc Binary files /dev/null and b/assets/coin.png differ diff --git a/assets/load.png b/assets/load.png new file mode 100644 index 0000000..49db516 Binary files /dev/null and b/assets/load.png differ diff --git a/assets/save.png b/assets/save.png new file mode 100644 index 0000000..0872fc4 Binary files /dev/null and b/assets/save.png differ diff --git a/assets/start_button.png b/assets/start_button.png new file mode 100644 index 0000000..2e3f651 Binary files /dev/null and b/assets/start_button.png differ diff --git a/assets/trap.png b/assets/trap.png new file mode 100644 index 0000000..0d704dc Binary files /dev/null and b/assets/trap.png differ diff --git a/assets/wall.png b/assets/wall.png new file mode 100644 index 0000000..123c5f5 Binary files /dev/null and b/assets/wall.png differ diff --git a/draw.py b/draw.py new file mode 100644 index 0000000..34b71fc --- /dev/null +++ b/draw.py @@ -0,0 +1,120 @@ +import pygame +import os + +class Button: + def __init__(self, rect: pygame.Rect, texture): + self.rect = rect + self.texture = texture + self.hovered = False + self.pressed = False + + def handle_event(self, event): + if event.type == pygame.MOUSEMOTION: + self.hovered = self.rect.collidepoint(event.pos) + elif event.type == pygame.MOUSEBUTTONDOWN: + if self.hovered and event.button == 1: + self.pressed = True + elif event.type == pygame.MOUSEBUTTONUP: + if self.pressed and self.hovered and event.button == 1: + self.pressed = False + return True # Button was clicked + self.pressed = False + return False + + def draw(self, screen): + screen.blit(self.texture, self.rect) + +class Toast: + def __init__(self, text, screen_width=None, screen_height=None, duration=2, font=None, color=(255, 255, 255), bg_color=(0, 0, 0), pos=None, font_size=28): + self.text = text + self.duration = duration # seconds + self.start_time = None + # Use a font that supports Chinese characters, or use the default system font + if font is None: + # Try to use a system font that supports UTF-8 + try: + self.font = pygame.font.SysFont("microsoftyahei,simsun,simhei", font_size) + except: + # Fall back to default font if specific fonts are not available + self.font = pygame.font.Font(None, font_size) + else: + self.font = font + self.color = color + self.bg_color = bg_color + self.pos = pos # (x, y) or None for center + self.visible = False + self.surface = None + self.rect = None + self.screen_width = screen_width + self.screen_height = screen_height + + def show(self): + self.start_time = pygame.time.get_ticks() + self.visible = True + txt_surface = self.font.render(self.text, True, self.color) + padding = 16 + self.surface = pygame.Surface((txt_surface.get_width() + padding, txt_surface.get_height() + padding), pygame.SRCALPHA) + self.surface.fill(self.bg_color) + self.surface.blit(txt_surface, (padding // 2, padding // 2)) + self.rect = self.surface.get_rect() + if self.pos: + self.rect.topleft = self.pos + elif self.screen_width and self.screen_height: + # Center on screen using provided dimensions + self.rect.center = (self.screen_width // 2, self.screen_height // 2) + + def draw(self, screen): + if not self.visible: + return + elapsed = (pygame.time.get_ticks() - self.start_time) / 1000.0 + if elapsed > self.duration: + self.visible = False + return + if self.pos is None and (self.screen_width is None or self.screen_height is None): + # Center on screen if dimensions weren't provided at initialization + screen_rect = screen.get_rect() + self.rect.center = screen_rect.center + screen.blit(self.surface, self.rect) + + + +class InputBox: + def __init__(self, rect: pygame.Rect = pygame.Rect(100, 100, 140, 32)) -> None: + """ + rect,传入矩形实体,传达输入框的位置和大小 + """ + self.boxBody: pygame.Rect = rect + self.color_inactive = pygame.Color('lightskyblue3') # 未被选中的颜色 + self.color_active = pygame.Color('dodgerblue2') # 被选中的颜色 + self.color = self.color_inactive # 当前颜色,初始为未激活颜色 + self.active = False + self.text = '' # 输入的内容 + self.done = False + self.font = pygame.font.Font(None, 32) + + def dealEvent(self, event: pygame.event.Event): + if(event.type == pygame.MOUSEBUTTONDOWN): + if(self.boxBody.collidepoint(event.pos)): # 若按下鼠标且位置在文本框 + self.active = not self.active + else: + self.active = False + self.color = self.color_active if( + self.active) else self.color_inactive + if(event.type == pygame.KEYDOWN): # 键盘输入响应 + if(self.active): + if(event.key == pygame.K_RETURN): + print(self.text) + # self.text='' + elif(event.key == pygame.K_BACKSPACE): + self.text = self.text[:-1] + else: + self.text += event.unicode + + def draw(self, screen: pygame.surface.Surface): + txtSurface = self.font.render( + self.text, True, self.color) # 文字转换为图片 + width = max(200, txtSurface.get_width()+10) # 当文字过长时,延长文本框 + self.boxBody.w = width + screen.blit(txtSurface, (self.boxBody.x+5, self.boxBody.y+5)) + pygame.draw.rect(screen, self.color, self.boxBody, 2) + diff --git a/main.py b/main.py new file mode 100644 index 0000000..dff69ed --- /dev/null +++ b/main.py @@ -0,0 +1,95 @@ +import pygame +from pygame import Surface +from pygame.constants import QUIT +from maze import Maze + +from draw import Button, Toast +import sys +import os + +UI_HEIGHT = 800 +UI_WIDTH = 1100 + +MAZE_SIZE = 800 +WALL_SIZE = 50 +FPS = 120 + +screen: Surface = None # 窗口实例 +clock = None # 时钟实例 + +textFont = None # 字体 + + +def pygameInit(title: str = "pygame"): + """初始化 pygame""" + pygame.init() + pygame.mixer.init() # 声音初始化 + pygame.display.set_caption(title) + global screen, clock, textFont # 修改全局变量 + screen = pygame.display.set_mode((UI_WIDTH, UI_HEIGHT)) + clock = pygame.time.Clock() + # Initialize font with UTF-8 support + pygame.font.init() + textFont = pygame.font.Font("syht.otf", 18) + +if __name__ == "__main__": + pygameInit("maze") + maze = Maze(wall_size=WALL_SIZE, maze_size=MAZE_SIZE, file_name="maze.csv") + image_wall = pygame.image.load("assets/wall.png").convert_alpha() + image_wall = pygame.transform.scale(image_wall, (WALL_SIZE, WALL_SIZE)) # 例如缩放到50x50像素 + + image_coin = pygame.image.load("assets/coin.png").convert_alpha() + image_coin = pygame.transform.scale(image_coin, (WALL_SIZE, WALL_SIZE)) # 例如缩放到50x50像素 + + image_trap = pygame.image.load("assets/trap.png").convert_alpha() + image_trap = pygame.transform.scale(image_trap, (WALL_SIZE, WALL_SIZE)) # 例如缩放到50x50像素 + + button_start_texture = pygame.image.load("assets/start_button.png").convert_alpha() + button_start_texture = pygame.transform.scale(button_start_texture, (200, 100)) + button_start = Button(pygame.rect.Rect(MAZE_SIZE + ((UI_WIDTH - MAZE_SIZE) / 2 - 100), 0, 200, 100), button_start_texture) + + button_save_texture = pygame.image.load("assets/save.png").convert_alpha() + button_save_texture = pygame.transform.scale(button_save_texture, (80, 80)) + button_save = Button(pygame.rect.Rect(MAZE_SIZE + ((UI_WIDTH - MAZE_SIZE) / 2 - 100), 110, 80, 80), button_save_texture) + + + # 没有生成迷宫就保存的提示框 + mes1 = Toast("没有生成迷宫,无法保存", UI_WIDTH, UI_HEIGHT, font=textFont) + mes2 = Toast("迷宫已保存", UI_WIDTH, UI_HEIGHT, font=textFont) + + + running = True + while running: + clock.tick(FPS) # 限制帧数 + screen.fill((255, 255, 255)) # 铺底 + for event in pygame.event.get(): + button_start.handle_event(event=event) + button_save.handle_event(event=event) + + if button_start.pressed == True: + maze.generate() + + if button_save.pressed == True: + if len(maze.grid) == 0: + mes1.show() + else: + maze.export_to_csv("maze.csv") + mes2.text = "迷宫已保存至maze.csv" + mes2.show() + + if event.type == QUIT: + running = False + + + maze.draw(screen=screen, wall_texture=image_wall, coin_texture=image_coin, trap_texture=image_trap) + button_start.draw(screen=screen) + button_save.draw(screen=screen) + + mes1.draw(screen=screen) + mes2.draw(screen=screen) + pygame.display.flip() + pygame.quit() + + + + \ No newline at end of file diff --git a/maze.csv b/maze.csv new file mode 100644 index 0000000..3cacfa1 --- /dev/null +++ b/maze.csv @@ -0,0 +1,16 @@ +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 +1,0,l28,1,0,0,0,0,0,l14,l22,1,0,0,0,1 +1,0,0,1,1,1,1,1,1,0,1,1,0,1,0,1 +1,0,0,1,0,0,g28,t10,0,0,0,1,0,1,t14,1 +1,0,g28,1,0,1,1,1,1,1,1,1,0,1,1,1 +1,g27,0,1,0,1,0,t14,0,0,0,1,0,0,0,1 +1,0,0,1,0,1,1,1,l11,1,1,1,0,1,1,1 +1,0,0,b95,0,0,0,0,0,0,0,0,0,0,l17,1 +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1 +1,0,0,1,0,1,g20,0,0,0,1,0,l17,0,0,1 +1,0,e,1,0,1,0,1,1,0,1,1,g26,1,1,1 +1,0,0,1,0,0,0,1,0,0,0,0,g13,0,0,1 +1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1 +1,s,0,1,g27,0,0,1,0,0,0,0,t16,0,0,1 +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1 +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 diff --git a/maze.py b/maze.py new file mode 100644 index 0000000..5ad6363 --- /dev/null +++ b/maze.py @@ -0,0 +1,79 @@ +import pygame +from maze_generator import MazeGenerator +from tanxin import * +import time + +class Maze: + def __init__(self, wall_size, maze_size, file_name): + self.wall_size = wall_size + self.maze_size = maze_size + self.size = int(maze_size / wall_size) + self.file_name = file_name + + + self.grid = [] + self.generater = MazeGenerator(self.size, self.file_name) + + + def generate(self): + seed = int(time.time() * 1000) % (2**32) + self.generater.generate(seed=seed) + # player = GreedyPlayer(generater.maze) + # player.find_path() + self.grid = self.generater.maze + + def export_to_csv(self, filename): + self.generater.export_to_csv(filename=filename) + + + + def draw(self, screen, wall_texture, coin_texture, trap_texture): + tile_size = wall_texture.get_width() + + if len(self.grid) == 0: + return + + for y in range(self.size): + for x in range(self.size): + if self.grid[y][x] == '1': + screen.blit(wall_texture, (x * tile_size, y * tile_size)) + if self.grid[y][x].startswith('g'): + screen.blit(coin_texture, (x * tile_size, y * tile_size)) + if self.grid[y][x].startswith('t'): + screen.blit(trap_texture, (x * tile_size, y * tile_size)) + if self.grid[y][x].startswith('|') or self.grid[y][x].startswith('-'): + font = pygame.font.SysFont(None, tile_size // 2) + num = 12 + center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2) + radius = tile_size // 3 + pygame.draw.circle(screen, (255, 215, 0), center, radius) + if num: + text = font.render(str(num), True, (0, 0, 0)) + text_rect = text.get_rect(center=center) + screen.blit(text, text_rect) + + if self.grid[y][x].startswith('s'): + font = pygame.font.SysFont(None, tile_size // 2) + text = "s" + center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2) + radius = tile_size // 3 + pygame.draw.circle(screen, (255, 215, 0), center, radius) + if text: + text = font.render(text, True, (0, 0, 0)) + text_rect = text.get_rect(center=center) + screen.blit(text, text_rect) + + if self.grid[y][x].startswith('e'): + font = pygame.font.SysFont(None, tile_size // 2) + text = "e" + center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2) + radius = tile_size // 3 + pygame.draw.circle(screen, (255, 215, 0), center, radius) + if text: + text = font.render(text, True, (0, 0, 0)) + text_rect = text.get_rect(center=center) + screen.blit(text, text_rect) + + + pygame.draw.line(screen, (0, 0, 0), (self.maze_size, 0), (self.maze_size, self.maze_size), 5) + diff --git a/maze_generator.py b/maze_generator.py new file mode 100644 index 0000000..a68a582 --- /dev/null +++ b/maze_generator.py @@ -0,0 +1,256 @@ +import random +import csv +import os + + +class MazeGenerator: + def __init__(self, size, filename, name="Default Maze"): + # 迷宫基础元素 + self.ROUTE = '0' + self.WALL = '1' + # 特殊元素 + self.BOSS = 'b' + self.START = 's' + self.END = 'e' + self.TRAP = 't' + self.MECHANISM = 'l' + self.GOLD = 'g' + + self.size = size + self.maze = [] + self.filename = filename + self.name = name # 迷宫名称 + self.special_elements = [] # 存储特殊元素的位置和值 + + def initialize_maze(self): + """初始化迷宫,四周设置为墙""" + self.maze = [[self.ROUTE for _ in range(self.size)] for _ in range(self.size)] + for i in range(self.size): + self.maze[0][i] = self.WALL + self.maze[i][0] = self.WALL + self.maze[self.size - 1][i] = self.WALL + self.maze[i][self.size - 1] = self.WALL + + def create_maze(self, x1, y1, x2, y2): + """递归分割法生成迷宫""" + if x2 - x1 < 2 or y2 - y1 < 2: + return + + x = x1 + 1 + random.randint(0, (x2 - x1 - 2)) + y = y1 + 1 + random.randint(0, (y2 - y1 - 2)) + + # 画墙 + for i in range(x1, x2 + 1): + self.maze[i][y] = self.WALL + for i in range(y1, y2 + 1): + self.maze[x][i] = self.WALL + + # 递归分割四个区域 + self.create_maze(x1, y1, x - 1, y - 1) + self.create_maze(x + 1, y + 1, x2, y2) + self.create_maze(x + 1, y1, x2, y - 1) + self.create_maze(x1, y + 1, x - 1, y2) + + # 随机打通三面墙 + r = [0, 0, 0, 0] + r[random.randint(0, 3)] = 1 + for i in range(4): + if r[i] == 0: + rx, ry = x, y + if i == 0: # 上方 + while True: + rx = x1 + random.randint(0, (x - x1 - 1)) + wall_count = sum([ + (int)(self.maze[rx - 1][ry]), (int)(self.maze[rx + 1][ry]), + (int)(self.maze[rx][ry - 1]), (int)(self.maze[rx][ry + 1]) + ]) + if wall_count <= 2 * (int)(self.WALL): + break + elif i == 1: # 右侧 + while True: + ry = y + 1 + random.randint(0, (y2 - y - 1)) + wall_count = sum([ + (int)(self.maze[rx - 1][ry]), (int)(self.maze[rx + 1][ry]), + (int)(self.maze[rx][ry - 1]), (int)(self.maze[rx][ry + 1]) + ]) + if wall_count <= 2 * (int)(self.WALL): + break + elif i == 2: # 下方 + while True: + rx = x + 1 + random.randint(0, (x2 - x - 1)) + wall_count = sum([ + (int)(self.maze[rx - 1][ry]), (int)(self.maze[rx + 1][ry]), + (int)(self.maze[rx][ry - 1]), (int)(self.maze[rx][ry + 1]) + ]) + if wall_count <= 2 * (int)(self.WALL): + break + elif i == 3: # 左侧 + while True: + ry = y1 + random.randint(0, (y - y1 - 1)) + wall_count = sum([ + (int)(self.maze[rx - 1][ry]), (int)(self.maze[rx + 1][ry]), + (int)(self.maze[rx][ry - 1]), (int)(self.maze[rx][ry + 1]) + ]) + if wall_count <= 2 * (int)(self.WALL): + break + self.maze[rx][ry] = self.ROUTE + + def set_random_exits(self): + """随机设置迷宫入口和出口""" + available = self.get_available_cells() + if len(available) < 2: + raise ValueError("迷宫空间不足,无法设置随机出入口") + start, end = random.sample(available, 2) + self.maze[start[0]][start[1]] = self.START + self.maze[end[0]][end[1]] = self.END + self.special_elements.extend([(start[0], start[1], self.START), (end[0], end[1], self.END)]) + + def get_available_cells(self): + """获取所有可通行单元格""" + cells = [] + for i in range(1, self.size - 1): + for j in range(1, self.size - 1): + if self.maze[i][j] == self.ROUTE: + cells.append((i, j)) + return cells + + def place_special_elements(self, boss_count=1, traps_range=(3, 8), + mechanisms_range=(2, 6), skill_traps=5,gold_range=(3,8)): + """放置特殊元素(支持技能触发陷阱)""" + available = self.get_available_cells() + random.shuffle(available) + + # 计算所需单元格数量 + required = 2 + boss_count + random.randint(*traps_range) + random.randint(*mechanisms_range) + skill_traps + random.randint(*gold_range) + if len(available) < required: + raise ValueError(f"空间不足,需要{required}个单元格,实际可用{len(available)}") + + # 放置出入口 + + start, end = available.pop(), available.pop() + self.special_elements.extend([(start[0], start[1], self.START), (end[0], end[1], self.END)]) + self.maze[start[0]][start[1]] = self.START + self.maze[end[0]][end[1]] = self.END + + # 放置BOSS + for _ in range(boss_count): + pos = available.pop() + val = random.randint(50, 100) + self.special_elements.append((pos[0], pos[1], f"{self.BOSS}{val}")) + self.maze[pos[0]][pos[1]] = f"{self.BOSS}{val}" + + # 放置普通陷阱 + traps = random.randint(*traps_range) + for _ in range(traps): + pos = available.pop() + val = random.randint(5, 20) + self.special_elements.append((pos[0], pos[1], f"{self.TRAP}{val}")) + self.maze[pos[0]][pos[1]] = f"{self.TRAP}{val}" + + # 放置机关 + mechanisms = random.randint(*mechanisms_range) + for _ in range(mechanisms): + pos = available.pop() + val = random.randint(10, 30) + self.special_elements.append((pos[0], pos[1], f"{self.MECHANISM}{val}")) + self.maze[pos[0]][pos[1]] = f"{self.MECHANISM}{val}" + # 放置金币 + mechanisms = random.randint(*gold_range) + for _ in range(mechanisms): + pos = available.pop() + val = random.randint(10, 30) + self.special_elements.append((pos[0], pos[1], f"{self.GOLD}{val}")) + self.maze[pos[0]][pos[1]] = f"{self.GOLD}{val}" + + def generate(self, seed=None, boss_count=1, traps_range=(3, 8), + mechanisms_range=(2, 6), skill_traps=5): + """生成迷宫主方法""" + random.seed(seed or random.randint(0, 1000)) + self.initialize_maze() + self.create_maze(1, 1, self.size - 2, self.size - 2) + self.place_special_elements(boss_count, traps_range, mechanisms_range, skill_traps) + print(f"成功生成迷宫: {self.name}") + + def export_to_csv(self, filename): + """导出迷宫到CSV文件""" + try: + with open(filename, 'w', newline='') as f: + writer = csv.writer(f) + for row in self.maze: + writer.writerow(row) + print(f"迷宫已导出至: {os.path.abspath(filename)}") + except Exception as e: + print(f"导出失败: {str(e)}") + + def read_from_csv(self): + """从CSV读取迷宫数据""" + try: + with open(self.filename, 'r', newline='') as f: + reader = csv.reader(f) + self.maze = [list(map(self._parse_element, row)) for row in reader] + self.size = len(self.maze) + print(f"成功从{os.path.abspath(self.filename)}读取迷宫") + return True + except Exception as e: + print(f"读取失败: {str(e)}") + return False + + def _parse_element(self, elem): + """解析CSV中的元素类型""" + if elem == 's': return self.START + if elem == 'e': return self.END + if elem.startswith('b'): return self.BOSS + if elem.startswith('t'): return self.TRAP + if elem.startswith('l'): return self.MECHANISM + if elem.startswith('g'): return self.GOLD + + return int(elem) + + def print_maze(self): + """打印迷宫到控制台(带元素标识)""" + symbols = { + self.WALL: '■', self.ROUTE: '·', self.START: 'S', + self.END: 'E', self.BOSS: 'B', self.TRAP: 'T', + self.GOLD: 'G','|':'|','-':'—', + } + for row in self.maze: + display = [] + for cell in row: + symbol = symbols.get(((str)(cell))[0], ((str)(cell))[0]) + display.append(str(symbol)) + print(' '.join(display)) + def read_csv(self): + l = [] + with open(f'{self.filename}', 'r', newline='') as f: + reader = csv.reader(f) + for row in reader: + l.append(row) + return l + + +def main(): + # 示例1: 生成带技能陷阱的迷宫 + generator = MazeGenerator( + size=20, + filename="dungeon_maze.csv", + name="龙脊峡谷迷宫" + ) + generator.generate( + seed=666, + boss_count=2, + traps_range=(5, 10), + mechanisms_range=(3, 7), + skill_traps=8 + ) + generator.print_maze() + generator.export_to_csv() + + + reader = MazeGenerator(size=1, filename="dungeon_maze.csv") + if reader.read_from_csv(): + print("\n读取的迷宫:") + reader.print_maze() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/syht.otf b/syht.otf new file mode 100644 index 0000000..886f82f Binary files /dev/null and b/syht.otf differ diff --git a/tanxin.py b/tanxin.py new file mode 100644 index 0000000..fc413b2 --- /dev/null +++ b/tanxin.py @@ -0,0 +1,210 @@ +import math +from maze import * +import math + +import math + + +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 + + + + + +# 使用示例 +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() + see.print_maze() + player = GreedyPlayer(map_data) + player.find_path() + see.maze = player.marked_map + see.print_maze() + + + + + + +if __name__ == "__main__": + main() + + + +