diff --git a/Shogi.Domain/BoardState.cs b/Shogi.Domain/BoardState.cs
index c2b8ca4..2c54c74 100644
--- a/Shogi.Domain/BoardState.cs
+++ b/Shogi.Domain/BoardState.cs
@@ -17,7 +17,6 @@ public class BoardState
playerInCheck: null,
previousMove: new Move());
- public delegate void ForEachDelegate(Piece element, Vector2 position);
///
/// Key is position notation, such as "E4".
///
diff --git a/Shogi.Domain/StandardRules.cs b/Shogi.Domain/StandardRules.cs
index 15e5597..841720b 100644
--- a/Shogi.Domain/StandardRules.cs
+++ b/Shogi.Domain/StandardRules.cs
@@ -3,256 +3,261 @@ using BoardTile = System.Collections.Generic.KeyValuePair
- /// 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.
- /// True if a promotion is expected as a result of this move.
- /// A describing the success or failure of the move.
- internal MoveResult Move(string fromNotation, string toNotation, bool isPromotionRequested = false)
- {
- var from = Notation.FromBoardNotation(fromNotation);
- var to = Notation.FromBoardNotation(toNotation);
- var fromPiece = boardState[from];
- if (fromPiece == null)
- {
- return new MoveResult(false, $"Tile [{fromNotation}] is empty. There is no piece to move.");
- }
+ ///
+ /// 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.
+ /// True if a promotion is expected as a result of this move.
+ /// A describing the success or failure of the move.
+ internal MoveResult Move(string fromNotation, string toNotation, bool isPromotionRequested = false)
+ {
+ var from = Notation.FromBoardNotation(fromNotation);
+ var to = Notation.FromBoardNotation(toNotation);
+ var fromPiece = boardState[from];
+ if (fromPiece == null)
+ {
+ return new MoveResult(false, $"Tile [{fromNotation}] is empty. There is no piece to move.");
+ }
- if (fromPiece.Owner != boardState.WhoseTurn)
- {
- return new MoveResult(false, "Not allowed to move the opponents piece");
- }
+ if (fromPiece.Owner != boardState.WhoseTurn)
+ {
+ return new MoveResult(false, "Not allowed to move the opponents piece");
+ }
- var path = fromPiece.GetPathFromStartToEnd(from, to);
+ var path = fromPiece.GetPathFromStartToEnd(from, to);
- if (boardState.IsPathBlocked(path))
- {
- return new MoveResult(false, "Another piece obstructs the desired move.");
- }
+ if (boardState.IsPathBlocked(path))
+ {
+ return new MoveResult(false, "Another piece obstructs the desired move.");
+ }
- if (boardState[to] != null)
- {
- boardState.Capture(to);
- }
+ if (boardState[to] != null)
+ {
+ boardState.Capture(to);
+ }
- if (isPromotionRequested && (boardState.IsWithinPromotionZone(to) || boardState.IsWithinPromotionZone(from)))
- {
- fromPiece.Promote();
- }
+ if (isPromotionRequested && (boardState.IsWithinPromotionZone(to) || boardState.IsWithinPromotionZone(from)))
+ {
+ fromPiece.Promote();
+ }
- boardState[to] = fromPiece;
- boardState[from] = null;
+ boardState[to] = fromPiece;
+ boardState[from] = null;
- boardState.PreviousMove = new Move(from, to);
- var otherPlayer = boardState.WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
- boardState.WhoseTurn = otherPlayer;
+ boardState.PreviousMove = new Move(from, to);
+ var otherPlayer = boardState.WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
+ boardState.WhoseTurn = otherPlayer;
- return new MoveResult(true);
- }
+ return new MoveResult(true);
+ }
- /// Move a piece from the hand to the board ignorant if check or check-mate.
- ///
- ///
- /// The target position expressed in board notation.
- /// A describing the success or failure of the simulation.
- internal MoveResult Move(WhichPiece pieceInHand, string toNotation)
- {
- var to = Notation.FromBoardNotation(toNotation);
- var index = boardState.ActivePlayerHand.FindIndex(p => p.WhichPiece == pieceInHand);
- if (index == -1)
- {
- return new MoveResult(false, $"{pieceInHand} does not exist in the hand.");
- }
- if (boardState[to] != null)
- {
- return new MoveResult(false, $"Illegal move - attempting to capture while playing a piece from the hand.");
- }
+ /// Move a piece from the hand to the board ignorant if check or check-mate.
+ ///
+ ///
+ /// The target position expressed in board notation.
+ /// A describing the success or failure of the simulation.
+ internal MoveResult Move(WhichPiece pieceInHand, string toNotation)
+ {
+ var to = Notation.FromBoardNotation(toNotation);
+ var index = boardState.ActivePlayerHand.FindIndex(p => p.WhichPiece == pieceInHand);
+ if (index == -1)
+ {
+ return new MoveResult(false, $"{pieceInHand} does not exist in the hand.");
+ }
+ if (boardState[to] != null)
+ {
+ return new MoveResult(false, "Illegal move - attempting to capture while playing a piece from the hand.");
+ }
- switch (pieceInHand)
- {
- case WhichPiece.Knight:
- {
- // Knight cannot be placed onto the farthest two ranks from the hand.
- if ((boardState.WhoseTurn == WhichPlayer.Player1 && to.Y > 6)
- || (boardState.WhoseTurn == WhichPlayer.Player2 && to.Y < 2))
- {
- return new MoveResult(false, "Knight has no valid moves after placed.");
- }
- break;
- }
- case WhichPiece.Lance:
- case WhichPiece.Pawn:
- {
- // Lance and Pawn cannot be placed onto the farthest rank from the hand.
- if ((boardState.WhoseTurn == WhichPlayer.Player1 && to.Y == 8)
- || (boardState.WhoseTurn == WhichPlayer.Player2 && to.Y == 0))
- {
- return new MoveResult(false, $"{pieceInHand} has no valid moves after placed.");
- }
- break;
- }
- }
+ switch (pieceInHand)
+ {
+ case WhichPiece.Knight:
+ {
+ // Knight cannot be placed onto the farthest two ranks from the hand.
+ if ((boardState.WhoseTurn == WhichPlayer.Player1 && to.Y > 6)
+ || (boardState.WhoseTurn == WhichPlayer.Player2 && to.Y < 2))
+ {
+ return new MoveResult(false, "Knight has no valid moves after placed.");
+ }
+ break;
+ }
+ case WhichPiece.Lance:
+ case WhichPiece.Pawn:
+ {
+ // Lance and Pawn cannot be placed onto the farthest rank from the hand.
+ if ((boardState.WhoseTurn == WhichPlayer.Player1 && to.Y == 8)
+ || (boardState.WhoseTurn == WhichPlayer.Player2 && to.Y == 0))
+ {
+ return new MoveResult(false, $"{pieceInHand} has no valid moves after placed.");
+ }
+ break;
+ }
+ }
- // Mutate the board.
- boardState[to] = boardState.ActivePlayerHand[index];
- boardState.ActivePlayerHand.RemoveAt(index);
- //MoveHistory.Add(move);
- return new MoveResult(true);
- }
+ // Mutate the board.
+ boardState[to] = boardState.ActivePlayerHand[index];
+ boardState.ActivePlayerHand.RemoveAt(index);
+ boardState.PreviousMove = new Move(pieceInHand, to);
+ return new MoveResult(true);
+ }
- ///
- /// Determines if the last move put the player who moved in check.
- ///
- ///
- /// This strategy recognizes that a "discover check" could only occur from a subset of pieces: Rook, Bishop, Lance.
- /// In this way, only those pieces need to be considered when evaluating if a move placed the moving player in check.
- ///
- internal bool IsPlayerInCheckAfterMove()
- {
- var previousMovedPiece = boardState[boardState.PreviousMove.To];
- if (previousMovedPiece == null) throw new ArgumentNullException(nameof(previousMovedPiece), $"No piece exists at position {boardState.PreviousMove.To}.");
- var kingPosition = previousMovedPiece.Owner == WhichPlayer.Player1 ? boardState.Player1KingPosition : boardState.Player2KingPosition;
+ ///
+ /// Determines if the last move put the player who moved in check.
+ ///
+ ///
+ /// This strategy recognizes that a "discover check" could only occur from a subset of pieces: Rook, Bishop, Lance.
+ /// In this way, only those pieces need to be considered when evaluating if a move placed the moving player in check.
+ ///
+ internal bool DidPlayerPutThemselfInCheck()
+ {
+ if (boardState.PreviousMove.From == null)
+ {
+ // You can't place yourself in check by placing a piece from your hand.
+ return false;
+ }
- var isCheck = false;
+ var previousMovedPiece = boardState[boardState.PreviousMove.To];
+ if (previousMovedPiece == null) throw new ArgumentNullException(nameof(previousMovedPiece), $"No piece exists at position {boardState.PreviousMove.To}.");
+ var kingPosition = previousMovedPiece.Owner == WhichPlayer.Player1 ? boardState.Player1KingPosition : boardState.Player2KingPosition;
- // Get line equation from king through the now-unoccupied location.
- var direction = Vector2.Subtract(kingPosition, boardState.PreviousMove.From);
- var slope = Math.Abs(direction.Y / direction.X);
- var path = BoardState.GetPathAlongDirectionFromStartToEdgeOfBoard(boardState.PreviousMove.From, Vector2.Normalize(direction));
- var threat = boardState.QueryFirstPieceInPath(path);
- if (threat == null || threat.Owner == previousMovedPiece.Owner) return false;
- // If absolute slope is 45°, look for a bishop along the line.
- // If absolute slope is 0° or 90°, look for a rook along the line.
- // if absolute slope is 0°, look for lance along the line.
- if (float.IsInfinity(slope))
- {
- isCheck = threat.WhichPiece switch
- {
- WhichPiece.Lance => !threat.IsPromoted,
- WhichPiece.Rook => true,
- _ => false
- };
- }
- else if (slope == 1)
- {
- isCheck = threat.WhichPiece switch
- {
- WhichPiece.Bishop => true,
- _ => false
- };
- }
- else if (slope == 0)
- {
- isCheck = threat.WhichPiece switch
- {
- WhichPiece.Rook => true,
- _ => false
- };
- }
- return isCheck;
- }
+ var isDiscoverCheck = false;
+ // Get line equation from king through the now-unoccupied location.
+ var direction = Vector2.Subtract(kingPosition, boardState.PreviousMove.From.Value);
+ var slope = Math.Abs(direction.Y / direction.X);
+ var path = BoardState.GetPathAlongDirectionFromStartToEdgeOfBoard(boardState.PreviousMove.From.Value, Vector2.Normalize(direction));
+ var threat = boardState.QueryFirstPieceInPath(path);
+ if (threat == null || threat.Owner == previousMovedPiece.Owner) return false;
+ // If absolute slope is 45°, look for a bishop along the line.
+ // If absolute slope is 0° or 90°, look for a rook along the line.
+ // if absolute slope is 0°, look for lance along the line.
+ if (float.IsInfinity(slope))
+ {
+ isDiscoverCheck = threat.WhichPiece switch
+ {
+ WhichPiece.Lance => !threat.IsPromoted,
+ WhichPiece.Rook => true,
+ _ => false
+ };
+ }
+ else if (slope == 1)
+ {
+ isDiscoverCheck = threat.WhichPiece switch
+ {
+ WhichPiece.Bishop => true,
+ _ => false
+ };
+ }
+ else if (slope == 0)
+ {
+ isDiscoverCheck = threat.WhichPiece switch
+ {
+ WhichPiece.Rook => true,
+ _ => false
+ };
+ }
+ return isDiscoverCheck;
+ }
- internal bool IsOpponentInCheckAfterMove() => IsOpposingKingThreatenedByPosition(boardState.PreviousMove.To);
+ internal bool IsOpponentInCheckAfterMove() => IsOpposingKingThreatenedByPosition(boardState.PreviousMove.To);
- internal bool IsOpposingKingThreatenedByPosition(Vector2 position)
- {
- var previousMovedPiece = boardState[position];
- if (previousMovedPiece == null) return false;
+ internal bool IsOpposingKingThreatenedByPosition(Vector2 position)
+ {
+ var previousMovedPiece = boardState[position];
+ if (previousMovedPiece == null) return false;
- var kingPosition = previousMovedPiece.Owner == WhichPlayer.Player1 ? boardState.Player2KingPosition : boardState.Player1KingPosition;
- var path = previousMovedPiece.GetPathFromStartToEnd(position, kingPosition);
- var threatenedPiece = boardState.QueryFirstPieceInPath(path);
- if (!path.Any() || threatenedPiece == null) return false;
+ var kingPosition = previousMovedPiece.Owner == WhichPlayer.Player1 ? boardState.Player2KingPosition : boardState.Player1KingPosition;
+ var path = previousMovedPiece.GetPathFromStartToEnd(position, kingPosition);
+ var threatenedPiece = boardState.QueryFirstPieceInPath(path);
+ if (!path.Any() || threatenedPiece == null) return false;
- return threatenedPiece.WhichPiece == WhichPiece.King;
- }
+ return threatenedPiece.WhichPiece == WhichPiece.King;
+ }
- internal bool IsOpponentInCheckMate()
- {
- // Assume checkmate, then try to disprove.
- if (!boardState.InCheck.HasValue) return false;
- // Get all pieces from opponent who threaten the king in question.
- var opponent = boardState.WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
- var tilesOccupiedByOpponent = boardState.GetTilesOccupiedBy(opponent);
- var kingPosition = boardState.WhoseTurn == WhichPlayer.Player1
- ? boardState.Player1KingPosition
- : boardState.Player2KingPosition;
- var threats = tilesOccupiedByOpponent.Where(tile => PieceHasLineOfSight(tile, kingPosition)).ToList();
- if (threats.Count == 1)
- {
- /* If there is exactly one threat it is possible to block the check.
- * Foreach piece owned by whichPlayer
- * if piece can intercept check, return false;
- */
- var threat = threats.Single();
- var pathFromThreatToKing = threat.Value.GetPathFromStartToEnd(threat.Key, kingPosition);
- var tilesThatCouldBlockTheThreat = boardState.GetTilesOccupiedBy(boardState.WhoseTurn);
- foreach (var threatBlockingPosition in pathFromThreatToKing)
- {
- var tilesThatDoBlockThreat = tilesThatCouldBlockTheThreat
- .Where(tile => PieceHasLineOfSight(tile, threatBlockingPosition))
- .ToList();
+ internal bool IsOpponentInCheckMate()
+ {
+ // Assume checkmate, then try to disprove.
+ if (!boardState.InCheck.HasValue) return false;
+ // Get all pieces from opponent who threaten the king in question.
+ var opponent = boardState.WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
+ var tilesOccupiedByOpponent = boardState.GetTilesOccupiedBy(opponent);
+ var kingPosition = boardState.WhoseTurn == WhichPlayer.Player1
+ ? boardState.Player1KingPosition
+ : boardState.Player2KingPosition;
+ var threats = tilesOccupiedByOpponent.Where(tile => PieceHasLineOfSight(tile, kingPosition)).ToList();
+ if (threats.Count == 1)
+ {
+ /* If there is exactly one threat it is possible to block the check.
+ * Foreach piece owned by whichPlayer
+ * if piece can intercept check, return false;
+ */
+ var threat = threats.Single();
+ var pathFromThreatToKing = threat.Value.GetPathFromStartToEnd(threat.Key, kingPosition);
+ var tilesThatCouldBlockTheThreat = boardState.GetTilesOccupiedBy(boardState.WhoseTurn);
+ foreach (var threatBlockingPosition in pathFromThreatToKing)
+ {
+ var tilesThatDoBlockThreat = tilesThatCouldBlockTheThreat
+ .Where(tile => PieceHasLineOfSight(tile, threatBlockingPosition))
+ .ToList();
- if (tilesThatDoBlockThreat.Any())
- {
- return false; // Cannot be check-mate if a piece can intercept the threat.
- }
- }
- }
- else
- {
- /*
- * If no ability to block the check, maybe the king can evade check by moving.
- */
+ if (tilesThatDoBlockThreat.Any())
+ {
+ return false; // Cannot be check-mate if a piece can intercept the threat.
+ }
+ }
+ }
+ else
+ {
+ /*
+ * If no ability to block the check, maybe the king can evade check by moving.
+ */
- foreach (var maybeSafePosition in GetPossiblePositionsForKing(this.boardState.WhoseTurn))
- {
- threats = tilesOccupiedByOpponent
- .Where(tile => PieceHasLineOfSight(tile, maybeSafePosition))
- .ToList();
- if (!threats.Any())
- {
- return false;
- }
- }
+ foreach (var maybeSafePosition in GetPossiblePositionsForKing(this.boardState.WhoseTurn))
+ {
+ threats = tilesOccupiedByOpponent
+ .Where(tile => PieceHasLineOfSight(tile, maybeSafePosition))
+ .ToList();
+ if (!threats.Any())
+ {
+ return false;
+ }
+ }
- }
+ }
- return true;
- }
+ return true;
+ }
- private IList GetPossiblePositionsForKing(WhichPlayer whichPlayer)
- {
- var kingPosition = whichPlayer == WhichPlayer.Player1
- ? boardState.Player1KingPosition
- : boardState.Player2KingPosition;
+ private IList GetPossiblePositionsForKing(WhichPlayer whichPlayer)
+ {
+ var kingPosition = whichPlayer == WhichPlayer.Player1
+ ? boardState.Player1KingPosition
+ : boardState.Player2KingPosition;
- return King.KingPaths
- .Select(path => path.Direction + kingPosition)
- .Where(newPosition => newPosition.IsInsideBoardBoundary())
- // Where tile at position is empty, meaning the king could move there.
- .Where(newPosition => boardState[newPosition] == null)
- .ToList();
- }
+ return King.KingPaths
+ .Select(path => path.Direction + kingPosition)
+ .Where(newPosition => newPosition.IsInsideBoardBoundary())
+ // Where tile at position is empty, meaning the king could move there.
+ .Where(newPosition => boardState[newPosition] == null)
+ .ToList();
+ }
- private bool PieceHasLineOfSight(BoardTile tile, Vector2 lineOfSightTarget)
- {
- var path = tile.Value.GetPathFromStartToEnd(tile.Key, lineOfSightTarget);
- return path
- .SkipLast(1)
- .All(position => boardState[Notation.ToBoardNotation(position)] == null);
- }
- }
+ private bool PieceHasLineOfSight(BoardTile tile, Vector2 lineOfSightTarget)
+ {
+ var path = tile.Value.GetPathFromStartToEnd(tile.Key, lineOfSightTarget);
+ return path
+ .SkipLast(1)
+ .All(position => boardState[Notation.ToBoardNotation(position)] == null);
+ }
+ }
}
diff --git a/Shogi.Domain/ValueObjects/Move.cs b/Shogi.Domain/ValueObjects/Move.cs
index 384c344..a9d7e45 100644
--- a/Shogi.Domain/ValueObjects/Move.cs
+++ b/Shogi.Domain/ValueObjects/Move.cs
@@ -3,6 +3,20 @@
///
/// Represents a single piece being moved by a player from to .
///
-public record struct Move(Vector2 From, Vector2 To)
+public readonly record struct Move
{
+ public Move(Vector2 from, Vector2 to)
+ {
+ From = from;
+ To = to;
+ }
+ public Move(WhichPiece pieceFromHand, Vector2 to)
+ {
+ PieceFromHand = pieceFromHand;
+ To = to;
+ }
+
+ public Vector2? From { get; }
+ public Vector2 To { get; }
+ public WhichPiece PieceFromHand { get; }
}
diff --git a/Shogi.Domain/ValueObjects/ShogiBoard.cs b/Shogi.Domain/ValueObjects/ShogiBoard.cs
index cd4923e..116a962 100644
--- a/Shogi.Domain/ValueObjects/ShogiBoard.cs
+++ b/Shogi.Domain/ValueObjects/ShogiBoard.cs
@@ -44,12 +44,14 @@ public sealed class ShogiBoard
throw new InvalidOperationException("Unable to move because you are still in check.");
}
- var otherPlayer = BoardState.WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
- if (simulation.IsPlayerInCheckAfterMove())
+ if (simulation.DidPlayerPutThemselfInCheck())
{
throw new InvalidOperationException("Illegal move. This move places you in check.");
}
+ var otherPlayer = BoardState.WhoseTurn == WhichPlayer.Player1
+ ? WhichPlayer.Player2
+ : WhichPlayer.Player1;
_ = rules.Move(from, to, isPromotion);
if (rules.IsOpponentInCheckAfterMove())
{
@@ -113,31 +115,36 @@ public sealed class ShogiBoard
throw new InvalidOperationException(moveResult.Reason);
}
- var otherPlayer = tempBoard.WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
- if (BoardState.InCheck == BoardState.WhoseTurn)
+ // If already in check, assert the move that resulted in check no longer results in check.
+ if (BoardState.InCheck == BoardState.WhoseTurn
+ && simulation.IsOpposingKingThreatenedByPosition(BoardState.PreviousMove.To))
{
- //if (simulation.IsPlayerInCheckAfterMove(boardState.PreviousMoveTo, toVector, boardState.WhoseTurn))
- //{
- // throw new InvalidOperationException("Illegal move. You're still in check!");
- //}
+ throw new InvalidOperationException("Unable to drop piece becauase you are still in check.");
}
- var kingPosition = otherPlayer == WhichPlayer.Player1 ? tempBoard.Player1KingPosition : tempBoard.Player2KingPosition;
- //if (simulation.IsPlayerInCheckAfterMove(toVector, kingPosition, otherPlayer))
- //{
+ if (simulation.DidPlayerPutThemselfInCheck())
+ {
+ throw new InvalidOperationException("Illegal move. This move places you in check.");
+ }
- //}
+ // Update the non-simulation board.
+ var otherPlayer = tempBoard.WhoseTurn == WhichPlayer.Player1
+ ? WhichPlayer.Player2
+ : WhichPlayer.Player1;
+ _ = rules.Move(pieceInHand, to);
+ if (rules.IsOpponentInCheckAfterMove())
+ {
+ BoardState.InCheck = otherPlayer;
+ // A pawn, placed from the hand, cannot be the cause of checkmate.
+ if (rules.IsOpponentInCheckMate() && pieceInHand != WhichPiece.Pawn)
+ {
+ BoardState.IsCheckmate = true;
+ }
+ }
- //rules.Move(from, to, isPromotion);
- //if (rules.IsPlayerInCheckAfterMove(fromVector, toVector, otherPlayer))
- //{
- // board.InCheck = otherPlayer;
- // board.IsCheckmate = rules.EvaluateCheckmate();
- //}
- //else
- //{
- // board.InCheck = null;
- //}
+ var kingPosition = otherPlayer == WhichPlayer.Player1
+ ? tempBoard.Player1KingPosition
+ : tempBoard.Player2KingPosition;
BoardState.WhoseTurn = otherPlayer;
}
@@ -149,7 +156,7 @@ public sealed class ShogiBoard
{
var builder = new StringBuilder();
builder.Append(" ");
- builder.Append("Player 2(.)");
+ builder.Append("Player 2");
builder.AppendLine();
for (var rank = 8; rank >= 0; rank--)
{
diff --git a/Tests/UnitTests/ShogiShould.cs b/Tests/UnitTests/ShogiShould.cs
index dc17499..640fb56 100644
--- a/Tests/UnitTests/ShogiShould.cs
+++ b/Tests/UnitTests/ShogiShould.cs
@@ -1,5 +1,6 @@
using Shogi.Domain.ValueObjects;
using System;
+using System.Linq;
namespace Shogi.Domain.UnitTests
{
@@ -299,15 +300,16 @@ namespace Shogi.Domain.UnitTests
shogi.Move("A7", "A6", false);
// P1 drop Bishop, place P2 in check
shogi.Move(WhichPiece.Bishop, "G7");
-
shogi.BoardState.InCheck.Should().Be(WhichPlayer.Player2);
shogi.BoardState.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
shogi.BoardState["E5"].Should().BeNull();
// Act - P2 places a Bishop while in check.
- shogi.Move(WhichPiece.Bishop, "E5");
+ var act = () => shogi.Move(WhichPiece.Bishop, "E5");
// Assert
+ using var scope = new AssertionScope();
+ act.Should().Throw();
shogi.BoardState["E5"].Should().BeNull();
shogi.BoardState.InCheck.Should().Be(WhichPlayer.Player2);
shogi.BoardState.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
@@ -326,25 +328,21 @@ namespace Shogi.Domain.UnitTests
shogi.Move("B2", "H8", false);
// P2 Pawn
shogi.Move("G6", "G5", false);
- using (new AssertionScope())
- {
- shogi.BoardState.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
- shogi.BoardState["I9"].Should().NotBeNull();
- shogi.BoardState["I9"]!.WhichPiece.Should().Be(WhichPiece.Lance);
- shogi.BoardState["I9"]!.Owner.Should().Be(WhichPlayer.Player2);
- }
+ shogi.BoardState.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
+ shogi.BoardState["I9"].Should().NotBeNull();
+ shogi.BoardState["I9"]!.WhichPiece.Should().Be(WhichPiece.Lance);
+ shogi.BoardState["I9"]!.Owner.Should().Be(WhichPlayer.Player2);
// Act - P1 tries to place a piece where an opponent's piece resides.
- shogi.Move(WhichPiece.Bishop, "I9");
+ var act = () => shogi.Move(WhichPiece.Bishop, "I9");
// Assert
- using (new AssertionScope())
- {
- shogi.BoardState.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
- shogi.BoardState["I9"].Should().NotBeNull();
- shogi.BoardState["I9"]!.WhichPiece.Should().Be(WhichPiece.Lance);
- shogi.BoardState["I9"]!.Owner.Should().Be(WhichPlayer.Player2);
- }
+ using var scope = new AssertionScope();
+ act.Should().Throw();
+ shogi.BoardState.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
+ shogi.BoardState["I9"].Should().NotBeNull();
+ shogi.BoardState["I9"]!.WhichPiece.Should().Be(WhichPiece.Lance);
+ shogi.BoardState["I9"]!.Owner.Should().Be(WhichPlayer.Player2);
}
[Fact]
@@ -446,6 +444,7 @@ namespace Shogi.Domain.UnitTests
// Assert - checkmate
console.WriteLine(shogi.ToStringStateAsAscii());
+ console.WriteLine(string.Join(",", shogi.BoardState.Player1Hand.Select(p => p.WhichPiece.ToString())));
board.IsCheckmate.Should().BeTrue();
board.InCheck.Should().Be(WhichPlayer.Player2);
}