aaf66b921f
- server/ monorepo: Hokm.Engine (C# port of TS engine+AI, validated by sim), Hokm.Server (SignalR GameHub, in-memory matchmaking/rooms, server-side turn timers + bot fill + disconnect handling, per-seat state broadcast), Hokm.Sim - JWT dev auth (OTP 1234 + email); CORS for the Next client; /hub/game - NuGet restored from mirrors (Soroush Nexus + Liara); NuGetAudit off - README + .NET .gitignore; static class Engine renamed Rules (namespace clash) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
53 lines
1.7 KiB
C#
53 lines
1.7 KiB
C#
using Hokm.Engine;
|
|
|
|
// All-AI simulation to validate the C# engine port (mirrors scripts/sim.ts).
|
|
var rng = new Random(12345);
|
|
int N = 300;
|
|
int totalRounds = 0;
|
|
|
|
for (int i = 0; i < N; i++)
|
|
{
|
|
var (rounds, tricks) = PlayMatch(rng);
|
|
totalRounds += rounds;
|
|
if (tricks <= 0) throw new Exception("no tricks played");
|
|
}
|
|
|
|
Console.WriteLine($"OK: {N} matches completed. avg rounds/match = {(double)totalRounds / N:0.0}");
|
|
|
|
static (int rounds, int tricks) PlayMatch(Random rng)
|
|
{
|
|
var g = Rules.CreateInitial(new[] { "P0", "P1", "P2", "P3" }, 7);
|
|
Rules.SelectHakem(g, rng);
|
|
Rules.DealForTrump(g, rng);
|
|
|
|
int rounds = 0, tricks = 0, guard = 0;
|
|
while (g.Phase != Phase.MatchOver)
|
|
{
|
|
if (++guard > 200000) throw new Exception("loop guard tripped");
|
|
|
|
switch (g.Phase)
|
|
{
|
|
case Phase.ChoosingTrump:
|
|
Rules.ChooseTrump(g, Ai.ChooseTrump(g.Players[g.Hakem!.Value].Hand));
|
|
break;
|
|
case Phase.Playing:
|
|
int seat = g.Turn!.Value;
|
|
Rules.PlayCard(g, seat, Ai.ChooseCard(g, seat));
|
|
break;
|
|
case Phase.TrickComplete:
|
|
tricks++;
|
|
if (g.CurrentTrick.Count != 4) throw new Exception("trick not full");
|
|
Rules.AdvanceAfterTrick(g, 2);
|
|
break;
|
|
case Phase.RoundOver:
|
|
rounds++;
|
|
if (g.RoundTricks[0] + g.RoundTricks[1] > 13) throw new Exception("too many tricks");
|
|
Rules.StartNextRound(g, rng);
|
|
break;
|
|
default:
|
|
throw new Exception("unexpected phase: " + g.Phase);
|
|
}
|
|
}
|
|
return (rounds, tricks);
|
|
}
|