fix: code review — 修复4个问题

1. CardTracker.UnknownCount: 48→52 (硬编码修正)
2. IsmctsBot.GenerateDeck → 静态FullDeck池复用 (减少对象分配)
3. CardTracker: 删除3个死字段(_passStreak/_isNewRound/_bombCounts)
4. CardTracker.Update: 清理pass约束记录逻辑

验证: 100局0错误, 0.9s/局
This commit is contained in:
2026-07-13 00:21:15 +08:00
parent 7fdc45057c
commit 180be25ffa
8 changed files with 25 additions and 34 deletions

View File

@ -29,13 +29,6 @@ namespace PdkFriendServer.Logic
// 对手约束:[pos-2] = 该对手 pass 过的牌型列表 // 对手约束:[pos-2] = 该对手 pass 过的牌型列表
private readonly List<PassConstraint>[] _opponentConstraints; private readonly List<PassConstraint>[] _opponentConstraints;
// 当前轮次 pass 计数
private int _passStreak;
private bool _isNewRound;
// 每个位置的炸弹计数
private readonly int[] _bombCounts = new int[3];
public CardTracker() public CardTracker()
{ {
_opponentConstraints = new List<PassConstraint>[3]; _opponentConstraints = new List<PassConstraint>[3];
@ -64,32 +57,21 @@ namespace PdkFriendServer.Logic
var currentMax = view.MaxPlayCard; var currentMax = view.MaxPlayCard;
if (currentMax.Ids != null && currentMax.Ids.Length > 0) if (currentMax.Ids != null && currentMax.Ids.Length > 0)
{ {
// 如果是新出牌(不是上一轮的) // 新出牌 → 记录
if (_lastMaxPlay.Ids == null || if (_lastMaxPlay.Ids == null ||
!_lastMaxPlay.Ids.SequenceEqual(currentMax.Ids)) !_lastMaxPlay.Ids.SequenceEqual(currentMax.Ids))
{ {
foreach (var id in currentMax.Ids) _seenCardIds.Add(id); foreach (var id in currentMax.Ids) _seenCardIds.Add(id);
_passStreak = 0;
_isNewRound = false;
}
else if (_lastMaxPlay.Ids != null)
{
// 同一手牌还在桌上 → 有人 pass 了
_passStreak++;
// 记录 pass 约束(轮到谁 pass 了就是谁)
int passer = currentMax.Pos > 0
? (currentMax.Pos % 3 + 1) // 下一位
: 0;
if (passer > 0 && passer != myPos)
{
RecordPassConstraint(passer, currentMax);
}
}
} }
else else
{ {
_isNewRound = true; // 同一手牌还在桌上 → 有人 pass 了
_passStreak = 0; int passer = currentMax.Pos > 0
? (currentMax.Pos % 3 + 1)
: 0;
if (passer > 0 && passer != myPos)
RecordPassConstraint(passer, currentMax);
}
} }
_lastMaxPlay = new PlayOutCardPdkF _lastMaxPlay = new PlayOutCardPdkF
@ -99,10 +81,6 @@ namespace PdkFriendServer.Logic
Type = currentMax.Type, Type = currentMax.Type,
Ids = currentMax.Ids?.ToArray() Ids = currentMax.Ids?.ToArray()
}; };
// 3. 追踪炸弹计数
for (int i = 0; i < 3; i++)
_bombCounts[i] = view.ZhaDans[i];
} }
/// <summary> /// <summary>
@ -113,16 +91,13 @@ namespace PdkFriendServer.Logic
_seenCardIds.Clear(); _seenCardIds.Clear();
_lastMyHandIds = null; _lastMyHandIds = null;
_lastMaxPlay = new PlayOutCardPdkF(); _lastMaxPlay = new PlayOutCardPdkF();
_passStreak = 0;
_isNewRound = true;
foreach (var c in _opponentConstraints) c.Clear(); foreach (var c in _opponentConstraints) c.Clear();
Array.Clear(_bombCounts, 0, 3);
} }
// ---- 查询接口 ---- // ---- 查询接口 ----
/// <summary>返回外面还有多少未知卡</summary> /// <summary>返回外面还有多少未知卡</summary>
public int UnknownCount => 48 - _seenCardIds.Count; public int UnknownCount => 52 - _seenCardIds.Count;
/// <summary>在外面可能存在的炸弹数(按 rank 计)</summary> /// <summary>在外面可能存在的炸弹数(按 rank 计)</summary>
public int BombsOutside(TCardInfoPdkF[] myHand) public int BombsOutside(TCardInfoPdkF[] myHand)

View File

@ -295,7 +295,7 @@ namespace PdkFriendServer.Logic
hands[nextPos - 1] = new List<TCardInfoPdkF>(); hands[nextPos - 1] = new List<TCardInfoPdkF>();
hands[oppPos - 1] = new List<TCardInfoPdkF>(); hands[oppPos - 1] = new List<TCardInfoPdkF>();
var allCards = GenerateDeck(); var allCards = GetDeckCopy();
var unknown = allCards.Where(c => !myHand.Any(h => h.ID == c.ID)).ToList(); var unknown = allCards.Where(c => !myHand.Any(h => h.ID == c.ID)).ToList();
// 使用 CardTracker 进行约束采样 // 使用 CardTracker 进行约束采样
@ -495,13 +495,29 @@ namespace PdkFriendServer.Logic
return best; return best;
} }
private static List<TCardInfoPdkF> GenerateDeck() // 静态牌池52张牌只创建一次
private static readonly TCardInfoPdkF[] FullDeck;
static IsmctsBot()
{ {
var d = new List<TCardInfoPdkF>(); int id = 1; FullDeck = new TCardInfoPdkF[52];
int id = 1;
foreach (int f in new[] { 1, 2, 3, 4 }) foreach (int f in new[] { 1, 2, 3, 4 })
for (int n = 1; n <= 13; n++) for (int n = 1; n <= 13; n++)
d.Add(new TCardInfoPdkF { ID = (byte)id++, Flower = (byte)f, GameNum = (byte)(n == 1 ? 14 : n == 2 ? 16 : n), GameState = 1 }); FullDeck[id - 1] = new TCardInfoPdkF
return d; {
ID = (byte)id,
Flower = (byte)f,
GameNum = (byte)(n == 1 ? 14 : n == 2 ? 16 : n),
GameState = 1
};
}
private static List<TCardInfoPdkF> GetDeckCopy()
{
return FullDeck.Where(c => c.ID > 0).Select(c => new TCardInfoPdkF
{
ID = c.ID, Flower = c.Flower, GameNum = c.GameNum, GameState = 1
}).ToList();
} }
private void Shuffle<T>(List<T> l) { for (int i = l.Count - 1; i > 0; i--) { int j = _rng.Next(i + 1); (l[i], l[j]) = (l[j], l[i]); } } private void Shuffle<T>(List<T> l) { for (int i = l.Count - 1; i > 0; i--) { int j = _rng.Next(i + 1); (l[i], l[j]) = (l[j], l[i]); } }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.