using FluentAssertions; using FluentAssertions.Execution; using Gameboard.ShogiUI.Sockets.Extensions; using Gameboard.ShogiUI.Sockets.Models; using System.Linq; using Xunit; using Xunit.Abstractions; using WhichPiece = Gameboard.ShogiUI.Sockets.ServiceModels.Types.WhichPiece; using WhichPerspective = Gameboard.ShogiUI.Sockets.ServiceModels.Types.WhichPerspective; namespace Gameboard.ShogiUI.xUnitTests { public class ShogiShould { private readonly ITestOutputHelper output; public ShogiShould(ITestOutputHelper output) { this.output = output; } [Fact] public void InitializeBoardState() { // Act var board = new Shogi().Board; // Assert board["A1"].WhichPiece.Should().Be(WhichPiece.Lance); board["A1"].Owner.Should().Be(WhichPerspective.Player1); board["A1"].IsPromoted.Should().Be(false); board["B1"].WhichPiece.Should().Be(WhichPiece.Knight); board["B1"].Owner.Should().Be(WhichPerspective.Player1); board["B1"].IsPromoted.Should().Be(false); board["C1"].WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["C1"].Owner.Should().Be(WhichPerspective.Player1); board["C1"].IsPromoted.Should().Be(false); board["D1"].WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["D1"].Owner.Should().Be(WhichPerspective.Player1); board["D1"].IsPromoted.Should().Be(false); board["E1"].WhichPiece.Should().Be(WhichPiece.King); board["E1"].Owner.Should().Be(WhichPerspective.Player1); board["E1"].IsPromoted.Should().Be(false); board["F1"].WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["F1"].Owner.Should().Be(WhichPerspective.Player1); board["F1"].IsPromoted.Should().Be(false); board["G1"].WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["G1"].Owner.Should().Be(WhichPerspective.Player1); board["G1"].IsPromoted.Should().Be(false); board["H1"].WhichPiece.Should().Be(WhichPiece.Knight); board["H1"].Owner.Should().Be(WhichPerspective.Player1); board["H1"].IsPromoted.Should().Be(false); board["I1"].WhichPiece.Should().Be(WhichPiece.Lance); board["I1"].Owner.Should().Be(WhichPerspective.Player1); board["I1"].IsPromoted.Should().Be(false); board["A2"].Should().BeNull(); board["B2"].WhichPiece.Should().Be(WhichPiece.Bishop); board["B2"].Owner.Should().Be(WhichPerspective.Player1); board["B2"].IsPromoted.Should().Be(false); board["C2"].Should().BeNull(); board["D2"].Should().BeNull(); board["E2"].Should().BeNull(); board["F2"].Should().BeNull(); board["G2"].Should().BeNull(); board["H2"].WhichPiece.Should().Be(WhichPiece.Rook); board["H2"].Owner.Should().Be(WhichPerspective.Player1); board["H2"].IsPromoted.Should().Be(false); board["I2"].Should().BeNull(); board["A3"].WhichPiece.Should().Be(WhichPiece.Pawn); board["A3"].Owner.Should().Be(WhichPerspective.Player1); board["A3"].IsPromoted.Should().Be(false); board["B3"].WhichPiece.Should().Be(WhichPiece.Pawn); board["B3"].Owner.Should().Be(WhichPerspective.Player1); board["B3"].IsPromoted.Should().Be(false); board["C3"].WhichPiece.Should().Be(WhichPiece.Pawn); board["C3"].Owner.Should().Be(WhichPerspective.Player1); board["C3"].IsPromoted.Should().Be(false); board["D3"].WhichPiece.Should().Be(WhichPiece.Pawn); board["D3"].Owner.Should().Be(WhichPerspective.Player1); board["D3"].IsPromoted.Should().Be(false); board["E3"].WhichPiece.Should().Be(WhichPiece.Pawn); board["E3"].Owner.Should().Be(WhichPerspective.Player1); board["E3"].IsPromoted.Should().Be(false); board["F3"].WhichPiece.Should().Be(WhichPiece.Pawn); board["F3"].Owner.Should().Be(WhichPerspective.Player1); board["F3"].IsPromoted.Should().Be(false); board["G3"].WhichPiece.Should().Be(WhichPiece.Pawn); board["G3"].Owner.Should().Be(WhichPerspective.Player1); board["G3"].IsPromoted.Should().Be(false); board["H3"].WhichPiece.Should().Be(WhichPiece.Pawn); board["H3"].Owner.Should().Be(WhichPerspective.Player1); board["H3"].IsPromoted.Should().Be(false); board["I3"].WhichPiece.Should().Be(WhichPiece.Pawn); board["I3"].Owner.Should().Be(WhichPerspective.Player1); board["I3"].IsPromoted.Should().Be(false); board["A4"].Should().BeNull(); board["B4"].Should().BeNull(); board["C4"].Should().BeNull(); board["D4"].Should().BeNull(); board["E4"].Should().BeNull(); board["F4"].Should().BeNull(); board["G4"].Should().BeNull(); board["H4"].Should().BeNull(); board["I4"].Should().BeNull(); board["A5"].Should().BeNull(); board["B5"].Should().BeNull(); board["C5"].Should().BeNull(); board["D5"].Should().BeNull(); board["E5"].Should().BeNull(); board["F5"].Should().BeNull(); board["G5"].Should().BeNull(); board["H5"].Should().BeNull(); board["I5"].Should().BeNull(); board["A6"].Should().BeNull(); board["B6"].Should().BeNull(); board["C6"].Should().BeNull(); board["D6"].Should().BeNull(); board["E6"].Should().BeNull(); board["F6"].Should().BeNull(); board["G6"].Should().BeNull(); board["H6"].Should().BeNull(); board["I6"].Should().BeNull(); board["A7"].WhichPiece.Should().Be(WhichPiece.Pawn); board["A7"].Owner.Should().Be(WhichPerspective.Player2); board["A7"].IsPromoted.Should().Be(false); board["B7"].WhichPiece.Should().Be(WhichPiece.Pawn); board["B7"].Owner.Should().Be(WhichPerspective.Player2); board["B7"].IsPromoted.Should().Be(false); board["C7"].WhichPiece.Should().Be(WhichPiece.Pawn); board["C7"].Owner.Should().Be(WhichPerspective.Player2); board["C7"].IsPromoted.Should().Be(false); board["D7"].WhichPiece.Should().Be(WhichPiece.Pawn); board["D7"].Owner.Should().Be(WhichPerspective.Player2); board["D7"].IsPromoted.Should().Be(false); board["E7"].WhichPiece.Should().Be(WhichPiece.Pawn); board["E7"].Owner.Should().Be(WhichPerspective.Player2); board["E7"].IsPromoted.Should().Be(false); board["F7"].WhichPiece.Should().Be(WhichPiece.Pawn); board["F7"].Owner.Should().Be(WhichPerspective.Player2); board["F7"].IsPromoted.Should().Be(false); board["G7"].WhichPiece.Should().Be(WhichPiece.Pawn); board["G7"].Owner.Should().Be(WhichPerspective.Player2); board["G7"].IsPromoted.Should().Be(false); board["H7"].WhichPiece.Should().Be(WhichPiece.Pawn); board["H7"].Owner.Should().Be(WhichPerspective.Player2); board["H7"].IsPromoted.Should().Be(false); board["I7"].WhichPiece.Should().Be(WhichPiece.Pawn); board["I7"].Owner.Should().Be(WhichPerspective.Player2); board["I7"].IsPromoted.Should().Be(false); board["A8"].Should().BeNull(); board["B8"].WhichPiece.Should().Be(WhichPiece.Rook); board["B8"].Owner.Should().Be(WhichPerspective.Player2); board["B8"].IsPromoted.Should().Be(false); board["C8"].Should().BeNull(); board["D8"].Should().BeNull(); board["E8"].Should().BeNull(); board["F8"].Should().BeNull(); board["G8"].Should().BeNull(); board["H8"].WhichPiece.Should().Be(WhichPiece.Bishop); board["H8"].Owner.Should().Be(WhichPerspective.Player2); board["H8"].IsPromoted.Should().Be(false); board["I8"].Should().BeNull(); board["A9"].WhichPiece.Should().Be(WhichPiece.Lance); board["A9"].Owner.Should().Be(WhichPerspective.Player2); board["A9"].IsPromoted.Should().Be(false); board["B9"].WhichPiece.Should().Be(WhichPiece.Knight); board["B9"].Owner.Should().Be(WhichPerspective.Player2); board["B9"].IsPromoted.Should().Be(false); board["C9"].WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["C9"].Owner.Should().Be(WhichPerspective.Player2); board["C9"].IsPromoted.Should().Be(false); board["D9"].WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["D9"].Owner.Should().Be(WhichPerspective.Player2); board["D9"].IsPromoted.Should().Be(false); board["E9"].WhichPiece.Should().Be(WhichPiece.King); board["E9"].Owner.Should().Be(WhichPerspective.Player2); board["E9"].IsPromoted.Should().Be(false); board["F9"].WhichPiece.Should().Be(WhichPiece.GoldGeneral); board["F9"].Owner.Should().Be(WhichPerspective.Player2); board["F9"].IsPromoted.Should().Be(false); board["G9"].WhichPiece.Should().Be(WhichPiece.SilverGeneral); board["G9"].Owner.Should().Be(WhichPerspective.Player2); board["G9"].IsPromoted.Should().Be(false); board["H9"].WhichPiece.Should().Be(WhichPiece.Knight); board["H9"].Owner.Should().Be(WhichPerspective.Player2); board["H9"].IsPromoted.Should().Be(false); board["I9"].WhichPiece.Should().Be(WhichPiece.Lance); board["I9"].Owner.Should().Be(WhichPerspective.Player2); board["I9"].IsPromoted.Should().Be(false); } [Fact] public void InitializeBoardStateWithMoves() { var moves = new[] { // P1 Pawn new Move("A3", "A4") }; var shogi = new Shogi(moves); shogi.Board["A3"].Should().BeNull(); shogi.Board["A4"].WhichPiece.Should().Be(WhichPiece.Pawn); } [Fact] public void AllowValidMoves_AfterCheck() { // Arrange var moves = new[] { // P1 Pawn new Move("C3", "C4"), // P2 Pawn new Move("G7", "G6"), // P1 Bishop puts P2 in check new Move("B2", "G7"), }; var shogi = new Shogi(moves); shogi.InCheck.Should().Be(WhichPerspective.Player2); // Act - P2 is able to un-check theirself. /// P2 King moves out of check var moveSuccess = shogi.Move(new Move("E9", "E8")); // Assert using var _ = new AssertionScope(); moveSuccess.Should().BeTrue(); shogi.InCheck.Should().BeNull(); } [Fact] public void PreventInvalidMoves_MoveFromEmptyPosition() { // Arrange var shogi = new Shogi(); shogi.Board["D5"].Should().BeNull(); // Act var moveSuccess = shogi.Move(new Move("D5", "D6")); // Assert moveSuccess.Should().BeFalse(); shogi.Board["D5"].Should().BeNull(); shogi.Board["D6"].Should().BeNull(); } [Fact] public void PreventInvalidMoves_MoveToCurrentPosition() { // Arrange var shogi = new Shogi(); // Act - P1 "moves" pawn to the position it already exists at. var moveSuccess = shogi.Move(new Move("A3", "A3")); // Assert moveSuccess.Should().BeFalse(); shogi.Board["A3"].WhichPiece.Should().Be(WhichPiece.Pawn); shogi.Player1Hand.Should().BeEmpty(); shogi.Player2Hand.Should().BeEmpty(); } [Fact] public void PreventInvalidMoves_MoveSet() { // Arrange var shogi = new Shogi(); // Act - Move Lance illegally var moveSuccess = shogi.Move(new Move("A1", "D5")); // Assert moveSuccess.Should().BeFalse(); shogi.Board["A1"].WhichPiece.Should().Be(WhichPiece.Lance); shogi.Board["A5"].Should().BeNull(); shogi.Player1Hand.Should().BeEmpty(); shogi.Player2Hand.Should().BeEmpty(); } [Fact] public void PreventInvalidMoves_Ownership() { // Arrange var shogi = new Shogi(); shogi.WhoseTurn.Should().Be(WhichPerspective.Player1); shogi.Board["A7"].Owner.Should().Be(WhichPerspective.Player2); // Act - Move Player2 Pawn when it is Player1 turn. var moveSuccess = shogi.Move(new Move("A7", "A6")); // Assert moveSuccess.Should().BeFalse(); shogi.Board["A7"].WhichPiece.Should().Be(WhichPiece.Pawn); shogi.Board["A6"].Should().BeNull(); } [Fact] public void PreventInvalidMoves_MoveThroughAllies() { // Arrange var shogi = new Shogi(); // Act - Move P1 Lance through P1 Pawn. var moveSuccess = shogi.Move(new Move("A1", "A5")); // Assert moveSuccess.Should().BeFalse(); shogi.Board["A1"].WhichPiece.Should().Be(WhichPiece.Lance); shogi.Board["A3"].WhichPiece.Should().Be(WhichPiece.Pawn); shogi.Board["A5"].Should().BeNull(); } [Fact] public void PreventInvalidMoves_CaptureAlly() { // Arrange var shogi = new Shogi(); // Act - P1 Knight tries to capture P1 Pawn. var moveSuccess = shogi.Move(new Move("B1", "C3")); // Arrange moveSuccess.Should().BeFalse(); shogi.Board["B1"].WhichPiece.Should().Be(WhichPiece.Knight); shogi.Board["C3"].WhichPiece.Should().Be(WhichPiece.Pawn); shogi.Player1Hand.Should().BeEmpty(); shogi.Player2Hand.Should().BeEmpty(); } [Fact] public void PreventInvalidMoves_Check() { // Arrange var moves = new[] { // P1 Pawn new Move("C3", "C4"), // P2 Pawn new Move("G7", "G6"), // P1 Bishop puts P2 in check new Move("B2", "G7") }; var shogi = new Shogi(moves); shogi.InCheck.Should().Be(WhichPerspective.Player2); // Act - P2 moves Lance while in check. var moveSuccess = shogi.Move(new Move("I9", "I8")); // Assert moveSuccess.Should().BeFalse(); shogi.InCheck.Should().Be(WhichPerspective.Player2); shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance); shogi.Board["I8"].Should().BeNull(); } [Fact] public void PreventInvalidDrops_MoveSet() { // Arrange var moves = new[] { // P1 Pawn new Move("C3", "C4"), // P2 Pawn new Move("I7", "I6"), // P1 Bishop takes P2 Pawn. new Move("B2", "G7"), // P2 Gold, block check from P1 Bishop. new Move("F9", "F8"), // P1 Bishop takes P2 Bishop, promotes so it can capture P2 Knight and P2 Lance new Move("G7", "H8", true), // P2 Pawn again new Move("I6", "I5"), // P1 Bishop takes P2 Knight new Move("H8", "H9"), // P2 Pawn again new Move("I5", "I4"), // P1 Bishop takes P2 Lance new Move("H9", "I9"), // P2 Pawn captures P1 Pawn new Move("I4", "I3") }; var shogi = new Shogi(moves); shogi.Player1Hand.Count.Should().Be(4); shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight); shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Lance); shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Pawn); shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop); shogi.WhoseTurn.Should().Be(WhichPerspective.Player1); // Act | Assert - Illegally placing Knight from the hand in farthest row. shogi.Board["H9"].Should().BeNull(); var dropSuccess = shogi.Move(new Move(WhichPiece.Knight, "H9")); dropSuccess.Should().BeFalse(); shogi.Board["H9"].Should().BeNull(); shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight); // Act | Assert - Illegally placing Knight from the hand in second farthest row. shogi.Board["H8"].Should().BeNull(); dropSuccess = shogi.Move(new Move(WhichPiece.Knight, "H8")); dropSuccess.Should().BeFalse(); shogi.Board["H8"].Should().BeNull(); shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight); // Act | Assert - Illegally place Lance from the hand. shogi.Board["H9"].Should().BeNull(); dropSuccess = shogi.Move(new Move(WhichPiece.Knight, "H9")); dropSuccess.Should().BeFalse(); shogi.Board["H9"].Should().BeNull(); shogi.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Lance); // Act | Assert - Illegally place Pawn from the hand. shogi.Board["H9"].Should().BeNull(); dropSuccess = shogi.Move(new Move(WhichPiece.Pawn, "H9")); dropSuccess.Should().BeFalse(); shogi.Board["H9"].Should().BeNull(); shogi.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(WhichPerspective.Player2); shogi.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop); shogi.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(); shogi.Board["E5"].Should().BeNull(); shogi.InCheck.Should().Be(WhichPerspective.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); shogi.Board["I9"].Should().NotBeNull(); shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance); shogi.Board["I9"].Owner.Should().Be(WhichPerspective.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); shogi.Board["I9"].Should().NotBeNull(); shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance); shogi.Board["I9"].Owner.Should().Be(WhichPerspective.Player2); } } [Fact] public void Check() { // Arrange var moves = new[] { // P1 Pawn new Move("C3", "C4"), // P2 Pawn new Move("G7", "G6"), }; var shogi = new Shogi(moves); // Act - P1 Bishop, check shogi.Move(new Move("B2", "G7")); // Assert shogi.InCheck.Should().Be(WhichPerspective.Player2); } [Fact] public void Promote() { // Arrange var moves = new[] { // P1 Pawn new Move("C3", "C4" ), // P2 Pawn new Move("G7", "G6" ) }; var shogi = new Shogi(moves); // Act - P1 moves across promote threshold. var moveSuccess = shogi.Move(new Move("B2", "G7", true)); // Assert using (new AssertionScope()) { moveSuccess.Should().BeTrue(); shogi.Board["B2"].Should().BeNull(); shogi.Board["G7"].Should().NotBeNull(); shogi.Board["G7"].WhichPiece.Should().Be(WhichPiece.Bishop); shogi.Board["G7"].Owner.Should().Be(WhichPerspective.Player1); shogi.Board["G7"].IsPromoted.Should().BeTrue(); } } [Fact] public void CheckMate() { // Arrange var moves = new[] { // P1 Rook new Move("H2", "E2"), // P2 Gold new Move("F9", "G8"), // P1 Pawn new Move("E3", "E4"), // P2 other Gold new Move("D9", "C8"), // P1 same Pawn new Move("E4", "E5"), // P2 Pawn new Move("E7", "E6"), // P1 Pawn takes P2 Pawn new Move("E5", "E6"), // P2 King new Move("E9", "E8"), // P1 Pawn promotes, threatens P2 King new Move("E6", "E7", true), // P2 King retreat new Move("E8", "E9"), }; var shogi = new Shogi(moves); output.WriteLine(shogi.PrintStateAsAscii()); // Act - P1 Pawn wins by checkmate. var moveSuccess = shogi.Move(new Move("E7", "E8")); output.WriteLine(shogi.PrintStateAsAscii()); // Assert - checkmate moveSuccess.Should().BeTrue(); shogi.IsCheckmate.Should().BeTrue(); shogi.InCheck.Should().Be(WhichPerspective.Player2); } [Fact] public void Capture() { // Arrange var moves = new[] { new Move("C3", "C4"), new Move("G7", "G6") }; var shogi = new Shogi(moves); // Act - P1 Bishop captures P2 Bishop var moveSuccess = shogi.Move(new Move("B2", "H8")); // Assert moveSuccess.Should().BeTrue(); shogi.Board["B2"].Should().BeNull(); shogi.Board["H8"].WhichPiece.Should().Be(WhichPiece.Bishop); shogi.Board["H8"].Owner.Should().Be(WhichPerspective.Player1); shogi.Board.Values .Where(p => p != null) .Should().ContainSingle(piece => piece.WhichPiece == WhichPiece.Bishop); shogi.Player1Hand .Should() .ContainSingle(p => p.WhichPiece == WhichPiece.Bishop && p.Owner == WhichPerspective.Player1); } } }