新增4个番型识别器: - 四归一: 4张同牌跨面子使用(不在单个kezi/kong) — 纯结构检测 - 圈风: 当前风圈对应的风刻(31=东/32=南/33=西/34=北) - 门风: 玩家座位风位对应的风刻(庄=东) - 单钓将: 自摸时摸到的牌完成了将牌的单吊对子 GameState扩展: - WindRound(当前风圈) + SeatWinds(玩家→风向) + IsSingleWait flag - Deal()分配座位风位(庄家=东,按逆时针) - StepTurn自摸时检测单钓将(摸牌←→对子匹配) 效果: 国标--check 6→0 (圈风/门风/单钓将/四归一全部消除) 所有玩法合规检查通过(南昌/血战仅base_fan=0标记警告)
97 lines
4.2 KiB
C#
97 lines
4.2 KiB
C#
namespace RuleEngine.Core;
|
|
|
|
using RuleEngine;
|
|
|
|
public class GameEvent
|
|
{
|
|
public string Type { get; set; } = "";
|
|
public string Player { get; set; } = "";
|
|
public string? Description { get; set; }
|
|
public int? Tile { get; set; }
|
|
}
|
|
|
|
public class MahjongGameState
|
|
{
|
|
public string Phase { get; set; } = "";
|
|
public Dictionary<string, List<int>> Hands { get; set; } = new();
|
|
public Dictionary<string, List<Meld>> Exposed { get; set; } = new();
|
|
public Dictionary<string, List<int>> FlowerPool { get; set; } = new();
|
|
public List<int> Deck { get; set; } = new();
|
|
public List<int> DiscardPool { get; set; } = new();
|
|
public Dictionary<string, List<int>> DiscardHistory { get; set; } = new();
|
|
public int? LastDiscard { get; set; }
|
|
public string? LastDiscardPlayer { get; set; }
|
|
public string CurrentPlayer { get; set; } = "";
|
|
public List<string> PlayerOrder { get; set; } = new();
|
|
public string Dealer { get; set; } = "";
|
|
public int RoundNumber { get; set; }
|
|
public Dictionary<string, int> Scores { get; set; } = new();
|
|
public List<string> HuPlayers { get; set; } = new();
|
|
public List<string> AlivePlayers { get; set; } = new();
|
|
public Dictionary<string, bool> FuFlags { get; set; } = new();
|
|
public bool IsDeckExhausted { get; set; }
|
|
public List<GameEvent> RecentEvents { get; set; } = new();
|
|
public int FlowerReplaced { get; set; }
|
|
public int? LastDrawnTile { get; set; }
|
|
public WildcardRegistry Wildcards { get; set; } = new();
|
|
|
|
// Event flags for event-based fan types (杠上开花/海底捞月/抢杠胡/天胡/地胡)
|
|
public bool IsKongDraw { get; set; } // 杠后摸牌 — set after kong draw, cleared on next discard
|
|
public bool IsLastTile { get; set; } // 海底 — set when deck has 1 tile before draw
|
|
public bool IsRobbedKong { get; set; } // 抢杠 — set when someone adds to pung (bu_kong)
|
|
public bool IsFirstTurn { get; set; } // 第一轮 — set for dealer after deal, non-dealer after first draw
|
|
public string? DealerOnFirstTurn { get; set; } // 庄家首轮(用于天胡判断)
|
|
public bool IsSingleWait { get; set; } // 单钓将: 自摸时摸到的牌完成了将牌的对子
|
|
|
|
// 圈风/门风: 用于圈风刻/门风刻番型
|
|
public int WindRound { get; set; } = 31; // 当前风圈: 31=东/32=南/33=西/34=北
|
|
public Dictionary<string, int> SeatWinds { get; set; } = new(); // 玩家→座位风位
|
|
|
|
public MahjongGameState Clone()
|
|
{
|
|
return new MahjongGameState
|
|
{
|
|
Phase = Phase,
|
|
Hands = Hands.ToDictionary(kv => kv.Key, kv => new List<int>(kv.Value)),
|
|
Exposed = Exposed.ToDictionary(kv => kv.Key, kv => kv.Value.Select(m => m.Clone()).ToList()),
|
|
FlowerPool = FlowerPool.ToDictionary(kv => kv.Key, kv => new List<int>(kv.Value)),
|
|
Deck = new List<int>(Deck),
|
|
DiscardPool = new List<int>(DiscardPool),
|
|
LastDiscard = LastDiscard,
|
|
LastDiscardPlayer = LastDiscardPlayer,
|
|
CurrentPlayer = CurrentPlayer,
|
|
PlayerOrder = new List<string>(PlayerOrder),
|
|
Dealer = Dealer,
|
|
RoundNumber = RoundNumber,
|
|
Scores = new Dictionary<string, int>(Scores),
|
|
HuPlayers = new List<string>(HuPlayers),
|
|
AlivePlayers = new List<string>(AlivePlayers),
|
|
FuFlags = new Dictionary<string, bool>(FuFlags),
|
|
IsDeckExhausted = IsDeckExhausted,
|
|
RecentEvents = new List<GameEvent>(RecentEvents),
|
|
FlowerReplaced = FlowerReplaced,
|
|
LastDrawnTile = LastDrawnTile,
|
|
Wildcards = Wildcards.Clone(),
|
|
IsKongDraw = IsKongDraw,
|
|
IsLastTile = IsLastTile,
|
|
IsRobbedKong = IsRobbedKong,
|
|
IsFirstTurn = IsFirstTurn,
|
|
DealerOnFirstTurn = DealerOnFirstTurn,
|
|
IsSingleWait = IsSingleWait,
|
|
WindRound = WindRound,
|
|
SeatWinds = new Dictionary<string, int>(SeatWinds)
|
|
};
|
|
}
|
|
|
|
public void AddEvent(string type, string player, int? tile = null, string? desc = null)
|
|
{
|
|
RecentEvents.Add(new GameEvent
|
|
{
|
|
Type = type,
|
|
Player = player,
|
|
Tile = tile,
|
|
Description = desc
|
|
});
|
|
}
|
|
}
|