AI 系统设计与优化方案
整合:AI 模块架构 + AI 间谍机制 + 士兵间谍战术 | 2026-04-29
一、AI 模块架构
1.1 目录结构
AI/
├── Search/ # 搜索层 — 决策入口,驱动搜索树
│ ├── AISearchEngine.cs FindBestMove / SearchState / ChooseHumanLikeMove
│ └── AIProfile.cs AI 难度配置(搜索深度、思考延迟、Human-Like 窗口)
│
├── Evaluation/ # 估值层 — 纯函数,评估局面与走法
│ ├── AIEvaluator.cs 棋子价值 / 距离 / GetOppositeSide / 护将 / 堡垒判断
│ ├── AIMoveScorer.cs EvaluateSimState(局面估值)/ ScoreSimMoveOrdering(走法排序)
│
├── Simulation/ # 模拟层 — 状态快照与走法生成
│ ├── AIMoveGenerator.cs GenerateSimMoves / BuildSimAvailablePaths / ApplySimMove / TryCreateSimMove
│ ├── AISimState.cs 模拟局面快照(pieces / occupiedGrid / spyIds)
│ ├── AISimPiece.cs 模拟棋子快照
│ ├── AISimMove.cs 模拟走法数据结构
│ ├── AISimMoveDecision.cs 决策结果结构体
│ └── AISimHelpers.cs 状态构建 / 间谍揭露判断 / 辅助函数
│
└── Tactic/ # 战术层 — 战术选择与走法奖励加权
├── AITacticManager.cs 战术初始化 / 阶段混合 / GetTacticBonus
└── AITacticProfile.cs 战术数据结构 + 8 个预定战术库
1.2 分层依赖(自上而下,无循环)
Search ──────→ Simulation ──────→ Evaluation
│ │ │
│ └──→ Tactic ←───────┘
│ │
└──────────────────────┘
1.3 数据流
GameManager (开局)
└→ AITacticManager.InitializeForGame()
GameManager.RunAITurn()
├→ AISimHelpers.BuildCurrentSimState() # 构建模拟快照
├→ AISimHelpers.ShouldAIRevealSpy() # 间谍揭露判断(大臣)
├→ AISimHelpers.ShouldAIRevealSoldierSpy() # 间谍揭露判断(士兵)← 新增
├→ AITacticManager.UpdateForStep(stepCount) # 更新阶段战术
└→ AISearchEngine.FindBestMove() # 搜索最佳走法
├→ AIMoveGenerator.GenerateSimMoves() # 生成走法
│ └→ AIMoveScorer.ScoreSimMoveOrdering() # 排序 + 战术加权
├→ AIMoveGenerator.ApplySimMove() # 应用走法到临时状态
└→ AISearchEngine.SearchState() # Minimax 递归
├→ AIMoveScorer.EvaluateSimState() # 终局面估值
└→ AIMoveGenerator.GenerateSimMoves() # 展开下层走法
二、问题汇总
| # | 问题 | 严重程度 | 当前所在文件 |
|---|---|---|---|
| P1 | 揭露阈值 revealGain > 1.5f 过低,开局轻易触发 |
高 | AISimHelpers.ShouldAIRevealSpy |
| P4 | occupiedGrid 静态注入,破坏搜索树隔离 |
中 | AIMinimax.WithSimOccupiedGrid(已删除) |
| P6 | 无历史行为统计,无法建立对方送死模式 | 低 | — |
三、方案详述
P1:揭露阈值动态化
现状:
// AISimHelpers.ShouldAIRevealSpy
if (revealGain > 1.5f) return true; // 固定阈值,开局轻易跨越
优化方案:
float GetRevealThreshold(int stepCount, int aiSpyCount, int enemySpyCount)
{
float baseThreshold = 3.0f; // 从 1.5f 提高到 3.0f
float stepPenalty = Mathf.Min(stepCount * 0.05f, 1.5f);
float spyDisadvantage = (enemySpyCount > aiSpyCount) ? 0.5f : 0f;
return baseThreshold - stepPenalty - spyDisadvantage;
}
P4:消除 occupiedGrid 静态注入
优化方案: RuleConfig.GetStepsFromRules 和 GetFortressMoves 增加 int[] occupiedGrid = null 参数,优先使用传入参数,否则回退到 PitchGuide.occupiedGrid。AI 调用处直接传 state.occupiedGrid,不再污染全局。
四、战术系统
权重向量方案。每步走法在
ScoreSimMoveOrdering末尾叠加战术奖励分。
4.1 战术维度(7 个权重)
| 维度 | 高值表现 | 低值表现 |
|---|---|---|
| aggression | 优先逼近敌方将军、吃子 | 保持距离、防守反击 |
| spyFocus | 保护间谍、延迟揭露 | 不依赖间谍,正常行棋 |
| centerControl | 抢占棋盘中心格子 | 边路迂回 |
| fortressControl | 占领利用堡垒 | 远离堡垒 |
| tradeValue | 愿意兑子(如马换马) | 只打安全子、避免兑子 |
| lordProtection | 守卫贴近 Lord、拦截威胁 | 守卫自由行动 |
| mobility | 远距离移动、激活后方棋子 | 逐步推进 |
4.2 预定战术库(8 个)
| 战术 | aggression | spyFocus | centerControl | fortressControl | tradeValue | lordProtection | mobility |
|---|---|---|---|---|---|---|---|
| 猛攻 | +1.5 | -0.5 | +0.5 | 0 | +1.2 | -0.5 | +0.8 |
| 稳守 | -0.8 | 0 | +0.3 | +0.5 | -0.5 | +1.5 | -0.3 |
| 暗流 | 0 | +2.0 | 0 | 0 | +0.3 | +0.2 | +0.5 |
| 速决 | +1.0 | -1.0 | +0.8 | 0 | +1.0 | -0.3 | +1.5 |
| 蚕食 | +0.5 | 0 | +0.4 | 0 | +1.5 | +0.5 | -0.2 |
| 控中 | 0 | 0 | +2.0 | +0.3 | 0 | +0.5 | 0 |
| 奇袭 | +1.3 | +0.5 | -1.0 | -0.5 | +0.8 | -0.8 | +1.5 |
| 固垒 | -0.5 | 0 | -0.3 | +2.0 | -0.3 | +1.0 | -0.5 |
4.3 阶段混合规则
| 游戏阶段 | 步数 | 战术规则 |
|---|---|---|
| 开局 | 0-12步 | 主战术 70% + 辅助战术 30% |
| 中盘 | 13-25步 | 主战术 100% |
| 残局 | 26步+ | 主战术 + 自动 +aggression +lordProtection(追杀模式) |
五、士兵间谍机制
5.1 核心规则
| 规则 | 内容 |
|---|---|
| 揭露次数 | 每方仅一次,揭露时对方全部间谍(士兵+大臣)同时暴露 |
| 揭露时机 | 劣势或决定性战役(优势时不揭露) |
| 揭露效果 | 两个士兵间谍阵营转回 host side,之后与普通士兵无异 |
| 己方误吃 | 无任何特殊表现——只当普通吃子,不触发揭露,不影响状态 |
| 升变与揭露 | 完全独立——升变不触发揭露,揭露不改变升变进度 |
| 无自动揭露 | 到终局棋子也不会自动反叛 |
5.2 大臣间谍 vs 士兵间谍的 AI 决策差异
| 维度 | 大臣间谍 | 士兵间谍 |
|---|---|---|
| 揭露判断 | ShouldAIRevealSpy(动态阈值) |
ShouldAIRevealSoldierSpy(劣势触发) |
| 揭露后棋子类型变化 | 无(仍是 Minister → 按被吃棋子类型升格) | 无(仍是 Soldier → 走到底线升 Officer) |
| AI 对己方间谍的感知 | 知道具体哪枚是间谍 | 不知道(只知道数量) |
| AI 对敌方间谍的感知 | 不知道具体哪枚 | 不知道(只知道数量) |
| 战术操控性 | 高(可规划升变路径) | 低(无主动操控手段) |
5.3 士兵间谍的价值来源
主动价值: 揭露时机的选择权在己方,劣势或决定性战役时打出。
被动价值: 敌方始终处于"吃/不吃士兵"的不确定恐惧中——因为你还没揭露,这份恐惧持续给己方施加压力空间。
核心公式:
士兵间谍价值 = 敌方对"揭露"的恐惧
揭露价值最大化时机 = 己方劣势 + 敌方也处于犹豫中
5.4 士兵间谍揭露决策
触发条件(满足其一):
| 条件 | 说明 |
|---|---|
aiSideDisadvantage > 2.0f |
劣势足够大,不赌不行了 |
| 当前回合能直接吃将军 | 决定性战役,一击必杀 |
| 敌方升级棋子(Officer/Priest/Assassin/Iron Guard)存活多 | 赌己方士兵间谍已混入其中 |
| 己方剩余士兵间谍 = 1,局势胶着 | 不用就浪费了 |
绝对不应揭露的时机:
- 己方明显优势时(揭露 = 白送敌人确定性)
- 游戏刚开始(士兵间谍还没来得及制造恐惧价值)
5.5 敌方视角的评分惩罚
- 当 AI 评估"吃某个士兵"的走法时,加入犹豫惩罚
- 惩罚幅度 ≈
soldierSpyCount * 0.2f(两个未揭露时约 0.4) - 这个惩罚在己方揭露后消失
六、预期效果
| 指标 | 当前 | 优化后 |
|---|---|---|
| 开局揭露率 | 高(阈值 1.5) | 低(阈值 3.0起步) ✅ |
| occupiedGrid 泄露 | 有 | 无(参数传入) ✅ |
| 历史行为校准 | 无 | 暂缓 ⏸️ |
七、实施记录
| 日期 | 范围 | 文件变更 |
|---|---|---|
| 2026-04-29 | P1 揭露阈值动态化 | Simulation/AISimHelpers.cs |
| 2026-04-29 | P4 消除静态注入 | Rules/RuleConfig.GetFortressMoves +occupiedGrid参数 |
| 2026-04-29 | 战术系统 | 新建 Tactic/AITacticManager.cs + AITacticProfile.cs |
| 2026-04-29 | AIMinimax 拆分 | 新建 Search/ Evaluation/ Simulation/ Tactic/ |
| 2026-04-29 | 文档合并 | 本文档整合架构 + 士兵间谍机制 |
所有内容仅供学习与交流,转载须标明链接。未经同意,禁止作为商业用途,有特殊需求请联系站长。
