Commit | Line | Data |
---|---|---|
723262f9 BA |
1 | import { ChessRules, Move, PiPo } from "@/base_rules"; |
2 | import { randInt } from "@/utils/alea"; | |
4f0025fb | 3 | import { ArrayFun } from "@/utils/array"; |
723262f9 | 4 | |
278a28a1 BA |
5 | export class Pandemonium2Rules extends ChessRules { |
6 | ||
7 | static get PawnSpecs() { | |
8 | return Object.assign( | |
9 | { }, | |
10 | ChessRules.PawnSpecs, | |
11 | { promotions: [V.GILDING] } | |
12 | ); | |
13 | } | |
723262f9 | 14 | |
e30523f2 | 15 | get loseOnRepetition() { |
809ab1a8 BA |
16 | // If current side is under check: lost |
17 | return this.underCheck(this.turn); | |
18 | } | |
19 | ||
723262f9 BA |
20 | static get GILDING() { |
21 | return "g"; | |
22 | } | |
23 | ||
24 | static get SCEPTER() { | |
25 | return "s"; | |
26 | } | |
27 | ||
28 | static get HORSE() { | |
29 | return "h"; | |
30 | } | |
31 | ||
32 | static get DRAGON() { | |
33 | return "d"; | |
34 | } | |
35 | ||
36 | static get CARDINAL() { | |
37 | return "c"; | |
38 | } | |
39 | ||
40 | static get WHOLE() { | |
41 | return "w"; | |
42 | } | |
43 | ||
44 | static get MARSHAL() { | |
45 | return "m"; | |
46 | } | |
47 | ||
48 | static get APRICOT() { | |
49 | return "a"; | |
50 | } | |
51 | ||
52 | static get PIECES() { | |
53 | return ( | |
54 | ChessRules.PIECES.concat([ | |
55 | V.GILDING, V.SCEPTER, V.HORSE, V.DRAGON, | |
56 | V.CARDINAL, V.WHOLE, V.MARSHAL, V.APRICOT]) | |
57 | ); | |
58 | } | |
59 | ||
60 | getPpath(b) { | |
61 | const prefix = (ChessRules.PIECES.includes(b[1]) ? "" : "Pandemonium/"); | |
62 | return prefix + b; | |
63 | } | |
64 | ||
65 | static get size() { | |
278a28a1 | 66 | return { x: 8, y: 10}; |
723262f9 BA |
67 | } |
68 | ||
69 | getColor(i, j) { | |
70 | if (i >= V.size.x) return i == V.size.x ? "w" : "b"; | |
71 | return this.board[i][j].charAt(0); | |
72 | } | |
73 | ||
74 | getPiece(i, j) { | |
75 | if (i >= V.size.x) return V.RESERVE_PIECES[j]; | |
76 | return this.board[i][j].charAt(1); | |
77 | } | |
78 | ||
79 | setOtherVariables(fen) { | |
80 | super.setOtherVariables(fen); | |
81 | // Sub-turn is useful only at first move... | |
82 | this.subTurn = 1; | |
83 | // Also init reserves (used by the interface to show landable pieces) | |
84 | const reserve = | |
85 | V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10)); | |
86 | this.reserve = { | |
87 | w: { | |
88 | [V.PAWN]: reserve[0], | |
89 | [V.ROOK]: reserve[1], | |
90 | [V.KNIGHT]: reserve[2], | |
91 | [V.BISHOP]: reserve[3], | |
92 | [V.QUEEN]: reserve[4], | |
93 | [V.CARDINAL]: reserve[5], | |
94 | [V.MARSHAL]: reserve[6], | |
95 | }, | |
96 | b: { | |
97 | [V.PAWN]: reserve[7], | |
98 | [V.ROOK]: reserve[8], | |
99 | [V.KNIGHT]: reserve[9], | |
100 | [V.BISHOP]: reserve[10], | |
101 | [V.QUEEN]: reserve[11], | |
102 | [V.CARDINAL]: reserve[12], | |
103 | [V.MARSHAL]: reserve[13] | |
104 | } | |
105 | }; | |
106 | } | |
107 | ||
723262f9 BA |
108 | static IsGoodFen(fen) { |
109 | if (!ChessRules.IsGoodFen(fen)) return false; | |
110 | const fenParsed = V.ParseFen(fen); | |
111 | // Check reserves | |
112 | if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{14,14}$/)) | |
113 | return false; | |
114 | return true; | |
115 | } | |
116 | ||
117 | static ParseFen(fen) { | |
118 | const fenParts = fen.split(" "); | |
119 | return Object.assign( | |
120 | ChessRules.ParseFen(fen), | |
121 | { reserve: fenParts[5] } | |
122 | ); | |
123 | } | |
124 | ||
125 | getFen() { | |
126 | return super.getFen() + " " + this.getReserveFen(); | |
127 | } | |
128 | ||
129 | getFenForRepeat() { | |
130 | return super.getFenForRepeat() + "_" + this.getReserveFen(); | |
131 | } | |
132 | ||
133 | getReserveFen() { | |
134 | let counts = new Array(14); | |
135 | for (let i = 0; i < V.RESERVE_PIECES.length; i++) { | |
136 | counts[i] = this.reserve["w"][V.RESERVE_PIECES[i]]; | |
137 | counts[7 + i] = this.reserve["b"][V.RESERVE_PIECES[i]]; | |
138 | } | |
139 | return counts.join(""); | |
140 | } | |
141 | ||
4313762d BA |
142 | static GenRandInitFen(options) { |
143 | if (options.randomness == 0) { | |
4f0025fb | 144 | return ( |
278a28a1 | 145 | "rnbqkmcbnr/pppppppppp/91/91/91/91/PPPPPPPPPP/RNBQKMCBNR " + |
4f0025fb BA |
146 | "w 0 ajaj - 00000000000000" |
147 | ); | |
148 | } | |
149 | ||
150 | let pieces = { w: new Array(10), b: new Array(10) }; | |
151 | let flags = ""; | |
152 | for (let c of ["w", "b"]) { | |
4313762d | 153 | if (c == 'b' && options.randomness == 1) { |
4f0025fb BA |
154 | pieces['b'] = pieces['w']; |
155 | flags += flags; | |
156 | break; | |
157 | } | |
158 | ||
159 | let positions = ArrayFun.range(10); | |
160 | ||
161 | // Get random squares for bishops (different colors) | |
162 | let randIndex = 2 * randInt(5); | |
163 | let bishop1Pos = positions[randIndex]; | |
164 | let randIndex_tmp = 2 * randInt(5) + 1; | |
165 | let bishop2Pos = positions[randIndex_tmp]; | |
166 | positions.splice(Math.max(randIndex, randIndex_tmp), 1); | |
167 | positions.splice(Math.min(randIndex, randIndex_tmp), 1); | |
168 | ||
169 | randIndex = randInt(8); | |
170 | let knight1Pos = positions[randIndex]; | |
171 | positions.splice(randIndex, 1); | |
172 | randIndex = randInt(7); | |
173 | let knight2Pos = positions[randIndex]; | |
174 | positions.splice(randIndex, 1); | |
175 | ||
176 | randIndex = randInt(6); | |
177 | let queenPos = positions[randIndex]; | |
178 | positions.splice(randIndex, 1); | |
179 | ||
180 | // Random squares for cardinal + marshal | |
181 | randIndex = randInt(5); | |
182 | let cardinalPos = positions[randIndex]; | |
183 | positions.splice(randIndex, 1); | |
184 | randIndex = randInt(4); | |
185 | let marshalPos = positions[randIndex]; | |
186 | positions.splice(randIndex, 1); | |
187 | ||
188 | let rook1Pos = positions[0]; | |
189 | let kingPos = positions[1]; | |
190 | let rook2Pos = positions[2]; | |
191 | ||
192 | pieces[c][rook1Pos] = "r"; | |
193 | pieces[c][knight1Pos] = "n"; | |
194 | pieces[c][bishop1Pos] = "b"; | |
195 | pieces[c][queenPos] = "q"; | |
196 | pieces[c][kingPos] = "k"; | |
197 | pieces[c][marshalPos] = "m"; | |
198 | pieces[c][cardinalPos] = "c"; | |
199 | pieces[c][bishop2Pos] = "b"; | |
200 | pieces[c][knight2Pos] = "n"; | |
201 | pieces[c][rook2Pos] = "r"; | |
202 | flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos); | |
203 | } | |
723262f9 | 204 | return ( |
4f0025fb BA |
205 | pieces["b"].join("") + |
206 | "/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/" + | |
207 | pieces["w"].join("").toUpperCase() + | |
847e5591 | 208 | " w 0 " + flags + " - 00000000000000" |
723262f9 | 209 | ); |
723262f9 BA |
210 | } |
211 | ||
723262f9 BA |
212 | getReservePpath(index, color) { |
213 | const p = V.RESERVE_PIECES[index]; | |
214 | const prefix = (ChessRules.PIECES.includes(p) ? "" : "Pandemonium/"); | |
215 | return prefix + color + p;; | |
216 | } | |
217 | ||
218 | // Ordering on reserve pieces | |
219 | static get RESERVE_PIECES() { | |
220 | return ( | |
221 | [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.CARDINAL, V.MARSHAL] | |
222 | ); | |
223 | } | |
224 | ||
225 | getReserveMoves([x, y]) { | |
226 | const color = this.turn; | |
822d71d6 | 227 | const oppCol = V.GetOppCol(color); |
723262f9 BA |
228 | const p = V.RESERVE_PIECES[y]; |
229 | if (this.reserve[color][p] == 0) return []; | |
230 | const bounds = (p == V.PAWN ? [1, V.size.x - 1] : [0, V.size.x]); | |
231 | let moves = []; | |
232 | for (let i = bounds[0]; i < bounds[1]; i++) { | |
233 | for (let j = 0; j < V.size.y; j++) { | |
234 | if (this.board[i][j] == V.EMPTY) { | |
235 | let mv = new Move({ | |
236 | appear: [ | |
237 | new PiPo({ | |
238 | x: i, | |
239 | y: j, | |
240 | c: color, | |
241 | p: p | |
242 | }) | |
243 | ], | |
244 | vanish: [], | |
245 | start: { x: x, y: y }, //a bit artificial... | |
246 | end: { x: i, y: j } | |
247 | }); | |
248 | if (p == V.PAWN) { | |
249 | // Do not drop on checkmate: | |
250 | this.play(mv); | |
251 | const res = ( | |
252 | this.underCheck(oppCol) && !this.atLeastOneMove("noReserve") | |
253 | ); | |
254 | this.undo(mv); | |
255 | if (res) continue; | |
256 | } | |
257 | moves.push(mv); | |
258 | } | |
259 | } | |
260 | } | |
261 | return moves; | |
262 | } | |
263 | ||
264 | static get PromoteMap() { | |
265 | return { | |
266 | r: 'd', | |
267 | n: 's', | |
268 | b: 'h', | |
269 | c: 'w', | |
270 | m: 'a' | |
271 | }; | |
272 | } | |
273 | ||
278a28a1 BA |
274 | applyPromotions(moves, promoted) { |
275 | const lastRank = (this.turn == 'w' ? 0 : V.size.x - 1); | |
276 | let promotions = []; | |
277 | moves.forEach(m => { | |
278 | if ([m.start.x, m.end.x].includes(lastRank)) { | |
279 | let pMove = JSON.parse(JSON.stringify(m)); | |
280 | pMove.appear[0].p = promoted; | |
281 | promotions.push(pMove); | |
282 | } | |
283 | }); | |
284 | Array.prototype.push.apply(moves, promotions); | |
285 | } | |
286 | ||
723262f9 BA |
287 | getPotentialMovesFrom([x, y]) { |
288 | const c = this.getColor(x, y); | |
289 | const oppCol = V.GetOppCol(c); | |
290 | if (this.movesCount <= 1) { | |
291 | if (this.kingPos[c][0] == x && this.kingPos[c][1] == y) { | |
292 | // Pass (if setup is ok) | |
293 | return [ | |
294 | new Move({ | |
295 | appear: [], | |
296 | vanish: [], | |
297 | start: { x: this.kingPos[c][0], y: this.kingPos[c][1] }, | |
298 | end: { x: this.kingPos[oppCol][0], y: this.kingPos[oppCol][1] } | |
299 | }) | |
300 | ]; | |
301 | } | |
278a28a1 | 302 | const firstRank = (this.movesCount == 0 ? V.size.x - 1 : 0); |
4f0025fb BA |
303 | if (x != firstRank || this.getPiece(x, y) != V.KNIGHT) return []; |
304 | // Swap with who? search for matching bishop: | |
305 | let knights = [], | |
306 | bishops = []; | |
278a28a1 | 307 | for (let i = 0; i < V.size.y; i++) { |
4f0025fb BA |
308 | const elt = this.board[x][i][1]; |
309 | if (elt == 'n') knights.push(i); | |
310 | else if (elt == 'b') bishops.push(i); | |
723262f9 | 311 | } |
4f0025fb BA |
312 | const destFile = (knights[0] == y ? bishops[0] : bishops[1]); |
313 | return [ | |
314 | new Move({ | |
315 | appear: [ | |
316 | new PiPo({ | |
317 | x: x, | |
318 | y: destFile, | |
319 | c: c, | |
320 | p: V.KNIGHT | |
321 | }), | |
322 | new PiPo({ | |
323 | x: x, | |
324 | y: y, | |
325 | c: c, | |
326 | p: V.BISHOP | |
327 | }) | |
328 | ], | |
329 | vanish: [ | |
330 | new PiPo({ | |
331 | x: x, | |
332 | y: y, | |
333 | c: c, | |
334 | p: V.KNIGHT | |
335 | }), | |
336 | new PiPo({ | |
337 | x: x, | |
338 | y: destFile, | |
339 | c: c, | |
340 | p: V.BISHOP | |
341 | }) | |
342 | ], | |
343 | start: { x: x, y: y }, | |
344 | end: { x: x, y: destFile } | |
345 | }) | |
346 | ]; | |
723262f9 BA |
347 | } |
348 | // Normal move (after initial setup) | |
e85bddc1 | 349 | if (x >= V.size.x) return this.getReserveMoves([x, y]); |
723262f9 BA |
350 | const p = this.getPiece(x, y); |
351 | const sq = [x, y]; | |
352 | let moves = []; | |
353 | if (ChessRules.PIECES.includes(p)) | |
354 | moves = super.getPotentialMovesFrom(sq); | |
355 | if ([V.GILDING, V.APRICOT, V.WHOLE].includes(p)) | |
356 | moves = super.getPotentialQueenMoves(sq); | |
357 | switch (p) { | |
358 | case V.SCEPTER: | |
359 | moves = this.getPotentialScepterMoves(sq); | |
360 | break; | |
361 | case V.HORSE: | |
362 | moves = this.getPotentialHorseMoves(sq); | |
363 | break; | |
364 | case V.DRAGON: | |
365 | moves = this.getPotentialDragonMoves(sq); | |
366 | break; | |
367 | case V.CARDINAL: | |
368 | moves = this.getPotentialCardinalMoves(sq); | |
369 | break; | |
370 | case V.MARSHAL: | |
371 | moves = this.getPotentialMarshalMoves(sq); | |
372 | break; | |
373 | } | |
374 | // Maybe apply promotions: | |
278a28a1 BA |
375 | if (Object.keys(V.PromoteMap).includes(p)) |
376 | this.applyPromotions(moves, V.PromoteMap[p]); | |
723262f9 BA |
377 | return moves; |
378 | } | |
379 | ||
380 | getPotentialMarshalMoves(sq) { | |
381 | return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat( | |
4313762d | 382 | this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], 1) |
723262f9 BA |
383 | ); |
384 | } | |
385 | ||
386 | getPotentialCardinalMoves(sq) { | |
387 | return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat( | |
4313762d | 388 | this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], 1) |
723262f9 BA |
389 | ); |
390 | } | |
391 | ||
392 | getPotentialScepterMoves(sq) { | |
393 | const steps = | |
394 | V.steps[V.KNIGHT].concat(V.steps[V.BISHOP]).concat(V.steps[V.ROOK]); | |
4313762d | 395 | return this.getSlideNJumpMoves(sq, steps, 1); |
723262f9 BA |
396 | } |
397 | ||
398 | getPotentialHorseMoves(sq) { | |
399 | return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat( | |
4313762d | 400 | this.getSlideNJumpMoves(sq, V.steps[V.ROOK], 1)); |
723262f9 BA |
401 | } |
402 | ||
403 | getPotentialDragonMoves(sq) { | |
404 | return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat( | |
4313762d | 405 | this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], 1)); |
723262f9 BA |
406 | } |
407 | ||
723262f9 BA |
408 | getPotentialKingMoves(sq) { |
409 | // Initialize with normal moves | |
410 | let moves = this.getSlideNJumpMoves( | |
4313762d | 411 | sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]), 1); |
723262f9 BA |
412 | const c = this.turn; |
413 | if ( | |
414 | this.castleFlags[c][0] < V.size.y || | |
415 | this.castleFlags[c][1] < V.size.y | |
723262f9 BA |
416 | ) { |
417 | const finalSquares = [ | |
418 | [1, 2], | |
419 | [7, 6] | |
420 | ]; | |
b2e8c34e | 421 | moves = moves.concat(super.getCastleMoves(sq, finalSquares)); |
723262f9 | 422 | } |
b2e8c34e | 423 | return moves; |
723262f9 BA |
424 | } |
425 | ||
426 | isAttacked(sq, color) { | |
427 | return ( | |
428 | this.isAttackedByPawn(sq, color) || | |
429 | this.isAttackedByRook(sq, color) || | |
430 | this.isAttackedByKnight(sq, color) || | |
431 | this.isAttackedByBishop(sq, color) || | |
432 | this.isAttackedByKing(sq, color) || | |
433 | this.isAttackedByQueens(sq, color) || | |
434 | this.isAttackedByScepter(sq, color) || | |
435 | this.isAttackedByDragon(sq, color) || | |
436 | this.isAttackedByHorse(sq, color) || | |
437 | this.isAttackedByMarshal(sq, color) || | |
438 | this.isAttackedByCardinal(sq, color) | |
439 | ); | |
440 | } | |
441 | ||
442 | isAttackedByQueens([x, y], color) { | |
443 | // pieces: because queen = gilding = whole = apricot | |
444 | const pieces = [V.QUEEN, V.GILDING, V.WHOLE, V.APRICOT]; | |
445 | const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); | |
446 | for (let step of steps) { | |
447 | let rx = x + step[0], | |
448 | ry = y + step[1]; | |
449 | while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY) { | |
450 | rx += step[0]; | |
451 | ry += step[1]; | |
452 | } | |
453 | if ( | |
454 | V.OnBoard(rx, ry) && | |
455 | this.board[rx][ry] != V.EMPTY && | |
456 | pieces.includes(this.getPiece(rx, ry)) && | |
457 | this.getColor(rx, ry) == color | |
458 | ) { | |
459 | return true; | |
460 | } | |
461 | } | |
462 | return false; | |
463 | } | |
464 | ||
465 | isAttackedByScepter(sq, color) { | |
466 | const steps = | |
467 | V.steps[V.KNIGHT].concat(V.steps[V.ROOK]).concat(V.steps[V.BISHOP]); | |
468 | return ( | |
4313762d | 469 | super.isAttackedBySlideNJump(sq, color, V.SCEPTER, steps, 1) |
723262f9 BA |
470 | ); |
471 | } | |
472 | ||
473 | isAttackedByHorse(sq, color) { | |
474 | return ( | |
d9ed47ad | 475 | super.isAttackedBySlideNJump(sq, color, V.HORSE, V.steps[V.BISHOP]) || |
723262f9 | 476 | super.isAttackedBySlideNJump( |
4313762d | 477 | sq, color, V.HORSE, V.steps[V.ROOK], 1) |
723262f9 BA |
478 | ); |
479 | } | |
480 | ||
481 | isAttackedByDragon(sq, color) { | |
482 | return ( | |
d9ed47ad | 483 | super.isAttackedBySlideNJump(sq, color, V.DRAGON, V.steps[V.ROOK]) || |
723262f9 | 484 | super.isAttackedBySlideNJump( |
4313762d | 485 | sq, color, V.DRAGON, V.steps[V.BISHOP], 1) |
723262f9 BA |
486 | ); |
487 | } | |
488 | ||
489 | isAttackedByMarshal(sq, color) { | |
490 | return ( | |
491 | super.isAttackedBySlideNJump(sq, color, V.MARSHAL, V.steps[V.ROOK]) || | |
492 | super.isAttackedBySlideNJump( | |
4313762d | 493 | sq, color, V.MARSHAL, V.steps[V.KNIGHT], 1) |
723262f9 BA |
494 | ); |
495 | } | |
496 | ||
497 | isAttackedByCardinal(sq, color) { | |
498 | return ( | |
499 | super.isAttackedBySlideNJump(sq, color, V.CARDINAL, V.steps[V.BISHOP]) || | |
500 | super.isAttackedBySlideNJump( | |
4313762d | 501 | sq, color, V.CARDINAL, V.steps[V.KNIGHT], 1) |
723262f9 BA |
502 | ); |
503 | } | |
504 | ||
505 | getAllValidMoves() { | |
506 | let moves = super.getAllPotentialMoves(); | |
1220a5b9 BA |
507 | if (this.movesCount >= 2) { |
508 | const color = this.turn; | |
509 | for (let i = 0; i < V.RESERVE_PIECES.length; i++) { | |
510 | moves = moves.concat( | |
511 | this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i]) | |
512 | ); | |
513 | } | |
723262f9 BA |
514 | } |
515 | return this.filterValid(moves); | |
516 | } | |
517 | ||
518 | atLeastOneMove(noReserve) { | |
519 | if (!super.atLeastOneMove()) { | |
520 | if (!noReserve) { | |
521 | // Search one reserve move | |
522 | for (let i = 0; i < V.RESERVE_PIECES.length; i++) { | |
523 | let moves = this.filterValid( | |
524 | this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i]) | |
525 | ); | |
526 | if (moves.length > 0) return true; | |
527 | } | |
528 | } | |
529 | return false; | |
530 | } | |
531 | return true; | |
532 | } | |
533 | ||
534 | // Reverse 'PromoteMap' | |
535 | static get P_CORRESPONDANCES() { | |
536 | return { | |
537 | d: 'r', | |
538 | s: 'n', | |
539 | h: 'b', | |
540 | w: 'c', | |
51a4d21e BA |
541 | a: 'm', |
542 | g: 'p' | |
723262f9 BA |
543 | }; |
544 | } | |
545 | ||
546 | static MayDecode(piece) { | |
547 | if (Object.keys(V.P_CORRESPONDANCES).includes(piece)) | |
548 | return V.P_CORRESPONDANCES[piece]; | |
549 | return piece; | |
550 | } | |
551 | ||
552 | play(move) { | |
553 | move.subTurn = this.subTurn; //much easier | |
554 | if (this.movesCount >= 2 || this.subTurn == 2 || move.vanish.length == 0) { | |
555 | this.turn = V.GetOppCol(this.turn); | |
556 | this.subTurn = 1; | |
557 | this.movesCount++; | |
558 | } | |
559 | else this.subTurn = 2; | |
560 | move.flags = JSON.stringify(this.aggregateFlags()); | |
561 | this.epSquares.push(this.getEpSquare(move)); | |
562 | V.PlayOnBoard(this.board, move); | |
563 | this.postPlay(move); | |
564 | } | |
565 | ||
723262f9 BA |
566 | postPlay(move) { |
567 | if (move.vanish.length == 0 && move.appear.length == 0) return; | |
568 | super.postPlay(move); | |
569 | const color = move.appear[0].c; | |
570 | if (move.vanish.length == 0) | |
571 | // Drop unpromoted piece: | |
572 | this.reserve[color][move.appear[0].p]--; | |
1220a5b9 | 573 | else if (move.vanish.length == 2 && move.appear.length == 1) |
723262f9 BA |
574 | // May capture a promoted piece: |
575 | this.reserve[color][V.MayDecode(move.vanish[1].p)]++; | |
576 | } | |
577 | ||
578 | undo(move) { | |
579 | this.epSquares.pop(); | |
580 | this.disaggregateFlags(JSON.parse(move.flags)); | |
581 | V.UndoOnBoard(this.board, move); | |
582 | if (this.movesCount >= 2 || this.subTurn == 1 || move.vanish.length == 0) { | |
583 | this.turn = V.GetOppCol(this.turn); | |
584 | this.movesCount--; | |
585 | } | |
586 | this.subTurn = move.subTurn; | |
587 | this.postUndo(move); | |
588 | } | |
589 | ||
590 | postUndo(move) { | |
591 | if (move.vanish.length == 0 && move.appear.length == 0) return; | |
592 | super.postUndo(move); | |
593 | const color = move.appear[0].c; | |
594 | if (move.vanish.length == 0) | |
595 | this.reserve[color][move.appear[0].p]++; | |
1220a5b9 | 596 | else if (move.vanish.length == 2 && move.appear.length == 1) |
723262f9 BA |
597 | this.reserve[color][V.MayDecode(move.vanish[1].p)]--; |
598 | } | |
599 | ||
723262f9 BA |
600 | static get VALUES() { |
601 | return Object.assign( | |
99ba622a BA |
602 | {}, |
603 | ChessRules.VALUES, | |
723262f9 | 604 | { |
99ba622a | 605 | n: 2.5, //knight is weaker |
723262f9 BA |
606 | g: 9, |
607 | s: 5, | |
608 | h: 6, | |
609 | d: 7, | |
610 | c: 7, | |
611 | w: 9, | |
612 | m: 8, | |
613 | a: 9 | |
99ba622a | 614 | } |
723262f9 BA |
615 | ); |
616 | } | |
617 | ||
618 | static get SEARCH_DEPTH() { | |
619 | return 2; | |
620 | } | |
621 | ||
622 | getComputerMove() { | |
623 | if (this.movesCount <= 1) { | |
624 | // Special case: swap and pass at random | |
625 | const moves1 = this.getAllValidMoves(); | |
626 | const m1 = moves1[randInt(moves1.length)]; | |
627 | this.play(m1); | |
628 | if (m1.vanish.length == 0) { | |
629 | this.undo(m1); | |
630 | return m1; | |
631 | } | |
632 | const moves2 = this.getAllValidMoves(); | |
633 | const m2 = moves2[randInt(moves2.length)]; | |
634 | this.undo(m1); | |
635 | return [m1, m2]; | |
636 | } | |
637 | return super.getComputerMove(); | |
638 | } | |
639 | ||
640 | evalPosition() { | |
641 | let evaluation = super.evalPosition(); | |
642 | // Add reserves: | |
643 | for (let i = 0; i < V.RESERVE_PIECES.length; i++) { | |
644 | const p = V.RESERVE_PIECES[i]; | |
645 | evaluation += this.reserve["w"][p] * V.VALUES[p]; | |
646 | evaluation -= this.reserve["b"][p] * V.VALUES[p]; | |
647 | } | |
648 | return evaluation; | |
649 | } | |
650 | ||
651 | getNotation(move) { | |
652 | if (move.vanish.length == 0) { | |
653 | if (move.appear.length == 0) return "pass"; | |
654 | const pieceName = | |
655 | (move.appear[0].p == V.PAWN ? "" : move.appear[0].p.toUpperCase()); | |
656 | return pieceName + "@" + V.CoordsToSquare(move.end); | |
657 | } | |
658 | if (move.appear.length == 2) { | |
659 | if (move.appear[0].p != V.KING) | |
660 | return V.CoordsToSquare(move.start) + "S" + V.CoordsToSquare(move.end); | |
661 | return (move.end.y < move.start.y ? "0-0" : "0-0-0"); | |
662 | } | |
663 | let notation = super.getNotation(move); | |
664 | if (move.vanish[0].p != V.PAWN && move.appear[0].p != move.vanish[0].p) | |
665 | // Add promotion indication: | |
666 | notation += "=" + move.appear[0].p.toUpperCase(); | |
667 | return notation; | |
668 | } | |
669 | ||
670 | }; |