feat(engine): add transformMoveGenerator + modifyMoveAttrs preset hooks
Adds two optional hooks to PresetDef: - transformMoveGenerator wraps a piece's move generator. Presets compose in list order; each receives the previous wrapper's output. Returned moves are pseudo-legal and still pass through the engine's self-check filter. - modifyMoveAttrs lets presets contribute additive range/direction deltas for generators that support them. ChessEngine.getAllLegalMoves folds the transform chain before getExtraMoves/filterMoves, preserving existing hook ordering and the downstream self-check filter. Tests cover: no-op wrap preserves moves, wrap adds pawn backward, two wraps compose, and self-check filter still prunes transform-added illegal moves.
This commit is contained in:
parent
fea511790b
commit
fab8a8115b
3 changed files with 414 additions and 4 deletions
|
|
@ -718,8 +718,28 @@ export class ChessEngine {
|
|||
.filter(p => p.type !== undefined);
|
||||
|
||||
for (const piece of pieces) {
|
||||
const getter = lookupMoveGenerator(piece.type);
|
||||
if (!getter) continue;
|
||||
const baseGetter = lookupMoveGenerator(piece.type);
|
||||
// Fold the transformMoveGenerator chain over all active presets
|
||||
// (scope-filtered for `color`). Each preset's wrapper receives the
|
||||
// OUTPUT of the previous — composing cleanly. We seed with the
|
||||
// type-registry generator, or a degenerate empty-move generator if
|
||||
// the piece type is unknown (keeps later wrappers well-defined).
|
||||
const scopedPresets = this.activePresets.getForColor(color);
|
||||
const seedGetter: MoveGetter =
|
||||
baseGetter ?? ((_s: Session, _id: EntityId) => [] as LegalMove[]);
|
||||
const getter: MoveGetter = scopedPresets
|
||||
.filter(p => p.transformMoveGenerator)
|
||||
.reduce<MoveGetter>(
|
||||
(gen, preset) => preset.transformMoveGenerator!(this, piece.id, gen),
|
||||
seedGetter,
|
||||
);
|
||||
// Skip pieces with no known type AND no transform — same semantics
|
||||
// as the original "unknown type ⇒ immobile" guard, but now a
|
||||
// transform preset can still produce moves for an otherwise unknown
|
||||
// type.
|
||||
if (!baseGetter && scopedPresets.every(p => !p.transformMoveGenerator)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let pieceMoves = getter(this.session, piece.id);
|
||||
|
||||
|
|
@ -759,7 +779,7 @@ export class ChessEngine {
|
|||
// Order matters — `getExtraMoves` contributes to the set that
|
||||
// `filterMoves` operates on, so every active preset sees the full
|
||||
// aggregated set (including prior presets' additions).
|
||||
const activePresets = this.activePresets.getForColor(color);
|
||||
const activePresets = scopedPresets;
|
||||
for (const preset of activePresets) {
|
||||
if (preset.getExtraMoves) {
|
||||
pieceMoves = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue