Initial commit: Client Doc docs Server Tools

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ud18010
2026-07-10 10:24:29 +08:00
co-authored by Cursor
commit 7e35d8da31
3374 changed files with 680813 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
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);
}
}
}