feat: 计分倍数可配置化 (自摸/点炮/庄家)

ScoringDslConfig + ScoringConfig 新增:
- self_draw_multiplier (默认1): 自摸每家付 baseFan × N
- discard_win_multiplier (默认3): 放炮者付 baseFan × N
- dealer_multiplier (默认1): 庄家输赢 × N

ScoreEngine.Settle:
- 硬编码 *3 → 读 DiscardWinMultiplier
- 庄家倍数: dealerWon→所有人付倍数; 闲家赢→仅庄家付倍数

南昌麻将 DSL: self_draw=1, discard_win=1, dealer=2

58 tests pass, 5 variants all 0 errors
This commit is contained in:
xiaoou
2026-07-04 21:12:45 +08:00
parent ae9c4c4142
commit 248af7642d
4 changed files with 28 additions and 6 deletions

View File

@ -234,6 +234,9 @@ public class ScoringDslConfig
public string Mode { get; set; } = "fan_table";
public int MaxCap { get; set; } = int.MaxValue;
public List<PreHookConfig> PreHooks { get; set; } = new();
public int SelfDrawMultiplier { get; set; } = 1; // 自摸: 每家付 baseFan × N
public int DiscardWinMultiplier { get; set; } = 3; // 点炮: 放炮者付 baseFan × N (默认3=传统包赔)
public int DealerMultiplier { get; set; } = 1; // 庄家: 庄家输赢 × N
}
public class FlowerDslConfig
{

View File

@ -51,7 +51,10 @@ public class MahjongRoom
JiHuSelfDrawOnly = _rules.WinRule?.JiHuSelfDrawOnly ?? false,
FlowerNormalScore = _rules.FlowerRules?.Scoring?.Normal ?? 0,
FlowerMatchScore = _rules.FlowerRules?.Scoring?.Matching?.Value ?? 0,
FlowerMapping = _rules.FlowerRules?.Scoring?.Matching?.Mapping
FlowerMapping = _rules.FlowerRules?.Scoring?.Matching?.Mapping,
SelfDrawMultiplier = _rules.Scoring.SelfDrawMultiplier,
DiscardWinMultiplier = _rules.Scoring.DiscardWinMultiplier,
DealerMultiplier = _rules.Scoring.DealerMultiplier
}, _solver, fanConfig);
// Init state

View File

@ -13,6 +13,9 @@ public class ScoringConfig
public int FlowerNormalScore { get; set; } = 0;
public int FlowerMatchScore { get; set; } = 0;
public Dictionary<int, List<string>>? FlowerMapping { get; set; }
public int SelfDrawMultiplier { get; set; } = 1;
public int DiscardWinMultiplier { get; set; } = 3;
public int DealerMultiplier { get; set; } = 1;
}
public class MahjongScoreEngine
@ -71,19 +74,29 @@ public class MahjongScoreEngine
if (isSelfDraw)
{
int perPlayerPay = baseFan * _config.SelfDrawMultiplier;
bool dealerWon = (state.Dealer == winner);
foreach (var p in state.AlivePlayers)
{
if (p == winner) continue;
state.Scores[p] = (state.Scores.GetValueOrDefault(p) - baseFan);
state.Scores[winner] = (state.Scores.GetValueOrDefault(winner) + baseFan);
// 庄家赢→每家付倍数; 闲家赢→仅庄家付倍数
int mul = (dealerWon || p == state.Dealer) ? _config.DealerMultiplier : 1;
int pay = perPlayerPay * mul;
state.Scores[p] = (state.Scores.GetValueOrDefault(p) - pay);
state.Scores[winner] = (state.Scores.GetValueOrDefault(winner) + pay);
}
}
else
{
if (state.LastDiscardPlayer != null && state.LastDiscardPlayer != winner)
{
state.Scores[state.LastDiscardPlayer] = (state.Scores.GetValueOrDefault(state.LastDiscardPlayer) - baseFan * 3);
state.Scores[winner] = (state.Scores.GetValueOrDefault(winner) + baseFan * 3);
int discardPay = baseFan * _config.DiscardWinMultiplier;
// 庄家赢了或庄家放炮→加倍
bool dealerInvolved = (state.Dealer == winner || state.Dealer == state.LastDiscardPlayer);
int mul = dealerInvolved ? _config.DealerMultiplier : 1;
int pay = discardPay * mul;
state.Scores[state.LastDiscardPlayer] = (state.Scores.GetValueOrDefault(state.LastDiscardPlayer) - pay);
state.Scores[winner] = (state.Scores.GetValueOrDefault(winner) + pay);
}
}
}

View File

@ -97,4 +97,7 @@ phases:
scoring:
mode: fan_table
max_cap: 999
max_cap: 999
self_draw_multiplier: 1 # 自摸每家付1倍
discard_win_multiplier: 1 # 放炮付1倍(南昌麻将放炮不翻倍)
dealer_multiplier: 2 # 庄家输赢×2