All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- removed misleading keywords (
fen,board-game) - added
threefold-repetitionanddraw-detectionkeywords
- Corrected README to match 2.0.0 API: removed nonexistent
Game.fromFen()andgame.fen(), documentednew Game(position)constructor, fixedundo()/redo()return types (void, notthis), fixedMove.promotionsignature, removedMoveInputfrom exports listing. - Updated
AGENTS.mdto reflect single runtime dependency (@echecs/position), removed nonexistentsrc/fen.ts, added missing test files, removed stale references toisAttacked/applyMoveToState, updated naming convention examples. - Changed
package.jsonkeyword fromno-dependenciestominimal-dependencies.
- BREAKING: upgraded to
@echecs/positionv3.Colorvalues are now'white'/'black'(was'w'/'b').PieceTypevalues are now full words ('pawn','knight','bishop','rook','queen','king') instead of single letters. - BREAKING:
CastlingRightsshape is now{ white: { king, queen }, black: { king, queen } }(was{ wK, wQ, bK, bQ }). - BREAKING:
PromotionPieceTypeuses full words ('queen','rook','bishop','knight') instead of single letters. - BREAKING: constructor now accepts an optional
Positionargument instead of always starting from the initial position.new Game()still defaults to the starting position;new Game(position)starts from anyPosition. - BREAKING:
MoveInputtype merged intoMovewith optionalpromotion.Game.move()now takesMovedirectly. - Rewrote move generation to use
position.reach()for pseudo-legal targets andposition.derive()+isCheckfor legality filtering. Removed all 0x88 internal board manipulation. @echecs/fenmoved from runtime dependency to devDependency. FEN parsing/serialization is no longer part of the public API.
- Local
MoveandPromotionPieceTypetypes (removed from@echecs/positionv3). - Re-exported
STARTING_POSITION,EnPassantSquare, andSideCastlingRightsfrom@echecs/position. - Full game playthrough test (81-move Fischer-Spassky 1972 Game 6) via
@echecs/san. - Zobrist hash consistency tests (move/undo cycles, transpositions).
- Regression edge-case tests ported from chess.js.
- BREAKING:
Game.fromFen()static method — construct aPositionand pass it tonew Game(position)instead. - BREAKING:
game.fen()method — use@echecs/fendirectly withgame.position(). - BREAKING:
isAttacked()method — removed fromGame. Position v3 no longer exposes attack queries.
- TSDoc comments on all public methods and the
Gameclass itself, with examples,@throws, and@remarkswhere applicable. - Exported
MoveInputinterface as a public type.
- Descriptive error messages for illegal moves in
Game.move(). Messages now explain why a move is illegal: no piece on square, opponent's piece, game over, piece has no legal moves, piece cannot reach target, missing promotion, or promotion not allowed. - Explicit tests for castling rights revocation when a rook is captured on its starting square (a1, h1, a8, h8).
game.position()getter returning the currentPositionfrom@echecs/position.
- Internal state replaced with
Positionfrom@echecs/position. TheGameclass is now a thin stateful wrapper (undo/redo, legal move caching) over an immutablePositioncore. - FEN parsing and serialization delegated to
@echecs/fen. - Types (
Color,Piece,Move,Square,CastlingRights,PieceType,PromotionPieceType) are now re-exported from@echecs/position. - Attack tables (
ATTACKS,RAYS,PIECE_MASKS) imported from@echecs/position/internalinstead of built locally. - Threefold repetition detection uses Zobrist hashes instead of FEN strings.
@echecs/positionand@echecs/fenare now runtime dependencies.
src/types.ts,src/board.ts,src/fen.ts— replaced by external packages.FenStateinternal interface — replaced byPositionclass.isInsufficientMaterialfromdetection.ts— delegated toposition.isInsufficientMaterial.
Game.isAttacked(square, color)— returnstrueif any piece ofcolorattackssquare. Matches chess.js semantics: pinned pieces still attack, own pieces count as attacked squares, no X-ray, same square returnsfalse.
- First stable release. No API changes from
0.1.1.
- Switched internal board representation from a flat
[64]array to the 0x88[128]layout. Valid squares satisfyindex & 0x88 === 0; the padding slots enable a single-instruction off-board check and the ATTACKS/RAYS lookup tables. No change to the public API. isSquareAttackedBynow uses precomputedATTACKS[240]andRAYS[240]tables (initialised at module load) instead of generating attack move lists and scanning them. Attack detection is O(1) per piece for non-sliding pieces and O(ray length) for sliding pieces with an early bitmask skip.Gamenow lazily caches the legal move list and check flag per position. The cache is populated on the first call tomoves(),isCheck(),isCheckmate(),isStalemate(),isDraw(), orisGameOver()from a given position, and invalidated on everymove(),undo(), orredo(). Repeated queries from the same position are O(1) after the first call.isInsufficientMaterialnow correctly handles KB vs KB positions where all bishops are on the same square colour (previously returnedfalse).move()validates legality against the cached move list before clearing the cache, avoiding a redundantgenerateMovescall in the commonmoves()→move()pattern.undo()andredo()check whether the history stack is empty before invalidating the cache, so no-op calls at the start/end of history do not evict the cache unnecessarily.isDraw()inGamecallsthis.isStalemate()(cached) rather than delegating to the pure detection function, avoiding a redundantgenerateMovescall.
OFF_BOARD = 0x88exported fromsrc/board.tsfor use inmoves.ts.BENCHMARK_RESULTS.md— comparative benchmarks againstchess.js@1.4.0across all shared operations, plus a raw perft benchmark.pnpm benchscript — runs the comparison benchmark suite via Vitest bench.vitest.config.ts— excludes benchmark files from coverage.- Extended test coverage:
- Perft positions 2–7 from the chess programming wiki (depths 3–4), sourced from the chess.js test suite.
- Perft depth 4 from the starting position (197,281 nodes).
- Additional
isCheckmate,isStalemate,isCheck, andisInsufficientMaterialpositions from the chess.js test suite. - Regression: invalid castling rights in FEN must not crash
isGameOver().
- Initial release with core chess game engine.
Gameclass — mutable, withmove(),undo(),redo(),history(),moves(),get(),board(),fen(),turn(),isCheck(),isCheckmate(),isStalemate(),isDraw(),isGameOver().Game.fromFen(fen)static factory for loading arbitrary positions.- Legal move generation for all piece types including castling, en passant, and promotion.
- Game-state detection: check, checkmate, stalemate, 50-move rule, insufficient material, threefold repetition.
- FEN parsing and serialisation (
parseFen,serialiseFen,STARTING_FEN). - Zero runtime dependencies.