From f79a1312c7c20dbe3cfcd91bc642dbd1e35f6913 Mon Sep 17 00:00:00 2001 From: Lucas Morgan Date: Sat, 29 Jan 2022 10:07:53 -0600 Subject: [PATCH] yep --- Shogi.Domain.UnitTests/ShogiShould.cs | 318 ++++++++++++++------------ Shogi.Domain/Shogi.cs | 20 +- Shogi.Domain/ShogiBoardState.cs | 3 + Shogi.Domain/StandardRules.cs | 10 +- 4 files changed, 190 insertions(+), 161 deletions(-) diff --git a/Shogi.Domain.UnitTests/ShogiShould.cs b/Shogi.Domain.UnitTests/ShogiShould.cs index 9e7724e..2e8f734 100644 --- a/Shogi.Domain.UnitTests/ShogiShould.cs +++ b/Shogi.Domain.UnitTests/ShogiShould.cs @@ -75,181 +75,195 @@ namespace Shogi.Domain.UnitTests board["D6"].Should().BeNull(); } - //[Fact] - //public void PreventInvalidMoves_MoveToCurrentPosition() - //{ - // // Arrange - // var shogi = new Shogi(); + [Fact] + public void PreventInvalidMoves_MoveToCurrentPosition() + { + // Arrange + var board = new ShogiBoardState(); + var shogi = new Shogi(board); - // // Act - P1 "moves" pawn to the position it already exists at. - // var moveSuccess = shogi.Move(new Move("A3", "A3")); + // Act - P1 "moves" pawn to the position it already exists at. + var act = () => shogi.Move("A3", "A3", false); - // // Assert - // moveSuccess.Should().BeFalse(); - // shogi.Board["A3"].WhichPiece.Should().Be(WhichPiece.Pawn); - // shogi.Player1Hand.Should().BeEmpty(); - // shogi.Player2Hand.Should().BeEmpty(); - //} + // Assert + act.Should().Throw(); + board["A3"].Should().NotBeNull(); + board["A3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); + board.Player1Hand.Should().BeEmpty(); + board.Player2Hand.Should().BeEmpty(); + } - //[Fact] - //public void PreventInvalidMoves_MoveSet() - //{ - // // Arrange - // var shogi = new Shogi(); + [Fact] + public void PreventInvalidMoves_MoveSet() + { + // Arrange + var board = new ShogiBoardState(); + var shogi = new Shogi(board); - // // Act - Move Lance illegally - // var moveSuccess = shogi.Move(new Move("A1", "D5")); + // Act - Move Lance illegally + var act = () => shogi.Move("A1", "D5", false); - // // 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(); - //} + // Assert + act.Should().Throw(); + board["A1"].Should().NotBeNull(); + board["A1"]!.WhichPiece.Should().Be(WhichPiece.Lance); + board["A5"].Should().BeNull(); + board.Player1Hand.Should().BeEmpty(); + board.Player2Hand.Should().BeEmpty(); + } - //[Fact] - //public void PreventInvalidMoves_Ownership() - //{ - // // Arrange - // var shogi = new Shogi(); - // shogi.WhoseTurn.Should().Be(WhichPlayer.Player1); - // shogi.Board["A7"].Owner.Should().Be(WhichPlayer.Player2); + [Fact] + public void PreventInvalidMoves_Ownership() + { + // Arrange + var board = new ShogiBoardState(); + var shogi = new Shogi(board); + board.WhoseTurn.Should().Be(WhichPlayer.Player1); + board["A7"].Should().NotBeNull(); + board["A7"]!.Owner.Should().Be(WhichPlayer.Player2); - // // Act - Move Player2 Pawn when it is Player1 turn. - // var moveSuccess = shogi.Move(new Move("A7", "A6")); + // Act - Move Player2 Pawn when it is Player1 turn. + var act = () => shogi.Move("A7", "A6", false); - // // Assert - // moveSuccess.Should().BeFalse(); - // shogi.Board["A7"].WhichPiece.Should().Be(WhichPiece.Pawn); - // shogi.Board["A6"].Should().BeNull(); - //} + // Assert + act.Should().Throw(); + board["A7"].Should().NotBeNull(); + board["A7"]!.WhichPiece.Should().Be(WhichPiece.Pawn); + board["A6"].Should().BeNull(); + } - //[Fact] - //public void PreventInvalidMoves_MoveThroughAllies() - //{ - // // Arrange - // var shogi = new Shogi(); + [Fact] + public void PreventInvalidMoves_MoveThroughAllies() + { + // Arrange + var board = new ShogiBoardState(); + var shogi = new Shogi(board); - // // Act - Move P1 Lance through P1 Pawn. - // var moveSuccess = shogi.Move(new Move("A1", "A5")); + // Act - Move P1 Lance through P1 Pawn. + var act = () => shogi.Move("A1", "A5", false); - // // 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(); - //} + // Assert + act.Should().Throw(); + board["A1"].Should().NotBeNull(); + board["A1"]!.WhichPiece.Should().Be(WhichPiece.Lance); + board["A3"].Should().NotBeNull(); + board["A3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); + board["A5"].Should().BeNull(); + } - //[Fact] - //public void PreventInvalidMoves_CaptureAlly() - //{ - // // Arrange - // var shogi = new Shogi(); + [Fact] + public void PreventInvalidMoves_CaptureAlly() + { + // Arrange + var board = new ShogiBoardState(); + var shogi = new Shogi(board); - // // Act - P1 Knight tries to capture P1 Pawn. - // var moveSuccess = shogi.Move(new Move("B1", "C3")); + // Act - P1 Knight tries to capture P1 Pawn. + var act = () => shogi.Move("B1", "C3", false); - // // 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(); - //} + // Arrange + act.Should().Throw(); + board["B1"].Should().NotBeNull(); + board["B1"]!.WhichPiece.Should().Be(WhichPiece.Knight); + board["C3"].Should().NotBeNull(); + board["C3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); + board.Player1Hand.Should().BeEmpty(); + board.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(WhichPlayer.Player2); + [Fact] + public void PreventInvalidMoves_Check() + { + // Arrange + var board = new ShogiBoardState(); + var shogi = new Shogi(board); + // 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 moves Lance while in check. - // var moveSuccess = shogi.Move(new Move("I9", "I8")); + // Act - P2 moves Lance while in check. + var act = () => shogi.Move("I9", "I8", false); - // // Assert - // moveSuccess.Should().BeFalse(); - // shogi.InCheck.Should().Be(WhichPlayer.Player2); - // shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance); - // shogi.Board["I8"].Should().BeNull(); - //} + // Assert + act.Should().Throw(); + board.InCheck.Should().Be(WhichPlayer.Player2); + board["I9"].Should().NotBeNull(); + board["I9"]!.WhichPiece.Should().Be(WhichPiece.Lance); + 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(WhichPlayer.Player1); + [Fact] + public void PreventInvalidDrops_MoveSet() + { + // Arrange + var board = new ShogiBoardState(); + var shogi = new Shogi(board); - // // 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); + // 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); - // // 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); + 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 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 placing Knight from the hand in farthest row. + board["H9"].Should().BeNull(); + shogi.Move() + 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 place Pawn from the hand in a row which already has an unpromoted Pawn. - // // TODO - //} + // 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() diff --git a/Shogi.Domain/Shogi.cs b/Shogi.Domain/Shogi.cs index 2cf501c..b895ffa 100644 --- a/Shogi.Domain/Shogi.cs +++ b/Shogi.Domain/Shogi.cs @@ -43,16 +43,24 @@ public void Move(string from, string to, bool isPromotion) { - var fromVector = ShogiBoardState.FromBoardNotation(from); - var toVector = ShogiBoardState.FromBoardNotation(to); - var moveResult = rules.Move(from, to, isPromotion); + var tempBoard = new ShogiBoardState(board); + var simulation = new StandardRules(tempBoard); + var moveResult = simulation.Move(from, to, isPromotion); if (!moveResult.Success) { throw new InvalidOperationException(moveResult.Reason); } + var fromVector = ShogiBoardState.FromBoardNotation(from); + var toVector = ShogiBoardState.FromBoardNotation(to); var otherPlayer = board.WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1; - if (rules.EvaluateCheckAfterMove(fromVector, toVector, otherPlayer)) + if (simulation.IsPlayerInCheckAfterMove(fromVector, toVector, board.WhoseTurn)) + { + throw new InvalidOperationException("Illegal move. This move places you in check."); + } + + rules.Move(from, to, isPromotion); + if (rules.IsPlayerInCheckAfterMove(fromVector, toVector, otherPlayer)) { board.InCheck = otherPlayer; board.IsCheckmate = rules.EvaluateCheckmate(); @@ -64,6 +72,10 @@ board.WhoseTurn = otherPlayer; } + public void Move(WhichPiece pieceInHand, string to) + { + + } ///// ///// Attempts a given move. Returns false if the move is illegal. ///// diff --git a/Shogi.Domain/ShogiBoardState.cs b/Shogi.Domain/ShogiBoardState.cs index 216b4b3..3d240bf 100644 --- a/Shogi.Domain/ShogiBoardState.cs +++ b/Shogi.Domain/ShogiBoardState.cs @@ -42,6 +42,9 @@ namespace Shogi.Domain { board[kvp.Key] = kvp.Value == null ? null : new Piece(kvp.Value); } + WhoseTurn = other.WhoseTurn; + InCheck = other.InCheck; + IsCheckmate = other.IsCheckmate; MoveHistory.AddRange(other.MoveHistory); Player1Hand.AddRange(other.Player1Hand); Player2Hand.AddRange(other.Player2Hand); diff --git a/Shogi.Domain/StandardRules.cs b/Shogi.Domain/StandardRules.cs index 9d8d908..27e1f70 100644 --- a/Shogi.Domain/StandardRules.cs +++ b/Shogi.Domain/StandardRules.cs @@ -39,11 +39,11 @@ namespace Shogi.Domain } /// - /// Move a piece from a board tile to another board tile. + /// Move a piece from a board tile to another board tile ignorant of check or check-mate. /// /// The position of the piece being moved expressed in board notation. /// The target position expressed in board notation. - /// A describing the success or failure of the simulation. + /// A describing the success or failure of the move. public MoveResult Move(string fromNotation, string toNotation, bool isPromotion = false) { var from = ShogiBoardState.FromBoardNotation(fromNotation); @@ -105,7 +105,7 @@ namespace Shogi.Domain } /// - /// Move a piece from the hand to the board. + /// Move a piece from the hand to the board ignorant if check or check-mate. /// /// /// The target position expressed in board notation. @@ -184,7 +184,7 @@ namespace Shogi.Domain return isCheck; } - public bool EvaluateCheckAfterMove(Vector2 from, Vector2 to, WhichPlayer whichPlayer) + public bool IsPlayerInCheckAfterMove(Vector2 from, Vector2 to, WhichPlayer whichPlayer) { if (whichPlayer == board.InCheck) return true; // If we already know the player is in check, don't bother. @@ -265,7 +265,7 @@ namespace Shogi.Domain var simulationResult = simulationBoard.Move(fromNotation, toNotation, false); if (simulationResult.Success) { - if (!EvaluateCheckAfterMove(from, position, board.InCheck.Value)) + if (!IsPlayerInCheckAfterMove(from, position, board.InCheck.Value)) { isCheckmate = false; }