Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user