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 | ||
3208c667 BA |
326 | // All types of leaps used here: |
327 | static get Leap2Ortho() { | |
328 | return [ [-2, 0], [0, -2], [2, 0], [0, 2] ]; | |
329 | } | |
330 | static get Leap2Diago() { | |
331 | return [ [-2, -2], [-2, 2], [2, -2], [2, 2] ]; | |
332 | } | |
333 | static get Leap3Ortho() { | |
334 | return [ [-3, 0], [0, -3], [3, 0], [0, 3] ]; | |
335 | } | |
336 | static get Leap3Diago() { | |
337 | return [ [-3, -3], [-3, 3], [3, -3], [3, 3] ]; | |
338 | } | |
339 | static get CamelSteps() { | |
340 | return [ | |
341 | [-3, -1], [-3, 1], [-1, -3], [-1, 3], | |
342 | [1, -3], [1, 3], [3, -1], [3, 1] | |
343 | ]; | |
344 | } | |
345 | static get VerticalKnight() { | |
346 | return [ [-2, -1], [-2, 1], [2, -1], [2, 1] ]; | |
347 | } | |
348 | static get HorizontalKnight() { | |
349 | return [ [-1, -2], [-1, 2], [1, -2], [1, 2] ]; | |
350 | } | |
351 | ||
352 | getPotentialLeopardMoves(sq) { | |
353 | return ( | |
354 | this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], 2) | |
355 | .concat(super.getPotentialKnightMoves(sq)) | |
356 | ); | |
357 | } | |
358 | ||
359 | getPotentialCannonMoves(sq) { | |
360 | const steps = | |
361 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]) | |
362 | .concat(V.Leap2Ortho).concat(V.HorizontalKnight); | |
4313762d | 363 | return super.getSlideNJumpMoves(sq, steps, 1); |
3208c667 BA |
364 | } |
365 | ||
366 | getPotentialUnicornMoves(sq) { | |
367 | return ( | |
368 | super.getPotentialKnightMoves(sq) | |
4313762d | 369 | .concat(super.getSlideNJumpMoves(sq, V.CamelSteps, 1)) |
3208c667 BA |
370 | ); |
371 | } | |
372 | ||
373 | getPotentialElephantMoves(sq) { | |
374 | const steps = | |
375 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]) | |
376 | .concat(V.Leap2Ortho) | |
377 | .concat(V.Leap2Diago); | |
4313762d | 378 | return super.getSlideNJumpMoves(sq, steps, 1); |
3208c667 BA |
379 | } |
380 | ||
381 | getPotentialHawkMoves(sq) { | |
382 | const steps = | |
383 | V.Leap2Ortho.concat(V.Leap2Diago) | |
384 | .concat(V.Leap3Ortho).concat(V.Leap3Diago); | |
4313762d | 385 | return super.getSlideNJumpMoves(sq, steps, 1); |
3208c667 BA |
386 | } |
387 | ||
388 | getPotentialFortressMoves(sq) { | |
389 | const steps = V.Leap2Ortho.concat(V.VerticalKnight) | |
390 | return ( | |
4313762d | 391 | super.getSlideNJumpMoves(sq, steps, 1) |
3208c667 BA |
392 | .concat(this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], 3)) |
393 | ); | |
394 | } | |
395 | ||
396 | getPotentialSpiderMoves(sq) { | |
397 | const steps = V.Leap2Ortho.concat(V.steps[V.KNIGHT]) | |
398 | return ( | |
4313762d | 399 | super.getSlideNJumpMoves(sq, steps, 1) |
3208c667 BA |
400 | .concat(this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], 2)) |
401 | ); | |
402 | } | |
403 | ||
404 | getPossibleMovesFrom([x, y]) { | |
405 | if (this.movesCount <= 5) | |
406 | return (x >= V.size.x ? this.getReserveMoves([x, y]) : []); | |
407 | return super.getPossibleMovesFrom([x, y]); | |
408 | } | |
409 | ||
410 | getAllValidMoves() { | |
411 | if (this.movesCount >= 6) return super.getAllValidMoves(); | |
412 | let moves = []; | |
413 | const color = this.turn; | |
414 | for (let i = 0; i < V.RESERVE_PIECES.length; i++) { | |
415 | moves = moves.concat( | |
416 | this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i]) | |
417 | ); | |
418 | } | |
419 | return moves; | |
420 | } | |
421 | ||
422 | atLeastOneMove() { | |
423 | if (this.movesCount <= 5) return true; | |
424 | return super.atLeastOneMove(); | |
425 | } | |
426 | ||
427 | isAttacked(sq, color) { | |
428 | if (super.isAttacked(sq, color)) return true; | |
429 | if ( | |
430 | this.extraPieces.includes(V.LEOPARD) && | |
431 | this.isAttackedByLeopard(sq, color) | |
432 | ) { | |
433 | return true; | |
434 | } | |
435 | if ( | |
436 | this.extraPieces.includes(V.CANNON) && | |
437 | this.isAttackedByCannon(sq, color) | |
438 | ) { | |
439 | return true; | |
440 | } | |
441 | if ( | |
442 | this.extraPieces.includes(V.UNICORN) && | |
443 | this.isAttackedByUnicorn(sq, color) | |
444 | ) { | |
445 | return true; | |
446 | } | |
447 | if ( | |
448 | this.extraPieces.includes(V.ELEPHANT) && | |
449 | this.isAttackedByElephant(sq, color) | |
450 | ) { | |
451 | return true; | |
452 | } | |
453 | if ( | |
454 | this.extraPieces.includes(V.HAWK) && | |
455 | this.isAttackedByHawk(sq, color) | |
456 | ) { | |
457 | return true; | |
458 | } | |
459 | if ( | |
460 | this.extraPieces.includes(V.FORTRESS) && | |
461 | this.isAttackedByFortress(sq, color) | |
462 | ) { | |
463 | return true; | |
464 | } | |
465 | if ( | |
466 | this.extraPieces.includes(V.SPIDER) && | |
467 | this.isAttackedBySpider(sq, color) | |
468 | ) { | |
469 | return true; | |
470 | } | |
471 | return false; | |
472 | } | |
473 | ||
3208c667 BA |
474 | isAttackedByLeopard(sq, color) { |
475 | return ( | |
476 | super.isAttackedBySlideNJump( | |
4313762d | 477 | sq, color, V.LEOPARD, V.steps[V.KNIGHT], 1) || |
3208c667 BA |
478 | this.isAttackedBySlideNJump(sq, color, V.LEOPARD, V.steps[V.BISHOP], 2) |
479 | ); | |
480 | } | |
481 | ||
482 | isAttackedByCannon(sq, color) { | |
483 | const steps = | |
484 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]) | |
485 | .concat(V.Leap2Ortho).concat(V.HorizontalKnight); | |
4313762d | 486 | return super.isAttackedBySlideNJump(sq, color, V.CANNON, steps, 1); |
3208c667 BA |
487 | } |
488 | ||
489 | isAttackedByUnicorn(sq, color) { | |
490 | const steps = V.steps[V.KNIGHT].concat(V.CamelSteps) | |
491 | return ( | |
4313762d | 492 | super.isAttackedBySlideNJump(sq, color, V.UNICORN, steps, 1) |
3208c667 BA |
493 | ); |
494 | } | |
495 | ||
496 | isAttackedByElephant(sq, color) { | |
497 | const steps = | |
498 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]) | |
499 | .concat(V.Leap2Ortho) | |
500 | .concat(V.Leap2Diago); | |
501 | return ( | |
4313762d | 502 | super.isAttackedBySlideNJump(sq, color, V.ELEPHANT, steps, 1) |
3208c667 BA |
503 | ); |
504 | } | |
505 | ||
506 | isAttackedByHawk(sq, color) { | |
507 | const steps = | |
508 | V.Leap2Ortho.concat(V.Leap2Diago) | |
509 | .concat(V.Leap3Ortho).concat(V.Leap3Diago); | |
4313762d | 510 | return super.isAttackedBySlideNJump(sq, color, V.HAWK, steps, 1); |
3208c667 BA |
511 | } |
512 | ||
513 | isAttackedByFortress(sq, color) { | |
514 | const steps = V.Leap2Ortho.concat(V.VerticalKnight) | |
515 | return ( | |
4313762d | 516 | super.isAttackedBySlideNJump(sq, color, V.FORTRESS, steps, 1) || |
3208c667 BA |
517 | this.isAttackedBySlideNJump(sq, color, V.FORTRESS, V.steps[V.BISHOP], 3) |
518 | ); | |
519 | } | |
520 | ||
521 | isAttackedBySpider(sq, color) { | |
522 | const steps = V.Leap2Ortho.concat(V.steps[V.KNIGHT]) | |
523 | return ( | |
4313762d | 524 | super.isAttackedBySlideNJump(sq, color, V.SPIDER, steps, 1) || |
3208c667 BA |
525 | this.isAttackedBySlideNJump(sq, color, V.SPIDER, V.steps[V.BISHOP], 2) |
526 | ); | |
527 | } | |
528 | ||
529 | getCheckSquares() { | |
530 | if (this.movesCount <= 6) return []; | |
531 | return super.getCheckSquares(); | |
532 | } | |
533 | ||
534 | // At movesCount == 0,1: show full reserves [minus chosen piece1] | |
535 | // At movesCount == 2,3: show reserve with only 2 selected pieces | |
536 | // At movesCount == 4,5: show reserve with only piece2 | |
537 | // Then, no reserve. | |
538 | postPlay(move) { | |
539 | if (this.movesCount > 6) super.postPlay(move); | |
540 | else { | |
541 | switch (this.movesCount) { | |
542 | case 1: | |
543 | this.reserve['w'][move.appear[0].p]--; | |
544 | this.reserve['b'][move.appear[0].p]--; | |
545 | this.extraPieces[0] = move.appear[0].p; | |
546 | break; | |
547 | case 2: | |
548 | this.extraPieces[1] = move.appear[0].p; | |
549 | for (let p of V.RESERVE_PIECES) { | |
550 | const resVal = (this.extraPieces.includes(p) ? 1 : 0); | |
551 | this.reserve['w'][p] = resVal; | |
552 | this.reserve['b'][p] = resVal; | |
553 | } | |
554 | break; | |
555 | case 3: | |
556 | this.reserve['w'][this.extraPieces[0]]--; | |
557 | break; | |
558 | case 4: | |
559 | this.reserve['b'][this.extraPieces[0]]--; | |
560 | break; | |
561 | case 5: | |
562 | this.reserve['w'][this.extraPieces[1]]--; | |
563 | break; | |
564 | case 6: | |
565 | this.reserve = null; | |
566 | this.board[3][3] = ""; | |
567 | this.board[3][4] = ""; | |
568 | break; | |
569 | } | |
570 | } | |
571 | } | |
572 | ||
573 | postUndo(move) { | |
574 | if (this.movesCount >= 6) super.postUndo(move); | |
575 | else { | |
576 | switch (this.movesCount) { | |
577 | case 0: | |
578 | this.reserve['w'][move.appear[0].p]++; | |
579 | this.reserve['b'][move.appear[0].p]++; | |
580 | this.extraPieces[0] = '-'; | |
581 | break; | |
582 | case 1: | |
583 | this.extraPieces[1] = '-'; | |
584 | for (let p of V.RESERVE_PIECES) { | |
585 | const resVal = (p != this.extraPieces[0] ? 1 : 0); | |
586 | this.reserve['w'][p] = resVal; | |
587 | this.reserve['b'][p] = resVal; | |
588 | } | |
589 | break; | |
590 | case 2: | |
591 | this.reserve['w'][this.extraPieces[0]]++; | |
592 | break; | |
593 | case 3: | |
594 | this.reserve['b'][this.extraPieces[0]]++; | |
595 | break; | |
596 | case 4: | |
597 | this.reserve['w'][this.extraPieces[1]]++; | |
598 | break; | |
599 | case 5: | |
600 | this.reserve = { w: {}, b: {} }; | |
601 | for (let c of ['w', 'b']) | |
602 | V.RESERVE_PIECES.forEach(p => this.reserve[c][p] = 0); | |
603 | this.reserve['b'][this.extraPieces[1]] = 1; | |
604 | this.board[3][3] = 'b' + this.extraPieces[1]; | |
605 | this.board[3][4] = 'w' + this.extraPieces[0]; | |
606 | break; | |
607 | } | |
608 | } | |
609 | } | |
610 | ||
611 | getComputerMove() { | |
612 | if (this.movesCount >= 6) return super.getComputerMove(); | |
613 | // Choose a move at random | |
614 | const moves = this.getAllValidMoves(); | |
615 | return moves[randInt(moves.length)]; | |
616 | } | |
617 | ||
618 | static get SEARCH_DEPTH() { | |
619 | return 2; | |
620 | } | |
621 | ||
622 | evalPosition() { | |
623 | let evaluation = 0; | |
624 | for (let i = 0; i < V.size.x; i++) { | |
625 | for (let j = 0; j < V.size.y; j++) { | |
626 | if (this.board[i][j] != V.EMPTY) { | |
627 | const sign = this.getColor(i, j) == "w" ? 1 : -1; | |
628 | const piece = this.getPiece(i, j); | |
629 | evaluation += sign * V.VALUES[piece]; | |
630 | const symbol = this.board[i][j][1]; | |
631 | if (V.AUGMENTED_PIECES.includes(symbol)) { | |
632 | const extraPiece = this.getExtraPiece(symbol); | |
633 | evaluation += sign * V.VALUES[extraPiece] | |
634 | } | |
635 | } | |
636 | } | |
637 | } | |
638 | return evaluation; | |
639 | } | |
640 | ||
641 | static get VALUES() { | |
642 | return Object.assign( | |
643 | { | |
644 | d: 6.7, | |
645 | w: 7.5, | |
646 | x: 5.6, | |
647 | e: 6.3, | |
648 | h: 5.5, | |
649 | f: 7.6, | |
650 | y: 8.15 | |
651 | }, | |
652 | ChessRules.VALUES | |
653 | ); | |
654 | } | |
655 | ||
656 | static get ExtraDictionary() { | |
657 | return { | |
658 | [V.LEOPARD]: { prefix: 'L', name: "Leopard" }, | |
659 | [V.CANNON]: { prefix: 'C', name: "Cannon" }, | |
660 | [V.UNICORN]: { prefix: 'U', name: "Unicorn" }, | |
661 | [V.ELEPHANT]: { prefix: 'E', name: "Elephant" }, | |
662 | [V.HAWK]: { prefix: 'H', name: "Hawk" }, | |
663 | [V.FORTRESS]: { prefix: 'F', name: "Fortress" }, | |
664 | [V.SPIDER]: { prefix: 'S', name: "Spider" } | |
665 | } | |
666 | } | |
667 | ||
668 | getNotation(move) { | |
669 | if (this.movesCount <= 5) { | |
670 | if (this.movesCount <= 1) | |
671 | return V.ExtraDictionary[move.appear[0].p].name; | |
672 | // Put something on the board: | |
673 | return ( | |
674 | V.ExtraDictionary[V.RESERVE_PIECES[move.start.y]].prefix + | |
675 | "@" + V.CoordsToSquare(move.end) | |
676 | ); | |
677 | } | |
678 | let notation = ""; | |
679 | if ( | |
680 | V.AUGMENTED_PIECES.includes(move.vanish[0].p) || | |
681 | ( | |
682 | move.vanish.length >= 2 && | |
683 | V.AUGMENTED_PIECES.includes(move.vanish[1].p) | |
684 | ) | |
685 | ) { | |
686 | // Simplify move before calling super.getNotation() | |
687 | let smove = JSON.parse(JSON.stringify(move)); | |
688 | if (ChessRules.PIECES.includes(move.vanish[0].p)) { | |
689 | // Castle with an augmented rook | |
690 | smove.appear.pop(); | |
691 | smove.vanish[1].p = smove.appear[1].p; | |
692 | } | |
693 | else { | |
694 | // Moving an augmented piece | |
695 | smove.appear.pop(); | |
696 | smove.vanish[0].p = smove.appear[0].p; | |
697 | if ( | |
698 | smove.vanish.length == 2 && | |
699 | smove.vanish[0].c == smove.vanish[1].c && | |
700 | V.AUGMENTED_PIECES.includes(move.vanish[1].p) | |
701 | ) { | |
702 | // Castle with an augmented rook | |
703 | smove.appear.pop(); | |
704 | smove.vanish[1].p = smove.appear[1].p; | |
705 | } | |
706 | } | |
707 | notation = super.getNotation(smove); | |
708 | } | |
709 | // Else, more common case: | |
710 | notation = super.getNotation(move); | |
711 | const pieceSymbol = notation.charAt(0).toLowerCase(); | |
712 | if (move.vanish[0].p != V.PAWN && V.RESERVE_PIECES.includes(pieceSymbol)) | |
713 | notation = V.ExtraDictionary[pieceSymbol].prefix + notation.substr(1); | |
714 | return notation; | |
715 | } | |
716 | ||
717 | }; |