@echecs/position 1.0.3 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +59 -52
- package/dist/index.d.ts +141 -23
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -8
- package/dist/internal/index.d.ts +0 -25
- package/dist/internal/index.js +0 -1
- package/dist/internal-BOZCKSNm.js +0 -1
- package/dist/internal-BOZCKSNm.js.map +0 -1
- package/dist/types-BN6JJgNK.d.ts +0 -33
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.1] - 2026-04-05
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Docs CI workflow using renamed script.
|
|
8
|
+
- Lockfile out of sync with dependency versions.
|
|
9
|
+
|
|
10
|
+
## [2.0.0] - 2026-04-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `Position.derive()` method — returns a new position with changes applied,
|
|
15
|
+
without mutating the original. Accepts piece changes as `[square, piece]`
|
|
16
|
+
tuples and optional position option overrides.
|
|
17
|
+
- `DeriveOptions` type exported for use with `derive()`.
|
|
18
|
+
- `EnPassantSquare` type — restricts en passant target squares to rank 3 and
|
|
19
|
+
rank 6 only.
|
|
20
|
+
- `SideCastlingRights` type — `{ king: boolean; queen: boolean }`.
|
|
21
|
+
- TypeDoc plugin to collapse expanded `Square` and `EnPassantSquare` unions in
|
|
22
|
+
generated docs.
|
|
23
|
+
- JSDoc comments on all public API members.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- `Color` values changed from `'b'`/`'w'` to `'black'`/`'white'`.
|
|
28
|
+
- `PieceType` values changed from single letters to full words: `'bishop'`,
|
|
29
|
+
`'king'`, `'knight'`, `'pawn'`, `'queen'`, `'rook'`.
|
|
30
|
+
- `CastlingRights` restructured from flat `{ bK, bQ, wK, wQ }` to nested
|
|
31
|
+
`{ black: { king, queen }, white: { king, queen } }`.
|
|
32
|
+
- `PositionOptions.enPassantSquare` now uses `EnPassantSquare` (rank 3/6 only)
|
|
33
|
+
instead of `Square`.
|
|
34
|
+
- TypeDoc docs script renamed to `docs:build` to avoid conflict with pnpm
|
|
35
|
+
built-in `docs` command.
|
|
36
|
+
|
|
37
|
+
### Removed
|
|
38
|
+
|
|
39
|
+
- `Move` and `PromotionPieceType` types — consumers should define their own.
|
|
40
|
+
- `findPiece()` method — redundant with `pieces()` filtering.
|
|
41
|
+
- `COLORS`, `FILES`, `RANKS`, `PIECE_TYPES`, `SQUARES`, `EMPTY_BOARD` constants
|
|
42
|
+
from public API (still used internally).
|
|
43
|
+
- `squareColor`, `squareFile`, `squareRank` functions from public API.
|
|
44
|
+
- `./internal` export condition — `@echecs/game` should migrate to
|
|
45
|
+
`Position.derive()`.
|
|
46
|
+
|
|
3
47
|
## [1.0.3] - 2026-04-04
|
|
4
48
|
|
|
5
49
|
### Fixed
|
package/README.md
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
**Position** is a TypeScript library representing a complete chess position —
|
|
10
10
|
the board, turn, castling rights, en passant square, halfmove clock, and
|
|
11
11
|
fullmove number — as an immutable value object with a clean query API. It is the
|
|
12
|
-
foundational package
|
|
12
|
+
foundational package in the `@echecs` family of chess libraries. Zero runtime
|
|
13
|
+
dependencies.
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
@@ -25,20 +26,27 @@ import { Position, STARTING_POSITION } from '@echecs/position';
|
|
|
25
26
|
// Starting position
|
|
26
27
|
const pos = new Position();
|
|
27
28
|
|
|
28
|
-
console.log(pos.turn); // '
|
|
29
|
+
console.log(pos.turn); // 'white'
|
|
29
30
|
console.log(pos.fullmoveNumber); // 1
|
|
30
31
|
console.log(pos.isCheck); // false
|
|
31
32
|
|
|
32
33
|
// Query the board
|
|
33
|
-
const piece = pos.piece('e1'); // { color: '
|
|
34
|
-
const whites = pos.pieces('
|
|
35
|
-
|
|
36
|
-
// Find all squares a piece occupies
|
|
37
|
-
const whiteKing = pos.findPiece({ color: 'w', type: 'k' }); // ['e1']
|
|
34
|
+
const piece = pos.piece('e1'); // { color: 'white', type: 'king' }
|
|
35
|
+
const whites = pos.pieces('white'); // Map<Square, Piece> of all white pieces
|
|
38
36
|
|
|
39
37
|
// Attack queries
|
|
40
|
-
const attackers = pos.attackers('e5', '
|
|
41
|
-
const attacked = pos.isAttacked('f7', '
|
|
38
|
+
const attackers = pos.attackers('e5', 'black'); // squares of black pieces attacking e5
|
|
39
|
+
const attacked = pos.isAttacked('f7', 'white'); // true if white attacks f7
|
|
40
|
+
|
|
41
|
+
// Derive a new position
|
|
42
|
+
const next = pos.derive({
|
|
43
|
+
changes: [
|
|
44
|
+
['e2', undefined],
|
|
45
|
+
['e4', { color: 'white', type: 'pawn' }],
|
|
46
|
+
],
|
|
47
|
+
turn: 'black',
|
|
48
|
+
enPassantSquare: 'e3',
|
|
49
|
+
});
|
|
42
50
|
```
|
|
43
51
|
|
|
44
52
|
## API
|
|
@@ -50,13 +58,7 @@ Full API reference is available at https://mormubis.github.io/position/
|
|
|
50
58
|
```ts
|
|
51
59
|
new Position()
|
|
52
60
|
new Position(board: Map<Square, Piece>)
|
|
53
|
-
new Position(board: Map<Square, Piece>, options?:
|
|
54
|
-
castlingRights?: CastlingRights
|
|
55
|
-
enPassantSquare?: Square
|
|
56
|
-
fullmoveNumber?: number
|
|
57
|
-
halfmoveClock?: number
|
|
58
|
-
turn?: Color
|
|
59
|
-
})
|
|
61
|
+
new Position(board: Map<Square, Piece>, options?: PositionOptions)
|
|
60
62
|
```
|
|
61
63
|
|
|
62
64
|
The no-argument form creates the standard chess starting position. Pass a custom
|
|
@@ -64,17 +66,17 @@ The no-argument form creates the standard chess starting position. Pass a custom
|
|
|
64
66
|
|
|
65
67
|
### Getters
|
|
66
68
|
|
|
67
|
-
| Getter | Type
|
|
68
|
-
| ------------------------ |
|
|
69
|
-
| `castlingRights` | `CastlingRights`
|
|
70
|
-
| `enPassantSquare` | `
|
|
71
|
-
| `fullmoveNumber` | `number`
|
|
72
|
-
| `halfmoveClock` | `number`
|
|
73
|
-
| `hash` | `string`
|
|
74
|
-
| `isCheck` | `boolean`
|
|
75
|
-
| `isInsufficientMaterial` | `boolean`
|
|
76
|
-
| `isValid` | `boolean`
|
|
77
|
-
| `turn` | `Color`
|
|
69
|
+
| Getter | Type | Description |
|
|
70
|
+
| ------------------------ | ------------------------------ | --------------------------------------------------------------- |
|
|
71
|
+
| `castlingRights` | `CastlingRights` | Which castling moves remain available |
|
|
72
|
+
| `enPassantSquare` | `EnPassantSquare \| undefined` | En passant target square (rank 3 or 6), if any |
|
|
73
|
+
| `fullmoveNumber` | `number` | Game turn counter — increments after each black move |
|
|
74
|
+
| `halfmoveClock` | `number` | Half-moves since last pawn advance or capture (fifty-move rule) |
|
|
75
|
+
| `hash` | `string` | Zobrist hash string for position identity |
|
|
76
|
+
| `isCheck` | `boolean` | Whether the side to move is in check |
|
|
77
|
+
| `isInsufficientMaterial` | `boolean` | Whether the position is a FIDE draw by insufficient material |
|
|
78
|
+
| `isValid` | `boolean` | Whether the position is legally reachable |
|
|
79
|
+
| `turn` | `Color` | Side to move (`'white'` or `'black'`) |
|
|
78
80
|
|
|
79
81
|
### Methods
|
|
80
82
|
|
|
@@ -83,15 +85,27 @@ The no-argument form creates the standard chess starting position. Pass a custom
|
|
|
83
85
|
Returns all squares occupied by pieces of `color` that attack `square`.
|
|
84
86
|
|
|
85
87
|
```typescript
|
|
86
|
-
pos.attackers('e5', '
|
|
88
|
+
pos.attackers('e5', 'black'); // e.g. ['d7', 'f6']
|
|
87
89
|
```
|
|
88
90
|
|
|
89
|
-
#### `
|
|
91
|
+
#### `derive(changes?): Position`
|
|
90
92
|
|
|
91
|
-
Returns
|
|
93
|
+
Returns a new `Position` with the given changes applied. The original is not
|
|
94
|
+
modified. Fields not provided are carried over from the source.
|
|
92
95
|
|
|
93
96
|
```typescript
|
|
94
|
-
|
|
97
|
+
// move e2 pawn to e4
|
|
98
|
+
const next = pos.derive({
|
|
99
|
+
changes: [
|
|
100
|
+
['e2', undefined],
|
|
101
|
+
['e4', { color: 'white', type: 'pawn' }],
|
|
102
|
+
],
|
|
103
|
+
turn: 'black',
|
|
104
|
+
enPassantSquare: 'e3',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// clone
|
|
108
|
+
const clone = pos.derive();
|
|
95
109
|
```
|
|
96
110
|
|
|
97
111
|
#### `isAttacked(square, color): boolean`
|
|
@@ -99,7 +113,7 @@ pos.findPiece({ color: 'w', type: 'q' }); // ['d1']
|
|
|
99
113
|
Returns `true` if any piece of `color` attacks `square`.
|
|
100
114
|
|
|
101
115
|
```typescript
|
|
102
|
-
pos.isAttacked('f3', '
|
|
116
|
+
pos.isAttacked('f3', 'white'); // true if white attacks f3
|
|
103
117
|
```
|
|
104
118
|
|
|
105
119
|
#### `piece(square): Piece | undefined`
|
|
@@ -107,7 +121,7 @@ pos.isAttacked('f3', 'w'); // true if white attacks f3
|
|
|
107
121
|
Returns the piece on `square`, or `undefined` if the square is empty.
|
|
108
122
|
|
|
109
123
|
```typescript
|
|
110
|
-
pos.piece('e1'); // { color: '
|
|
124
|
+
pos.piece('e1'); // { color: 'white', type: 'king' }
|
|
111
125
|
pos.piece('e5'); // undefined (empty in starting position)
|
|
112
126
|
```
|
|
113
127
|
|
|
@@ -117,42 +131,35 @@ Returns a map of all pieces, optionally filtered by `color`.
|
|
|
117
131
|
|
|
118
132
|
```typescript
|
|
119
133
|
pos.pieces(); // all 32 pieces in starting position
|
|
120
|
-
pos.pieces('
|
|
134
|
+
pos.pieces('white'); // 16 white pieces
|
|
121
135
|
```
|
|
122
136
|
|
|
123
137
|
### Constants
|
|
124
138
|
|
|
125
139
|
```typescript
|
|
126
|
-
import {
|
|
127
|
-
COLORS, // ['b', 'w']
|
|
128
|
-
FILES, // ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
|
|
129
|
-
RANKS, // ['1', '2', '3', '4', '5', '6', '7', '8']
|
|
130
|
-
PIECE_TYPES, // ['b', 'k', 'n', 'p', 'q', 'r']
|
|
131
|
-
SQUARES, // all 64 squares, a8–h1 (rank 8 to rank 1)
|
|
132
|
-
EMPTY_BOARD, // empty Map<Square, Piece>
|
|
133
|
-
STARTING_POSITION, // Position instance for the standard starting position
|
|
134
|
-
} from '@echecs/position';
|
|
140
|
+
import { STARTING_POSITION } from '@echecs/position';
|
|
135
141
|
```
|
|
136
142
|
|
|
143
|
+
`STARTING_POSITION` is a `Position` instance for the standard chess starting
|
|
144
|
+
position. Equivalent to `new Position()`.
|
|
145
|
+
|
|
137
146
|
### Types
|
|
138
147
|
|
|
139
148
|
All types are exported for use in consuming code and companion packages.
|
|
140
149
|
|
|
141
150
|
```typescript
|
|
142
151
|
import type {
|
|
143
|
-
CastlingRights, // {
|
|
144
|
-
Color, // '
|
|
152
|
+
CastlingRights, // { black: SideCastlingRights; white: SideCastlingRights }
|
|
153
|
+
Color, // 'black' | 'white'
|
|
154
|
+
DeriveOptions, // options accepted by Position.derive()
|
|
155
|
+
EnPassantSquare, // en passant target square (rank 3 or 6 only)
|
|
145
156
|
File, // 'a' | 'b' | ... | 'h'
|
|
146
|
-
Move, // { from: Square; to: Square; promotion: PromotionPieceType | undefined }
|
|
147
157
|
Piece, // { color: Color; type: PieceType }
|
|
148
|
-
PieceType, // '
|
|
158
|
+
PieceType, // 'bishop' | 'king' | 'knight' | 'pawn' | 'queen' | 'rook'
|
|
149
159
|
PositionOptions, // options accepted by the Position constructor
|
|
150
|
-
PromotionPieceType, // 'b' | 'n' | 'q' | 'r'
|
|
151
160
|
Rank, // '1' | '2' | ... | '8'
|
|
161
|
+
SideCastlingRights, // { king: boolean; queen: boolean }
|
|
152
162
|
Square, // 'a1' | 'a2' | ... | 'h8'
|
|
153
|
-
SquareColor, // '
|
|
163
|
+
SquareColor, // 'dark' | 'light'
|
|
154
164
|
} from '@echecs/position';
|
|
155
165
|
```
|
|
156
|
-
|
|
157
|
-
`Move` and `PromotionPieceType` are exported for use by companion packages
|
|
158
|
-
(`@echecs/san`, `@echecs/game`) that build on this foundational type.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,107 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
/** Side to move — `'white'` or `'black'`. */
|
|
3
|
+
type Color = 'black' | 'white';
|
|
4
|
+
/** Board file (column), `'a'` through `'h'`. */
|
|
5
|
+
type File = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h';
|
|
6
|
+
/** Chess piece type: bishop, king, knight, pawn, queen, or rook. */
|
|
7
|
+
type PieceType = 'bishop' | 'king' | 'knight' | 'pawn' | 'queen' | 'rook';
|
|
8
|
+
/** Board rank (row), `'1'` through `'8'`. */
|
|
9
|
+
type Rank = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8';
|
|
10
|
+
/** En passant target square — always on rank 3 or rank 6. */
|
|
11
|
+
type EnPassantSquare = `${File}${'3' | '6'}`;
|
|
12
|
+
/**
|
|
13
|
+
* A board square, e.g. `'e4'`. Combination of {@link File} and {@link Rank}.
|
|
14
|
+
*
|
|
15
|
+
* @preventExpand
|
|
16
|
+
*/
|
|
17
|
+
type Square = `${File}${Rank}`;
|
|
18
|
+
/** Square color on the board — `'dark'` or `'light'`. */
|
|
19
|
+
type SquareColor = 'dark' | 'light';
|
|
20
|
+
/** Castling availability for one side. */
|
|
21
|
+
interface SideCastlingRights {
|
|
22
|
+
/** Can castle kingside. */
|
|
23
|
+
king: boolean;
|
|
24
|
+
/** Can castle queenside. */
|
|
25
|
+
queen: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Which castling moves remain available for each side. */
|
|
28
|
+
interface CastlingRights {
|
|
29
|
+
/** Black's castling rights. */
|
|
30
|
+
black: SideCastlingRights;
|
|
31
|
+
/** White's castling rights. */
|
|
32
|
+
white: SideCastlingRights;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Options accepted by {@link Position.derive}. Extends {@link PositionOptions}
|
|
36
|
+
* with a `changes` field for applying piece changes.
|
|
37
|
+
*/
|
|
38
|
+
interface DeriveOptions extends PositionOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Piece changes as `[square, piece]` tuples. Set piece to `undefined` to
|
|
41
|
+
* clear a square. Only changed squares need to be listed.
|
|
42
|
+
*/
|
|
43
|
+
changes?: [Square, Piece | undefined][];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Options for constructing a {@link Position}. All fields are optional —
|
|
47
|
+
* omitted fields use defaults (standard starting position values).
|
|
48
|
+
*/
|
|
49
|
+
interface PositionOptions {
|
|
50
|
+
/** Castling availability. Defaults to all four castling moves available. */
|
|
51
|
+
castlingRights?: CastlingRights;
|
|
52
|
+
/** En passant target square, if any. */
|
|
53
|
+
enPassantSquare?: EnPassantSquare;
|
|
54
|
+
/**
|
|
55
|
+
* Game turn counter — starts at `1` and increments after each black move.
|
|
56
|
+
* After `1. e4 e5 2. Nf3` the fullmove number is `2`. Defaults to `1`.
|
|
57
|
+
*/
|
|
58
|
+
fullmoveNumber?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Number of half-moves since the last pawn advance or capture. Resets to
|
|
61
|
+
* `0` on every pawn move or capture. When it reaches `100` (50 full moves
|
|
62
|
+
* per side) either player may claim a draw. Defaults to `0`.
|
|
63
|
+
*/
|
|
64
|
+
halfmoveClock?: number;
|
|
65
|
+
/** Side to move. Defaults to `'white'`. */
|
|
66
|
+
turn?: Color;
|
|
67
|
+
}
|
|
68
|
+
/** A chess piece — color and type. */
|
|
69
|
+
interface Piece {
|
|
70
|
+
/** The piece's color. */
|
|
71
|
+
color: Color;
|
|
72
|
+
/** The piece's type. */
|
|
73
|
+
type: PieceType;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
3
76
|
//#region src/position.d.ts
|
|
77
|
+
/**
|
|
78
|
+
* An immutable chess position — board state, turn, castling rights,
|
|
79
|
+
* en passant square, and move counters.
|
|
80
|
+
*
|
|
81
|
+
* Query the position with getters and methods. Produce new positions
|
|
82
|
+
* with {@link Position.derive | derive}.
|
|
83
|
+
*/
|
|
4
84
|
declare class Position {
|
|
5
85
|
#private;
|
|
86
|
+
/**
|
|
87
|
+
* Creates a new position.
|
|
88
|
+
*
|
|
89
|
+
* @param board - Piece placement. Defaults to the standard starting position.
|
|
90
|
+
* @param options - Turn, castling rights, en passant, and move counters.
|
|
91
|
+
*/
|
|
6
92
|
constructor(board?: Map<Square, Piece>, options?: PositionOptions);
|
|
93
|
+
/** Which castling moves remain available. */
|
|
7
94
|
get castlingRights(): CastlingRights;
|
|
8
|
-
|
|
95
|
+
/** En passant target square, or `undefined` if none. */
|
|
96
|
+
get enPassantSquare(): EnPassantSquare | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Game turn counter — starts at `1` and increments after each black move.
|
|
99
|
+
*/
|
|
9
100
|
get fullmoveNumber(): number;
|
|
101
|
+
/**
|
|
102
|
+
* Number of half-moves since the last pawn advance or capture. Resets on
|
|
103
|
+
* every pawn move or capture. A draw can be claimed when it reaches `100`.
|
|
104
|
+
*/
|
|
10
105
|
get halfmoveClock(): number;
|
|
11
106
|
/**
|
|
12
107
|
* A Zobrist hash of the position as a 16-character hex string.
|
|
@@ -17,36 +112,59 @@ declare class Position {
|
|
|
17
112
|
* versions. Do not persist hashes across version upgrades.
|
|
18
113
|
*/
|
|
19
114
|
get hash(): string;
|
|
115
|
+
/**
|
|
116
|
+
* Whether the position is a draw by insufficient material (FIDE rules):
|
|
117
|
+
* K vs K, K+B vs K, K+N vs K, or K+B vs K+B with same-color bishops.
|
|
118
|
+
*/
|
|
20
119
|
get isInsufficientMaterial(): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Whether the position is legally reachable: exactly one king per side,
|
|
122
|
+
* no pawns on ranks 1 or 8, and the side not to move is not in check.
|
|
123
|
+
*/
|
|
21
124
|
get isValid(): boolean;
|
|
125
|
+
/** Whether the side to move is in check. */
|
|
22
126
|
get isCheck(): boolean;
|
|
127
|
+
/** Side to move — `'white'` or `'black'`. */
|
|
23
128
|
get turn(): Color;
|
|
129
|
+
/**
|
|
130
|
+
* Returns a new position with the given changes applied. Fields not
|
|
131
|
+
* provided are carried over from the source. Calling with no argument
|
|
132
|
+
* returns a clone.
|
|
133
|
+
*
|
|
134
|
+
* @param changes - Board deltas and option overrides to apply.
|
|
135
|
+
*/
|
|
136
|
+
derive(changes?: DeriveOptions): Position;
|
|
137
|
+
/**
|
|
138
|
+
* Returns all squares occupied by pieces of the given color that
|
|
139
|
+
* attack the target square.
|
|
140
|
+
*
|
|
141
|
+
* @param square - The target square.
|
|
142
|
+
* @param by - The attacking color.
|
|
143
|
+
*/
|
|
24
144
|
attackers(square: Square, by: Color): Square[];
|
|
25
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Returns `true` if any piece of the given color attacks the target square.
|
|
147
|
+
*
|
|
148
|
+
* @param square - The target square.
|
|
149
|
+
* @param by - The attacking color.
|
|
150
|
+
*/
|
|
26
151
|
isAttacked(square: Square, by: Color): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Returns the piece on the given square, or `undefined` if empty.
|
|
154
|
+
*
|
|
155
|
+
* @param square - The square to query.
|
|
156
|
+
*/
|
|
27
157
|
piece(square: Square): Piece | undefined;
|
|
158
|
+
/**
|
|
159
|
+
* Returns a map of all pieces on the board, optionally filtered by color.
|
|
160
|
+
*
|
|
161
|
+
* @param color - If provided, only pieces of this color are returned.
|
|
162
|
+
*/
|
|
28
163
|
pieces(color?: Color): Map<Square, Piece>;
|
|
29
164
|
}
|
|
30
165
|
//#endregion
|
|
31
|
-
//#region src/primitives.d.ts
|
|
32
|
-
declare const COLORS: Color[];
|
|
33
|
-
declare const FILES: File[];
|
|
34
|
-
declare const RANKS: Rank[];
|
|
35
|
-
declare const PIECE_TYPES: PieceType[];
|
|
36
|
-
declare const SQUARES: Square[];
|
|
37
|
-
//#endregion
|
|
38
166
|
//#region src/constants.d.ts
|
|
39
|
-
|
|
167
|
+
/** The standard chess starting position. */
|
|
40
168
|
declare const STARTING_POSITION: Position;
|
|
41
169
|
//#endregion
|
|
42
|
-
|
|
43
|
-
declare function squareFile(square: Square): File;
|
|
44
|
-
declare function squareRank(square: Square): Rank;
|
|
45
|
-
/**
|
|
46
|
-
* Returns 'dark' or 'light' for the given square.
|
|
47
|
-
* a1 is dark: file index 0 + rank 1 = 1 (odd) → 'dark'.
|
|
48
|
-
* b1 is light: file index 1 + rank 1 = 2 (even) → 'light'.
|
|
49
|
-
*/
|
|
50
|
-
declare function squareColor(square: Square): SquareColor;
|
|
51
|
-
//#endregion
|
|
52
|
-
export { COLORS, type CastlingRights, type Color, EMPTY_BOARD, FILES, type File, type Move, PIECE_TYPES, type Piece, type PieceType, Position, type PositionOptions, type PromotionPieceType, RANKS, type Rank, SQUARES, STARTING_POSITION, type Square, type SquareColor, squareColor, squareFile, squareRank };
|
|
170
|
+
export { type CastlingRights, type Color, type DeriveOptions, type EnPassantSquare, type File, type Piece, type PieceType, Position, type PositionOptions, type Rank, STARTING_POSITION, type SideCastlingRights, type Square, type SquareColor };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
function e(e){let t=(e.codePointAt(0)??0)-(`a`.codePointAt(0)??0);return(8-Number.parseInt(e[1]??`1`,10))*16+t}function t(t){let n=Array.from({length:128});for(let[r,i]of t)n[e(r)]=i;return n}const n=[-33,-31,-18,-14,14,18,31,33],r=[-17,-15,15,17],i=[-16,-1,1,16],a=[-17,-16,-15,-1,1,15,16,17],o={bishop:4,king:16,knight:2,pawn:1,queen:12,rook:8},s=Array.from({length:240}).fill(0),c=Array.from({length:240}).fill(0);(function(){for(let e of n)s[e+119]=(s[e+119]??0)|2;for(let e of a)s[e+119]=(s[e+119]??0)|16;for(let e of[15,17])s[e+119]=(s[e+119]??0)|1,s[-e+119]=(s[-e+119]??0)|1;for(let e=0;e<=119;e++)if(!(e&136)){for(let t of i){let n=e+t;for(;!(n&136);){let r=n-e;s[r+119]=(s[r+119]??0)|8,c[r+119]=t,n+=t}}for(let t of r){let n=e+t;for(;!(n&136);){let r=n-e;s[r+119]=(s[r+119]??0)|4,c[r+119]=t,n+=t}}}})();const l=[`black`,`white`],u=[`a`,`b`,`c`,`d`,`e`,`f`,`g`,`h`],d=[`1`,`2`,`3`,`4`,`5`,`6`,`7`,`8`],f=[`bishop`,`king`,`knight`,`pawn`,`queen`,`rook`],p=u.flatMap(e=>d.toReversed().map(t=>`${e}${t}`));function m(e){let t=BigInt(e);return()=>(t=t*6364136223846793005n+1442695040888963407n&18446744073709551615n,t)}const h=m(3735928559),g=Object.fromEntries(p.map(e=>[e,Object.fromEntries(f.map(e=>[e,Object.fromEntries(l.map(e=>[e,h()]))]))])),_={black:h(),white:h()},v={"black.king":h(),"black.queen":h(),"white.king":h(),"white.queen":h()},y=Object.fromEntries(u.map(e=>[e,h()]));function b(e){return((e.codePointAt(0)??0)-(`a`.codePointAt(0)??0)+Number.parseInt(e[1]??`1`,10))%2==1?`dark`:`light`}const x=[`rook`,`knight`,`bishop`,`queen`,`king`,`bishop`,`knight`,`rook`],S=[`a`,`b`,`c`,`d`,`e`,`f`,`g`,`h`],C=new Map;for(let[e,t]of x.entries()){let n=S[e];n!==void 0&&(C.set(`${n}1`,{color:`white`,type:t}),C.set(`${n}2`,{color:`white`,type:`pawn`}),C.set(`${n}7`,{color:`black`,type:`pawn`}),C.set(`${n}8`,{color:`black`,type:t}))}const w=C,T={castlingRights:{black:{king:!0,queen:!0},white:{king:!0,queen:!0}},enPassantSquare:void 0,fullmoveNumber:1,halfmoveClock:0,turn:`white`};var E=class n{#e;#t;#n;#r;#i;#a;#o;constructor(e,t){this.#e=new Map(e??w);let n={...T,...t};this.#t=n.castlingRights,this.#n=n.enPassantSquare,this.#r=n.fullmoveNumber,this.#i=n.halfmoveClock,this.#o=n.turn}get castlingRights(){return this.#t}get enPassantSquare(){return this.#n}get fullmoveNumber(){return this.#r}get halfmoveClock(){return this.#i}get hash(){if(this.#a!==void 0)return this.#a;let e=0n;for(let[t,n]of this.#e)e^=g[t]?.[n.type]?.[n.color]??0n;e^=_[this.#o];for(let[t,n]of Object.entries(this.#t))for(let[r,i]of Object.entries(n))i&&(e^=v[`${t}.${r}`]??0n);if(this.#n!==void 0){let t=this.#n[0];e^=y[t]}return this.#a=e.toString(16).padStart(16,`0`),this.#a}get isInsufficientMaterial(){let e=[];for(let[t,n]of this.#e)n.type!==`king`&&e.push([t,n]);if(e.length===0)return!0;if(e.length===1){let t=e[0]?.[1];return t?.type===`bishop`||t?.type===`knight`}if(e.every(([,e])=>e.type===`bishop`)){let t=e[0];if(t===void 0)return!0;let n=b(t[0]);return e.every(([e])=>b(e)===n)}return!1}get isValid(){let e=0,t=0;for(let[n,r]of this.#e)if(r.type===`king`&&(r.color===`black`?e++:t++),r.type===`pawn`&&(n[1]===`1`||n[1]===`8`))return!1;if(e!==1||t!==1)return!1;let n=this.#o===`white`?`black`:`white`;for(let[e,t]of this.#e)if(t.type===`king`&&t.color===n&&this.isAttacked(e,this.#o))return!1;return!0}get isCheck(){for(let[e,t]of this.#e)if(t.type===`king`&&t.color===this.#o){let t=this.#o===`white`?`black`:`white`;return this.isAttacked(e,t)}return!1}get turn(){return this.#o}#s(e,t,n,r,i){let a=t-n,l=a+119;if(l<0||l>=240||((s[l]??0)&(o[r.type]??0))===0||r.type===`pawn`&&(i===`white`&&a>0||i===`black`&&a<0))return!1;let u=c[l]??0;if(u===0)return!0;let d=n+u;for(;d!==t;){if(d&136||e[d]!==void 0)return!1;d+=u}return!0}derive(e){let t=new Map(this.#e);if(e?.changes)for(let[n,r]of e.changes)r===void 0?t.delete(n):t.set(n,r);return new n(t,{castlingRights:e?.castlingRights??this.#t,enPassantSquare:`enPassantSquare`in(e??{})?e?.enPassantSquare:this.#n,fullmoveNumber:e?.fullmoveNumber??this.#r,halfmoveClock:e?.halfmoveClock??this.#i,turn:e?.turn??this.#o})}attackers(n,r){let i=t(this.#e),a=e(n),o=[];for(let[t,n]of this.#e){if(n.color!==r)continue;let s=e(t);this.#s(i,a,s,n,r)&&o.push(t)}return o}isAttacked(n,r){let i=t(this.#e),a=e(n);for(let[t,n]of this.#e){if(n.color!==r)continue;let o=e(t);if(this.#s(i,a,o,n,r))return!0}return!1}piece(e){return this.#e.get(e)}pieces(e){if(e===void 0)return new Map(this.#e);let t=new Map;for(let[n,r]of this.#e)r.color===e&&t.set(n,r);return t}};const D=new E;export{E as Position,D as STARTING_POSITION};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#board","#castlingRights","#enPassantSquare","#fullmoveNumber","#halfmoveClock","#turn","#hash","#isAttackedByPiece"],"sources":["../src/squares.ts","../src/starting-board.ts","../src/position.ts","../src/constants.ts"],"sourcesContent":["import type { File, Rank, Square, SquareColor } from './types.js';\n\nfunction squareFile(square: Square): File {\n return square[0] as File;\n}\n\nfunction squareRank(square: Square): Rank {\n return square[1] as Rank;\n}\n\n/**\n * Returns 'dark' or 'light' for the given square.\n * a1 is dark: file index 0 + rank 1 = 1 (odd) → 'dark'.\n * b1 is light: file index 1 + rank 1 = 2 (even) → 'light'.\n */\nfunction squareColor(square: Square): SquareColor {\n const file = (square.codePointAt(0) ?? 0) - ('a'.codePointAt(0) ?? 0);\n const rank = Number.parseInt(square[1] ?? '1', 10);\n return (file + rank) % 2 === 1 ? 'dark' : 'light';\n}\n\nexport { squareColor, squareFile, squareRank };\n","import type { Piece, Square } from './types.js';\n\nconst BACK_RANK_TYPES = ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'] as const;\nconst BACK_RANK_FILES = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] as const;\n\nconst startingBoardMap = new Map<Square, Piece>();\nfor (const [index, type] of BACK_RANK_TYPES.entries()) {\n const file = BACK_RANK_FILES[index];\n if (file === undefined) {\n continue;\n }\n startingBoardMap.set(`${file}1` as Square, { color: 'w', type });\n startingBoardMap.set(`${file}2` as Square, { color: 'w', type: 'p' });\n startingBoardMap.set(`${file}7` as Square, { color: 'b', type: 'p' });\n startingBoardMap.set(`${file}8` as Square, { color: 'b', type });\n}\n\nexport const startingBoard = startingBoardMap;\n","import {\n ATTACKS,\n CASTLING_TABLE,\n DIFF_OFFSET,\n EP_TABLE,\n OFF_BOARD,\n PIECE_MASKS,\n PIECE_TABLE,\n RAYS,\n TURN_TABLE,\n boardFromMap,\n squareToIndex,\n} from './internal/index.js';\nimport { squareColor } from './squares.js';\nimport { startingBoard } from './starting-board.js';\n\nimport type {\n CastlingRights,\n Color,\n File,\n Piece,\n PositionOptions,\n Square,\n} from './types.js';\n\nconst DEFAULT_OPTIONS: Required<Omit<PositionOptions, 'enPassantSquare'>> &\n Pick<PositionOptions, 'enPassantSquare'> = {\n castlingRights: { bK: true, bQ: true, wK: true, wQ: true },\n enPassantSquare: undefined,\n fullmoveNumber: 1,\n halfmoveClock: 0,\n turn: 'w',\n};\n\nexport class Position {\n readonly #board: Map<Square, Piece>;\n readonly #castlingRights: CastlingRights;\n readonly #enPassantSquare: Square | undefined;\n readonly #fullmoveNumber: number;\n readonly #halfmoveClock: number;\n #hash: string | undefined;\n readonly #turn: Color;\n\n constructor(board?: Map<Square, Piece>, options?: PositionOptions) {\n this.#board = new Map(board ?? startingBoard);\n const options_ = { ...DEFAULT_OPTIONS, ...options };\n this.#castlingRights = options_.castlingRights;\n this.#enPassantSquare = options_.enPassantSquare;\n this.#fullmoveNumber = options_.fullmoveNumber;\n this.#halfmoveClock = options_.halfmoveClock;\n this.#turn = options_.turn;\n }\n\n get castlingRights(): CastlingRights {\n return this.#castlingRights;\n }\n\n get enPassantSquare(): Square | undefined {\n return this.#enPassantSquare;\n }\n\n get fullmoveNumber(): number {\n return this.#fullmoveNumber;\n }\n\n get halfmoveClock(): number {\n return this.#halfmoveClock;\n }\n\n /**\n * A Zobrist hash of the position as a 16-character hex string.\n * Computed once and cached. Two positions with the same hash are\n * considered identical (with negligible collision probability).\n *\n * @remarks The hash algorithm and format are not stable across major\n * versions. Do not persist hashes across version upgrades.\n */\n get hash(): string {\n if (this.#hash !== undefined) {\n return this.#hash;\n }\n\n let h = 0n;\n\n for (const [sq, p] of this.#board) {\n h ^= PIECE_TABLE[sq]?.[p.type]?.[p.color] ?? 0n;\n }\n\n h ^= TURN_TABLE[this.#turn];\n\n for (const [right, active] of Object.entries(this.#castlingRights) as [\n string,\n boolean,\n ][]) {\n if (active) {\n h ^= CASTLING_TABLE[right] ?? 0n;\n }\n }\n\n if (this.#enPassantSquare !== undefined) {\n const file = this.#enPassantSquare[0] as File;\n h ^= EP_TABLE[file];\n }\n\n this.#hash = h.toString(16).padStart(16, '0');\n return this.#hash;\n }\n\n get isInsufficientMaterial(): boolean {\n const nonKingEntries: [Square, Piece][] = [];\n for (const [sq, p] of this.#board) {\n if (p.type !== 'k') {\n nonKingEntries.push([sq, p]);\n }\n }\n\n // K vs K\n if (nonKingEntries.length === 0) {\n return true;\n }\n\n // K vs KB or K vs KN\n if (nonKingEntries.length === 1) {\n const sole = nonKingEntries[0]?.[1];\n return sole?.type === 'b' || sole?.type === 'n';\n }\n\n // KB vs KB (any number) — all non-king pieces must be bishops on the same square color\n const allBishops = nonKingEntries.every(([, p]) => p.type === 'b');\n if (allBishops) {\n const first = nonKingEntries[0];\n if (first === undefined) {\n return true;\n }\n const firstSquareColor = squareColor(first[0]);\n return nonKingEntries.every(\n ([sq]) => squareColor(sq) === firstSquareColor,\n );\n }\n\n return false;\n }\n\n get isValid(): boolean {\n let blackKings = 0;\n let whiteKings = 0;\n\n for (const [square, p] of this.#board) {\n if (p.type === 'k') {\n if (p.color === 'b') {\n blackKings++;\n } else {\n whiteKings++;\n }\n }\n\n // No pawns on rank 1 or 8\n if (p.type === 'p' && (square[1] === '1' || square[1] === '8')) {\n return false;\n }\n }\n\n if (blackKings !== 1 || whiteKings !== 1) {\n return false;\n }\n\n // Side not to move must not be in check\n const opponent: Color = this.#turn === 'w' ? 'b' : 'w';\n for (const [square, p] of this.#board) {\n if (\n p.type === 'k' &&\n p.color === opponent &&\n this.isAttacked(square, this.#turn)\n ) {\n return false;\n }\n }\n\n return true;\n }\n\n get isCheck(): boolean {\n for (const [square, p] of this.#board) {\n if (p.type === 'k' && p.color === this.#turn) {\n const opponent: Color = this.#turn === 'w' ? 'b' : 'w';\n return this.isAttacked(square, opponent);\n }\n }\n return false;\n }\n\n get turn(): Color {\n return this.#turn;\n }\n\n #isAttackedByPiece(\n board: (Piece | undefined)[],\n targetIndex: number,\n fromIndex: number,\n p: Piece,\n by: Color,\n ): boolean {\n const diff = targetIndex - fromIndex;\n const tableIndex = diff + DIFF_OFFSET;\n\n if (tableIndex < 0 || tableIndex >= 240) {\n return false;\n }\n\n const attackMask = ATTACKS[tableIndex] ?? 0;\n const pieceMask = PIECE_MASKS[p.type] ?? 0;\n\n if ((attackMask & pieceMask) === 0) {\n return false;\n }\n\n if (p.type === 'p') {\n if (by === 'w' && diff > 0) {\n return false;\n }\n\n if (by === 'b' && diff < 0) {\n return false;\n }\n }\n\n const ray = RAYS[tableIndex] ?? 0;\n\n if (ray === 0) {\n return true;\n }\n\n let index = fromIndex + ray;\n while (index !== targetIndex) {\n if ((index & OFF_BOARD) !== 0) {\n return false;\n }\n\n if (board[index] !== undefined) {\n return false;\n }\n\n index += ray;\n }\n\n return true;\n }\n\n attackers(square: Square, by: Color): Square[] {\n const board = boardFromMap(this.#board);\n const targetIndex = squareToIndex(square);\n const result: Square[] = [];\n\n for (const [sq, p] of this.#board) {\n if (p.color !== by) {\n continue;\n }\n\n const fromIndex = squareToIndex(sq);\n if (this.#isAttackedByPiece(board, targetIndex, fromIndex, p, by)) {\n result.push(sq);\n }\n }\n\n return result;\n }\n\n findPiece(piece: Piece): Square[] {\n const result: Square[] = [];\n for (const [sq, p] of this.#board) {\n if (p.color === piece.color && p.type === piece.type) {\n result.push(sq);\n }\n }\n return result;\n }\n\n isAttacked(square: Square, by: Color): boolean {\n const board = boardFromMap(this.#board);\n const targetIndex = squareToIndex(square);\n\n for (const [sq, p] of this.#board) {\n if (p.color !== by) {\n continue;\n }\n\n const fromIndex = squareToIndex(sq);\n if (this.#isAttackedByPiece(board, targetIndex, fromIndex, p, by)) {\n return true;\n }\n }\n\n return false;\n }\n\n piece(square: Square): Piece | undefined {\n return this.#board.get(square);\n }\n\n pieces(color?: Color): Map<Square, Piece> {\n if (color === undefined) {\n return new Map(this.#board);\n }\n const result = new Map<Square, Piece>();\n for (const [sq, p] of this.#board) {\n if (p.color === color) {\n result.set(sq, p);\n }\n }\n return result;\n }\n}\n","import { Position } from './position.js';\n\nimport type { Piece, Square } from './types.js';\n\nconst EMPTY_BOARD = new Map<Square, Piece>();\n\nconst STARTING_POSITION = new Position();\n\nexport { EMPTY_BOARD, STARTING_POSITION };\n\nexport { COLORS, FILES, PIECE_TYPES, RANKS, SQUARES } from './primitives.js';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["#board","#castlingRights","#enPassantSquare","#fullmoveNumber","#halfmoveClock","#turn","#hash","#isAttackedByPiece"],"sources":["../src/internal/board.ts","../src/internal/tables.ts","../src/primitives.ts","../src/internal/zobrist.ts","../src/squares.ts","../src/starting-board.ts","../src/position.ts","../src/constants.ts"],"sourcesContent":["import type { Piece, Square } from '../types.js';\n\nconst OFF_BOARD = 0x88;\n\nfunction squareToIndex(square: Square): number {\n const file = (square.codePointAt(0) ?? 0) - ('a'.codePointAt(0) ?? 0);\n const rank = Number.parseInt(square[1] ?? '1', 10);\n return (8 - rank) * 16 + file;\n}\n\n/**\n * Converts a Map<Square, Piece> to the internal 0x88 array representation.\n * The bridge between the public Position type and the 0x88 internal layout.\n */\nfunction boardFromMap(map: Map<Square, Piece>): (Piece | undefined)[] {\n const board: (Piece | undefined)[] = Array.from({ length: 128 });\n for (const [square, p] of map) {\n board[squareToIndex(square)] = p;\n }\n return board;\n}\n\nexport { OFF_BOARD, boardFromMap, squareToIndex };\n","import { OFF_BOARD } from './board.js';\n\nimport type { PieceType } from '../types.js';\n\n// ── Direction constants ───────────────────────────────────────────────────────\n\nconst KNIGHT_OFFSETS_0X88 = [-33, -31, -18, -14, 14, 18, 31, 33] as const;\nconst BISHOP_DIRS_0X88 = [-17, -15, 15, 17] as const;\nconst ROOK_DIRS_0X88 = [-16, -1, 1, 16] as const;\nconst KING_OFFSETS_0X88 = [-17, -16, -15, -1, 1, 15, 16, 17] as const;\n\n// ── Piece bitmasks ────────────────────────────────────────────────────────────\n\nconst PAWN_MASK = 0x01;\nconst KNIGHT_MASK = 0x02;\nconst BISHOP_MASK = 0x04;\nconst ROOK_MASK = 0x08;\nconst KING_MASK = 0x10;\n\nconst PIECE_MASKS: Record<PieceType, number> = {\n bishop: BISHOP_MASK,\n king: KING_MASK,\n knight: KNIGHT_MASK,\n pawn: PAWN_MASK,\n queen: BISHOP_MASK | ROOK_MASK,\n rook: ROOK_MASK,\n};\n\n// ── ATTACKS / RAYS lookup tables ──────────────────────────────────────────────\n//\n// ATTACKS[diff + DIFF_OFFSET] — bitmask of piece types that can attack along\n// the vector represented by `diff` (target_index - attacker_index).\n//\n// RAYS[diff + DIFF_OFFSET] — the ray step direction for sliding pieces (0 for\n// non-sliding pieces).\n//\n// DIFF_OFFSET = 119 centres the diff range [-119, +119] at index 0.\n\nconst DIFF_OFFSET = 119;\n\nconst ATTACKS: number[] = Array.from<number>({ length: 240 }).fill(0);\nconst RAYS: number[] = Array.from<number>({ length: 240 }).fill(0);\n\n(function initAttackTables() {\n // Knight\n for (const offset of KNIGHT_OFFSETS_0X88) {\n ATTACKS[offset + DIFF_OFFSET] =\n (ATTACKS[offset + DIFF_OFFSET] ?? 0) | KNIGHT_MASK;\n }\n\n // King\n for (const offset of KING_OFFSETS_0X88) {\n ATTACKS[offset + DIFF_OFFSET] =\n (ATTACKS[offset + DIFF_OFFSET] ?? 0) | KING_MASK;\n }\n\n // Pawns — white attacks at offsets -17 and -15 (toward rank 8 = lower index)\n // black attacks at +15 and +17. Both share PAWN_MASK; color checked at use time.\n for (const offset of [15, 17]) {\n ATTACKS[offset + DIFF_OFFSET] =\n (ATTACKS[offset + DIFF_OFFSET] ?? 0) | PAWN_MASK;\n ATTACKS[-offset + DIFF_OFFSET] =\n (ATTACKS[-offset + DIFF_OFFSET] ?? 0) | PAWN_MASK;\n }\n\n // Sliding pieces — walk every ray from every valid square\n for (let from = 0; from <= 119; from++) {\n if (from & OFF_BOARD) {\n continue;\n }\n\n for (const direction of ROOK_DIRS_0X88) {\n let to = from + direction;\n while (!(to & OFF_BOARD)) {\n const diff = to - from;\n ATTACKS[diff + DIFF_OFFSET] =\n (ATTACKS[diff + DIFF_OFFSET] ?? 0) | ROOK_MASK;\n RAYS[diff + DIFF_OFFSET] = direction;\n to += direction;\n }\n }\n\n for (const direction of BISHOP_DIRS_0X88) {\n let to = from + direction;\n while (!(to & OFF_BOARD)) {\n const diff = to - from;\n ATTACKS[diff + DIFF_OFFSET] =\n (ATTACKS[diff + DIFF_OFFSET] ?? 0) | BISHOP_MASK;\n RAYS[diff + DIFF_OFFSET] = direction;\n to += direction;\n }\n }\n }\n})();\n\nexport { ATTACKS, DIFF_OFFSET, PIECE_MASKS, RAYS };\n","import type { Color, File, PieceType, Rank, Square } from './types.js';\n\n/** All colors: `['black', 'white']`. */\nconst COLORS: Color[] = ['black', 'white'];\n\n/** All files: `['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']`. */\nconst FILES: File[] = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];\n\n/** All ranks: `['1', '2', '3', '4', '5', '6', '7', '8']`. */\nconst RANKS: Rank[] = ['1', '2', '3', '4', '5', '6', '7', '8'];\n\n/** All piece types: `['bishop', 'king', 'knight', 'pawn', 'queen', 'rook']`. */\nconst PIECE_TYPES: PieceType[] = [\n 'bishop',\n 'king',\n 'knight',\n 'pawn',\n 'queen',\n 'rook',\n];\n\n/** All 64 squares, ordered a8–h1 (file-major, rank 8 to rank 1). */\nconst SQUARES: Square[] = FILES.flatMap((f) =>\n RANKS.toReversed().map((r) => `${f}${r}` as Square),\n);\n\nexport { COLORS, FILES, PIECE_TYPES, RANKS, SQUARES };\n","import { COLORS, FILES, PIECE_TYPES, SQUARES } from '../primitives.js';\n\nimport type { Color, File, PieceType, Square } from '../types.js';\n\n// Seeded LCG for deterministic random numbers (no Math.random — must be stable across runs)\nfunction lcg(seed: number): () => bigint {\n let s = BigInt(seed);\n return () => {\n s =\n (s * 6_364_136_223_846_793_005n + 1_442_695_040_888_963_407n) &\n 0xff_ff_ff_ff_ff_ff_ff_ffn;\n return s;\n };\n}\n\nconst next = lcg(0xde_ad_be_ef);\n\n// Piece table: PIECE_TABLE[square][pieceType][color]\nconst PIECE_TABLE: Record<\n Square,\n Partial<Record<PieceType, Record<Color, bigint>>>\n> = Object.fromEntries(\n SQUARES.map((sq) => [\n sq,\n Object.fromEntries(\n PIECE_TYPES.map((pt) => [\n pt,\n Object.fromEntries(COLORS.map((c) => [c, next()])),\n ]),\n ),\n ]),\n) as Record<Square, Partial<Record<PieceType, Record<Color, bigint>>>>;\n\nconst TURN_TABLE: Record<Color, bigint> = {\n black: next(),\n white: next(),\n};\n\nconst CASTLING_TABLE: Record<string, bigint> = {\n 'black.king': next(),\n 'black.queen': next(),\n 'white.king': next(),\n 'white.queen': next(),\n};\n\nconst EP_TABLE: Record<File, bigint> = Object.fromEntries(\n FILES.map((f) => [f, next()]),\n) as Record<File, bigint>;\n\nexport { CASTLING_TABLE, EP_TABLE, PIECE_TABLE, TURN_TABLE };\n","import type { Square, SquareColor } from './types.js';\n\n/**\n * Returns the color of a square — `'dark'` or `'light'`.\n *\n * @param square - The square to query.\n */\nfunction squareColor(square: Square): SquareColor {\n const file = (square.codePointAt(0) ?? 0) - ('a'.codePointAt(0) ?? 0);\n const rank = Number.parseInt(square[1] ?? '1', 10);\n return (file + rank) % 2 === 1 ? 'dark' : 'light';\n}\n\nexport { squareColor };\n","import type { Piece, Square } from './types.js';\n\nconst BACK_RANK_TYPES = [\n 'rook',\n 'knight',\n 'bishop',\n 'queen',\n 'king',\n 'bishop',\n 'knight',\n 'rook',\n] as const;\nconst BACK_RANK_FILES = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] as const;\n\nconst startingBoardMap = new Map<Square, Piece>();\nfor (const [index, type] of BACK_RANK_TYPES.entries()) {\n const file = BACK_RANK_FILES[index];\n if (file === undefined) {\n continue;\n }\n startingBoardMap.set(`${file}1` as Square, { color: 'white', type });\n startingBoardMap.set(`${file}2` as Square, { color: 'white', type: 'pawn' });\n startingBoardMap.set(`${file}7` as Square, { color: 'black', type: 'pawn' });\n startingBoardMap.set(`${file}8` as Square, { color: 'black', type });\n}\n\nexport const startingBoard = startingBoardMap;\n","import {\n ATTACKS,\n CASTLING_TABLE,\n DIFF_OFFSET,\n EP_TABLE,\n OFF_BOARD,\n PIECE_MASKS,\n PIECE_TABLE,\n RAYS,\n TURN_TABLE,\n boardFromMap,\n squareToIndex,\n} from './internal/index.js';\nimport { squareColor } from './squares.js';\nimport { startingBoard } from './starting-board.js';\n\nimport type {\n CastlingRights,\n Color,\n DeriveOptions,\n EnPassantSquare,\n File,\n Piece,\n PositionOptions,\n Square,\n} from './types.js';\n\nconst DEFAULT_OPTIONS: Required<Omit<PositionOptions, 'enPassantSquare'>> &\n Pick<PositionOptions, 'enPassantSquare'> = {\n castlingRights: {\n black: { king: true, queen: true },\n white: { king: true, queen: true },\n },\n enPassantSquare: undefined,\n fullmoveNumber: 1,\n halfmoveClock: 0,\n turn: 'white',\n};\n\n/**\n * An immutable chess position — board state, turn, castling rights,\n * en passant square, and move counters.\n *\n * Query the position with getters and methods. Produce new positions\n * with {@link Position.derive | derive}.\n */\nexport class Position {\n readonly #board: Map<Square, Piece>;\n readonly #castlingRights: CastlingRights;\n readonly #enPassantSquare: EnPassantSquare | undefined;\n readonly #fullmoveNumber: number;\n readonly #halfmoveClock: number;\n #hash: string | undefined;\n readonly #turn: Color;\n\n /**\n * Creates a new position.\n *\n * @param board - Piece placement. Defaults to the standard starting position.\n * @param options - Turn, castling rights, en passant, and move counters.\n */\n constructor(board?: Map<Square, Piece>, options?: PositionOptions) {\n this.#board = new Map(board ?? startingBoard);\n const options_ = { ...DEFAULT_OPTIONS, ...options };\n this.#castlingRights = options_.castlingRights;\n this.#enPassantSquare = options_.enPassantSquare;\n this.#fullmoveNumber = options_.fullmoveNumber;\n this.#halfmoveClock = options_.halfmoveClock;\n this.#turn = options_.turn;\n }\n\n /** Which castling moves remain available. */\n get castlingRights(): CastlingRights {\n return this.#castlingRights;\n }\n\n /** En passant target square, or `undefined` if none. */\n get enPassantSquare(): EnPassantSquare | undefined {\n return this.#enPassantSquare;\n }\n\n /**\n * Game turn counter — starts at `1` and increments after each black move.\n */\n get fullmoveNumber(): number {\n return this.#fullmoveNumber;\n }\n\n /**\n * Number of half-moves since the last pawn advance or capture. Resets on\n * every pawn move or capture. A draw can be claimed when it reaches `100`.\n */\n get halfmoveClock(): number {\n return this.#halfmoveClock;\n }\n\n /**\n * A Zobrist hash of the position as a 16-character hex string.\n * Computed once and cached. Two positions with the same hash are\n * considered identical (with negligible collision probability).\n *\n * @remarks The hash algorithm and format are not stable across major\n * versions. Do not persist hashes across version upgrades.\n */\n get hash(): string {\n if (this.#hash !== undefined) {\n return this.#hash;\n }\n\n let h = 0n;\n\n for (const [sq, p] of this.#board) {\n h ^= PIECE_TABLE[sq]?.[p.type]?.[p.color] ?? 0n;\n }\n\n h ^= TURN_TABLE[this.#turn];\n\n for (const [color, sides] of Object.entries(this.#castlingRights) as [\n string,\n { king: boolean; queen: boolean },\n ][]) {\n for (const [side, active] of Object.entries(sides) as [\n string,\n boolean,\n ][]) {\n if (active) {\n h ^= CASTLING_TABLE[`${color}.${side}`] ?? 0n;\n }\n }\n }\n\n if (this.#enPassantSquare !== undefined) {\n const file = this.#enPassantSquare[0] as File;\n h ^= EP_TABLE[file];\n }\n\n this.#hash = h.toString(16).padStart(16, '0');\n return this.#hash;\n }\n\n /**\n * Whether the position is a draw by insufficient material (FIDE rules):\n * K vs K, K+B vs K, K+N vs K, or K+B vs K+B with same-color bishops.\n */\n get isInsufficientMaterial(): boolean {\n const nonKingEntries: [Square, Piece][] = [];\n for (const [sq, p] of this.#board) {\n if (p.type !== 'king') {\n nonKingEntries.push([sq, p]);\n }\n }\n\n // K vs K\n if (nonKingEntries.length === 0) {\n return true;\n }\n\n // K vs KB or K vs KN\n if (nonKingEntries.length === 1) {\n const sole = nonKingEntries[0]?.[1];\n return sole?.type === 'bishop' || sole?.type === 'knight';\n }\n\n // KB vs KB (any number) — all non-king pieces must be bishops on the same square color\n const allBishops = nonKingEntries.every(([, p]) => p.type === 'bishop');\n if (allBishops) {\n const first = nonKingEntries[0];\n if (first === undefined) {\n return true;\n }\n const firstSquareColor = squareColor(first[0]);\n return nonKingEntries.every(\n ([sq]) => squareColor(sq) === firstSquareColor,\n );\n }\n\n return false;\n }\n\n /**\n * Whether the position is legally reachable: exactly one king per side,\n * no pawns on ranks 1 or 8, and the side not to move is not in check.\n */\n get isValid(): boolean {\n let blackKings = 0;\n let whiteKings = 0;\n\n for (const [square, p] of this.#board) {\n if (p.type === 'king') {\n if (p.color === 'black') {\n blackKings++;\n } else {\n whiteKings++;\n }\n }\n\n // No pawns on rank 1 or 8\n if (p.type === 'pawn' && (square[1] === '1' || square[1] === '8')) {\n return false;\n }\n }\n\n if (blackKings !== 1 || whiteKings !== 1) {\n return false;\n }\n\n // Side not to move must not be in check\n const opponent: Color = this.#turn === 'white' ? 'black' : 'white';\n for (const [square, p] of this.#board) {\n if (\n p.type === 'king' &&\n p.color === opponent &&\n this.isAttacked(square, this.#turn)\n ) {\n return false;\n }\n }\n\n return true;\n }\n\n /** Whether the side to move is in check. */\n get isCheck(): boolean {\n for (const [square, p] of this.#board) {\n if (p.type === 'king' && p.color === this.#turn) {\n const opponent: Color = this.#turn === 'white' ? 'black' : 'white';\n return this.isAttacked(square, opponent);\n }\n }\n return false;\n }\n\n /** Side to move — `'white'` or `'black'`. */\n get turn(): Color {\n return this.#turn;\n }\n\n #isAttackedByPiece(\n board: (Piece | undefined)[],\n targetIndex: number,\n fromIndex: number,\n p: Piece,\n by: Color,\n ): boolean {\n const diff = targetIndex - fromIndex;\n const tableIndex = diff + DIFF_OFFSET;\n\n if (tableIndex < 0 || tableIndex >= 240) {\n return false;\n }\n\n const attackMask = ATTACKS[tableIndex] ?? 0;\n const pieceMask = PIECE_MASKS[p.type] ?? 0;\n\n if ((attackMask & pieceMask) === 0) {\n return false;\n }\n\n if (p.type === 'pawn') {\n if (by === 'white' && diff > 0) {\n return false;\n }\n\n if (by === 'black' && diff < 0) {\n return false;\n }\n }\n\n const ray = RAYS[tableIndex] ?? 0;\n\n if (ray === 0) {\n return true;\n }\n\n let index = fromIndex + ray;\n while (index !== targetIndex) {\n if ((index & OFF_BOARD) !== 0) {\n return false;\n }\n\n if (board[index] !== undefined) {\n return false;\n }\n\n index += ray;\n }\n\n return true;\n }\n\n /**\n * Returns a new position with the given changes applied. Fields not\n * provided are carried over from the source. Calling with no argument\n * returns a clone.\n *\n * @param changes - Board deltas and option overrides to apply.\n */\n derive(changes?: DeriveOptions): Position {\n const board = new Map(this.#board);\n\n if (changes?.changes) {\n for (const [square, piece] of changes.changes) {\n if (piece === undefined) {\n board.delete(square);\n } else {\n board.set(square, piece);\n }\n }\n }\n\n return new Position(board, {\n castlingRights: changes?.castlingRights ?? this.#castlingRights,\n enPassantSquare:\n 'enPassantSquare' in (changes ?? {})\n ? changes?.enPassantSquare\n : this.#enPassantSquare,\n fullmoveNumber: changes?.fullmoveNumber ?? this.#fullmoveNumber,\n halfmoveClock: changes?.halfmoveClock ?? this.#halfmoveClock,\n turn: changes?.turn ?? this.#turn,\n });\n }\n\n /**\n * Returns all squares occupied by pieces of the given color that\n * attack the target square.\n *\n * @param square - The target square.\n * @param by - The attacking color.\n */\n attackers(square: Square, by: Color): Square[] {\n const board = boardFromMap(this.#board);\n const targetIndex = squareToIndex(square);\n const result: Square[] = [];\n\n for (const [sq, p] of this.#board) {\n if (p.color !== by) {\n continue;\n }\n\n const fromIndex = squareToIndex(sq);\n if (this.#isAttackedByPiece(board, targetIndex, fromIndex, p, by)) {\n result.push(sq);\n }\n }\n\n return result;\n }\n\n /**\n * Returns `true` if any piece of the given color attacks the target square.\n *\n * @param square - The target square.\n * @param by - The attacking color.\n */\n isAttacked(square: Square, by: Color): boolean {\n const board = boardFromMap(this.#board);\n const targetIndex = squareToIndex(square);\n\n for (const [sq, p] of this.#board) {\n if (p.color !== by) {\n continue;\n }\n\n const fromIndex = squareToIndex(sq);\n if (this.#isAttackedByPiece(board, targetIndex, fromIndex, p, by)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Returns the piece on the given square, or `undefined` if empty.\n *\n * @param square - The square to query.\n */\n piece(square: Square): Piece | undefined {\n return this.#board.get(square);\n }\n\n /**\n * Returns a map of all pieces on the board, optionally filtered by color.\n *\n * @param color - If provided, only pieces of this color are returned.\n */\n pieces(color?: Color): Map<Square, Piece> {\n if (color === undefined) {\n return new Map(this.#board);\n }\n const result = new Map<Square, Piece>();\n for (const [sq, p] of this.#board) {\n if (p.color === color) {\n result.set(sq, p);\n }\n }\n return result;\n }\n}\n","import { Position } from './position.js';\n\n/** The standard chess starting position. */\nconst STARTING_POSITION = new Position();\n\nexport { STARTING_POSITION };\n\nexport { COLORS, FILES, PIECE_TYPES, RANKS, SQUARES } from './primitives.js';\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
"eslint-config-prettier": "^10.1.8",
|
|
14
14
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
15
15
|
"eslint-plugin-import-x": "^4.16.2",
|
|
16
|
-
"eslint-plugin-unicorn": "^
|
|
16
|
+
"eslint-plugin-unicorn": "^64.0.0",
|
|
17
17
|
"husky": "^9.1.7",
|
|
18
18
|
"lint-staged": "^16.4.0",
|
|
19
19
|
"prettier": "^3.8.1",
|
|
20
20
|
"tsdown": "^0.21.4",
|
|
21
21
|
"typedoc": "^0.28.17",
|
|
22
|
-
"typescript": "^
|
|
22
|
+
"typescript": "^6.0.2",
|
|
23
23
|
"typescript-eslint": "^8.57.0",
|
|
24
24
|
"vitest": "^4.1.0"
|
|
25
25
|
},
|
|
@@ -30,10 +30,6 @@
|
|
|
30
30
|
".": {
|
|
31
31
|
"import": "./dist/index.js",
|
|
32
32
|
"types": "./dist/index.d.ts"
|
|
33
|
-
},
|
|
34
|
-
"./internal": {
|
|
35
|
-
"import": "./dist/internal/index.js",
|
|
36
|
-
"types": "./dist/internal/index.d.ts"
|
|
37
33
|
}
|
|
38
34
|
},
|
|
39
35
|
"files": [
|
|
@@ -61,10 +57,10 @@
|
|
|
61
57
|
"sideEffects": false,
|
|
62
58
|
"type": "module",
|
|
63
59
|
"types": "dist/index.d.ts",
|
|
64
|
-
"version": "
|
|
60
|
+
"version": "2.0.1",
|
|
65
61
|
"scripts": {
|
|
66
62
|
"build": "tsdown",
|
|
67
|
-
"docs": "typedoc",
|
|
63
|
+
"docs": "typedoc src/index.ts --out docs --entryPointStrategy expand --plugin ./typedoc-plugin-collapse-square.mjs",
|
|
68
64
|
"format": "pnpm run format:ci --write",
|
|
69
65
|
"format:ci": "prettier -l \"**/*.+(css|js|json|jsx|md|mjs|mts|ts|tsx|yml|yaml)\"",
|
|
70
66
|
"lint": "pnpm run lint:style && pnpm run lint:types",
|
package/dist/internal/index.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { a as Piece, n as Color, o as PieceType, r as File, u as Square } from "../types-BN6JJgNK.js";
|
|
2
|
-
|
|
3
|
-
//#region src/internal/board.d.ts
|
|
4
|
-
declare const OFF_BOARD = 136;
|
|
5
|
-
declare function squareToIndex(square: Square): number;
|
|
6
|
-
declare function indexToSquare(index: number): Square;
|
|
7
|
-
/**
|
|
8
|
-
* Converts a Map<Square, Piece> to the internal 0x88 array representation.
|
|
9
|
-
* The bridge between the public Position type and the 0x88 internal layout.
|
|
10
|
-
*/
|
|
11
|
-
declare function boardFromMap(map: Map<Square, Piece>): (Piece | undefined)[];
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/internal/tables.d.ts
|
|
14
|
-
declare const PIECE_MASKS: Record<PieceType, number>;
|
|
15
|
-
declare const DIFF_OFFSET = 119;
|
|
16
|
-
declare const ATTACKS: number[];
|
|
17
|
-
declare const RAYS: number[];
|
|
18
|
-
//#endregion
|
|
19
|
-
//#region src/internal/zobrist.d.ts
|
|
20
|
-
declare const PIECE_TABLE: Record<Square, Partial<Record<PieceType, Record<Color, bigint>>>>;
|
|
21
|
-
declare const TURN_TABLE: Record<Color, bigint>;
|
|
22
|
-
declare const CASTLING_TABLE: Record<string, bigint>;
|
|
23
|
-
declare const EP_TABLE: Record<File, bigint>;
|
|
24
|
-
//#endregion
|
|
25
|
-
export { ATTACKS, CASTLING_TABLE, DIFF_OFFSET, EP_TABLE, OFF_BOARD, PIECE_MASKS, PIECE_TABLE, RAYS, TURN_TABLE, boardFromMap, indexToSquare, squareToIndex };
|
package/dist/internal/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as e,d as t,f as n,g as r,h as i,i as a,m as o,n as s,p as c,r as l,t as u,u as d}from"../internal-BOZCKSNm.js";export{d as ATTACKS,u as CASTLING_TABLE,t as DIFF_OFFSET,s as EP_TABLE,o as OFF_BOARD,n as PIECE_MASKS,l as PIECE_TABLE,c as RAYS,a as TURN_TABLE,i as boardFromMap,r as indexToSquare,e as squareToIndex};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=136;function t(e){let t=(e.codePointAt(0)??0)-(`a`.codePointAt(0)??0);return(8-Number.parseInt(e[1]??`1`,10))*16+t}function n(e){let t=8-Math.floor(e/16),n=e%16;return`${String.fromCodePoint((`a`.codePointAt(0)??0)+n)}${t}`}function r(e){let n=Array.from({length:128});for(let[r,i]of e)n[t(r)]=i;return n}const i=[-33,-31,-18,-14,14,18,31,33],a=[-17,-15,15,17],o=[-16,-1,1,16],s=[-17,-16,-15,-1,1,15,16,17],c={b:4,k:16,n:2,p:1,q:12,r:8},l=119,u=Array.from({length:240}).fill(0),d=Array.from({length:240}).fill(0);(function(){for(let e of i)u[e+119]=(u[e+119]??0)|2;for(let e of s)u[e+119]=(u[e+119]??0)|16;for(let e of[15,17])u[e+119]=(u[e+119]??0)|1,u[-e+119]=(u[-e+119]??0)|1;for(let e=0;e<=119;e++)if(!(e&136)){for(let t of o){let n=e+t;for(;!(n&136);){let r=n-e;u[r+119]=(u[r+119]??0)|8,d[r+119]=t,n+=t}}for(let t of a){let n=e+t;for(;!(n&136);){let r=n-e;u[r+119]=(u[r+119]??0)|4,d[r+119]=t,n+=t}}}})();const f=[`b`,`w`],p=[`a`,`b`,`c`,`d`,`e`,`f`,`g`,`h`],m=[`1`,`2`,`3`,`4`,`5`,`6`,`7`,`8`],h=[`b`,`k`,`n`,`p`,`q`,`r`],g=p.flatMap(e=>m.toReversed().map(t=>`${e}${t}`));function _(e){let t=BigInt(e);return()=>(t=t*6364136223846793005n+1442695040888963407n&18446744073709551615n,t)}const v=_(3735928559),y=Object.fromEntries(g.map(e=>[e,Object.fromEntries(h.map(e=>[e,Object.fromEntries(f.map(e=>[e,v()]))]))])),b={b:v(),w:v()},x={bK:v(),bQ:v(),wK:v(),wQ:v()},S=Object.fromEntries(p.map(e=>[e,v()]));export{t as _,f as a,m as c,l as d,c as f,n as g,r as h,b as i,g as l,e as m,S as n,p as o,d as p,y as r,h as s,x as t,u};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"internal-BOZCKSNm.js","names":[],"sources":["../src/internal/board.ts","../src/internal/tables.ts","../src/primitives.ts","../src/internal/zobrist.ts"],"sourcesContent":["import type { Piece, Square } from '../types.js';\n\nconst OFF_BOARD = 0x88;\n\nfunction squareToIndex(square: Square): number {\n const file = (square.codePointAt(0) ?? 0) - ('a'.codePointAt(0) ?? 0);\n const rank = Number.parseInt(square[1] ?? '1', 10);\n return (8 - rank) * 16 + file;\n}\n\nfunction indexToSquare(index: number): Square {\n const rank = 8 - Math.floor(index / 16);\n const file = index % 16;\n return `${String.fromCodePoint(('a'.codePointAt(0) ?? 0) + file)}${rank}` as Square;\n}\n\n/**\n * Converts a Map<Square, Piece> to the internal 0x88 array representation.\n * The bridge between the public Position type and the 0x88 internal layout.\n */\nfunction boardFromMap(map: Map<Square, Piece>): (Piece | undefined)[] {\n const board: (Piece | undefined)[] = Array.from({ length: 128 });\n for (const [square, p] of map) {\n board[squareToIndex(square)] = p;\n }\n return board;\n}\n\nexport { OFF_BOARD, boardFromMap, indexToSquare, squareToIndex };\n","import { OFF_BOARD } from './board.js';\n\nimport type { PieceType } from '../types.js';\n\n// ── Direction constants ───────────────────────────────────────────────────────\n\nconst KNIGHT_OFFSETS_0X88 = [-33, -31, -18, -14, 14, 18, 31, 33] as const;\nconst BISHOP_DIRS_0X88 = [-17, -15, 15, 17] as const;\nconst ROOK_DIRS_0X88 = [-16, -1, 1, 16] as const;\nconst KING_OFFSETS_0X88 = [-17, -16, -15, -1, 1, 15, 16, 17] as const;\n\n// ── Piece bitmasks ────────────────────────────────────────────────────────────\n\nconst PAWN_MASK = 0x01;\nconst KNIGHT_MASK = 0x02;\nconst BISHOP_MASK = 0x04;\nconst ROOK_MASK = 0x08;\nconst KING_MASK = 0x10;\n\nconst PIECE_MASKS: Record<PieceType, number> = {\n b: BISHOP_MASK,\n k: KING_MASK,\n n: KNIGHT_MASK,\n p: PAWN_MASK,\n q: BISHOP_MASK | ROOK_MASK,\n r: ROOK_MASK,\n};\n\n// ── ATTACKS / RAYS lookup tables ──────────────────────────────────────────────\n//\n// ATTACKS[diff + DIFF_OFFSET] — bitmask of piece types that can attack along\n// the vector represented by `diff` (target_index - attacker_index).\n//\n// RAYS[diff + DIFF_OFFSET] — the ray step direction for sliding pieces (0 for\n// non-sliding pieces).\n//\n// DIFF_OFFSET = 119 centres the diff range [-119, +119] at index 0.\n\nconst DIFF_OFFSET = 119;\n\nconst ATTACKS: number[] = Array.from<number>({ length: 240 }).fill(0);\nconst RAYS: number[] = Array.from<number>({ length: 240 }).fill(0);\n\n(function initAttackTables() {\n // Knight\n for (const offset of KNIGHT_OFFSETS_0X88) {\n ATTACKS[offset + DIFF_OFFSET] =\n (ATTACKS[offset + DIFF_OFFSET] ?? 0) | KNIGHT_MASK;\n }\n\n // King\n for (const offset of KING_OFFSETS_0X88) {\n ATTACKS[offset + DIFF_OFFSET] =\n (ATTACKS[offset + DIFF_OFFSET] ?? 0) | KING_MASK;\n }\n\n // Pawns — white attacks at offsets -17 and -15 (toward rank 8 = lower index)\n // black attacks at +15 and +17. Both share PAWN_MASK; color checked at use time.\n for (const offset of [15, 17]) {\n ATTACKS[offset + DIFF_OFFSET] =\n (ATTACKS[offset + DIFF_OFFSET] ?? 0) | PAWN_MASK;\n ATTACKS[-offset + DIFF_OFFSET] =\n (ATTACKS[-offset + DIFF_OFFSET] ?? 0) | PAWN_MASK;\n }\n\n // Sliding pieces — walk every ray from every valid square\n for (let from = 0; from <= 119; from++) {\n if (from & OFF_BOARD) {\n continue;\n }\n\n for (const direction of ROOK_DIRS_0X88) {\n let to = from + direction;\n while (!(to & OFF_BOARD)) {\n const diff = to - from;\n ATTACKS[diff + DIFF_OFFSET] =\n (ATTACKS[diff + DIFF_OFFSET] ?? 0) | ROOK_MASK;\n RAYS[diff + DIFF_OFFSET] = direction;\n to += direction;\n }\n }\n\n for (const direction of BISHOP_DIRS_0X88) {\n let to = from + direction;\n while (!(to & OFF_BOARD)) {\n const diff = to - from;\n ATTACKS[diff + DIFF_OFFSET] =\n (ATTACKS[diff + DIFF_OFFSET] ?? 0) | BISHOP_MASK;\n RAYS[diff + DIFF_OFFSET] = direction;\n to += direction;\n }\n }\n }\n})();\n\nexport { ATTACKS, DIFF_OFFSET, PIECE_MASKS, RAYS };\n","import type { Color, File, PieceType, Rank, Square } from './types.js';\n\nconst COLORS: Color[] = ['b', 'w'];\nconst FILES: File[] = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];\nconst RANKS: Rank[] = ['1', '2', '3', '4', '5', '6', '7', '8'];\nconst PIECE_TYPES: PieceType[] = ['b', 'k', 'n', 'p', 'q', 'r'];\nconst SQUARES: Square[] = FILES.flatMap((f) =>\n RANKS.toReversed().map((r) => `${f}${r}` as Square),\n);\n\nexport { COLORS, FILES, PIECE_TYPES, RANKS, SQUARES };\n","import { COLORS, FILES, PIECE_TYPES, SQUARES } from '../primitives.js';\n\nimport type { Color, File, PieceType, Square } from '../types.js';\n\n// Seeded LCG for deterministic random numbers (no Math.random — must be stable across runs)\nfunction lcg(seed: number): () => bigint {\n let s = BigInt(seed);\n return () => {\n s =\n (s * 6_364_136_223_846_793_005n + 1_442_695_040_888_963_407n) &\n 0xff_ff_ff_ff_ff_ff_ff_ffn;\n return s;\n };\n}\n\nconst next = lcg(0xde_ad_be_ef);\n\n// Piece table: PIECE_TABLE[square][pieceType][color]\nconst PIECE_TABLE: Record<\n Square,\n Partial<Record<PieceType, Record<Color, bigint>>>\n> = Object.fromEntries(\n SQUARES.map((sq) => [\n sq,\n Object.fromEntries(\n PIECE_TYPES.map((pt) => [\n pt,\n Object.fromEntries(COLORS.map((c) => [c, next()])),\n ]),\n ),\n ]),\n) as Record<Square, Partial<Record<PieceType, Record<Color, bigint>>>>;\n\nconst TURN_TABLE: Record<Color, bigint> = {\n b: next(),\n w: next(),\n};\n\nconst CASTLING_TABLE: Record<string, bigint> = {\n bK: next(),\n bQ: next(),\n wK: next(),\n wQ: next(),\n};\n\nconst EP_TABLE: Record<File, bigint> = Object.fromEntries(\n FILES.map((f) => [f, next()]),\n) as Record<File, bigint>;\n\nexport { CASTLING_TABLE, EP_TABLE, PIECE_TABLE, TURN_TABLE };\n"],"mappings":"AAEA,MAAM,EAAY,IAElB,SAAS,EAAc,EAAwB,CAC7C,IAAM,GAAQ,EAAO,YAAY,EAAE,EAAI,IAAM,IAAI,YAAY,EAAE,EAAI,GAEnE,OAAQ,EADK,OAAO,SAAS,EAAO,IAAM,IAAK,GAAG,EAC9B,GAAK,EAG3B,SAAS,EAAc,EAAuB,CAC5C,IAAM,EAAO,EAAI,KAAK,MAAM,EAAQ,GAAG,CACjC,EAAO,EAAQ,GACrB,MAAO,GAAG,OAAO,eAAe,IAAI,YAAY,EAAE,EAAI,GAAK,EAAK,GAAG,IAOrE,SAAS,EAAa,EAAgD,CACpE,IAAM,EAA+B,MAAM,KAAK,CAAE,OAAQ,IAAK,CAAC,CAChE,IAAK,GAAM,CAAC,EAAQ,KAAM,EACxB,EAAM,EAAc,EAAO,EAAI,EAEjC,OAAO,ECnBT,MAAM,EAAsB,CAAC,IAAK,IAAK,IAAK,IAAK,GAAI,GAAI,GAAI,GAAG,CAC1D,EAAmB,CAAC,IAAK,IAAK,GAAI,GAAG,CACrC,EAAiB,CAAC,IAAK,GAAI,EAAG,GAAG,CACjC,EAAoB,CAAC,IAAK,IAAK,IAAK,GAAI,EAAG,GAAI,GAAI,GAAG,CAUtD,EAAyC,CAC7C,EAAG,EACH,EAAG,GACH,EAAG,EACH,EAAG,EACH,EAAG,GACH,EAAG,EACJ,CAYK,EAAc,IAEd,EAAoB,MAAM,KAAa,CAAE,OAAQ,IAAK,CAAC,CAAC,KAAK,EAAE,CAC/D,EAAiB,MAAM,KAAa,CAAE,OAAQ,IAAK,CAAC,CAAC,KAAK,EAAE,EAEjE,UAA4B,CAE3B,IAAK,IAAM,KAAU,EACnB,EAAQ,EAAA,MACL,EAAQ,EAAA,MAAyB,GAAK,EAI3C,IAAK,IAAM,KAAU,EACnB,EAAQ,EAAA,MACL,EAAQ,EAAA,MAAyB,GAAK,GAK3C,IAAK,IAAM,IAAU,CAAC,GAAI,GAAG,CAC3B,EAAQ,EAAA,MACL,EAAQ,EAAA,MAAyB,GAAK,EACzC,EAAQ,CAAC,EAAA,MACN,EAAQ,CAAC,EAAA,MAAyB,GAAK,EAI5C,IAAK,IAAI,EAAO,EAAG,GAAQ,IAAK,IAC1B,OAAA,KAIJ,KAAK,IAAM,KAAa,EAAgB,CACtC,IAAI,EAAK,EAAO,EAChB,KAAO,EAAE,EAAA,MAAiB,CACxB,IAAM,EAAO,EAAK,EAClB,EAAQ,EAAA,MACL,EAAQ,EAAA,MAAuB,GAAK,EACvC,EAAK,EAAA,KAAsB,EAC3B,GAAM,GAIV,IAAK,IAAM,KAAa,EAAkB,CACxC,IAAI,EAAK,EAAO,EAChB,KAAO,EAAE,EAAA,MAAiB,CACxB,IAAM,EAAO,EAAK,EAClB,EAAQ,EAAA,MACL,EAAQ,EAAA,MAAuB,GAAK,EACvC,EAAK,EAAA,KAAsB,EAC3B,GAAM,QAIV,CC3FJ,MAAM,EAAkB,CAAC,IAAK,IAAI,CAC5B,EAAgB,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAI,CACxD,EAAgB,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAI,CACxD,EAA2B,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAI,CACzD,EAAoB,EAAM,QAAS,GACvC,EAAM,YAAY,CAAC,IAAK,GAAM,GAAG,IAAI,IAAc,CACpD,CCHD,SAAS,EAAI,EAA4B,CACvC,IAAI,EAAI,OAAO,EAAK,CACpB,WACE,EACG,EAAI,qBAA6B,qBAClC,sBACK,GAIX,MAAM,EAAO,EAAI,WAAc,CAGzB,EAGF,OAAO,YACT,EAAQ,IAAK,GAAO,CAClB,EACA,OAAO,YACL,EAAY,IAAK,GAAO,CACtB,EACA,OAAO,YAAY,EAAO,IAAK,GAAM,CAAC,EAAG,GAAM,CAAC,CAAC,CAAC,CACnD,CAAC,CACH,CACF,CAAC,CACH,CAEK,EAAoC,CACxC,EAAG,GAAM,CACT,EAAG,GAAM,CACV,CAEK,EAAyC,CAC7C,GAAI,GAAM,CACV,GAAI,GAAM,CACV,GAAI,GAAM,CACV,GAAI,GAAM,CACX,CAEK,EAAiC,OAAO,YAC5C,EAAM,IAAK,GAAM,CAAC,EAAG,GAAM,CAAC,CAAC,CAC9B"}
|
package/dist/types-BN6JJgNK.d.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
//#region src/types.d.ts
|
|
2
|
-
type Color = 'b' | 'w';
|
|
3
|
-
type File = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h';
|
|
4
|
-
type PieceType = 'b' | 'k' | 'n' | 'p' | 'q' | 'r';
|
|
5
|
-
type PromotionPieceType = 'b' | 'n' | 'q' | 'r';
|
|
6
|
-
type Rank = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8';
|
|
7
|
-
/** @preventExpand */
|
|
8
|
-
type Square = `${File}${Rank}`;
|
|
9
|
-
type SquareColor = 'dark' | 'light';
|
|
10
|
-
interface CastlingRights {
|
|
11
|
-
bK: boolean;
|
|
12
|
-
bQ: boolean;
|
|
13
|
-
wK: boolean;
|
|
14
|
-
wQ: boolean;
|
|
15
|
-
}
|
|
16
|
-
interface PositionOptions {
|
|
17
|
-
castlingRights?: CastlingRights;
|
|
18
|
-
enPassantSquare?: Square;
|
|
19
|
-
fullmoveNumber?: number;
|
|
20
|
-
halfmoveClock?: number;
|
|
21
|
-
turn?: Color;
|
|
22
|
-
}
|
|
23
|
-
interface Move {
|
|
24
|
-
from: Square;
|
|
25
|
-
promotion: PromotionPieceType | undefined;
|
|
26
|
-
to: Square;
|
|
27
|
-
}
|
|
28
|
-
interface Piece {
|
|
29
|
-
color: Color;
|
|
30
|
-
type: PieceType;
|
|
31
|
-
}
|
|
32
|
-
//#endregion
|
|
33
|
-
export { Piece as a, PromotionPieceType as c, SquareColor as d, Move as i, Rank as l, Color as n, PieceType as o, File as r, PositionOptions as s, CastlingRights as t, Square as u };
|