fix: 癞子=136张内的牌(红中),不是额外+4张 + 宝牌HUD显示
- MahjongTile.ConfigureWildcards: 支持固定wildcard(如红中35) - IsWildcard: 同时检查encoding 50+和配置的wildcard - ToString: wildcard牌显示🀫中/🀫发等 - Deck: 固定wildcard作为普通牌加入(红中在136内) - Deal: fixed类型wildcard不额外加牌 - HUD: 显示 '🀫 宝牌(癞子): 🀫中' - Wuhan DSL: 移除wildcardCount:4 - 测试: Wuhan牌库136张
This commit is contained in:
@ -55,8 +55,8 @@ public class MahjongRoomIntegrationTests
|
||||
room.Deal();
|
||||
|
||||
int totalTiles = room.State.Hands.Sum(h => h.Value.Count) + room.State.Deck.Count;
|
||||
// 136 tiles + 4 wildcards = 140
|
||||
Assert.Equal(140, totalTiles);
|
||||
// 136 tiles total (108 numbered + 28 honors, 红中 is wildcard but part of the 136)
|
||||
Assert.Equal(136, totalTiles);
|
||||
}
|
||||
|
||||
// === Turn flow ===
|
||||
|
||||
@ -33,6 +33,11 @@ public class HumanMahjongPlayer
|
||||
Console.WriteLine($"=== {Name} 的回合 ===");
|
||||
Console.WriteLine($" 牌墙: {state.Deck.Count}张 | 弃牌堆: {state.DiscardPool.Count}张");
|
||||
|
||||
// Show wildcard info
|
||||
var wcTiles = MahjongTile.ConfiguredWildcards;
|
||||
if (wcTiles.Count > 0)
|
||||
Console.WriteLine($" 🀫 宝牌(癞子): {string.Join(" ", wcTiles.Select(MahjongTile.ToString))}");
|
||||
|
||||
// Show exposed melds
|
||||
if (state.Exposed.TryGetValue(Name, out var exposed) && exposed.Count > 0)
|
||||
{
|
||||
|
||||
@ -10,12 +10,13 @@ public class MahjongDeck
|
||||
foreach (var t in allTiles)
|
||||
{
|
||||
int count = MahjongTile.IsFlower(t) ? 1 : 4;
|
||||
// Wildcards are handled by AllTiles already
|
||||
if (MahjongTile.IsWildcard(t)) continue;
|
||||
// Skip extra encoding-50+ wildcards (they're added separately below)
|
||||
// But include fixed wildcards (e.g. 红中) as normal tiles
|
||||
if (t >= MahjongTile.WildcardBase) continue;
|
||||
for (int i = 0; i < count; i++)
|
||||
Tiles.Add(t);
|
||||
}
|
||||
// Add wildcards separately
|
||||
// Add extra wildcard tiles
|
||||
for (int i = 0; i < wildcardCount; i++)
|
||||
Tiles.Add(MahjongTile.WildcardBase + i);
|
||||
}
|
||||
|
||||
@ -6,7 +6,19 @@ public static class MahjongTile
|
||||
// Encoding: 万1-9=1-9, 条1-9=11-19, 筒1-9=21-29
|
||||
// 字: 东=31,南=32,西=33,北=34,中=35,发=36,白=37
|
||||
// 花: 春=41..菊=48
|
||||
// 宝牌: 50-59
|
||||
// 宝牌: 50-59 (extra wildcards added to deck)
|
||||
|
||||
private static readonly HashSet<int> _wildcards = new();
|
||||
|
||||
/// <summary>Configure which normal tiles (e.g. 红中=35) also act as wildcards.</summary>
|
||||
public static void ConfigureWildcards(IEnumerable<int> tiles)
|
||||
{
|
||||
_wildcards.Clear();
|
||||
foreach (var t in tiles) _wildcards.Add(t);
|
||||
}
|
||||
|
||||
/// <summary>Get the set of normal tiles configured as wildcards.</summary>
|
||||
public static IReadOnlySet<int> ConfiguredWildcards => _wildcards;
|
||||
|
||||
public static int Encode(string suit, int rank)
|
||||
{
|
||||
@ -24,6 +36,7 @@ public static class MahjongTile
|
||||
if (tile >= 1 && tile <= 9) return $"{tile}万";
|
||||
if (tile >= 11 && tile <= 19) return $"{tile - 10}条";
|
||||
if (tile >= 21 && tile <= 29) return $"{tile - 20}筒";
|
||||
if (_wildcards.Contains(tile)) return $"🀫{TileName(tile)}";
|
||||
return tile switch
|
||||
{
|
||||
31 => "东", 32 => "南", 33 => "西", 34 => "北",
|
||||
@ -35,6 +48,13 @@ public static class MahjongTile
|
||||
};
|
||||
}
|
||||
|
||||
private static string TileName(int tile) => tile switch
|
||||
{
|
||||
31 => "东", 32 => "南", 33 => "西", 34 => "北",
|
||||
35 => "中", 36 => "发", 37 => "白",
|
||||
_ => $"?{tile}"
|
||||
};
|
||||
|
||||
/// 0=万, 1=条, 2=筒, 3=字, 4=花, 5=宝牌
|
||||
public static int Suit(int tile)
|
||||
{
|
||||
@ -64,7 +84,7 @@ public static class MahjongTile
|
||||
|
||||
public static bool IsHonor(int tile) => tile >= 31 && tile <= 37;
|
||||
public static bool IsFlower(int tile) => tile >= 41 && tile <= 48;
|
||||
public static bool IsWildcard(int tile) => tile >= 50 && tile <= 59;
|
||||
public static bool IsWildcard(int tile) => tile >= 50 && tile <= 59 || _wildcards.Contains(tile);
|
||||
public static bool IsNumbered(int tile) => tile >= 1 && tile <= 29;
|
||||
public static bool IsTerminal(int tile)
|
||||
{
|
||||
|
||||
@ -438,7 +438,25 @@ public class MahjongRoom
|
||||
int perPlayer = _rules.Deal.CardsPerPlayer;
|
||||
bool includeFlowers = _rules.Deck.IncludeFlowers;
|
||||
bool includeHonors = _rules.Deck.IncludeHonors;
|
||||
int wildcardCount = _rules.WildcardRules != null ? _rules.Deck.WildcardCount : 0;
|
||||
|
||||
// Configure fixed wildcards (e.g. Wuhan 红中)
|
||||
int wildcardCount = 0;
|
||||
if (_rules.WildcardRules != null)
|
||||
{
|
||||
if (_rules.WildcardRules.Type == "fixed" && _rules.WildcardRules.Tiles != null)
|
||||
{
|
||||
// Fixed wildcards: specified tiles ARE the wildcards, not extra tiles
|
||||
var wcTiles = _rules.WildcardRules.Tiles
|
||||
.Select(t => t switch { "红中" => 35, "发财" => 36, "白板" => 37, _ => -1 })
|
||||
.Where(t => t > 0).ToArray();
|
||||
MahjongTile.ConfigureWildcards(wcTiles);
|
||||
wildcardCount = 0; // No extra tiles
|
||||
}
|
||||
else
|
||||
{
|
||||
wildcardCount = _rules.Deck.WildcardCount;
|
||||
}
|
||||
}
|
||||
|
||||
var deck = new MahjongDeck(includeHonors, includeFlowers, wildcardCount);
|
||||
deck.Shuffle(_rng);
|
||||
@ -479,8 +497,6 @@ public class MahjongRoom
|
||||
|
||||
private void CheckTing()
|
||||
{
|
||||
bool hasWildcard = _rules.WildcardRules != null;
|
||||
int wildcardCount = hasWildcard ? _rules.Deck.WildcardCount : 0;
|
||||
bool require258 = _rules.WinCondition?.PairMustBe258 ?? false;
|
||||
|
||||
// All possible tiles that could be drawn (numbered 1-9 across 3 suits + 7 honors)
|
||||
@ -500,8 +516,10 @@ public class MahjongRoom
|
||||
int alreadyHave = hand.Count(t => t == tile);
|
||||
if (alreadyHave >= 4) continue; // can't draw more of this tile
|
||||
|
||||
var handWithTile = new List<int>(hand) { tile };
|
||||
int wc = CountWildcardsInHand(handWithTile);
|
||||
var r = _solver.CheckWin(hand, newTile: tile,
|
||||
wildcardCount: wildcardCount, require258Pair: require258);
|
||||
wildcardCount: wc, require258Pair: require258);
|
||||
if (r != null && r.IsWin) { ting = true; break; }
|
||||
}
|
||||
|
||||
|
||||
@ -17,8 +17,6 @@ deck:
|
||||
generator: mahjong
|
||||
include_honors: true
|
||||
include_flowers: false
|
||||
wildcard_tile: 红中
|
||||
wildcard_count: 4
|
||||
total: 136
|
||||
|
||||
deal:
|
||||
|
||||
Reference in New Issue
Block a user