Skip to content

feat: 新增并发批量战斗模拟;引入MariaDB作为可配置数据存储层;修复经验数据写入bug - #5

Merged
AofeiLi-code merged 2 commits into
AofeiLi-code:mainfrom
cy6909:fight-simulate
May 1, 2026
Merged

feat: 新增并发批量战斗模拟;引入MariaDB作为可配置数据存储层;修复经验数据写入bug#5
AofeiLi-code merged 2 commits into
AofeiLi-code:mainfrom
cy6909:fight-simulate

Conversation

@cy6909

@cy6909 cy6909 commented Apr 29, 2026

Copy link
Copy Markdown

【新增文件】

  1. .env - 数据库连接配置文件(DB_ENABLED开关+连接参数)
  2. sim/data_store.py - 统一数据存储接口层
    • DataStore类:根据.env自动选择后端(MariaDB/JSON)
    • SQLAlchemy模型:BattleRecord、ExperienceRecord、PokemonRecord
    • init_db()/migrate_experience_from_json()等工具函数
  3. db_manage.py - 数据库管理CLI(init/migrate/stats命令)
  4. sim/batch_concurrent.py - 并发批量战斗模拟器
    • multiprocessing.Pool并行MCTS对战
    • 经验数据合并+存储逻辑
  5. battle.py - 菜单集成并发模拟模式(选项2,默认推荐)

【修改文件】

  1. sim/experience_db.py - ExperienceDB增加数据库后端支持
    • _use_db属性:动态读取DataStore.db_enabled选择后端
    • save()/load()统一接口自动路由到对应后端
    • _dirty_keys增量保存策略,避免全量重写
  2. battle.py - 菜单选项4集成并发模拟+经验合并

【修复的3个核心Bug】

  1. Bug#1: _merge_experience_dbs()类型错误(ActionStats vs dict)
    • 根因:子进程返回的是ActionStats对象,但合并函数用.get('w',0)当dict处理
    • 修复:在合并时加isinstance(stats_obj, ActionStats)判断
  2. Bug#2: MCTS Agent替换经验库时_search引用未同步
    • 根因:agent_a.experience_db=exp_a只改了agent属性, 但MCTSSearch内部持有独立的experience_db引用导致脱节
    • 修复:同时设置agent_a._search.experience_db = exp_a
  3. Bug#3: _merge_experience_dbs()团队数据交叉污染
    • 根因:result['exp_a']包含完整经验库(team a和b), 合并函数遍历整个dict导致a队经验被放进merged的'a'和'b'
    • 修复:改为直接按队伍分别提取,A队只取get('a',{})

【数据流】
子进程(_run_single_battle) → pickle序列化传递ActionStats对象 → 主进程合并(merged_db_a/merged_db_b) → DataStore.update_experience() → MariaDB experience_records表(增量UPSERT,同key累加wins+total)

【测试验证】

  1. python db_manage.py init - 数据库初始化成功
  2. python db_manage.py migrate - JSON→数据库迁移成功(371条精灵记录)
  3. 5场并发对战测试:经验数据正确写入MariaDB,a队wins>0,b队total>0
  4. ExperienceDB.load_or_create('预设毒队')从数据库加载:164条有效记录
  5. MCTSAgent('a','预设毒队',load_exp=True)正确加载数据库经验
  6. 新一轮3场模拟后经验累加:231→412条,确认增量保存正常
  7. get_weights()在相同state_key下对不同action_key返回独立胜率权重

【向后兼容】
.env中DB_ENABLED=false时完全退化为JSON文件模式,不影响现有功能

【新增文件】
1. .env - 数据库连接配置文件(DB_ENABLED开关+连接参数)
2. sim/data_store.py - 统一数据存储接口层
   - DataStore类:根据.env自动选择后端(MariaDB/JSON)
   - SQLAlchemy模型:BattleRecord、ExperienceRecord、PokemonRecord
   - init_db()/migrate_experience_from_json()等工具函数
3. db_manage.py - 数据库管理CLI(init/migrate/stats命令)
4. sim/batch_concurrent.py - 并发批量战斗模拟器
   - multiprocessing.Pool并行MCTS对战
   - 经验数据合并+存储逻辑
5. battle.py - 菜单集成并发模拟模式(选项2,默认推荐)

【修改文件】
1. sim/experience_db.py - ExperienceDB增加数据库后端支持
   - _use_db属性:动态读取DataStore.db_enabled选择后端
   - save()/load()统一接口自动路由到对应后端
   - _dirty_keys增量保存策略,避免全量重写
2. battle.py - 菜单选项4集成并发模拟+经验合并

【修复的3个核心Bug】
1. Bug#1: _merge_experience_dbs()类型错误(ActionStats vs dict)
   - 根因:子进程返回的是ActionStats对象,但合并函数用.get('w',0)当dict处理
   - 修复:在合并时加isinstance(stats_obj, ActionStats)判断
2. Bug#2: MCTS Agent替换经验库时_search引用未同步
   - 根因:agent_a.experience_db=exp_a只改了agent属性,
     但MCTSSearch内部持有独立的experience_db引用导致脱节
   - 修复:同时设置agent_a._search.experience_db = exp_a
3. Bug#3: _merge_experience_dbs()团队数据交叉污染
   - 根因:result['exp_a']包含完整经验库(team a和b),
     合并函数遍历整个dict导致a队经验被放进merged的'a'和'b'
   - 修复:改为直接按队伍分别提取,A队只取get('a',{})

【数据流】
子进程(_run_single_battle) → pickle序列化传递ActionStats对象
→ 主进程合并(merged_db_a/merged_db_b) → DataStore.update_experience()
→ MariaDB experience_records表(增量UPSERT,同key累加wins+total)

【测试验证】
1. python db_manage.py init - 数据库初始化成功
2. python db_manage.py migrate - JSON→数据库迁移成功(371条精灵记录)
3. 5场并发对战测试:经验数据正确写入MariaDB,a队wins>0,b队total>0
4. ExperienceDB.load_or_create('预设毒队')从数据库加载:164条有效记录
5. MCTSAgent('a','预设毒队',load_exp=True)正确加载数据库经验
6. 新一轮3场模拟后经验累加:231→412条,确认增量保存正常
7. get_weights()在相同state_key下对不同action_key返回独立胜率权重

【向后兼容】
.env中DB_ENABLED=false时完全退化为JSON文件模式,不影响现有功能
@AofeiLi-code
AofeiLi-code merged commit 08d1bf3 into AofeiLi-code:main May 1, 2026
@AofeiLi-code

AofeiLi-code commented May 1, 2026

Copy link
Copy Markdown
Owner

Thank you for your contribution!

AofeiLi-code pushed a commit that referenced this pull request May 9, 2026
AofeiLi-code pushed a commit that referenced this pull request May 9, 2026
合并 origin/main 后,PR #5 的新代码在中文 Windows 下完全跑不起来;
3 个最小补丁让 DataStore + run_battle 可用:

1. sim/agent_base.py — `AgentProtocol` 加 @runtime_checkable
   原因:run_battle 内 isinstance(agent, AgentProtocol) 检查 has_human,
   Python typing.Protocol 默认禁止 isinstance,触发 TypeError。
   后果:菜单 1 任意模式(含 AI vs AI)一调 run_battle 就崩。

2. sim/data_store.py:_load_env — open() 加 encoding="utf-8"
   原因:.env 含中文注释,中文 Windows 默认 GBK 解码会爆。
   后果:python db_manage.py * 任意子命令开局即 UnicodeDecodeError。

3. sim/data_store.py:migrate_experience_from_json — 兼容 ExperienceDB 实际序列化结构
   原因:ExperienceDB.save() 写出的是 {"total_games": N, "db": {"a":..., "b":...}},
   但迁移函数直接遍历顶层找 "a"/"b",结果一条经验都没识别。
   后果:python db_manage.py migrate 报"成功"但 0 条经验入库。

验证:迁移后 30310 条经验 + 371 条精灵入库,
菜单 1 模式 1 + 菜单 4 并发批量均跑通,DB 增量保存正常。
AofeiLi-code pushed a commit that referenced this pull request May 9, 2026
上游 PR #5 引入 HumanAgent + AgentProtocol 后,
run_battle 已通过 agent_a/agent_b 参数支持人机对战,
旧的 run_human_vs_ai_battle() 与配套的 _human_choose_action()
不再被任何菜单调用,纯死代码。

同时删除 96 行的 _human_choose_action UI(被 HumanAgent 内部 UI 替代)。
共删除约 160 行死代码,battle.py 更聚焦在菜单 + run_battle 编排。
AofeiLi-code pushed a commit that referenced this pull request May 9, 2026
requirements.txt
  上游 PR #5 用了但没列:sqlalchemy / pymysql / pyyaml / python-dotenv
  全部补齐并加分组注释。

.env / data/llm_config.yaml 移出版本控制
  原因:两份文件都含密码 / API key,上游不慎把含明文 root 密码的 .env
  和带 endpoint/model 的 llm_config 直接 commit 进 repo。
  本 commit 加 .gitignore 规则 + 配 .example 模板。
  注:物理删除(git rm --cached)已在前一个 fix commit 误带入,
  本 commit 仅补 gitignore 规则与 .example 模板。

LLM 配置默认换为 DeepSeek
  llm_config.yaml.example:endpoint=api.deepseek.com/v1,
  model=deepseek-v4-pro,api_key 占位。
  本机真实配置存 data/llm_config.yaml(已 gitignore)。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants