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