实现路径显示
This commit is contained in:
parent
05b35d4640
commit
460256c1cb
72
CONFIG_README.md
Normal file
72
CONFIG_README.md
Normal file
@ -0,0 +1,72 @@
|
||||
# 配置文件说明
|
||||
|
||||
## 概述
|
||||
`config.py` 包含了整个迷宫游戏的所有配置参数,使得项目更易于维护和自定义。
|
||||
|
||||
## 配置参数说明
|
||||
|
||||
### UI 界面配置
|
||||
- `UI_HEIGHT = 1000`: 游戏窗口高度
|
||||
- `UI_WIDTH = 1500`: 游戏窗口宽度
|
||||
|
||||
### 迷宫配置
|
||||
- `MAZE_SIZE = 800`: 迷宫显示区域大小
|
||||
- `WALL_SIZE = 50`: 每个墙体/格子的像素大小
|
||||
|
||||
### 性能配置
|
||||
- `FPS = 120`: 游戏帧率
|
||||
- `AUTO_PLAY_INTERVAL = 30`: 自动播放时每多少帧前进一步
|
||||
|
||||
### 按钮尺寸配置
|
||||
- `BUTTON_START_SIZE = (200, 100)`: 开始按钮尺寸
|
||||
- `BUTTON_SAVE_SIZE = (80, 80)`: 保存按钮尺寸
|
||||
- `BUTTON_CONTROL_SIZE = (100, 50)`: 路径控制按钮尺寸
|
||||
|
||||
### 字体配置
|
||||
- `FONT_FILE = "syht.otf"`: 字体文件路径
|
||||
- `FONT_SIZE = 18`: 字体大小
|
||||
|
||||
### 资源路径配置
|
||||
所有图片资源的路径都定义在这里,便于统一管理:
|
||||
- `WALL_IMAGE`: 墙体纹理
|
||||
- `COIN_IMAGE`: 金币纹理
|
||||
- `TRAP_IMAGE`: 陷阱纹理
|
||||
- `START_BUTTON_IMAGE`: 开始按钮图片
|
||||
- `SAVE_BUTTON_IMAGE`: 保存按钮图片
|
||||
|
||||
### 颜色配置
|
||||
预定义了常用的颜色值:
|
||||
- `COLOR_WHITE`: 白色背景
|
||||
- `COLOR_BLACK`: 黑色文字
|
||||
- `COLOR_GRAY`: 灰色按钮
|
||||
- `COLOR_GREEN`: 绿色高亮
|
||||
- `COLOR_GOLD`: 金色路径
|
||||
|
||||
### 布局配置
|
||||
`get_button_positions()` 函数返回所有UI元素的位置,包括:
|
||||
- 各种按钮的位置
|
||||
- 文字显示的位置
|
||||
- 根据窗口大小自动计算相对位置
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 导入配置
|
||||
```python
|
||||
from config import *
|
||||
```
|
||||
|
||||
### 获取按钮位置
|
||||
```python
|
||||
button_positions = get_button_positions()
|
||||
start_x, start_y = button_positions['start_button']
|
||||
```
|
||||
|
||||
### 修改配置
|
||||
要修改游戏参数,只需编辑 `config.py` 文件即可,无需修改主程序代码。
|
||||
|
||||
## 优势
|
||||
1. **集中管理**: 所有配置参数集中在一个文件中
|
||||
2. **易于维护**: 修改参数不需要在多个文件中查找
|
||||
3. **可扩展性**: 新增配置项只需在配置文件中添加
|
||||
4. **代码整洁**: 主程序代码更加简洁明了
|
||||
5. **复用性**: 配置可以被多个模块共享使用
|
138
CSV_SAVE_SYSTEM_README.md
Normal file
138
CSV_SAVE_SYSTEM_README.md
Normal file
@ -0,0 +1,138 @@
|
||||
# CSV存档系统使用说明
|
||||
|
||||
## 🎯 新的存档方式
|
||||
|
||||
根据您的需求,存档系统已经完全重新设计,现在只使用CSV格式保存迷宫和路径信息。
|
||||
|
||||
### 📁 文件格式
|
||||
|
||||
存档文件采用CSV格式,路径信息直接嵌入在迷宫数据中:
|
||||
|
||||
#### 路径标记格式:
|
||||
- **空地 + 路径**: `0` → `p10` (表示第10步)
|
||||
- **金币 + 路径**: `g15` → `g15p10` (金币15,路径第10步)
|
||||
- **陷阱 + 路径**: `t20` → `t20p10` (陷阱20,路径第10步)
|
||||
- **机关 + 路径**: `l24` → `l24p10` (机关24,路径第10步)
|
||||
- **boss + 路径**: `b92` → `b92p10` (boss92,路径第10步)
|
||||
- **起点 + 路径**: `s` → `sp1` (起点,路径第1步)
|
||||
- **终点 + 路径**: `e` → `ep25` (终点,路径第25步)
|
||||
|
||||
#### 示例CSV内容:
|
||||
```csv
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||
1,g28p11,p10,p9,p14,p16,p17,p18,p19,p20,p21,p22,p23,t14p24,p25,1
|
||||
1,1,1,g27p8,1,1,1,1,1,1,1,1,1,1,p26,1
|
||||
1,p5,p6,p7,0,0,0,t14,0,0,1,0,1,p28,p27,1
|
||||
1,p4,1,1,1,1,1,0,1,t16,1,0,1,ep29,1,1
|
||||
```
|
||||
|
||||
### 🎮 功能特性
|
||||
|
||||
#### 1. 保存功能
|
||||
- **快速保存**: `Ctrl+S` - 自动使用时间戳命名
|
||||
- **命名保存**: 通过存档界面自定义名称
|
||||
- **路径完整性**: 保存完整的路径序列到CSV
|
||||
|
||||
#### 2. 读档功能
|
||||
- **CSV解析**: 自动解析CSV中的路径信息
|
||||
- **路径重建**: 完整恢复路径序列和位置
|
||||
- **格式兼容**: 支持复杂的路径标记格式
|
||||
|
||||
#### 3. 路径处理
|
||||
- **多路径支持**: 处理一个格子被多次经过的情况(如`p35p37`)
|
||||
- **特殊格子**: 正确处理起点、终点、金币、陷阱等特殊元素
|
||||
- **顺序保持**: 确保路径顺序完全正确
|
||||
|
||||
### 🔧 操作方法
|
||||
|
||||
#### 保存游戏:
|
||||
1. **快速保存**: 按 `Ctrl+S` 自动保存到 `saves/maze_save_时间戳.csv`
|
||||
2. **命名保存**:
|
||||
- 点击"读档"按钮
|
||||
- 按 `N` 键新建存档
|
||||
- 输入文件名(自动添加.csv后缀)
|
||||
- 按回车确认
|
||||
|
||||
#### 加载游戏:
|
||||
1. 点击"读档"按钮或按 `Ctrl+L`
|
||||
2. 使用↑↓键选择存档文件
|
||||
3. 按回车加载选中的存档
|
||||
4. 游戏会显示完整的路径信息
|
||||
|
||||
#### 管理存档:
|
||||
- **查看列表**: 显示所有CSV存档文件
|
||||
- **删除存档**: 选中后按 `Delete` 键
|
||||
- **按时间排序**: 最新的存档显示在前面
|
||||
|
||||
### 📂 存档文件结构
|
||||
|
||||
```
|
||||
saves/
|
||||
├── maze_save_20250629_174154.csv # 时间戳命名的存档
|
||||
├── my_best_solution.csv # 自定义命名的存档
|
||||
└── level_1_complete.csv # 另一个自定义存档
|
||||
```
|
||||
|
||||
### 💡 高级特性
|
||||
|
||||
#### 路径重叠处理:
|
||||
当路径多次经过同一个格子时,系统会正确保存多个路径标记:
|
||||
- `p35p37` - 表示第35步和第37步都经过这个格子
|
||||
- `g15p10p20` - 表示金币15,第10步和第20步都经过
|
||||
|
||||
#### 自动解析:
|
||||
系统使用正则表达式自动识别所有路径标记:
|
||||
```python
|
||||
# 识别 g15p10p20 中的所有路径号:10, 20
|
||||
path_matches = re.findall(r'p(\d+)', cell)
|
||||
```
|
||||
|
||||
### 🎨 界面说明
|
||||
|
||||
#### 存档界面:
|
||||
- **文件格式**: 显示"输入存档名称(.csv)"提示
|
||||
- **路径信息**: 加载时显示"存档加载成功"
|
||||
- **完整显示**: 加载后立即显示完整路径
|
||||
|
||||
#### 快捷键:
|
||||
- **Ctrl+S**: 保存包含路径的CSV文件
|
||||
- **Ctrl+L**: 打开读档界面
|
||||
- **N键**: 新建存档(在存档界面中)
|
||||
- **Delete**: 删除选中的存档
|
||||
|
||||
### 📊 数据完整性
|
||||
|
||||
#### 保存时:
|
||||
- ✅ 完整的迷宫结构
|
||||
- ✅ 完整的路径序列(1到N的所有步骤)
|
||||
- ✅ 特殊元素的属性值
|
||||
- ✅ 路径与元素的关联关系
|
||||
|
||||
#### 加载时:
|
||||
- ✅ 自动解析所有路径标记
|
||||
- ✅ 重建完整的路径序列
|
||||
- ✅ 恢复原始迷宫结构
|
||||
- ✅ 正确处理路径重叠
|
||||
|
||||
### 🔄 兼容性
|
||||
|
||||
- **向前兼容**: 支持读取旧版本生成的CSV文件
|
||||
- **标准格式**: 使用标准CSV格式,可用Excel等工具查看
|
||||
- **跨平台**: 文件在不同操作系统间完全兼容
|
||||
|
||||
### 📝 注意事项
|
||||
|
||||
1. **文件名**: 会自动添加.csv后缀,无需手动输入
|
||||
2. **路径完整性**: 确保所有路径步骤都被正确保存和加载
|
||||
3. **特殊字符**: 避免在存档名中使用特殊字符
|
||||
4. **存储位置**: 所有存档保存在`saves/`目录下
|
||||
|
||||
### 🏆 优势
|
||||
|
||||
1. **简洁明了**: 路径信息直接可见,易于理解
|
||||
2. **完全兼容**: 与您现有的CSV工作流程完美集成
|
||||
3. **数据完整**: 保存所有必要信息,无信息丢失
|
||||
4. **易于调试**: 可以直接查看和编辑CSV文件
|
||||
5. **标准格式**: 使用标准CSV格式,通用性强
|
||||
|
||||
现在您的存档系统完全符合您的需求:只保存CSV文件,路径信息嵌入其中,格式清晰易懂!🎉
|
98
SAVE_SYSTEM_README.md
Normal file
98
SAVE_SYSTEM_README.md
Normal file
@ -0,0 +1,98 @@
|
||||
# 存档系统使用说明
|
||||
|
||||
## 新增功能
|
||||
|
||||
### 🎮 完整的存档系统
|
||||
现在游戏支持完整的存档和读档功能,不仅保存迷宫结构,还会保存路径信息和当前进度。
|
||||
|
||||
### 🎯 功能特性
|
||||
|
||||
#### 1. 保存功能
|
||||
- **CSV保存**: 点击 "保存" 按钮保存迷宫结构到CSV文件(兼容旧版本)
|
||||
- **完整存档**: 使用 `Ctrl+S` 或点击 "读档" 按钮选择 "新建" 保存完整游戏状态
|
||||
|
||||
#### 2. 读档功能
|
||||
- **存档列表**: 点击 "读档" 按钮查看所有可用存档
|
||||
- **快速读档**: 使用 `Ctrl+L` 快速打开读档界面
|
||||
|
||||
#### 3. 存档管理
|
||||
- **存档信息**: 显示存档名称、保存时间和迷宫大小
|
||||
- **删除存档**: 选中存档后按 `Delete` 键删除
|
||||
- **存档排序**: 按保存时间倒序排列
|
||||
|
||||
### 🔧 操作方法
|
||||
|
||||
#### 保存游戏
|
||||
1. **快速保存**: `Ctrl+S` - 使用时间戳自动命名
|
||||
2. **命名保存**:
|
||||
- 点击 "读档" 按钮
|
||||
- 按 `N` 键新建存档
|
||||
- 输入存档名称
|
||||
- 按回车确认
|
||||
|
||||
#### 加载游戏
|
||||
1. **打开界面**: 点击 "读档" 按钮或按 `Ctrl+L`
|
||||
2. **选择存档**: 使用↑↓键选择或直接点击
|
||||
3. **加载**: 按回车键或双击加载选中的存档
|
||||
4. **关闭**: 按 `ESC` 关闭界面
|
||||
|
||||
#### 管理存档
|
||||
- **删除存档**: 选中后按 `Delete` 键
|
||||
- **查看信息**: 存档列表显示名称和保存时间
|
||||
|
||||
### 📁 存档文件结构
|
||||
|
||||
存档保存在 `saves/` 目录下:
|
||||
```
|
||||
saves/
|
||||
├── maze_save_20231229_143022.json # 完整存档文件
|
||||
├── maze_save_20231229_143022_maze.csv # 对应的CSV文件
|
||||
└── ...
|
||||
```
|
||||
|
||||
#### 存档内容包括:
|
||||
- **元数据**: 存档名称、时间、迷宫尺寸
|
||||
- **迷宫数据**: 完整的迷宫结构
|
||||
- **路径数据**: 完整路径、当前步数、完成状态
|
||||
|
||||
### 🎨 界面说明
|
||||
|
||||
#### 存档界面控制:
|
||||
- **↑↓键**: 选择存档
|
||||
- **回车**: 加载选中的存档
|
||||
- **Delete**: 删除选中的存档
|
||||
- **N键**: 新建存档
|
||||
- **ESC**: 关闭界面
|
||||
|
||||
#### 键盘快捷键:
|
||||
- **Ctrl+S**: 快速保存当前游戏状态
|
||||
- **Ctrl+L**: 打开读档界面
|
||||
- **空格**: 路径下一步
|
||||
- **R键**: 重置路径
|
||||
- **A键**: 自动播放切换
|
||||
|
||||
### 💡 使用技巧
|
||||
|
||||
1. **定期保存**: 在路径规划的关键点保存进度
|
||||
2. **命名规范**: 使用有意义的存档名称便于识别
|
||||
3. **备份重要存档**: 重要的迷宫解法可以导出CSV备份
|
||||
4. **清理存档**: 定期删除不需要的存档文件
|
||||
|
||||
### 🔄 兼容性
|
||||
|
||||
- **向后兼容**: 依然支持CSV格式的迷宫文件
|
||||
- **数据完整**: 新存档格式包含完整的游戏状态
|
||||
- **灵活切换**: 可以在新旧保存方式之间自由选择
|
||||
|
||||
### 📝 注意事项
|
||||
|
||||
1. 存档文件使用JSON格式,包含完整的游戏状态
|
||||
2. 删除存档操作不可撤销,请谨慎操作
|
||||
3. 存档目录会自动创建,无需手动设置
|
||||
4. 加载存档会覆盖当前游戏状态
|
||||
|
||||
现在你可以:
|
||||
- 在解谜过程中随时保存进度
|
||||
- 尝试不同的路径策略
|
||||
- 保存有趣的迷宫布局
|
||||
- 与他人分享迷宫存档
|
61
config.py
Normal file
61
config.py
Normal file
@ -0,0 +1,61 @@
|
||||
# 游戏配置文件
|
||||
|
||||
# UI 界面配置
|
||||
UI_HEIGHT = 1000
|
||||
UI_WIDTH = 1500
|
||||
|
||||
# 迷宫配置
|
||||
MAZE_SIZE = 800
|
||||
WALL_SIZE = 50
|
||||
|
||||
# 游戏性能配置
|
||||
FPS = 120
|
||||
|
||||
# 路径播放配置
|
||||
AUTO_PLAY_INTERVAL = 30 # 每30帧自动前进一步
|
||||
|
||||
# 按钮尺寸配置
|
||||
BUTTON_START_SIZE = (200, 100)
|
||||
BUTTON_SAVE_SIZE = (80, 80)
|
||||
BUTTON_CONTROL_SIZE = (100, 50)
|
||||
|
||||
# 字体配置
|
||||
FONT_FILE = "syht.otf"
|
||||
FONT_SIZE = 18
|
||||
|
||||
# 资源路径配置
|
||||
ASSETS_PATH = "assets"
|
||||
WALL_IMAGE = f"{ASSETS_PATH}/wall.png"
|
||||
COIN_IMAGE = f"{ASSETS_PATH}/coin.png"
|
||||
TRAP_IMAGE = f"{ASSETS_PATH}/trap.png"
|
||||
START_BUTTON_IMAGE = f"{ASSETS_PATH}/start_button.png"
|
||||
SAVE_BUTTON_IMAGE = f"{ASSETS_PATH}/save.png"
|
||||
LOAD_BUTTON_IMAGE = f"{ASSETS_PATH}/load.png"
|
||||
|
||||
# 默认文件名
|
||||
DEFAULT_MAZE_FILE = "maze.csv"
|
||||
|
||||
# 颜色配置
|
||||
COLOR_WHITE = (255, 255, 255)
|
||||
COLOR_BLACK = (0, 0, 0)
|
||||
COLOR_GRAY = (200, 200, 200)
|
||||
COLOR_LIGHT_GRAY = (100, 100, 100)
|
||||
COLOR_GREEN = (100, 255, 100)
|
||||
COLOR_GOLD = (255, 215, 0)
|
||||
|
||||
# 布局配置
|
||||
def get_button_positions():
|
||||
"""返回按钮位置配置"""
|
||||
control_panel_x = MAZE_SIZE + ((UI_WIDTH - MAZE_SIZE) / 2 - 100)
|
||||
|
||||
return {
|
||||
'start_button': (MAZE_SIZE + 50, 0),
|
||||
'save_button': (MAZE_SIZE + 50, 110),
|
||||
'load_button': (MAZE_SIZE + 150, 110),
|
||||
'next_step_button': (MAZE_SIZE + 50, 200),
|
||||
'reset_path_button': (MAZE_SIZE + 200, 200),
|
||||
'auto_play_button': (MAZE_SIZE + 350, 200),
|
||||
'progress_text': (MAZE_SIZE + 50, 270),
|
||||
'hint_text': (MAZE_SIZE + 50, 300),
|
||||
'save_list_area': (MAZE_SIZE + 50, 350, 400, 200) # x, y, width, height
|
||||
}
|
186
main.py
186
main.py
@ -4,16 +4,11 @@ from pygame.constants import QUIT
|
||||
from maze import Maze
|
||||
|
||||
from draw import Button, Toast
|
||||
from save_ui import SaveLoadUI
|
||||
from config import *
|
||||
import sys
|
||||
import os
|
||||
|
||||
UI_HEIGHT = 1000
|
||||
UI_WIDTH = 1500
|
||||
|
||||
MAZE_SIZE = 150
|
||||
WALL_SIZE = 50
|
||||
FPS = 120
|
||||
|
||||
screen: Surface = None # 窗口实例
|
||||
clock = None # 时钟实例
|
||||
|
||||
@ -30,52 +25,139 @@ def pygameInit(title: str = "pygame"):
|
||||
clock = pygame.time.Clock()
|
||||
# Initialize font with UTF-8 support
|
||||
pygame.font.init()
|
||||
textFont = pygame.font.Font("syht.otf", 18)
|
||||
textFont = pygame.font.Font(FONT_FILE, FONT_SIZE)
|
||||
|
||||
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像素
|
||||
maze = Maze(wall_size=WALL_SIZE, maze_size=MAZE_SIZE, file_name=DEFAULT_MAZE_FILE)
|
||||
|
||||
image_coin = pygame.image.load("assets/coin.png").convert_alpha()
|
||||
image_coin = pygame.transform.scale(image_coin, (WALL_SIZE, WALL_SIZE)) # 例如缩放到50x50像素
|
||||
# 加载图片资源
|
||||
image_wall = pygame.image.load(WALL_IMAGE).convert_alpha()
|
||||
image_wall = pygame.transform.scale(image_wall, (WALL_SIZE, WALL_SIZE))
|
||||
|
||||
image_trap = pygame.image.load("assets/trap.png").convert_alpha()
|
||||
image_trap = pygame.transform.scale(image_trap, (WALL_SIZE, WALL_SIZE)) # 例如缩放到50x50像素
|
||||
image_coin = pygame.image.load(COIN_IMAGE).convert_alpha()
|
||||
image_coin = pygame.transform.scale(image_coin, (WALL_SIZE, WALL_SIZE))
|
||||
|
||||
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)
|
||||
image_trap = pygame.image.load(TRAP_IMAGE).convert_alpha()
|
||||
image_trap = pygame.transform.scale(image_trap, (WALL_SIZE, WALL_SIZE))
|
||||
|
||||
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)
|
||||
# 获取按钮位置配置
|
||||
button_positions = get_button_positions()
|
||||
|
||||
# 创建按钮
|
||||
button_start_texture = pygame.image.load(START_BUTTON_IMAGE).convert_alpha()
|
||||
button_start_texture = pygame.transform.scale(button_start_texture, BUTTON_START_SIZE)
|
||||
button_start = Button(pygame.rect.Rect(*button_positions['start_button'], *BUTTON_START_SIZE), button_start_texture)
|
||||
|
||||
# 没有生成迷宫就保存的提示框
|
||||
button_save_texture = pygame.image.load(SAVE_BUTTON_IMAGE).convert_alpha()
|
||||
button_save_texture = pygame.transform.scale(button_save_texture, BUTTON_SAVE_SIZE)
|
||||
button_save = Button(pygame.rect.Rect(*button_positions['save_button'], *BUTTON_SAVE_SIZE), button_save_texture)
|
||||
|
||||
button_load_texture = pygame.image.load(LOAD_BUTTON_IMAGE).convert_alpha()
|
||||
button_load_texture = pygame.transform.scale(button_load_texture, BUTTON_SAVE_SIZE)
|
||||
button_load = Button(pygame.rect.Rect(*button_positions['load_button'], *BUTTON_SAVE_SIZE), button_load_texture)
|
||||
|
||||
# 添加路径控制按钮
|
||||
button_next_step = Button(pygame.rect.Rect(*button_positions['next_step_button'], *BUTTON_CONTROL_SIZE), None)
|
||||
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)
|
||||
|
||||
# 提示信息
|
||||
mes1 = Toast("没有生成迷宫,无法保存", UI_WIDTH, UI_HEIGHT, font=textFont)
|
||||
mes2 = Toast("迷宫已保存", UI_WIDTH, UI_HEIGHT, font=textFont)
|
||||
mes3 = Toast("存档已保存", UI_WIDTH, UI_HEIGHT, font=textFont)
|
||||
mes4 = Toast("存档加载成功", UI_WIDTH, UI_HEIGHT, font=textFont)
|
||||
mes5 = Toast("加载失败", UI_WIDTH, UI_HEIGHT, font=textFont)
|
||||
|
||||
# 创建存档界面
|
||||
save_ui = SaveLoadUI(textFont)
|
||||
|
||||
# 路径控制变量
|
||||
auto_play = False
|
||||
auto_play_timer = 0
|
||||
auto_play_interval = AUTO_PLAY_INTERVAL
|
||||
|
||||
running = True
|
||||
while running:
|
||||
clock.tick(FPS) # 限制帧数
|
||||
screen.fill((255, 255, 255)) # 铺底
|
||||
screen.fill(COLOR_WHITE) # 铺底
|
||||
|
||||
# 自动播放逻辑
|
||||
if auto_play and len(maze.full_path) > 0:
|
||||
auto_play_timer += 1
|
||||
if auto_play_timer >= auto_play_interval:
|
||||
if not maze.next_path_step():
|
||||
auto_play = False # 路径播放完成后停止自动播放
|
||||
auto_play_timer = 0
|
||||
|
||||
for event in pygame.event.get():
|
||||
button_start.handle_event(event=event)
|
||||
button_save.handle_event(event=event)
|
||||
# 首先让存档界面处理事件
|
||||
save_result = save_ui.handle_event(event, maze)
|
||||
if save_result == "save_success":
|
||||
mes3.show()
|
||||
elif save_result == "load_success":
|
||||
mes4.show()
|
||||
auto_play = False # 加载游戏后停止自动播放
|
||||
elif save_result == "load_failed":
|
||||
mes5.show()
|
||||
|
||||
if button_start.pressed == True:
|
||||
maze.generate()
|
||||
# 如果存档界面正在显示,不处理其他按钮事件
|
||||
if not save_ui.show_save_list:
|
||||
button_start.handle_event(event=event)
|
||||
button_save.handle_event(event=event)
|
||||
button_load.handle_event(event=event)
|
||||
button_next_step.handle_event(event=event)
|
||||
button_reset_path.handle_event(event=event)
|
||||
button_auto_play.handle_event(event=event)
|
||||
|
||||
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 button_start.pressed == True:
|
||||
maze.generate()
|
||||
auto_play = False # 生成新迷宫时停止自动播放
|
||||
|
||||
if button_save.pressed == True:
|
||||
if len(maze.grid) == 0:
|
||||
mes1.show()
|
||||
else:
|
||||
maze.export_to_csv(DEFAULT_MAZE_FILE)
|
||||
mes2.text = f"迷宫已保存至{DEFAULT_MAZE_FILE}"
|
||||
mes2.show()
|
||||
|
||||
if button_load.pressed == True:
|
||||
save_ui.update_save_list(maze)
|
||||
save_ui.toggle_save_list()
|
||||
|
||||
# 路径控制
|
||||
if button_next_step.pressed == True and len(maze.full_path) > 0:
|
||||
maze.next_path_step()
|
||||
|
||||
if button_reset_path.pressed == True and len(maze.full_path) > 0:
|
||||
maze.reset_path()
|
||||
auto_play = False
|
||||
|
||||
if button_auto_play.pressed == True and len(maze.full_path) > 0:
|
||||
auto_play = not auto_play
|
||||
auto_play_timer = 0
|
||||
|
||||
# 键盘控制
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_SPACE and len(maze.full_path) > 0:
|
||||
maze.next_path_step()
|
||||
elif event.key == pygame.K_r and len(maze.full_path) > 0:
|
||||
maze.reset_path()
|
||||
auto_play = False
|
||||
elif event.key == pygame.K_a and len(maze.full_path) > 0:
|
||||
auto_play = not auto_play
|
||||
auto_play_timer = 0
|
||||
elif event.key == pygame.K_s and pygame.key.get_pressed()[pygame.K_LCTRL]:
|
||||
# Ctrl+S 保存包含路径的CSV
|
||||
if len(maze.grid) > 0:
|
||||
result = maze.save_game()
|
||||
if result:
|
||||
mes3.show()
|
||||
elif event.key == pygame.K_l and pygame.key.get_pressed()[pygame.K_LCTRL]:
|
||||
# Ctrl+L 打开读档界面
|
||||
save_ui.update_save_list(maze)
|
||||
save_ui.toggle_save_list()
|
||||
|
||||
if event.type == QUIT:
|
||||
running = False
|
||||
@ -84,9 +166,45 @@ if __name__ == "__main__":
|
||||
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)
|
||||
button_load.draw(screen=screen)
|
||||
|
||||
# 绘制路径控制按钮
|
||||
if len(maze.full_path) > 0:
|
||||
# 绘制按钮背景
|
||||
pygame.draw.rect(screen, COLOR_GRAY, button_next_step.rect)
|
||||
pygame.draw.rect(screen, COLOR_GRAY, button_reset_path.rect)
|
||||
pygame.draw.rect(screen, COLOR_GREEN if auto_play else COLOR_GRAY, button_auto_play.rect)
|
||||
|
||||
# 绘制按钮文字
|
||||
next_text = textFont.render("下一步", True, COLOR_BLACK)
|
||||
reset_text = textFont.render("重置", True, COLOR_BLACK)
|
||||
auto_text = textFont.render("自动播放" if not auto_play else "停止", True, COLOR_BLACK)
|
||||
|
||||
screen.blit(next_text, (button_next_step.rect.x + 10, button_next_step.rect.y + 15))
|
||||
screen.blit(reset_text, (button_reset_path.rect.x + 25, button_reset_path.rect.y + 15))
|
||||
screen.blit(auto_text, (button_auto_play.rect.x + 5, button_auto_play.rect.y + 15))
|
||||
|
||||
# 显示当前步数信息
|
||||
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.full_path) > 0:
|
||||
hint_text = textFont.render("空格键: 下一步 | R键: 重置 | A键: 自动播放", True, COLOR_LIGHT_GRAY)
|
||||
screen.blit(hint_text, button_positions['hint_text'])
|
||||
|
||||
# 显示快捷键提示
|
||||
shortcut_text = textFont.render("Ctrl+S: 保存包含路径的CSV | Ctrl+L: 读档", True, COLOR_LIGHT_GRAY)
|
||||
screen.blit(shortcut_text, (MAZE_SIZE + 50, 330))
|
||||
|
||||
mes1.draw(screen=screen)
|
||||
mes2.draw(screen=screen)
|
||||
mes3.draw(screen=screen)
|
||||
mes4.draw(screen=screen)
|
||||
mes5.draw(screen=screen)
|
||||
|
||||
# 绘制存档界面(必须在最后绘制以显示在最上层)
|
||||
save_ui.draw(screen)
|
||||
pygame.display.flip()
|
||||
pygame.quit()
|
||||
|
||||
|
28
maze.csv
28
maze.csv
@ -1,16 +1,16 @@
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||
1,0,0,1,0,0,0,0,0,l25,0,t10,0,1,0,1
|
||||
1,1,0,1,1,1,1,1,1,1,1,1,l15,1,t7,1
|
||||
1,0,0,g26,0,e,t20,s,0,0,0,0,0,0,l25,1
|
||||
1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1
|
||||
1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1
|
||||
1,0,1,1,1,1,0,1,1,1,1,1,g24,1,0,1
|
||||
1,0,0,1,0,t16,0,0,1,0,0,0,0,1,l16,1
|
||||
1,1,1,1,1,1,1,1,1,1,1,0,1,1,g15,1
|
||||
1,0,t8,1,0,1,0,0,g12,0,1,0,0,1,0,1
|
||||
1,0,0,1,0,1,1,0,1,0,1,0,1,1,0,1
|
||||
1,0,0,0,0,0,0,0,1,0,1,0,t15,1,l16,1
|
||||
1,0,0,1,0,1,0,t16,1,0,1,0,0,1,0,1
|
||||
1,1,0,1,1,1,1,1,1,0,1,0,0,1,0,1
|
||||
1,0,0,0,0,0,0,0,1,0,t5,b89,0,1,0,1
|
||||
1,0,0,0,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,0,1
|
||||
1,0,0,0,1,0,0,t9,1,0,1,0,1,0,0,1
|
||||
1,0,1,1,1,1,l15,1,1,0,1,0,1,0,1,1
|
||||
1,b50,0,0,0,0,g11,0,1,0,1,0,l16,0,1,1
|
||||
1,1,0,1,1,1,1,1,1,0,1,0,1,0,1,1
|
||||
1,0,t20,0,0,0,0,0,1,0,0,0,1,0,1,1
|
||||
1,1,1,1,1,1,1,0,0,0,1,0,1,0,1,1
|
||||
1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,1
|
||||
1,0,1,1,1,0,l20,g19,1,t8,1,0,1,l12,1,1
|
||||
1,g11,e,0,0,0,1,g13,1,0,1,0,1,0,0,1
|
||||
1,0,1,1,0,1,1,1,1,0,1,1,1,1,s,1
|
||||
1,0,1,0,0,0,0,0,1,0,1,0,1,0,0,1
|
||||
1,0,1,1,1,l22,1,0,1,l27,1,0,0,t17,0,1
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||
|
|
99
maze.py
99
maze.py
@ -2,6 +2,7 @@ import pygame
|
||||
from maze_generator import MazeGenerator
|
||||
from SourceCollector import SourceCollector
|
||||
from tanxin import *
|
||||
from simple_save_manager import simple_save_manager
|
||||
import time
|
||||
|
||||
class Maze:
|
||||
@ -14,19 +15,109 @@ class Maze:
|
||||
|
||||
self.grid = []
|
||||
self.generater = MazeGenerator(self.size, self.file_name)
|
||||
self.source_collector = None
|
||||
self.path_step = 0 # 当前显示到路径的第几步
|
||||
self.full_path = [] # 完整路径
|
||||
self.is_path_complete = False # 路径是否完全显示
|
||||
|
||||
|
||||
def generate(self):
|
||||
seed = int(time.time() * 1000) % (2**32)
|
||||
self.generater.generate(seed=seed)
|
||||
obj = SourceCollector(maze=self.generater.maze)
|
||||
obj.run()
|
||||
self.grid = obj.output_list()
|
||||
print(self.grid)
|
||||
self.source_collector = SourceCollector(maze=self.generater.maze)
|
||||
self.source_collector.run()
|
||||
self.full_path = self.source_collector.get_path()
|
||||
for i in self.full_path:
|
||||
print(i)
|
||||
self.path_step = 0
|
||||
self.is_path_complete = False
|
||||
self.grid = self.generater.maze # 使用原始迷宫数据
|
||||
print(f"路径长度: {len(self.full_path)}")
|
||||
|
||||
def next_path_step(self):
|
||||
"""显示路径的下一步"""
|
||||
if self.path_step < len(self.full_path):
|
||||
self.path_step += 1
|
||||
self.update_grid_with_path()
|
||||
return True
|
||||
return False
|
||||
|
||||
def reset_path(self):
|
||||
"""重置路径显示"""
|
||||
self.path_step = 0
|
||||
self.is_path_complete = False
|
||||
self.grid = self.generater.maze if self.generater.maze else []
|
||||
|
||||
def auto_advance_path(self):
|
||||
"""自动推进路径显示"""
|
||||
if not self.is_path_complete:
|
||||
if not self.next_path_step():
|
||||
self.is_path_complete = True
|
||||
|
||||
def update_grid_with_path(self):
|
||||
"""根据当前步数更新网格显示"""
|
||||
if not self.full_path or not self.generater.maze:
|
||||
return
|
||||
|
||||
# 从原始迷宫开始
|
||||
self.grid = [row[:] for row in self.generater.maze] # 深拷贝
|
||||
|
||||
# 只显示到当前步数的路径
|
||||
for idx in range(min(self.path_step, len(self.full_path))):
|
||||
y, x = self.full_path[idx]
|
||||
if self.grid[y][x].startswith('s') or self.grid[y][x].startswith('e'):
|
||||
continue
|
||||
if self.grid[y][x].startswith('g') or self.grid[y][x].startswith('t'):
|
||||
self.grid[y][x] = f"{self.grid[y][x]}p{idx}"
|
||||
continue
|
||||
self.grid[y][x] = f"p{idx}"
|
||||
|
||||
def export_to_csv(self, filename):
|
||||
"""导出迷宫到CSV文件(兼容旧版本)"""
|
||||
self.generater.export_to_csv(filename=filename)
|
||||
|
||||
def save_game(self, save_name=None):
|
||||
"""保存包含路径信息的迷宫到CSV文件"""
|
||||
if len(self.grid) == 0:
|
||||
print("没有生成迷宫,无法保存")
|
||||
return None
|
||||
|
||||
return simple_save_manager.save_maze_with_path(self, save_name)
|
||||
|
||||
def load_game(self, save_file):
|
||||
"""从CSV存档文件加载游戏状态"""
|
||||
load_data = simple_save_manager.load_maze_from_csv(save_file)
|
||||
if load_data is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
# 恢复迷宫数据
|
||||
self.generater.maze = load_data['original_grid']
|
||||
self.size = len(load_data['original_grid'])
|
||||
|
||||
# 恢复路径数据
|
||||
self.full_path = load_data['path_sequence']
|
||||
self.path_step = len(self.full_path) # 显示完整路径
|
||||
self.is_path_complete = True
|
||||
|
||||
# 重新创建SourceCollector以便后续操作
|
||||
self.source_collector = SourceCollector(maze=self.generater.maze)
|
||||
|
||||
# 使用包含路径信息的网格作为显示网格
|
||||
self.grid = load_data['path_grid']
|
||||
|
||||
print(f"成功加载游戏状态,路径长度: {len(self.full_path)}")
|
||||
print(f"当前显示完整路径")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"加载游戏状态失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def get_save_list(self):
|
||||
"""获取所有可用的存档列表"""
|
||||
return simple_save_manager.get_save_list()
|
||||
|
||||
|
||||
|
||||
def draw(self, screen, wall_texture, coin_texture, trap_texture):
|
||||
|
142
save_manager.py
Normal file
142
save_manager.py
Normal file
@ -0,0 +1,142 @@
|
||||
import json
|
||||
import csv
|
||||
import os
|
||||
from datetime import datetime
|
||||
from config import DEFAULT_MAZE_FILE
|
||||
|
||||
class GameSaveManager:
|
||||
"""游戏存档管理器"""
|
||||
|
||||
def __init__(self):
|
||||
self.save_directory = "saves"
|
||||
self.ensure_save_directory()
|
||||
|
||||
def ensure_save_directory(self):
|
||||
"""确保存档目录存在"""
|
||||
if not os.path.exists(self.save_directory):
|
||||
os.makedirs(self.save_directory)
|
||||
|
||||
def save_game_state(self, maze_instance, save_name=None):
|
||||
"""
|
||||
保存游戏状态,包括迷宫和路径信息
|
||||
|
||||
Args:
|
||||
maze_instance: Maze类的实例
|
||||
save_name: 存档名称,如果为None则使用时间戳
|
||||
|
||||
Returns:
|
||||
str: 保存的文件路径
|
||||
"""
|
||||
if save_name is None:
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
save_name = f"maze_save_{timestamp}"
|
||||
|
||||
# 创建存档数据结构
|
||||
save_data = {
|
||||
"metadata": {
|
||||
"save_name": save_name,
|
||||
"save_time": datetime.now().isoformat(),
|
||||
"maze_size": maze_instance.size,
|
||||
"wall_size": maze_instance.wall_size,
|
||||
"maze_display_size": maze_instance.maze_size
|
||||
},
|
||||
"maze_data": {
|
||||
"grid": maze_instance.generater.maze if maze_instance.generater.maze else [],
|
||||
"generator_name": maze_instance.generater.name if hasattr(maze_instance.generater, 'name') else "Unknown"
|
||||
},
|
||||
"path_data": {
|
||||
"full_path": maze_instance.full_path,
|
||||
"current_step": maze_instance.path_step,
|
||||
"is_path_complete": maze_instance.is_path_complete
|
||||
}
|
||||
}
|
||||
|
||||
# 保存为JSON文件
|
||||
save_file = os.path.join(self.save_directory, f"{save_name}.json")
|
||||
try:
|
||||
with open(save_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(save_data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
# 同时保存CSV格式的迷宫数据(兼容性)
|
||||
csv_file = os.path.join(self.save_directory, f"{save_name}_maze.csv")
|
||||
self.save_maze_csv(maze_instance.generater.maze, csv_file)
|
||||
|
||||
print(f"游戏状态已保存至: {os.path.abspath(save_file)}")
|
||||
return save_file
|
||||
|
||||
except Exception as e:
|
||||
print(f"保存失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def load_game_state(self, save_file):
|
||||
"""
|
||||
加载游戏状态
|
||||
|
||||
Args:
|
||||
save_file: 存档文件路径
|
||||
|
||||
Returns:
|
||||
dict: 包含游戏状态的字典,如果加载失败返回None
|
||||
"""
|
||||
try:
|
||||
with open(save_file, 'r', encoding='utf-8') as f:
|
||||
save_data = json.load(f)
|
||||
|
||||
print(f"成功加载存档: {save_data['metadata']['save_name']}")
|
||||
print(f"保存时间: {save_data['metadata']['save_time']}")
|
||||
return save_data
|
||||
|
||||
except Exception as e:
|
||||
print(f"加载失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def save_maze_csv(self, maze_grid, filename):
|
||||
"""保存迷宫网格为CSV格式"""
|
||||
try:
|
||||
with open(filename, 'w', newline='', encoding='utf-8') as f:
|
||||
writer = csv.writer(f)
|
||||
for row in maze_grid:
|
||||
writer.writerow(row)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"CSV保存失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def get_save_list(self):
|
||||
"""获取所有存档文件列表"""
|
||||
saves = []
|
||||
if os.path.exists(self.save_directory):
|
||||
for filename in os.listdir(self.save_directory):
|
||||
if filename.endswith('.json'):
|
||||
save_path = os.path.join(self.save_directory, filename)
|
||||
try:
|
||||
with open(save_path, 'r', encoding='utf-8') as f:
|
||||
save_data = json.load(f)
|
||||
saves.append({
|
||||
'filename': filename,
|
||||
'path': save_path,
|
||||
'name': save_data['metadata']['save_name'],
|
||||
'save_time': save_data['metadata']['save_time'],
|
||||
'maze_size': save_data['metadata']['maze_size']
|
||||
})
|
||||
except:
|
||||
continue
|
||||
return sorted(saves, key=lambda x: x['save_time'], reverse=True)
|
||||
|
||||
def delete_save(self, save_file):
|
||||
"""删除存档文件"""
|
||||
try:
|
||||
if os.path.exists(save_file):
|
||||
os.remove(save_file)
|
||||
# 同时删除对应的CSV文件
|
||||
csv_file = save_file.replace('.json', '_maze.csv')
|
||||
if os.path.exists(csv_file):
|
||||
os.remove(csv_file)
|
||||
print(f"存档已删除: {save_file}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"删除失败: {str(e)}")
|
||||
return False
|
||||
|
||||
# 全局存档管理器实例
|
||||
save_manager = GameSaveManager()
|
162
save_ui.py
Normal file
162
save_ui.py
Normal file
@ -0,0 +1,162 @@
|
||||
import pygame
|
||||
from config import *
|
||||
|
||||
class SaveLoadUI:
|
||||
"""存档和读档界面"""
|
||||
|
||||
def __init__(self, font):
|
||||
self.font = font
|
||||
self.small_font = pygame.font.Font(FONT_FILE, FONT_SIZE - 4)
|
||||
self.show_save_list = False
|
||||
self.save_list = []
|
||||
self.selected_save = -1
|
||||
self.scroll_offset = 0
|
||||
self.max_visible_saves = 8
|
||||
|
||||
# 界面元素
|
||||
button_positions = get_button_positions()
|
||||
self.list_area = pygame.Rect(*button_positions['save_list_area'])
|
||||
self.save_input = ""
|
||||
self.input_active = False
|
||||
|
||||
def update_save_list(self, maze):
|
||||
"""更新存档列表"""
|
||||
self.save_list = maze.get_save_list()
|
||||
|
||||
def toggle_save_list(self):
|
||||
"""切换存档列表显示状态"""
|
||||
self.show_save_list = not self.show_save_list
|
||||
if not self.show_save_list:
|
||||
self.selected_save = -1
|
||||
self.input_active = False
|
||||
|
||||
def handle_event(self, event, maze):
|
||||
"""处理界面事件"""
|
||||
if not self.show_save_list:
|
||||
return None
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if self.input_active:
|
||||
if event.key == pygame.K_RETURN:
|
||||
# 保存游戏
|
||||
if self.save_input.strip():
|
||||
result = maze.save_game(self.save_input.strip())
|
||||
if result:
|
||||
self.update_save_list(maze)
|
||||
self.save_input = ""
|
||||
self.input_active = False
|
||||
return "save_success"
|
||||
return "save_failed"
|
||||
elif event.key == pygame.K_ESCAPE:
|
||||
self.input_active = False
|
||||
self.save_input = ""
|
||||
elif event.key == pygame.K_BACKSPACE:
|
||||
self.save_input = self.save_input[:-1]
|
||||
else:
|
||||
if len(self.save_input) < 20: # 限制输入长度
|
||||
self.save_input += event.unicode
|
||||
else:
|
||||
if event.key == pygame.K_UP:
|
||||
if self.selected_save > 0:
|
||||
self.selected_save -= 1
|
||||
elif event.key == pygame.K_DOWN:
|
||||
if self.selected_save < len(self.save_list) - 1:
|
||||
self.selected_save += 1
|
||||
elif event.key == pygame.K_RETURN:
|
||||
# 加载选中的存档
|
||||
if 0 <= self.selected_save < len(self.save_list):
|
||||
save_file = self.save_list[self.selected_save]['path']
|
||||
if maze.load_game(save_file):
|
||||
self.show_save_list = False
|
||||
return "load_success"
|
||||
return "load_failed"
|
||||
elif event.key == pygame.K_DELETE:
|
||||
# 删除选中的存档
|
||||
if 0 <= self.selected_save < len(self.save_list):
|
||||
save_file = self.save_list[self.selected_save]['path']
|
||||
from simple_save_manager import simple_save_manager
|
||||
if simple_save_manager.delete_save(save_file):
|
||||
self.update_save_list(maze)
|
||||
if self.selected_save >= len(self.save_list):
|
||||
self.selected_save = len(self.save_list) - 1
|
||||
return "delete_success"
|
||||
elif event.key == pygame.K_n:
|
||||
# 新建存档
|
||||
self.input_active = True
|
||||
self.save_input = ""
|
||||
elif event.key == pygame.K_ESCAPE:
|
||||
self.toggle_save_list()
|
||||
|
||||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||
if event.button == 1: # 左键点击
|
||||
mouse_pos = pygame.mouse.get_pos()
|
||||
# 检查是否点击在存档列表区域
|
||||
if self.list_area.collidepoint(mouse_pos):
|
||||
# 计算点击的存档索引
|
||||
relative_y = mouse_pos[1] - self.list_area.y - 30 # 减去标题高度
|
||||
if relative_y >= 0:
|
||||
clicked_index = relative_y // 25 # 每个存档项25像素高
|
||||
if 0 <= clicked_index < len(self.save_list):
|
||||
self.selected_save = clicked_index
|
||||
else:
|
||||
# 点击外部关闭界面
|
||||
self.toggle_save_list()
|
||||
|
||||
return None
|
||||
|
||||
def draw(self, screen):
|
||||
"""绘制存档界面"""
|
||||
if not self.show_save_list:
|
||||
return
|
||||
|
||||
# 绘制半透明背景
|
||||
overlay = pygame.Surface((UI_WIDTH, UI_HEIGHT))
|
||||
overlay.set_alpha(128)
|
||||
overlay.fill((0, 0, 0))
|
||||
screen.blit(overlay, (0, 0))
|
||||
|
||||
# 绘制存档列表背景
|
||||
pygame.draw.rect(screen, COLOR_WHITE, self.list_area)
|
||||
pygame.draw.rect(screen, COLOR_BLACK, self.list_area, 2)
|
||||
|
||||
# 绘制标题
|
||||
title_text = self.font.render("存档管理", True, COLOR_BLACK)
|
||||
screen.blit(title_text, (self.list_area.x + 10, self.list_area.y + 5))
|
||||
|
||||
# 绘制操作说明
|
||||
if self.input_active:
|
||||
hint_text = self.small_font.render("输入存档名称(.csv),按回车保存:", True, COLOR_BLACK)
|
||||
screen.blit(hint_text, (self.list_area.x + 10, self.list_area.y + 30))
|
||||
|
||||
# 绘制输入框
|
||||
input_rect = pygame.Rect(self.list_area.x + 10, self.list_area.y + 50, 300, 25)
|
||||
pygame.draw.rect(screen, COLOR_WHITE, input_rect)
|
||||
pygame.draw.rect(screen, COLOR_BLACK, input_rect, 2)
|
||||
|
||||
input_text = self.small_font.render(self.save_input + "|", True, COLOR_BLACK)
|
||||
screen.blit(input_text, (input_rect.x + 5, input_rect.y + 5))
|
||||
else:
|
||||
hint_text = self.small_font.render("↑↓选择 回车加载 Delete删除 N新建 ESC关闭", True, COLOR_BLACK)
|
||||
screen.blit(hint_text, (self.list_area.x + 10, self.list_area.y + 30))
|
||||
|
||||
# 绘制存档列表
|
||||
start_y = self.list_area.y + 55
|
||||
for i, save_info in enumerate(self.save_list):
|
||||
y_pos = start_y + i * 25
|
||||
if y_pos > self.list_area.bottom - 25:
|
||||
break
|
||||
|
||||
# 高亮选中项
|
||||
if i == self.selected_save:
|
||||
highlight_rect = pygame.Rect(self.list_area.x + 5, y_pos - 2, self.list_area.width - 10, 22)
|
||||
pygame.draw.rect(screen, COLOR_GRAY, highlight_rect)
|
||||
|
||||
# 显示存档信息
|
||||
save_text = f"{save_info['name']} - {save_info['save_time'][:16]}"
|
||||
text_surface = self.small_font.render(save_text, True, COLOR_BLACK)
|
||||
screen.blit(text_surface, (self.list_area.x + 10, y_pos))
|
||||
|
||||
# 如果没有存档,显示提示
|
||||
if not self.save_list and not self.input_active:
|
||||
no_save_text = self.small_font.render("没有找到存档文件", True, COLOR_LIGHT_GRAY)
|
||||
screen.blit(no_save_text, (self.list_area.x + 10, self.list_area.y + 60))
|
220
simple_save_manager.py
Normal file
220
simple_save_manager.py
Normal file
@ -0,0 +1,220 @@
|
||||
import csv
|
||||
import os
|
||||
from datetime import datetime
|
||||
from config import DEFAULT_MAZE_FILE
|
||||
|
||||
class SimpleSaveManager:
|
||||
"""简化的存档管理器 - 只保存CSV格式的迷宫文件"""
|
||||
|
||||
def __init__(self):
|
||||
self.save_directory = "saves"
|
||||
self.ensure_save_directory()
|
||||
|
||||
def ensure_save_directory(self):
|
||||
"""确保存档目录存在"""
|
||||
if not os.path.exists(self.save_directory):
|
||||
os.makedirs(self.save_directory)
|
||||
|
||||
def save_maze_with_path(self, maze_instance, save_name=None):
|
||||
"""
|
||||
保存包含路径信息的迷宫到CSV文件
|
||||
|
||||
Args:
|
||||
maze_instance: Maze类的实例
|
||||
save_name: 存档名称,如果为None则使用时间戳
|
||||
|
||||
Returns:
|
||||
str: 保存的文件路径
|
||||
"""
|
||||
if save_name is None:
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
save_name = f"maze_save_{timestamp}"
|
||||
|
||||
# 确保文件名以.csv结尾
|
||||
if not save_name.endswith('.csv'):
|
||||
save_name += '.csv'
|
||||
|
||||
save_file = os.path.join(self.save_directory, save_name)
|
||||
|
||||
try:
|
||||
# 生成包含路径信息的网格
|
||||
path_grid = self.create_path_grid(maze_instance)
|
||||
|
||||
# 保存到CSV文件
|
||||
with open(save_file, 'w', newline='', encoding='utf-8') as f:
|
||||
writer = csv.writer(f)
|
||||
for row in path_grid:
|
||||
writer.writerow(row)
|
||||
|
||||
print(f"迷宫已保存至: {os.path.abspath(save_file)}")
|
||||
return save_file
|
||||
|
||||
except Exception as e:
|
||||
print(f"保存失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def create_path_grid(self, maze_instance):
|
||||
"""
|
||||
创建包含路径信息的网格
|
||||
|
||||
Args:
|
||||
maze_instance: Maze类的实例
|
||||
|
||||
Returns:
|
||||
list: 包含路径信息的二维网格
|
||||
"""
|
||||
if not maze_instance.generater.maze or not maze_instance.full_path:
|
||||
return maze_instance.generater.maze if maze_instance.generater.maze else []
|
||||
|
||||
# 从原始迷宫开始
|
||||
path_grid = [row[:] for row in maze_instance.generater.maze] # 深拷贝
|
||||
|
||||
# 将完整路径信息添加到网格中
|
||||
for idx, (y, x) in enumerate(maze_instance.full_path):
|
||||
current_cell = path_grid[y][x]
|
||||
path_info = f"p{idx + 1}" # 路径编号从1开始
|
||||
|
||||
if current_cell.startswith('s'):
|
||||
# 起点:保留起点标记,但也添加路径信息
|
||||
path_grid[y][x] = f"s{path_info}"
|
||||
elif current_cell.startswith('e'):
|
||||
# 终点:保留终点标记,但也添加路径信息
|
||||
path_grid[y][x] = f"e{path_info}"
|
||||
elif current_cell.startswith('g'):
|
||||
# 金币:g10p31 格式
|
||||
path_grid[y][x] = f"{current_cell}{path_info}"
|
||||
elif current_cell.startswith('t'):
|
||||
# 陷阱:t19p31 格式
|
||||
path_grid[y][x] = f"{current_cell}{path_info}"
|
||||
elif current_cell.startswith('l'):
|
||||
# 机关:l24p31 格式
|
||||
path_grid[y][x] = f"{current_cell}{path_info}"
|
||||
elif current_cell.startswith('b'):
|
||||
# boss:b92p31 格式
|
||||
path_grid[y][x] = f"{current_cell}{path_info}"
|
||||
elif current_cell == '0':
|
||||
# 空地:直接是p31
|
||||
path_grid[y][x] = path_info
|
||||
else:
|
||||
# 其他情况也添加路径信息
|
||||
path_grid[y][x] = f"{current_cell}{path_info}"
|
||||
|
||||
return path_grid
|
||||
|
||||
def load_maze_from_csv(self, csv_file):
|
||||
"""
|
||||
从CSV文件加载迷宫,解析路径信息
|
||||
|
||||
Args:
|
||||
csv_file: CSV文件路径
|
||||
|
||||
Returns:
|
||||
dict: 包含迷宫数据和路径信息的字典
|
||||
"""
|
||||
try:
|
||||
with open(csv_file, 'r', newline='', encoding='utf-8') as f:
|
||||
reader = csv.reader(f)
|
||||
grid = [list(row) for row in reader]
|
||||
|
||||
# 解析路径信息
|
||||
path_data = self.extract_path_from_grid(grid)
|
||||
|
||||
# 创建原始迷宫(移除路径信息)
|
||||
original_grid = self.create_original_grid(grid)
|
||||
|
||||
result = {
|
||||
'original_grid': original_grid,
|
||||
'path_grid': grid,
|
||||
'path_sequence': path_data['path_sequence'],
|
||||
'path_positions': path_data['path_positions']
|
||||
}
|
||||
|
||||
print(f"成功从 {os.path.abspath(csv_file)} 加载迷宫")
|
||||
print(f"路径长度: {len(path_data['path_sequence'])}")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"加载失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def extract_path_from_grid(self, grid):
|
||||
"""从网格中提取路径信息"""
|
||||
path_positions = {} # {step_number: (y, x)}
|
||||
|
||||
for y in range(len(grid)):
|
||||
for x in range(len(grid[y])):
|
||||
cell = grid[y][x]
|
||||
|
||||
# 查找所有路径信息 p数字
|
||||
import re
|
||||
# 使用正则表达式找到所有p后跟数字的模式
|
||||
path_matches = re.findall(r'p(\d+)', cell)
|
||||
|
||||
for path_match in path_matches:
|
||||
try:
|
||||
step_number = int(path_match)
|
||||
path_positions[step_number] = (y, x)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
# 按步数排序创建路径序列
|
||||
path_sequence = []
|
||||
for step in sorted(path_positions.keys()):
|
||||
path_sequence.append(path_positions[step])
|
||||
|
||||
return {
|
||||
'path_sequence': path_sequence,
|
||||
'path_positions': path_positions
|
||||
}
|
||||
|
||||
def create_original_grid(self, path_grid):
|
||||
"""从包含路径的网格创建原始迷宫网格"""
|
||||
original_grid = []
|
||||
|
||||
for row in path_grid:
|
||||
original_row = []
|
||||
for cell in row:
|
||||
# 移除路径信息,恢复原始状态
|
||||
if 'p' in cell:
|
||||
p_index = cell.find('p')
|
||||
original_cell = cell[:p_index] if p_index > 0 else '0'
|
||||
else:
|
||||
original_cell = cell
|
||||
original_row.append(original_cell)
|
||||
original_grid.append(original_row)
|
||||
|
||||
return original_grid
|
||||
|
||||
def get_save_list(self):
|
||||
"""获取所有CSV存档文件列表"""
|
||||
saves = []
|
||||
if os.path.exists(self.save_directory):
|
||||
for filename in os.listdir(self.save_directory):
|
||||
if filename.endswith('.csv'):
|
||||
file_path = os.path.join(self.save_directory, filename)
|
||||
try:
|
||||
stat = os.stat(file_path)
|
||||
saves.append({
|
||||
'filename': filename,
|
||||
'path': file_path,
|
||||
'name': filename.replace('.csv', ''),
|
||||
'save_time': datetime.fromtimestamp(stat.st_mtime).isoformat(),
|
||||
'size': stat.st_size
|
||||
})
|
||||
except:
|
||||
continue
|
||||
return sorted(saves, key=lambda x: x['save_time'], reverse=True)
|
||||
|
||||
def delete_save(self, save_file):
|
||||
"""删除存档文件"""
|
||||
try:
|
||||
if os.path.exists(save_file):
|
||||
os.remove(save_file)
|
||||
print(f"存档已删除: {save_file}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"删除失败: {str(e)}")
|
||||
return False
|
||||
|
||||
# 全局简化存档管理器实例
|
||||
simple_save_manager = SimpleSaveManager()
|
Loading…
Reference in New Issue
Block a user