feat: 智能SimulateToEnd — PickBestTip按跟牌/领牌区分策略

回退纯贪心: Count×10+rank → 总是出最大牌(跟牌时浪费高牌,领牌时不效率)

修复:
- 跟牌(isNewRound=false): 出刚好压制的最小牌,保留大牌用于后续压制
  评分 = Count×1000 + rank(越小越好)
- 领牌(isNewRound=true): 优先多牌组合提高清牌效率
  评分 = Count×100 - rank(越大越好,多牌优先,同类型出小rank)

效果(100局vs 30局V4折算):
  ShunZi 76→102(+34%), DuiZi 224→236(+5%), ShunZi明显提升
  SanDai2 92→72(回到V1基准线), 多牌比例24.1%

验证: 100局0错误 0.8s/局
This commit is contained in:
2026-07-13 01:28:58 +08:00
parent 30a7f875f8
commit c23b53adf9
5 changed files with 35 additions and 9 deletions

View File

@ -438,7 +438,7 @@ namespace PdkFriendServer.Logic
if (curTips != null && curTips.Count > 0)
{
var pick = PickBestTip(curTips);
var pick = PickBestTip(curTips, curHand, false);
maxPlay = new PlayOutCardPdkF { Pos = (byte)current, GameNum = pick[0].GameNum, Type = pick.Count == 1 ? CardType1.DanZhang : CardType1.DuiZi, Ids = pick.Select(c => c.ID).ToArray() };
maxPlayer = current; passStreak = 0;
foreach (var id in maxPlay.Ids) curHand.RemoveAll(c => c.ID == id);
@ -450,7 +450,7 @@ namespace PdkFriendServer.Logic
var ft = PdkCardAlgorithm.GetTipCard(new PlayOutCardPdkF(), curHand.ToArray(), null, false);
if (ft != null && ft.Count > 0)
{
var pick = PickBestTip(ft);
var pick = PickBestTip(ft, curHand, true);
maxPlay = new PlayOutCardPdkF { Pos = (byte)current, GameNum = pick[0].GameNum, Type = CardType1.DanZhang, Ids = pick.Select(c => c.ID).ToArray() };
maxPlayer = current;
foreach (var id in maxPlay.Ids) curHand.RemoveAll(c => c.ID == id);
@ -576,17 +576,43 @@ namespace PdkFriendServer.Logic
return runs;
}
private static List<TCardInfoPdkF> PickBestTip(List<List<TCardInfoPdkF>> tips)
/// <summary>
/// 智能出牌选择:模拟中的虚拟对手应该合理出牌,而非纯贪心。
/// - 跟牌时:出刚好能压制的最小牌(保留大牌)
/// - 领牌时:优先多牌组合(提高清牌效率)
/// </summary>
private static List<TCardInfoPdkF> PickBestTip(List<List<TCardInfoPdkF>> tips,
List<TCardInfoPdkF> curHand, bool isNewRound)
{
if (tips == null || tips.Count == 0) return null;
var best = tips[tips.Count - 1];
int bestScore = best.Count * 10 + best[0].GameNum;
for (int i = tips.Count - 2; i >= 0; i--)
if (!isNewRound)
{
int s = tips[i].Count * 10 + tips[i][0].GameNum;
if (s > bestScore) { bestScore = s; best = tips[i]; }
// 跟牌:出刚好能压制的最小牌
// 策略:出最小 rank 的有效牌,保留大牌用于后续压制
var best = tips[0];
int bestScore = int.MaxValue;
foreach (var tip in tips)
{
// 同数量优先选最小 rank不同数量优先选少的
int score = tip.Count * 1000 + tip[0].GameNum;
if (score < bestScore) { bestScore = score; best = tip; }
}
return best;
}
else
{
// 领牌:优先高效清牌
var best = tips[0];
int bestScore = int.MinValue;
foreach (var tip in tips)
{
// 优先多牌;同数量出最小 rank保留大牌控制权
int score = tip.Count * 100 - tip[0].GameNum;
if (score > bestScore) { bestScore = score; best = tip; }
}
return best;
}
return best;
}
// 静态牌池52张牌只创建一次

Binary file not shown.

Binary file not shown.