Files
2026-07-10 10:24:29 +08:00

148 lines
6.1 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}
}