Commit | Line | Data |
---|---|---|
3208c667 BA |
1 | import { ChessRules, Move, PiPo } from "@/base_rules"; |
2 | import { randInt } from "@/utils/alea"; | |
3 | ||
4 | export class MusketeerRules extends ChessRules { | |
5 | ||
6 | // Extra pieces get strange letters because many taken by combinations below | |
7 | static get LEOPARD() { | |
8 | return "d"; | |
9 | } | |
10 | static get CANNON() { | |
11 | return "w"; | |
12 | } | |
13 | static get UNICORN() { | |
14 | return "x"; | |
15 | } | |
16 | static get ELEPHANT() { | |
17 | return "e"; | |
18 | } | |
19 | static get HAWK() { | |
20 | return "h"; | |
21 | } | |
22 | static get FORTRESS() { | |
23 | return "f"; | |
24 | } | |
25 | static get SPIDER() { | |
26 | return "y"; | |
27 | } | |
28 | ||
29 | static get RESERVE_PIECES() { | |
30 | return ( | |
31 | [V.LEOPARD, V.CANNON, V.UNICORN, V.ELEPHANT, | |
32 | V.HAWK, V.FORTRESS, V.SPIDER] | |
33 | ); | |
34 | } | |
35 | ||
36 | static get PIECES() { | |
37 | return ChessRules.PIECES.concat(V.RESERVE_PIECES); | |
38 | } | |
39 | ||
40 | // Decode if normal piece, or + piece1 or piece2 | |
41 | getPiece(i, j) { | |
42 | if (i >= V.size.x) return V.RESERVE_PIECES[j]; | |
43 | const piece = this.board[i][j].charAt(1); | |
44 | if (V.PIECES.includes(piece)) return piece; | |
45 | // Augmented piece: | |
46 | switch (piece) { | |
47 | case 'a': | |
48 | case 'c': | |
49 | return 'b'; | |
50 | case 'j': | |
51 | case 'l': | |
52 | return 'k'; | |
53 | case 'm': | |
54 | case 'o': | |
55 | return 'n'; | |
56 | case 's': | |
57 | case 't': | |
58 | return 'q'; | |
59 | case 'u': | |
60 | case 'v': | |
61 | return 'r'; | |
62 | } | |
63 | } | |
64 | ||
65 | getColor(i, j) { | |
66 | if (i >= V.size.x) return i == V.size.x ? "w" : "b"; | |
67 | return this.board[i][j].charAt(0); | |
68 | } | |
69 | ||
70 | // Code: a/c = bishop + piece1/piece2 j/l for king, | |
71 | // m/o for knight, s/t for queen, u/v for rook | |
72 | static get AUGMENTED_PIECES() { | |
73 | return [ | |
74 | 'a', | |
75 | 'c', | |
76 | 'j', | |
77 | 'l', | |
78 | 'm', | |
79 | 'o', | |
80 | 's', | |
81 | 't', | |
82 | 'u', | |
83 | 'v' | |
84 | ]; | |
85 | } | |
86 | ||
87 | getPpath(b) { | |
88 | return (ChessRules.PIECES.includes(b[1]) ? "" : "Musketeer/") + b; | |
89 | } | |
90 | ||
91 | getReservePpath(index, color) { | |
92 | return "Musketeer/" + color + V.RESERVE_PIECES[index]; | |
93 | } | |
94 | ||
95 | // Decode above notation into additional piece | |
96 | getExtraPiece(symbol) { | |
97 | if (['a','j','m','s','u'].includes(symbol)) | |
98 | return this.extraPieces[0]; | |
99 | return this.extraPieces[1]; | |
100 | } | |
101 | ||
102 | // Inverse operation: augment piece | |
103 | getAugmented(piece) { | |
104 | const p1 = [2, 3].includes(this.movesCount); | |
105 | switch (piece) { | |
106 | case V.ROOK: return (p1 ? 'u' : 'v'); | |
107 | case V.KNIGHT: return (p1 ? 'm' : 'o'); | |
108 | case V.BISHOP: return (p1 ? 'a' : 'c'); | |
109 | case V.QUEEN: return (p1 ? 's' : 't'); | |
110 | case V.KING: return (p1 ? 'j' : 'l'); | |
111 | } | |
112 | return '_'; //never reached | |
113 | } | |
114 | ||
115 | static IsGoodFen(fen) { | |
116 | if (!ChessRules.IsGoodFen(fen)) return false; | |
117 | const fenParsed = V.ParseFen(fen); | |
118 | // 5) Check extra pieces | |
119 | if (!fenParsed.extraPieces) return false; | |
120 | // Not exact matching (would need to look at movesCount), but OK for now | |
121 | if (!fenParsed.extraPieces.match(/^[dwxejfy-]{2,2}$/)) return false; | |
122 | return true; | |
123 | } | |
124 | ||
125 | static IsGoodPosition(position) { | |
126 | if (position.length == 0) return false; | |
127 | const rows = position.split("/"); | |
128 | if (rows.length != V.size.x) return false; | |
129 | let kings = { "w": 0, "b": 0 }; | |
130 | const allPiecesCodes = V.PIECES.concat(V.AUGMENTED_PIECES); | |
131 | const kingBlackCodes = ['j','k','l']; | |
132 | const kingWhiteCodes = ['J','K','L']; | |
133 | for (let row of rows) { | |
134 | let sumElts = 0; | |
135 | for (let i = 0; i < row.length; i++) { | |
136 | if (kingBlackCodes.includes(row[i])) kings['b']++; | |
137 | else if (kingWhiteCodes.includes(row[i])) kings['w']++; | |
138 | if (allPiecesCodes.includes(row[i].toLowerCase())) sumElts++; | |
139 | else { | |
140 | const num = parseInt(row[i], 10); | |
141 | if (isNaN(num)) return false; | |
142 | sumElts += num; | |
143 | } | |
144 | } | |
145 | if (sumElts != V.size.y) return false; | |
146 | } | |
147 | // Both kings should be on board, only one of each color: | |
148 | if (Object.values(kings).some(v => v != 1)) return false; | |
149 | return true; | |
150 | } | |
151 | ||
152 | static ParseFen(fen) { | |
153 | const fenParts = fen.split(" "); | |
154 | return Object.assign( | |
155 | ChessRules.ParseFen(fen), | |
156 | { extraPieces: fenParts[5] } | |
157 | ); | |
158 | } | |
159 | ||
160 | static GenRandInitFen(randomness) { | |
161 | return ChessRules.GenRandInitFen(randomness) + " --"; | |
162 | } | |
163 | ||
164 | getFen() { | |
165 | return super.getFen() + " " + this.extraPieces.join(""); | |
166 | } | |
167 | ||
168 | setOtherVariables(fen) { | |
169 | super.setOtherVariables(fen); | |
170 | // Extra pieces may not be defined yet (thus '-') | |
171 | this.extraPieces = V.ParseFen(fen).extraPieces.split(""); | |
172 | // At early stages, also init reserves | |
173 | if (this.movesCount <= 5) { | |
174 | const condShow = (piece) => { | |
175 | if (this.movesCount == 0) return true; | |
176 | if (this.movesCount == 1) return piece != this.extraPieces[0]; | |
177 | if (this.movesCount <= 3) return this.extraPiece.includes(piece); | |
178 | return this.extraPiece[1] == piece; | |
179 | } | |
180 | this.reserve = { w : {}, b: {} }; | |
181 | for (let c of ['w', 'b']) { | |
182 | V.RESERVE_PIECES.forEach(p => | |
183 | this.reserve[c][p] = condShow(p) ? 1 : 0); | |
184 | } | |
185 | } | |
186 | } | |
187 | ||
188 | // Kings may be augmented: | |
189 | scanKings(fen) { | |
190 | this.kingPos = { w: [-1, -1], b: [-1, -1] }; | |
191 | const rows = V.ParseFen(fen).position.split("/"); | |
192 | for (let i = 0; i < rows.length; i++) { | |
193 | let k = 0; //column index on board | |
194 | for (let j = 0; j < rows[i].length; j++) { | |
195 | const piece = rows[i].charAt(j); | |
196 | if (['j','k','l'].includes(piece.toLowerCase())) { | |
197 | const color = (piece.charCodeAt(0) <= 90 ? 'w' : 'b'); | |
198 | this.kingPos[color] = [i, k]; | |
199 | } | |
200 | else { | |
201 | const num = parseInt(rows[i].charAt(j), 10); | |
202 | if (!isNaN(num)) k += num - 1; | |
203 | } | |
204 | k++; | |
205 | } | |
206 | } | |
207 | } | |
208 | ||
209 | getReserveMoves([x, y]) { | |
210 | const color = this.turn; | |
211 | const p = V.RESERVE_PIECES[y]; | |
212 | if ( | |
213 | this.reserve[color][p] == 0 || | |
214 | ([2, 3].includes(this.movesCount) && p != this.extraPieces[0]) || | |
215 | ([4, 5].includes(this.movesCount) && p != this.extraPieces[1]) | |
216 | ) { | |
217 | return []; | |
218 | } | |
219 | let moves = []; | |
220 | const iIdx = | |
221 | (this.movesCount <= 1 ? [2, 3, 4, 5] : [color == 'w' ? 7 : 0]); | |
222 | const mappingAppear = [ [3, 4], [3, 3], [4, 4], [4, 3] ]; | |
223 | for (let i of iIdx) { | |
224 | for (let j = 0; j < V.size.y; j++) { | |
225 | if ( | |
226 | (this.movesCount <= 1 && this.board[i][j] == V.EMPTY) || | |
227 | ( | |
228 | this.movesCount >= 2 && | |
229 | ChessRules.PIECES.includes(this.board[i][j].charAt(1)) | |
230 | ) | |
231 | ) { | |
232 | const [appearX, appearY] = | |
233 | this.movesCount <= 1 | |
234 | ? mappingAppear[this.movesCount] | |
235 | : [i, j]; | |
236 | const pOnBoard = | |
237 | (this.movesCount >= 2 ? this.board[i][j].charAt(1) : ''); | |
238 | let mv = new Move({ | |
239 | appear: [ | |
240 | new PiPo({ | |
241 | x: appearX, | |
242 | y: appearY, | |
243 | c: color, | |
244 | p: (this.movesCount <= 1 ? p : this.getAugmented(pOnBoard)) | |
245 | }) | |
246 | ], | |
247 | vanish: [], | |
248 | start: { x: x, y: y }, //a bit artificial... | |
249 | end: { x: i, y: j } | |
250 | }); | |
251 | if (this.movesCount >= 2) | |
252 | mv.vanish.push(new PiPo({ x: i, y: j, c: color, p: pOnBoard })) | |
253 | moves.push(mv); | |
254 | } | |
255 | } | |
256 | } | |
257 | return moves; | |
258 | } | |
259 | ||
260 | // Assumption: movesCount >= 6 | |
261 | getPotentialMovesFrom([x, y]) { | |
262 | // Standard moves. If piece not in usual list, new piece appears. | |
263 | const initialPiece = this.getPiece(x, y); | |
264 | if (V.RESERVE_PIECES.includes(initialPiece)) { | |
265 | switch (initialPiece) { | |
266 | case V.LEOPARD: return this.getPotentialLeopardMoves([x, y]); | |
267 | case V.CANNON: return this.getPotentialCannonMoves([x, y]); | |
268 | case V.UNICORN: return this.getPotentialUnicornMoves([x, y]); | |
269 | case V.ELEPHANT: return this.getPotentialElephantMoves([x, y]); | |
270 | case V.HAWK: return this.getPotentialHawkMoves([x, y]); | |
271 | case V.FORTRESS: return this.getPotentialFortressMoves([x, y]); | |
272 | case V.SPIDER: return this.getPotentialSpiderMoves([x, y]); | |
273 | } | |
274 | return []; //never reached | |
275 | } | |
276 | // Following is mostly copy-paste from Titan Chess (TODO?) | |
277 | let moves = []; | |
278 | if (initialPiece == V.PAWN) { | |
279 | const promotions = | |
280 | ChessRules.PawnSpecs.promotions.concat(this.extraPieces); | |
281 | moves = super.getPotentialPawnMoves([x, y], promotions); | |
282 | } | |
283 | else moves = super.getPotentialMovesFrom([x, y]); | |
284 | const color = this.turn; | |
285 | if ( | |
286 | ((color == 'w' && x == 7) || (color == "b" && x == 0)) && | |
287 | V.AUGMENTED_PIECES.includes(this.board[x][y][1]) | |
288 | ) { | |
289 | const newPiece = this.getExtraPiece(this.board[x][y][1]); | |
290 | moves.forEach(m => { | |
291 | m.appear[0].p = initialPiece; | |
292 | m.appear.push( | |
293 | new PiPo({ | |
294 | p: newPiece, | |
295 | c: color, | |
296 | x: x, | |
297 | y: y | |
298 | }) | |
299 | ); | |
300 | }); | |
301 | moves.forEach(m => { | |
302 | if (m.vanish.length <= 1) return; | |
303 | const [vx, vy] = [m.vanish[1].x, m.vanish[1].y]; | |
304 | if ( | |
305 | m.appear.length >= 2 && //3 if the king was also augmented | |
306 | m.vanish.length == 2 && | |
307 | m.vanish[1].c == color && | |
308 | V.AUGMENTED_PIECES.includes(this.board[vx][vy][1]) | |
309 | ) { | |
310 | // Castle, rook is an "augmented piece" | |
311 | m.appear[1].p = V.ROOK; | |
312 | m.appear.push( | |
313 | new PiPo({ | |
314 | p: this.getExtraPiece(this.board[vx][vy][1]), | |
315 | c: color, | |
316 | x: vx, | |
317 | y: vy | |
318 | }) | |
319 | ); | |
320 | } | |
321 | }); | |
322 | } | |
323 | return moves; | |
324 | } | |
325 | ||
326 | getSlideNJumpMoves([x, y], steps, nbSteps) { | |
327 | let moves = []; | |
328 | outerLoop: for (let step of steps) { | |
329 | let i = x + step[0]; | |
330 | let j = y + step[1]; | |
cee75a57 | 331 | let stepCounter = 0; |
3208c667 BA |
332 | while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { |
333 | moves.push(this.getBasicMove([x, y], [i, j])); | |
cee75a57 | 334 | stepCounter++; |
3208c667 BA |
335 | if ( |
336 | !!nbSteps && | |
337 | // Next condition to remain compatible with super method | |
cee75a57 | 338 | (stepCounter >= nbSteps || isNaN(parseInt(nbSteps, 10))) |
3208c667 BA |
339 | ) { |
340 | continue outerLoop; | |
341 | } | |
342 | i += step[0]; | |
343 | j += step[1]; | |
3208c667 BA |
344 | } |
345 | if (V.OnBoard(i, j) && this.canTake([x, y], [i, j])) | |
346 | moves.push(this.getBasicMove([x, y], [i, j])); | |
347 | } | |
348 | return moves; | |
349 | } | |
350 | ||
351 | // All types of leaps used here: | |
352 | static get Leap2Ortho() { | |
353 | return [ [-2, 0], [0, -2], [2, 0], [0, 2] ]; | |
354 | } | |
355 | static get Leap2Diago() { | |
356 | return [ [-2, -2], [-2, 2], [2, -2], [2, 2] ]; | |
357 | } | |
358 | static get Leap3Ortho() { | |
359 | return [ [-3, 0], [0, -3], [3, 0], [0, 3] ]; | |
360 | } | |
361 | static get Leap3Diago() { | |
362 | return [ [-3, -3], [-3, 3], [3, -3], [3, 3] ]; | |
363 | } | |
364 | static get CamelSteps() { | |
365 | return [ | |
366 | [-3, -1], [-3, 1], [-1, -3], [-1, 3], | |
367 | [1, -3], [1, 3], [3, -1], [3, 1] | |
368 | ]; | |
369 | } | |
370 | static get VerticalKnight() { | |
371 | return [ [-2, -1], [-2, 1], [2, -1], [2, 1] ]; | |
372 | } | |
373 | static get HorizontalKnight() { | |
374 | return [ [-1, -2], [-1, 2], [1, -2], [1, 2] ]; | |
375 | } | |
376 | ||
377 | getPotentialLeopardMoves(sq) { | |
378 | return ( | |
379 | this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], 2) | |
380 | .concat(super.getPotentialKnightMoves(sq)) | |
381 | ); | |
382 | } | |
383 | ||
384 | getPotentialCannonMoves(sq) { | |
385 | const steps = | |
386 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]) | |
387 | .concat(V.Leap2Ortho).concat(V.HorizontalKnight); | |
388 | return super.getSlideNJumpMoves(sq, steps, "oneStep"); | |
389 | } | |
390 | ||
391 | getPotentialUnicornMoves(sq) { | |
392 | return ( | |
393 | super.getPotentialKnightMoves(sq) | |
394 | .concat(super.getSlideNJumpMoves(sq, V.CamelSteps, "oneStep")) | |
395 | ); | |
396 | } | |
397 | ||
398 | getPotentialElephantMoves(sq) { | |
399 | const steps = | |
400 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]) | |
401 | .concat(V.Leap2Ortho) | |
402 | .concat(V.Leap2Diago); | |
403 | return super.getSlideNJumpMoves(sq, steps, "oneStep"); | |
404 | } | |
405 | ||
406 | getPotentialHawkMoves(sq) { | |
407 | const steps = | |
408 | V.Leap2Ortho.concat(V.Leap2Diago) | |
409 | .concat(V.Leap3Ortho).concat(V.Leap3Diago); | |
410 | return super.getSlideNJumpMoves(sq, steps, "oneStep"); | |
411 | } | |
412 | ||
413 | getPotentialFortressMoves(sq) { | |
414 | const steps = V.Leap2Ortho.concat(V.VerticalKnight) | |
415 | return ( | |
416 | super.getSlideNJumpMoves(sq, steps, "oneStep") | |
417 | .concat(this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], 3)) | |
418 | ); | |
419 | } | |
420 | ||
421 | getPotentialSpiderMoves(sq) { | |
422 | const steps = V.Leap2Ortho.concat(V.steps[V.KNIGHT]) | |
423 | return ( | |
424 | super.getSlideNJumpMoves(sq, steps, "oneStep") | |
425 | .concat(this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], 2)) | |
426 | ); | |
427 | } | |
428 | ||
429 | getPossibleMovesFrom([x, y]) { | |
430 | if (this.movesCount <= 5) | |
431 | return (x >= V.size.x ? this.getReserveMoves([x, y]) : []); | |
432 | return super.getPossibleMovesFrom([x, y]); | |
433 | } | |
434 | ||
435 | getAllValidMoves() { | |
436 | if (this.movesCount >= 6) return super.getAllValidMoves(); | |
437 | let moves = []; | |
438 | const color = this.turn; | |
439 | for (let i = 0; i < V.RESERVE_PIECES.length; i++) { | |
440 | moves = moves.concat( | |
441 | this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i]) | |
442 | ); | |
443 | } | |
444 | return moves; | |
445 | } | |
446 | ||
447 | atLeastOneMove() { | |
448 | if (this.movesCount <= 5) return true; | |
449 | return super.atLeastOneMove(); | |
450 | } | |
451 | ||
452 | isAttacked(sq, color) { | |
453 | if (super.isAttacked(sq, color)) return true; | |
454 | if ( | |
455 | this.extraPieces.includes(V.LEOPARD) && | |
456 | this.isAttackedByLeopard(sq, color) | |
457 | ) { | |
458 | return true; | |
459 | } | |
460 | if ( | |
461 | this.extraPieces.includes(V.CANNON) && | |
462 | this.isAttackedByCannon(sq, color) | |
463 | ) { | |
464 | return true; | |
465 | } | |
466 | if ( | |
467 | this.extraPieces.includes(V.UNICORN) && | |
468 | this.isAttackedByUnicorn(sq, color) | |
469 | ) { | |
470 | return true; | |
471 | } | |
472 | if ( | |
473 | this.extraPieces.includes(V.ELEPHANT) && | |
474 | this.isAttackedByElephant(sq, color) | |
475 | ) { | |
476 | return true; | |
477 | } | |
478 | if ( | |
479 | this.extraPieces.includes(V.HAWK) && | |
480 | this.isAttackedByHawk(sq, color) | |
481 | ) { | |
482 | return true; | |
483 | } | |
484 | if ( | |
485 | this.extraPieces.includes(V.FORTRESS) && | |
486 | this.isAttackedByFortress(sq, color) | |
487 | ) { | |
488 | return true; | |
489 | } | |
490 | if ( | |
491 | this.extraPieces.includes(V.SPIDER) && | |
492 | this.isAttackedBySpider(sq, color) | |
493 | ) { | |
494 | return true; | |
495 | } | |
496 | return false; | |
497 | } | |
498 | ||
499 | // Modify because of the limiyted steps options of some of the pieces here | |
500 | isAttackedBySlideNJump([x, y], color, piece, steps, nbSteps) { | |
501 | if (!!nbSteps && isNaN(parseInt(nbSteps, 10))) nbSteps = 1; | |
502 | for (let step of steps) { | |
503 | let rx = x + step[0], | |
504 | ry = y + step[1]; | |
505 | let stepCounter = 1; | |
506 | while ( | |
507 | V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && | |
508 | (!nbSteps || stepCounter < nbSteps) | |
509 | ) { | |
510 | rx += step[0]; | |
511 | ry += step[1]; | |
512 | stepCounter++; | |
513 | } | |
514 | if ( | |
515 | V.OnBoard(rx, ry) && | |
516 | this.board[rx][ry] != V.EMPTY && | |
517 | this.getPiece(rx, ry) == piece && | |
518 | this.getColor(rx, ry) == color | |
519 | ) { | |
520 | return true; | |
521 | } | |
522 | } | |
523 | return false; | |
524 | } | |
525 | ||
526 | isAttackedByLeopard(sq, color) { | |
527 | return ( | |
528 | super.isAttackedBySlideNJump( | |
529 | sq, color, V.LEOPARD, V.steps[V.KNIGHT], "oneStep") || | |
530 | this.isAttackedBySlideNJump(sq, color, V.LEOPARD, V.steps[V.BISHOP], 2) | |
531 | ); | |
532 | } | |
533 | ||
534 | isAttackedByCannon(sq, color) { | |
535 | const steps = | |
536 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]) | |
537 | .concat(V.Leap2Ortho).concat(V.HorizontalKnight); | |
538 | return super.isAttackedBySlideNJump(sq, color, V.CANNON, steps, "oneStep"); | |
539 | } | |
540 | ||
541 | isAttackedByUnicorn(sq, color) { | |
542 | const steps = V.steps[V.KNIGHT].concat(V.CamelSteps) | |
543 | return ( | |
544 | super.isAttackedBySlideNJump(sq, color, V.UNICORN, steps, "oneStep") | |
545 | ); | |
546 | } | |
547 | ||
548 | isAttackedByElephant(sq, color) { | |
549 | const steps = | |
550 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]) | |
551 | .concat(V.Leap2Ortho) | |
552 | .concat(V.Leap2Diago); | |
553 | return ( | |
554 | super.isAttackedBySlideNJump(sq, color, V.ELEPHANT, steps, "oneStep") | |
555 | ); | |
556 | } | |
557 | ||
558 | isAttackedByHawk(sq, color) { | |
559 | const steps = | |
560 | V.Leap2Ortho.concat(V.Leap2Diago) | |
561 | .concat(V.Leap3Ortho).concat(V.Leap3Diago); | |
562 | return super.isAttackedBySlideNJump(sq, color, V.HAWK, steps, "oneStep"); | |
563 | } | |
564 | ||
565 | isAttackedByFortress(sq, color) { | |
566 | const steps = V.Leap2Ortho.concat(V.VerticalKnight) | |
567 | return ( | |
568 | super.isAttackedBySlideNJump(sq, color, V.FORTRESS, steps, "oneStep") || | |
569 | this.isAttackedBySlideNJump(sq, color, V.FORTRESS, V.steps[V.BISHOP], 3) | |
570 | ); | |
571 | } | |
572 | ||
573 | isAttackedBySpider(sq, color) { | |
574 | const steps = V.Leap2Ortho.concat(V.steps[V.KNIGHT]) | |
575 | return ( | |
576 | super.isAttackedBySlideNJump(sq, color, V.SPIDER, steps, "oneStep") || | |
577 | this.isAttackedBySlideNJump(sq, color, V.SPIDER, V.steps[V.BISHOP], 2) | |
578 | ); | |
579 | } | |
580 | ||
581 | getCheckSquares() { | |
582 | if (this.movesCount <= 6) return []; | |
583 | return super.getCheckSquares(); | |
584 | } | |
585 | ||
586 | // At movesCount == 0,1: show full reserves [minus chosen piece1] | |
587 | // At movesCount == 2,3: show reserve with only 2 selected pieces | |
588 | // At movesCount == 4,5: show reserve with only piece2 | |
589 | // Then, no reserve. | |
590 | postPlay(move) { | |
591 | if (this.movesCount > 6) super.postPlay(move); | |
592 | else { | |
593 | switch (this.movesCount) { | |
594 | case 1: | |
595 | this.reserve['w'][move.appear[0].p]--; | |
596 | this.reserve['b'][move.appear[0].p]--; | |
597 | this.extraPieces[0] = move.appear[0].p; | |
598 | break; | |
599 | case 2: | |
600 | this.extraPieces[1] = move.appear[0].p; | |
601 | for (let p of V.RESERVE_PIECES) { | |
602 | const resVal = (this.extraPieces.includes(p) ? 1 : 0); | |
603 | this.reserve['w'][p] = resVal; | |
604 | this.reserve['b'][p] = resVal; | |
605 | } | |
606 | break; | |
607 | case 3: | |
608 | this.reserve['w'][this.extraPieces[0]]--; | |
609 | break; | |
610 | case 4: | |
611 | this.reserve['b'][this.extraPieces[0]]--; | |
612 | break; | |
613 | case 5: | |
614 | this.reserve['w'][this.extraPieces[1]]--; | |
615 | break; | |
616 | case 6: | |
617 | this.reserve = null; | |
618 | this.board[3][3] = ""; | |
619 | this.board[3][4] = ""; | |
620 | break; | |
621 | } | |
622 | } | |
623 | } | |
624 | ||
625 | postUndo(move) { | |
626 | if (this.movesCount >= 6) super.postUndo(move); | |
627 | else { | |
628 | switch (this.movesCount) { | |
629 | case 0: | |
630 | this.reserve['w'][move.appear[0].p]++; | |
631 | this.reserve['b'][move.appear[0].p]++; | |
632 | this.extraPieces[0] = '-'; | |
633 | break; | |
634 | case 1: | |
635 | this.extraPieces[1] = '-'; | |
636 | for (let p of V.RESERVE_PIECES) { | |
637 | const resVal = (p != this.extraPieces[0] ? 1 : 0); | |
638 | this.reserve['w'][p] = resVal; | |
639 | this.reserve['b'][p] = resVal; | |
640 | } | |
641 | break; | |
642 | case 2: | |
643 | this.reserve['w'][this.extraPieces[0]]++; | |
644 | break; | |
645 | case 3: | |
646 | this.reserve['b'][this.extraPieces[0]]++; | |
647 | break; | |
648 | case 4: | |
649 | this.reserve['w'][this.extraPieces[1]]++; | |
650 | break; | |
651 | case 5: | |
652 | this.reserve = { w: {}, b: {} }; | |
653 | for (let c of ['w', 'b']) | |
654 | V.RESERVE_PIECES.forEach(p => this.reserve[c][p] = 0); | |
655 | this.reserve['b'][this.extraPieces[1]] = 1; | |
656 | this.board[3][3] = 'b' + this.extraPieces[1]; | |
657 | this.board[3][4] = 'w' + this.extraPieces[0]; | |
658 | break; | |
659 | } | |
660 | } | |
661 | } | |
662 | ||
663 | getComputerMove() { | |
664 | if (this.movesCount >= 6) return super.getComputerMove(); | |
665 | // Choose a move at random | |
666 | const moves = this.getAllValidMoves(); | |
667 | return moves[randInt(moves.length)]; | |
668 | } | |
669 | ||
670 | static get SEARCH_DEPTH() { | |
671 | return 2; | |
672 | } | |
673 | ||
674 | evalPosition() { | |
675 | let evaluation = 0; | |
676 | for (let i = 0; i < V.size.x; i++) { | |
677 | for (let j = 0; j < V.size.y; j++) { | |
678 | if (this.board[i][j] != V.EMPTY) { | |
679 | const sign = this.getColor(i, j) == "w" ? 1 : -1; | |
680 | const piece = this.getPiece(i, j); | |
681 | evaluation += sign * V.VALUES[piece]; | |
682 | const symbol = this.board[i][j][1]; | |
683 | if (V.AUGMENTED_PIECES.includes(symbol)) { | |
684 | const extraPiece = this.getExtraPiece(symbol); | |
685 | evaluation += sign * V.VALUES[extraPiece] | |
686 | } | |
687 | } | |
688 | } | |
689 | } | |
690 | return evaluation; | |
691 | } | |
692 | ||
693 | static get VALUES() { | |
694 | return Object.assign( | |
695 | { | |
696 | d: 6.7, | |
697 | w: 7.5, | |
698 | x: 5.6, | |
699 | e: 6.3, | |
700 | h: 5.5, | |
701 | f: 7.6, | |
702 | y: 8.15 | |
703 | }, | |
704 | ChessRules.VALUES | |
705 | ); | |
706 | } | |
707 | ||
708 | static get ExtraDictionary() { | |
709 | return { | |
710 | [V.LEOPARD]: { prefix: 'L', name: "Leopard" }, | |
711 | [V.CANNON]: { prefix: 'C', name: "Cannon" }, | |
712 | [V.UNICORN]: { prefix: 'U', name: "Unicorn" }, | |
713 | [V.ELEPHANT]: { prefix: 'E', name: "Elephant" }, | |
714 | [V.HAWK]: { prefix: 'H', name: "Hawk" }, | |
715 | [V.FORTRESS]: { prefix: 'F', name: "Fortress" }, | |
716 | [V.SPIDER]: { prefix: 'S', name: "Spider" } | |
717 | } | |
718 | } | |
719 | ||
720 | getNotation(move) { | |
721 | if (this.movesCount <= 5) { | |
722 | if (this.movesCount <= 1) | |
723 | return V.ExtraDictionary[move.appear[0].p].name; | |
724 | // Put something on the board: | |
725 | return ( | |
726 | V.ExtraDictionary[V.RESERVE_PIECES[move.start.y]].prefix + | |
727 | "@" + V.CoordsToSquare(move.end) | |
728 | ); | |
729 | } | |
730 | let notation = ""; | |
731 | if ( | |
732 | V.AUGMENTED_PIECES.includes(move.vanish[0].p) || | |
733 | ( | |
734 | move.vanish.length >= 2 && | |
735 | V.AUGMENTED_PIECES.includes(move.vanish[1].p) | |
736 | ) | |
737 | ) { | |
738 | // Simplify move before calling super.getNotation() | |
739 | let smove = JSON.parse(JSON.stringify(move)); | |
740 | if (ChessRules.PIECES.includes(move.vanish[0].p)) { | |
741 | // Castle with an augmented rook | |
742 | smove.appear.pop(); | |
743 | smove.vanish[1].p = smove.appear[1].p; | |
744 | } | |
745 | else { | |
746 | // Moving an augmented piece | |
747 | smove.appear.pop(); | |
748 | smove.vanish[0].p = smove.appear[0].p; | |
749 | if ( | |
750 | smove.vanish.length == 2 && | |
751 | smove.vanish[0].c == smove.vanish[1].c && | |
752 | V.AUGMENTED_PIECES.includes(move.vanish[1].p) | |
753 | ) { | |
754 | // Castle with an augmented rook | |
755 | smove.appear.pop(); | |
756 | smove.vanish[1].p = smove.appear[1].p; | |
757 | } | |
758 | } | |
759 | notation = super.getNotation(smove); | |
760 | } | |
761 | // Else, more common case: | |
762 | notation = super.getNotation(move); | |
763 | const pieceSymbol = notation.charAt(0).toLowerCase(); | |
764 | if (move.vanish[0].p != V.PAWN && V.RESERVE_PIECES.includes(pieceSymbol)) | |
765 | notation = V.ExtraDictionary[pieceSymbol].prefix + notation.substr(1); | |
766 | return notation; | |
767 | } | |
768 | ||
769 | }; |