c5ecf092e64fdab34db0972608efd0c8786fd310
[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 getPotentialMovesFrom(sq) {
196 switch (this.getPiece(sq[0], sq[1])) {
197 case V.C_ROOK: return this.getPotentialC_rookMoves(sq);
198 case V.C_KNIGHT: return this.getPotentialC_knightMoves(sq);
199 case V.C_BISHOP: return this.getPotentialC_bishopMoves(sq);
200 case V.C_QUEEN: return this.getPotentialC_queenMoves(sq);
201 case V.N_ROOK: return this.getPotentialN_rookMoves(sq);
202 case V.N_KNIGHT: return this.getPotentialN_knightMoves(sq);
203 case V.N_BISHOP: return this.getPotentialN_bishopMoves(sq);
204 case V.N_QUEEN: return this.getPotentialN_queenMoves(sq);
205 case V.R_ROOK: return this.getPotentialR_rookMoves(sq);
206 case V.R_KNIGHT: return this.getPotentialR_knightMoves(sq);
207 case V.R_BISHOP: return this.getPotentialR_bishopMoves(sq);
208 case V.R_QUEEN: return this.getPotentialR_queenMoves(sq);
209 case V.PAWN: {
210 // Can promote in anything from the two current armies
211 let promotions = [];
212 for (let army of ["army1", "army2"]) {
213 if (army == "army2" && this.army2 == this.army1) break;
214 switch (this[army]) {
215 case 'C': {
216 Array.prototype.push.apply(promotions,
217 [V.C_ROOK, V.C_KNIGHT, V.C_BISHOP, V.C_QUEEN]);
218 break;
219 }
220 case 'N': {
221 Array.prototype.push.apply(promotions,
222 [V.N_ROOK, V.N_KNIGHT, V.N_BISHOP, V.N_QUEEN]);
223 break;
224 }
225 case 'R': {
226 Array.prototype.push.apply(promotions,
227 [V.R_ROOK, V.R_KNIGHT, V.R_BISHOP, V.R_QUEEN]);
228 break;
229 }
230 case 'F': {
231 Array.prototype.push.apply(promotions,
232 [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]);
233 break;
234 }
235 }
236 }
237 return super.getPotentialPawnMoves(sq, promotions);
238 }
239 default: return super.getPotentialMovesFrom(sq);
240 }
241 return [];
242 }
243
244 static get steps() {
245 return Object.assign(
246 {
247 // Dabbabah
248 'd': [
249 [-2, 0],
250 [0, -2],
251 [2, 0],
252 [0, 2]
253 ],
254 // Alfil
255 'a': [
256 [2, 2],
257 [2, -2],
258 [-2, 2],
259 [-2, -2]
260 ],
261 // Ferz
262 'f': [
263 [1, 1],
264 [1, -1],
265 [-1, 1],
266 [-1, -1]
267 ],
268 // Wazir
269 'w': [
270 [-1, 0],
271 [0, -1],
272 [1, 0],
273 [0, 1]
274 ],
275 // Threeleaper
276 '$3': [
277 [-3, 0],
278 [0, -3],
279 [3, 0],
280 [0, 3]
281 ],
282 // Narrow knight
283 '$n': [
284 [-2, -1],
285 [-2, 1],
286 [2, -1],
287 [2, 1]
288 ]
289 },
290 ChessRules.steps,
291 );
292 }
293
294 getPotentialC_rookMoves(sq) {
295 return (
296 this.getSlideNJumpMoves(sq, V.steps.b).concat(
297 this.getSlideNJumpMoves(sq, V.steps.d, 1))
298 );
299 }
300
301 getPotentialC_knightMoves(sq) {
302 return (
303 this.getSlideNJumpMoves(sq, V.steps.a, 1).concat(
304 this.getSlideNJumpMoves(sq, V.steps.r, 1))
305 );
306 }
307
308 getPotentialC_bishopMoves(sq) {
309 return (
310 this.getSlideNJumpMoves(sq, V.steps.d, 1).concat(
311 this.getSlideNJumpMoves(sq, V.steps.a, 1)).concat(
312 this.getSlideNJumpMoves(sq, V.steps.b, 1))
313 );
314 }
315
316 getPotentialC_queenMoves(sq) {
317 return (
318 this.getSlideNJumpMoves(sq, V.steps.b).concat(
319 this.getSlideNJumpMoves(sq, V.steps.n, 1))
320 );
321 }
322
323 getPotentialN_rookMoves(sq) {
324 const c = this.turn;
325 const rookSteps = [ [0, -1], [0, 1], [c == 'w' ? -1 : 1, 0] ];
326 const backward = (c == 'w' ? 1 : -1);
327 const kingSteps = [ [backward, -1], [backward, 0], [backward, 1] ];
328 return (
329 this.getSlideNJumpMoves(sq, rookSteps).concat(
330 this.getSlideNJumpMoves(sq, kingSteps, 1))
331 );
332 }
333
334 getPotentialN_knightMoves(sq) {
335 return (
336 this.getSlideNJumpMoves(sq, V.steps.$n, 1).concat(
337 this.getSlideNJumpMoves(sq, V.steps.f, 1))
338 );
339 }
340
341 getPotentialN_bishopMoves(sq) {
342 const backward = (this.turn == 'w' ? 1 : -1);
343 const kingSteps = [
344 [0, -1], [0, 1], [backward, -1], [backward, 0], [backward, 1]
345 ];
346 const forward = -backward;
347 const knightSteps = [
348 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
349 ];
350 return (
351 this.getSlideNJumpMoves(sq, knightSteps, 1).concat(
352 this.getSlideNJumpMoves(sq, kingSteps, 1))
353 );
354 }
355
356 getPotentialN_queenMoves(sq) {
357 const backward = (this.turn == 'w' ? 1 : -1);
358 const forward = -backward;
359 const kingSteps = [
360 [forward, -1], [forward, 1],
361 [backward, -1], [backward, 0], [backward, 1]
362 ];
363 const knightSteps = [
364 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
365 ];
366 const rookSteps = [ [0, -1], [0, 1], [forward, 0] ];
367 return (
368 this.getSlideNJumpMoves(sq, rookSteps).concat(
369 this.getSlideNJumpMoves(sq, kingSteps, 1)).concat(
370 this.getSlideNJumpMoves(sq, knightSteps, 1))
371 );
372 }
373
374 getPotentialR_rookMoves(sq) {
375 return this.getSlideNJumpMoves(sq, V.steps.r, 4);
376 }
377
378 getPotentialR_knightMoves(sq) {
379 return (
380 this.getSlideNJumpMoves(sq, V.steps.d, 1).concat(
381 this.getSlideNJumpMoves(sq, V.steps.w, 1))
382 );
383 }
384
385 getPotentialR_bishopMoves(sq) {
386 return (
387 this.getSlideNJumpMoves(sq, V.steps.d, 1).concat(
388 this.getSlideNJumpMoves(sq, V.steps.f, 1)).concat(
389 this.getSlideNJumpMoves(sq, V.steps.$3, 1))
390 );
391 }
392
393 getPotentialR_queenMoves(sq) {
394 return (
395 this.getSlideNJumpMoves(sq, V.steps.r).concat(
396 this.getSlideNJumpMoves(sq, V.steps.n, 1))
397 );
398 }
399
400 getCastleMoves([x, y]) {
401 const color = this.getColor(x, y);
402 let finalSquares = [ [2, 3], [V.size.y - 2, V.size.y - 3] ];
403 if (
404 (color == 'w' && this.army1 == 'C') ||
405 (color == 'b' && this.army2 == 'C')
406 ) {
407 // Colorbound castle long in an unusual way:
408 finalSquares[0] = [1, 2];
409 }
410 return super.getCastleMoves([x, y], finalSquares);
411 }
412
413 isAttacked(sq, color) {
414 if (super.isAttackedByPawn(sq, color) || super.isAttackedByKing(sq, color))
415 return true;
416 for (let army of ['C', 'N', 'R', 'F']) {
417 if (
418 [this.army1, this.army2].includes(army) &&
419 (
420 this["isAttackedBy" + army + "_rook"](sq, color) ||
421 this["isAttackedBy" + army + "_knight"](sq, color) ||
422 this["isAttackedBy" + army + "_bishop"](sq, color) ||
423 this["isAttackedBy" + army + "_queen"](sq, color)
424 )
425 ) {
426 return true;
427 }
428 }
429 return false;
430 }
431
432 isAttackedByC_rook(sq, color) {
433 return (
434 this.isAttackedBySlideNJump(sq, color, V.C_ROOK, V.steps.b) ||
435 this.isAttackedBySlideNJump(sq, color, V.C_ROOK, V.steps.d, 1)
436 );
437 }
438
439 isAttackedByC_knight(sq, color) {
440 return (
441 this.isAttackedBySlideNJump(sq, color, V.C_KNIGHT, V.steps.r, 1) ||
442 this.isAttackedBySlideNJump(sq, color, V.C_KNIGHT, V.steps.a, 1)
443 );
444 }
445
446 isAttackedByC_bishop(sq, color) {
447 return (
448 this.isAttackedBySlideNJump(sq, color, V.C_BISHOP, V.steps.d, 1) ||
449 this.isAttackedBySlideNJump(sq, color, V.C_BISHOP, V.steps.a, 1) ||
450 this.isAttackedBySlideNJump(sq, color, V.C_BISHOP, V.steps.f, 1)
451 );
452 }
453
454 isAttackedByC_queen(sq, color) {
455 return (
456 this.isAttackedBySlideNJump(sq, color, V.C_QUEEN, V.steps.b) ||
457 this.isAttackedBySlideNJump(sq, color, V.C_QUEEN, V.steps.n, 1)
458 );
459 }
460
461 isAttackedByN_rook(sq, color) {
462 const rookSteps = [ [0, -1], [0, 1], [color == 'w' ? 1 : -1, 0] ];
463 const backward = (color == 'w' ? -1 : 1);
464 const kingSteps = [ [backward, -1], [backward, 0], [backward, 1] ];
465 return (
466 this.isAttackedBySlideNJump(sq, color, V.N_ROOK, rookSteps) ||
467 this.isAttackedBySlideNJump(sq, color, V.N_ROOK, kingSteps, 1)
468 );
469 }
470
471 isAttackedByN_knight(sq, color) {
472 return (
473 this.isAttackedBySlideNJump(sq, color, V.N_KNIGHT, V.steps.$n, 1) ||
474 this.isAttackedBySlideNJump(sq, color, V.N_KNIGHT, V.steps.f, 1)
475 );
476 }
477
478 isAttackedByN_bishop(sq, color) {
479 const backward = (color == 'w' ? -1 : 1);
480 const kingSteps = [
481 [0, -1], [0, 1], [backward, -1], [backward, 0], [backward, 1]
482 ];
483 const forward = -backward;
484 const knightSteps = [
485 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
486 ];
487 return (
488 this.isAttackedBySlideNJump(sq, color, V.N_BISHOP, knightSteps, 1) ||
489 this.isAttackedBySlideNJump(sq, color, V.N_BISHOP, kingSteps, 1)
490 );
491 }
492
493 isAttackedByN_queen(sq, color) {
494 const backward = (color == 'w' ? -1 : 1);
495 const forward = -backward;
496 const kingSteps = [
497 [forward, -1], [forward, 1],
498 [backward, -1], [backward, 0], [backward, 1]
499 ];
500 const knightSteps = [
501 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
502 ];
503 const rookSteps = [ [0, -1], [0, 1], [forward, 0] ];
504 return (
505 this.isAttackedBySlideNJump(sq, color, V.N_QUEEN, knightSteps, 1) ||
506 this.isAttackedBySlideNJump(sq, color, V.N_QUEEN, kingSteps, 1) ||
507 this.isAttackedBySlideNJump(sq, color, V.N_QUEEN, rookSteps)
508 );
509 }
510
511 isAttackedByR_rook(sq, color) {
512 return this.isAttackedBySlideNJump(sq, color, V.R_ROOK, V.steps.r, 4);
513 }
514
515 isAttackedByR_knight(sq, color) {
516 return (
517 this.isAttackedBySlideNJump(sq, color, V.R_KNIGHT, V.steps.d, 1) ||
518 this.isAttackedBySlideNJump(sq, color, V.R_KNIGHT, V.steps.w, 1)
519 );
520 }
521
522 isAttackedByR_bishop(sq, color) {
523 return (
524 this.isAttackedBySlideNJump(sq, color, V.R_BISHOP, V.steps.d, 1) ||
525 this.isAttackedBySlideNJump(sq, color, V.R_BISHOP, V.steps.f, 1) ||
526 this.isAttackedBySlideNJump(sq, color, V.R_BISHOP, V.steps.$3, 1)
527 );
528 }
529
530 isAttackedByR_queen(sq, color) {
531 return (
532 this.isAttackedBySlideNJump(sq, color, V.R_QUEEN, V.steps.r) ||
533 this.isAttackedBySlideNJump(sq, color, V.R_QUEEN, V.steps.n, 1)
534 );
535 }
536
537 // [HACK] So that the function above works also on Fide army:
538 isAttackedByF_rook(sq, color) {
539 return super.isAttackedByRook(sq, color);
540 }
541 isAttackedByF_knight(sq, color) {
542 return super.isAttackedByKnight(sq, color);
543 }
544 isAttackedByF_bishop(sq, color) {
545 return super.isAttackedByBishop(sq, color);
546 }
547 isAttackedByF_queen(sq, color) {
548 return super.isAttackedByQueen(sq, color);
549 }
550
551 static get VALUES() {
552 return Object.assign(
553 {
554 d: 4,
555 w: 3,
556 f: 5,
557 c: 7,
558 g: 4,
559 i: 3,
560 t: 4,
561 l: 7,
562 s: 4,
563 y: 3,
564 h: 4,
565 o: 8
566 },
567 ChessRules.VALUES
568 );
569 }
570
571 static get SEARCH_DEPTH() {
572 return 2;
573 }
574
575 };