using System; namespace Shogi.Domain.UnitTests { public class ShogiShould { private readonly ITestOutputHelper console; public ShogiShould(ITestOutputHelper console) { this.console = console; } [Fact] public void MoveAPieceToAnEmptyPosition() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; board["A4"].Should().BeNull(); var expectedPiece = board["A3"]; expectedPiece.Should().NotBeNull(); // Act shogi.Move("A3", "A4", false); // Assert board["A3"].Should().BeNull(); board["A4"].Should().Be(expectedPiece); } [Fact] public void AllowValidMoves_AfterCheck() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; // P1 Pawn shogi.Move("C3", "C4", false); // P2 Pawn shogi.Move("G7", "G6", false); // P1 Bishop puts P2 in check shogi.Move("B2", "G7", false); board.InCheck.Should().Be(WhichPlayer.Player2); // Act - P2 is able to un-check theirself. /// P2 King moves out of check shogi.Move("E9", "E8", false); // Assert using (new AssertionScope()) { board.InCheck.Should().BeNull(); } } [Fact] public void PreventInvalidMoves_MoveFromEmptyPosition() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; board["D5"].Should().BeNull(); // Act var act = () => shogi.Move("D5", "D6", false); // Assert act.Should().Throw(); board["D5"].Should().BeNull(); board["D6"].Should().BeNull(); board.Player1Hand.Should().BeEmpty(); board.Player2Hand.Should().BeEmpty(); } [Fact] public void PreventInvalidMoves_MoveToCurrentPosition() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; var expectedPiece = board["A3"]; // Act - P1 "moves" pawn to the position it already exists at. var act = () => shogi.Move("A3", "A3", false); // Assert using (new AssertionScope()) { act.Should().Throw(); board["A3"].Should().Be(expectedPiece); board.Player1Hand.Should().BeEmpty(); board.Player2Hand.Should().BeEmpty(); } } [Fact] public void PreventInvalidMoves_MoveSet() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; var expectedPiece = board["A1"]; expectedPiece!.WhichPiece.Should().Be(WhichPiece.Lance); // Act - Move Lance illegally var act = () => shogi.Move("A1", "D5", false); // Assert using (new AssertionScope()) { act.Should().Throw(); board["A1"].Should().Be(expectedPiece); board["A5"].Should().BeNull(); board.Player1Hand.Should().BeEmpty(); board.Player2Hand.Should().BeEmpty(); } } [Fact] public void PreventInvalidMoves_Ownership() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; var expectedPiece = board["A7"]; expectedPiece!.Owner.Should().Be(WhichPlayer.Player2); board.WhoseTurn.Should().Be(WhichPlayer.Player1); // Act - Move Player2 Pawn when it is Player1 turn. var act = () => shogi.Move("A7", "A6", false); // Assert using (new AssertionScope()) { act.Should().Throw(); board["A7"].Should().Be(expectedPiece); board["A6"].Should().BeNull(); } } [Fact] public void PreventInvalidMoves_MoveThroughAllies() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; var lance = board["A1"]; var pawn = board["A3"]; lance!.Owner.Should().Be(pawn!.Owner); // Act - Move P1 Lance through P1 Pawn. var act = () => shogi.Move("A1", "A5", false); // Assert using (new AssertionScope()) { act.Should().Throw(); board["A1"].Should().Be(lance); board["A3"].Should().Be(pawn); board["A5"].Should().BeNull(); } } [Fact] public void PreventInvalidMoves_CaptureAlly() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; var knight = board["B1"]; var pawn = board["C3"]; knight!.Owner.Should().Be(pawn!.Owner); // Act - P1 Knight tries to capture P1 Pawn. var act = () => shogi.Move("B1", "C3", false); // Arrange using (new AssertionScope()) { act.Should().Throw(); board["B1"].Should().Be(knight); board["C3"].Should().Be(pawn); board.Player1Hand.Should().BeEmpty(); board.Player2Hand.Should().BeEmpty(); } } [Fact] public void PreventInvalidMoves_Check() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; // P1 Pawn shogi.Move("C3", "C4", false); // P2 Pawn shogi.Move("G7", "G6", false); // P1 Bishop puts P2 in check shogi.Move("B2", "G7", false); board.InCheck.Should().Be(WhichPlayer.Player2); var lance = board["I9"]; // Act - P2 moves Lance while in check. var act = () => shogi.Move("I9", "I8", false); // Assert using (new AssertionScope()) { act.Should().Throw(); board.InCheck.Should().Be(WhichPlayer.Player2); board["I9"].Should().Be(lance); board["I8"].Should().BeNull(); } } [Fact] // TODO: Consider nesting classes to share this setup in a constructor but have act and assert as separate facts. public void PreventInvalidDrops_MoveSet() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; // P1 Pawn shogi.Move("C3", "C4", false); // P2 Pawn shogi.Move("I7", "I6", false); // P1 Bishop takes P2 Pawn. shogi.Move("B2", "G7", false); // P2 Gold, block check from P1 Bishop. shogi.Move("F9", "F8", false); // P1 Bishop takes P2 Bishop, promotes so it can capture P2 Knight and P2 Lance shogi.Move("G7", "H8", true); // P2 Pawn again shogi.Move("I6", "I5", false); // P1 Bishop takes P2 Knight shogi.Move("H8", "H9", false); // P2 Pawn again shogi.Move("I5", "I4", false); // P1 Bishop takes P2 Lance shogi.Move("H9", "I9", false); // P2 Pawn captures P1 Pawn shogi.Move("I4", "I3", false); board.Player1Hand.Count.Should().Be(4); board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight); board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Lance); board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Pawn); board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop); board.WhoseTurn.Should().Be(WhichPlayer.Player1); // Act | Assert - Illegally placing Knight from the hand in farthest rank. board["H9"].Should().BeNull(); var act = () => shogi.Move(WhichPiece.Knight, "H9"); act.Should().Throw(); board["H9"].Should().BeNull(); board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight); // Act | Assert - Illegally placing Knight from the hand in second farthest row. board["H8"].Should().BeNull(); act = () => shogi.Move(WhichPiece.Knight, "H8"); act.Should().Throw(); board["H8"].Should().BeNull(); board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight); // Act | Assert - Illegally place Lance from the hand. board["H9"].Should().BeNull(); act = () => shogi.Move(WhichPiece.Knight, "H9"); act.Should().Throw(); board["H9"].Should().BeNull(); board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Lance); // Act | Assert - Illegally place Pawn from the hand. board["H9"].Should().BeNull(); act = () => shogi.Move(WhichPiece.Pawn, "H9"); act.Should().Throw(); board["H9"].Should().BeNull(); board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Pawn); // // Act | Assert - Illegally place Pawn from the hand in a row which already has an unpromoted Pawn. // // TODO } //[Fact] //public void PreventInvalidDrop_Check() //{ // // Arrange // var moves = new[] // { // // P1 Pawn // new Move("C3", "C4"), // // P2 Pawn // new Move("G7", "G6"), // // P1 Pawn, arbitrary move. // new Move("A3", "A4"), // // P2 Bishop takes P1 Bishop // new Move("H8", "B2"), // // P1 Silver takes P2 Bishop // new Move("C1", "B2"), // // P2 Pawn, arbtrary move // new Move("A7", "A6"), // // P1 drop Bishop, place P2 in check // new Move(WhichPiece.Bishop, "G7") // }; // var shogi = new Shogi(moves); // shogi.InCheck.Should().Be(WhichPlayer.Player2); // shogi.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop); // board["E5"].Should().BeNull(); // // Act - P2 places a Bishop while in check. // var dropSuccess = shogi.Move(new Move(WhichPiece.Bishop, "E5")); // // Assert // dropSuccess.Should().BeFalse(); // board["E5"].Should().BeNull(); // shogi.InCheck.Should().Be(WhichPlayer.Player2); // shogi.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop); //} //[Fact] //public void PreventInvalidDrop_Capture() //{ // // Arrange // var moves = new[] // { // // P1 Pawn // new Move("C3", "C4"), // // P2 Pawn // new Move("G7", "G6"), // // P1 Bishop capture P2 Bishop // new Move("B2", "H8"), // // P2 Pawn // new Move("G6", "G5") // }; // var shogi = new Shogi(moves); // using (new AssertionScope()) // { // shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop); // board["I9"].Should().NotBeNull(); // board["I9"].WhichPiece.Should().Be(WhichPiece.Lance); // board["I9"].Owner.Should().Be(WhichPlayer.Player2); // } // // Act - P1 tries to place a piece where an opponent's piece resides. // var dropSuccess = shogi.Move(new Move(WhichPiece.Bishop, "I9")); // // Assert // using (new AssertionScope()) // { // dropSuccess.Should().BeFalse(); // shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop); // board["I9"].Should().NotBeNull(); // board["I9"].WhichPiece.Should().Be(WhichPiece.Lance); // board["I9"].Owner.Should().Be(WhichPlayer.Player2); // } //} [Fact] public void Check() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; // P1 Pawn shogi.Move("C3", "C4", false); // P2 Pawn shogi.Move("G7", "G6", false); // Act - P1 Bishop, check shogi.Move("B2", "G7", false); // Assert board.InCheck.Should().Be(WhichPlayer.Player2); } [Fact] public void Promote() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; // P1 Pawn shogi.Move("C3", "C4", false); // P2 Pawn shogi.Move("G7", "G6", false); // Act - P1 moves across promote threshold. shogi.Move("B2", "G7", true); // Assert using (new AssertionScope()) { board["B2"].Should().BeNull(); board["G7"].Should().NotBeNull(); board["G7"]!.WhichPiece.Should().Be(WhichPiece.Bishop); board["G7"]!.Owner.Should().Be(WhichPlayer.Player1); board["G7"]!.IsPromoted.Should().BeTrue(); } } [Fact] public void Capture() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; var p1Bishop = board["B2"]; p1Bishop!.WhichPiece.Should().Be(WhichPiece.Bishop); shogi.Move("C3", "C4", false); shogi.Move("G7", "G6", false); // Act - P1 Bishop captures P2 Bishop shogi.Move("B2", "H8", false); // Assert board["B2"].Should().BeNull(); board["H8"].Should().Be(p1Bishop); board .Player1Hand .Should() .ContainSingle(p => p.WhichPiece == WhichPiece.Bishop && p.Owner == WhichPlayer.Player1); } [Fact] public void CheckMate() { // Arrange var shogi = MockShogiBoard(); var board = shogi.BoardState; // P1 Rook shogi.Move("H2", "E2", false); // P2 Gold shogi.Move("F9", "G8", false); // P1 Pawn shogi.Move("E3", "E4", false); // P2 other Gold shogi.Move("D9", "C8", false); // P1 same Pawn shogi.Move("E4", "E5", false); // P2 Pawn shogi.Move("E7", "E6", false); // P1 Pawn takes P2 Pawn shogi.Move("E5", "E6", false); // P2 King shogi.Move("E9", "E8", false); // P1 Pawn promotes; threatens P2 King shogi.Move("E6", "E7", true); // P2 King retreat shogi.Move("E8", "E9", false); // Act - P1 Pawn wins by checkmate. shogi.Move("E7", "E8", false); // Assert - checkmate console.WriteLine(shogi.ToStringStateAsAscii()); board.IsCheckmate.Should().BeTrue(); board.InCheck.Should().Be(WhichPlayer.Player2); } private static ShogiBoard MockShogiBoard() => new ShogiBoard("Test Session", BoardState.StandardStarting); } }