Files
Shogi/Gameboard.ShogiUI.xUnitTests/ShogiShould.cs
2021-11-21 10:07:35 -06:00

621 lines
20 KiB
C#

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);
}
}
}