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