fix: 圈风/门风/单钓将/四归一加_fanConfig守卫 + 精牌显示 + TileName修复

问题1: 圈风/门风/单钓将/四归一在南昌武汉等非Guobiao玩法中误生成
  修复: 4个新识别器加 _fanConfig.ContainsKey() 守卫,只有配了才生成
  Guobiao DSL 补充4个番型定义(圈风2/门风2/四归一2/单钓将1)

问题2: 翻牌定精信息未在demo显示
  修复: RenderDeal() 显示精牌(翻牌+正精+副精)

问题3: TileName?{tile}→ToStringCore(tile)
  修复: 带🀫前缀的数字牌显示 '🀫3万' 而非 '🀫?21'
This commit is contained in:
xiaoou
2026-07-04 23:42:42 +08:00
parent 3f91d7c3f8
commit d734c0a384
4 changed files with 32 additions and 15 deletions

View File

@ -237,6 +237,16 @@ void RenderDeal(MahjongRoom room)
foreach (var p in state.PlayerOrder)
Console.WriteLine($" {p}{(p == state.Dealer ? " ()" : "")}: {state.Hands[p].Count}张");
Console.WriteLine($" 牌墙: {state.Deck.Count} 张");
// Show wildcard info if configured
var wc = state.Wildcards;
if (wc.ConfiguredWildcards.Count > 0)
{
var wcStr = string.Join(" ", wc.ConfiguredWildcards.Select(t => MahjongTile.ToString(t, wc)));
if (wc.RevealedTile.HasValue)
Console.WriteLine($" 🀫 精: {wcStr} (翻牌: {MahjongTile.ToString(wc.RevealedTile.Value)})");
else
Console.WriteLine($" 🀫 宝牌: {wcStr}");
}
Console.WriteLine($" (手牌已洗牌,按 Enter 查看牌局)");
}

View File

@ -52,11 +52,11 @@ public static class MahjongTile
}
private static string TileName(int tile) => tile switch
{
31 => "东", 32 => "南", 33 => "西", 34 => "北",
35 => "中", 36 => "发", 37 => "白",
_ => $"?{tile}"
};
{
31 => "东", 32 => "南", 33 => "西", 34 => "北",
35 => "中", 36 => "发", 37 => "白",
_ => ToStringCore(tile)
};
/// 0=万, 1=条, 2=筒, 3=字, 4=花, 5=宝牌
public static int Suit(int tile)

View File

@ -776,22 +776,24 @@ public class MeldsSolver
fans.Add("地胡");
// 圈风刻: kezi matching current wind round (e.g. 东风圈→有东刻子)
if (keziTiles.Any(t => t == state.WindRound))
if (_fanConfig.ContainsKey("圈风") && keziTiles.Any(t => t == state.WindRound))
fans.Add("圈风");
// 门风刻: kezi matching player's seat wind (applies per-hand, identified by state owner)
foreach (var (player, seatWind) in state.SeatWinds)
if (keziTiles.Any(t => t == seatWind))
fans.Add("门风");
// 门风刻: kezi matching player's seat wind
if (_fanConfig.ContainsKey("门风") && state.SeatWinds.Values.Any(sw => keziTiles.Any(t => t == sw)))
fans.Add("门风");
// 单钓将: detected at self-draw time (single tile→pair completion)
if (state.IsSingleWait) fans.Add("单钓将");
if (_fanConfig.ContainsKey("单钓将") && state.IsSingleWait) fans.Add("单钓将");
}
// 四归一: 4 identical numbered tiles used across 2+ melds (not in a single kong)
var tileCounts = allTiles.Where(t => MahjongTile.IsNumbered(t)).GroupBy(t => t);
bool hasSiGuiYi = tileCounts.Any(g =>
g.Count() >= 4 && !melds.Any(m => m.Type == "kezi" && m.Tiles.Contains(g.Key) && m.Tiles.All(x => x == g.Key)));
if (hasSiGuiYi) fans.Add("四归一");
if (_fanConfig.ContainsKey("四归一"))
{
var tileCounts4 = allTiles.Where(t => MahjongTile.IsNumbered(t)).GroupBy(t => t);
bool hasSiGuiYi = tileCounts4.Any(g =>
g.Count() >= 4 && !melds.Any(m => m.Type == "kezi" && m.Tiles.Contains(g.Key) && m.Tiles.All(x => x == g.Key)));
if (hasSiGuiYi) fans.Add("四归一");
}
// 鸡胡: base-level fan when no other fan with positive value exists.
// A fan has positive value if it's in _fanConfig with base_fan>0,

View File

@ -75,9 +75,14 @@ fan_types:
- { name: 断幺九, base_fan: 2, level: 2 }
- { name: 平和, base_fan: 2, level: 2 }
- { name: 门前清, base_fan: 2, level: 2 }
# 2番
- { name: 圈风, base_fan: 2, level: 2 }
- { name: 门风, base_fan: 2, level: 2 }
- { name: 四归一, base_fan: 2, level: 2 }
# 1番
- { name: 缺一门, base_fan: 1, level: 1 }
- { name: 无字, base_fan: 1, level: 1 }
- { name: 单钓将, base_fan: 1, level: 1 }
# 兜底
- { name: 鸡胡, base_fan: 1, level: 0 }