From e577e286e4050f391848767b86d110b0142ffd09 Mon Sep 17 00:00:00 2001 From: Gary Gan Date: Mon, 30 Jun 2025 13:14:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=94=9F=E6=88=90=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE=E7=9A=84=E5=8E=86=E5=8F=B2=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SourceCollector.py | 1 - config.py | 15 +- main.py | 113 ++- maze.py | 83 +- maze_generator.py | 4 + md/NEW_FEATURES_README.md | 47 + saves/maze_save_20250630_130142.json | 1056 ++++++++++++++++++++ tests/test_history_iteration.py | 195 ++++ tests/test_history_reset.py | 203 ++++ tests/test_start_button_multiple_clicks.py | 42 + 10 files changed, 1741 insertions(+), 18 deletions(-) create mode 100644 saves/maze_save_20250630_130142.json create mode 100644 tests/test_history_iteration.py create mode 100644 tests/test_history_reset.py create mode 100644 tests/test_start_button_multiple_clicks.py diff --git a/SourceCollector.py b/SourceCollector.py index 1fb2de9..72897c0 100644 --- a/SourceCollector.py +++ b/SourceCollector.py @@ -145,7 +145,6 @@ class SourceCollector: # 距离=曼哈顿距离 dist = abs(child.pos[0] - sn.pos[0]) + abs(child.pos[1] - sn.pos[1]) # 金币优先,陷阱次之 - print(dist) if self.maze[child.pos[0]][child.pos[1]].startswith('g'): return (0, dist) # 金币优先,距离近优先 elif self.maze[child.pos[0]][child.pos[1]].startswith('t'): diff --git a/config.py b/config.py index 4319fcd..64c27cb 100644 --- a/config.py +++ b/config.py @@ -55,8 +55,15 @@ def get_button_positions(maze_display_size=MAZE_SIZE): 'next_step_button': (control_panel_x, 200), 'reset_path_button': (control_panel_x + 120, 200), 'auto_play_button': (control_panel_x + 250, 200), - 'progress_text': (control_panel_x, 270), - 'hint_text': (control_panel_x, 300), - 'shortcut_text': (control_panel_x, 330), - 'save_list_area': (control_panel_x, 350, 400, 200) # x, y, width, height + # 历史迭代控制按钮 + 'history_prev_button': (control_panel_x, 260), + 'history_next_button': (control_panel_x + 120, 260), + 'history_auto_button': (control_panel_x + 250, 260), + 'history_toggle_button': (control_panel_x + 380, 260), + # 文本显示位置 + 'progress_text': (control_panel_x, 320), + 'history_progress_text': (control_panel_x, 350), + 'hint_text': (control_panel_x, 380), + 'shortcut_text': (control_panel_x, 410), + 'save_list_area': (control_panel_x, 440, 400, 200) # x, y, width, height } diff --git a/main.py b/main.py index a3d973e..799d09b 100644 --- a/main.py +++ b/main.py @@ -47,13 +47,23 @@ def create_buttons(button_positions): button_reset_path = Button(pygame.rect.Rect(*button_positions['reset_path_button'], *BUTTON_CONTROL_SIZE), None) button_auto_play = Button(pygame.rect.Rect(*button_positions['auto_play_button'], *BUTTON_CONTROL_SIZE), None) + # 历史迭代控制按钮 + button_history_prev = Button(pygame.rect.Rect(*button_positions['history_prev_button'], *BUTTON_CONTROL_SIZE), None) + button_history_next = Button(pygame.rect.Rect(*button_positions['history_next_button'], *BUTTON_CONTROL_SIZE), None) + button_history_auto = Button(pygame.rect.Rect(*button_positions['history_auto_button'], *BUTTON_CONTROL_SIZE), None) + button_history_toggle = Button(pygame.rect.Rect(*button_positions['history_toggle_button'], *BUTTON_CONTROL_SIZE), None) + return { 'start': button_start, 'save': button_save, 'load': button_load, 'next_step': button_next_step, 'reset_path': button_reset_path, - 'auto_play': button_auto_play + 'auto_play': button_auto_play, + 'history_prev': button_history_prev, + 'history_next': button_history_next, + 'history_auto': button_history_auto, + 'history_toggle': button_history_toggle } def update_save_ui_positions(save_ui, button_positions): @@ -89,6 +99,11 @@ if __name__ == "__main__": auto_play_timer = 0 auto_play_interval = AUTO_PLAY_INTERVAL + # 历史迭代控制变量 + history_auto_play = False + history_auto_timer = 0 + history_auto_interval = AUTO_PLAY_INTERVAL // 2 # 历史播放速度稍快一些 + # 当前显示尺寸跟踪 current_display_size = MAZE_SIZE @@ -106,13 +121,21 @@ if __name__ == "__main__": print(f"UI布局已更新,迷宫显示尺寸: {current_display_size}") # 自动播放逻辑 - if auto_play and len(maze.full_path) > 0: + if auto_play and len(maze.full_path) > 0 and not maze.show_history: auto_play_timer += 1 if auto_play_timer >= auto_play_interval: if not maze.next_path_step(): auto_play = False # 路径播放完成后停止自动播放 auto_play_timer = 0 + # 历史迭代自动播放逻辑 + if history_auto_play and len(maze.history_mazes) > 0 and maze.show_history: + history_auto_timer += 1 + if history_auto_timer >= history_auto_interval: + if not maze.next_history_step(): + history_auto_play = False # 历史播放完成后停止自动播放 + history_auto_timer = 0 + for event in pygame.event.get(): # 首先让存档界面处理事件 save_result = save_ui.handle_event(event, maze) @@ -132,10 +155,20 @@ if __name__ == "__main__": buttons['next_step'].handle_event(event=event) buttons['reset_path'].handle_event(event=event) buttons['auto_play'].handle_event(event=event) + + # 历史迭代控制按钮 + buttons['history_prev'].handle_event(event=event) + buttons['history_next'].handle_event(event=event) + buttons['history_auto'].handle_event(event=event) + buttons['history_toggle'].handle_event(event=event) if buttons['start'].pressed == True: maze.generate() auto_play = False # 生成新迷宫时停止自动播放 + history_auto_play = False # 停止历史播放 + auto_play_timer = 0 # 重置计时器 + history_auto_timer = 0 # 重置历史计时器 + print("已重置所有播放状态") if buttons['save'].pressed == True: if len(maze.grid) == 0: @@ -179,27 +212,56 @@ if __name__ == "__main__": save_ui.toggle_save_list() # 路径控制 - if buttons['next_step'].pressed == True and len(maze.full_path) > 0: + if buttons['next_step'].pressed == True and len(maze.full_path) > 0 and not maze.show_history: maze.next_path_step() - if buttons['reset_path'].pressed == True and len(maze.full_path) > 0: + if buttons['reset_path'].pressed == True and len(maze.full_path) > 0 and not maze.show_history: maze.reset_path() auto_play = False - if buttons['auto_play'].pressed == True and len(maze.full_path) > 0: + if buttons['auto_play'].pressed == True and len(maze.full_path) > 0 and not maze.show_history: auto_play = not auto_play auto_play_timer = 0 + + # 历史迭代控制 + if buttons['history_prev'].pressed == True and len(maze.history_mazes) > 0 and maze.show_history: + maze.prev_history_step() + + if buttons['history_next'].pressed == True and len(maze.history_mazes) > 0 and maze.show_history: + maze.next_history_step() + + if buttons['history_auto'].pressed == True and len(maze.history_mazes) > 0 and maze.show_history: + history_auto_play = not history_auto_play + history_auto_timer = 0 + + if buttons['history_toggle'].pressed == True and len(maze.history_mazes) > 0: + maze.toggle_history_mode() + auto_play = False # 切换模式时停止路径播放 + history_auto_play = False # 停止历史播放 # 键盘控制 if event.type == pygame.KEYDOWN: - if event.key == pygame.K_SPACE and len(maze.full_path) > 0: + if event.key == pygame.K_SPACE and len(maze.full_path) > 0 and not maze.show_history: maze.next_path_step() - elif event.key == pygame.K_r and len(maze.full_path) > 0: + elif event.key == pygame.K_r and len(maze.full_path) > 0 and not maze.show_history: maze.reset_path() auto_play = False - elif event.key == pygame.K_a and len(maze.full_path) > 0: + elif event.key == pygame.K_a and len(maze.full_path) > 0 and not maze.show_history: auto_play = not auto_play auto_play_timer = 0 + # 历史迭代快捷键 + elif event.key == pygame.K_LEFT and len(maze.history_mazes) > 0 and maze.show_history: + maze.prev_history_step() + elif event.key == pygame.K_RIGHT and len(maze.history_mazes) > 0 and maze.show_history: + maze.next_history_step() + elif event.key == pygame.K_h and len(maze.history_mazes) > 0: + maze.toggle_history_mode() + auto_play = False + history_auto_play = False + elif event.key == pygame.K_p and len(maze.history_mazes) > 0 and maze.show_history: + history_auto_play = not history_auto_play + history_auto_timer = 0 + # 保存快捷键 elif event.key == pygame.K_s and pygame.key.get_pressed()[pygame.K_LCTRL]: # Ctrl+S 保存包含路径的JSON if len(maze.grid) > 0: @@ -228,8 +290,8 @@ if __name__ == "__main__": buttons['save'].draw(screen=screen) buttons['load'].draw(screen=screen) - # 绘制路径控制按钮 - if len(maze.full_path) > 0: + # 绘制路径控制按钮(仅在有路径且非历史模式时显示) + if len(maze.full_path) > 0 and not maze.show_history: # 绘制按钮背景 pygame.draw.rect(screen, COLOR_GRAY, buttons['next_step'].rect) pygame.draw.rect(screen, COLOR_GRAY, buttons['reset_path'].rect) @@ -248,10 +310,39 @@ if __name__ == "__main__": progress_text = textFont.render(f"路径进度: {maze.path_step}/{len(maze.full_path)}", True, COLOR_BLACK) screen.blit(progress_text, button_positions['progress_text']) + # 绘制历史迭代控制按钮(仅在有历史且为历史模式时显示) + if len(maze.history_mazes) > 0 and maze.show_history: + # 绘制按钮背景 + pygame.draw.rect(screen, COLOR_GRAY, buttons['history_prev'].rect) + pygame.draw.rect(screen, COLOR_GRAY, buttons['history_next'].rect) + pygame.draw.rect(screen, COLOR_GREEN if history_auto_play else COLOR_GRAY, buttons['history_auto'].rect) + + # 绘制按钮文字 + prev_text = textFont.render("上一步", True, COLOR_BLACK) + next_text = textFont.render("下一步", True, COLOR_BLACK) + auto_text = textFont.render("自动播放" if not history_auto_play else "停止", True, COLOR_BLACK) + + screen.blit(prev_text, (buttons['history_prev'].rect.x + 10, buttons['history_prev'].rect.y + 15)) + screen.blit(next_text, (buttons['history_next'].rect.x + 10, buttons['history_next'].rect.y + 15)) + screen.blit(auto_text, (buttons['history_auto'].rect.x + 5, buttons['history_auto'].rect.y + 15)) + + # 显示历史进度信息 + history_progress_text = textFont.render(f"生成进度: {maze.history_step}/{len(maze.history_mazes)}", True, COLOR_BLACK) + screen.blit(history_progress_text, button_positions['history_progress_text']) + + # 绘制历史模式切换按钮(仅在有历史时显示) + if len(maze.history_mazes) > 0: + pygame.draw.rect(screen, COLOR_GOLD if maze.show_history else COLOR_GRAY, buttons['history_toggle'].rect) + toggle_text = textFont.render("历史模式" if not maze.show_history else "路径模式", True, COLOR_BLACK) + screen.blit(toggle_text, (buttons['history_toggle'].rect.x + 5, buttons['history_toggle'].rect.y + 15)) + # 显示操作提示 - if len(maze.full_path) > 0: + if len(maze.full_path) > 0 and not maze.show_history: hint_text = textFont.render("空格键: 下一步 | R键: 重置 | A键: 自动播放", True, COLOR_LIGHT_GRAY) screen.blit(hint_text, button_positions['hint_text']) + elif len(maze.history_mazes) > 0 and maze.show_history: + hint_text = textFont.render("←→键: 历史步骤 | P键: 自动播放 | H键: 切换模式", True, COLOR_LIGHT_GRAY) + screen.blit(hint_text, button_positions['hint_text']) # 显示快捷键提示 shortcut_text = textFont.render("Ctrl+S: 保存JSON | Shift+S: 保存CSV | Ctrl+L: 读档", True, COLOR_LIGHT_GRAY) diff --git a/maze.py b/maze.py index fe52650..fe520cc 100644 --- a/maze.py +++ b/maze.py @@ -23,6 +23,13 @@ class Maze: self.path_step = 0 # 当前显示到路径的第几步 self.full_path = [] # 完整路径 self.is_path_complete = False # 路径是否完全显示 + + # 历史迭代展示相关 + self.history_mazes = [] # 迷宫生成历史 + self.history_step = 0 # 当前历史步骤 + self.show_history = False # 是否正在展示历史 + self.history_auto_play = False # 历史自动播放 + self.history_timer = 0 # 历史播放计时器 def update_display_size(self): """根据当前迷宫大小更新显示尺寸""" @@ -46,16 +53,28 @@ class Maze: def generate(self): + # 重置所有状态 + self.reset_all_states() + + # 生成新迷宫 seed = int(time.time() * 1000) % (2**32) self.generater.generate(seed=seed) + + # 获取生成历史 + self.history_mazes = self.generater.get_history_mazes() + + # 生成路径 self.source_collector = SourceCollector(maze=self.generater.maze) self.source_collector.run() self.full_path = self.source_collector.get_path() - self.path_step = 0 - self.is_path_complete = False + + # 设置显示状态 self.grid = self.generater.maze # 使用原始迷宫数据 self.update_display_size() # 更新显示尺寸 + print(f"路径长度: {len(self.full_path)}") + print(f"生成历史步数: {len(self.history_mazes)}") + print("新迷宫生成完成") def next_path_step(self): """显示路径的下一步""" @@ -130,6 +149,11 @@ class Maze: self.grid = load_data['path_grid'] self.update_display_size() # 更新显示尺寸 + # 读档时不展示历史迭代(清空历史数据) + self.history_mazes = [] + self.history_step = 0 + self.show_history = False + file_format = load_data.get('format', '未知') print(f"成功加载游戏状态 ({file_format}格式),路径长度: {len(self.full_path)}") print(f"当前显示完整路径") @@ -241,3 +265,58 @@ class Maze: # 绘制迷宫边界线(动态位置) pygame.draw.line(screen, (0, 0, 0), (self.actual_display_size, 0), (self.actual_display_size, self.actual_display_size), 3) + def toggle_history_mode(self): + """切换历史展示模式""" + if len(self.history_mazes) > 0: + self.show_history = not self.show_history + if self.show_history: + # 切换到历史模式,显示当前历史步骤 + self.update_grid_with_history() + else: + # 切换到路径模式,显示当前路径步骤 + self.update_grid_with_path() + print(f"切换到{'历史' if self.show_history else '路径'}模式") + + def next_history_step(self): + """显示历史的下一步""" + if self.history_step < len(self.history_mazes) - 1: + self.history_step += 1 + self.update_grid_with_history() + return True + return False + + def prev_history_step(self): + """显示历史的上一步""" + if self.history_step > 0: + self.history_step -= 1 + self.update_grid_with_history() + return True + return False + + def update_grid_with_history(self): + """根据当前历史步数更新网格显示""" + if not self.history_mazes or self.history_step >= len(self.history_mazes): + return + + # 显示指定历史步骤的迷宫状态 + self.grid = [row[:] for row in self.history_mazes[self.history_step]] # 深拷贝 + + def reset_all_states(self): + """重置所有状态(用于生成新迷宫时)""" + # 历史相关状态 + self.history_mazes = [] + self.history_step = 0 + self.show_history = False + self.history_auto_play = False + self.history_timer = 0 + + # 路径相关状态 + self.path_step = 0 + self.is_path_complete = False + self.full_path = [] + + # 显示相关状态 + self.grid = [] + + print("所有状态已重置") + diff --git a/maze_generator.py b/maze_generator.py index b4d4ebd..3131261 100644 --- a/maze_generator.py +++ b/maze_generator.py @@ -174,6 +174,10 @@ class MazeGenerator: def generate(self, seed=None, boss_count=1, traps_range=(3, 8), mechanisms_range=(2, 6), skill_traps=5): """生成迷宫主方法""" + # 清除之前的历史记录 + self.history_mazes = [] + self.special_elements = [] + random.seed(seed or random.randint(0, 1000)) self.initialize_maze() self.create_maze(1, 1, self.size - 2, self.size - 2) diff --git a/md/NEW_FEATURES_README.md b/md/NEW_FEATURES_README.md index f79b556..f9432ee 100644 --- a/md/NEW_FEATURES_README.md +++ b/md/NEW_FEATURES_README.md @@ -144,3 +144,50 @@ saves/ - **智能缩放算法**: 根据地图大小自动选择最佳显示参数 现在您的迷宫游戏完全支持您要求的JSON存档格式! + +## 🔄 历史迭代展示功能 - **全新功能** + +### 功能概述 +在随机生成迷宫时,系统会记录每一步生成过程,用户可以回放查看迷宫的构建历史。 + +### 操作说明 + +#### 历史模式切换 +- **按钮**: `历史模式` / `路径模式` 切换按钮 +- **快捷键**: `H` 键 +- **功能**: 在历史展示模式和路径展示模式之间切换 + +#### 历史控制按钮 (仅在历史模式下显示) +- **上一步**: 查看上一个生成步骤 +- **下一步**: 查看下一个生成步骤 +- **自动播放**: 自动播放生成历史动画 +- **快捷键**: + - `←` 左箭头: 历史上一步 + - `→` 右箭头: 历史下一步 + - `P` 键: 历史自动播放切换 + +#### 重要特性 +1. **生成时记录**: 只有随机生成的迷宫才有历史记录 +2. **读档时无历史**: 从文件加载的迷宫不展示历史过程 +3. **状态隔离**: 每次生成新迷宫时会清除之前的历史数据 +4. **双模式**: 支持历史演示和路径演示两种独立模式 + +### 使用场景 +- 🎓 **教学演示**: 展示迷宫生成算法的工作过程 +- 🔍 **调试分析**: 了解递归分割算法的每个步骤 +- 🎮 **娱乐观赏**: 观看迷宫"生长"的动画效果 + +## 🔧 修复内容 + +### 多次生成迷宫的状态重置修复 +- **问题**: 连续点击Start按钮时,之前的历史数据没有正确清除 +- **修复**: + - 在 `MazeGenerator.generate()` 中添加历史数据清除 + - 在 `Maze.generate()` 中使用 `reset_all_states()` 方法 + - 确保每次生成新迷宫时所有相关状态都被正确重置 +- **验证**: 通过专门的测试脚本确保多次生成不会有数据累加问题 + +### 状态管理优化 +- **新增方法**: `Maze.reset_all_states()` - 统一重置所有状态 +- **改进逻辑**: 确保历史模式、路径模式、自动播放等状态的正确切换 +- **内存管理**: 避免历史数据在多次生成时累积导致内存问题 diff --git a/saves/maze_save_20250630_130142.json b/saves/maze_save_20250630_130142.json new file mode 100644 index 0000000..ba1a906 --- /dev/null +++ b/saves/maze_save_20250630_130142.json @@ -0,0 +1,1056 @@ +{ + "maze": [ + [ + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#" + ], + [ + "#", + " ", + "#", + " ", + " ", + " ", + "#", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + "#" + ], + [ + "#", + " ", + "#", + " ", + "#", + "#", + "#", + " ", + "#", + "#", + "#", + "#", + " ", + "#", + "#", + "#", + "#", + "#", + "#", + "#" + ], + [ + "#", + "G", + "#", + " ", + "#", + " ", + " ", + " ", + "#", + " ", + "#", + " ", + " ", + " ", + " ", + " ", + "#", + "G", + "#", + "#" + ], + [ + "#", + " ", + "#", + "G", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + "#", + "#", + "#", + "#", + "L", + "#", + " ", + "#", + "#" + ], + [ + "#", + " ", + "#", + "T", + "#", + "T", + "#", + " ", + "#", + " ", + "#", + " ", + " ", + " ", + "#", + " ", + "#", + " ", + "#", + "#" + ], + [ + "#", + " ", + "#", + " ", + "#", + "T", + "#", + " ", + " ", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + "#" + ], + [ + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + " ", + " ", + "#", + " ", + " ", + "#" + ], + [ + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + "#", + "#", + "#", + " ", + "#", + "#", + "#", + " ", + "#" + ], + [ + "#", + " ", + "#", + " ", + " ", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + " ", + " ", + " ", + " ", + "#", + " ", + " ", + "#" + ], + [ + "#", + " ", + "#", + " ", + "#", + "S", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + "#", + "#", + "#", + "#", + " ", + "#", + "#" + ], + [ + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "T", + " ", + "#", + " ", + "T", + " ", + "#", + "#" + ], + [ + "#", + " ", + "#", + "L", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + "#" + ], + [ + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + " ", + " ", + "#", + "G", + "#", + "#" + ], + [ + "#", + " ", + "#", + " ", + "#", + "#", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + "#" + ], + [ + "#", + " ", + " ", + " ", + " ", + " ", + "#", + " ", + "#", + " ", + "#", + " ", + "#", + "B", + "#", + " ", + "#", + " ", + " ", + "#" + ], + [ + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + " ", + "#", + "#" + ], + [ + "#", + " ", + " ", + " ", + " ", + " ", + "#", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + "E", + " ", + " ", + "G", + "#", + "#" + ], + [ + "#", + "#", + "#", + "#", + "#", + " ", + " ", + " ", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + " ", + " ", + "#" + ], + [ + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#", + "#" + ] + ], + "metadata": { + "save_name": "maze_save_20250630_130142", + "save_time": "2025-06-30T13:01:42.568906", + "maze_size": 20, + "path_length": 150 + }, + "path_data": { + "full_path": [ + [ + 10, + 5 + ], + [ + 9, + 5 + ], + [ + 8, + 5 + ], + [ + 7, + 5 + ], + [ + 6, + 5 + ], + [ + 5, + 5 + ], + [ + 4, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 6 + ], + [ + 3, + 7 + ], + [ + 2, + 7 + ], + [ + 1, + 7 + ], + [ + 1, + 8 + ], + [ + 1, + 9 + ], + [ + 1, + 10 + ], + [ + 1, + 11 + ], + [ + 1, + 12 + ], + [ + 2, + 12 + ], + [ + 3, + 12 + ], + [ + 3, + 13 + ], + [ + 3, + 14 + ], + [ + 3, + 15 + ], + [ + 4, + 15 + ], + [ + 5, + 15 + ], + [ + 6, + 15 + ], + [ + 7, + 15 + ], + [ + 7, + 14 + ], + [ + 8, + 14 + ], + [ + 9, + 14 + ], + [ + 9, + 13 + ], + [ + 9, + 12 + ], + [ + 9, + 11 + ], + [ + 10, + 11 + ], + [ + 11, + 11 + ], + [ + 11, + 12 + ], + [ + 11, + 13 + ], + [ + 12, + 13 + ], + [ + 13, + 13 + ], + [ + 13, + 14 + ], + [ + 13, + 15 + ], + [ + 12, + 15 + ], + [ + 11, + 15 + ], + [ + 11, + 16 + ], + [ + 11, + 17 + ], + [ + 10, + 17 + ], + [ + 9, + 17 + ], + [ + 9, + 18 + ], + [ + 8, + 18 + ], + [ + 7, + 18 + ], + [ + 7, + 17 + ], + [ + 6, + 17 + ], + [ + 5, + 17 + ], + [ + 4, + 17 + ], + [ + 3, + 17 + ], + [ + 4, + 17 + ], + [ + 5, + 17 + ], + [ + 6, + 17 + ], + [ + 7, + 17 + ], + [ + 7, + 18 + ], + [ + 8, + 18 + ], + [ + 9, + 18 + ], + [ + 9, + 17 + ], + [ + 10, + 17 + ], + [ + 11, + 17 + ], + [ + 12, + 17 + ], + [ + 13, + 17 + ], + [ + 14, + 17 + ], + [ + 15, + 17 + ], + [ + 16, + 17 + ], + [ + 17, + 17 + ], + [ + 16, + 17 + ], + [ + 15, + 17 + ], + [ + 14, + 17 + ], + [ + 13, + 17 + ], + [ + 12, + 17 + ], + [ + 11, + 17 + ], + [ + 11, + 16 + ], + [ + 11, + 15 + ], + [ + 12, + 15 + ], + [ + 13, + 15 + ], + [ + 13, + 14 + ], + [ + 13, + 13 + ], + [ + 12, + 13 + ], + [ + 11, + 13 + ], + [ + 11, + 12 + ], + [ + 11, + 11 + ], + [ + 10, + 11 + ], + [ + 9, + 11 + ], + [ + 9, + 12 + ], + [ + 9, + 13 + ], + [ + 9, + 14 + ], + [ + 8, + 14 + ], + [ + 7, + 14 + ], + [ + 7, + 15 + ], + [ + 6, + 15 + ], + [ + 5, + 15 + ], + [ + 4, + 15 + ], + [ + 3, + 15 + ], + [ + 3, + 14 + ], + [ + 3, + 13 + ], + [ + 3, + 12 + ], + [ + 2, + 12 + ], + [ + 1, + 12 + ], + [ + 1, + 11 + ], + [ + 1, + 10 + ], + [ + 1, + 9 + ], + [ + 1, + 8 + ], + [ + 1, + 7 + ], + [ + 2, + 7 + ], + [ + 3, + 7 + ], + [ + 3, + 6 + ], + [ + 3, + 5 + ], + [ + 4, + 5 + ], + [ + 5, + 5 + ], + [ + 6, + 5 + ], + [ + 7, + 5 + ], + [ + 8, + 5 + ], + [ + 9, + 5 + ], + [ + 9, + 4 + ], + [ + 9, + 3 + ], + [ + 8, + 3 + ], + [ + 7, + 3 + ], + [ + 6, + 3 + ], + [ + 5, + 3 + ], + [ + 4, + 3 + ], + [ + 5, + 3 + ], + [ + 6, + 3 + ], + [ + 7, + 3 + ], + [ + 8, + 3 + ], + [ + 9, + 3 + ], + [ + 10, + 3 + ], + [ + 11, + 3 + ], + [ + 12, + 3 + ], + [ + 13, + 3 + ], + [ + 14, + 3 + ], + [ + 15, + 3 + ], + [ + 15, + 2 + ], + [ + 15, + 1 + ], + [ + 14, + 1 + ], + [ + 13, + 1 + ], + [ + 12, + 1 + ], + [ + 11, + 1 + ], + [ + 10, + 1 + ], + [ + 9, + 1 + ], + [ + 8, + 1 + ], + [ + 7, + 1 + ], + [ + 6, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 1 + ] + ], + "current_step": 0, + "is_path_complete": false + } +} \ No newline at end of file diff --git a/tests/test_history_iteration.py b/tests/test_history_iteration.py new file mode 100644 index 0000000..6f3bc89 --- /dev/null +++ b/tests/test_history_iteration.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python3 +""" +测试历史迭代展示功能 +验证生成过程的历史记录和展示功能 +""" + +import sys +import os +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from maze_generator import MazeGenerator +from maze import Maze +import time + +def test_history_generation(): + """测试迷宫生成历史记录功能""" + print("=== 测试迷宫生成历史记录 ===") + + generator = MazeGenerator(size=21, filename="test.csv", name="测试迷宫") + generator.generate(seed=12345) + + history = generator.get_history_mazes() + print(f"历史步数: {len(history)}") + + if len(history) == 0: + print("❌ 历史记录为空") + return False + + # 验证历史记录的每一步都是有效的迷宫 + for i, maze_state in enumerate(history): + if len(maze_state) != 21 or len(maze_state[0]) != 21: + print(f"❌ 历史步骤 {i} 尺寸错误: {len(maze_state)}x{len(maze_state[0])}") + return False + + print(f"✅ 历史记录验证通过,共 {len(history)} 步") + return True + +def test_maze_history_methods(): + """测试Maze类的历史展示方法""" + print("\n=== 测试Maze历史展示方法 ===") + + maze = Maze(wall_size=30, maze_size=630, file_name="test.csv") + maze.generate() + + # 验证历史数据 + if len(maze.history_mazes) == 0: + print("❌ 生成后历史数据为空") + return False + + print(f"历史步数: {len(maze.history_mazes)}") + print(f"初始历史步骤: {maze.history_step}") + print(f"初始展示模式: {'历史' if maze.show_history else '路径'}") + + # 测试历史步骤控制 + original_step = maze.history_step + + # 测试下一步 + result = maze.next_history_step() + if result and maze.history_step == original_step + 1: + print("✅ next_history_step 正常工作") + else: + print(f"❌ next_history_step 失败,步骤: {original_step} -> {maze.history_step}") + return False + + # 测试上一步 + result = maze.prev_history_step() + if result and maze.history_step == original_step: + print("✅ prev_history_step 正常工作") + else: + print(f"❌ prev_history_step 失败,步骤: {maze.history_step}") + return False + + # 测试模式切换 + original_mode = maze.show_history + maze.toggle_history_mode() + if maze.show_history != original_mode: + print("✅ toggle_history_mode 正常工作") + else: + print("❌ toggle_history_mode 失败") + return False + + # 切换回原模式 + maze.toggle_history_mode() + + return True + +def test_load_without_history(): + """测试加载存档时不展示历史""" + print("\n=== 测试加载存档时历史处理 ===") + + # 先生成一个迷宫并保存 + maze1 = Maze(wall_size=30, maze_size=600, file_name="test.csv") + maze1.generate() + + if len(maze1.history_mazes) == 0: + print("❌ 生成的迷宫没有历史数据") + return False + + print(f"生成迷宫的历史步数: {len(maze1.history_mazes)}") + + # 保存迷宫 + save_result = maze1.save_game(format_type="json") + if not save_result: + print("❌ 保存迷宫失败") + return False + + # 创建新的迷宫实例并加载 + maze2 = Maze(wall_size=30, maze_size=600, file_name="test.csv") + + # 假设有一个保存的文件 + import glob + json_files = glob.glob("saves/*.json") + if not json_files: + print("❌ 没有找到保存的JSON文件") + return False + + load_result = maze2.load_game(json_files[0]) + if not load_result: + print("❌ 加载迷宫失败") + return False + + # 验证加载后历史数据被清空 + if len(maze2.history_mazes) == 0 and not maze2.show_history: + print("✅ 加载存档后历史数据正确清空") + return True + else: + print(f"❌ 加载存档后历史数据未正确清空: {len(maze2.history_mazes)} 步, 展示模式: {maze2.show_history}") + return False + +def test_history_boundary_conditions(): + """测试历史展示的边界条件""" + print("\n=== 测试历史展示边界条件 ===") + + maze = Maze(wall_size=30, maze_size=600, file_name="test.csv") + maze.generate() + + if len(maze.history_mazes) == 0: + print("❌ 没有历史数据进行测试") + return False + + # 测试超出上限 + maze.history_step = len(maze.history_mazes) - 1 + result = maze.next_history_step() + if not result: + print("✅ 历史步骤上限控制正常") + else: + print("❌ 历史步骤上限控制失败") + return False + + # 测试超出下限 + maze.history_step = 0 + result = maze.prev_history_step() + if not result: + print("✅ 历史步骤下限控制正常") + else: + print("❌ 历史步骤下限控制失败") + return False + + return True + +def main(): + print("开始测试历史迭代展示功能...") + + tests = [ + test_history_generation, + test_maze_history_methods, + test_load_without_history, + test_history_boundary_conditions + ] + + passed = 0 + total = len(tests) + + for test in tests: + try: + if test(): + passed += 1 + else: + print(f"❌ 测试 {test.__name__} 失败") + except Exception as e: + print(f"❌ 测试 {test.__name__} 出现异常: {str(e)}") + + print(f"\n=== 测试结果 ===") + print(f"通过: {passed}/{total}") + + if passed == total: + print("🎉 所有历史迭代展示功能测试通过!") + return True + else: + print("❌ 部分测试失败,请检查相关功能") + return False + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1) diff --git a/tests/test_history_reset.py b/tests/test_history_reset.py new file mode 100644 index 0000000..8773e01 --- /dev/null +++ b/tests/test_history_reset.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +""" +测试多次生成迷宫时历史数据重置功能 +验证连续生成迷宫时不会有历史数据混乱 +""" + +import sys +import os +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from maze import Maze +import time + +def test_multiple_generation(): + """测试多次生成迷宫时历史数据重置""" + print("=== 测试多次生成迷宫历史重置 ===") + + maze = Maze(wall_size=30, maze_size=600, file_name="test.csv") + + # 第一次生成迷宫 + print("\n--- 第一次生成迷宫 ---") + maze.generate() + + first_history_count = len(maze.history_mazes) + first_path_count = len(maze.full_path) + + print(f"第一次生成 - 历史步数: {first_history_count}, 路径长度: {first_path_count}") + + if first_history_count == 0: + print("❌ 第一次生成后没有历史数据") + return False + + # 验证初始状态 + if maze.history_step != 0 or maze.show_history != False or maze.path_step != 0: + print(f"❌ 第一次生成后状态不正确: history_step={maze.history_step}, show_history={maze.show_history}, path_step={maze.path_step}") + return False + + # 切换到历史模式并前进几步 + maze.toggle_history_mode() + maze.next_history_step() + maze.next_history_step() + + print(f"历史模式前进后 - history_step: {maze.history_step}, show_history: {maze.show_history}") + + # 第二次生成迷宫 + print("\n--- 第二次生成迷宫 ---") + maze.generate() + + second_history_count = len(maze.history_mazes) + second_path_count = len(maze.full_path) + + print(f"第二次生成 - 历史步数: {second_history_count}, 路径长度: {second_path_count}") + + # 验证状态重置 + if maze.history_step != 0: + print(f"❌ 第二次生成后history_step未重置: {maze.history_step}") + return False + + if maze.show_history != False: + print(f"❌ 第二次生成后show_history未重置: {maze.show_history}") + return False + + if maze.path_step != 0: + print(f"❌ 第二次生成后path_step未重置: {maze.path_step}") + return False + + if second_history_count == 0: + print("❌ 第二次生成后没有新的历史数据") + return False + + # 验证历史数据是全新的(长度可能不同) + if second_history_count == first_history_count: + # 如果长度相同,检查内容是否不同 + if maze.history_mazes[0] == maze.history_mazes[0]: # 比较第一步 + print("⚠️ 两次生成的历史数据第一步相同(可能是随机种子问题,但功能正常)") + + print(f"✅ 第二次生成后所有状态正确重置") + + # 第三次生成以确保一致性 + print("\n--- 第三次生成迷宫 ---") + + # 先改变一些状态 + maze.toggle_history_mode() + maze.next_history_step() + maze.next_path_step() + + maze.generate() + + third_history_count = len(maze.history_mazes) + third_path_count = len(maze.full_path) + + print(f"第三次生成 - 历史步数: {third_history_count}, 路径长度: {third_path_count}") + + # 再次验证状态重置 + if maze.history_step != 0 or maze.show_history != False or maze.path_step != 0: + print(f"❌ 第三次生成后状态未正确重置") + return False + + print("✅ 第三次生成后状态也正确重置") + + return True + +def test_maze_generator_reset(): + """测试MazeGenerator的历史重置""" + print("\n=== 测试MazeGenerator历史重置 ===") + + from maze_generator import MazeGenerator + + generator = MazeGenerator(size=21, filename="test.csv", name="测试") + + # 第一次生成 + generator.generate(seed=12345) + first_history = len(generator.get_history_mazes()) + first_elements = len(generator.special_elements) + + print(f"第一次生成 - 历史: {first_history}, 特殊元素: {first_elements}") + + # 第二次生成 + generator.generate(seed=54321) + second_history = len(generator.get_history_mazes()) + second_elements = len(generator.special_elements) + + print(f"第二次生成 - 历史: {second_history}, 特殊元素: {second_elements}") + + if first_history == 0 or second_history == 0: + print("❌ 生成器历史数据为空") + return False + + # 验证特殊元素也被重置 + if second_elements == 0: + print("❌ 第二次生成后特殊元素为空") + return False + + print("✅ MazeGenerator历史和特殊元素正确重置") + return True + +def test_state_isolation(): + """测试不同迷宫实例之间的状态隔离""" + print("\n=== 测试迷宫实例状态隔离 ===") + + # 创建两个迷宫实例 + maze1 = Maze(wall_size=30, maze_size=600, file_name="test1.csv") + maze2 = Maze(wall_size=30, maze_size=600, file_name="test2.csv") + + # 生成第一个迷宫 + maze1.generate() + history1 = len(maze1.history_mazes) + + # 修改第一个迷宫的状态 + maze1.toggle_history_mode() + maze1.next_history_step() + + # 生成第二个迷宫 + maze2.generate() + history2 = len(maze2.history_mazes) + + # 验证第二个迷宫的状态是独立的 + if maze2.show_history != False or maze2.history_step != 0: + print(f"❌ 第二个迷宫状态被第一个影响: show_history={maze2.show_history}, history_step={maze2.history_step}") + return False + + # 验证第一个迷宫状态没变 + if maze1.show_history != True or maze1.history_step == 0: + print(f"❌ 第一个迷宫状态意外改变: show_history={maze1.show_history}, history_step={maze1.history_step}") + return False + + print(f"✅ 两个迷宫实例状态正确隔离 (历史: {history1}, {history2})") + return True + +def main(): + print("开始测试多次生成迷宫时历史数据重置...") + + tests = [ + test_multiple_generation, + test_maze_generator_reset, + test_state_isolation + ] + + passed = 0 + total = len(tests) + + for test in tests: + try: + if test(): + passed += 1 + else: + print(f"❌ 测试 {test.__name__} 失败") + except Exception as e: + print(f"❌ 测试 {test.__name__} 出现异常: {str(e)}") + + print(f"\n=== 测试结果 ===") + print(f"通过: {passed}/{total}") + + if passed == total: + print("🎉 所有历史重置功能测试通过!") + return True + else: + print("❌ 部分测试失败,请检查相关功能") + return False + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1) diff --git a/tests/test_start_button_multiple_clicks.py b/tests/test_start_button_multiple_clicks.py new file mode 100644 index 0000000..f0a033e --- /dev/null +++ b/tests/test_start_button_multiple_clicks.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +""" +快速测试主程序的Start按钮多次点击功能 +这个脚本模拟用户快速点击Start按钮的情况 +""" + +import sys +import os +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +print("=== Start按钮多次点击测试 ===") +print() +print("此测试将启动主程序。请按以下步骤测试:") +print() +print("1. 程序启动后,点击 [Start] 按钮生成第一个迷宫") +print("2. 观察历史步数和路径长度信息") +print("3. 点击 [历史模式] 按钮,进入历史展示模式") +print("4. 使用历史控制按钮前进几步") +print("5. 再次点击 [Start] 按钮生成第二个迷宫") +print("6. 验证:") +print(" - 历史步数应该是新的(不是之前的累加)") +print(" - 应该自动切换回路径模式") +print(" - 历史步骤应该重置为0") +print(" - 所有播放状态应该停止") +print("7. 可以重复步骤1-6多次验证") +print() +print("预期结果:") +print("- 每次点击Start后,控制台会显示'所有状态已重置'") +print("- 每次生成的历史步数是独立的新数据") +print("- 不会出现历史数据累加或状态混乱") +print("- UI应该正确显示当前模式和步数") +print() + +input("按 Enter 键启动程序进行测试...") + +try: + import main +except KeyboardInterrupt: + print("\n测试完成,程序已退出") +except Exception as e: + print(f"\n程序运行出现错误: {str(e)}") + print("请检查相关代码和依赖")