diff --git a/Shogi.Domain/ValueObjects/BoardState.cs b/Shogi.Domain/ValueObjects/BoardState.cs
index bf679ec..03a51a6 100644
--- a/Shogi.Domain/ValueObjects/BoardState.cs
+++ b/Shogi.Domain/ValueObjects/BoardState.cs
@@ -182,16 +182,6 @@ public class BoardState
return new MoveResult(true);
}
- ///
- /// Returns true if the given path can be traversed without colliding into a piece.
- ///
- public bool IsPathBlocked(IEnumerable path)
- {
- return !path.Any()
- || path.SkipLast(1).Any(position => this[position] != null)
- || this[path.Last()]?.Owner == WhoseTurn;
- }
-
internal bool IsWithinPromotionZone(Vector2 position)
{
// TODO: Move this promotion zone logic into the StandardRules class.
diff --git a/Shogi.Domain/ValueObjects/Piece.cs b/Shogi.Domain/ValueObjects/Piece.cs
index 42316bd..51826d8 100644
--- a/Shogi.Domain/ValueObjects/Piece.cs
+++ b/Shogi.Domain/ValueObjects/Piece.cs
@@ -25,8 +25,6 @@ namespace Shogi.Domain.ValueObjects
public WhichPiece WhichPiece { get; }
public WhichPlayer Owner { get; private set; }
public bool IsPromoted { get; private set; }
- public bool IsUpsideDown => Owner == WhichPlayer.Player2;
-
protected Piece(WhichPiece piece, WhichPlayer owner, bool isPromoted = false)
{
WhichPiece = piece;
@@ -60,7 +58,7 @@ namespace Shogi.Domain.ValueObjects
{
var steps = new List(10);
- var path = MoveSet.GetNearestPath(start, end);
+ var path = GetNearestPath(MoveSet, start, end);
var position = start;
while (Vector2.Distance(start, position) < Vector2.Distance(start, end))
{
@@ -77,5 +75,25 @@ namespace Shogi.Domain.ValueObjects
return [];
}
+
+ private static Path GetNearestPath(IEnumerable paths, Vector2 start, Vector2 end)
+ {
+ if (!paths.DefaultIfEmpty().Any())
+ {
+ throw new ArgumentException("No paths to get nearest path from.");
+ }
+
+ var shortestPath = paths.First();
+ foreach (var path in paths.Skip(1))
+ {
+ var distance = Vector2.Distance(start + path.Step, end);
+ var shortestDistance = Vector2.Distance(start + shortestPath.Step, end);
+ if (distance < shortestDistance)
+ {
+ shortestPath = path;
+ }
+ }
+ return shortestPath;
+ }
}
}
diff --git a/Shogi.Domain/ValueObjects/ShogiBoard.cs b/Shogi.Domain/ValueObjects/ShogiBoard.cs
index 49a8035..1ddfa64 100644
--- a/Shogi.Domain/ValueObjects/ShogiBoard.cs
+++ b/Shogi.Domain/ValueObjects/ShogiBoard.cs
@@ -1,4 +1,5 @@
using Shogi.Domain.YetToBeAssimilatedIntoDDD;
+using System.Threading.Tasks;
namespace Shogi.Domain.ValueObjects;
///
@@ -8,18 +9,16 @@ namespace Shogi.Domain.ValueObjects;
///
public sealed class ShogiBoard
{
- private readonly StandardRules rules;
+ private static readonly int[] zeroToEight = [0, 1, 2, 3, 4, 5, 6, 7, 8];
private static readonly Vector2 BoardSize = new(9, 9);
public ShogiBoard(BoardState initialState)
{
BoardState = initialState;
- rules = new StandardRules(BoardState);
}
public BoardState BoardState { get; }
- private static readonly int[] zeroToEight = [0, 1, 2, 3, 4, 5, 6, 7, 8];
///
/// Move a piece from a board position to another board position, potentially capturing an opponents piece. Respects all rules of the game.
@@ -164,23 +163,100 @@ public sealed class ShogiBoard
: WhichPlayer.Player1;
}
- //if (rules.IsOpponentInCheckAfterMove())
- //{
- // BoardState.InCheck = otherPlayer;
- // // A pawn, placed from the hand, cannot be the cause of checkmate.
+ // A pawn, placed from the hand, cannot be the cause of checkmate.
// if (rules.IsOpponentInCheckMate() && pieceInHand != WhichPiece.Pawn)
// {
// BoardState.IsCheckmate = true;
// }
- //}
return new MoveResult(true);
}
- public GameOverResult EvaluateGameOver()
+ private async Task EvaluateGameOver()
{
+
+ if (!BoardState.InCheck.HasValue)
+ {
+ return GameOverResult.GameIsNotOver;
+ }
+ var kingInCheck = BoardState.State
+ .Single(kvp => kvp.Value?.WhichPiece == WhichPiece.King && kvp.Value?.Owner == BoardState.InCheck);
+ var kingInCheckVectorPosition = Notation.FromBoardNotation(kingInCheck.Key);
+
+ var piecesOfPlayerInCheck = BoardState.State
+ .Where(kvp => kvp.Value?.Owner == BoardState.InCheck)
+ .Cast>();
+
+ foreach (var (notation, piece) in piecesOfPlayerInCheck)
+ {
+ // Get possible locations this piece could move to.
+ var allPossibleMoves = GetPossiblePositionsForPiece(Notation.FromBoardNotation(notation), piece);
+ // Try to make a legal move, disproving checkmate.
+ foreach (var move in allPossibleMoves)
+ {
+ var simState = new BoardState(BoardState);
+ simState.Move(notation, Notation.ToBoardNotation(move), false);
+ var inCheckResult = IsEitherPlayerInCheck(simState);
+ if (inCheckResult == InCheckResult.)
+ }
+ }
+
+ // while (tasks.Any()) var result = Task.WhenAny(tasks); // Then check for GameIsNotOver and maybe return early.
+
+
+ var gameOverResult = BoardState.State
+ .Where(kvp => kvp.Value != null)
+ .Cast>()
+ .Aggregate(GameOverResult.GameIsNotOver, (inCheckResult, kvp) =>
+ {
+ var newInCheckResult = inCheckResult;
+ var threatPiece = kvp.Value;
+ var opposingKingPosition = Notation.FromBoardNotation(kingInCheck.Single(king => king.Value.Owner != threatPiece.Owner).Key);
+ var positionsThreatened = threatPiece.GetPathFromStartToEnd(Notation.FromBoardNotation(kvp.Key), opposingKingPosition);
+
+ foreach (var position in positionsThreatened)
+ {
+ // No piece at this position, so pathing is unobstructed. Continue pathing.
+ if (simState[position] == null) continue;
+
+ var threatenedPiece = simState[position]!;
+ if (threatenedPiece.WhichPiece == WhichPiece.King && threatenedPiece.Owner != threatPiece.Owner)
+ {
+ newInCheckResult |= threatenedPiece.Owner == WhichPlayer.Player1 ? InCheckResult.Player1InCheck : InCheckResult.Player2InCheck;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return newInCheckResult;
+ });
+
return GameOverResult.GameIsNotOver;
+
+ Vector2[] GetPossiblePositionsForPiece(Vector2 piecePosition, Piece piece)
+ {
+ var paths = piece.MoveSet;
+ return
+ paths
+ .SelectMany(path =>
+ {
+ var list = new List(10);
+ var position = path.Step + piecePosition;
+ while (position.IsInsideBoardBoundary())
+ {
+ list.Add(position);
+ position += path.Step;
+ }
+
+ return list;
+ })
+ // Where tile at position is empty, meaning the piece could move there.
+ .Where(newPosition => BoardState[newPosition] == null)
+ .ToArray();
+ }
}
private static InCheckResult IsEitherPlayerInCheck(BoardState simState)
diff --git a/Shogi.Domain/YetToBeAssimilatedIntoDDD/Pathing/Path.cs b/Shogi.Domain/YetToBeAssimilatedIntoDDD/Pathing/Path.cs
index c4bf9be..4be696e 100644
--- a/Shogi.Domain/YetToBeAssimilatedIntoDDD/Pathing/Path.cs
+++ b/Shogi.Domain/YetToBeAssimilatedIntoDDD/Pathing/Path.cs
@@ -20,34 +20,4 @@ public record Path
Step = step;
this.Distance = distance;
}
- public Path Invert() => new(Vector2.Negate(Step), Distance);
-
- //public enum PathingResult
- //{
- // Obstructed,
- // CompletedWithoutObstruction
- //}
-}
-
-public static class PathExtensions
-{
- public static Path GetNearestPath(this IEnumerable paths, Vector2 start, Vector2 end)
- {
- if (!paths.DefaultIfEmpty().Any())
- {
- throw new ArgumentException("No paths to get nearest path from.");
- }
-
- var shortestPath = paths.First();
- foreach (var path in paths.Skip(1))
- {
- var distance = Vector2.Distance(start + path.Step, end);
- var shortestDistance = Vector2.Distance(start + shortestPath.Step, end);
- if (distance < shortestDistance)
- {
- shortestPath = path;
- }
- }
- return shortestPath;
- }
-}
+ public Path Invert() => new(Vector2.Negate(Step), Distance);
\ No newline at end of file