Fix Cwda (track kings)
[vchess.git] / client / src / variants / Cwda.js
CommitLineData
4313762d 1import { ChessRules } from "@/base_rules";
801e2870 2
cee75a57 3export class CwdaRules extends ChessRules {
7e8a7ea1 4
4313762d
BA
5 static get Options() {
6 return {
7 select: ChessRules.Options.select.concat([
8 {
9 label: "Army 1",
10 variable: "army1",
11 defaut: 'C',
12 options: [
13 { label: "Colorbound Clobberers", value: 'C' },
14 { label: "Nutty Knights", value: 'N' },
15 { label: "Remarkable Rookies", value: 'R' },
16 { label: "Fide", value: 'F' }
17 ]
18 },
19 {
20 label: "Army 2",
21 variable: "army2",
22 defaut: 'C',
23 options: [
24 { label: "Colorbound Clobberers", value: 'C' },
25 { label: "Nutty Knights", value: 'N' },
26 { label: "Remarkable Rookies", value: 'R' },
27 { label: "Fide", value: 'F' }
28 ]
29 }
30 ])
31 };
32 }
33
34 static AbbreviateOptions(opts) {
35 return opts["army1"] + opts["army2"];
36 }
37
38 static IsValidOptions(opts) {
39 // Both armies filled, avoid Fide vs Fide
40 return (
41 opts.army1 && opts.army2 &&
42 (opts.army1 != 'F' || opts.army2 != 'F')
801e2870
BA
43 );
44 }
45
46 getPpath(b) {
4313762d
BA
47 return (ChessRules.PIECES.includes(b[1]) ? "" : "Cwda/") + b;
48 }
49
50 static get PiecesMap() {
51 return {
52 // Colorbound Clobberers
53 'C': {
54 'r': 'd',
55 'n': 'w',
56 'b': 'f',
57 'q': 'c',
74afb57d
BA
58 'k': 'k',
59 'p': 'p'
4313762d
BA
60 },
61 // Nutty Knights
62 'N': {
63 'r': 'g',
64 'n': 'i',
65 'b': 't',
66 'q': 'l',
74afb57d
BA
67 'k': 'k', //TODO: e
68 'p': 'p' //TODO: v
4313762d
BA
69 },
70 // Remarkable Rookies
71 'R': {
72 'r': 's',
73 'n': 'y',
74 'b': 'h',
75 'q': 'o',
74afb57d
BA
76 'k': 'a',
77 'p': 'u'
4313762d 78 }
801e2870 79 };
4313762d 80 }
801e2870 81
4313762d
BA
82 static GenRandInitFen(options) {
83 const baseFen = ChessRules.GenRandInitFen(options.randomness);
74afb57d 84 let blackLine = baseFen.substr(0, 8), blackPawns = "pppppppp";
4313762d
BA
85 if (options.army2 != 'F') {
86 blackLine = blackLine.split('')
87 .map(p => V.PiecesMap[options.army2][p]).join('');
74afb57d 88 blackPawns = V.PiecesMap[options.army2]['p'].repeat(8);
4313762d 89 }
74afb57d 90 let whiteLine = baseFen.substr(35, 8), whitePawns = "PPPPPPPP";
4313762d
BA
91 if (options.army1 != 'F') {
92 whiteLine = whiteLine.split('')
93 .map(p => V.PiecesMap[options.army1][p.toLowerCase()])
94 .join('').toUpperCase();
74afb57d 95 whitePawns = V.PiecesMap[options.army1]['p'].toUpperCase().repeat(8);
4313762d 96 }
801e2870 97 return (
74afb57d
BA
98 blackLine + "/" + blackPawns +
99 baseFen.substring(17, 26) +
100 whitePawns + "/" + whiteLine +
4313762d 101 baseFen.substr(43) + " " + options.army1 + options.army2
801e2870
BA
102 );
103 }
104
4313762d
BA
105 setOtherVariables(fen) {
106 super.setOtherVariables(fen);
107 const armies = V.ParseFen(fen).armies;
108 this.army1 = armies.charAt(0);
109 this.army2 = armies.charAt(1);
110 }
111
0652acd9
BA
112 scanKings(fen) {
113 this.kingPos = { w: [-1, -1], b: [-1, -1] };
114 const fenRows = V.ParseFen(fen).position.split("/");
115 for (let i = 0; i < fenRows.length; i++) {
116 let k = 0;
117 for (let j = 0; j < fenRows[i].length; j++) {
118 const newChar = fenRows[i].charAt(j);
119 if (['a', 'e', 'k'].includes(newChar)) this.kingPos["b"] = [i, k];
120 else if (['A', 'E', 'K'].includes(newChar)) this.kingPos["w"] = [i, k];
121 else {
122 const num = parseInt(fenRows[i].charAt(j), 10);
123 if (!isNaN(num)) k += num - 1;
124 }
125 k++;
126 }
127 }
128 }
129
4313762d
BA
130 static ParseFen(fen) {
131 return Object.assign(
132 { armies: fen.split(" ")[5] },
133 ChessRules.ParseFen(fen)
134 );
135 }
136
137 static IsGoodFen(fen) {
138 if (!ChessRules.IsGoodFen(fen)) return false;
139 const armies = V.ParseFen(fen).armies;
10cceb25 140 return (!!armies && armies.match(/^[CNRF]{2,2}$/));
4313762d
BA
141 }
142
143 getFen() {
144 return super.getFen() + " " + this.army1 + this.army2;
145 }
146
801e2870
BA
147 static get C_ROOK() {
148 return 'd';
149 }
150 static get C_KNIGHT() {
4313762d 151 return 'w';
801e2870
BA
152 }
153 static get C_BISHOP() {
4313762d 154 return 'f';
801e2870
BA
155 }
156 static get C_QUEEN() {
4313762d
BA
157 return 'c';
158 }
159 static get N_ROOK() {
160 return 'g';
161 }
162 static get N_KNIGHT() {
163 return 'i';
164 }
165 static get N_BISHOP() {
166 return 't';
167 }
168 static get N_QUEEN() {
169 return 'l';
170 }
74afb57d
BA
171 static get N_KING() {
172 return 'e';
173 }
174 static get N_PAWN() {
175 return 'v';
176 }
4313762d 177 static get R_ROOK() {
801e2870
BA
178 return 's';
179 }
4313762d
BA
180 static get R_KNIGHT() {
181 return 'y';
182 }
183 static get R_BISHOP() {
184 return 'h';
185 }
186 static get R_QUEEN() {
187 return 'o';
188 }
74afb57d
BA
189 static get R_KING() {
190 return 'a';
191 }
192 static get R_PAWN() {
193 return 'u';
194 }
195
196 getPiece(x, y) {
197 const p = this.board[x][y][1];
198 if (['u', 'v'].includes(p)) return 'p';
199 if (['a', 'e'].includes(p)) return 'k';
200 return p;
201 }
801e2870
BA
202
203 static get PIECES() {
4313762d 204 return ChessRules.PIECES.concat(
74afb57d
BA
205 [
206 V.C_ROOK, V.C_KNIGHT, V.C_BISHOP, V.C_QUEEN,
207 V.N_ROOK, V.N_KNIGHT, V.N_BISHOP, V.N_QUEEN, V.N_KING, V.N_PAWN,
208 V.R_ROOK, V.R_KNIGHT, V.R_BISHOP, V.R_QUEEN, V.R_KING, V.R_PAWN
209 ]
210 );
801e2870
BA
211 }
212
f25f375a
BA
213 getEpSquare(moveOrSquare) {
214 if (!moveOrSquare) return undefined; //TODO: necessary line?!
215 if (typeof moveOrSquare === "string") {
216 const square = moveOrSquare;
217 if (square == "-") return undefined;
218 return V.SquareToCoords(square);
219 }
220 // Argument is a move:
221 const move = moveOrSquare;
222 const s = move.start,
223 e = move.end;
224 if (
225 s.y == e.y &&
226 Math.abs(s.x - e.x) == 2 &&
227 ['p', 'u', 'v'].includes(move.appear[0].p)
228 ) {
229 return {
230 x: (s.x + e.x) / 2,
231 y: s.y
232 };
233 }
234 return undefined; //default
235 }
236
4313762d
BA
237 getPotentialMovesFrom(sq) {
238 switch (this.getPiece(sq[0], sq[1])) {
239 case V.C_ROOK: return this.getPotentialC_rookMoves(sq);
240 case V.C_KNIGHT: return this.getPotentialC_knightMoves(sq);
241 case V.C_BISHOP: return this.getPotentialC_bishopMoves(sq);
242 case V.C_QUEEN: return this.getPotentialC_queenMoves(sq);
243 case V.N_ROOK: return this.getPotentialN_rookMoves(sq);
244 case V.N_KNIGHT: return this.getPotentialN_knightMoves(sq);
245 case V.N_BISHOP: return this.getPotentialN_bishopMoves(sq);
246 case V.N_QUEEN: return this.getPotentialN_queenMoves(sq);
247 case V.R_ROOK: return this.getPotentialR_rookMoves(sq);
248 case V.R_KNIGHT: return this.getPotentialR_knightMoves(sq);
249 case V.R_BISHOP: return this.getPotentialR_bishopMoves(sq);
250 case V.R_QUEEN: return this.getPotentialR_queenMoves(sq);
251 case V.PAWN: {
252 // Can promote in anything from the two current armies
253 let promotions = [];
254 for (let army of ["army1", "army2"]) {
255 if (army == "army2" && this.army2 == this.army1) break;
256 switch (this[army]) {
257 case 'C': {
258 Array.prototype.push.apply(promotions,
259 [V.C_ROOK, V.C_KNIGHT, V.C_BISHOP, V.C_QUEEN]);
260 break;
261 }
262 case 'N': {
263 Array.prototype.push.apply(promotions,
264 [V.N_ROOK, V.N_KNIGHT, V.N_BISHOP, V.N_QUEEN]);
265 break;
266 }
267 case 'R': {
268 Array.prototype.push.apply(promotions,
269 [V.R_ROOK, V.R_KNIGHT, V.R_BISHOP, V.R_QUEEN]);
270 break;
271 }
272 case 'F': {
273 Array.prototype.push.apply(promotions,
274 [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]);
275 break;
276 }
277 }
278 }
279 return super.getPotentialPawnMoves(sq, promotions);
280 }
281 default: return super.getPotentialMovesFrom(sq);
801e2870
BA
282 }
283 return [];
284 }
285
286 static get steps() {
287 return Object.assign(
801e2870
BA
288 {
289 // Dabbabah
290 'd': [
291 [-2, 0],
292 [0, -2],
293 [2, 0],
294 [0, 2]
295 ],
296 // Alfil
297 'a': [
298 [2, 2],
299 [2, -2],
300 [-2, 2],
301 [-2, -2]
302 ],
303 // Ferz
304 'f': [
305 [1, 1],
306 [1, -1],
307 [-1, 1],
308 [-1, -1]
4313762d
BA
309 ],
310 // Wazir
311 'w': [
312 [-1, 0],
313 [0, -1],
314 [1, 0],
315 [0, 1]
316 ],
317 // Threeleaper
318 '$3': [
319 [-3, 0],
320 [0, -3],
321 [3, 0],
322 [0, 3]
323 ],
324 // Narrow knight
325 '$n': [
326 [-2, -1],
327 [-2, 1],
328 [2, -1],
329 [2, 1]
801e2870 330 ]
4313762d
BA
331 },
332 ChessRules.steps,
801e2870
BA
333 );
334 }
335
336 getPotentialC_rookMoves(sq) {
337 return (
4313762d
BA
338 this.getSlideNJumpMoves(sq, V.steps.b).concat(
339 this.getSlideNJumpMoves(sq, V.steps.d, 1))
801e2870
BA
340 );
341 }
342
343 getPotentialC_knightMoves(sq) {
344 return (
4313762d
BA
345 this.getSlideNJumpMoves(sq, V.steps.a, 1).concat(
346 this.getSlideNJumpMoves(sq, V.steps.r, 1))
801e2870
BA
347 );
348 }
349
350 getPotentialC_bishopMoves(sq) {
351 return (
4313762d
BA
352 this.getSlideNJumpMoves(sq, V.steps.d, 1).concat(
353 this.getSlideNJumpMoves(sq, V.steps.a, 1)).concat(
354 this.getSlideNJumpMoves(sq, V.steps.b, 1))
801e2870
BA
355 );
356 }
357
358 getPotentialC_queenMoves(sq) {
359 return (
4313762d
BA
360 this.getSlideNJumpMoves(sq, V.steps.b).concat(
361 this.getSlideNJumpMoves(sq, V.steps.n, 1))
362 );
363 }
364
365 getPotentialN_rookMoves(sq) {
366 const c = this.turn;
367 const rookSteps = [ [0, -1], [0, 1], [c == 'w' ? -1 : 1, 0] ];
368 const backward = (c == 'w' ? 1 : -1);
369 const kingSteps = [ [backward, -1], [backward, 0], [backward, 1] ];
370 return (
371 this.getSlideNJumpMoves(sq, rookSteps).concat(
372 this.getSlideNJumpMoves(sq, kingSteps, 1))
373 );
374 }
375
376 getPotentialN_knightMoves(sq) {
4313762d
BA
377 return (
378 this.getSlideNJumpMoves(sq, V.steps.$n, 1).concat(
902378e6 379 this.getSlideNJumpMoves(sq, V.steps.f, 1))
4313762d
BA
380 );
381 }
382
383 getPotentialN_bishopMoves(sq) {
384 const backward = (this.turn == 'w' ? 1 : -1);
385 const kingSteps = [
386 [0, -1], [0, 1], [backward, -1], [backward, 0], [backward, 1]
387 ];
388 const forward = -backward;
389 const knightSteps = [
390 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
391 ];
392 return (
393 this.getSlideNJumpMoves(sq, knightSteps, 1).concat(
394 this.getSlideNJumpMoves(sq, kingSteps, 1))
395 );
396 }
397
398 getPotentialN_queenMoves(sq) {
399 const backward = (this.turn == 'w' ? 1 : -1);
400 const forward = -backward;
401 const kingSteps = [
402 [forward, -1], [forward, 1],
403 [backward, -1], [backward, 0], [backward, 1]
404 ];
405 const knightSteps = [
406 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
407 ];
408 const rookSteps = [ [0, -1], [0, 1], [forward, 0] ];
409 return (
410 this.getSlideNJumpMoves(sq, rookSteps).concat(
411 this.getSlideNJumpMoves(sq, kingSteps, 1)).concat(
412 this.getSlideNJumpMoves(sq, knightSteps, 1))
413 );
414 }
415
416 getPotentialR_rookMoves(sq) {
417 return this.getSlideNJumpMoves(sq, V.steps.r, 4);
418 }
419
420 getPotentialR_knightMoves(sq) {
421 return (
422 this.getSlideNJumpMoves(sq, V.steps.d, 1).concat(
423 this.getSlideNJumpMoves(sq, V.steps.w, 1))
424 );
425 }
426
427 getPotentialR_bishopMoves(sq) {
428 return (
429 this.getSlideNJumpMoves(sq, V.steps.d, 1).concat(
430 this.getSlideNJumpMoves(sq, V.steps.f, 1)).concat(
431 this.getSlideNJumpMoves(sq, V.steps.$3, 1))
432 );
433 }
434
435 getPotentialR_queenMoves(sq) {
436 return (
437 this.getSlideNJumpMoves(sq, V.steps.r).concat(
438 this.getSlideNJumpMoves(sq, V.steps.n, 1))
801e2870
BA
439 );
440 }
441
801e2870 442 getCastleMoves([x, y]) {
7e8a7ea1 443 const color = this.getColor(x, y);
4313762d
BA
444 let finalSquares = [ [2, 3], [V.size.y - 2, V.size.y - 3] ];
445 if (
446 (color == 'w' && this.army1 == 'C') ||
447 (color == 'b' && this.army2 == 'C')
448 ) {
449 // Colorbound castle long in an unusual way:
450 finalSquares[0] = [1, 2];
451 }
7e8a7ea1 452 return super.getCastleMoves([x, y], finalSquares);
801e2870
BA
453 }
454
455 isAttacked(sq, color) {
4313762d
BA
456 if (super.isAttackedByPawn(sq, color) || super.isAttackedByKing(sq, color))
457 return true;
458 for (let army of ['C', 'N', 'R', 'F']) {
459 if (
460 [this.army1, this.army2].includes(army) &&
461 (
462 this["isAttackedBy" + army + "_rook"](sq, color) ||
463 this["isAttackedBy" + army + "_knight"](sq, color) ||
464 this["isAttackedBy" + army + "_bishop"](sq, color) ||
465 this["isAttackedBy" + army + "_queen"](sq, color)
466 )
467 ) {
468 return true;
469 }
470 }
471 return false;
801e2870
BA
472 }
473
474 isAttackedByC_rook(sq, color) {
475 return (
4313762d
BA
476 this.isAttackedBySlideNJump(sq, color, V.C_ROOK, V.steps.b) ||
477 this.isAttackedBySlideNJump(sq, color, V.C_ROOK, V.steps.d, 1)
801e2870
BA
478 );
479 }
480
481 isAttackedByC_knight(sq, color) {
482 return (
4313762d
BA
483 this.isAttackedBySlideNJump(sq, color, V.C_KNIGHT, V.steps.r, 1) ||
484 this.isAttackedBySlideNJump(sq, color, V.C_KNIGHT, V.steps.a, 1)
801e2870
BA
485 );
486 }
487
488 isAttackedByC_bishop(sq, color) {
489 return (
4313762d
BA
490 this.isAttackedBySlideNJump(sq, color, V.C_BISHOP, V.steps.d, 1) ||
491 this.isAttackedBySlideNJump(sq, color, V.C_BISHOP, V.steps.a, 1) ||
492 this.isAttackedBySlideNJump(sq, color, V.C_BISHOP, V.steps.f, 1)
801e2870
BA
493 );
494 }
495
496 isAttackedByC_queen(sq, color) {
497 return (
4313762d
BA
498 this.isAttackedBySlideNJump(sq, color, V.C_QUEEN, V.steps.b) ||
499 this.isAttackedBySlideNJump(sq, color, V.C_QUEEN, V.steps.n, 1)
500 );
501 }
502
503 isAttackedByN_rook(sq, color) {
504 const rookSteps = [ [0, -1], [0, 1], [color == 'w' ? 1 : -1, 0] ];
505 const backward = (color == 'w' ? -1 : 1);
506 const kingSteps = [ [backward, -1], [backward, 0], [backward, 1] ];
507 return (
508 this.isAttackedBySlideNJump(sq, color, V.N_ROOK, rookSteps) ||
509 this.isAttackedBySlideNJump(sq, color, V.N_ROOK, kingSteps, 1)
510 );
511 }
512
513 isAttackedByN_knight(sq, color) {
4313762d 514 return (
902378e6
BA
515 this.isAttackedBySlideNJump(sq, color, V.N_KNIGHT, V.steps.$n, 1) ||
516 this.isAttackedBySlideNJump(sq, color, V.N_KNIGHT, V.steps.f, 1)
4313762d
BA
517 );
518 }
519
520 isAttackedByN_bishop(sq, color) {
521 const backward = (color == 'w' ? -1 : 1);
522 const kingSteps = [
523 [0, -1], [0, 1], [backward, -1], [backward, 0], [backward, 1]
524 ];
525 const forward = -backward;
526 const knightSteps = [
527 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
528 ];
529 return (
530 this.isAttackedBySlideNJump(sq, color, V.N_BISHOP, knightSteps, 1) ||
531 this.isAttackedBySlideNJump(sq, color, V.N_BISHOP, kingSteps, 1)
532 );
533 }
534
535 isAttackedByN_queen(sq, color) {
536 const backward = (color == 'w' ? -1 : 1);
537 const forward = -backward;
538 const kingSteps = [
539 [forward, -1], [forward, 1],
540 [backward, -1], [backward, 0], [backward, 1]
541 ];
542 const knightSteps = [
543 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
544 ];
545 const rookSteps = [ [0, -1], [0, 1], [forward, 0] ];
546 return (
547 this.isAttackedBySlideNJump(sq, color, V.N_QUEEN, knightSteps, 1) ||
548 this.isAttackedBySlideNJump(sq, color, V.N_QUEEN, kingSteps, 1) ||
549 this.isAttackedBySlideNJump(sq, color, V.N_QUEEN, rookSteps)
550 );
551 }
552
553 isAttackedByR_rook(sq, color) {
554 return this.isAttackedBySlideNJump(sq, color, V.R_ROOK, V.steps.r, 4);
555 }
556
557 isAttackedByR_knight(sq, color) {
558 return (
559 this.isAttackedBySlideNJump(sq, color, V.R_KNIGHT, V.steps.d, 1) ||
560 this.isAttackedBySlideNJump(sq, color, V.R_KNIGHT, V.steps.w, 1)
561 );
562 }
563
564 isAttackedByR_bishop(sq, color) {
565 return (
566 this.isAttackedBySlideNJump(sq, color, V.R_BISHOP, V.steps.d, 1) ||
567 this.isAttackedBySlideNJump(sq, color, V.R_BISHOP, V.steps.f, 1) ||
568 this.isAttackedBySlideNJump(sq, color, V.R_BISHOP, V.steps.$3, 1)
569 );
570 }
571
572 isAttackedByR_queen(sq, color) {
573 return (
574 this.isAttackedBySlideNJump(sq, color, V.R_QUEEN, V.steps.r) ||
575 this.isAttackedBySlideNJump(sq, color, V.R_QUEEN, V.steps.n, 1)
801e2870
BA
576 );
577 }
578
b2f43cc3
BA
579 // [HACK] So that the function above works also on Fide army:
580 isAttackedByF_rook(sq, color) {
581 return super.isAttackedByRook(sq, color);
582 }
583 isAttackedByF_knight(sq, color) {
584 return super.isAttackedByKnight(sq, color);
585 }
586 isAttackedByF_bishop(sq, color) {
587 return super.isAttackedByBishop(sq, color);
588 }
589 isAttackedByF_queen(sq, color) {
590 return super.isAttackedByQueen(sq, color);
591 }
592
0652acd9
BA
593 postPlay(move) {
594 const c = V.GetOppCol(this.turn);
595 const piece = move.appear[0].p;
596 // Update king position + flags
597 if (['k', 'a', 'e'].includes(piece)) {
598 this.kingPos[c][0] = move.appear[0].x;
599 this.kingPos[c][1] = move.appear[0].y;
600 this.castleFlags[c] = [V.size.y, V.size.y];
601 }
602 // Next call is still required because the king may eat an opponent's rook
603 // TODO: castleFlags will be turned off twice then.
604 super.updateCastleFlags(move, piece);
605 }
606
607 postUndo(move) {
608 // (Potentially) Reset king position
609 const c = this.getColor(move.start.x, move.start.y);
610 const piece = move.appear[0].p;
611 if (['k', 'a', 'e'].includes(piece))
612 this.kingPos[c] = [move.start.x, move.start.y];
613 }
614
801e2870
BA
615 static get VALUES() {
616 return Object.assign(
801e2870
BA
617 {
618 d: 4,
4313762d
BA
619 w: 3,
620 f: 5,
621 c: 7,
622 g: 4,
623 i: 3,
624 t: 4,
625 l: 7,
626 s: 4,
627 y: 3,
628 h: 4,
629 o: 8
630 },
631 ChessRules.VALUES
801e2870
BA
632 );
633 }
48252022
BA
634
635 static get SEARCH_DEPTH() {
636 return 2;
637 }
7e8a7ea1 638
f25f375a
BA
639 getNotation(move) {
640 let notation = super.getNotation(move);
641 if (['u', 'v'].includes(move.appear[0].p))
642 notation = notation.slice(0, -2);
643 return notation;
644 }
645
801e2870 646};