squash a bunch of commits

This commit is contained in:
2022-10-30 12:03:16 -05:00
parent 09b72c1858
commit 93027e8c57
222 changed files with 6157 additions and 3201 deletions

View File

@@ -0,0 +1,25 @@
using System.Numerics;
namespace Shogi.Domain.UnitTests
{
public class NotationShould
{
[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));
}
[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

@@ -0,0 +1,225 @@
using Shogi.Domain.Pathing;
using Shogi.Domain.ValueObjects;
using System.Numerics;
namespace Shogi.Domain.UnitTests
{
public class RookShould
{
public class MoveSet
{
private readonly Rook rook1;
private readonly Rook rook2;
public MoveSet()
{
this.rook1 = new Rook(WhichPlayer.Player1);
this.rook2 = new Rook(WhichPlayer.Player2);
}
[Fact]
public void Player1_HasCorrectMoveSet()
{
var moveSet = rook1.MoveSet;
moveSet.Should().HaveCount(4);
moveSet.Should().ContainEquivalentOf(new Path(Direction.Up, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Left, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Right, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Down, Distance.MultiStep));
}
[Fact]
public void Player1_Promoted_HasCorrectMoveSet()
{
// Arrange
rook1.Promote();
rook1.IsPromoted.Should().BeTrue();
// Assert
var moveSet = rook1.MoveSet;
moveSet.Should().HaveCount(8);
moveSet.Should().ContainEquivalentOf(new Path(Direction.Up, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Left, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Right, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Down, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.UpLeft, Distance.OneStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.DownLeft, Distance.OneStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.UpRight, Distance.OneStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.DownRight, Distance.OneStep));
}
[Fact]
public void Player2_HasCorrectMoveSet()
{
var moveSet = rook2.MoveSet;
moveSet.Should().HaveCount(4);
moveSet.Should().ContainEquivalentOf(new Path(Direction.Up, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Left, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Right, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Down, Distance.MultiStep));
}
[Fact]
public void Player2_Promoted_HasCorrectMoveSet()
{
// Arrange
rook2.Promote();
rook2.IsPromoted.Should().BeTrue();
// Assert
var moveSet = rook2.MoveSet;
moveSet.Should().HaveCount(8);
moveSet.Should().ContainEquivalentOf(new Path(Direction.Up, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Left, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Right, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.Down, Distance.MultiStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.UpLeft, Distance.OneStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.DownLeft, Distance.OneStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.UpRight, Distance.OneStep));
moveSet.Should().ContainEquivalentOf(new Path(Direction.DownRight, Distance.OneStep));
}
}
private readonly Rook rookPlayer1;
private readonly Rook rookPlayer2;
public RookShould()
{
this.rookPlayer1 = new Rook(WhichPlayer.Player1);
this.rookPlayer2 = new Rook(WhichPlayer.Player2);
}
[Fact]
public void Promote()
{
this.rookPlayer1.IsPromoted.Should().BeFalse();
this.rookPlayer1.CanPromote.Should().BeTrue();
this.rookPlayer1.Promote();
this.rookPlayer1.IsPromoted.Should().BeTrue();
this.rookPlayer1.CanPromote.Should().BeFalse();
}
[Fact]
public void GetStepsFromStartToEnd_Player1NotPromoted_LateralMove()
{
Vector2 start = new(0, 0);
Vector2 end = new(0, 5);
var steps = rookPlayer1.GetPathFromStartToEnd(start, end);
rookPlayer1.IsPromoted.Should().BeFalse();
steps.Should().HaveCount(5);
steps.Should().Contain(new Vector2(0, 1));
steps.Should().Contain(new Vector2(0, 2));
steps.Should().Contain(new Vector2(0, 3));
steps.Should().Contain(new Vector2(0, 4));
steps.Should().Contain(new Vector2(0, 5));
}
[Fact]
public void GetStepsFromStartToEnd_Player1NotPromoted_DiagonalMove()
{
Vector2 start = new(0, 0);
Vector2 end = new(1, 1);
var steps = rookPlayer1.GetPathFromStartToEnd(start, end);
rookPlayer1.IsPromoted.Should().BeFalse();
steps.Should().BeEmpty();
}
[Fact]
public void GetStepsFromStartToEnd_Player1Promoted_LateralMove()
{
Vector2 start = new(0, 0);
Vector2 end = new(0, 5);
rookPlayer1.Promote();
var steps = rookPlayer1.GetPathFromStartToEnd(start, end);
rookPlayer1.IsPromoted.Should().BeTrue();
steps.Should().HaveCount(5);
steps.Should().Contain(new Vector2(0, 1));
steps.Should().Contain(new Vector2(0, 2));
steps.Should().Contain(new Vector2(0, 3));
steps.Should().Contain(new Vector2(0, 4));
steps.Should().Contain(new Vector2(0, 5));
}
[Fact]
public void GetStepsFromStartToEnd_Player1Promoted_DiagonalMove()
{
Vector2 start = new(0, 0);
Vector2 end = new(1, 1);
rookPlayer1.Promote();
var steps = rookPlayer1.GetPathFromStartToEnd(start, end);
rookPlayer1.IsPromoted.Should().BeTrue();
steps.Should().HaveCount(1);
steps.Should().Contain(new Vector2(1, 1));
}
[Fact]
public void GetStepsFromStartToEnd_Player2NotPromoted_LateralMove()
{
Vector2 start = new(0, 0);
Vector2 end = new(0, 5);
var steps = rookPlayer1.GetPathFromStartToEnd(start, end);
rookPlayer1.IsPromoted.Should().BeFalse();
steps.Should().HaveCount(5);
steps.Should().Contain(new Vector2(0, 1));
steps.Should().Contain(new Vector2(0, 2));
steps.Should().Contain(new Vector2(0, 3));
steps.Should().Contain(new Vector2(0, 4));
steps.Should().Contain(new Vector2(0, 5));
}
[Fact]
public void GetStepsFromStartToEnd_Player2NotPromoted_DiagonalMove()
{
Vector2 start = new(0, 0);
Vector2 end = new(1, 1);
var steps = rookPlayer1.GetPathFromStartToEnd(start, end);
rookPlayer1.IsPromoted.Should().BeFalse();
steps.Should().BeEmpty();
}
[Fact]
public void GetStepsFromStartToEnd_Player2Promoted_LateralMove()
{
Vector2 start = new(0, 0);
Vector2 end = new(0, 5);
rookPlayer1.Promote();
var steps = rookPlayer1.GetPathFromStartToEnd(start, end);
rookPlayer1.IsPromoted.Should().BeTrue();
steps.Should().HaveCount(5);
steps.Should().Contain(new Vector2(0, 1));
steps.Should().Contain(new Vector2(0, 2));
steps.Should().Contain(new Vector2(0, 3));
steps.Should().Contain(new Vector2(0, 4));
steps.Should().Contain(new Vector2(0, 5));
}
[Fact]
public void GetStepsFromStartToEnd_Player2Promoted_DiagonalMove()
{
Vector2 start = new(0, 0);
Vector2 end = new(1, 1);
rookPlayer1.Promote();
var steps = rookPlayer1.GetPathFromStartToEnd(start, end);
rookPlayer1.IsPromoted.Should().BeTrue();
steps.Should().HaveCount(1);
steps.Should().Contain(new Vector2(1, 1));
}
}
}

View File

@@ -0,0 +1,25 @@
using Newtonsoft.Json;
using Shogi.Contracts.Api;
using Shogi.Contracts.Types;
using System;
namespace UnitTests.ServiceModels;
public class ServiceModelsShouldSerialize
{
[Fact]
public void ServiceModelsSerializeAndDeserialize()
{
using var scope = new AssertionScope();
AssertTypeDeserializes(new CreateGuestTokenResponse("MyId", "MyDisplayName", Guid.NewGuid()));
AssertTypeDeserializes(new SessionMetadata { Name = "MyName", PlayerCount = 5 });
}
private static void AssertTypeDeserializes<T>(T t) where T : class
{
var serialized = JsonConvert.SerializeObject(t);
var deserialized = JsonConvert.DeserializeObject<T>(serialized);
deserialized.Should().BeEquivalentTo(t);
}
}

View File

@@ -0,0 +1,186 @@
using FluentAssertions;
using Xunit;
namespace Shogi.Domain.UnitTests
{
public class ShogiBoardStateShould
{
[Fact]
public void InitializeBoardState()
{
// Act
var board = new BoardState();
// Assert
board["A1"]?.WhichPiece.Should().Be(WhichPiece.Lance);
board["A1"]?.Owner.Should().Be(WhichPlayer.Player1);
board["A1"]?.IsPromoted.Should().Be(false);
board["B1"]?.WhichPiece.Should().Be(WhichPiece.Knight);
board["B1"]?.Owner.Should().Be(WhichPlayer.Player1);
board["B1"]?.IsPromoted.Should().Be(false);
board["C1"]?.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["C1"]?.Owner.Should().Be(WhichPlayer.Player1);
board["C1"]?.IsPromoted.Should().Be(false);
board["D1"]?.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["D1"]?.Owner.Should().Be(WhichPlayer.Player1);
board["D1"]?.IsPromoted.Should().Be(false);
board["E1"]?.WhichPiece.Should().Be(WhichPiece.King);
board["E1"]?.Owner.Should().Be(WhichPlayer.Player1);
board["E1"]?.IsPromoted.Should().Be(false);
board["F1"]?.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["F1"]?.Owner.Should().Be(WhichPlayer.Player1);
board["F1"]?.IsPromoted.Should().Be(false);
board["G1"]?.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["G1"]?.Owner.Should().Be(WhichPlayer.Player1);
board["G1"]?.IsPromoted.Should().Be(false);
board["H1"]?.WhichPiece.Should().Be(WhichPiece.Knight);
board["H1"]?.Owner.Should().Be(WhichPlayer.Player1);
board["H1"]?.IsPromoted.Should().Be(false);
board["I1"]?.WhichPiece.Should().Be(WhichPiece.Lance);
board["I1"]?.Owner.Should().Be(WhichPlayer.Player1);
board["I1"]?.IsPromoted.Should().Be(false);
board["A2"].Should().BeNull();
board["B2"]?.WhichPiece.Should().Be(WhichPiece.Bishop);
board["B2"]?.Owner.Should().Be(WhichPlayer.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(WhichPlayer.Player1);
board["H2"]?.IsPromoted.Should().Be(false);
board["I2"].Should().BeNull();
board["A3"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["A3"]?.Owner.Should().Be(WhichPlayer.Player1);
board["A3"]?.IsPromoted.Should().Be(false);
board["B3"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["B3"]?.Owner.Should().Be(WhichPlayer.Player1);
board["B3"]?.IsPromoted.Should().Be(false);
board["C3"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["C3"]?.Owner.Should().Be(WhichPlayer.Player1);
board["C3"]?.IsPromoted.Should().Be(false);
board["D3"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["D3"]?.Owner.Should().Be(WhichPlayer.Player1);
board["D3"]?.IsPromoted.Should().Be(false);
board["E3"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["E3"]?.Owner.Should().Be(WhichPlayer.Player1);
board["E3"]?.IsPromoted.Should().Be(false);
board["F3"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["F3"]?.Owner.Should().Be(WhichPlayer.Player1);
board["F3"]?.IsPromoted.Should().Be(false);
board["G3"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["G3"]?.Owner.Should().Be(WhichPlayer.Player1);
board["G3"]?.IsPromoted.Should().Be(false);
board["H3"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["H3"]?.Owner.Should().Be(WhichPlayer.Player1);
board["H3"]?.IsPromoted.Should().Be(false);
board["I3"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["I3"]?.Owner.Should().Be(WhichPlayer.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(WhichPlayer.Player2);
board["A7"]?.IsPromoted.Should().Be(false);
board["B7"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["B7"]?.Owner.Should().Be(WhichPlayer.Player2);
board["B7"]?.IsPromoted.Should().Be(false);
board["C7"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["C7"]?.Owner.Should().Be(WhichPlayer.Player2);
board["C7"]?.IsPromoted.Should().Be(false);
board["D7"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["D7"]?.Owner.Should().Be(WhichPlayer.Player2);
board["D7"]?.IsPromoted.Should().Be(false);
board["E7"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["E7"]?.Owner.Should().Be(WhichPlayer.Player2);
board["E7"]?.IsPromoted.Should().Be(false);
board["F7"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["F7"]?.Owner.Should().Be(WhichPlayer.Player2);
board["F7"]?.IsPromoted.Should().Be(false);
board["G7"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["G7"]?.Owner.Should().Be(WhichPlayer.Player2);
board["G7"]?.IsPromoted.Should().Be(false);
board["H7"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["H7"]?.Owner.Should().Be(WhichPlayer.Player2);
board["H7"]?.IsPromoted.Should().Be(false);
board["I7"]?.WhichPiece.Should().Be(WhichPiece.Pawn);
board["I7"]?.Owner.Should().Be(WhichPlayer.Player2);
board["I7"]?.IsPromoted.Should().Be(false);
board["A8"].Should().BeNull();
board["B8"]?.WhichPiece.Should().Be(WhichPiece.Rook);
board["B8"]?.Owner.Should().Be(WhichPlayer.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(WhichPlayer.Player2);
board["H8"]?.IsPromoted.Should().Be(false);
board["I8"].Should().BeNull();
board["A9"]?.WhichPiece.Should().Be(WhichPiece.Lance);
board["A9"]?.Owner.Should().Be(WhichPlayer.Player2);
board["A9"]?.IsPromoted.Should().Be(false);
board["B9"]?.WhichPiece.Should().Be(WhichPiece.Knight);
board["B9"]?.Owner.Should().Be(WhichPlayer.Player2);
board["B9"]?.IsPromoted.Should().Be(false);
board["C9"]?.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["C9"]?.Owner.Should().Be(WhichPlayer.Player2);
board["C9"]?.IsPromoted.Should().Be(false);
board["D9"]?.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["D9"]?.Owner.Should().Be(WhichPlayer.Player2);
board["D9"]?.IsPromoted.Should().Be(false);
board["E9"]?.WhichPiece.Should().Be(WhichPiece.King);
board["E9"]?.Owner.Should().Be(WhichPlayer.Player2);
board["E9"]?.IsPromoted.Should().Be(false);
board["F9"]?.WhichPiece.Should().Be(WhichPiece.GoldGeneral);
board["F9"]?.Owner.Should().Be(WhichPlayer.Player2);
board["F9"]?.IsPromoted.Should().Be(false);
board["G9"]?.WhichPiece.Should().Be(WhichPiece.SilverGeneral);
board["G9"]?.Owner.Should().Be(WhichPlayer.Player2);
board["G9"]?.IsPromoted.Should().Be(false);
board["H9"]?.WhichPiece.Should().Be(WhichPiece.Knight);
board["H9"]?.Owner.Should().Be(WhichPlayer.Player2);
board["H9"]?.IsPromoted.Should().Be(false);
board["I9"]?.WhichPiece.Should().Be(WhichPiece.Lance);
board["I9"]?.Owner.Should().Be(WhichPlayer.Player2);
board["I9"]?.IsPromoted.Should().Be(false);
}
}
}

View File

@@ -0,0 +1,462 @@
using System;
namespace Shogi.Domain.UnitTests
{
public class ShogiShould
{
private readonly ITestOutputHelper console;
public ShogiShould(ITestOutputHelper console)
{
this.console = console;
}
[Fact]
public void MoveAPieceToAnEmptyPosition()
{
// Arrange
var shogi = MockSession();
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 = MockSession();
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 = MockSession();
var board = shogi.BoardState;
board["D5"].Should().BeNull();
// Act
var act = () => shogi.Move("D5", "D6", false);
// Assert
act.Should().Throw<InvalidOperationException>();
board["D5"].Should().BeNull();
board["D6"].Should().BeNull();
board.Player1Hand.Should().BeEmpty();
board.Player2Hand.Should().BeEmpty();
}
[Fact]
public void PreventInvalidMoves_MoveToCurrentPosition()
{
// Arrange
var shogi = MockSession();
var board = shogi.BoardState;
var expectedPiece = board["A3"];
// Act - P1 "moves" pawn to the position it already exists at.
var act = () => shogi.Move("A3", "A3", false);
// Assert
using (new AssertionScope())
{
act.Should().Throw<InvalidOperationException>();
board["A3"].Should().Be(expectedPiece);
board.Player1Hand.Should().BeEmpty();
board.Player2Hand.Should().BeEmpty();
}
}
[Fact]
public void PreventInvalidMoves_MoveSet()
{
// Arrange
var shogi = MockSession();
var board = shogi.BoardState;
var expectedPiece = board["A1"];
expectedPiece!.WhichPiece.Should().Be(WhichPiece.Lance);
// Act - Move Lance illegally
var act = () => shogi.Move("A1", "D5", false);
// Assert
using (new AssertionScope())
{
act.Should().Throw<InvalidOperationException>();
board["A1"].Should().Be(expectedPiece);
board["A5"].Should().BeNull();
board.Player1Hand.Should().BeEmpty();
board.Player2Hand.Should().BeEmpty();
}
}
[Fact]
public void PreventInvalidMoves_Ownership()
{
// Arrange
var shogi = MockSession();
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 act = () => shogi.Move("A7", "A6", false);
// Assert
using (new AssertionScope())
{
act.Should().Throw<InvalidOperationException>();
board["A7"].Should().Be(expectedPiece);
board["A6"].Should().BeNull();
}
}
[Fact]
public void PreventInvalidMoves_MoveThroughAllies()
{
// Arrange
var shogi = MockSession();
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 act = () => shogi.Move("A1", "A5", false);
// Assert
using (new AssertionScope())
{
act.Should().Throw<InvalidOperationException>();
board["A1"].Should().Be(lance);
board["A3"].Should().Be(pawn);
board["A5"].Should().BeNull();
}
}
[Fact]
public void PreventInvalidMoves_CaptureAlly()
{
// Arrange
var shogi = MockSession();
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 act = () => shogi.Move("B1", "C3", false);
// Arrange
using (new AssertionScope())
{
act.Should().Throw<InvalidOperationException>();
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 = MockSession();
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 act = () => shogi.Move("I9", "I8", false);
// Assert
using (new AssertionScope())
{
act.Should().Throw<InvalidOperationException>();
board.InCheck.Should().Be(WhichPlayer.Player2);
board["I9"].Should().Be(lance);
board["I8"].Should().BeNull();
}
}
[Fact]
// TODO: Consider nesting classes to share this setup in a constructor but have act and assert as separate facts.
public void PreventInvalidDrops_MoveSet()
{
// Arrange
var shogi = MockSession();
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 act = () => shogi.Move(WhichPiece.Knight, "H9");
act.Should().Throw<InvalidOperationException>();
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();
act = () => shogi.Move(WhichPiece.Knight, "H8");
act.Should().Throw<InvalidOperationException>();
board["H8"].Should().BeNull();
board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Knight);
// Act | Assert - Illegally place Lance from the hand.
board["H9"].Should().BeNull();
act = () => shogi.Move(WhichPiece.Knight, "H9");
act.Should().Throw<InvalidOperationException>();
board["H9"].Should().BeNull();
board.Player1Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Lance);
// Act | Assert - Illegally place Pawn from the hand.
board["H9"].Should().BeNull();
act = () => shogi.Move(WhichPiece.Pawn, "H9");
act.Should().Throw<InvalidOperationException>();
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 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(WhichPlayer.Player2);
// shogi.Player2Hand.Should().ContainSingle(_ => _.WhichPiece == WhichPiece.Bishop);
// 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();
// board["E5"].Should().BeNull();
// shogi.InCheck.Should().Be(WhichPlayer.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);
// board["I9"].Should().NotBeNull();
// board["I9"].WhichPiece.Should().Be(WhichPiece.Lance);
// board["I9"].Owner.Should().Be(WhichPlayer.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);
// board["I9"].Should().NotBeNull();
// board["I9"].WhichPiece.Should().Be(WhichPiece.Lance);
// board["I9"].Owner.Should().Be(WhichPlayer.Player2);
// }
//}
[Fact]
public void Check()
{
// Arrange
var shogi = MockSession();
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 = MockSession();
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 = MockSession();
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 = MockSession();
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
console.WriteLine(shogi.ToStringStateAsAscii());
board.IsCheckmate.Should().BeTrue();
board.InCheck.Should().Be(WhichPlayer.Player2);
}
private static Session MockSession() => new Session("Test Session", BoardState.StandardStarting, "Test P1", "Test P2");
}
}

View File

@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Using Include="FluentAssertions" />
<Using Include="FluentAssertions.Execution" />
<Using Include="Xunit" />
<Using Include="Xunit.Abstractions" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shogi.Contracts\Shogi.Contracts.csproj" />
<ProjectReference Include="..\..\Shogi.Domain\Shogi.Domain.csproj" />
</ItemGroup>
</Project>