This commit is contained in:
2024-10-11 11:10:38 -05:00
parent 81dd267290
commit f75553a0ad
14 changed files with 616 additions and 514 deletions

View File

@@ -0,0 +1,81 @@
namespace Shogi.Domain.Other;
/// <summary>
/// </summary>
/// <typeparam name="TPiece"></typeparam>
/// <param name="boardState">A 2D array of pieces, representing your board. Indexed as [x, y].</param>
public class BoardRules<TPiece>(TPiece?[,] boardState) where TPiece : IRulesLifecycle<TPiece>
{
private readonly Vector2 MaxIndex = new(boardState.GetLength(0) - 1, boardState.GetLength(1) - 1);
/// <summary>
/// Validates a move, invoking the <see cref="IRulesLifecycle{TPiece}.OnMoveValidation(MoveValidationContext{TPiece})"/> callback which you should implement.
/// A move is considered valid if it could be made legally, ignoring check or check-mate rules.
/// Check and check-mate verifying happens in a different lifecycle callback.
/// </summary>
/// <param name="from">The position of the piece being moved.</param>
/// <param name="to">The desired destination of the piece being moved.</param>
/// <param name="isPromotion">TODO</param>
/// <returns></returns>
public RulesLifecycleResult ValidateMove(Vector2 from, Vector2 to, bool isPromotion)
{
if (IsWithinBounds(from) && IsWithinBounds(to))
{
var piece = boardState[(int)from.X, (int)from.Y];
if (piece == null)
{
return new RulesLifecycleResult(IsError: true, $"There is no piece at position {from}.");
}
return piece.OnMoveValidation(new MoveValidationContext<TPiece>(from, to, isPromotion, boardState));
}
return new RulesLifecycleResult(IsError: true, "test message");
}
//foreach (var piece in boardState)
//{
// if (piece != null)
// {
// var result = piece.OnMoveValidation(new MoveValidationContext<TPiece>(from, to, isPromotion, boardState));
// if (result.IsError)
// {
// return result;
// }
// }
//}
//public int ValidateMove(Vector2 start, Vector2 end)
//{
// 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));
// }
// return 0;
//}
private bool IsWithinBounds(Vector2 position)
{
var isPositive = position - position == Vector2.Zero;
return isPositive && position.X <= MaxIndex.X && position.Y <= MaxIndex.Y;
}
}

View File

@@ -0,0 +1,15 @@
namespace Shogi.Domain.Other;
public interface IRulesLifecycle<TPiece> where TPiece : IRulesLifecycle<TPiece>
{
/// <summary>
/// Invoked by <see cref="BoardRules{TPiece}.ValidateMove(Vector2, Vector2, bool)"/> during the MoveValidation life cycle event.
/// If a move begins or ends outside the board space coordinates, this function is not called.
/// The purpose is to ensure a proposed board move is valid with regard to the moved piece's rules.
/// This event does not worry about check or check-mate, or if a move is legal.
///
/// </summary>
/// <param name="context">A context object with information for you to use to assess whether a move is valid for your implementing piece.</param>
/// <returns>A new <see cref="RulesLifecycleResult"/> object indicating whether or not the move is valid.</returns>
RulesLifecycleResult OnMoveValidation(MoveValidationContext<TPiece> context);
}

View File

@@ -0,0 +1,14 @@
namespace Shogi.Domain.Other;
public record MoveValidationContext<TPiece>(
Vector2 From,
Vector2 To,
bool IsPromotion,
TPiece?[,] BoardState) where TPiece : IRulesLifecycle<TPiece>
{
public TPiece? GetPieceByRelativePosition(Vector2 relativePosition)
{
var absolute = From + relativePosition;
return BoardState[(int)absolute.X, (int)absolute.Y];
}
}

View File

@@ -1,11 +0,0 @@
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

@@ -1,61 +0,0 @@
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

@@ -0,0 +1,5 @@
namespace Shogi.Domain.Other;
public record RulesLifecycleResult(bool IsError, string ResultMessage = "")
{
}