yep
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
"iisSettings": {
|
"iisSettings": {
|
||||||
"windowsAuthentication": false,
|
"windowsAuthentication": false,
|
||||||
"anonymousAuthentication": true,
|
"anonymousAuthentication": true,
|
||||||
"iisExpress": {
|
"iisExpress": {
|
||||||
"applicationUrl": "http://localhost:63676",
|
"applicationUrl": "http://localhost:50728/",
|
||||||
"sslPort": 44396
|
"sslPort": 44315
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"IIS Express": {
|
"IIS Express": {
|
||||||
"commandName": "IISExpress",
|
"commandName": "IISExpress",
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ namespace Shogi.Domain.UnitTests
|
|||||||
//[Fact]
|
//[Fact]
|
||||||
//public void InitializeBoardStateWithMoves()
|
//public void InitializeBoardStateWithMoves()
|
||||||
//{
|
//{
|
||||||
|
// var board = new ShogiBoardState();
|
||||||
|
// var rules = new StandardRules(board);
|
||||||
// var moves = new[]
|
// var moves = new[]
|
||||||
// {
|
// {
|
||||||
// // P1 Pawn
|
// // P1 Pawn
|
||||||
|
|||||||
31
Shogi.Domain.UnitTests/StandardRulesShould.cs
Normal file
31
Shogi.Domain.UnitTests/StandardRulesShould.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using FluentAssertions;
|
||||||
|
using FluentAssertions.Execution;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Shogi.Domain.UnitTests
|
||||||
|
{
|
||||||
|
public class StandardRulesShould
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void AllowValidMoves_AfterCheck()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var board = new ShogiBoardState();
|
||||||
|
var rules = new StandardRules(board);
|
||||||
|
rules.Move("C3", "C4"); // P1 Pawn
|
||||||
|
rules.Move("G7", "G6"); // P2 Pawn
|
||||||
|
rules.Move("B2", "G7"); // P1 Bishop puts P2 in check
|
||||||
|
board.InCheck.Should().Be(WhichPlayer.Player2);
|
||||||
|
|
||||||
|
// Act - P2 is able to un-check theirself.
|
||||||
|
/// P2 King moves out of check
|
||||||
|
var moveSuccess = rules.Move("E9", "E8");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
using var _ = new AssertionScope();
|
||||||
|
moveSuccess.Success.Should().BeTrue();
|
||||||
|
moveSuccess.Reason.Should().BeEmpty();
|
||||||
|
board.InCheck.Should().BeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,25 +31,21 @@
|
|||||||
|
|
||||||
public MoveResult CanMove(string from, string to, bool isPromotion)
|
public MoveResult CanMove(string from, string to, bool isPromotion)
|
||||||
{
|
{
|
||||||
// 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));
|
var simulator = new StandardRules(new ShogiBoardState(board));
|
||||||
return simulator.Move(fromVector, toVector, isPromotion);
|
return simulator.Move(from, to, isPromotion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MoveResult CanMove(WhichPiece pieceInHand, string to)
|
public MoveResult CanMove(WhichPiece pieceInHand, string to)
|
||||||
{
|
{
|
||||||
var toVector = ShogiBoardState.FromBoardNotation(to);
|
|
||||||
var simulator = new StandardRules(new ShogiBoardState(board));
|
var simulator = new StandardRules(new ShogiBoardState(board));
|
||||||
return simulator.Move(pieceInHand, toVector);
|
return simulator.Move(pieceInHand, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Move(string from, string to, bool isPromotion)
|
public void Move(string from, string to, bool isPromotion)
|
||||||
{
|
{
|
||||||
var fromVector = ShogiBoardState.FromBoardNotation(from);
|
var fromVector = ShogiBoardState.FromBoardNotation(from);
|
||||||
var toVector = ShogiBoardState.FromBoardNotation(to);
|
var toVector = ShogiBoardState.FromBoardNotation(to);
|
||||||
var moveResult = rules.Move(fromVector, toVector, isPromotion);
|
var moveResult = rules.Move(from, to, isPromotion);
|
||||||
if (!moveResult.Success)
|
if (!moveResult.Success)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(moveResult.Reason);
|
throw new InvalidOperationException(moveResult.Reason);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Shogi.Domain.UnitTests")]
|
||||||
namespace Shogi.Domain
|
namespace Shogi.Domain
|
||||||
{
|
{
|
||||||
internal class StandardRules
|
internal class StandardRules
|
||||||
@@ -39,11 +41,13 @@ namespace Shogi.Domain
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Move a piece from a board tile to another board tile.
|
/// Move a piece from a board tile to another board tile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="from">The position of the piece being moved expressed in board notation.</param>
|
/// <param name="fromNotation">The position of the piece being moved expressed in board notation.</param>
|
||||||
/// <param name="to">The target position expressed in board notation.</param>
|
/// <param name="toNotation">The target position expressed in board notation.</param>
|
||||||
/// <returns>A <see cref="MoveResult" /> describing the success or failure of the simulation.</returns>
|
/// <returns>A <see cref="MoveResult" /> describing the success or failure of the simulation.</returns>
|
||||||
public MoveResult Move(Vector2 from, Vector2 to, bool isPromotion = false)
|
public MoveResult Move(string fromNotation, string toNotation, bool isPromotion = false)
|
||||||
{
|
{
|
||||||
|
var from = ShogiBoardState.FromBoardNotation(fromNotation);
|
||||||
|
var to = ShogiBoardState.FromBoardNotation(toNotation);
|
||||||
var fromPiece = board[from];
|
var fromPiece = board[from];
|
||||||
if (fromPiece == null)
|
if (fromPiece == null)
|
||||||
{
|
{
|
||||||
@@ -55,7 +59,7 @@ namespace Shogi.Domain
|
|||||||
return new MoveResult(false, "Not allowed to move the opponents piece");
|
return new MoveResult(false, "Not allowed to move the opponents piece");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPathable(from, to) == false)
|
if (ShogiIsPathable(from, to) == false)
|
||||||
{
|
{
|
||||||
return new MoveResult(false, $"Proposed move is not part of the move-set for piece {fromPiece.WhichPiece}.");
|
return new MoveResult(false, $"Proposed move is not part of the move-set for piece {fromPiece.WhichPiece}.");
|
||||||
}
|
}
|
||||||
@@ -106,8 +110,9 @@ namespace Shogi.Domain
|
|||||||
/// <param name="pieceInHand"></param>
|
/// <param name="pieceInHand"></param>
|
||||||
/// <param name="to">The target position 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>
|
/// <returns>A <see cref="MoveResult" /> describing the success or failure of the simulation.</returns>
|
||||||
public MoveResult Move(WhichPiece pieceInHand, Vector2 to)
|
public MoveResult Move(WhichPiece pieceInHand, string toNotation)
|
||||||
{
|
{
|
||||||
|
var to = ShogiBoardState.FromBoardNotation(toNotation);
|
||||||
var index = board.Hand.FindIndex(p => p.WhichPiece == pieceInHand);
|
var index = board.Hand.FindIndex(p => p.WhichPiece == pieceInHand);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
@@ -150,7 +155,7 @@ namespace Shogi.Domain
|
|||||||
return new MoveResult(true);
|
return new MoveResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsPathable(Vector2 from, Vector2 to)
|
private bool ShogiIsPathable(Vector2 from, Vector2 to)
|
||||||
{
|
{
|
||||||
var piece = board[from];
|
var piece = board[from];
|
||||||
if (piece == null) return false;
|
if (piece == null) return false;
|
||||||
@@ -255,7 +260,9 @@ namespace Shogi.Domain
|
|||||||
PathEvery(from, (other, position) =>
|
PathEvery(from, (other, position) =>
|
||||||
{
|
{
|
||||||
var simulationBoard = new StandardRules(new ShogiBoardState(board));
|
var simulationBoard = new StandardRules(new ShogiBoardState(board));
|
||||||
var simulationResult = simulationBoard.Move(from, position, false);
|
var fromNotation = ShogiBoardState.ToBoardNotation(from);
|
||||||
|
var toNotation = ShogiBoardState.ToBoardNotation(position);
|
||||||
|
var simulationResult = simulationBoard.Move(fromNotation, toNotation, false);
|
||||||
if (simulationResult.Success)
|
if (simulationResult.Success)
|
||||||
{
|
{
|
||||||
if (!EvaluateCheckAfterMove(from, position, board.InCheck.Value))
|
if (!EvaluateCheckAfterMove(from, position, board.InCheck.Value))
|
||||||
@@ -288,7 +295,7 @@ namespace Shogi.Domain
|
|||||||
if (piece == null) return false;
|
if (piece == null) return false;
|
||||||
|
|
||||||
var path = FindDirectionTowardsDestination(GetMoveSet(piece.WhichPiece).GetMoves(piece.IsUpsideDown), origin, destination);
|
var path = FindDirectionTowardsDestination(GetMoveSet(piece.WhichPiece).GetMoves(piece.IsUpsideDown), origin, destination);
|
||||||
if (!IsPathable(origin, destination))
|
if (!IsPathable(origin, destination, path.Direction))
|
||||||
{
|
{
|
||||||
// Assumption: if a single best-choice step towards the destination cannot happen, no pathing can happen.
|
// Assumption: if a single best-choice step towards the destination cannot happen, no pathing can happen.
|
||||||
return false;
|
return false;
|
||||||
@@ -341,6 +348,25 @@ namespace Shogi.Domain
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsPathable(Vector2 origin, Vector2 destination, Vector2 direction)
|
||||||
|
{
|
||||||
|
var next = Vector2.Add(origin, direction);
|
||||||
|
if (Vector2.Distance(next, destination) >= Vector2.Distance(origin, destination)) return false;
|
||||||
|
|
||||||
|
var slope = (destination.Y - origin.Y) / (destination.X - origin.X);
|
||||||
|
if (float.IsInfinity(slope))
|
||||||
|
{
|
||||||
|
return next.X == destination.X;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// b = -mx + y
|
||||||
|
var yIntercept = -slope * origin.X + origin.Y;
|
||||||
|
// y = mx + b
|
||||||
|
return next.Y == slope * next.X + yIntercept;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Path the line from origin to destination, ignoring any Paths defined by the element at origin.
|
/// Path the line from origin to destination, ignoring any Paths defined by the element at origin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user