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个通过
223 lines
7.2 KiB
C#
223 lines
7.2 KiB
C#
using RuleEngine.Core;
|
|
using RuleEngine.Patterns;
|
|
|
|
namespace RuleEngine.Tests;
|
|
|
|
public class MahjongTileTests
|
|
{
|
|
[Fact]
|
|
public void Encode_万1_Returns1() => Assert.Equal(1, MahjongTile.Encode("万", 1));
|
|
[Fact]
|
|
public void Encode_条9_Returns19() => Assert.Equal(19, MahjongTile.Encode("条", 9));
|
|
[Fact]
|
|
public void Encode_筒3_Returns23() => Assert.Equal(23, MahjongTile.Encode("筒", 3));
|
|
[Fact]
|
|
public void Suit_万1_Returns0() => Assert.Equal(0, MahjongTile.Suit(1));
|
|
[Fact]
|
|
public void Suit_条19_Returns1() => Assert.Equal(1, MahjongTile.Suit(19));
|
|
[Fact]
|
|
public void Rank_万9_Returns9() => Assert.Equal(9, MahjongTile.Rank(9));
|
|
[Fact]
|
|
public void Rank_条11_Returns1() => Assert.Equal(1, MahjongTile.Rank(11));
|
|
[Fact]
|
|
public void ToString_各类型正确()
|
|
{
|
|
Assert.Equal("5万", MahjongTile.ToString(5));
|
|
Assert.Equal("8条", MahjongTile.ToString(18));
|
|
Assert.Equal("3筒", MahjongTile.ToString(23));
|
|
Assert.Equal("东", MahjongTile.ToString(31));
|
|
Assert.Equal("春", MahjongTile.ToString(41));
|
|
Assert.Equal("🃏", MahjongTile.ToString(50));
|
|
}
|
|
[Fact]
|
|
public void AllTiles_108张() => Assert.Equal(27, MahjongTile.AllTiles().Length);
|
|
[Fact]
|
|
public void AllTiles_含字牌_34种() => Assert.Equal(34, MahjongTile.AllTiles(includeHonors: true).Length);
|
|
[Fact]
|
|
public void AllTiles_含花牌_35种() => Assert.Equal(35, MahjongTile.AllTiles(includeFlowers: true).Length);
|
|
[Fact]
|
|
public void IsWildcard_50_True() => Assert.True(MahjongTile.IsWildcard(50));
|
|
[Fact]
|
|
public void IsWildcard_普通牌_False() => Assert.False(MahjongTile.IsWildcard(1));
|
|
}
|
|
|
|
public class DeckTests
|
|
{
|
|
[Fact]
|
|
public void 四川牌库_108张()
|
|
{
|
|
var deck = new MahjongDeck(includeHonors: false, includeFlowers: false);
|
|
Assert.Equal(108, deck.Tiles.Count);
|
|
}
|
|
[Fact]
|
|
public void 广东牌库_136张()
|
|
{
|
|
// 108 numbered + 28 honors = 136 (no flowers)
|
|
var deck = new MahjongDeck(includeHonors: true, includeFlowers: false);
|
|
Assert.Equal(136, deck.Tiles.Count);
|
|
}
|
|
[Fact]
|
|
public void 国标牌库_144张()
|
|
{
|
|
// 108 numbered + 28 honors + 8 flowers = 144
|
|
var deck = new MahjongDeck(includeHonors: true, includeFlowers: true);
|
|
Assert.Equal(144, deck.Tiles.Count);
|
|
}
|
|
[Fact]
|
|
public void 每张牌4份()
|
|
{
|
|
var deck = new MahjongDeck();
|
|
var groups = deck.Tiles.Where(t => !MahjongTile.IsFlower(t)).GroupBy(t => t);
|
|
Assert.All(groups, g => Assert.Equal(4, g.Count()));
|
|
}
|
|
[Fact]
|
|
public void 抽牌_减少计数()
|
|
{
|
|
var deck = new MahjongDeck();
|
|
int before = deck.Count;
|
|
deck.Draw();
|
|
Assert.Equal(before - 1, deck.Count);
|
|
}
|
|
}
|
|
|
|
public class MeldsSolverTests
|
|
{
|
|
private readonly MeldsSolver _solver = new(new Dictionary<string, FanConfig>());
|
|
|
|
// === 标准胡牌 ===
|
|
[Fact]
|
|
public void 标准胡_4面子1对_ShouldWin()
|
|
{
|
|
// 123万 456万 789万 111条 99条
|
|
var hand = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 11, 11, 19, 19 };
|
|
var result = _solver.CheckWin(hand);
|
|
Assert.True(result.IsWin);
|
|
}
|
|
|
|
[Fact]
|
|
public void 标准胡_碰碰胡_ShouldWin()
|
|
{
|
|
// 111万 333万 555万 111条 99条
|
|
var hand = new List<int> { 1, 1, 1, 3, 3, 3, 5, 5, 5, 11, 11, 11, 19, 19 };
|
|
var result = _solver.CheckWin(hand);
|
|
Assert.True(result.IsWin);
|
|
}
|
|
|
|
[Fact]
|
|
public void 不能胡_缺面子_ShouldNotWin()
|
|
{
|
|
// All sequential but wrong structure: 123 456 78? can't form 4 melds
|
|
var hand = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16 };
|
|
var result = _solver.CheckWin(hand);
|
|
Assert.False(result.IsWin);
|
|
}
|
|
|
|
[Fact]
|
|
public void 不能胡_14张全不同_ShouldNotWin()
|
|
{
|
|
var hand = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 21, 15, 19 };
|
|
var result = _solver.CheckWin(hand);
|
|
Assert.False(result.IsWin);
|
|
}
|
|
|
|
// === 七对 ===
|
|
[Fact]
|
|
public void 七对_ShouldWin()
|
|
{
|
|
var hand = new List<int> { 1, 1, 3, 3, 5, 5, 7, 7, 11, 11, 13, 13, 21, 21 };
|
|
var result = _solver.CheckWin(hand);
|
|
Assert.True(result.IsWin);
|
|
}
|
|
|
|
[Fact]
|
|
public void 七对_缺一对_ShouldNotWin()
|
|
{
|
|
var hand = new List<int> { 1, 1, 3, 3, 5, 5, 7, 7, 11, 11, 13, 13, 21, 5 };
|
|
var result = _solver.CheckWin(hand);
|
|
Assert.False(result.IsWin);
|
|
}
|
|
|
|
// === 十三幺 ===
|
|
[Fact]
|
|
public void 十三幺_ShouldWin()
|
|
{
|
|
var hand = new List<int> { 1, 9, 11, 19, 21, 29, 31, 32, 33, 34, 35, 36, 37, 1 };
|
|
var result = _solver.CheckWin(hand);
|
|
Assert.True(result.IsWin);
|
|
}
|
|
|
|
[Fact]
|
|
public void 十三幺_缺字牌_ShouldNotWin()
|
|
{
|
|
var hand = new List<int> { 1, 9, 11, 19, 21, 29, 2, 3, 4, 5, 6, 7, 8, 1 };
|
|
var result = _solver.CheckWin(hand);
|
|
Assert.False(result.IsWin);
|
|
}
|
|
|
|
// === 番型识别 ===
|
|
[Fact]
|
|
public void 清一色_ShouldIdentify()
|
|
{
|
|
// 全万: 111万 234万 567万 789万 99万
|
|
var hand = new List<int> { 1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 9, 9 };
|
|
var result = _solver.CheckWin(hand);
|
|
Assert.True(result.IsWin);
|
|
var fans = _solver.IdentifyFans(result!);
|
|
Assert.Contains("清一色", fans);
|
|
}
|
|
|
|
[Fact]
|
|
public void 对对胡_ShouldIdentify()
|
|
{
|
|
var hand = new List<int> { 1, 1, 1, 3, 3, 3, 5, 5, 5, 11, 11, 11, 19, 19 };
|
|
var result = _solver.CheckWin(hand);
|
|
var fans = _solver.IdentifyFans(result!);
|
|
Assert.Contains("对对胡", fans);
|
|
}
|
|
|
|
// === Wildcard ===
|
|
[Fact]
|
|
public void 鬼牌_1wildcard补刻子_ShouldWin()
|
|
{
|
|
// 111万 234万 567万 11条 99条 + 1 wildcard to complete 111条kezi
|
|
var hand = new List<int> { 1, 1, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 19, 19 };
|
|
var result = _solver.CheckWin(hand, wildcardCount: 1);
|
|
Assert.True(result.IsWin);
|
|
}
|
|
|
|
[Fact]
|
|
public void 鬼牌_2wildcard做对_ShouldWin()
|
|
{
|
|
// 111万 234万 555万 888万 + 2 wildcards as the pair
|
|
var hand = new List<int> { 1, 1, 1, 2, 3, 4, 5, 5, 5, 8, 8, 8 };
|
|
var result = _solver.CheckWin(hand, wildcardCount: 2);
|
|
Assert.True(result.IsWin);
|
|
}
|
|
|
|
// === 258将 ===
|
|
[Fact]
|
|
public void 武汉麻将_258将_2万_ShouldWin()
|
|
{
|
|
var hand = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 11, 11, 2, 2 };
|
|
var result = _solver.CheckWin(hand, require258Pair: true);
|
|
Assert.True(result.IsWin);
|
|
}
|
|
|
|
[Fact]
|
|
public void 武汉麻将_258将_7万将_ShouldFail()
|
|
{
|
|
var hand = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 11, 11, 7, 7 };
|
|
var result = _solver.CheckWin(hand, require258Pair: true);
|
|
Assert.False(result.IsWin);
|
|
}
|
|
|
|
// === 全不靠 ===
|
|
[Fact]
|
|
public void 全不靠_147万_258条_369筒_ShouldWin()
|
|
{
|
|
var hand = new List<int> { 1, 4, 7, 12, 15, 18, 23, 26, 29, 31, 32, 33, 34, 35 };
|
|
// Should be close enough for AllOrphans
|
|
// Note: This test may need adjustment based on exact implementation
|
|
}
|
|
}
|