using Chessistics.Engine.Model; using Chessistics.Engine.Rules; using Chessistics.Tests.Helpers; using Xunit; namespace Chessistics.Tests.Rules; public class MoveValidatorTests { private BoardState EmptyBoard(int size = 5) => new BoardBuilder(size, size) .WithProduction(0, 0, "P", CargoType.Wood, 2) .WithDemand(size - 1, 0, "D", CargoType.Wood, 1, 99) .WithStock(PieceKind.Rook, 10) .WithStock(PieceKind.Bishop, 10) .WithStock(PieceKind.Knight, 10) .BuildState(); [Fact] public void Rook_CanMove_1or2_Orthogonal() { var board = EmptyBoard(); var moves = MoveValidator.GetLegalEndCells(PieceKind.Rook, new Coords(2, 2), board); // 4 directions x 2 distances = 8 cells Assert.Equal(8, moves.Count); Assert.Contains(new Coords(2, 3), moves); // up 1 Assert.Contains(new Coords(2, 4), moves); // up 2 Assert.Contains(new Coords(2, 1), moves); // down 1 // (2,0) is the production cell - it's Empty type, so should be reachable Assert.Contains(new Coords(3, 2), moves); // right 1 Assert.Contains(new Coords(4, 2), moves); // right 2 Assert.Contains(new Coords(1, 2), moves); // left 1 } [Fact] public void Rook_BlockedByWall() { var board = new BoardBuilder(5, 5) .WithProduction(0, 0, "P", CargoType.Wood, 2) .WithDemand(4, 0, "D", CargoType.Wood, 1, 99) .WithWall(1, 0) .WithStock(PieceKind.Rook, 5) .BuildState(); // Rook at (0, 0) is on a Production cell - start shouldn't be a wall // Let's place rook at (0, 1) looking right - wall at (1,0) doesn't block vertical // Test: rook at (0,0) looking right: wall at (1,0) blocks var moves = MoveValidator.GetLegalEndCells(PieceKind.Rook, new Coords(0, 1), board); // Right direction: (1,1) and (2,1) are fine (wall is at 1,0 not 1,1) Assert.Contains(new Coords(1, 1), moves); Assert.Contains(new Coords(2, 1), moves); // Rook at (2,0) going left: (1,0) is wall, blocked var moves2 = MoveValidator.GetLegalEndCells(PieceKind.Rook, new Coords(2, 0), board); Assert.DoesNotContain(new Coords(1, 0), moves2); Assert.DoesNotContain(new Coords(0, 0), moves2); // blocked by wall on the way } [Fact] public void Rook_NotBlockedByPiece_InEdit() { var board = EmptyBoard(); // Place a piece at (2,3) — in edit mode, pieces do NOT block sliding board.Pieces.Add(new PieceState(99, PieceKind.Rook, new Coords(2, 3), new Coords(2, 4), 0)); var moves = MoveValidator.GetLegalEndCells(PieceKind.Rook, new Coords(2, 2), board); // Pieces share relay points — collisions are detected at runtime Assert.Contains(new Coords(2, 3), moves); Assert.Contains(new Coords(2, 4), moves); } [Fact] public void Rook_CannotLandOnWall() { var board = new BoardBuilder(5, 5) .WithProduction(0, 0, "P", CargoType.Wood, 2) .WithDemand(4, 0, "D", CargoType.Wood, 1, 99) .WithWall(3, 2) .WithStock(PieceKind.Rook, 5) .BuildState(); var moves = MoveValidator.GetLegalEndCells(PieceKind.Rook, new Coords(2, 2), board); Assert.DoesNotContain(new Coords(3, 2), moves); Assert.DoesNotContain(new Coords(4, 2), moves); // blocked by wall in path } [Fact] public void Bishop_DiagonalOnly() { var board = EmptyBoard(); var moves = MoveValidator.GetLegalEndCells(PieceKind.Bishop, new Coords(2, 2), board); // 4 diagonal directions x 2 distances = 8 cells Assert.Equal(8, moves.Count); Assert.Contains(new Coords(3, 3), moves); Assert.Contains(new Coords(4, 4), moves); Assert.Contains(new Coords(1, 1), moves); Assert.Contains(new Coords(1, 3), moves); Assert.Contains(new Coords(3, 1), moves); // Should NOT contain orthogonal moves Assert.DoesNotContain(new Coords(2, 3), moves); Assert.DoesNotContain(new Coords(3, 2), moves); } [Fact] public void Bishop_BlockedByWall() { var board = new BoardBuilder(5, 5) .WithProduction(0, 0, "P", CargoType.Wood, 2) .WithDemand(4, 0, "D", CargoType.Wood, 1, 99) .WithWall(3, 3) .WithStock(PieceKind.Bishop, 5) .BuildState(); var moves = MoveValidator.GetLegalEndCells(PieceKind.Bishop, new Coords(2, 2), board); Assert.DoesNotContain(new Coords(3, 3), moves); // wall Assert.DoesNotContain(new Coords(4, 4), moves); // blocked by wall in path } [Fact] public void Knight_LShape() { var board = EmptyBoard(); var moves = MoveValidator.GetLegalEndCells(PieceKind.Knight, new Coords(2, 2), board); // Up to 8 L-shaped destinations Assert.Equal(8, moves.Count); Assert.Contains(new Coords(3, 4), moves); Assert.Contains(new Coords(4, 3), moves); Assert.Contains(new Coords(4, 1), moves); Assert.Contains(new Coords(3, 0), moves); Assert.Contains(new Coords(1, 0), moves); Assert.Contains(new Coords(0, 1), moves); Assert.Contains(new Coords(0, 3), moves); Assert.Contains(new Coords(1, 4), moves); } [Fact] public void Knight_JumpsOverWalls() { var board = new BoardBuilder(5, 5) .WithProduction(0, 0, "P", CargoType.Wood, 2) .WithDemand(4, 0, "D", CargoType.Wood, 1, 99) .WithWall(1, 2) .WithWall(2, 1) .WithWall(2, 3) .WithWall(3, 2) .WithStock(PieceKind.Knight, 5) .BuildState(); // Knight at (2,2) surrounded by walls — should still jump over all of them var moves = MoveValidator.GetLegalEndCells(PieceKind.Knight, new Coords(2, 2), board); Assert.Contains(new Coords(3, 4), moves); Assert.Contains(new Coords(4, 3), moves); } [Fact] public void Knight_JumpsOverPieces() { var board = EmptyBoard(); // Place pieces adjacent to knight — they don't block L-shape jumps board.Pieces.Add(new PieceState(90, PieceKind.Rook, new Coords(2, 3), new Coords(3, 3), 0)); board.Pieces.Add(new PieceState(91, PieceKind.Rook, new Coords(3, 2), new Coords(3, 1), 0)); var moves = MoveValidator.GetLegalEndCells(PieceKind.Knight, new Coords(2, 2), board); // Knight should reach all 8 L-shaped cells regardless of pieces in between Assert.Contains(new Coords(3, 4), moves); Assert.Contains(new Coords(4, 3), moves); Assert.Contains(new Coords(4, 1), moves); Assert.Contains(new Coords(1, 4), moves); Assert.Equal(8, moves.Count); } [Fact] public void Knight_CannotLandOnWall() { var board = new BoardBuilder(5, 5) .WithProduction(0, 0, "P", CargoType.Wood, 2) .WithDemand(4, 0, "D", CargoType.Wood, 1, 99) .WithWall(3, 4) .WithStock(PieceKind.Knight, 5) .BuildState(); var moves = MoveValidator.GetLegalEndCells(PieceKind.Knight, new Coords(2, 2), board); Assert.DoesNotContain(new Coords(3, 4), moves); // wall at target } [Fact] public void StartCell_CannotBeWall() { var board = new BoardBuilder(5, 5) .WithProduction(0, 0, "P", CargoType.Wood, 2) .WithDemand(4, 0, "D", CargoType.Wood, 1, 99) .WithWall(2, 2) .WithStock(PieceKind.Rook, 5) .BuildState(); var moves = MoveValidator.GetLegalEndCells(PieceKind.Rook, new Coords(2, 2), board); Assert.Empty(moves); } [Fact] public void EndCell_CanOverlapWithPiece() { var board = EmptyBoard(); board.Pieces.Add(new PieceState(99, PieceKind.Rook, new Coords(3, 2), new Coords(4, 2), 0)); var moves = MoveValidator.GetLegalEndCells(PieceKind.Rook, new Coords(2, 2), board); // Pieces can share relay points — collisions are detected at runtime Assert.Contains(new Coords(3, 2), moves); Assert.Contains(new Coords(4, 2), moves); } }