100 lines
2.9 KiB
C#
100 lines
2.9 KiB
C#
using Shogi.Domain.ValueObjects.Movement;
|
|
using System.Diagnostics;
|
|
|
|
namespace Shogi.Domain.ValueObjects
|
|
{
|
|
[DebuggerDisplay("{WhichPiece} {Owner}")]
|
|
public abstract record class Piece
|
|
{
|
|
public static Piece Create(WhichPiece piece, WhichPlayer owner, bool isPromoted = false)
|
|
{
|
|
return piece switch
|
|
{
|
|
WhichPiece.King => new King(owner, isPromoted),
|
|
WhichPiece.GoldGeneral => new GoldGeneral(owner, isPromoted),
|
|
WhichPiece.SilverGeneral => new SilverGeneral(owner, isPromoted),
|
|
WhichPiece.Bishop => new Bishop(owner, isPromoted),
|
|
WhichPiece.Rook => new Rook(owner, isPromoted),
|
|
WhichPiece.Knight => new Knight(owner, isPromoted),
|
|
WhichPiece.Lance => new Lance(owner, isPromoted),
|
|
WhichPiece.Pawn => new Pawn(owner, isPromoted),
|
|
_ => throw new ArgumentException($"Unknown {nameof(WhichPiece)} when cloning a {nameof(Piece)}.")
|
|
};
|
|
}
|
|
public abstract IEnumerable<Path> MoveSet { get; }
|
|
public WhichPiece WhichPiece { get; }
|
|
public WhichPlayer Owner { get; private set; }
|
|
public bool IsPromoted { get; private set; }
|
|
protected Piece(WhichPiece piece, WhichPlayer owner, bool isPromoted = false)
|
|
{
|
|
WhichPiece = piece;
|
|
Owner = owner;
|
|
IsPromoted = isPromoted;
|
|
}
|
|
|
|
public bool CanPromote => !IsPromoted
|
|
&& WhichPiece != WhichPiece.King
|
|
&& WhichPiece != WhichPiece.GoldGeneral;
|
|
|
|
public void Promote() => IsPromoted = CanPromote;
|
|
|
|
/// <summary>
|
|
/// Prep the piece for capture by changing ownership and demoting.
|
|
/// </summary>
|
|
public void Capture(WhichPlayer newOwner)
|
|
{
|
|
Owner = newOwner;
|
|
IsPromoted = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Respecting the move-set of the Piece, collect all positions along the shortest path from start to end.
|
|
/// Useful if you need to iterate a move-set.
|
|
/// </summary>
|
|
/// <param name="start"></param>
|
|
/// <param name="end"></param>
|
|
/// <returns>An empty list if the piece cannot legally traverse from start to end. Otherwise, a list of positions.</returns>
|
|
public IEnumerable<Vector2> GetPathFromStartToEnd(Vector2 start, Vector2 end)
|
|
{
|
|
var steps = new List<Vector2>(10);
|
|
|
|
var path = GetNearestPath(MoveSet, start, end);
|
|
var position = start;
|
|
while (Vector2.Distance(start, position) < Vector2.Distance(start, end))
|
|
{
|
|
position += path.Step;
|
|
steps.Add(position);
|
|
|
|
if (path.Distance == Distance.OneStep) break;
|
|
}
|
|
|
|
if (position == end)
|
|
{
|
|
return steps;
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
private static Path GetNearestPath(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.Step, end);
|
|
var shortestDistance = Vector2.Distance(start + shortestPath.Step, end);
|
|
if (distance < shortestDistance)
|
|
{
|
|
shortestPath = path;
|
|
}
|
|
}
|
|
return shortestPath;
|
|
}
|
|
}
|
|
}
|