yep
This commit is contained in:
@@ -1,91 +1,126 @@
|
||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
||||
using Shogi.Domain.Other;
|
||||
using Shogi.Domain.YetToBeAssimilatedIntoDDD.Pathing;
|
||||
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; }
|
||||
public bool IsUpsideDown => Owner == WhichPlayer.Player2;
|
||||
[DebuggerDisplay("{WhichPiece} {Owner}")]
|
||||
public abstract record class Piece : IRulesLifecycle<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; }
|
||||
public bool IsUpsideDown => Owner == WhichPlayer.Player2;
|
||||
|
||||
protected Piece(WhichPiece piece, WhichPlayer owner, bool isPromoted = false)
|
||||
{
|
||||
WhichPiece = piece;
|
||||
Owner = owner;
|
||||
IsPromoted = isPromoted;
|
||||
}
|
||||
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 bool CanPromote => !IsPromoted
|
||||
&& WhichPiece != WhichPiece.King
|
||||
&& WhichPiece != WhichPiece.GoldGeneral;
|
||||
|
||||
public void Promote() => IsPromoted = CanPromote;
|
||||
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>
|
||||
/// 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);
|
||||
/// <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 = MoveSet.GetNearestPath(start, end);
|
||||
var position = start;
|
||||
while (Vector2.Distance(start, position) < Vector2.Distance(start, end))
|
||||
{
|
||||
position += path.Direction;
|
||||
steps.Add(position);
|
||||
var path = MoveSet.GetNearestPath(start, end);
|
||||
var position = start;
|
||||
while (Vector2.Distance(start, position) < Vector2.Distance(start, end))
|
||||
{
|
||||
position += path.NormalizedDirection;
|
||||
steps.Add(position);
|
||||
|
||||
if (path.Distance == Distance.OneStep) break;
|
||||
}
|
||||
if (path.Distance == Distance.OneStep) break;
|
||||
}
|
||||
|
||||
if (position == end)
|
||||
{
|
||||
return steps;
|
||||
}
|
||||
if (position == end)
|
||||
{
|
||||
return steps;
|
||||
}
|
||||
|
||||
return Array.Empty<Vector2>();
|
||||
}
|
||||
return Array.Empty<Vector2>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all positions this piece could move to from the currentPosition, respecting the move-set of this piece.
|
||||
/// </summary>
|
||||
/// <param name="currentPosition"></param>
|
||||
/// <returns>A list of positions the piece could move to.</returns>
|
||||
public IEnumerable<Vector2> GetPossiblePositions(Vector2 currentPosition)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
#region IRulesLifecycle
|
||||
public RulesLifecycleResult OnMoveValidation(MoveValidationContext<Piece> ctx)
|
||||
{
|
||||
var paths = this.MoveSet;
|
||||
|
||||
var matchingPaths = paths.Where(p => p.NormalizedDirection == Vector2.Normalize(ctx.To - ctx.From));
|
||||
if (!matchingPaths.Any())
|
||||
{
|
||||
return new RulesLifecycleResult(IsError: true, "Piece cannot move like that.");
|
||||
}
|
||||
|
||||
var multiStepPaths = matchingPaths.Where(p => p.Distance == Distance.MultiStep).ToArray();
|
||||
foreach (var path in multiStepPaths)
|
||||
{
|
||||
// Assert that no pieces exist along the from -> to path.
|
||||
var isPathObstructed = GetPositionsAlongPath(ctx.From, ctx.To, path)
|
||||
.Any(pos => ctx.BoardState[(int)pos.X, (int)pos.Y] != null);
|
||||
if (isPathObstructed)
|
||||
{
|
||||
return new RulesLifecycleResult(IsError: true, "Piece cannot move through other pieces.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var pieceAtTo = ctx.BoardState[(int)ctx.To.X, (int)ctx.To.Y];
|
||||
if (pieceAtTo?.Owner == this.Owner)
|
||||
{
|
||||
return new RulesLifecycleResult(IsError: true, "Cannot capture your own pieces.");
|
||||
}
|
||||
|
||||
return new RulesLifecycleResult(IsError: false);
|
||||
|
||||
static IEnumerable<Vector2> GetPositionsAlongPath(Vector2 from, Vector2 to, Path path)
|
||||
{
|
||||
var next = from;
|
||||
while (next != to && next.X >= 0 && next.X < 9 && next.Y >= 0 && next.Y < 9)
|
||||
{
|
||||
next += path.NormalizedDirection;
|
||||
yield return next;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user