yep
This commit is contained in:
@@ -17,7 +17,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PathFinding", "PathFinding\
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gameboard.ShogiUI.xUnitTests", "Gameboard.ShogiUI.xUnitTests\Gameboard.ShogiUI.xUnitTests.csproj", "{12530716-C11E-40CE-9F71-CCCC243F03E1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shogi.Domain", "Shogi.Domain\Shogi.Domain.csproj", "{0211B1E4-20F0-4058-AAC4-3845D19910AF}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shogi.Domain", "Shogi.Domain\Shogi.Domain.csproj", "{0211B1E4-20F0-4058-AAC4-3845D19910AF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shogi.Domain.UnitTests", "Shogi.Domain.UnitTests\Shogi.Domain.UnitTests.csproj", "{F256989E-B6AF-4731-9DB4-88991C40B2CE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -53,6 +55,10 @@ Global
|
||||
{0211B1E4-20F0-4058-AAC4-3845D19910AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0211B1E4-20F0-4058-AAC4-3845D19910AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0211B1E4-20F0-4058-AAC4-3845D19910AF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F256989E-B6AF-4731-9DB4-88991C40B2CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F256989E-B6AF-4731-9DB4-88991C40B2CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F256989E-B6AF-4731-9DB4-88991C40B2CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F256989E-B6AF-4731-9DB4-88991C40B2CE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -60,6 +66,7 @@ Global
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{DC8A933A-DBCB-46B9-AA0B-7B3DC9E763F3} = {F35A56FB-B8D8-4CB7-ABF6-D40049C9B42E}
|
||||
{12530716-C11E-40CE-9F71-CCCC243F03E1} = {F35A56FB-B8D8-4CB7-ABF6-D40049C9B42E}
|
||||
{F256989E-B6AF-4731-9DB4-88991C40B2CE} = {F35A56FB-B8D8-4CB7-ABF6-D40049C9B42E}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {1D0B04F2-0DA1-4CB4-A82A-5A1C3B52ACEB}
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation" Version="10.3.4" />
|
||||
<PackageReference Include="IdentityModel" Version="5.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureAD.UI" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Identity.Web" Version="1.20.0" />
|
||||
<PackageReference Include="Microsoft.Identity.Web.MicrosoftGraph" Version="1.20.0" />
|
||||
<PackageReference Include="FluentValidation" Version="10.3.6" />
|
||||
<PackageReference Include="IdentityModel" Version="6.0.0-preview.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureAD.UI" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Identity.Web" Version="1.21.1" />
|
||||
<PackageReference Include="Microsoft.Identity.Web.MicrosoftGraph" Version="1.21.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NSwag.AspNetCore" Version="13.14.3" />
|
||||
<PackageReference Include="NSwag.AspNetCore" Version="13.15.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" Version="4.17.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.2.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.7" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0-preview-20211130-02" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
|
||||
<PackageReference Include="xunit" Version="2.4.2-pre.12" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" Version="4.17.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.2.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0-preview-20211130-02" />
|
||||
<PackageReference Include="xunit" Version="2.4.2-pre.12" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
28
Shogi.Domain.UnitTests/Shogi.Domain.UnitTests.csproj
Normal file
28
Shogi.Domain.UnitTests/Shogi.Domain.UnitTests.csproj
Normal file
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.2.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0-preview-20211130-02" />
|
||||
<PackageReference Include="xunit" Version="2.4.2-pre.12" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shogi.Domain\Shogi.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
186
Shogi.Domain.UnitTests/ShogiBoardStateShould.cs
Normal file
186
Shogi.Domain.UnitTests/ShogiBoardStateShould.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace Shogi.Domain.UnitTests
|
||||
{
|
||||
public class ShogiBoardStateShould
|
||||
{
|
||||
[Fact]
|
||||
public void InitializeBoardState()
|
||||
{
|
||||
// Act
|
||||
var board = new ShogiBoardState();
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
438
Shogi.Domain.UnitTests/ShogiShould.cs
Normal file
438
Shogi.Domain.UnitTests/ShogiShould.cs
Normal file
@@ -0,0 +1,438 @@
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Execution;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Shogi.Domain.UnitTests
|
||||
{
|
||||
public class ShogiShould
|
||||
{
|
||||
private readonly ITestOutputHelper output;
|
||||
public ShogiShould(ITestOutputHelper output)
|
||||
{
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
//[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(WhichPlayer.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(WhichPlayer.Player1);
|
||||
// shogi.Board["A7"].Owner.Should().Be(WhichPlayer.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(WhichPlayer.Player2);
|
||||
|
||||
// // Act - P2 moves Lance while in check.
|
||||
// var moveSuccess = shogi.Move(new Move("I9", "I8"));
|
||||
|
||||
// // Assert
|
||||
// moveSuccess.Should().BeFalse();
|
||||
// shogi.InCheck.Should().Be(WhichPlayer.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(WhichPlayer.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(WhichPlayer.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(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);
|
||||
// shogi.Board["I9"].Should().NotBeNull();
|
||||
// shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance);
|
||||
// shogi.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);
|
||||
// shogi.Board["I9"].Should().NotBeNull();
|
||||
// shogi.Board["I9"].WhichPiece.Should().Be(WhichPiece.Lance);
|
||||
// shogi.Board["I9"].Owner.Should().Be(WhichPlayer.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(WhichPlayer.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(WhichPlayer.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(WhichPlayer.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(WhichPlayer.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 == WhichPlayer.Player1);
|
||||
//}
|
||||
}
|
||||
}
|
||||
18
Shogi.Domain/Direction.cs
Normal file
18
Shogi.Domain/Direction.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Shogi.Domain
|
||||
{
|
||||
public static class Direction
|
||||
{
|
||||
public static readonly Vector2 Up = new(0, 1);
|
||||
public static readonly Vector2 Down = new(0, -1);
|
||||
public static readonly Vector2 Left = new(-1, 0);
|
||||
public static readonly Vector2 Right = new(1, 0);
|
||||
public static readonly Vector2 UpLeft = new(-1, 1);
|
||||
public static readonly Vector2 UpRight = new(1, 1);
|
||||
public static readonly Vector2 DownLeft = new(-1, -1);
|
||||
public static readonly Vector2 DownRight = new(1, -1);
|
||||
public static readonly Vector2 KnightLeft = new(-1, 2);
|
||||
public static readonly Vector2 KnightRight = new(1, 2);
|
||||
}
|
||||
}
|
||||
8
Shogi.Domain/Distance.cs
Normal file
8
Shogi.Domain/Distance.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Shogi.Domain
|
||||
{
|
||||
public enum Distance
|
||||
{
|
||||
OneStep,
|
||||
MultiStep
|
||||
}
|
||||
}
|
||||
@@ -3,51 +3,15 @@ using System.Numerics;
|
||||
|
||||
namespace Shogi.Domain
|
||||
{
|
||||
[DebuggerDisplay("{From} - {To}")]
|
||||
[DebuggerDisplay("{Direction} - {Distance}")]
|
||||
public class Move
|
||||
{
|
||||
public Vector2? From { get; } // TODO: Use string notation
|
||||
public bool IsPromotion { get; }
|
||||
public WhichPiece? PieceFromHand { get; }
|
||||
public Vector2 To { get; }
|
||||
|
||||
public Move(Vector2 from, Vector2 to, bool isPromotion = false)
|
||||
public Vector2 Direction { get; }
|
||||
public Distance Distance { get; }
|
||||
public Move(Vector2 direction, Distance distance = Distance.OneStep)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
IsPromotion = isPromotion;
|
||||
}
|
||||
public Move(WhichPiece pieceFromHand, Vector2 to)
|
||||
{
|
||||
PieceFromHand = pieceFromHand;
|
||||
To = to;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor to represent moving a piece on the Board to another position on the Board.
|
||||
/// </summary>
|
||||
/// <param name="fromNotation">Position the piece is being moved from.</param>
|
||||
/// <param name="toNotation">Position the piece is being moved to.</param>
|
||||
/// <param name="isPromotion">If the moving piece should be promoted.</param>
|
||||
public Move(string fromNotation, string toNotation, bool isPromotion = false)
|
||||
{
|
||||
//From = NotationHelper.FromBoardNotation(fromNotation);
|
||||
//To = NotationHelper.FromBoardNotation(toNotation);
|
||||
//IsPromotion = isPromotion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor to represent moving a piece from the Hand to the Board.
|
||||
/// </summary>
|
||||
/// <param name="pieceFromHand">The piece being moved from the Hand to the Board.</param>
|
||||
/// <param name="toNotation">Position the piece is being moved to.</param>
|
||||
/// <param name="isPromotion">If the moving piece should be promoted.</param>
|
||||
public Move(WhichPiece pieceFromHand, string toNotation, bool isPromotion = false)
|
||||
{
|
||||
//From = null;
|
||||
//PieceFromHand = pieceFromHand;
|
||||
//To = NotationHelper.FromBoardNotation(toNotation);
|
||||
//IsPromotion = isPromotion;
|
||||
Direction = direction;
|
||||
Distance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
106
Shogi.Domain/MoveSet.cs
Normal file
106
Shogi.Domain/MoveSet.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Shogi.Domain
|
||||
{
|
||||
public class MoveSet
|
||||
{
|
||||
|
||||
public static readonly MoveSet King = new(new List<Move>(8)
|
||||
{
|
||||
new Move(Direction.Up),
|
||||
new Move(Direction.Left),
|
||||
new Move(Direction.Right),
|
||||
new Move(Direction.Down),
|
||||
new Move(Direction.UpLeft),
|
||||
new Move(Direction.UpRight),
|
||||
new Move(Direction.DownLeft),
|
||||
new Move(Direction.DownRight)
|
||||
});
|
||||
|
||||
public static readonly MoveSet Bishop = new(new List<Move>(4)
|
||||
{
|
||||
new Move(Direction.UpLeft, Distance.MultiStep),
|
||||
new Move(Direction.UpRight, Distance.MultiStep),
|
||||
new Move(Direction.DownLeft, Distance.MultiStep),
|
||||
new Move(Direction.DownRight, Distance.MultiStep)
|
||||
});
|
||||
|
||||
public static readonly MoveSet PromotedBishop = new(new List<Move>(8)
|
||||
{
|
||||
new Move(Direction.Up),
|
||||
new Move(Direction.Left),
|
||||
new Move(Direction.Right),
|
||||
new Move(Direction.Down),
|
||||
new Move(Direction.UpLeft, Distance.MultiStep),
|
||||
new Move(Direction.UpRight, Distance.MultiStep),
|
||||
new Move(Direction.DownLeft, Distance.MultiStep),
|
||||
new Move(Direction.DownRight, Distance.MultiStep)
|
||||
});
|
||||
|
||||
public static readonly MoveSet GoldGeneral = new(new List<Move>(6)
|
||||
{
|
||||
new Move(Direction.Up),
|
||||
new Move(Direction.UpLeft),
|
||||
new Move(Direction.UpRight),
|
||||
new Move(Direction.Left),
|
||||
new Move(Direction.Right),
|
||||
new Move(Direction.Down)
|
||||
});
|
||||
|
||||
public static readonly MoveSet Knight = new(new List<Move>(2)
|
||||
{
|
||||
new Move(Direction.KnightLeft),
|
||||
new Move(Direction.KnightRight)
|
||||
});
|
||||
|
||||
public static readonly MoveSet Lance = new(new List<Move>(1)
|
||||
{
|
||||
new Move(Direction.Up, Distance.MultiStep),
|
||||
});
|
||||
|
||||
public static readonly MoveSet Pawn = new(new List<Move>(1)
|
||||
{
|
||||
new Move(Direction.Up)
|
||||
});
|
||||
|
||||
public static readonly MoveSet Rook = new(new List<Move>(4)
|
||||
{
|
||||
new Move(Direction.Up, Distance.MultiStep),
|
||||
new Move(Direction.Left, Distance.MultiStep),
|
||||
new Move(Direction.Right, Distance.MultiStep),
|
||||
new Move(Direction.Down, Distance.MultiStep)
|
||||
});
|
||||
|
||||
public static readonly MoveSet PromotedRook = new(new List<Move>(8)
|
||||
{
|
||||
new Move(Direction.Up, Distance.MultiStep),
|
||||
new Move(Direction.Left, Distance.MultiStep),
|
||||
new Move(Direction.Right, Distance.MultiStep),
|
||||
new Move(Direction.Down, Distance.MultiStep),
|
||||
new Move(Direction.UpLeft),
|
||||
new Move(Direction.UpRight),
|
||||
new Move(Direction.DownLeft),
|
||||
new Move(Direction.DownRight)
|
||||
});
|
||||
|
||||
public static readonly MoveSet SilverGeneral = new(new List<Move>(4)
|
||||
{
|
||||
new Move(Direction.Up),
|
||||
new Move(Direction.UpLeft),
|
||||
new Move(Direction.UpRight),
|
||||
new Move(Direction.DownLeft),
|
||||
new Move(Direction.DownRight)
|
||||
});
|
||||
|
||||
private readonly ICollection<Move> moves;
|
||||
private readonly ICollection<Move> upsidedownMoves;
|
||||
|
||||
private MoveSet(ICollection<Move> moves)
|
||||
{
|
||||
this.moves = moves;
|
||||
upsidedownMoves = moves.Select(m => new Move(Vector2.Negate(m.Direction), m.Distance)).ToList();
|
||||
}
|
||||
|
||||
public ICollection<Move> GetMoves(bool isUpsideDown) => isUpsideDown ? upsidedownMoves : moves;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Shogi.Domain
|
||||
namespace Shogi.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// Facilitates Shogi board state transitions, cognisant of Shogi rules.
|
||||
@@ -17,74 +15,89 @@ namespace Shogi.Domain
|
||||
{
|
||||
this.board = board;
|
||||
rules = new StandardRules(this.board);
|
||||
Error = string.Empty;
|
||||
}
|
||||
|
||||
public Shogi(IList<Move> moves) : this()
|
||||
//public Shogi(IList<Move> moves) : this(new ShogiBoardState())
|
||||
//{
|
||||
// for (var i = 0; i < moves.Count; i++)
|
||||
// {
|
||||
// if (!Move(moves[i]))
|
||||
// {
|
||||
// throw new InvalidOperationException($"Unable to construct ShogiBoard with the given move at index {i}.");
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
public MoveResult CanMove(string from, string to, bool isPromotion)
|
||||
{
|
||||
for (var i = 0; i < moves.Count; i++)
|
||||
{
|
||||
if (!Move(moves[i]))
|
||||
{
|
||||
// Todo: Add some smarts to know why a move was invalid. In check? Piece not found? etc.
|
||||
throw new InvalidOperationException($"Unable to construct ShogiBoard with the given move at index {i}. {Error}");
|
||||
}
|
||||
}
|
||||
// TODO: ShogiBoardState.FromBoardNotation should not throw an execption in this query method.
|
||||
var fromVector = ShogiBoardState.FromBoardNotation(from);
|
||||
var toVector = ShogiBoardState.FromBoardNotation(to);
|
||||
var simulator = new StandardRules(new ShogiBoardState(board));
|
||||
return simulator.Move(fromVector, toVector, isPromotion);
|
||||
}
|
||||
|
||||
public bool Move(Move move)
|
||||
public MoveResult CanMove(WhichPiece pieceInHand, string to)
|
||||
{
|
||||
var moveSuccess = TryMove(move);
|
||||
|
||||
if (!moveSuccess)
|
||||
{
|
||||
return false;
|
||||
var toVector = ShogiBoardState.FromBoardNotation(to);
|
||||
var simulator = new StandardRules(new ShogiBoardState(board));
|
||||
return simulator.Move(pieceInHand, toVector);
|
||||
}
|
||||
|
||||
var otherPlayer = WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
|
||||
if (EvaluateCheckAfterMove(move, otherPlayer))
|
||||
public void Move(string from, string to, bool isPromotion)
|
||||
{
|
||||
InCheck = otherPlayer;
|
||||
IsCheckmate = EvaluateCheckmate();
|
||||
var fromVector = ShogiBoardState.FromBoardNotation(from);
|
||||
var toVector = ShogiBoardState.FromBoardNotation(to);
|
||||
var moveResult = rules.Move(fromVector, toVector, isPromotion);
|
||||
if (!moveResult.Success)
|
||||
{
|
||||
throw new InvalidOperationException(moveResult.Reason);
|
||||
}
|
||||
|
||||
var otherPlayer = board.WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
|
||||
if (rules.EvaluateCheckAfterMove(fromVector, toVector, otherPlayer))
|
||||
{
|
||||
board.InCheck = otherPlayer;
|
||||
board.IsCheckmate = rules.EvaluateCheckmate();
|
||||
}
|
||||
else
|
||||
{
|
||||
InCheck = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts a given move. Returns false if the move is illegal.
|
||||
/// </summary>
|
||||
private bool TryMove(Move move)
|
||||
{
|
||||
// Try making the move in a "throw away" board.
|
||||
var simulator = new StandardRules(new ShogiBoardState(this.board));
|
||||
|
||||
var simulatedMoveResults = move.PieceFromHand.HasValue
|
||||
? simulator.PlaceFromHand(move)
|
||||
: simulator.PlaceFromBoard(move);
|
||||
if (!simulatedMoveResults)
|
||||
{
|
||||
// Surface the error description.
|
||||
Error = simulationBoard.Error;
|
||||
return false;
|
||||
}
|
||||
// If already in check, assert the move that resulted in check no longer results in check.
|
||||
if (InCheck == WhoseTurn)
|
||||
{
|
||||
if (simulationBoard.EvaluateCheckAfterMove(MoveHistory[^1], WhoseTurn))
|
||||
{
|
||||
// Sneakily using this.WhoseTurn instead of validationBoard.WhoseTurn;
|
||||
return false;
|
||||
board.InCheck = null;
|
||||
}
|
||||
}
|
||||
|
||||
// The move is valid and legal; update board state.
|
||||
if (move.PieceFromHand.HasValue) PlaceFromHand(move);
|
||||
else PlaceFromBoard(move);
|
||||
return true;
|
||||
}
|
||||
///// <summary>
|
||||
///// Attempts a given move. Returns false if the move is illegal.
|
||||
///// </summary>
|
||||
//private bool TryMove(Move move)
|
||||
//{
|
||||
// // Try making the move in a "throw away" board.
|
||||
// var simulator = new StandardRules(new ShogiBoardState(this.board));
|
||||
|
||||
// var simulatedMoveResults = move.PieceFromHand.HasValue
|
||||
// ? simulator.PlaceFromHand(move)
|
||||
// : simulator.PlaceFromBoard(move);
|
||||
// if (!simulatedMoveResults)
|
||||
// {
|
||||
// // Surface the error description.
|
||||
// Error = simulationBoard.Error;
|
||||
// return false;
|
||||
// }
|
||||
// // If already in check, assert the move that resulted in check no longer results in check.
|
||||
// if (InCheck == WhoseTurn)
|
||||
// {
|
||||
// // Sneakily using this.WhoseTurn instead of validationBoard.WhoseTurn;
|
||||
// if (simulationBoard.EvaluateCheckAfterMove(MoveHistory[^1], WhoseTurn))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // The move is valid and legal; update board state.
|
||||
// if (move.PieceFromHand.HasValue) PlaceFromHand(move);
|
||||
// else PlaceFromBoard(move);
|
||||
// return true;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,15 +13,15 @@ namespace Shogi.Domain
|
||||
/// <summary>
|
||||
/// Key is position notation, such as "E4".
|
||||
/// </summary>
|
||||
private Dictionary<string, Piece?> board;
|
||||
private readonly Dictionary<string, Piece?> board;
|
||||
|
||||
public List<Piece> Hand => WhoseTurn == WhichPlayer.Player1 ? Player1Hand : Player2Hand;
|
||||
public List<Piece> Player1Hand { get; }
|
||||
public List<Piece> Player2Hand { get; }
|
||||
public List<Move> MoveHistory { get; }
|
||||
public WhichPlayer WhoseTurn => MoveHistory.Count % 2 == 0 ? WhichPlayer.Player1 : WhichPlayer.Player2;
|
||||
public WhichPlayer? InCheck { get; private set; }
|
||||
public bool IsCheckmate { get; private set; }
|
||||
public WhichPlayer? InCheck { get; set; }
|
||||
public bool IsCheckmate { get; set; }
|
||||
|
||||
public ShogiBoardState()
|
||||
{
|
||||
|
||||
@@ -4,6 +4,10 @@ namespace Shogi.Domain
|
||||
{
|
||||
internal class StandardRules
|
||||
{
|
||||
/// <param name="element">Guaranteed to be non-null.</param>
|
||||
/// <param name="position"></param>
|
||||
public delegate void Callback(Piece collider, Vector2 position);
|
||||
|
||||
private readonly ShogiBoardState board;
|
||||
private Vector2 player1KingPosition;
|
||||
private Vector2 player2KingPosition;
|
||||
@@ -38,18 +42,20 @@ namespace Shogi.Domain
|
||||
/// <param name="from">The position of the piece being moved expressed in board notation.</param>
|
||||
/// <param name="to">The target position expressed in board notation.</param>
|
||||
/// <returns>A <see cref="MoveResult" /> describing the success or failure of the simulation.</returns>
|
||||
public MoveResult Move(string from, string to, bool isPromotion = false)
|
||||
public MoveResult Move(Vector2 from, Vector2 to, bool isPromotion = false)
|
||||
{
|
||||
var fromPiece = board[from];
|
||||
if (fromPiece == null)
|
||||
{
|
||||
return new MoveResult(false, $"Tile [{from}] is empty. There is no piece to move.");
|
||||
}
|
||||
|
||||
if (fromPiece.Owner != board.WhoseTurn)
|
||||
{
|
||||
return new MoveResult(false, "Not allowed to move the opponents piece");
|
||||
}
|
||||
if (IsPathable(move.From.Value, move.To) == false)
|
||||
|
||||
if (IsPathable(from, to) == false)
|
||||
{
|
||||
return new MoveResult(false, $"Proposed move is not part of the move-set for piece {fromPiece.WhichPiece}.");
|
||||
}
|
||||
@@ -68,13 +74,11 @@ namespace Shogi.Domain
|
||||
//Mutate the board.
|
||||
if (isPromotion)
|
||||
{
|
||||
var fromVector = ShogiBoardState.FromBoardNotation(from);
|
||||
var toVector = ShogiBoardState.FromBoardNotation(to);
|
||||
if (board.WhoseTurn == WhichPlayer.Player1 && (toVector.Y > 5 || fromVector.Y > 5))
|
||||
if (board.WhoseTurn == WhichPlayer.Player1 && (to.Y > 5 || from.Y > 5))
|
||||
{
|
||||
fromPiece.Promote();
|
||||
}
|
||||
else if (board.WhoseTurn == WhichPlayer.Player2 && (toVector.Y < 3 || fromVector.Y < 3))
|
||||
else if (board.WhoseTurn == WhichPlayer.Player2 && (to.Y < 3 || from.Y < 3))
|
||||
{
|
||||
fromPiece.Promote();
|
||||
}
|
||||
@@ -85,17 +89,15 @@ namespace Shogi.Domain
|
||||
{
|
||||
if (fromPiece.Owner == WhichPlayer.Player1)
|
||||
{
|
||||
player1King.X = move.To.X;
|
||||
player1King.Y = move.To.Y;
|
||||
player1KingPosition = from;
|
||||
}
|
||||
else if (fromPiece.Owner == WhichPlayer.Player2)
|
||||
{
|
||||
player2King.X = move.To.X;
|
||||
player2King.Y = move.To.Y;
|
||||
player2KingPosition = from;
|
||||
}
|
||||
}
|
||||
MoveHistory.Add(move);
|
||||
return true;
|
||||
//MoveHistory.Add(move);
|
||||
return new MoveResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -104,30 +106,27 @@ namespace Shogi.Domain
|
||||
/// <param name="pieceInHand"></param>
|
||||
/// <param name="to">The target position expressed in board notation.</param>
|
||||
/// <returns>A <see cref="MoveResult" /> describing the success or failure of the simulation.</returns>
|
||||
public void Move(WhichPiece pieceInHand, string to)
|
||||
public MoveResult Move(WhichPiece pieceInHand, Vector2 to)
|
||||
{
|
||||
var index = Hand.FindIndex(p => p.WhichPiece == move.PieceFromHand);
|
||||
var index = board.Hand.FindIndex(p => p.WhichPiece == pieceInHand);
|
||||
if (index == -1)
|
||||
{
|
||||
Error = $"{move.PieceFromHand} does not exist in the hand.";
|
||||
return false;
|
||||
return new MoveResult(false, $"{pieceInHand} does not exist in the hand.");
|
||||
}
|
||||
if (Board[move.To] != null)
|
||||
if (board[to] != null)
|
||||
{
|
||||
Error = $"Illegal move - attempting to capture while playing a piece from the hand.";
|
||||
return false;
|
||||
return new MoveResult(false, $"Illegal move - attempting to capture while playing a piece from the hand.");
|
||||
}
|
||||
|
||||
switch (move.PieceFromHand!.Value)
|
||||
switch (pieceInHand)
|
||||
{
|
||||
case WhichPiece.Knight:
|
||||
{
|
||||
// Knight cannot be placed onto the farthest two ranks from the hand.
|
||||
if ((WhoseTurn == WhichPlayer.Player1 && move.To.Y > 6)
|
||||
|| (WhoseTurn == WhichPlayer.Player2 && move.To.Y < 2))
|
||||
if ((board.WhoseTurn == WhichPlayer.Player1 && to.Y > 6)
|
||||
|| (board.WhoseTurn == WhichPlayer.Player2 && to.Y < 2))
|
||||
{
|
||||
Error = $"Knight has no valid moves after placed.";
|
||||
return false;
|
||||
return new MoveResult(false, "Knight has no valid moves after placed.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -135,51 +134,63 @@ namespace Shogi.Domain
|
||||
case WhichPiece.Pawn:
|
||||
{
|
||||
// Lance and Pawn cannot be placed onto the farthest rank from the hand.
|
||||
if ((WhoseTurn == WhichPlayer.Player1 && move.To.Y == 8)
|
||||
|| (WhoseTurn == WhichPlayer.Player2 && move.To.Y == 0))
|
||||
if ((board.WhoseTurn == WhichPlayer.Player1 && to.Y == 8)
|
||||
|| (board.WhoseTurn == WhichPlayer.Player2 && to.Y == 0))
|
||||
{
|
||||
Error = $"{move.PieceFromHand} has no valid moves after placed.";
|
||||
return false;
|
||||
return new MoveResult(false, $"{pieceInHand} has no valid moves after placed.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Mutate the board.
|
||||
Board[move.To] = Hand[index];
|
||||
Hand.RemoveAt(index);
|
||||
MoveHistory.Add(move);
|
||||
|
||||
return true;
|
||||
board[to] = board.Hand[index];
|
||||
board.Hand.RemoveAt(index);
|
||||
//MoveHistory.Add(move);
|
||||
return new MoveResult(true);
|
||||
}
|
||||
|
||||
private bool IsPathable(Vector2 from, Vector2 to)
|
||||
{
|
||||
var piece = Board[from];
|
||||
var piece = board[from];
|
||||
if (piece == null) return false;
|
||||
|
||||
var isObstructed = false;
|
||||
var isPathable = pathFinder.PathTo(from, to, (other, position) =>
|
||||
var isPathable = PathTo(from, to, (other, position) =>
|
||||
{
|
||||
if (other.Owner == piece.Owner) isObstructed = true;
|
||||
});
|
||||
return !isObstructed && isPathable;
|
||||
}
|
||||
|
||||
private bool EvaluateCheckAfterMove(Move move, WhichPlayer WhichPerspective)
|
||||
public bool EvaluateCheckAfterMove(WhichPiece pieceInHand, Vector2 to, WhichPlayer whichPlayer)
|
||||
{
|
||||
if (WhichPerspective == InCheck) return true; // If we already know the player is in check, don't bother.
|
||||
if (whichPlayer == board.InCheck) return true; // If we already know the player is in check, don't bother.
|
||||
|
||||
var isCheck = false;
|
||||
var kingPosition = WhichPerspective == WhichPlayer.Player1 ? player1King : player2King;
|
||||
var kingPosition = whichPlayer == WhichPlayer.Player1 ? player1KingPosition : player2KingPosition;
|
||||
|
||||
// Check if the move put the king in check.
|
||||
if (pathFinder.PathTo(move.To, kingPosition)) return true;
|
||||
if (PathTo(to, kingPosition)) return true;
|
||||
|
||||
if (move.From.HasValue)
|
||||
// TODO: Check for illegal move from hand. It is illegal to place from the hand such that you check-mate your opponent.
|
||||
// Go read the shogi rules to be sure this is true.
|
||||
|
||||
return isCheck;
|
||||
}
|
||||
|
||||
public bool EvaluateCheckAfterMove(Vector2 from, Vector2 to, WhichPlayer whichPlayer)
|
||||
{
|
||||
if (whichPlayer == board.InCheck) return true; // If we already know the player is in check, don't bother.
|
||||
|
||||
var isCheck = false;
|
||||
var kingPosition = whichPlayer == WhichPlayer.Player1 ? player1KingPosition : player2KingPosition;
|
||||
|
||||
// Check if the move put the king in check.
|
||||
if (PathTo(to, kingPosition)) return true;
|
||||
|
||||
// Get line equation from king through the now-unoccupied location.
|
||||
var direction = Vector2.Subtract(kingPosition, move.From!.Value);
|
||||
var direction = Vector2.Subtract(kingPosition, from);
|
||||
var slope = Math.Abs(direction.Y / direction.X);
|
||||
// If absolute slope is 45°, look for a bishop along the line.
|
||||
// If absolute slope is 0° or 90°, look for a rook along the line.
|
||||
@@ -187,9 +198,9 @@ namespace Shogi.Domain
|
||||
if (float.IsInfinity(slope))
|
||||
{
|
||||
// if slope of the move is also infinity...can skip this?
|
||||
pathFinder.LinePathTo(kingPosition, direction, (piece, position) =>
|
||||
LinePathTo(kingPosition, direction, (piece, position) =>
|
||||
{
|
||||
if (piece.Owner != WhichPerspective)
|
||||
if (piece.Owner != whichPlayer)
|
||||
{
|
||||
switch (piece.WhichPiece)
|
||||
{
|
||||
@@ -205,9 +216,9 @@ namespace Shogi.Domain
|
||||
}
|
||||
else if (slope == 1)
|
||||
{
|
||||
pathFinder.LinePathTo(kingPosition, direction, (piece, position) =>
|
||||
LinePathTo(kingPosition, direction, (piece, position) =>
|
||||
{
|
||||
if (piece.Owner != WhichPerspective && piece.WhichPiece == WhichPiece.Bishop)
|
||||
if (piece.Owner != whichPlayer && piece.WhichPiece == WhichPiece.Bishop)
|
||||
{
|
||||
isCheck = true;
|
||||
}
|
||||
@@ -215,54 +226,159 @@ namespace Shogi.Domain
|
||||
}
|
||||
else if (slope == 0)
|
||||
{
|
||||
pathFinder.LinePathTo(kingPosition, direction, (piece, position) =>
|
||||
LinePathTo(kingPosition, direction, (piece, position) =>
|
||||
{
|
||||
if (piece.Owner != WhichPerspective && piece.WhichPiece == WhichPiece.Rook)
|
||||
if (piece.Owner != whichPlayer && piece.WhichPiece == WhichPiece.Rook)
|
||||
{
|
||||
isCheck = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Check for illegal move from hand. It is illegal to place from the hand such that you check-mate your opponent.
|
||||
// Go read the shogi rules to be sure this is true.
|
||||
}
|
||||
|
||||
return isCheck;
|
||||
}
|
||||
|
||||
private bool EvaluateCheckmate()
|
||||
public bool EvaluateCheckmate()
|
||||
{
|
||||
if (!InCheck.HasValue) return false;
|
||||
if (!board.InCheck.HasValue) return false;
|
||||
|
||||
// Assume true and try to disprove.
|
||||
var isCheckmate = true;
|
||||
Board.ForEachNotNull((piece, from) => // For each piece...
|
||||
board.ForEachNotNull((piece, from) => // For each piece...
|
||||
{
|
||||
// Short circuit
|
||||
if (!isCheckmate) return;
|
||||
|
||||
if (piece.Owner == InCheck) // ...owned by the player in check...
|
||||
if (piece.Owner == board.InCheck) // ...owned by the player in check...
|
||||
{
|
||||
// ...evaluate if any move gets the player out of check.
|
||||
pathFinder.PathEvery(from, (other, position) =>
|
||||
PathEvery(from, (other, position) =>
|
||||
{
|
||||
var simulationBoard = new Shogi(this);
|
||||
var moveToTry = new Move(from, position);
|
||||
var moveSuccess = simulationBoard.TryMove(moveToTry);
|
||||
if (moveSuccess)
|
||||
var simulationBoard = new StandardRules(new ShogiBoardState(board));
|
||||
var simulationResult = simulationBoard.Move(from, position, false);
|
||||
if (simulationResult.Success)
|
||||
{
|
||||
if (!EvaluateCheckAfterMove(moveToTry, InCheck.Value))
|
||||
if (!EvaluateCheckAfterMove(from, position, board.InCheck.Value))
|
||||
{
|
||||
isCheckmate = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// TODO: Assert that a player could not place a piece from their hand to avoid check.
|
||||
});
|
||||
return isCheckmate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate the collection such that each "step" is always towards the destination, respecting the Paths available to the element at origin.
|
||||
/// </summary>
|
||||
/// <param name="element">The pathing element.</param>
|
||||
/// <param name="origin">The starting location.</param>
|
||||
/// <param name="destination">The destination.</param>
|
||||
/// <param name="callback">Do cool stuff here.</param>
|
||||
/// <returns>True if the element reached the destination.</returns>
|
||||
public bool PathTo(Vector2 origin, Vector2 destination, Callback? callback = null)
|
||||
{
|
||||
if (destination.X > 8 || destination.Y > 8 || destination.X < 0 || destination.Y < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var piece = board[origin];
|
||||
if (piece == null) return false;
|
||||
|
||||
var path = FindDirectionTowardsDestination(GetMoveSet(piece.WhichPiece).GetMoves(piece.IsUpsideDown), origin, destination);
|
||||
if (!IsPathable(origin, destination))
|
||||
{
|
||||
// Assumption: if a single best-choice step towards the destination cannot happen, no pathing can happen.
|
||||
return false;
|
||||
}
|
||||
|
||||
var shouldPath = true;
|
||||
var next = origin;
|
||||
while (shouldPath && next != destination)
|
||||
{
|
||||
next = Vector2.Add(next, path.Direction);
|
||||
var collider = board[next];
|
||||
if (collider != null)
|
||||
{
|
||||
callback?.Invoke(collider, next);
|
||||
shouldPath = false;
|
||||
}
|
||||
else if (path.Distance == Distance.OneStep)
|
||||
{
|
||||
shouldPath = false;
|
||||
}
|
||||
}
|
||||
return next == destination;
|
||||
}
|
||||
|
||||
public void PathEvery(Vector2 from, Callback callback)
|
||||
{
|
||||
var piece = board[from];
|
||||
if (piece == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var path in GetMoveSet(piece.WhichPiece).GetMoves(piece.IsUpsideDown))
|
||||
{
|
||||
var shouldPath = true;
|
||||
var next = Vector2.Add(from, path.Direction); ;
|
||||
while (shouldPath && next.X < 8 && next.Y < 8 && next.X >= 0 && next.Y >= 0)
|
||||
{
|
||||
var collider = board[(int)next.Y, (int)next.X];
|
||||
if (collider != null)
|
||||
{
|
||||
callback(collider, next);
|
||||
shouldPath = false;
|
||||
}
|
||||
if (path.Distance == Distance.OneStep)
|
||||
{
|
||||
shouldPath = false;
|
||||
}
|
||||
next = Vector2.Add(next, path.Direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Path the line from origin to destination, ignoring any Paths defined by the element at origin.
|
||||
/// </summary>
|
||||
public void LinePathTo(Vector2 origin, Vector2 direction, Callback callback)
|
||||
{
|
||||
direction = Vector2.Normalize(direction);
|
||||
|
||||
var next = Vector2.Add(origin, direction);
|
||||
while (next.X >= 0 && next.X < 8 && next.Y >= 0 && next.Y < 8)
|
||||
{
|
||||
var element = board[next];
|
||||
if (element != null) callback(element, next);
|
||||
next = Vector2.Add(next, direction);
|
||||
}
|
||||
}
|
||||
|
||||
public static Move FindDirectionTowardsDestination(ICollection<Move> paths, Vector2 origin, Vector2 destination) =>
|
||||
paths.Aggregate((a, b) =>
|
||||
{
|
||||
var distanceA = Vector2.Distance(destination, Vector2.Add(origin, a.Direction));
|
||||
var distanceB = Vector2.Distance(destination, Vector2.Add(origin, b.Direction));
|
||||
return distanceA < distanceB ? a : b;
|
||||
});
|
||||
|
||||
public static MoveSet GetMoveSet(WhichPiece whichPiece)
|
||||
{
|
||||
return whichPiece switch
|
||||
{
|
||||
WhichPiece.King => MoveSet.King,
|
||||
WhichPiece.GoldGeneral => MoveSet.GoldGeneral,
|
||||
WhichPiece.SilverGeneral => MoveSet.SilverGeneral,
|
||||
WhichPiece.Bishop => MoveSet.Bishop,
|
||||
WhichPiece.Rook => MoveSet.Rook,
|
||||
WhichPiece.Knight => MoveSet.Knight,
|
||||
WhichPiece.Lance => MoveSet.Lance,
|
||||
WhichPiece.Pawn => MoveSet.Pawn,
|
||||
_ => throw new ArgumentException($"{nameof(WhichPiece)} not recognized."),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user