@journey-to-code/games 0.0.0-stage → 0.1.0

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.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/dist/backgammon.d.ts +146 -0
  3. package/dist/backgammon.js +1221 -0
  4. package/dist/backgammon.js.map +1 -0
  5. package/dist/blackjack.d.ts +66 -0
  6. package/dist/blackjack.js +452 -0
  7. package/dist/blackjack.js.map +1 -0
  8. package/dist/bluff.d.ts +60 -0
  9. package/dist/bluff.js +239 -0
  10. package/dist/bluff.js.map +1 -0
  11. package/dist/board.d.ts +387 -0
  12. package/dist/board.js +105 -0
  13. package/dist/board.js.map +1 -0
  14. package/dist/bridge.d.ts +86 -0
  15. package/dist/bridge.js +494 -0
  16. package/dist/bridge.js.map +1 -0
  17. package/dist/canasta.d.ts +88 -0
  18. package/dist/canasta.js +646 -0
  19. package/dist/canasta.js.map +1 -0
  20. package/dist/checkers.d.ts +108 -0
  21. package/dist/checkers.js +756 -0
  22. package/dist/checkers.js.map +1 -0
  23. package/dist/chess.d.ts +162 -0
  24. package/dist/chess.js +904 -0
  25. package/dist/chess.js.map +1 -0
  26. package/dist/chesscore.d.ts +136 -0
  27. package/dist/chesscore.js +1121 -0
  28. package/dist/chesscore.js.map +1 -0
  29. package/dist/combat.d.ts +102 -0
  30. package/dist/combat.js +772 -0
  31. package/dist/combat.js.map +1 -0
  32. package/dist/contractrummy.d.ts +77 -0
  33. package/dist/contractrummy.js +430 -0
  34. package/dist/contractrummy.js.map +1 -0
  35. package/dist/crazy8.d.ts +65 -0
  36. package/dist/crazy8.js +366 -0
  37. package/dist/crazy8.js.map +1 -0
  38. package/dist/cribbage.d.ts +75 -0
  39. package/dist/cribbage.js +428 -0
  40. package/dist/cribbage.js.map +1 -0
  41. package/dist/cuckoo.d.ts +43 -0
  42. package/dist/cuckoo.js +216 -0
  43. package/dist/cuckoo.js.map +1 -0
  44. package/dist/drinkingpyramid.d.ts +54 -0
  45. package/dist/drinkingpyramid.js +213 -0
  46. package/dist/drinkingpyramid.js.map +1 -0
  47. package/dist/drinks.d.ts +25 -0
  48. package/dist/drinks.js +40 -0
  49. package/dist/drinks.js.map +1 -0
  50. package/dist/dropfour.d.ts +103 -0
  51. package/dist/dropfour.js +577 -0
  52. package/dist/dropfour.js.map +1 -0
  53. package/dist/durak.d.ts +58 -0
  54. package/dist/durak.js +332 -0
  55. package/dist/durak.js.map +1 -0
  56. package/dist/euchre.d.ts +80 -0
  57. package/dist/euchre.js +453 -0
  58. package/dist/euchre.js.map +1 -0
  59. package/dist/fivedraw.d.ts +50 -0
  60. package/dist/fivedraw.js +289 -0
  61. package/dist/fivedraw.js.map +1 -0
  62. package/dist/fivehundred.d.ts +90 -0
  63. package/dist/fivehundred.js +504 -0
  64. package/dist/fivehundred.js.map +1 -0
  65. package/dist/freecell.d.ts +50 -0
  66. package/dist/freecell.js +327 -0
  67. package/dist/freecell.js.map +1 -0
  68. package/dist/fruitsalad.d.ts +69 -0
  69. package/dist/fruitsalad.js +427 -0
  70. package/dist/fruitsalad.js.map +1 -0
  71. package/dist/gin.d.ts +72 -0
  72. package/dist/gin.js +393 -0
  73. package/dist/gin.js.map +1 -0
  74. package/dist/go.d.ts +132 -0
  75. package/dist/go.js +659 -0
  76. package/dist/go.js.map +1 -0
  77. package/dist/goboard.d.ts +218 -0
  78. package/dist/goboard.js +880 -0
  79. package/dist/goboard.js.map +1 -0
  80. package/dist/gofish.d.ts +37 -0
  81. package/dist/gofish.js +235 -0
  82. package/dist/gofish.js.map +1 -0
  83. package/dist/golf.d.ts +64 -0
  84. package/dist/golf.js +343 -0
  85. package/dist/golf.js.map +1 -0
  86. package/dist/handfoot.d.ts +86 -0
  87. package/dist/handfoot.js +515 -0
  88. package/dist/handfoot.js.map +1 -0
  89. package/dist/hearts.d.ts +65 -0
  90. package/dist/hearts.js +390 -0
  91. package/dist/hearts.js.map +1 -0
  92. package/dist/holdem.d.ts +45 -0
  93. package/dist/holdem.js +286 -0
  94. package/dist/holdem.js.map +1 -0
  95. package/dist/horserace.d.ts +49 -0
  96. package/dist/horserace.js +207 -0
  97. package/dist/horserace.js.map +1 -0
  98. package/dist/index.d.ts +65 -0
  99. package/dist/index.js +66 -0
  100. package/dist/index.js.map +1 -0
  101. package/dist/kingscorner.d.ts +59 -0
  102. package/dist/kingscorner.js +374 -0
  103. package/dist/kingscorner.js.map +1 -0
  104. package/dist/kingscup.d.ts +44 -0
  105. package/dist/kingscup.js +185 -0
  106. package/dist/kingscup.js.map +1 -0
  107. package/dist/kit.d.ts +283 -0
  108. package/dist/kit.js +73 -0
  109. package/dist/kit.js.map +1 -0
  110. package/dist/klondike.d.ts +57 -0
  111. package/dist/klondike.js +314 -0
  112. package/dist/klondike.js.map +1 -0
  113. package/dist/liarspoker.d.ts +61 -0
  114. package/dist/liarspoker.js +298 -0
  115. package/dist/liarspoker.js.map +1 -0
  116. package/dist/memory.d.ts +36 -0
  117. package/dist/memory.js +181 -0
  118. package/dist/memory.js.map +1 -0
  119. package/dist/minigolf.d.ts +104 -0
  120. package/dist/minigolf.js +724 -0
  121. package/dist/minigolf.js.map +1 -0
  122. package/dist/minigolfcourse.d.ts +127 -0
  123. package/dist/minigolfcourse.js +470 -0
  124. package/dist/minigolfcourse.js.map +1 -0
  125. package/dist/offers.d.ts +136 -0
  126. package/dist/offers.js +279 -0
  127. package/dist/offers.js.map +1 -0
  128. package/dist/ohhell.d.ts +66 -0
  129. package/dist/ohhell.js +331 -0
  130. package/dist/ohhell.js.map +1 -0
  131. package/dist/oldmaid.d.ts +40 -0
  132. package/dist/oldmaid.js +186 -0
  133. package/dist/oldmaid.js.map +1 -0
  134. package/dist/pinochle.d.ts +94 -0
  135. package/dist/pinochle.js +466 -0
  136. package/dist/pinochle.js.map +1 -0
  137. package/dist/pitch.d.ts +77 -0
  138. package/dist/pitch.js +362 -0
  139. package/dist/pitch.js.map +1 -0
  140. package/dist/poker.d.ts +79 -0
  141. package/dist/poker.js +268 -0
  142. package/dist/poker.js.map +1 -0
  143. package/dist/president.d.ts +56 -0
  144. package/dist/president.js +339 -0
  145. package/dist/president.js.map +1 -0
  146. package/dist/pyramid.d.ts +39 -0
  147. package/dist/pyramid.js +200 -0
  148. package/dist/pyramid.js.map +1 -0
  149. package/dist/registry.d.ts +17 -0
  150. package/dist/registry.js +70 -0
  151. package/dist/registry.js.map +1 -0
  152. package/dist/reversi.d.ts +69 -0
  153. package/dist/reversi.js +584 -0
  154. package/dist/reversi.js.map +1 -0
  155. package/dist/ridethebus.d.ts +48 -0
  156. package/dist/ridethebus.js +257 -0
  157. package/dist/ridethebus.js.map +1 -0
  158. package/dist/rummy500.d.ts +89 -0
  159. package/dist/rummy500.js +474 -0
  160. package/dist/rummy500.js.map +1 -0
  161. package/dist/sevens.d.ts +48 -0
  162. package/dist/sevens.js +221 -0
  163. package/dist/sevens.js.map +1 -0
  164. package/dist/slapjack.d.ts +42 -0
  165. package/dist/slapjack.js +224 -0
  166. package/dist/slapjack.js.map +1 -0
  167. package/dist/speed.d.ts +44 -0
  168. package/dist/speed.js +201 -0
  169. package/dist/speed.js.map +1 -0
  170. package/dist/spider.d.ts +51 -0
  171. package/dist/spider.js +320 -0
  172. package/dist/spider.js.map +1 -0
  173. package/dist/spite.d.ts +75 -0
  174. package/dist/spite.js +372 -0
  175. package/dist/spite.js.map +1 -0
  176. package/dist/sport.d.ts +266 -0
  177. package/dist/sport.js +48 -0
  178. package/dist/sport.js.map +1 -0
  179. package/dist/switch.d.ts +69 -0
  180. package/dist/switch.js +393 -0
  181. package/dist/switch.js.map +1 -0
  182. package/dist/table.d.ts +284 -0
  183. package/dist/table.js +67 -0
  184. package/dist/table.js.map +1 -0
  185. package/dist/talk.d.ts +17 -0
  186. package/dist/talk.js +44 -0
  187. package/dist/talk.js.map +1 -0
  188. package/dist/thirtyone.d.ts +66 -0
  189. package/dist/thirtyone.js +372 -0
  190. package/dist/thirtyone.js.map +1 -0
  191. package/dist/tictactoe.d.ts +68 -0
  192. package/dist/tictactoe.js +541 -0
  193. package/dist/tictactoe.js.map +1 -0
  194. package/dist/tricks.d.ts +21 -0
  195. package/dist/tricks.js +48 -0
  196. package/dist/tricks.js.map +1 -0
  197. package/dist/war.d.ts +44 -0
  198. package/dist/war.js +185 -0
  199. package/dist/war.js.map +1 -0
  200. package/dist/whist.d.ts +52 -0
  201. package/dist/whist.js +218 -0
  202. package/dist/whist.js.map +1 -0
  203. package/dist/yacht.d.ts +96 -0
  204. package/dist/yacht.js +653 -0
  205. package/dist/yacht.js.map +1 -0
  206. package/package.json +41 -4
  207. package/README.md +0 -3
@@ -0,0 +1,1121 @@
1
+ // The inside of Chess: a position a computer can search quickly, every move that can be made from
2
+ // it, and the computer player's search. chess.ts is the game (the table's state, notation, clocks,
3
+ // what a seat sees); this file knows nothing of seats or tables.
4
+ //
5
+ // The board is the classic "0x88" one: 128 numbers, a square is rank × 16 + file, and a square is
6
+ // off the board exactly when it has a bit of 0x88 set, so a piece walking off an edge is caught
7
+ // with one test. Rank 0 is White's home rank and file 0 the a-file. A piece is its kind (pawn 1 to
8
+ // king 6) plus 8 if it is Black's.
9
+ //
10
+ // Everything here is our own: the evaluation's tables are worked out from a few plain ideas (the
11
+ // middle of the board, getting pieces off the back rank, pushing passed pawns) and not copied
12
+ // from anybody's engine, and there is no opening book.
13
+ export const PAWN = 1;
14
+ export const KNIGHT = 2;
15
+ export const BISHOP = 3;
16
+ export const ROOK = 4;
17
+ export const QUEEN = 5;
18
+ export const KING = 6;
19
+ export const WHITE = 0;
20
+ export const BLACK = 1;
21
+ const KNIGHT_STEPS = [-33, -31, -18, -14, 14, 18, 31, 33];
22
+ const KING_STEPS = [-17, -16, -15, -1, 1, 15, 16, 17];
23
+ const BISHOP_STEPS = [-17, -15, 15, 17];
24
+ const ROOK_STEPS = [-16, -1, 1, 16];
25
+ // A move is one number: where from (7 bits), where to (7), what a pawn becomes (3), and what kind
26
+ // of move it is (3).
27
+ export const FLAG_EP = 1;
28
+ export const FLAG_CASTLE_K = 2;
29
+ export const FLAG_CASTLE_Q = 3;
30
+ export const FLAG_DOUBLE = 4;
31
+ export const moveFrom = (m) => m & 127;
32
+ export const moveTo = (m) => (m >> 7) & 127;
33
+ export const movePromo = (m) => (m >> 14) & 7;
34
+ export const moveFlag = (m) => (m >> 17) & 7;
35
+ const mv = (from, to, promo = 0, flag = 0) => from | (to << 7) | (promo << 14) | (flag << 17);
36
+ /** A view's cell (row by row from a8) as a square here, and back. */
37
+ export const squareOf = (cell) => ((7 - (cell >> 3)) << 4) | (cell & 7);
38
+ export const cellOf = (sq) => ((7 - (sq >> 4)) << 3) | (sq & 7);
39
+ // ---- Zobrist keys -------------------------------------------------------------------------------
40
+ // A position's key is the XOR of a random number for each piece on each square, the side to move,
41
+ // the castling rights and the en passant file. The numbers come from a fixed seed, so a search
42
+ // gives the same answer every time and on every machine. Keys are 64 bits, kept as two halves.
43
+ const Z_LO = new Int32Array(16 * 128 + 16 + 8 + 1);
44
+ const Z_HI = new Int32Array(Z_LO.length);
45
+ const Z_CASTLE = 16 * 128;
46
+ const Z_EP = Z_CASTLE + 16;
47
+ const Z_SIDE = Z_EP + 8;
48
+ {
49
+ // mulberry32, written out so the table can never change under a search by a change elsewhere.
50
+ let seed = 0x6c0ffee5;
51
+ const next = () => {
52
+ seed = (seed + 0x6d2b79f5) | 0;
53
+ let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
54
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
55
+ return (t ^ (t >>> 14)) | 0;
56
+ };
57
+ for (let i = 0; i < Z_LO.length; i++) {
58
+ Z_LO[i] = next();
59
+ Z_HI[i] = next();
60
+ }
61
+ }
62
+ // ---- What a piece is worth, and where --------------------------------------------------------------
63
+ // Two tables a piece: the middlegame and the endgame, mixed by how much is left on the board.
64
+ const WORTH = [0, 100, 320, 330, 500, 900, 0];
65
+ /** How much each piece counts towards "still a middlegame": 24 with everything on. */
66
+ const PHASE = [0, 0, 1, 1, 2, 4, 0];
67
+ const MG = new Int16Array(16 * 128);
68
+ const EG = new Int16Array(16 * 128);
69
+ {
70
+ for (let kind = PAWN; kind <= KING; kind++) {
71
+ for (let r = 0; r < 8; r++) {
72
+ for (let f = 0; f < 8; f++) {
73
+ // How far in from the edge, 0 to 3 each way.
74
+ const cf = Math.min(f, 7 - f);
75
+ const cr = Math.min(r, 7 - r);
76
+ const centre = cf + cr;
77
+ let mg = 0;
78
+ let eg = 0;
79
+ if (kind === PAWN) {
80
+ const adv = r - 1;
81
+ // The middle pawns want to be out; one still at home is in its bishop's way.
82
+ mg = adv * 4 + (cf === 3 ? [-8, 4, 18, 22, 22, 22][adv] : cf === 2 ? [0, 2, 8, 10, 10, 10][adv] : 0);
83
+ eg = adv * adv * 2 + adv * 4;
84
+ if (r === 0 || r === 7)
85
+ mg = eg = 0;
86
+ }
87
+ else if (kind === KNIGHT) {
88
+ mg = centre * 8 - 28 - (r === 0 ? 10 : 0);
89
+ eg = centre * 6 - 20;
90
+ }
91
+ else if (kind === BISHOP) {
92
+ mg = centre * 4 - 10 - (r === 0 ? 8 : 0);
93
+ eg = centre * 3 - 8;
94
+ }
95
+ else if (kind === ROOK) {
96
+ mg = (r === 6 ? 18 : 0) + cf * 3 - 4;
97
+ eg = r === 6 ? 10 : 0;
98
+ }
99
+ else if (kind === QUEEN) {
100
+ mg = centre - 3 - (r === 0 ? 2 : 0);
101
+ eg = centre * 4 - 10;
102
+ }
103
+ else {
104
+ // Tucked in a corner while there are queens about; in the middle once there aren't.
105
+ mg = r === 0 ? [18, 26, 6, -6][cf] : -20 - r * 14;
106
+ eg = centre * 10 - 30;
107
+ }
108
+ const white = r * 16 + f;
109
+ const black = (7 - r) * 16 + f;
110
+ MG[kind * 128 + white] = WORTH[kind] + mg;
111
+ EG[kind * 128 + white] = WORTH[kind] + eg;
112
+ MG[(kind | 8) * 128 + black] = -(WORTH[kind] + mg);
113
+ EG[(kind | 8) * 128 + black] = -(WORTH[kind] + eg);
114
+ }
115
+ }
116
+ }
117
+ }
118
+ /** Moves a search can have made and not yet taken back: its deepest line and the captures looked at beyond it. */
119
+ const STACK = 256;
120
+ export class Position {
121
+ b = new Int8Array(128);
122
+ side = WHITE;
123
+ /** Castling rights, a bit each: White's king side (1), queen side (2), Black's (4, 8). */
124
+ castle = 0;
125
+ /** Where each castling rook started (Chess960 puts them anywhere). */
126
+ rookFrom = new Int16Array(4).fill(-1);
127
+ /** Per square: the rights that survive a move from or to it. */
128
+ mask = new Uint8Array(128).fill(15);
129
+ ep = -1;
130
+ half = 0;
131
+ full = 1;
132
+ king = new Int16Array(2);
133
+ lo = 0;
134
+ hi = 0;
135
+ /** Material and squares, White's less Black's, for the middlegame and the endgame. */
136
+ mg = 0;
137
+ eg = 0;
138
+ phase = 0;
139
+ sp = 0;
140
+ uCap = new Int8Array(STACK);
141
+ uCastle = new Int8Array(STACK);
142
+ uEp = new Int16Array(STACK);
143
+ uHalf = new Int16Array(STACK);
144
+ uLo = new Int32Array(STACK);
145
+ uHi = new Int32Array(STACK);
146
+ uMg = new Int32Array(STACK);
147
+ uEg = new Int32Array(STACK);
148
+ uPhase = new Int8Array(STACK);
149
+ constructor(setup) {
150
+ this.reset(setup);
151
+ }
152
+ /** Set the position up afresh. */
153
+ reset(setup) {
154
+ this.b.fill(0);
155
+ this.mask.fill(15);
156
+ this.rookFrom.fill(-1);
157
+ this.castle = this.lo = this.hi = this.mg = this.eg = this.phase = this.sp = 0;
158
+ this.ep = -1;
159
+ for (let cell = 0; cell < 64; cell++) {
160
+ const p = setup.board[cell];
161
+ if (!p)
162
+ continue;
163
+ const sq = squareOf(cell);
164
+ this.put(sq, p);
165
+ if ((p & 7) === KING)
166
+ this.king[p >> 3] = sq;
167
+ }
168
+ this.side = setup.side;
169
+ for (let i = 0; i < 4; i++) {
170
+ const cell = setup.rooks[i];
171
+ if (cell < 0)
172
+ continue;
173
+ const sq = squareOf(cell);
174
+ this.castle |= 1 << i;
175
+ this.rookFrom[i] = sq;
176
+ this.mask[sq] &= ~(1 << i);
177
+ this.mask[this.king[i >> 1]] &= ~(3 << ((i >> 1) * 2));
178
+ }
179
+ this.half = setup.half;
180
+ this.full = setup.full;
181
+ // Only an en passant square a pawn could really take on counts: otherwise the same position
182
+ // would look different the move after a pawn's double step.
183
+ if (setup.ep >= 0) {
184
+ const sq = squareOf(setup.ep);
185
+ const from = sq + (this.side === WHITE ? -16 : 16);
186
+ const taker = PAWN | (this.side << 3);
187
+ if ((!((from - 1) & 0x88) && this.b[from - 1] === taker) || (!((from + 1) & 0x88) && this.b[from + 1] === taker))
188
+ this.ep = sq;
189
+ }
190
+ this.lo ^= Z_LO[Z_CASTLE + this.castle];
191
+ this.hi ^= Z_HI[Z_CASTLE + this.castle];
192
+ if (this.ep >= 0) {
193
+ this.lo ^= Z_LO[Z_EP + (this.ep & 7)];
194
+ this.hi ^= Z_HI[Z_EP + (this.ep & 7)];
195
+ }
196
+ if (this.side === BLACK) {
197
+ this.lo ^= Z_LO[Z_SIDE];
198
+ this.hi ^= Z_HI[Z_SIDE];
199
+ }
200
+ }
201
+ put(sq, p) {
202
+ this.b[sq] = p;
203
+ const i = p * 128 + sq;
204
+ this.lo ^= Z_LO[i];
205
+ this.hi ^= Z_HI[i];
206
+ this.mg += MG[i];
207
+ this.eg += EG[i];
208
+ this.phase += PHASE[p & 7];
209
+ }
210
+ take(sq) {
211
+ const p = this.b[sq];
212
+ this.b[sq] = 0;
213
+ const i = p * 128 + sq;
214
+ this.lo ^= Z_LO[i];
215
+ this.hi ^= Z_HI[i];
216
+ this.mg -= MG[i];
217
+ this.eg -= EG[i];
218
+ this.phase -= PHASE[p & 7];
219
+ }
220
+ /** Whether `by` attacks the square. */
221
+ attacked(sq, by) {
222
+ const b = this.b;
223
+ const own = by << 3;
224
+ // A white pawn attacks the two squares diagonally above it.
225
+ const pawn = PAWN | own;
226
+ const back = by === WHITE ? -16 : 16;
227
+ let from = sq + back - 1;
228
+ if (!(from & 0x88) && b[from] === pawn)
229
+ return true;
230
+ from = sq + back + 1;
231
+ if (!(from & 0x88) && b[from] === pawn)
232
+ return true;
233
+ const knight = KNIGHT | own;
234
+ const king = KING | own;
235
+ for (let i = 0; i < 8; i++) {
236
+ from = sq + KNIGHT_STEPS[i];
237
+ if (!(from & 0x88) && b[from] === knight)
238
+ return true;
239
+ from = sq + KING_STEPS[i];
240
+ if (!(from & 0x88) && b[from] === king)
241
+ return true;
242
+ }
243
+ const bishop = BISHOP | own;
244
+ const rook = ROOK | own;
245
+ const queen = QUEEN | own;
246
+ for (let i = 0; i < 4; i++) {
247
+ let step = BISHOP_STEPS[i];
248
+ from = sq + step;
249
+ while (!(from & 0x88)) {
250
+ const p = b[from];
251
+ if (p) {
252
+ if (p === bishop || p === queen)
253
+ return true;
254
+ break;
255
+ }
256
+ from += step;
257
+ }
258
+ step = ROOK_STEPS[i];
259
+ from = sq + step;
260
+ while (!(from & 0x88)) {
261
+ const p = b[from];
262
+ if (p) {
263
+ if (p === rook || p === queen)
264
+ return true;
265
+ break;
266
+ }
267
+ from += step;
268
+ }
269
+ }
270
+ return false;
271
+ }
272
+ inCheck() {
273
+ return this.attacked(this.king[this.side], this.side ^ 1);
274
+ }
275
+ /**
276
+ * Every move the side to move could make if its own king's safety didn't matter (`make` refuses
277
+ * the ones that leave it in check), written into `out` from `at`. Returns where the list ends.
278
+ * With `loud`, only captures and promotions to a queen: what a search looks at before it trusts
279
+ * a position to be quiet.
280
+ */
281
+ gen(out, at, loud = false) {
282
+ const b = this.b;
283
+ const us = this.side;
284
+ const them = us ^ 1;
285
+ const up = us === WHITE ? 16 : -16;
286
+ const startRank = us === WHITE ? 1 : 6;
287
+ const lastRank = us === WHITE ? 7 : 0;
288
+ for (let rank = 0; rank < 8; rank++) {
289
+ for (let sq = rank << 4, end = sq + 8; sq < end; sq++) {
290
+ const p = b[sq];
291
+ if (!p || p >> 3 !== us)
292
+ continue;
293
+ const kind = p & 7;
294
+ if (kind === PAWN) {
295
+ const one = sq + up;
296
+ const promotes = one >> 4 === lastRank;
297
+ if (!b[one]) {
298
+ if (promotes) {
299
+ out[at++] = mv(sq, one, QUEEN);
300
+ if (!loud) {
301
+ out[at++] = mv(sq, one, KNIGHT);
302
+ out[at++] = mv(sq, one, ROOK);
303
+ out[at++] = mv(sq, one, BISHOP);
304
+ }
305
+ }
306
+ else if (!loud) {
307
+ out[at++] = mv(sq, one);
308
+ if (rank === startRank && !b[one + up])
309
+ out[at++] = mv(sq, one + up, 0, FLAG_DOUBLE);
310
+ }
311
+ }
312
+ for (let side = -1; side <= 1; side += 2) {
313
+ const to = one + side;
314
+ if (to & 0x88)
315
+ continue;
316
+ const target = b[to];
317
+ if (target && target >> 3 === them) {
318
+ if (promotes) {
319
+ out[at++] = mv(sq, to, QUEEN);
320
+ if (!loud) {
321
+ out[at++] = mv(sq, to, KNIGHT);
322
+ out[at++] = mv(sq, to, ROOK);
323
+ out[at++] = mv(sq, to, BISHOP);
324
+ }
325
+ }
326
+ else
327
+ out[at++] = mv(sq, to);
328
+ }
329
+ else if (to === this.ep)
330
+ out[at++] = mv(sq, to, 0, FLAG_EP);
331
+ }
332
+ }
333
+ else if (kind === KNIGHT || kind === KING) {
334
+ const steps = kind === KNIGHT ? KNIGHT_STEPS : KING_STEPS;
335
+ for (let i = 0; i < 8; i++) {
336
+ const to = sq + steps[i];
337
+ if (to & 0x88)
338
+ continue;
339
+ const target = b[to];
340
+ if (!target ? !loud : target >> 3 === them)
341
+ out[at++] = mv(sq, to);
342
+ }
343
+ }
344
+ else {
345
+ for (let i = 0; i < 4; i++) {
346
+ for (let set = 0; set < 2; set++) {
347
+ if (set === 0 ? kind === ROOK : kind === BISHOP)
348
+ continue;
349
+ const step = set === 0 ? BISHOP_STEPS[i] : ROOK_STEPS[i];
350
+ let to = sq + step;
351
+ while (!(to & 0x88)) {
352
+ const target = b[to];
353
+ if (target) {
354
+ if (target >> 3 === them)
355
+ out[at++] = mv(sq, to);
356
+ break;
357
+ }
358
+ if (!loud)
359
+ out[at++] = mv(sq, to);
360
+ to += step;
361
+ }
362
+ }
363
+ }
364
+ }
365
+ }
366
+ }
367
+ if (loud || !(this.castle & (3 << (us * 2))))
368
+ return at;
369
+ // Castling, written for any starting row (Chess960): the king ends on the g- or c-file and the
370
+ // rook on the f- or d-file, wherever they began. Every square either of them crosses or lands
371
+ // on has to be empty but for the two of them, and the king may not start on, cross or land on
372
+ // an attacked square. (A king left in check once the rook has moved away is caught by `make`.)
373
+ const home = us === WHITE ? 0 : 112;
374
+ const kf = this.king[us];
375
+ for (let k = 0; k < 2; k++) {
376
+ const right = us * 2 + k;
377
+ if (!(this.castle & (1 << right)))
378
+ continue;
379
+ const rf = this.rookFrom[right];
380
+ const kt = home + (k === 0 ? 6 : 2);
381
+ const rt = home + (k === 0 ? 5 : 3);
382
+ let ok = true;
383
+ for (let sq = Math.min(kf, rf, kt, rt), end = Math.max(kf, rf, kt, rt); sq <= end && ok; sq++)
384
+ if (sq !== kf && sq !== rf && b[sq])
385
+ ok = false;
386
+ const step = kt > kf ? 1 : -1;
387
+ for (let sq = kf; ok; sq += step) {
388
+ if (this.attacked(sq, them))
389
+ ok = false;
390
+ if (sq === kt)
391
+ break;
392
+ }
393
+ if (ok)
394
+ out[at++] = mv(kf, kt, 0, FLAG_CASTLE_K + k);
395
+ }
396
+ return at;
397
+ }
398
+ /** Make a move from `gen`. False, with nothing changed, if it would leave the mover's king in check. */
399
+ make(m) {
400
+ const b = this.b;
401
+ const from = m & 127;
402
+ const to = (m >> 7) & 127;
403
+ const promo = (m >> 14) & 7;
404
+ const flag = (m >> 17) & 7;
405
+ const us = this.side;
406
+ const them = us ^ 1;
407
+ const piece = b[from];
408
+ const sp = this.sp++;
409
+ this.uCastle[sp] = this.castle;
410
+ this.uEp[sp] = this.ep;
411
+ this.uHalf[sp] = this.half;
412
+ this.uLo[sp] = this.lo;
413
+ this.uHi[sp] = this.hi;
414
+ this.uMg[sp] = this.mg;
415
+ this.uEg[sp] = this.eg;
416
+ this.uPhase[sp] = this.phase;
417
+ let captured = 0;
418
+ let key = Z_CASTLE + this.castle;
419
+ this.lo ^= Z_LO[key];
420
+ this.hi ^= Z_HI[key];
421
+ if (this.ep >= 0) {
422
+ key = Z_EP + (this.ep & 7);
423
+ this.lo ^= Z_LO[key];
424
+ this.hi ^= Z_HI[key];
425
+ }
426
+ if (flag === FLAG_CASTLE_K || flag === FLAG_CASTLE_Q) {
427
+ const rf = this.rookFrom[us * 2 + flag - FLAG_CASTLE_K];
428
+ const rook = b[rf];
429
+ const rt = (to & 0x70) + (flag === FLAG_CASTLE_K ? 5 : 3);
430
+ // Both off, then both on: either may be landing where the other stood.
431
+ this.take(from);
432
+ this.take(rf);
433
+ this.put(to, piece);
434
+ this.put(rt, rook);
435
+ this.king[us] = to;
436
+ }
437
+ else {
438
+ if (flag === FLAG_EP) {
439
+ const at = to + (us === WHITE ? -16 : 16);
440
+ captured = b[at];
441
+ this.take(at);
442
+ }
443
+ else if (b[to]) {
444
+ captured = b[to];
445
+ this.take(to);
446
+ }
447
+ this.take(from);
448
+ this.put(to, promo ? promo | (us << 3) : piece);
449
+ if ((piece & 7) === KING)
450
+ this.king[us] = to;
451
+ }
452
+ this.uCap[sp] = captured;
453
+ this.half = (piece & 7) === PAWN || captured ? 0 : this.half + 1;
454
+ this.ep = -1;
455
+ if (flag === FLAG_DOUBLE) {
456
+ const taker = PAWN | (them << 3);
457
+ if ((!((to - 1) & 0x88) && b[to - 1] === taker) || (!((to + 1) & 0x88) && b[to + 1] === taker))
458
+ this.ep = (from + to) >> 1;
459
+ }
460
+ this.castle &= this.mask[from] & this.mask[to];
461
+ key = Z_CASTLE + this.castle;
462
+ this.lo ^= Z_LO[key] ^ Z_LO[Z_SIDE];
463
+ this.hi ^= Z_HI[key] ^ Z_HI[Z_SIDE];
464
+ if (this.ep >= 0) {
465
+ key = Z_EP + (this.ep & 7);
466
+ this.lo ^= Z_LO[key];
467
+ this.hi ^= Z_HI[key];
468
+ }
469
+ this.side = them;
470
+ if (us === BLACK)
471
+ this.full++;
472
+ if (this.attacked(this.king[us], them)) {
473
+ this.unmake(m);
474
+ return false;
475
+ }
476
+ return true;
477
+ }
478
+ unmake(m) {
479
+ const b = this.b;
480
+ const from = m & 127;
481
+ const to = (m >> 7) & 127;
482
+ const flag = (m >> 17) & 7;
483
+ const us = this.side ^ 1;
484
+ const sp = --this.sp;
485
+ this.side = us;
486
+ if (us === BLACK)
487
+ this.full--;
488
+ this.castle = this.uCastle[sp];
489
+ this.ep = this.uEp[sp];
490
+ this.half = this.uHalf[sp];
491
+ this.lo = this.uLo[sp];
492
+ this.hi = this.uHi[sp];
493
+ this.mg = this.uMg[sp];
494
+ this.eg = this.uEg[sp];
495
+ this.phase = this.uPhase[sp];
496
+ if (flag === FLAG_CASTLE_K || flag === FLAG_CASTLE_Q) {
497
+ const rf = this.rookFrom[us * 2 + flag - FLAG_CASTLE_K];
498
+ const rt = (to & 0x70) + (flag === FLAG_CASTLE_K ? 5 : 3);
499
+ const king = b[to];
500
+ const rook = b[rt];
501
+ b[to] = 0;
502
+ b[rt] = 0;
503
+ b[from] = king;
504
+ b[rf] = rook;
505
+ this.king[us] = from;
506
+ return;
507
+ }
508
+ const piece = (m >> 14) & 7 ? PAWN | (us << 3) : b[to];
509
+ const captured = this.uCap[sp];
510
+ b[from] = piece;
511
+ if (flag === FLAG_EP) {
512
+ b[to] = 0;
513
+ b[to + (us === WHITE ? -16 : 16)] = captured;
514
+ }
515
+ else
516
+ b[to] = captured;
517
+ if ((piece & 7) === KING)
518
+ this.king[us] = from;
519
+ }
520
+ /** Pass the turn: for a search asking "what if I did nothing?". Never when in check. */
521
+ makeNull() {
522
+ const sp = this.sp++;
523
+ this.uEp[sp] = this.ep;
524
+ this.uLo[sp] = this.lo;
525
+ this.uHi[sp] = this.hi;
526
+ if (this.ep >= 0) {
527
+ const key = Z_EP + (this.ep & 7);
528
+ this.lo ^= Z_LO[key];
529
+ this.hi ^= Z_HI[key];
530
+ }
531
+ this.lo ^= Z_LO[Z_SIDE];
532
+ this.hi ^= Z_HI[Z_SIDE];
533
+ this.ep = -1;
534
+ this.side ^= 1;
535
+ }
536
+ unmakeNull() {
537
+ const sp = --this.sp;
538
+ this.side ^= 1;
539
+ this.ep = this.uEp[sp];
540
+ this.lo = this.uLo[sp];
541
+ this.hi = this.uHi[sp];
542
+ }
543
+ /** Every legal move. */
544
+ legal() {
545
+ const end = this.gen(SCRATCH, 0);
546
+ const out = [];
547
+ for (let i = 0; i < end; i++) {
548
+ const m = SCRATCH[i];
549
+ if (this.make(m)) {
550
+ this.unmake(m);
551
+ out.push(m);
552
+ }
553
+ }
554
+ return out;
555
+ }
556
+ /** Whether the side to move has any legal move at all. */
557
+ canMove() {
558
+ const end = this.gen(SCRATCH, 0);
559
+ for (let i = 0; i < end; i++) {
560
+ const m = SCRATCH[i];
561
+ if (this.make(m)) {
562
+ this.unmake(m);
563
+ return true;
564
+ }
565
+ }
566
+ return false;
567
+ }
568
+ /**
569
+ * Nobody can ever be mated with what is left: bare kings, a king and one knight or bishop
570
+ * against a king, or kings and bishops that all stand on squares of one colour.
571
+ */
572
+ dead() {
573
+ const b = this.b;
574
+ let knights = 0;
575
+ let light = 0;
576
+ let dark = 0;
577
+ for (let rank = 0; rank < 8; rank++) {
578
+ for (let sq = rank << 4, end = sq + 8; sq < end; sq++) {
579
+ const kind = b[sq] & 7;
580
+ if (!kind || kind === KING)
581
+ continue;
582
+ if (kind === KNIGHT)
583
+ knights++;
584
+ else if (kind === BISHOP) {
585
+ if ((rank + sq) & 1)
586
+ light++;
587
+ else
588
+ dark++;
589
+ }
590
+ else
591
+ return false;
592
+ }
593
+ }
594
+ if (knights + light + dark <= 1)
595
+ return true;
596
+ return knights === 0 && (light === 0 || dark === 0);
597
+ }
598
+ }
599
+ /** A position's key (see `Position.lo`, `hi`) without setting the position up: for the positions a game has already been through. */
600
+ export function positionKey(setup) {
601
+ const pos = KEYED;
602
+ pos.reset(setup);
603
+ return { lo: pos.lo, hi: pos.hi };
604
+ }
605
+ const SCRATCH = new Int32Array(256);
606
+ const MAX_PLY = 96;
607
+ const KEYED = new Position({ board: [], side: 0, rooks: [], ep: -1, half: 0, full: 1 });
608
+ const MOVES = new Int32Array((MAX_PLY + 2) * 256);
609
+ const ORDER = new Int32Array((MAX_PLY + 2) * 256);
610
+ /** How many positions are exactly `depth` moves from here: the standard test of move generation. */
611
+ export function perft(pos, depth, ply = 0) {
612
+ const start = ply * 256;
613
+ const end = pos.gen(MOVES, start);
614
+ let count = 0;
615
+ for (let i = start; i < end; i++) {
616
+ const m = MOVES[i];
617
+ if (!pos.make(m))
618
+ continue;
619
+ count += depth <= 1 ? 1 : perft(pos, depth - 1, ply + 1);
620
+ pos.unmake(m);
621
+ }
622
+ return count;
623
+ }
624
+ // ---- Judging a position -------------------------------------------------------------------------
625
+ // Material and where it stands (kept up to date as moves are made), then a look over the board for
626
+ // what that misses: pawns that are doubled, alone or past the other side's pawns; how many squares
627
+ // the pieces have; a pair of bishops; rooks on open files; and pawns still in front of a king
628
+ // while there are enough pieces about to hurt it.
629
+ const PASSED = [0, 4, 8, 16, 30, 52, 85, 0];
630
+ const pawnCount = [new Int8Array(10), new Int8Array(10)];
631
+ /** White's rearmost pawn on each file (8 for none), Black's rearmost from its own side (−1 for none). */
632
+ const whiteBack = new Int8Array(10);
633
+ const blackBack = new Int8Array(10);
634
+ /** White's most advanced pawn on each file (−1 for none) and Black's (8 for none). */
635
+ const whiteFront = new Int8Array(10);
636
+ const blackFront = new Int8Array(10);
637
+ /** How good the position is for the side to move, in hundredths of a pawn. */
638
+ export function evaluate(pos) {
639
+ const b = pos.b;
640
+ for (let f = 0; f < 10; f++) {
641
+ pawnCount[0][f] = 0;
642
+ pawnCount[1][f] = 0;
643
+ whiteBack[f] = 8;
644
+ whiteFront[f] = -1;
645
+ blackBack[f] = -1;
646
+ blackFront[f] = 8;
647
+ }
648
+ for (let rank = 1; rank < 7; rank++) {
649
+ for (let f = 0; f < 8; f++) {
650
+ const p = b[(rank << 4) + f];
651
+ if (p === PAWN) {
652
+ pawnCount[0][f + 1]++;
653
+ if (rank < whiteBack[f + 1])
654
+ whiteBack[f + 1] = rank;
655
+ if (rank > whiteFront[f + 1])
656
+ whiteFront[f + 1] = rank;
657
+ }
658
+ else if (p === (PAWN | 8)) {
659
+ pawnCount[1][f + 1]++;
660
+ if (rank > blackBack[f + 1])
661
+ blackBack[f + 1] = rank;
662
+ if (rank < blackFront[f + 1])
663
+ blackFront[f + 1] = rank;
664
+ }
665
+ }
666
+ }
667
+ let mg = pos.mg;
668
+ let eg = pos.eg;
669
+ const bishops = [0, 0];
670
+ for (let rank = 0; rank < 8; rank++) {
671
+ for (let f = 0; f < 8; f++) {
672
+ const sq = (rank << 4) + f;
673
+ const p = b[sq];
674
+ if (!p)
675
+ continue;
676
+ const side = p >> 3;
677
+ const sign = side === WHITE ? 1 : -1;
678
+ const kind = p & 7;
679
+ const file = f + 1;
680
+ if (kind === PAWN) {
681
+ const mine = pawnCount[side];
682
+ let m = 0;
683
+ let e = 0;
684
+ if (mine[file] > 1) {
685
+ m -= 8;
686
+ e -= 14;
687
+ }
688
+ if (!mine[file - 1] && !mine[file + 1]) {
689
+ m -= 10;
690
+ e -= 12;
691
+ }
692
+ // Passed: no pawn of theirs ahead of it on its own file or the ones beside it.
693
+ const passed = side === WHITE ? blackBack[file - 1] <= rank && blackBack[file] <= rank && blackBack[file + 1] <= rank : whiteBack[file - 1] >= rank && whiteBack[file] >= rank && whiteBack[file + 1] >= rank;
694
+ if (passed) {
695
+ const gone = side === WHITE ? rank : 7 - rank;
696
+ m += PASSED[gone] >> 1;
697
+ e += PASSED[gone];
698
+ }
699
+ mg += sign * m;
700
+ eg += sign * e;
701
+ }
702
+ else if (kind === KING) {
703
+ // Pawns in front of a king at home, and files beside it with none of its own.
704
+ const home = side === WHITE ? rank <= 1 : rank >= 6;
705
+ if (!home)
706
+ continue;
707
+ const ahead = side === WHITE ? 16 : -16;
708
+ let shield = 0;
709
+ const pawn = PAWN | (side << 3);
710
+ for (let df = -1; df <= 1; df++) {
711
+ if (f + df < 0 || f + df > 7)
712
+ continue;
713
+ const one = sq + ahead + df;
714
+ if (b[one] === pawn)
715
+ shield += 12;
716
+ else if (!((one + ahead) & 0x88) && b[one + ahead] === pawn)
717
+ shield += 6;
718
+ if (!pawnCount[side][file + df])
719
+ shield -= 14;
720
+ }
721
+ mg += sign * shield;
722
+ }
723
+ else {
724
+ // Squares it can go to, counting captures: a piece with nowhere to go isn't doing much.
725
+ let reach = 0;
726
+ if (kind === KNIGHT) {
727
+ for (let i = 0; i < 8; i++) {
728
+ const to = sq + KNIGHT_STEPS[i];
729
+ if (!(to & 0x88) && (!b[to] || b[to] >> 3 !== side))
730
+ reach++;
731
+ }
732
+ mg += sign * (reach - 4) * 4;
733
+ eg += sign * (reach - 4) * 4;
734
+ continue;
735
+ }
736
+ for (let i = 0; i < 4; i++) {
737
+ for (let set = 0; set < 2; set++) {
738
+ if (set === 0 ? kind === ROOK : kind === BISHOP)
739
+ continue;
740
+ const step = set === 0 ? BISHOP_STEPS[i] : ROOK_STEPS[i];
741
+ let to = sq + step;
742
+ while (!(to & 0x88)) {
743
+ const target = b[to];
744
+ if (target) {
745
+ if (target >> 3 !== side)
746
+ reach++;
747
+ break;
748
+ }
749
+ reach++;
750
+ to += step;
751
+ }
752
+ }
753
+ }
754
+ if (kind === BISHOP) {
755
+ bishops[side]++;
756
+ mg += sign * (reach - 6) * 4;
757
+ eg += sign * (reach - 6) * 4;
758
+ }
759
+ else if (kind === ROOK) {
760
+ mg += sign * (reach - 7) * 2;
761
+ eg += sign * (reach - 7) * 3;
762
+ if (!pawnCount[side][file]) {
763
+ const open = pawnCount[side ^ 1][file] ? 8 : 16;
764
+ mg += sign * open;
765
+ eg += sign * (open >> 1);
766
+ }
767
+ }
768
+ else {
769
+ mg += sign * (reach - 13);
770
+ eg += sign * (reach - 13) * 2;
771
+ }
772
+ }
773
+ }
774
+ }
775
+ if (bishops[0] >= 2) {
776
+ mg += 28;
777
+ eg += 40;
778
+ }
779
+ if (bishops[1] >= 2) {
780
+ mg -= 28;
781
+ eg -= 40;
782
+ }
783
+ const phase = pos.phase > 24 ? 24 : pos.phase;
784
+ const white = ((mg * phase + eg * (24 - phase)) / 24) | 0;
785
+ return (pos.side === WHITE ? white : -white) + 8;
786
+ }
787
+ // ---- The search ---------------------------------------------------------------------------------
788
+ // Look ahead by iterative deepening: a full search one move deep, then two, and so on, each one
789
+ // using what the last found to try the best moves first, until the work allowed or the time
790
+ // allowed is used up; the move played is the best of the last depth that finished (or of the part
791
+ // of the next that had already beaten it). Inside it is alpha-beta with the usual helpers:
792
+ //
793
+ // - a transposition table, so a position reached by another order of moves isn't searched twice;
794
+ // - move ordering: the table's move, captures (biggest victim by smallest attacker), two "killer"
795
+ // quiet moves a ply, then quiet moves by how often they have cut a search short;
796
+ // - a quiescence search, so a position is never judged in the middle of an exchange;
797
+ // - a check is looked at one move deeper, and positions that repeat are a draw;
798
+ // - a null move (if doing nothing is still too good, don't look further) and late quiet moves
799
+ // searched less deeply.
800
+ export const MATE = 30_000;
801
+ const TT_BITS = 18;
802
+ const TT_SIZE = 1 << TT_BITS;
803
+ const ttHi = new Int32Array(TT_SIZE);
804
+ const ttMove = new Int32Array(TT_SIZE);
805
+ const ttScore = new Int16Array(TT_SIZE);
806
+ const ttDepth = new Int8Array(TT_SIZE);
807
+ const ttFlag = new Uint8Array(TT_SIZE);
808
+ /** Which search wrote an entry: anything older is as good as empty, so no search hears from the one before. */
809
+ const ttGen = new Uint16Array(TT_SIZE);
810
+ let gen = 0;
811
+ const EXACT = 1;
812
+ const LOWER = 2;
813
+ const UPPER = 3;
814
+ const killers = new Int32Array((MAX_PLY + 2) * 2);
815
+ const history = new Int32Array(2 * 128 * 128);
816
+ const pathLo = new Int32Array(MAX_PLY + 4);
817
+ const pathHi = new Int32Array(MAX_PLY + 4);
818
+ let nodes = 0;
819
+ let nodeCap = Infinity;
820
+ let stopAt = Infinity;
821
+ let stopped = false;
822
+ let timedOut = false;
823
+ let seenLo = [];
824
+ let seenHi = [];
825
+ const now = () => performance.now();
826
+ /** Count a position, and say whether the search has to stop. The clock is only looked at now and then. */
827
+ function spend() {
828
+ if (stopped)
829
+ return true;
830
+ nodes++;
831
+ if (nodes > nodeCap)
832
+ return (stopped = true);
833
+ if ((nodes & 127) === 0 && stopAt !== Infinity && now() >= stopAt) {
834
+ timedOut = true;
835
+ return (stopped = true);
836
+ }
837
+ return false;
838
+ }
839
+ /** Has this position come up before, in the game or on the way here? */
840
+ function repeated(pos, ply) {
841
+ if (pos.half < 4)
842
+ return false;
843
+ const lo = pos.lo;
844
+ const hi = pos.hi;
845
+ // Only positions since the last capture or pawn move can come round again.
846
+ for (let i = ply - 2; i >= 0 && ply - i <= pos.half; i -= 2)
847
+ if (pathLo[i] === lo && pathHi[i] === hi)
848
+ return true;
849
+ if (pos.half >= ply)
850
+ for (let i = 0; i < seenLo.length; i++)
851
+ if (seenLo[i] === lo && seenHi[i] === hi)
852
+ return true;
853
+ return false;
854
+ }
855
+ /** Put the move worth most at `i`, so moves are tried best first without sorting the ones never reached. */
856
+ function pick(i, end) {
857
+ let best = i;
858
+ for (let j = i + 1; j < end; j++)
859
+ if (ORDER[j] > ORDER[best])
860
+ best = j;
861
+ if (best !== i) {
862
+ const m = MOVES[i];
863
+ MOVES[i] = MOVES[best];
864
+ MOVES[best] = m;
865
+ const o = ORDER[i];
866
+ ORDER[i] = ORDER[best];
867
+ ORDER[best] = o;
868
+ }
869
+ }
870
+ function quiesce(pos, alpha, beta, ply) {
871
+ if (spend())
872
+ return 0;
873
+ const stand = evaluate(pos);
874
+ if (ply >= MAX_PLY)
875
+ return stand;
876
+ if (stand >= beta)
877
+ return stand;
878
+ if (stand > alpha)
879
+ alpha = stand;
880
+ const b = pos.b;
881
+ const start = ply * 256;
882
+ const end = pos.gen(MOVES, start, true);
883
+ for (let i = start; i < end; i++) {
884
+ const m = MOVES[i];
885
+ const victim = b[(m >> 7) & 127] & 7;
886
+ ORDER[i] = (victim || PAWN) * 16 - (b[m & 127] & 7) + ((m >> 14) & 7 ? 80 : 0);
887
+ }
888
+ let best = stand;
889
+ for (let i = start; i < end; i++) {
890
+ pick(i, end);
891
+ const m = MOVES[i];
892
+ // A capture that couldn't bring the score up to what is already on offer isn't worth the look.
893
+ const gain = WORTH[b[(m >> 7) & 127] & 7] + ((m >> 14) & 7 ? 800 : 0) + (m >> 17 === FLAG_EP ? 100 : 0);
894
+ if (stand + gain + 200 < alpha && pos.phase > 4)
895
+ continue;
896
+ if (!pos.make(m))
897
+ continue;
898
+ const value = -quiesce(pos, -beta, -alpha, ply + 1);
899
+ pos.unmake(m);
900
+ if (stopped)
901
+ return 0;
902
+ if (value > best) {
903
+ best = value;
904
+ if (value > alpha) {
905
+ alpha = value;
906
+ if (alpha >= beta)
907
+ break;
908
+ }
909
+ }
910
+ }
911
+ return best;
912
+ }
913
+ function search(pos, depth, alpha, beta, ply, nullOk) {
914
+ pathLo[ply] = pos.lo;
915
+ pathHi[ply] = pos.hi;
916
+ // A draw by the fifty-move rule or by coming back to a position. (Bare kings and the like are
917
+ // caught by the evaluation staying level.)
918
+ if (pos.half >= 100 || repeated(pos, ply))
919
+ return 0;
920
+ const checked = pos.inCheck();
921
+ if (checked)
922
+ depth++;
923
+ if (depth <= 0)
924
+ return quiesce(pos, alpha, beta, ply);
925
+ if (spend())
926
+ return 0;
927
+ if (ply >= MAX_PLY)
928
+ return evaluate(pos);
929
+ const slot = pos.lo & (TT_SIZE - 1);
930
+ let first = 0;
931
+ if (ttGen[slot] === gen && ttHi[slot] === pos.hi) {
932
+ first = ttMove[slot];
933
+ if (ttDepth[slot] >= depth) {
934
+ let score = ttScore[slot];
935
+ // A mate is so many moves from here, not from where it was first found.
936
+ if (score > MATE - 200)
937
+ score -= ply;
938
+ else if (score < -MATE + 200)
939
+ score += ply;
940
+ const flag = ttFlag[slot];
941
+ if (flag === EXACT || (flag === LOWER && score >= beta) || (flag === UPPER && score <= alpha))
942
+ return score;
943
+ }
944
+ }
945
+ const us = pos.side;
946
+ // If passing would still be too good for the other side to allow, so is moving. Not with only
947
+ // pawns left, where having to move can be the whole problem.
948
+ if (nullOk && !checked && depth >= 3 && beta < MATE - 200 && pos.phase > 2) {
949
+ const stand = evaluate(pos);
950
+ if (stand >= beta) {
951
+ pos.makeNull();
952
+ const value = -search(pos, depth - 3, -beta, -beta + 1, ply + 1, false);
953
+ pos.unmakeNull();
954
+ if (stopped)
955
+ return 0;
956
+ if (value >= beta)
957
+ return value > MATE - 200 ? beta : value;
958
+ }
959
+ }
960
+ const b = pos.b;
961
+ const start = ply * 256;
962
+ const end = pos.gen(MOVES, start);
963
+ const k1 = killers[ply * 2];
964
+ const k2 = killers[ply * 2 + 1];
965
+ for (let i = start; i < end; i++) {
966
+ const m = MOVES[i];
967
+ const victim = b[(m >> 7) & 127] & 7;
968
+ const promo = (m >> 14) & 7;
969
+ if (m === first)
970
+ ORDER[i] = 2_000_000_000;
971
+ else if (victim || promo === QUEEN || (m >> 17) === FLAG_EP)
972
+ ORDER[i] = 1_000_000_000 + (victim || PAWN) * 16 - (b[m & 127] & 7) + promo * 100;
973
+ else if (m === k1)
974
+ ORDER[i] = 900_000_000;
975
+ else if (m === k2)
976
+ ORDER[i] = 800_000_000;
977
+ else
978
+ ORDER[i] = history[(us << 14) | ((m & 127) << 7) | ((m >> 7) & 127)];
979
+ }
980
+ const alpha0 = alpha;
981
+ let best = -Infinity;
982
+ let bestMove = 0;
983
+ let tried = 0;
984
+ for (let i = start; i < end; i++) {
985
+ pick(i, end);
986
+ const m = MOVES[i];
987
+ const quiet = !(b[(m >> 7) & 127] || (m >> 14) & 7 || (m >> 17) === FLAG_EP);
988
+ if (!pos.make(m))
989
+ continue;
990
+ tried++;
991
+ let value;
992
+ if (tried === 1)
993
+ value = -search(pos, depth - 1, -beta, -alpha, ply + 1, true);
994
+ else {
995
+ // Later quiet moves are unlikely to be the best: look at them less deeply first, with a
996
+ // window that only asks "better than what we have?", and properly only if they are.
997
+ const less = quiet && !checked && depth >= 3 && tried > 4 ? (tried > 12 && depth >= 6 ? 2 : 1) : 0;
998
+ value = -search(pos, depth - 1 - less, -alpha - 1, -alpha, ply + 1, true);
999
+ if (value > alpha && less && !stopped)
1000
+ value = -search(pos, depth - 1, -alpha - 1, -alpha, ply + 1, true);
1001
+ if (value > alpha && value < beta && !stopped)
1002
+ value = -search(pos, depth - 1, -beta, -alpha, ply + 1, true);
1003
+ }
1004
+ pos.unmake(m);
1005
+ if (stopped)
1006
+ return 0;
1007
+ if (value > best) {
1008
+ best = value;
1009
+ bestMove = m;
1010
+ if (value > alpha) {
1011
+ alpha = value;
1012
+ if (alpha >= beta) {
1013
+ if (quiet) {
1014
+ if (killers[ply * 2] !== m) {
1015
+ killers[ply * 2 + 1] = killers[ply * 2];
1016
+ killers[ply * 2] = m;
1017
+ }
1018
+ const h = (us << 14) | ((m & 127) << 7) | ((m >> 7) & 127);
1019
+ history[h] = Math.min(history[h] + depth * depth, 700_000_000);
1020
+ }
1021
+ break;
1022
+ }
1023
+ }
1024
+ }
1025
+ }
1026
+ // No move at all: mated (the sooner the worse), or stalemated.
1027
+ if (!tried)
1028
+ return checked ? -MATE + ply : 0;
1029
+ ttGen[slot] = gen;
1030
+ ttHi[slot] = pos.hi;
1031
+ ttMove[slot] = bestMove;
1032
+ ttDepth[slot] = depth;
1033
+ ttScore[slot] = best > MATE - 200 ? best + ply : best < -MATE + 200 ? best - ply : best;
1034
+ ttFlag[slot] = best <= alpha0 ? UPPER : best >= beta ? LOWER : EXACT;
1035
+ return best;
1036
+ }
1037
+ /**
1038
+ * The best move for the side to move, within the limits given. With one legal move it is played
1039
+ * without a thought. The position is left as it was found.
1040
+ */
1041
+ export function think(pos, limits) {
1042
+ const began = now();
1043
+ const moves = pos.legal();
1044
+ if (moves.length < 2)
1045
+ return { move: moves[0] ?? 0, score: 0, depth: 0, nodes: 0, timedOut: false };
1046
+ nodes = 0;
1047
+ nodeCap = limits.nodes;
1048
+ stopAt = limits.ms === Infinity ? Infinity : began + limits.ms;
1049
+ stopped = false;
1050
+ timedOut = false;
1051
+ seenLo = (limits.seen ?? []).map((k) => k.lo);
1052
+ seenHi = (limits.seen ?? []).map((k) => k.hi);
1053
+ if (++gen > 65_000) {
1054
+ gen = 1;
1055
+ ttGen.fill(0);
1056
+ }
1057
+ killers.fill(0);
1058
+ history.fill(0);
1059
+ pathLo[0] = pos.lo;
1060
+ pathHi[0] = pos.hi;
1061
+ // Captures first, so that a search stopped before it has finished a single move still has a
1062
+ // sensible one to give.
1063
+ const b = pos.b;
1064
+ const options = moves
1065
+ .map((move) => ({ move, value: (b[moveTo(move)] & 7) * 16 - (b[moveFrom(move)] & 7), luck: Math.floor(limits.rand() * limits.luck) }))
1066
+ .sort((x, y) => y.value - x.value);
1067
+ let best = options[0];
1068
+ let score = 0;
1069
+ let reached = 0;
1070
+ for (let depth = 1; depth <= limits.depth && !stopped; depth++) {
1071
+ let top = -Infinity;
1072
+ let pickOf = options[0];
1073
+ let exact = 0;
1074
+ for (let i = 0; i < options.length; i++) {
1075
+ const o = options[i];
1076
+ pos.make(o.move);
1077
+ let value;
1078
+ if (i === 0)
1079
+ value = -search(pos, depth - 1, -MATE, MATE, 1, true);
1080
+ else {
1081
+ // A move only has to be scored exactly if, with its luck, it could come out on top.
1082
+ const need = top - o.luck;
1083
+ value = -search(pos, depth - 1, -need - 1, -need, 1, true);
1084
+ if (value > need && !stopped)
1085
+ value = -search(pos, depth - 1, -MATE, -need, 1, true);
1086
+ }
1087
+ pos.unmake(o.move);
1088
+ if (stopped)
1089
+ break;
1090
+ o.value = value + o.luck;
1091
+ if (o.value > top) {
1092
+ top = o.value;
1093
+ pickOf = o;
1094
+ exact = value;
1095
+ }
1096
+ }
1097
+ // Stopped part way: the moves looked at so far were looked at properly (last time's best
1098
+ // first), so the best of those stands. Stopped before the first was done, nothing is learned.
1099
+ if (stopped && top === -Infinity)
1100
+ break;
1101
+ best = pickOf;
1102
+ score = exact;
1103
+ if (stopped)
1104
+ break;
1105
+ reached = depth;
1106
+ // Best first next time round. Moves that failed low keep their order from before.
1107
+ const lead = options.indexOf(best);
1108
+ options.splice(lead, 1);
1109
+ options.unshift(best);
1110
+ // A forced mate found: looking deeper won't find a better move.
1111
+ if (score > MATE - 200 || score < -MATE + 200)
1112
+ break;
1113
+ }
1114
+ return { move: best.move, score, depth: reached, nodes, timedOut };
1115
+ }
1116
+ /** The value of a position from a search to a fixed depth with no luck in it: for tests and for judging who is ahead. */
1117
+ export function searchScore(pos, depth, cap = Infinity) {
1118
+ const result = think(pos, { depth, nodes: cap, ms: Infinity, luck: 0, rand: () => 0 });
1119
+ return result.move ? result.score : pos.inCheck() ? -MATE : 0;
1120
+ }
1121
+ //# sourceMappingURL=chesscore.js.map