app/fen_identity.h¶
turn a FEN into a physically realizable identity snapshot.
The setup planner (board_setup_planner.h) deliberately refuses to take a FEN as a target: it plans toward a piece-*identity* snapshot (BoardIdentity
), because “every identity snapshot is a state the board actually reached, so it is
realizable by construction,” whereas a FEN can describe positions the board cannot hold. That was fine for review, where every target came from a game the board itself played.
Puzzles break the assumption (docs/lichess-docs/PLAN.md §3.1). A Lichess puzzle arrives as a bare FEN drawn from someone else’s game, and the board must decide which physical piece goes on each square. This module is that bridge, and it is the one genuinely new algorithm in the Lichess work.
Two things make it more than bookkeeping:
It minimises setup cost. Each of the (at most) 32 physical pieces is assigned to a square of its own type by an optimal min-cost matching, where cost is the travel from where that piece currently is. A piece already standing on its target square costs zero, so the resulting plan naturally moves only the delta between the current position and the puzzle — never a teardown and rebuild.
It handles promoted pieces. A FEN from a real game can want two queens or three knights, which a 32-piece set does not contain. The board already has an answer: promotion is physically faked — the pawn’s magnet stays on the square and keeps its SLOT_Px identity (piece_tracker.h). So a spare pawn piece can stand in for a promoted piece, and such a FEN is realizable after all. Whether to allow that is a policy the caller chooses (FenIdentityOptions::allowPromotedStandIns), because it is honest in a game the player promoted in and baffling in a puzzle they were handed cold.
Pure: no gantry, no engine, no I/O. Fully unit-testable.