checkpoint
This commit is contained in:
120
BoardRules/BoardRules.cs
Normal file
120
BoardRules/BoardRules.cs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
//using System.Drawing;
|
||||||
|
//using System.Numerics;
|
||||||
|
|
||||||
|
//namespace BoardRules;
|
||||||
|
|
||||||
|
//public static class PieceMoves
|
||||||
|
//{
|
||||||
|
// public static readonly ICollection<Vector2> GoldGeneralMoves =
|
||||||
|
// [
|
||||||
|
// new(-1, 1),
|
||||||
|
// new(0, 1),
|
||||||
|
// new(1, 1),
|
||||||
|
// new(-1, 0),
|
||||||
|
// new(1, 0),
|
||||||
|
// new(0, -1)
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// public static readonly ICollection<Vector2> PawnMoves = [new(0, 1)];
|
||||||
|
|
||||||
|
// public static readonly ICollection<Vector2> KnightMoves = [new(-1, 2), new(1, 2)];
|
||||||
|
|
||||||
|
// public static readonly ICollection<Vector2> BishopMoves =
|
||||||
|
// [
|
||||||
|
// new(float.NegativeInfinity, float.NegativeInfinity),
|
||||||
|
// new(float.NegativeInfinity, float.PositiveInfinity),
|
||||||
|
// new(float.PositiveInfinity, float.PositiveInfinity),
|
||||||
|
// new(float.PositiveInfinity, float.NegativeInfinity),
|
||||||
|
// ];
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public class BoardRules
|
||||||
|
//{
|
||||||
|
// private readonly Dictionary<string, IPiece> pieces = [];
|
||||||
|
|
||||||
|
// public BoardRules WithSize(int width, int height)
|
||||||
|
// {
|
||||||
|
// this.BoardSize = new Size(width, height);
|
||||||
|
// return this;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public Size BoardSize { get; private set; }
|
||||||
|
|
||||||
|
// public BoardRules AddPiece(IPiece piece)
|
||||||
|
// {
|
||||||
|
// pieces.Add(piece.Name, piece);
|
||||||
|
// return this;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public class BoardPieceRules(BoardRules rules, IPiece piece)
|
||||||
|
//{
|
||||||
|
// public IPiece Piece { get; } = piece;
|
||||||
|
// public BoardRules WithStartingPositions(ICollection<Vector2> positions)
|
||||||
|
// {
|
||||||
|
// // Validate positions against board size
|
||||||
|
// foreach (var pos in positions)
|
||||||
|
// {
|
||||||
|
// if (pos.X < 0 || pos.Y < 0 || pos.X >= rules.BoardSize.Width || pos.Y >= rules.BoardSize.Height)
|
||||||
|
// {
|
||||||
|
// throw new ArgumentOutOfRangeException(nameof(positions), $"Position {pos} is out of bounds for board size {rules.BoardSize}.");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // Assuming piece has a way to set starting positions, which it currently does not.
|
||||||
|
// // This is just a placeholder to show intent.
|
||||||
|
// // piece.SetStartingPositions(positions);
|
||||||
|
// return rules;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public interface IPiece
|
||||||
|
//{
|
||||||
|
// public string Name { get; }
|
||||||
|
// public ICollection<Vector2> MoveSet { get; }
|
||||||
|
// public ICollection<Vector2> PromotedMoveSet { get; }
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// The starting positions for this type of piece on the board. There could be one or many.
|
||||||
|
// /// </summary>
|
||||||
|
// public ICollection<Vector2> StartingPositions { get; }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public class GoldGeneral : IPiece
|
||||||
|
//{
|
||||||
|
// public string Name => nameof(GoldGeneral);
|
||||||
|
// public ICollection<Vector2> MoveSet => PieceMoves.GoldGeneralMoves;
|
||||||
|
// public ICollection<Vector2> PromotedMoveSet => PieceMoves.GoldGeneralMoves;
|
||||||
|
|
||||||
|
// public ICollection<Vector2> StartingPositions => [new(3, 0), new(5, 0), new(4, 1)];
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public class Pawn : IPiece
|
||||||
|
//{
|
||||||
|
// public string Name => nameof(Pawn);
|
||||||
|
// public ICollection<Vector2> MoveSet => PieceMoves.PawnMoves;
|
||||||
|
// public ICollection<Vector2> PromotedMoveSet => PieceMoves.GoldGeneralMoves;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public class Knight : IPiece
|
||||||
|
//{
|
||||||
|
// public string Name => nameof(Knight);
|
||||||
|
// public ICollection<Vector2> MoveSet => PieceMoves.KnightMoves;
|
||||||
|
// public ICollection<Vector2> PromotedMoveSet => PieceMoves.GoldGeneralMoves;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public class Bishop : IPiece
|
||||||
|
//{
|
||||||
|
// public string Name => nameof(Bishop);
|
||||||
|
// public ICollection<Vector2> MoveSet => PieceMoves.BishopMoves;
|
||||||
|
// public ICollection<Vector2> PromotedMoveSet => PieceMoves.BishopMoves;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public class Luke
|
||||||
|
//{
|
||||||
|
// public void Yep()
|
||||||
|
// {
|
||||||
|
// var board = new BoardRules()
|
||||||
|
// .WithSize(9, 9)
|
||||||
|
// .AddPiece(new Pawn())
|
||||||
|
// }
|
||||||
|
//}
|
||||||
9
BoardRules/BoardRules.csproj
Normal file
9
BoardRules/BoardRules.csproj
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -32,3 +32,9 @@ public enum WhichPiece
|
|||||||
Pawn,
|
Pawn,
|
||||||
//PromotedPawn,
|
//PromotedPawn,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum WhichPlayer
|
||||||
|
{
|
||||||
|
Player1,
|
||||||
|
Player2
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
namespace Shogi.Domain.ValueObjects;
|
||||||
|
|
||||||
public static class Direction
|
public static class Direction
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
namespace Shogi.Domain.ValueObjects;
|
||||||
|
|
||||||
public enum Distance
|
public enum Distance
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
namespace Shogi.Domain.ValueObjects.Movement;
|
||||||
|
|
||||||
[DebuggerDisplay("{Step} - {Distance}")]
|
[DebuggerDisplay("{Step} - {Distance}")]
|
||||||
public record Path
|
public record Path
|
||||||
@@ -17,7 +17,7 @@ public record Path
|
|||||||
public Path(Vector2 step, Distance distance = Distance.OneStep)
|
public Path(Vector2 step, Distance distance = Distance.OneStep)
|
||||||
{
|
{
|
||||||
Step = step;
|
Step = step;
|
||||||
this.Distance = distance;
|
Distance = distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path Invert() => new(Vector2.Negate(Step), Distance);
|
public Path Invert() => new(Vector2.Negate(Step), Distance);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
using Shogi.Domain.ValueObjects.Movement;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Shogi.Domain.ValueObjects
|
namespace Shogi.Domain.ValueObjects
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
using Shogi.Domain.ValueObjects.Movement;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Shogi.Domain.ValueObjects;
|
namespace Shogi.Domain.ValueObjects;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
using Shogi.Domain.ValueObjects.Movement;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Shogi.Domain.ValueObjects;
|
namespace Shogi.Domain.ValueObjects;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
using Shogi.Domain.ValueObjects.Movement;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Shogi.Domain.ValueObjects
|
namespace Shogi.Domain.ValueObjects
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
using Shogi.Domain.ValueObjects.Movement;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Shogi.Domain.ValueObjects
|
namespace Shogi.Domain.ValueObjects
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
using Shogi.Domain.ValueObjects.Movement;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Shogi.Domain.ValueObjects
|
namespace Shogi.Domain.ValueObjects
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
using Shogi.Domain.ValueObjects.Movement;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Shogi.Domain.ValueObjects
|
namespace Shogi.Domain.ValueObjects
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
using Shogi.Domain.ValueObjects.Movement;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Shogi.Domain.ValueObjects;
|
namespace Shogi.Domain.ValueObjects;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
using Shogi.Domain.ValueObjects.Movement;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Shogi.Domain.ValueObjects
|
namespace Shogi.Domain.ValueObjects
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD;
|
using Shogi.Domain.ValueObjects.Movement;
|
||||||
|
using Shogi.Domain.YetToBeAssimilatedIntoDDD;
|
||||||
namespace Shogi.Domain.ValueObjects;
|
namespace Shogi.Domain.ValueObjects;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -247,7 +248,7 @@ public sealed class ShogiBoard(BoardState initialState)
|
|||||||
{
|
{
|
||||||
var list = new List<Vector2>(10);
|
var list = new List<Vector2>(10);
|
||||||
var position = path.Step + piecePosition;
|
var position = path.Step + piecePosition;
|
||||||
if (path.Distance == YetToBeAssimilatedIntoDDD.Pathing.Distance.MultiStep)
|
if (path.Distance == Distance.MultiStep)
|
||||||
{
|
{
|
||||||
|
|
||||||
while (position.IsInsideBoardBoundary())
|
while (position.IsInsideBoardBoundary())
|
||||||
@@ -340,7 +341,7 @@ public sealed class ShogiBoard(BoardState initialState)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var multiStepPaths = matchingPaths
|
var multiStepPaths = matchingPaths
|
||||||
.Where(path => path.Distance == YetToBeAssimilatedIntoDDD.Pathing.Distance.MultiStep)
|
.Where(path => path.Distance == Distance.MultiStep)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
if (multiStepPaths.Length == 0)
|
if (multiStepPaths.Length == 0)
|
||||||
{
|
{
|
||||||
@@ -371,7 +372,7 @@ public sealed class ShogiBoard(BoardState initialState)
|
|||||||
return new MoveResult(true);
|
return new MoveResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<Vector2> GetPositionsAlongPath(Vector2 from, Vector2 to, YetToBeAssimilatedIntoDDD.Pathing.Path path)
|
private static IEnumerable<Vector2> GetPositionsAlongPath(Vector2 from, Vector2 to, Path path)
|
||||||
{
|
{
|
||||||
var next = from;
|
var next = from;
|
||||||
while (next != to && next.X >= 0 && next.X < 9 && next.Y >= 0 && next.Y < 9)
|
while (next != to && next.X >= 0 && next.X < 9 && next.Y >= 0 && next.Y < 9)
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace Shogi.Domain.ValueObjects
|
|
||||||
{
|
|
||||||
public enum WhichPlayer
|
|
||||||
{
|
|
||||||
Player1,
|
|
||||||
Player2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -30,6 +30,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shogi.Api", "Shogi.Api\Shog
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "E2ETests", "Tests\E2ETests\E2ETests.csproj", "{401120C3-45D6-4A23-8D87-C2BED29F4950}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "E2ETests", "Tests\E2ETests\E2ETests.csproj", "{401120C3-45D6-4A23-8D87-C2BED29F4950}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BoardRules", "BoardRules\BoardRules.csproj", "{5B2F47A0-6AD5-4DA9-9CFE-9F52F634DD5E}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -67,6 +69,10 @@ Global
|
|||||||
{401120C3-45D6-4A23-8D87-C2BED29F4950}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{401120C3-45D6-4A23-8D87-C2BED29F4950}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{401120C3-45D6-4A23-8D87-C2BED29F4950}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{401120C3-45D6-4A23-8D87-C2BED29F4950}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{401120C3-45D6-4A23-8D87-C2BED29F4950}.Release|Any CPU.Build.0 = Release|Any CPU
|
{401120C3-45D6-4A23-8D87-C2BED29F4950}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5B2F47A0-6AD5-4DA9-9CFE-9F52F634DD5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5B2F47A0-6AD5-4DA9-9CFE-9F52F634DD5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5B2F47A0-6AD5-4DA9-9CFE-9F52F634DD5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5B2F47A0-6AD5-4DA9-9CFE-9F52F634DD5E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -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]
|
Notation.FromBoardNotation("A1").Should().Be(new Vector2(0, 0));
|
||||||
public void ConvertFromNotationToVector()
|
Notation.FromBoardNotation("E5").Should().Be(new Vector2(4, 4));
|
||||||
{
|
Notation.FromBoardNotation("I9").Should().Be(new Vector2(8, 8));
|
||||||
Notation.FromBoardNotation("A1").Should().Be(new Vector2(0, 0));
|
Notation.FromBoardNotation("C3").Should().Be(new Vector2(2, 2));
|
||||||
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]
|
[Fact]
|
||||||
public void ConvertFromVectorToNotation()
|
public void ConvertFromVectorToNotation()
|
||||||
{
|
{
|
||||||
Notation.ToBoardNotation(new Vector2(0, 0)).Should().Be("A1");
|
Notation.ToBoardNotation(new Vector2(0, 0)).Should().Be("A1");
|
||||||
Notation.ToBoardNotation(new Vector2(4, 4)).Should().Be("E5");
|
Notation.ToBoardNotation(new Vector2(4, 4)).Should().Be("E5");
|
||||||
Notation.ToBoardNotation(new Vector2(8, 8)).Should().Be("I9");
|
Notation.ToBoardNotation(new Vector2(8, 8)).Should().Be("I9");
|
||||||
Notation.ToBoardNotation(new Vector2(2, 2)).Should().Be("C3");
|
Notation.ToBoardNotation(new Vector2(2, 2)).Should().Be("C3");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Shogi.Domain.ValueObjects;
|
using Shogi.Domain.ValueObjects;
|
||||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
using Shogi.Domain.ValueObjects.Movement;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace UnitTests;
|
namespace UnitTests;
|
||||||
|
|||||||
@@ -1,460 +1,457 @@
|
|||||||
using Shogi.Domain.ValueObjects;
|
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;
|
this.console = console;
|
||||||
public ShogiShould(ITestOutputHelper 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]
|
[Fact]
|
||||||
public void MoveAPieceToAnEmptyPosition()
|
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.Should().NotBeNull();
|
||||||
moveResult.IsSuccess.Should().BeFalse();
|
moveResult.IsSuccess.Should().BeFalse(); board["A3"].Should().Be(expectedPiece);
|
||||||
board["D5"].Should().BeNull();
|
|
||||||
board["D6"].Should().BeNull();
|
|
||||||
board.Player1Hand.Should().BeEmpty();
|
board.Player1Hand.Should().BeEmpty();
|
||||||
board.Player2Hand.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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user