checkpoint

This commit is contained in:
2025-09-05 18:13:35 -05:00
parent e2a8b771d9
commit 0a415a2292
24 changed files with 622 additions and 492 deletions

View File

@@ -1,26 +1,25 @@
using System.Numerics;
using Shogi.Domain.YetToBeAssimilatedIntoDDD;
using Shogi.Domain.YetToBeAssimilatedIntoDDD;
using System.Numerics;
namespace UnitTests
namespace UnitTests;
public class NotationShould
{
public class NotationShould
[Fact]
public void ConvertFromNotationToVector()
{
[Fact]
public void ConvertFromNotationToVector()
{
Notation.FromBoardNotation("A1").Should().Be(new Vector2(0, 0));
Notation.FromBoardNotation("E5").Should().Be(new Vector2(4, 4));
Notation.FromBoardNotation("I9").Should().Be(new Vector2(8, 8));
Notation.FromBoardNotation("C3").Should().Be(new Vector2(2, 2));
}
Notation.FromBoardNotation("A1").Should().Be(new Vector2(0, 0));
Notation.FromBoardNotation("E5").Should().Be(new Vector2(4, 4));
Notation.FromBoardNotation("I9").Should().Be(new Vector2(8, 8));
Notation.FromBoardNotation("C3").Should().Be(new Vector2(2, 2));
}
[Fact]
public void ConvertFromVectorToNotation()
{
Notation.ToBoardNotation(new Vector2(0, 0)).Should().Be("A1");
Notation.ToBoardNotation(new Vector2(4, 4)).Should().Be("E5");
Notation.ToBoardNotation(new Vector2(8, 8)).Should().Be("I9");
Notation.ToBoardNotation(new Vector2(2, 2)).Should().Be("C3");
}
[Fact]
public void ConvertFromVectorToNotation()
{
Notation.ToBoardNotation(new Vector2(0, 0)).Should().Be("A1");
Notation.ToBoardNotation(new Vector2(4, 4)).Should().Be("E5");
Notation.ToBoardNotation(new Vector2(8, 8)).Should().Be("I9");
Notation.ToBoardNotation(new Vector2(2, 2)).Should().Be("C3");
}
}

View File

@@ -1,5 +1,5 @@
using Shogi.Domain.ValueObjects;
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
using Shogi.Domain.ValueObjects.Movement;
using System.Numerics;
namespace UnitTests;

View File

@@ -1,460 +1,457 @@
using Shogi.Domain.ValueObjects;
using System;
using System.Linq;
namespace UnitTests
namespace UnitTests;
public class ShogiShould
{
public class ShogiShould
private readonly ITestOutputHelper console;
public ShogiShould(ITestOutputHelper console)
{
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())
{
this.console = console;
board.InCheck.Should().BeNull();
}
}
[Fact]
public void MoveAPieceToAnEmptyPosition()
[Fact]
public void PreventInvalidMoves_MoveFromEmptyPosition()
{
// Arrange
var shogi = MockShogiBoard();
var board = shogi.BoardState;
board["D5"].Should().BeNull();
// Act
var moveResult = shogi.Move("D5", "D6", false);
// Assert
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
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 moveResult = shogi.Move("A3", "A3", false);
// Assert
using (new AssertionScope())
{
// 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 moveResult = shogi.Move("D5", "D6", false);
// Assert
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
board["D5"].Should().BeNull();
board["D6"].Should().BeNull();
moveResult.IsSuccess.Should().BeFalse(); board["A3"].Should().Be(expectedPiece);
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 moveResult = shogi.Move("A3", "A3", false);
// Assert
using (new AssertionScope())
{
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse(); 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["D1"];
expectedPiece!.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
// Act - Move General illegally
var moveResult = shogi.Move("D1", "D5", false);
// Assert
using (new AssertionScope())
{
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
board["D1"].Should().Be(expectedPiece);
board["D5"].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 moveResult = shogi.Move("A7", "A6", false);
// Assert
using (new AssertionScope())
{
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse(); 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 moveResult = shogi.Move("A1", "A5", false);
// Assert
using (new AssertionScope())
{
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse(); 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 moveResult = shogi.Move("B1", "C3", false);
// Arrange
using (new AssertionScope())
{
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse(); 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 moveResult = shogi.Move("I9", "I8", false);
// Assert
using (new AssertionScope())
{
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse(); board.InCheck.Should().Be(WhichPlayer.Player2);
board["I9"].Should().Be(lance);
board["I8"].Should().BeNull();
}
}
[Fact]
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 moveResult = shogi.Move(WhichPiece.Knight, "H9");
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
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();
moveResult = shogi.Move(WhichPiece.Knight, "H8");
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
board["H8"].Should().BeNull();
board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight);
// Act | Assert - Illegally place Lance from the hand.
board["H9"].Should().BeNull();
moveResult = shogi.Move(WhichPiece.Knight, "H9");
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
board["H9"].Should().BeNull();
board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Lance);
// Act | Assert - Illegally place Pawn from the hand.
board["H9"].Should().BeNull();
moveResult = shogi.Move(WhichPiece.Pawn, "H9");
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
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 shogi = MockShogiBoard();
// P1 Pawn
shogi.Move("C3", "C4", false).IsSuccess.Should().BeTrue();
// P2 Pawn
shogi.Move("G7", "G6", false).IsSuccess.Should().BeTrue();
// P1 Pawn, arbitrary move.
shogi.Move("A3", "A4", false).IsSuccess.Should().BeTrue();
// P2 Bishop takes P1 Bishop
shogi.Move("H8", "B2", false).IsSuccess.Should().BeTrue();
// P1 Silver takes P2 Bishop
shogi.Move("C1", "B2", false).IsSuccess.Should().BeTrue();
// P2 Pawn, arbtrary move
shogi.Move("A7", "A6", false).IsSuccess.Should().BeTrue();
// P1 drop Bishop, place P2 in check
shogi.Move(WhichPiece.Bishop, "G7").IsSuccess.Should().BeTrue();
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.
var moveResult = shogi.Move(WhichPiece.Bishop, "E5");
// Assert
using var scope = new AssertionScope();
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
shogi.BoardState["E5"].Should().BeNull();
shogi.BoardState.InCheck.Should().Be(WhichPlayer.Player2);
shogi.BoardState.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
}
[Fact]
public void PreventInvalidDrop_Capture()
{
// Arrange
var shogi = MockShogiBoard();
// P1 Pawn
shogi.Move("C3", "C4", false);
// P2 Pawn
shogi.Move("G7", "G6", false);
// P1 Bishop capture P2 Bishop
shogi.Move("B2", "H8", false);
// P2 Pawn
shogi.Move("G6", "G5", false);
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.
var moveResult = shogi.Move(WhichPiece.Bishop, "I9");
// Assert
using var scope = new AssertionScope();
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
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]
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
board.IsCheckmate.Should().BeTrue();
board.InCheck.Should().Be(WhichPlayer.Player2);
}
private static ShogiBoard MockShogiBoard() => new(BoardState.StandardStarting);
}
[Fact]
public void PreventInvalidMoves_MoveSet()
{
// Arrange
var shogi = MockShogiBoard();
var board = shogi.BoardState;
var expectedPiece = board["D1"];
expectedPiece!.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
// Act - Move General illegally
var moveResult = shogi.Move("D1", "D5", false);
// Assert
using (new AssertionScope())
{
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
board["D1"].Should().Be(expectedPiece);
board["D5"].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 moveResult = shogi.Move("A7", "A6", false);
// Assert
using (new AssertionScope())
{
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse(); 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 moveResult = shogi.Move("A1", "A5", false);
// Assert
using (new AssertionScope())
{
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse(); 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 moveResult = shogi.Move("B1", "C3", false);
// Arrange
using (new AssertionScope())
{
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse(); 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 moveResult = shogi.Move("I9", "I8", false);
// Assert
using (new AssertionScope())
{
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse(); board.InCheck.Should().Be(WhichPlayer.Player2);
board["I9"].Should().Be(lance);
board["I8"].Should().BeNull();
}
}
[Fact]
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 moveResult = shogi.Move(WhichPiece.Knight, "H9");
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
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();
moveResult = shogi.Move(WhichPiece.Knight, "H8");
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
board["H8"].Should().BeNull();
board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight);
// Act | Assert - Illegally place Lance from the hand.
board["H9"].Should().BeNull();
moveResult = shogi.Move(WhichPiece.Knight, "H9");
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
board["H9"].Should().BeNull();
board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Lance);
// Act | Assert - Illegally place Pawn from the hand.
board["H9"].Should().BeNull();
moveResult = shogi.Move(WhichPiece.Pawn, "H9");
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
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 shogi = MockShogiBoard();
// P1 Pawn
shogi.Move("C3", "C4", false).IsSuccess.Should().BeTrue();
// P2 Pawn
shogi.Move("G7", "G6", false).IsSuccess.Should().BeTrue();
// P1 Pawn, arbitrary move.
shogi.Move("A3", "A4", false).IsSuccess.Should().BeTrue();
// P2 Bishop takes P1 Bishop
shogi.Move("H8", "B2", false).IsSuccess.Should().BeTrue();
// P1 Silver takes P2 Bishop
shogi.Move("C1", "B2", false).IsSuccess.Should().BeTrue();
// P2 Pawn, arbtrary move
shogi.Move("A7", "A6", false).IsSuccess.Should().BeTrue();
// P1 drop Bishop, place P2 in check
shogi.Move(WhichPiece.Bishop, "G7").IsSuccess.Should().BeTrue();
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.
var moveResult = shogi.Move(WhichPiece.Bishop, "E5");
// Assert
using var scope = new AssertionScope();
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
shogi.BoardState["E5"].Should().BeNull();
shogi.BoardState.InCheck.Should().Be(WhichPlayer.Player2);
shogi.BoardState.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
}
[Fact]
public void PreventInvalidDrop_Capture()
{
// Arrange
var shogi = MockShogiBoard();
// P1 Pawn
shogi.Move("C3", "C4", false);
// P2 Pawn
shogi.Move("G7", "G6", false);
// P1 Bishop capture P2 Bishop
shogi.Move("B2", "H8", false);
// P2 Pawn
shogi.Move("G6", "G5", false);
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.
var moveResult = shogi.Move(WhichPiece.Bishop, "I9");
// Assert
using var scope = new AssertionScope();
moveResult.Should().NotBeNull();
moveResult.IsSuccess.Should().BeFalse();
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]
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
board.IsCheckmate.Should().BeTrue();
board.InCheck.Should().Be(WhichPlayer.Player2);
}
private static ShogiBoard MockShogiBoard() => new(BoardState.StandardStarting);
}