feat: add avatar metrics tracking feature (#111)
* feat: add avatar metrics tracking feature (#110) Add AvatarMetrics dataclass for tracking avatar state snapshots - Add AvatarMetrics dataclass for recording monthly snapshots - Add metrics_history field to Avatar with opt-in tracking - Implement automatic monthly snapshot recording in Simulator - Add backward compatibility support for existing save files - Set default tracking limit to 1200 months (100 years) - Add comprehensive tests with 100% coverage - Move documentation to specs directory with simplified chinese * fix: convert Traditional Chinese comments to Simplified Chinese 修正程式碼中的繁體中文註解為簡體中文,以符合專案規範。 Fix Traditional Chinese comments to Simplified Chinese in codebase.
This commit is contained in:
278
docs/specs/avatar-metrics-tracking.md
Normal file
278
docs/specs/avatar-metrics-tracking.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# Avatar 状态追踪功能
|
||||
|
||||
## 概述
|
||||
|
||||
新增可选的 Avatar 状态追踪功能,用于记录角色成长轨迹。该功能默认关闭,不影响现有游戏逻辑。
|
||||
|
||||
## 功能特点
|
||||
|
||||
- **可选性**:默认关闭,不影响现有功能
|
||||
- **轻量**:仅记录关键指标(修为、资源、社交等)
|
||||
- **不可变**:快照一旦创建不修改
|
||||
- **持久化**:支持存档/读档
|
||||
- **自动清理**:默认最多保留 1200 笔记录(100 年)
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 启用追踪
|
||||
|
||||
```python
|
||||
# 启用 Avatar 状态追踪
|
||||
avatar.enable_metrics_tracking = True
|
||||
```
|
||||
|
||||
### 自动记录
|
||||
|
||||
追踪启用后,模拟器会在每月自动调用 `record_metrics()`:
|
||||
|
||||
```python
|
||||
# 在 simulator.py 的 _finalize_step() 中自动执行
|
||||
# avatar.record_metrics() # 每月自动调用
|
||||
```
|
||||
|
||||
### 手动记录并标记事件
|
||||
|
||||
```python
|
||||
from src.classes.avatar_metrics import MetricTag
|
||||
|
||||
# 手动记录状态(可选事件标记)
|
||||
avatar.record_metrics(tags=["breakthrough"])
|
||||
avatar.record_metrics(tags=[MetricTag.INJURED.value, MetricTag.BATTLE.value])
|
||||
avatar.record_metrics(tags=["custom_event"]) # 支持自定义标签
|
||||
```
|
||||
|
||||
### 查看摘要
|
||||
|
||||
```python
|
||||
# 获取状态追踪摘要
|
||||
summary = avatar.get_metrics_summary()
|
||||
print(summary)
|
||||
# 输出示例:
|
||||
# {
|
||||
# "enabled": True,
|
||||
# "count": 120,
|
||||
# "first_record": 100,
|
||||
# "latest_record": 220,
|
||||
# "cultivation_growth": 5
|
||||
# }
|
||||
```
|
||||
|
||||
### 访问历史记录
|
||||
|
||||
```python
|
||||
# 直接访问历史记录列表
|
||||
for metrics in avatar.metrics_history:
|
||||
print(f"Month {metrics.timestamp}: Level {metrics.cultivation_level}, HP {metrics.hp}")
|
||||
```
|
||||
|
||||
## 设计原则
|
||||
|
||||
### 1. 可选性
|
||||
|
||||
- 默认关闭(`enable_metrics_tracking = False`)
|
||||
- 不影响现有 API 和逻辑
|
||||
- 可随时启用或禁用
|
||||
|
||||
### 2. 轻量级
|
||||
|
||||
仅记录关键指标:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `timestamp` | `MonthStamp` | 记录时间 |
|
||||
| `age` | `int` | 年龄 |
|
||||
| `cultivation_level` | `int` | 修为等级 |
|
||||
| `cultivation_progress` | `int` | 修为进度 |
|
||||
| `hp` | `float` | 当前生命值 |
|
||||
| `hp_max` | `float` | 最大生命值 |
|
||||
| `spirit_stones` | `int` | 灵石数量 |
|
||||
| `relations_count` | `int` | 关系数量 |
|
||||
| `known_regions_count` | `int` | 已知区域数量 |
|
||||
| `tags` | `List[str]` | 事件标签 |
|
||||
|
||||
### 3. 不可变性
|
||||
|
||||
- 快照一旦创建不修改
|
||||
- 使用 dataclass 保证结构清晰
|
||||
- 支持序列化/反序列化
|
||||
|
||||
### 4. 向后兼容
|
||||
|
||||
- 使用 `default_factory` 避免破坏旧代码
|
||||
- 存档/读档完全兼容
|
||||
- 旧存档会使用默认值(空列表、False)
|
||||
|
||||
## 性能影响
|
||||
|
||||
### 关闭时
|
||||
|
||||
- 零影响(不占用额外内存或 CPU)
|
||||
- `record_metrics()` 直接返回 `None`
|
||||
|
||||
### 开启时
|
||||
|
||||
- 每月新增约 200 bytes 快照
|
||||
- 100 年约 240 KB
|
||||
- **有上限**:默认最多保留 1200 笔记录(可通过 `max_metrics_history` 调整)
|
||||
|
||||
```python
|
||||
# 调整历史记录上限
|
||||
avatar.max_metrics_history = 600 # 改为 50 年
|
||||
```
|
||||
|
||||
## 默认标签
|
||||
|
||||
建议使用 `MetricTag` 枚举中的默认标签:
|
||||
|
||||
| 标签 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| `BREAKTHROUGH` | `"breakthrough"` | 突破 |
|
||||
| `INJURED` | `"injured"` | 受伤 |
|
||||
| `RECOVERED` | `"recovered"` | 康复 |
|
||||
| `SECT_JOIN` | `"sect_join"` | 加入宗门 |
|
||||
| `SECT_LEAVE` | `"sect_leave"` | 离开宗门 |
|
||||
| `TECHNIQUE_LEARN` | `"technique_learn"` | 学习功法 |
|
||||
| `DEATH` | `"death"` | 死亡 |
|
||||
| `BATTLE` | `"battle"` | 战斗 |
|
||||
| `DUNGEON` | `"dungeon"` | 探索秘境 |
|
||||
|
||||
### 使用标签
|
||||
|
||||
```python
|
||||
from src.classes.avatar_metrics import MetricTag
|
||||
|
||||
# 使用枚举(推荐)
|
||||
avatar.record_metrics(tags=[MetricTag.BREAKTHROUGH.value])
|
||||
|
||||
# 多个标签
|
||||
avatar.record_metrics(tags=[
|
||||
MetricTag.INJURED.value,
|
||||
MetricTag.BATTLE.value
|
||||
])
|
||||
|
||||
# 自定义标签(也支持)
|
||||
avatar.record_metrics(tags=["custom_event", "special_occurrence"])
|
||||
```
|
||||
|
||||
## 数据结构
|
||||
|
||||
### AvatarMetrics
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class AvatarMetrics:
|
||||
timestamp: MonthStamp
|
||||
age: int
|
||||
cultivation_level: int
|
||||
cultivation_progress: int
|
||||
hp: float
|
||||
hp_max: float
|
||||
spirit_stones: int
|
||||
relations_count: int
|
||||
known_regions_count: int
|
||||
tags: List[str]
|
||||
|
||||
def to_save_dict(self) -> dict:
|
||||
"""转换为可序列化的字典"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def from_save_dict(cls, data: dict) -> "AvatarMetrics":
|
||||
"""从字典重建"""
|
||||
pass
|
||||
```
|
||||
|
||||
## 序列化
|
||||
|
||||
### 存档
|
||||
|
||||
状态追踪数据会自动包含在存档中:
|
||||
|
||||
```python
|
||||
save_dict = avatar.to_save_dict()
|
||||
# 包含:
|
||||
# - "metrics_history": [...]
|
||||
# - "enable_metrics_tracking": True
|
||||
```
|
||||
|
||||
### 读档
|
||||
|
||||
读档时自动恢复:
|
||||
|
||||
```python
|
||||
avatar = Avatar.from_save_dict(data, world)
|
||||
# metrics_history 和 enable_metrics_tracking 自动恢复
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
### 1. 标签的可变性
|
||||
|
||||
`tags` 字段使用 `List[str]` 而非 `List[MetricTag]`,提供灵活性:
|
||||
- 支持默认标签(使用 `MetricTag.value`)
|
||||
- 支持自定义标签
|
||||
- 允许混合使用
|
||||
|
||||
### 2. 自动清理
|
||||
|
||||
历史记录超过 `max_metrics_history` 时会自动清理旧记录:
|
||||
```python
|
||||
# 保留最新的 N 笔记录
|
||||
if len(self.metrics_history) > self.max_metrics_history:
|
||||
self.metrics_history = self.metrics_history[-self.max_metrics_history:]
|
||||
```
|
||||
|
||||
### 3. 不可变性
|
||||
|
||||
快照对象本身是可变的(Python dataclass 默认),但设计上应视为不可变:
|
||||
- 创建后不修改 `AvatarMetrics` 对象
|
||||
- 如需更新,创建新快照
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 追踪修为成长
|
||||
|
||||
```python
|
||||
# 启用追踪
|
||||
avatar.enable_metrics_tracking = True
|
||||
|
||||
# 模拟运行...
|
||||
# 自动记录每月状态
|
||||
|
||||
# 分析成长
|
||||
first = avatar.metrics_history[0]
|
||||
latest = avatar.metrics_history[-1]
|
||||
print(f"修为增长: {latest.cultivation_level - first.cultivation_level}")
|
||||
```
|
||||
|
||||
### 标记重大事件
|
||||
|
||||
```python
|
||||
# 突破时标记
|
||||
if avatar.cultivation_progress.realm != old_realm:
|
||||
avatar.record_metrics(tags=[MetricTag.BREAKTHROUGH.value])
|
||||
|
||||
# 受伤时标记
|
||||
if avatar.hp.value < avatar.hp.max_value * 0.3:
|
||||
avatar.record_metrics(tags=[MetricTag.INJURED.value])
|
||||
```
|
||||
|
||||
### 分析游戏数据
|
||||
|
||||
```python
|
||||
# 导出数据到 CSV/Pandas
|
||||
import pandas as pd
|
||||
|
||||
data = []
|
||||
for metrics in avatar.metrics_history:
|
||||
data.append({
|
||||
"timestamp": metrics.timestamp,
|
||||
"age": metrics.age,
|
||||
"level": metrics.cultivation_level,
|
||||
"hp": metrics.hp,
|
||||
"spirit_stones": metrics.spirit_stones,
|
||||
})
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
df.plot(x="timestamp", y="level")
|
||||
```
|
||||
Reference in New Issue
Block a user