using FluentAssertions; using Game.TicTacToe; using OECS; using Xunit; namespace Game.TicTacToe.Tests; public class GameFlowTests { [Fact] public void NewGame_HasEmptyBoard() { var (world, _) = TestHelpers.SetupGame(); var emptyCount = 0; foreach (var _ in world.Select(new Query().Without())) emptyCount++; emptyCount.Should().Be(9); } [Fact] public void PlaceMark_ClaimsCell() { var (world, group) = TestHelpers.SetupGame(); world.Commands.Enqueue(new PlaceMarkCommand { Row = 0, Col = 0 }); group.RunLogical(); var markedCount = 0; foreach (var it in world.Select()) { markedCount++; it.Val1.Row.Should().Be(0); it.Val1.Col.Should().Be(0); it.Val2.Player.Should().Be(Player.X); } markedCount.Should().Be(1); } [Fact] public void PlaceMark_TogglesPlayer() { var (world, group) = TestHelpers.SetupGame(); world.Commands.Enqueue(new PlaceMarkCommand { Row = 0, Col = 0 }); // X group.RunLogical(); world.ReadSingleton().CurrentPlayer.Should().Be(Player.O); world.Commands.Enqueue(new PlaceMarkCommand { Row = 1, Col = 0 }); // O group.RunLogical(); world.ReadSingleton().CurrentPlayer.Should().Be(Player.X); } [Fact] public void PlaceMark_CannotOverwriteClaimedCell() { var (world, group) = TestHelpers.SetupGame(); world.Commands.Enqueue(new PlaceMarkCommand { Row = 0, Col = 0 }); // X group.RunLogical(); world.Commands.Enqueue(new PlaceMarkCommand { Row = 0, Col = 0 }); // O tries same cell group.RunLogical(); var marks = new List<(int Row, int Col, Player Player)>(); foreach (var it in world.Select()) marks.Add((it.Val1.Row, it.Val1.Col, it.Val2.Player)); marks.Should().ContainSingle() .Which.Should().Be((0, 0, Player.X)); } [Fact] public void XWins_Row() { var (world, group) = TestHelpers.SetupGame(); TestHelpers.PlayMoves(world, group, (0, 0), (1, 0), (0, 1), (1, 1), (0, 2)); world.ReadSingleton().Status.Should().Be(GameStatus.XWon); } [Fact] public void OWins_Column() { var (world, group) = TestHelpers.SetupGame(); TestHelpers.PlayMoves(world, group, (0, 0), (1, 0), (0, 1), (1, 1), (2, 2), (1, 2)); world.ReadSingleton().Status.Should().Be(GameStatus.OWon); } [Fact] public void XWins_Diagonal() { var (world, group) = TestHelpers.SetupGame(); TestHelpers.PlayMoves(world, group, (0, 0), (1, 0), (1, 1), (1, 2), (2, 2)); world.ReadSingleton().Status.Should().Be(GameStatus.XWon); } [Fact] public void Draw_AllCellsFilled() { var (world, group) = TestHelpers.SetupGame(); TestHelpers.PlayMoves(world, group, (0, 0), (0, 1), (0, 2), (1, 1), (1, 0), (1, 2), (2, 1), (2, 0), (2, 2)); world.ReadSingleton().Status.Should().Be(GameStatus.Draw); } [Fact] public void MovesAfterGameOver_AreIgnored() { var (world, group) = TestHelpers.SetupGame(); TestHelpers.PlayMoves(world, group, (0, 0), (1, 0), (0, 1), (1, 1), (0, 2)); world.ReadSingleton().Status.Should().Be(GameStatus.XWon); var moveCountBefore = world.ReadSingleton().MoveCount; world.Commands.Enqueue(new PlaceMarkCommand { Row = 2, Col = 2 }); group.RunLogical(); world.ReadSingleton().MoveCount.Should().Be(moveCountBefore); } [Fact] public void SaveLoad_RoundTrips() { var (world, group) = TestHelpers.SetupGame(); TestHelpers.PlayMoves(world, group, (0, 0), (1, 0)); using var stream = new MemoryStream(); WorldSerializer.Save(world, stream); stream.Position = 0; var world2 = new World(); WorldSerializer.Load(world2, stream); var state = world2.ReadSingleton(); state.CurrentPlayer.Should().Be(Player.X); state.MoveCount.Should().Be(2); var marks = new List<(int Row, int Col, Player Player)>(); foreach (var it in world2.Select()) marks.Add((it.Val1.Row, it.Val1.Col, it.Val2.Player)); marks.Should().BeEquivalentTo([(0, 0, Player.X), (1, 0, Player.O)]); } }