RuleEngine 核心类: - MahjongTile.cs — int编码 (1-29万条筒, 31-37字, 41-48花, 50-59宝牌) - MeldsSolver.cs — 标准回溯 + wildcard缺口填充 + 七对/十三幺/全不靠 - PhaseMachine.cs — 回合机(摸打碰杠胡 + 优先级仲裁) - ScoreEngine.cs — 番型计分 + 互斥图 - DslLoader.cs — YAML DSL加载 + 能力检查 4个DSL: 四川血战/广东鸡平胡/国标麻将/武汉麻将 控制台Demo: 交互模式 + 自动模式(--auto) 测试: 33个测试用例, 32个通过
105 lines
3.4 KiB
C#
105 lines
3.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
|
|
|
|
public static int Encode(string suit, int rank)
|
|
{
|
|
return suit switch
|
|
{
|
|
"万" => rank,
|
|
"条" => 10 + rank,
|
|
"筒" => 20 + rank,
|
|
_ => throw new ArgumentException($"Unknown suit: {suit}")
|
|
};
|
|
}
|
|
|
|
public static string ToString(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}"
|
|
};
|
|
}
|
|
|
|
/// 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 IsWildcard(int tile) => tile >= 50 && tile <= 59;
|
|
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 const int WildcardBase = 50;
|
|
|
|
public static int[] AllTiles(bool includeHonors = false, bool includeFlowers = false, int wildcardCount = 0)
|
|
{
|
|
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;
|
|
}
|
|
}
|