Skip to content

Commit aa8d676

Browse files
committed
release: @echecs/game@2.0.1
1 parent 7bdee16 commit aa8d676

4 files changed

Lines changed: 56 additions & 44 deletions

File tree

AGENTS.md

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# AGENTS.md
22

33
Agent guidance for the `@echecs/game` repository — a TypeScript chess game
4-
engine depending on `@echecs/position` and `@echecs/fen`, providing legal move
5-
generation, undo/redo, and game-state detection.
4+
engine depending on `@echecs/position`, providing legal move generation,
5+
undo/redo, and game-state detection.
66

77
**Backlog:** tracked in
88
[GitHub Issues](https://github.com/mormubis/game/issues).
@@ -12,9 +12,9 @@ generation, undo/redo, and game-state detection.
1212
## Project Overview
1313

1414
`@echecs/game` exposes a single mutable `Game` class. The internal state is a
15-
`Position` object (from `@echecs/position`) plus castling rights, en passant
16-
target, halfmove clock, and fullmove number. Runtime dependencies are
17-
`@echecs/position` and `@echecs/fen`; no SAN notation, no PGN.
15+
`Position` object (from `@echecs/position`) which contains the board, castling
16+
rights, en passant target, halfmove clock, fullmove number, and turn. Single
17+
runtime dependency: `@echecs/position`. No SAN notation, no PGN.
1818

1919
---
2020

@@ -23,7 +23,8 @@ target, halfmove clock, and fullmove number. Runtime dependencies are
2323
| Package | Type | Purpose |
2424
| ------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------- |
2525
| `@echecs/position` | Runtime | `Position` class, types (`Color`, `Piece`, `Square`, etc.), `reach()` for pseudo-legal targets, `derive()` for position transitions |
26-
| `@echecs/fen` | Runtime | FEN parsing (`parse`) and serialization (`stringify`) |
26+
| `@echecs/fen` | Dev | FEN parsing (`parse`) and serialization (`stringify`) — used in tests only |
27+
| `@echecs/san` | Dev | SAN move parsing — used in playthrough tests only |
2728

2829
---
2930

@@ -49,13 +50,15 @@ Key source files:
4950
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
5051
| `src/index.ts` | Public re-exports (`Game` class, `Position` class, and all public types from `@echecs/position`) |
5152
| `src/types.ts` | Local `Move` and `PromotionPieceType` types (removed from `@echecs/position` v3) |
52-
| `src/fen.ts` | FEN conversion layer between `@echecs/fen` v1 types and position v3 types |
5353
| `src/game.ts` | `Game` class — public API, undo/redo stacks, history, wraps `Position` from `@echecs/position` |
5454
| `src/moves.ts` | Legal move generation, `move` (applies move to Position), uses `position.reach()` for pseudo-legal targets and `position.derive()` + `isCheck` for legality filtering |
5555
| `src/detection.ts` | `isCheckmate`, `isStalemate`, `isDraw`, `isThreefoldRepetition` — all take `Position` + `Move[]` |
5656
| `src/__tests__/game.spec.ts` | Unit tests for the `Game` class |
5757
| `src/__tests__/moves.spec.ts` | Unit tests for move generation, including perft |
5858
| `src/__tests__/detection.spec.ts` | Unit tests for game-state detection |
59+
| `src/__tests__/playthrough.spec.ts` | Full game playthrough test (Fischer-Spassky 1972 Game 6) via `@echecs/san` |
60+
| `src/__tests__/hash.spec.ts` | Zobrist hash consistency tests (move/undo cycles, transpositions) |
61+
| `src/__tests__/regression.spec.ts` | Regression edge-case tests ported from chess.js |
5962
| `src/__tests__/helpers.ts` | Test helper: `fromFen` utility for constructing Position from FEN strings |
6063
| `src/__tests__/comparison.bench.ts` | Comparative benchmarks vs `chess.js` |
6164

@@ -170,14 +173,14 @@ Groups, separated by a blank line, in this order:
170173

171174
## Naming Conventions
172175

173-
| Construct | Convention | Examples |
174-
| ---------------------- | ---------------------- | --------------------------------------------------- |
175-
| Classes | `PascalCase` | `Game` |
176-
| Functions | `camelCase` | `generateMoves`, `parseFen`, `squareToIndex` |
177-
| Types / Interfaces | `PascalCase` | `Color`, `Move`, `Piece`, `FenState` |
178-
| Module-level constants | `SCREAMING_SNAKE_CASE` | `STARTING_FEN`, `INITIAL_BOARD`, `PROMOTION_PIECES` |
179-
| Variables / Parameters | `camelCase` | `state`, `move`, `square`, `depth` |
180-
| Source files | `camelCase.ts` | `index.ts`, `moves.ts`, `board.ts` |
176+
| Construct | Convention | Examples |
177+
| ---------------------- | ---------------------- | --------------------------------------------- |
178+
| Classes | `PascalCase` | `Game` |
179+
| Functions | `camelCase` | `generateMoves`, `enemyColor`, `boardChanges` |
180+
| Types / Interfaces | `PascalCase` | `Color`, `Move`, `Piece`, `HistoryEntry` |
181+
| Module-level constants | `SCREAMING_SNAKE_CASE` | `STARTING_POSITION`, `PROMOTION_PIECES` |
182+
| Variables / Parameters | `camelCase` | `state`, `move`, `square`, `depth` |
183+
| Source files | `camelCase.ts` | `index.ts`, `moves.ts`, `game.ts` |
181184

182185
---
183186

@@ -225,7 +228,6 @@ position state used internally by all modules:
225228

226229
- Board: `Map<Square, Piece>` (public API) / 0x88 array (internal)
227230
- `castlingRights`, `enPassantSquare`, `fullmoveNumber`, `halfmoveClock`, `turn`
228-
- Attack queries: `isAttacked(square, by)`, `attackers(square, by)`
229231
- State queries: `isCheck`, `isInsufficientMaterial`, `isValid`, `hash`
230232
- Piece access: `at(square)` returns `Piece | undefined`
231233
- Pseudo-legal targets: `reach(square)` returns target squares for the piece on
@@ -243,8 +245,9 @@ value object — `derive()` returns new instances, never mutates. `Move` and
243245
`generateMoves(position, square?)` produces legal moves only:
244246

245247
1. Generate pseudo-legal moves per piece type for the active color.
246-
2. For each pseudo-legal move, apply it via `applyMoveToState` and check if the
247-
active color's king is in check. Discard if so.
248+
2. For each pseudo-legal move, apply board changes via `boardChanges` +
249+
`position.derive({ changes })` and check if the active color's king is in
250+
check. Discard if so.
248251

249252
`isInCheck` uses a separate `isKingAttackedOn` path that does **not** generate
250253
castling moves — this breaks the infinite recursion that would otherwise occur
@@ -283,10 +286,11 @@ Private fields:
283286

284287
**Caching:** `#cache` is populated lazily via the private `#cachedState` getter
285288
on the first call to `moves()`, `isCheck()`, `isCheckmate()`, `isStalemate()`,
286-
`isDraw()`, or `isGameOver()` from a given position. It is invalidated
287-
(`#cache = undefined`) at the start of `move()`, `undo()`, and `redo()` — but
288-
only after confirming the operation is not a no-op. Repeated queries from the
289-
same position are O(1) after the first call.
289+
`isDraw()`, or `isGameOver()` from a given position. `move()` reads the cache
290+
for legality validation, then invalidates after applying. `undo()` and `redo()`
291+
check whether the history stack is empty before invalidating, so no-op calls do
292+
not evict the cache. Repeated queries from the same position are O(1) after the
293+
first call.
290294

291295
### Detection (`src/detection.ts`)
292296

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,21 @@ and this project adheres to
88

99
## [Unreleased]
1010

11+
## [2.0.1] - 2026-04-09
12+
13+
### Fixed
14+
15+
- Corrected README to match 2.0.0 API: removed nonexistent `Game.fromFen()` and
16+
`game.fen()`, documented `new Game(position)` constructor, fixed `undo()` /
17+
`redo()` return types (`void`, not `this`), fixed `Move.promotion` signature,
18+
removed `MoveInput` from exports listing.
19+
- Updated `AGENTS.md` to reflect single runtime dependency (`@echecs/position`),
20+
removed nonexistent `src/fen.ts`, added missing test files, removed stale
21+
references to `isAttacked` / `applyMoveToState`, updated naming convention
22+
examples.
23+
- Changed `package.json` keyword from `no-dependencies` to
24+
`minimal-dependencies`.
25+
1126
## [2.0.0] - 2026-04-09
1227

1328
### Changed

README.md

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
[ECHECS](https://github.com/mormubis) project.
99

1010
It provides a single mutable `Game` class that manages board state, generates
11-
legal moves, and detects game-ending conditions. Depends on `@echecs/position`
12-
and `@echecs/fen` at runtime.
11+
legal moves, and detects game-ending conditions. Single runtime dependency:
12+
[`@echecs/position`](https://www.npmjs.com/package/@echecs/position).
1313

1414
## Installation
1515

@@ -29,7 +29,6 @@ game.move({ from: 'e7', to: 'e5' });
2929

3030
console.log(game.turn()); // 'white'
3131
console.log(game.moves()); // all legal moves for white
32-
console.log(game.fen()); // current position as FEN string
3332
```
3433

3534
## API
@@ -44,15 +43,14 @@ Creates a new game from the standard starting position.
4443
const game = new Game();
4544
```
4645

47-
#### `Game.fromFen(fen)`
46+
#### `new Game(position)`
4847

49-
Creates a game from an arbitrary FEN string. Throws `Error` if the FEN is
50-
invalid.
48+
Creates a game from an existing `Position` object (from `@echecs/position`).
5149

5250
```typescript
53-
const game = Game.fromFen(
54-
'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1',
55-
);
51+
import { Position } from '@echecs/game';
52+
53+
const game = new Game(position);
5654
```
5755

5856
### Board queries
@@ -79,10 +77,6 @@ with `board()[0]` = rank 1 (a1–h1) and `board()[7]` = rank 8.
7977
game.board()[0]?.[4]; // { color: 'white', type: 'king' } (e1)
8078
```
8179

82-
#### `game.fen()`
83-
84-
Returns the current position as a FEN string.
85-
8680
#### `game.position()`
8781

8882
Returns the underlying `Position` object. Useful for direct access to castling
@@ -113,7 +107,7 @@ Each `Move` object has the shape:
113107
interface Move {
114108
from: Square;
115109
to: Square;
116-
promotion: PromotionPieceType | undefined;
110+
promotion?: PromotionPieceType;
117111
}
118112
```
119113

@@ -134,12 +128,12 @@ game.move({ from: 'e7', to: 'e8', promotion: 'queen' }); // promotion
134128

135129
#### `game.undo()`
136130

137-
Steps back one move. Returns `this`. No-op at the start of the game.
131+
Steps back one move. No-op at the start of the game.
138132

139133
#### `game.redo()`
140134

141-
Steps forward one move (after an undo). Returns `this`. No-op at the end of
142-
history. Cleared whenever a new `move()` is made.
135+
Steps forward one move (after an undo). No-op at the end of history. Cleared
136+
whenever a new `move()` is made.
143137

144138
```typescript
145139
game.move({ from: 'e2', to: 'e4' });
@@ -155,7 +149,7 @@ Returns the list of moves played so far. Undone moves are not included.
155149

156150
```typescript
157151
game.move({ from: 'e2', to: 'e4' });
158-
game.history(); // [{ from: 'e2', to: 'e4', promotion: undefined }]
152+
game.history(); // [{ from: 'e2', to: 'e4' }]
159153
```
160154

161155
### State detection
@@ -201,8 +195,7 @@ import type {
201195
CastlingRights, // { white: SideCastlingRights, black: SideCastlingRights }
202196
Color, // 'white' | 'black'
203197
EnPassantSquare, // typed en passant target square
204-
Move, // { from: Square, to: Square, promotion: PromotionPieceType | undefined }
205-
MoveInput, // input shape for game.move()
198+
Move, // { from: Square, to: Square, promotion?: PromotionPieceType }
206199
Piece, // { color: Color, type: PieceType }
207200
PieceType, // 'pawn' | 'knight' | 'bishop' | 'rook' | 'queen' | 'king'
208201
PromotionPieceType, // 'queen' | 'rook' | 'bishop' | 'knight'

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"game",
5353
"legal-moves",
5454
"move-generation",
55-
"no-dependencies",
55+
"minimal-dependencies",
5656
"redo",
5757
"stalemate",
5858
"typescript",
@@ -83,7 +83,7 @@
8383
},
8484
"type": "module",
8585
"types": "dist/index.d.ts",
86-
"version": "2.0.0",
86+
"version": "2.0.1",
8787
"dependencies": {
8888
"@echecs/position": "^3.0.3"
8989
}

0 commit comments

Comments
 (0)