using System.Collections.Generic; using Xunit; using XWorld.Framework; using RPS.Core; namespace XWorld.Server.Host.Tests { public class RpsLogicTests { private const float Choose = 5f, Reveal = 5f, Total = 60f; private static RpsState New() => new RpsLogic().CreateInitial( new RoomConfig { GameId = "rps", Version = 1, Seed = 1, PlayerCount = 2 }, new DeterministicRandom(1)); [Fact] public void Initial_IsChoosingRound1() { var s = New(); Assert.Equal(1, s.Round); Assert.Equal(RpsPhase.Choosing, s.Phase); Assert.Equal(Choice.None, s.Choices[0]); } [Fact] public void Choose_LocksSeatChoice_DuringChoosing() { var logic = new RpsLogic(); var s = New(); s = logic.Step(s, RpsInput.Choose(0, Choice.Rock), null).State; Assert.Equal(Choice.Rock, s.Choices[0]); // 二次出招不覆盖 s = logic.Step(s, RpsInput.Choose(0, Choice.Paper), null).State; Assert.Equal(Choice.Rock, s.Choices[0]); } [Fact] public void ChoosingTimeout_AutoRandomsMissing_AndResolves_RockBeatsScissors() { var logic = new RpsLogic(); var s = New(); s = logic.Step(s, RpsInput.Choose(0, Choice.Rock), null).State; s = logic.Step(s, RpsInput.Choose(1, Choice.Scissors), null).State; // 推进超过 5s 选择阶段 → 结算本回合 var r = logic.Step(s, RpsInput.Tick(Choose), new DeterministicRandom(1)); s = r.State; Assert.Equal(RpsPhase.Revealing, s.Phase); Assert.Equal(1, s.Scores[0]); // rock 胜 scissors Assert.Equal(0, s.Scores[1]); Assert.Contains(r.Events, e => e.Kind == RpsEventKind.RoundResolved && e.RoundWinner == 0); } [Fact] public void Tie_NoScore() { var logic = new RpsLogic(); var s = New(); s = logic.Step(s, RpsInput.Choose(0, Choice.Rock), null).State; s = logic.Step(s, RpsInput.Choose(1, Choice.Rock), null).State; s = logic.Step(s, RpsInput.Tick(Choose), new DeterministicRandom(1)).State; Assert.Equal(0, s.Scores[0]); Assert.Equal(0, s.Scores[1]); } [Fact] public void RevealTimeout_StartsNextRound() { var logic = new RpsLogic(); var s = New(); s = logic.Step(s, RpsInput.Choose(0, Choice.Rock), null).State; s = logic.Step(s, RpsInput.Choose(1, Choice.Scissors), null).State; s = logic.Step(s, RpsInput.Tick(Choose), new DeterministicRandom(1)).State; // → Revealing s = logic.Step(s, RpsInput.Tick(Reveal), new DeterministicRandom(1)).State; // → next round Assert.Equal(2, s.Round); Assert.Equal(RpsPhase.Choosing, s.Phase); Assert.Equal(Choice.None, s.Choices[0]); // 新回合清空 } [Fact] public void TotalTimeout_Finishes_WithWinnerByScore() { var logic = new RpsLogic(); var s = New(); // 座位0 赢一回合,然后把总时间推到 60s s = logic.Step(s, RpsInput.Choose(0, Choice.Rock), null).State; s = logic.Step(s, RpsInput.Choose(1, Choice.Scissors), null).State; s = logic.Step(s, RpsInput.Tick(Choose), new DeterministicRandom(1)).State; var r = logic.Step(s, RpsInput.Tick(Total), new DeterministicRandom(1)); // 越过总时长 s = r.State; Assert.Equal(RpsPhase.Finished, s.Phase); Assert.Equal(0, s.Winner); // 座位0 分高 Assert.Contains(r.Events, e => e.Kind == RpsEventKind.GameFinished && e.GameWinner == 0); } [Fact] public void Encode_Decode_RoundTrips() { var logic = new RpsLogic(); var s = New(); s.Round = 3; s.Phase = RpsPhase.Revealing; s.Scores[0] = 2; s.Scores[1] = 1; s.Choices[0] = Choice.Paper; s.Choices[1] = Choice.Rock; s.TotalElapsed = 12.5f; s.PhaseElapsed = 2.5f; s.IsAi[0] = false; s.IsAi[1] = true; s.Winner = 1; var back = logic.Decode(logic.Encode(s)); Assert.Equal(3, back.Round); Assert.Equal(RpsPhase.Revealing, back.Phase); Assert.Equal(2, back.Scores[0]); Assert.Equal(Choice.Paper, back.Choices[0]); Assert.Equal(Choice.Rock, back.Choices[1]); Assert.Equal(12.5f, back.TotalElapsed, 3); Assert.Equal(2.5f, back.PhaseElapsed, 3); Assert.False(back.IsAi[0]); Assert.True(back.IsAi[1]); Assert.Equal(1, back.Winner); } [Fact] public void SameSeed_AutoRandom_IsDeterministic() { var logic = new RpsLogic(); RpsState Run(ulong seed) { var s = New(); // 双方都不出招,靠超时自动随机 return logic.Step(s, RpsInput.Tick(Choose), new DeterministicRandom(seed)).State; } var a = Run(7); var b = Run(7); Assert.Equal(a.Choices[0], b.Choices[0]); Assert.Equal(a.Choices[1], b.Choices[1]); } [Fact] public void TotalTimeout_DuringRevealing_Finishes() { var logic = new RpsLogic(); var s = New(); s = logic.Step(s, RpsInput.Choose(0, Choice.Rock), null).State; s = logic.Step(s, RpsInput.Choose(1, Choice.Scissors), null).State; s = logic.Step(s, RpsInput.Tick(5f), new DeterministicRandom(1)).State; // → Revealing, seat0 scored Assert.Equal(RpsPhase.Revealing, s.Phase); s.TotalElapsed = 58f; // 逼近总时长 var r = logic.Step(s, RpsInput.Tick(2f), new DeterministicRandom(1)); // phase+2(<5 仍 Revealing),total→60 → Finish s = r.State; Assert.Equal(RpsPhase.Finished, s.Phase); Assert.Equal(0, s.Winner); Assert.Contains(r.Events, e => e.Kind == RpsEventKind.GameFinished); } } }