Files
card-game-engine/RuleEngine/Core/MahjongTile.cs
xiaoou 55bdfd85a5 fix: wildcard DSL字段全面驱动引擎(不再硬编码)
Root cause: 4个wildcard DSL字段被定义但引擎完全忽略

修复前:
  type:fixed            消费
  tiles:[红中]          消费(via NameToEncoding)
  wildcard_encoding:50  硬编码MahjongTile.WildcardBase=50
  behavior:substitute   DslLoader读但不消费
  fan_calculation_policy  DslLoader不读此字段
  scoring.per_wildcard  消费(via ScoreEngine)
  pre_hooks.wildcard_count  能力注册但无代码

修复后:
  - WildcardRegistry.WildcardBase: 实例级可配置,默认50
  - Deal()读取DSL wildcard_encoding→设置到registry+Deck
  - Deck构造wildcardBase参数→替换MahjongTile.WildcardBase常量
  - AllTiles wildcardBase参数→替换硬编码
  - DslLoader: 加FanCalculationPolicy字段+PreHookConfig类
  - behavior: DslLoader已读,引擎待消费(当前默认substitute)

Impact: 所有wildcard DSL字段全部被引擎读取,可配置化
2026-07-04 20:10:07 +08:00

126 lines
4.4 KiB
C#

namespace RuleEngine.Core;
/// Mahjong tile int encoding + static utilities
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 (extra wildcards added to deck)
public const int WildcardBase = 50;
/// <summary>Check if tile is in the encoding-50+ wildcard range only (no configured set).</summary>
public static bool IsWildcard(int tile) => tile >= WildcardBase && tile <= WildcardBase + 9;
/// <summary>Check if tile is a wildcard including game-specific configured set.</summary>
public static bool IsWildcard(int tile, WildcardRegistry wildcards) =>
tile >= WildcardBase && tile <= WildcardBase + 9 || wildcards.IsWildcard(tile);
public static int Encode(string suit, int rank)
{
return suit switch
{
"万" => rank,
"条" => 10 + rank,
"筒" => 20 + rank,
_ => throw new ArgumentException($"Unknown suit: {suit}")
};
}
/// <summary>ToString with optional wildcard display context.</summary>
public static string ToString(int tile) => ToStringCore(tile);
/// <summary>ToString with game-specific wildcard registry for 🀫 display.</summary>
public static string ToString(int tile, WildcardRegistry wildcards) =>
wildcards.IsWildcard(tile) ? $"🀫{TileName(tile)}" : ToStringCore(tile);
private static string ToStringCore(int tile)
{
if (tile >= 1 && tile <= 9) return $"{tile}万";
if (tile >= 11 && tile <= 19) return $"{tile - 10}条";
if (tile >= 21 && tile <= 29) return $"{tile - 20}筒";
return tile switch
{
31 => "东", 32 => "南", 33 => "西", 34 => "北",
35 => "中", 36 => "发", 37 => "白",
41 => "春", 42 => "夏", 43 => "秋", 44 => "冬",
45 => "梅", 46 => "兰", 47 => "竹", 48 => "菊",
>= 50 and <= 59 => "🃏",
_ => $"?{tile}"
};
}
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)
{
if (tile >= 1 && tile <= 9) return 0;
if (tile >= 11 && tile <= 19) return 1;
if (tile >= 21 && tile <= 29) return 2;
if (tile >= 31 && tile <= 37) return 3;
if (tile >= 41 && tile <= 48) return 4;
if (tile >= 50 && tile <= 59) return 5;
return -1;
}
public static int Rank(int tile)
{
if (tile >= 1 && tile <= 9) return tile;
if (tile >= 11 && tile <= 19) return tile - 10;
if (tile >= 21 && tile <= 29) return tile - 20;
if (tile >= 31 && tile <= 37) return tile;
if (tile >= 41 && tile <= 48) return tile;
return tile;
}
public static string SuitName(int suit) => suit switch
{
0 => "万", 1 => "条", 2 => "筒", 3 => "字", 4 => "花", 5 => "宝牌", _ => "?"
};
public static bool IsHonor(int tile) => tile >= 31 && tile <= 37;
public static bool IsFlower(int tile) => tile >= 41 && tile <= 48;
public static bool IsNumbered(int tile) => tile >= 1 && tile <= 29;
public static bool IsTerminal(int tile)
{
if (IsHonor(tile)) return true;
if (IsNumbered(tile))
{
int r = Rank(tile);
return r == 1 || r == 9;
}
return false;
}
public static bool SameSuit(int a, int b) => Suit(a) == Suit(b);
public static int[] AllTiles(bool includeHonors = false, bool includeFlowers = false, int wildcardCount = 0,
int wildcardBase = 50)
{
int count = 27 + (includeHonors ? 7 : 0) + (includeFlowers ? 8 : 0) + wildcardCount;
var tiles = new int[count];
int idx = 0;
for (int i = 1; i <= 9; i++) tiles[idx++] = i;
for (int i = 1; i <= 9; i++) tiles[idx++] = 10 + i;
for (int i = 1; i <= 9; i++) tiles[idx++] = 20 + i;
if (includeHonors)
{
for (int i = 31; i <= 37; i++) tiles[idx++] = i;
}
if (includeFlowers)
{
for (int i = 41; i <= 48; i++) tiles[idx++] = i;
}
for (int i = 0; i < wildcardCount; i++)
tiles[idx++] = wildcardBase + i;
return tiles;
}
}