This commit is contained in:
2024-09-26 21:11:47 -05:00
parent 6b5bb96de7
commit 81dd267290
16 changed files with 636 additions and 339 deletions

View File

@@ -0,0 +1,11 @@
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
namespace Shogi.Domain.Other;
public record PieceRulesRegistration<TPiece>(TPiece WhichPiece, ICollection<Path> MoveSet) where TPiece : Enum
{
}
public record PieceInPlay<TPiece>(TPiece WhichPiece, int OwningPlayerNumber) where TPiece : Enum
{
}

View File

@@ -0,0 +1,61 @@
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
using System.Drawing;
namespace Shogi.Domain.Other;
public class Rules<TPiece> where TPiece : Enum
{
private Vector2 boardSize;
private TPiece theKing;
private Dictionary<TPiece, ICollection<Path>> piecePaths;
/// <summary>
/// Begin a new set of rules. If any rules already exist, this method will erase them.
/// </summary>
/// <param name="boardSize">The size of the game board in tiles. For examples, Chess is 8x8 and Shogi is 9x9.</param>
/// <param name="king">The piece that represents the King for each player or the piece that, when lost, results in losing the game.</param>
public Rules<TPiece> CreateNewRules(Vector2 boardSize, TPiece king)
{
this.boardSize = boardSize;
theKing = king;
piecePaths = [];
return this;
}
public Rules<TPiece> RegisterPieceWithRules(PieceRulesRegistration<TPiece> pieceToRegister)
{
if (piecePaths.ContainsKey(pieceToRegister.WhichPiece))
{
throw new ArgumentException("This type of piece has already been registered.", nameof(pieceToRegister));
}
piecePaths.Add(pieceToRegister.WhichPiece, pieceToRegister.MoveSet);
return this;
}
public int ValidateMove(Vector2 start, Vector2 end, TPiece[][] board)
{
if (board.GetLength(0) != boardSize.X || board.GetLength(1) != boardSize.Y)
{
throw new ArgumentException($"2D array dimensions must match boardSize given during {nameof(CreateNewRules)} method.", nameof(board));
}
if (start - start != Vector2.Zero)
{
throw new ArgumentException("Negative values not allowed.", nameof(start));
}
if (end - end != Vector2.Zero)
{
throw new ArgumentException("Negative values not allowed.", nameof(end));
}
if (start.X >= boardSize.X || start.Y >= boardSize.Y)
{
throw new ArgumentException("Start position must be within the given boardSize.", nameof(start));
}
if (end.X >= boardSize.X || end.Y >= boardSize.Y)
{
throw new ArgumentException("End position must be within the given boardSize.", nameof(end));
}
}
}

View File

@@ -7,22 +7,22 @@ namespace Shogi.Domain.ValueObjects
{
private static readonly ReadOnlyCollection<Path> BishopPaths = new(new List<Path>(4)
{
new Path(Direction.UpLeft, Distance.MultiStep),
new Path(Direction.UpRight, Distance.MultiStep),
new Path(Direction.DownLeft, Distance.MultiStep),
new Path(Direction.DownRight, Distance.MultiStep)
new Path(Direction.ForwardLeft, Distance.MultiStep),
new Path(Direction.ForwardRight, Distance.MultiStep),
new Path(Direction.BackwardLeft, Distance.MultiStep),
new Path(Direction.BackwardRight, Distance.MultiStep)
});
public static readonly ReadOnlyCollection<Path> PromotedBishopPaths = new(new List<Path>(8)
{
new Path(Direction.Up),
new Path(Direction.Forward),
new Path(Direction.Left),
new Path(Direction.Right),
new Path(Direction.Down),
new Path(Direction.UpLeft, Distance.MultiStep),
new Path(Direction.UpRight, Distance.MultiStep),
new Path(Direction.DownLeft, Distance.MultiStep),
new Path(Direction.DownRight, Distance.MultiStep)
new Path(Direction.Backward),
new Path(Direction.ForwardLeft, Distance.MultiStep),
new Path(Direction.ForwardRight, Distance.MultiStep),
new Path(Direction.BackwardLeft, Distance.MultiStep),
new Path(Direction.BackwardRight, Distance.MultiStep)
});
public static readonly ReadOnlyCollection<Path> Player2Paths =

View File

@@ -6,247 +6,249 @@ namespace Shogi.Domain.ValueObjects;
public class BoardState
{
/// <summary>
/// Board state before any moves have been made, using standard setup and rules.
/// </summary>
public static BoardState StandardStarting => new(
state: BuildStandardStartingBoardState(),
player1Hand: new(),
player2Hand: new(),
whoseTurn: WhichPlayer.Player1,
playerInCheck: null,
previousMove: new Move());
/// <summary>
/// Board state before any moves have been made, using standard setup and rules.
/// </summary>
public static BoardState StandardStarting => new(
state: BuildStandardStartingBoardState(),
player1Hand: [],
player2Hand: [],
whoseTurn: WhichPlayer.Player1,
playerInCheck: null,
previousMove: new Move());
/// <summary>
/// Key is position notation, such as "E4".
/// </summary>
private readonly Dictionary<string, Piece?> board;
/// <summary>
/// Key is position notation, such as "E4".
/// </summary>
private readonly Dictionary<string, Piece?> board;
public BoardState(
Dictionary<string, Piece?> state,
List<Piece> player1Hand,
List<Piece> player2Hand,
WhichPlayer whoseTurn,
WhichPlayer? playerInCheck,
Move previousMove)
{
board = state;
Player1Hand = player1Hand;
Player2Hand = player2Hand;
PreviousMove = previousMove;
WhoseTurn = whoseTurn;
InCheck = playerInCheck;
}
public BoardState(
Dictionary<string, Piece?> state,
List<Piece> player1Hand,
List<Piece> player2Hand,
WhichPlayer whoseTurn,
WhichPlayer? playerInCheck,
Move previousMove)
{
board = state;
Player1Hand = player1Hand;
Player2Hand = player2Hand;
PreviousMove = previousMove;
WhoseTurn = whoseTurn;
InCheck = playerInCheck;
}
/// <summary>
/// Copy constructor.
/// </summary>
public BoardState(BoardState other)
{
board = new(81);
foreach (var kvp in other.board)
{
var piece = kvp.Value;
board[kvp.Key] = piece == null ? null : Piece.Create(piece.WhichPiece, piece.Owner, piece.IsPromoted);
}
WhoseTurn = other.WhoseTurn;
InCheck = other.InCheck;
IsCheckmate = other.IsCheckmate;
PreviousMove = other.PreviousMove;
Player1Hand = new(other.Player1Hand);
Player2Hand = new(other.Player2Hand);
}
/// <summary>
/// Copy constructor.
/// </summary>
public BoardState(BoardState other)
{
board = new(81);
foreach (var kvp in other.board)
{
var piece = kvp.Value;
board[kvp.Key] = piece == null ? null : Piece.Create(piece.WhichPiece, piece.Owner, piece.IsPromoted);
}
WhoseTurn = other.WhoseTurn;
InCheck = other.InCheck;
IsCheckmate = other.IsCheckmate;
PreviousMove = other.PreviousMove;
Player1Hand = new(other.Player1Hand);
Player2Hand = new(other.Player2Hand);
}
public ReadOnlyDictionary<string, Piece?> State => new(board);
public List<Piece> ActivePlayerHand => WhoseTurn == WhichPlayer.Player1 ? Player1Hand : Player2Hand;
public Vector2 Player1KingPosition => Notation.FromBoardNotation(board.Where(kvp => kvp.Value != null).Single(kvp =>
{
var piece = kvp.Value;
return piece!.IsKing() && piece!.Owner == WhichPlayer.Player1;
}).Key);
public Vector2 Player2KingPosition => Notation.FromBoardNotation(board.Where(kvp => kvp.Value != null).Single(kvp =>
{
var piece = kvp.Value;
return piece!.IsKing() && piece!.Owner == WhichPlayer.Player2;
}).Key);
public List<Piece> Player1Hand { get; }
public List<Piece> Player2Hand { get; }
public Move PreviousMove { get; set; }
public WhichPlayer WhoseTurn { get; set; }
public WhichPlayer? InCheck { get; set; }
public bool IsCheckmate { get; set; }
public ReadOnlyDictionary<string, Piece?> State => new(board);
public List<Piece> ActivePlayerHand => WhoseTurn == WhichPlayer.Player1 ? Player1Hand : Player2Hand;
public Vector2 Player1KingPosition => Notation.FromBoardNotation(board.Where(kvp => kvp.Value != null).Single(kvp =>
{
var piece = kvp.Value;
return piece!.IsKing() && piece!.Owner == WhichPlayer.Player1;
}).Key);
public Vector2 Player2KingPosition => Notation.FromBoardNotation(board.Where(kvp => kvp.Value != null).Single(kvp =>
{
var piece = kvp.Value;
return piece!.IsKing() && piece!.Owner == WhichPlayer.Player2;
}).Key);
public List<Piece> Player1Hand { get; }
public List<Piece> Player2Hand { get; }
public Move PreviousMove { get; set; }
public WhichPlayer WhoseTurn { get; set; }
public WhichPlayer? InCheck { get; set; }
public bool IsCheckmate { get; set; }
public Piece? this[string notation]
{
// TODO: Validate "notation" here and throw an exception if invalid.
get => board[notation];
set => board[notation] = value;
}
public Piece? this[string notation]
{
// TODO: Validate "notation" here and throw an exception if invalid.
get => board[notation];
set => board[notation] = value;
}
public Piece? this[Vector2 vector]
{
get => this[Notation.ToBoardNotation(vector)];
set => this[Notation.ToBoardNotation(vector)] = value;
}
public Piece? this[Vector2 vector]
{
get => this[Notation.ToBoardNotation(vector)];
set => this[Notation.ToBoardNotation(vector)] = value;
}
public Piece? this[int x, int y]
{
get => this[Notation.ToBoardNotation(x, y)];
set => this[Notation.ToBoardNotation(x, y)] = value;
}
public Piece? this[int x, int y]
{
get => this[Notation.ToBoardNotation(x, y)];
set => this[Notation.ToBoardNotation(x, y)] = value;
}
/// <summary>
/// Returns true if the given path can be traversed without colliding into a piece.
/// </summary>
public bool IsPathBlocked(IEnumerable<Vector2> path)
{
return !path.Any()
|| path.SkipLast(1).Any(position => this[position] != null)
|| this[path.Last()]?.Owner == WhoseTurn;
}
/// <summary>
/// Returns true if the given path can be traversed without colliding into a piece.
/// </summary>
public bool IsPathBlocked(IEnumerable<Vector2> 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.
return WhoseTurn == WhichPlayer.Player1 && position.Y > 5
|| WhoseTurn == WhichPlayer.Player2 && position.Y < 3;
}
internal bool IsWithinPromotionZone(Vector2 position)
{
internal static bool IsWithinBoardBoundary(Vector2 position)
{
return position.X <= 8 && position.X >= 0
&& position.Y <= 8 && position.Y >= 0;
}
internal List<BoardTile> GetTilesOccupiedBy(WhichPlayer whichPlayer) => board
.Where(kvp => kvp.Value?.Owner == whichPlayer)
.Select(kvp => new BoardTile(Notation.FromBoardNotation(kvp.Key), kvp.Value!))
.ToList();
// TODO: Move this promotion zone logic into the StandardRules class.
return WhoseTurn == WhichPlayer.Player1 && position.Y > 5
|| WhoseTurn == WhichPlayer.Player2 && position.Y < 3;
}
internal void Capture(Vector2 to)
{
var piece = this[to];
if (piece == null) throw new InvalidOperationException("Cannot capture. Piece at position does not exist.");
internal static bool IsWithinBoardBoundary(Vector2 position)
{
return position.X <= 8 && position.X >= 0
&& position.Y <= 8 && position.Y >= 0;
}
piece.Capture(WhoseTurn);
ActivePlayerHand.Add(piece);
}
internal List<BoardTile> GetTilesOccupiedBy(WhichPlayer whichPlayer) => board
.Where(kvp => kvp.Value?.Owner == whichPlayer)
.Select(kvp => new BoardTile(Notation.FromBoardNotation(kvp.Key), kvp.Value!))
.ToList();
/// <summary>
/// Does not include the start position.
/// </summary>
internal static IEnumerable<Vector2> GetPathAlongDirectionFromStartToEdgeOfBoard(Vector2 start, Vector2 direction)
{
var next = start;
while (IsWithinBoardBoundary(next + direction))
{
next += direction;
yield return next;
}
}
internal void Capture(Vector2 to)
{
var piece = this[to];
if (piece == null) throw new InvalidOperationException("Cannot capture. Piece at position does not exist.");
internal Piece? QueryFirstPieceInPath(IEnumerable<Vector2> path)
{
foreach (var step in path)
{
if (this[step] != null) return this[step];
}
return null;
}
piece.Capture(WhoseTurn);
ActivePlayerHand.Add(piece);
}
private static Dictionary<string, Piece?> BuildStandardStartingBoardState()
{
return new Dictionary<string, Piece?>(81)
{
["A1"] = new Lance(WhichPlayer.Player1),
["B1"] = new Knight(WhichPlayer.Player1),
["C1"] = new SilverGeneral(WhichPlayer.Player1),
["D1"] = new GoldGeneral(WhichPlayer.Player1),
["E1"] = new King(WhichPlayer.Player1),
["F1"] = new GoldGeneral(WhichPlayer.Player1),
["G1"] = new SilverGeneral(WhichPlayer.Player1),
["H1"] = new Knight(WhichPlayer.Player1),
["I1"] = new Lance(WhichPlayer.Player1),
/// <summary>
/// Does not include the start position.
/// </summary>
internal static IEnumerable<Vector2> GetPathAlongDirectionFromStartToEdgeOfBoard(Vector2 start, Vector2 direction)
{
var next = start;
while (IsWithinBoardBoundary(next + direction))
{
next += direction;
yield return next;
}
}
["A2"] = null,
["B2"] = new Bishop(WhichPlayer.Player1),
["C2"] = null,
["D2"] = null,
["E2"] = null,
["F2"] = null,
["G2"] = null,
["H2"] = new Rook(WhichPlayer.Player1),
["I2"] = null,
internal Piece? QueryFirstPieceInPath(IEnumerable<Vector2> path)
{
foreach (var step in path)
{
if (this[step] != null) return this[step];
}
return null;
}
["A3"] = new Pawn(WhichPlayer.Player1),
["B3"] = new Pawn(WhichPlayer.Player1),
["C3"] = new Pawn(WhichPlayer.Player1),
["D3"] = new Pawn(WhichPlayer.Player1),
["E3"] = new Pawn(WhichPlayer.Player1),
["F3"] = new Pawn(WhichPlayer.Player1),
["G3"] = new Pawn(WhichPlayer.Player1),
["H3"] = new Pawn(WhichPlayer.Player1),
["I3"] = new Pawn(WhichPlayer.Player1),
private static Dictionary<string, Piece?> BuildStandardStartingBoardState()
{
return new Dictionary<string, Piece?>(81)
{
["A1"] = new Lance(WhichPlayer.Player1),
["B1"] = new Knight(WhichPlayer.Player1),
["C1"] = new SilverGeneral(WhichPlayer.Player1),
["D1"] = new GoldGeneral(WhichPlayer.Player1),
["E1"] = new King(WhichPlayer.Player1),
["F1"] = new GoldGeneral(WhichPlayer.Player1),
["G1"] = new SilverGeneral(WhichPlayer.Player1),
["H1"] = new Knight(WhichPlayer.Player1),
["I1"] = new Lance(WhichPlayer.Player1),
["A4"] = null,
["B4"] = null,
["C4"] = null,
["D4"] = null,
["E4"] = null,
["F4"] = null,
["G4"] = null,
["H4"] = null,
["I4"] = null,
["A2"] = null,
["B2"] = new Bishop(WhichPlayer.Player1),
["C2"] = null,
["D2"] = null,
["E2"] = null,
["F2"] = null,
["G2"] = null,
["H2"] = new Rook(WhichPlayer.Player1),
["I2"] = null,
["A5"] = null,
["B5"] = null,
["C5"] = null,
["D5"] = null,
["E5"] = null,
["F5"] = null,
["G5"] = null,
["H5"] = null,
["I5"] = null,
["A3"] = new Pawn(WhichPlayer.Player1),
["B3"] = new Pawn(WhichPlayer.Player1),
["C3"] = new Pawn(WhichPlayer.Player1),
["D3"] = new Pawn(WhichPlayer.Player1),
["E3"] = new Pawn(WhichPlayer.Player1),
["F3"] = new Pawn(WhichPlayer.Player1),
["G3"] = new Pawn(WhichPlayer.Player1),
["H3"] = new Pawn(WhichPlayer.Player1),
["I3"] = new Pawn(WhichPlayer.Player1),
["A6"] = null,
["B6"] = null,
["C6"] = null,
["D6"] = null,
["E6"] = null,
["F6"] = null,
["G6"] = null,
["H6"] = null,
["I6"] = null,
["A4"] = null,
["B4"] = null,
["C4"] = null,
["D4"] = null,
["E4"] = null,
["F4"] = null,
["G4"] = null,
["H4"] = null,
["I4"] = null,
["A7"] = new Pawn(WhichPlayer.Player2),
["B7"] = new Pawn(WhichPlayer.Player2),
["C7"] = new Pawn(WhichPlayer.Player2),
["D7"] = new Pawn(WhichPlayer.Player2),
["E7"] = new Pawn(WhichPlayer.Player2),
["F7"] = new Pawn(WhichPlayer.Player2),
["G7"] = new Pawn(WhichPlayer.Player2),
["H7"] = new Pawn(WhichPlayer.Player2),
["I7"] = new Pawn(WhichPlayer.Player2),
["A5"] = null,
["B5"] = null,
["C5"] = null,
["D5"] = null,
["E5"] = null,
["F5"] = null,
["G5"] = null,
["H5"] = null,
["I5"] = null,
["A8"] = null,
["B8"] = new Rook(WhichPlayer.Player2),
["C8"] = null,
["D8"] = null,
["E8"] = null,
["F8"] = null,
["G8"] = null,
["H8"] = new Bishop(WhichPlayer.Player2),
["I8"] = null,
["A6"] = null,
["B6"] = null,
["C6"] = null,
["D6"] = null,
["E6"] = null,
["F6"] = null,
["G6"] = null,
["H6"] = null,
["I6"] = null,
["A9"] = new Lance(WhichPlayer.Player2),
["B9"] = new Knight(WhichPlayer.Player2),
["C9"] = new SilverGeneral(WhichPlayer.Player2),
["D9"] = new GoldGeneral(WhichPlayer.Player2),
["E9"] = new King(WhichPlayer.Player2),
["F9"] = new GoldGeneral(WhichPlayer.Player2),
["G9"] = new SilverGeneral(WhichPlayer.Player2),
["H9"] = new Knight(WhichPlayer.Player2),
["I9"] = new Lance(WhichPlayer.Player2)
};
}
["A7"] = new Pawn(WhichPlayer.Player2),
["B7"] = new Pawn(WhichPlayer.Player2),
["C7"] = new Pawn(WhichPlayer.Player2),
["D7"] = new Pawn(WhichPlayer.Player2),
["E7"] = new Pawn(WhichPlayer.Player2),
["F7"] = new Pawn(WhichPlayer.Player2),
["G7"] = new Pawn(WhichPlayer.Player2),
["H7"] = new Pawn(WhichPlayer.Player2),
["I7"] = new Pawn(WhichPlayer.Player2),
["A8"] = null,
["B8"] = new Rook(WhichPlayer.Player2),
["C8"] = null,
["D8"] = null,
["E8"] = null,
["F8"] = null,
["G8"] = null,
["H8"] = new Bishop(WhichPlayer.Player2),
["I8"] = null,
["A9"] = new Lance(WhichPlayer.Player2),
["B9"] = new Knight(WhichPlayer.Player2),
["C9"] = new SilverGeneral(WhichPlayer.Player2),
["D9"] = new GoldGeneral(WhichPlayer.Player2),
["E9"] = new King(WhichPlayer.Player2),
["F9"] = new GoldGeneral(WhichPlayer.Player2),
["G9"] = new SilverGeneral(WhichPlayer.Player2),
["H9"] = new Knight(WhichPlayer.Player2),
["I9"] = new Lance(WhichPlayer.Player2)
};
}
}

View File

@@ -7,12 +7,12 @@ namespace Shogi.Domain.ValueObjects
{
public static readonly ReadOnlyCollection<Path> Player1Paths = new(new List<Path>(6)
{
new Path(Direction.Up),
new Path(Direction.UpLeft),
new Path(Direction.UpRight),
new Path(Direction.Forward),
new Path(Direction.ForwardLeft),
new Path(Direction.ForwardRight),
new Path(Direction.Left),
new Path(Direction.Right),
new Path(Direction.Down)
new Path(Direction.Backward)
});
public static readonly ReadOnlyCollection<Path> Player2Paths =

View File

@@ -7,14 +7,14 @@ namespace Shogi.Domain.ValueObjects
{
internal static readonly ReadOnlyCollection<Path> KingPaths = new(new List<Path>(8)
{
new Path(Direction.Up),
new Path(Direction.Forward),
new Path(Direction.Left),
new Path(Direction.Right),
new Path(Direction.Down),
new Path(Direction.UpLeft),
new Path(Direction.UpRight),
new Path(Direction.DownLeft),
new Path(Direction.DownRight)
new Path(Direction.Backward),
new Path(Direction.ForwardLeft),
new Path(Direction.ForwardRight),
new Path(Direction.BackwardLeft),
new Path(Direction.BackwardRight)
});
public King(WhichPlayer owner, bool isPromoted = false)

View File

@@ -7,7 +7,7 @@ namespace Shogi.Domain.ValueObjects
{
public static readonly ReadOnlyCollection<Path> Player1Paths = new(new List<Path>(1)
{
new Path(Direction.Up, Distance.MultiStep),
new Path(Direction.Forward, Distance.MultiStep),
});
public static readonly ReadOnlyCollection<Path> Player2Paths =

View File

@@ -7,7 +7,7 @@ namespace Shogi.Domain.ValueObjects
{
public static readonly ReadOnlyCollection<Path> Player1Paths = new(new List<Path>(1)
{
new Path(Direction.Up)
new Path(Direction.Forward)
});
public static readonly ReadOnlyCollection<Path> Player2Paths =

View File

@@ -7,22 +7,22 @@ public record class Rook : Piece
{
public static readonly ReadOnlyCollection<Path> Player1Paths = new(new List<Path>(4)
{
new Path(Direction.Up, Distance.MultiStep),
new Path(Direction.Forward, Distance.MultiStep),
new Path(Direction.Left, Distance.MultiStep),
new Path(Direction.Right, Distance.MultiStep),
new Path(Direction.Down, Distance.MultiStep)
new Path(Direction.Backward, Distance.MultiStep)
});
private static readonly ReadOnlyCollection<Path> PromotedPlayer1Paths = new(new List<Path>(8)
{
new Path(Direction.Up, Distance.MultiStep),
new Path(Direction.Forward, Distance.MultiStep),
new Path(Direction.Left, Distance.MultiStep),
new Path(Direction.Right, Distance.MultiStep),
new Path(Direction.Down, Distance.MultiStep),
new Path(Direction.UpLeft),
new Path(Direction.UpRight),
new Path(Direction.DownLeft),
new Path(Direction.DownRight)
new Path(Direction.Backward, Distance.MultiStep),
new Path(Direction.ForwardLeft),
new Path(Direction.ForwardRight),
new Path(Direction.BackwardLeft),
new Path(Direction.BackwardRight)
});
public static readonly ReadOnlyCollection<Path> Player2Paths =

View File

@@ -7,11 +7,11 @@ namespace Shogi.Domain.ValueObjects
{
public static readonly ReadOnlyCollection<Path> Player1Paths = new(new List<Path>(4)
{
new Path(Direction.Up),
new Path(Direction.UpLeft),
new Path(Direction.UpRight),
new Path(Direction.DownLeft),
new Path(Direction.DownRight)
new Path(Direction.Forward),
new Path(Direction.ForwardLeft),
new Path(Direction.ForwardRight),
new Path(Direction.BackwardLeft),
new Path(Direction.BackwardRight)
});
public static readonly ReadOnlyCollection<Path> Player2Paths =

View File

@@ -1,14 +1,19 @@
namespace Shogi.Domain.ValueObjects
namespace Shogi.Domain.ValueObjects;
public enum WhichPiece
{
public enum WhichPiece
{
King,
GoldGeneral,
SilverGeneral,
Bishop,
Rook,
Knight,
Lance,
Pawn
}
King,
GoldGeneral,
SilverGeneral,
PromotedSilverGeneral,
Bishop,
PromotedBishop,
Rook,
PromotedRook,
Knight,
PromotedKnight,
Lance,
PromotedLance,
Pawn
PromotedPawn
}

View File

@@ -1,22 +1,15 @@
using System.Numerics;
namespace Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
namespace Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing
{
/// <summary>
/// Directions are relative to the perspective of Player 1.
/// Up points towards player 1. Down points towards player 2.
/// </summary>
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);
}
}
public static class Direction
{
public static readonly Vector2 Forward = new(0, 1);
public static readonly Vector2 Backward = new(0, -1);
public static readonly Vector2 Left = new(-1, 0);
public static readonly Vector2 Right = new(1, 0);
public static readonly Vector2 ForwardLeft = new(-1, 1);
public static readonly Vector2 ForwardRight = new(1, 1);
public static readonly Vector2 BackwardLeft = new(-1, -1);
public static readonly Vector2 BackwardRight = new(1, -1);
public static readonly Vector2 KnightLeft = new(-1, 2);
public static readonly Vector2 KnightRight = new(1, 2);
}

View File

@@ -1,41 +1,33 @@
using System.Diagnostics;
namespace Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing
namespace Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
[DebuggerDisplay("{Direction} - {Distance}")]
public record Path(Vector2 Direction, Distance Distance = Distance.OneStep)
{
[DebuggerDisplay("{Direction} - {Distance}")]
public class Path
{
public Vector2 Direction { get; }
public Distance Distance { get; }
public Path(Vector2 direction, Distance distance = Distance.OneStep)
{
Direction = direction;
Distance = distance;
}
public Path Invert() => new(Vector2.Negate(Direction), Distance);
}
public static class PathExtensions
{
public static Path GetNearestPath(this IEnumerable<Path> 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.Direction, end);
var shortestDistance = Vector2.Distance(start + shortestPath.Direction, end);
if (distance < shortestDistance)
{
shortestPath = path;
}
}
return shortestPath;
}
}
public Path Invert() => new(Vector2.Negate(Direction), Distance);
}
public static class PathExtensions
{
public static Path GetNearestPath(this IEnumerable<Path> 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.Direction, end);
var shortestDistance = Vector2.Distance(start + shortestPath.Direction, end);
if (distance < shortestDistance)
{
shortestPath = path;
}
}
return shortestPath;
}
}