2026-04-10 14:58:03 +02:00
|
|
|
using Chessistics.Engine.Model;
|
|
|
|
|
|
|
|
|
|
namespace Chessistics.Engine.Rules;
|
|
|
|
|
|
|
|
|
|
public static class MoveValidator
|
|
|
|
|
{
|
|
|
|
|
private static readonly (int dc, int dr)[] OrthogonalDirs = [(0, 1), (0, -1), (1, 0), (-1, 0)];
|
|
|
|
|
private static readonly (int dc, int dr)[] DiagonalDirs = [(1, 1), (1, -1), (-1, 1), (-1, -1)];
|
2026-04-10 23:24:14 +02:00
|
|
|
private static readonly (int dc, int dr)[] AllDirs = [(0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (1, -1), (-1, 1), (-1, -1)];
|
2026-04-10 14:58:03 +02:00
|
|
|
private static readonly (int dc, int dr)[] KnightOffsets =
|
|
|
|
|
[
|
|
|
|
|
(1, 2), (2, 1), (2, -1), (1, -2),
|
|
|
|
|
(-1, -2), (-2, -1), (-2, 1), (-1, 2)
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public static IReadOnlyList<Coords> GetLegalEndCells(PieceKind kind, Coords start, BoardState board)
|
|
|
|
|
{
|
|
|
|
|
if (!board.IsOnBoard(start) || board.GetCell(start) == CellType.Wall)
|
|
|
|
|
return [];
|
|
|
|
|
|
|
|
|
|
return kind switch
|
|
|
|
|
{
|
Add Pion piece, surplus stock, and levels 4-6
- Pion (Pawn): orthogonal range 1, social status 1 (lowest), green color
- All existing levels get surplus stock including pawns for player choice
- Level 4 "Le Carrefour": 8x8, dual cargo diagonal routes, center wall block
- Level 5 "Le Labyrinthe": 8x6, wall corridors, knights essential for shortcuts
- Level 6 "Trois Royaumes": 10x8, 3 productions + 3 demands, full network puzzle
- Production interval concept removed (all produce every turn)
- GDD updated with Pion section and 6 level descriptions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:03:36 +02:00
|
|
|
PieceKind.Pawn => GetSlidingMoves(start, OrthogonalDirs, 1, board),
|
2026-04-10 14:58:03 +02:00
|
|
|
PieceKind.Rook => GetSlidingMoves(start, OrthogonalDirs, 2, board),
|
|
|
|
|
PieceKind.Bishop => GetSlidingMoves(start, DiagonalDirs, 2, board),
|
|
|
|
|
PieceKind.Knight => GetKnightMoves(start, board),
|
2026-04-10 23:24:14 +02:00
|
|
|
PieceKind.Queen => GetSlidingMoves(start, AllDirs, 2, board),
|
2026-04-10 14:58:03 +02:00
|
|
|
_ => []
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsLegalPlacement(PieceKind kind, Coords start, Coords end, BoardState board)
|
|
|
|
|
{
|
|
|
|
|
return GetLegalEndCells(kind, start, board).Contains(end);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static IReadOnlyList<Coords> GetSlidingMoves(
|
|
|
|
|
Coords start, (int dc, int dr)[] directions, int maxRange, BoardState board)
|
|
|
|
|
{
|
|
|
|
|
var result = new List<Coords>();
|
|
|
|
|
|
|
|
|
|
foreach (var (dc, dr) in directions)
|
|
|
|
|
{
|
|
|
|
|
for (int dist = 1; dist <= maxRange; dist++)
|
|
|
|
|
{
|
|
|
|
|
var target = new Coords(start.Col + dc * dist, start.Row + dr * dist);
|
|
|
|
|
|
|
|
|
|
if (!board.IsOnBoard(target))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (board.GetCell(target) == CellType.Wall)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Only walls block the path. Other pieces do NOT block during edit.
|
|
|
|
|
// Collisions are detected at runtime during simulation.
|
|
|
|
|
result.Add(target);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static IReadOnlyList<Coords> GetKnightMoves(Coords start, BoardState board)
|
|
|
|
|
{
|
|
|
|
|
var result = new List<Coords>();
|
|
|
|
|
|
|
|
|
|
foreach (var (dc, dr) in KnightOffsets)
|
|
|
|
|
{
|
|
|
|
|
var target = new Coords(start.Col + dc, start.Row + dr);
|
|
|
|
|
|
|
|
|
|
if (!board.IsOnBoard(target))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (board.GetCell(target) == CellType.Wall)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
result.Add(target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|