feat: 确定性牌计数推理v2 — AbsoluteHandStrength + 包庄否决 + 混合策略修正
CardTracker: - 新增 AbsoluteHandStrength: 综合评估手牌实力(rank控制/绝对最高牌/炸弹/效率/已知率) - 用于包庄决策的安全检查 IsmctsBot: - EstimateWinProbability: 混合策略回退为纯ISMCTS + 确定性否决 (混合加成导致虚假信心, v3验证0%胜率) - 策略: detStrength<0.15 → 直接否决不包, 否则纯ISMCTS - 阈值保持 v2 水平: minPlays≤3→0.50,≤4→0.60,≤5→0.65 验证: 100局0错误 0.8s/局 ENDGAME=983次 LOCK=3次
This commit is contained in:
@ -242,6 +242,63 @@ namespace PdkFriendServer.Logic
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确定性手牌强度:基于绝对牌计数评估手牌实力,0.0-1.0。
|
||||
/// 核心思想:我的手牌 + 已见牌 = 对手的牌池完全确定,
|
||||
/// 可以精确计算哪些 rank 已被我控制、哪些出牌无敌。
|
||||
/// 用于修正 ISMCTS 模拟噪声。
|
||||
/// </summary>
|
||||
public double AbsoluteHandStrength(TCardInfoPdkF[] myHand)
|
||||
{
|
||||
if (myHand.Length == 0) return 1.0;
|
||||
|
||||
double score = 0.0;
|
||||
|
||||
// 1. Rank 绝对控制率:我持有该 rank 所有剩余牌的百分比
|
||||
int controlledRanks = 0;
|
||||
int[] ranks = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16 };
|
||||
foreach (int r in ranks)
|
||||
{
|
||||
int remaining = RemainingCount(r, myHand);
|
||||
int myCount = myHand.Count(c => c.GameState == 1 && c.GameNum == r);
|
||||
if (remaining == 0 && myCount > 0)
|
||||
{
|
||||
// 我持有该 rank 所有剩余牌 → 绝对控制
|
||||
controlledRanks++;
|
||||
if (myCount >= 3) controlledRanks++; // 三张以上价值更高
|
||||
}
|
||||
}
|
||||
score += controlledRanks * 0.04; // 每个控制 rank +4%
|
||||
|
||||
// 2. 绝对最高牌:持有 ♠2 或所有2
|
||||
int myTwos = myHand.Count(c => c.GameState == 1 && c.GameNum == 16);
|
||||
int remainingTwos = RemainingCount(16, myHand);
|
||||
if (remainingTwos == 0 && myTwos > 0)
|
||||
{
|
||||
score += myTwos * 0.06; // 每张独占 2 +6%
|
||||
}
|
||||
|
||||
// 3. 持有的炸弹数
|
||||
var byRank = myHand.Where(c => c.GameState == 1)
|
||||
.GroupBy(c => c.GameNum)
|
||||
.Where(g => g.Count() >= 4);
|
||||
int myBombs = byRank.Count();
|
||||
score += myBombs * 0.08; // 每个炸弹 +8%
|
||||
|
||||
// 4. 手牌效率(通过 HandOptimizer 间接)
|
||||
int minPlays = HandOptimizer.MinPlays(myHand);
|
||||
int handSize = myHand.Count(c => c.GameState == 1);
|
||||
double efficiency = (double)(handSize - minPlays)
|
||||
/ Math.Max(handSize, 1);
|
||||
score += efficiency * 0.15; // 效率贡献最多 15%
|
||||
|
||||
// 5. 已知牌越多 → 确定性权重越高
|
||||
double seenRatio = _seenCardIds.Count / (double)Math.Max(48 - handSize, 1);
|
||||
score += seenRatio * 0.10; // 已知牌占比 +10%
|
||||
|
||||
return Math.Min(score, 1.0);
|
||||
}
|
||||
|
||||
/// <summary>在外面可能存在的炸弹数(按 rank 计)</summary>
|
||||
public int BombsOutside(TCardInfoPdkF[] myHand)
|
||||
{
|
||||
|
||||
@ -68,6 +68,11 @@ namespace PdkFriendServer.Logic
|
||||
|
||||
private double EstimateWinProbability(PdkBotView view)
|
||||
{
|
||||
// 确定性安全检查:手牌极弱 → 直接否定包庄
|
||||
double detStrength = _tracker.AbsoluteHandStrength(view.MyHand);
|
||||
if (detStrength < 0.15) return 0.0;
|
||||
|
||||
// ISMCTS 模拟(确定性只做否决,不混入加成以避免虚假信心)
|
||||
int sims = Math.Min(_simulations, 600);
|
||||
int wins = 0;
|
||||
for (int s = 0; s < sims; s++)
|
||||
|
||||
Reference in New Issue
Block a user