Some (experimental) fixes
[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 return (
308 this.getSlideNJumpMoves(sq, V.steps.$n, 1).concat(
309 this.getSlideNJumpMoves(sq, V.steps.f, 1))
310 );
311 }
312
313 getPotentialN_bishopMoves(sq) {
314 const backward = (this.turn == 'w' ? 1 : -1);
315 const kingSteps = [
316 [0, -1], [0, 1], [backward, -1], [backward, 0], [backward, 1]
317 ];
318 const forward = -backward;
319 const knightSteps = [
320 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
321 ];
322 return (
323 this.getSlideNJumpMoves(sq, knightSteps, 1).concat(
324 this.getSlideNJumpMoves(sq, kingSteps, 1))
325 );
326 }
327
328 getPotentialN_queenMoves(sq) {
329 const backward = (this.turn == 'w' ? 1 : -1);
330 const forward = -backward;
331 const kingSteps = [
332 [forward, -1], [forward, 1],
333 [backward, -1], [backward, 0], [backward, 1]
334 ];
335 const knightSteps = [
336 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
337 ];
338 const rookSteps = [ [0, -1], [0, 1], [forward, 0] ];
339 return (
340 this.getSlideNJumpMoves(sq, rookSteps).concat(
341 this.getSlideNJumpMoves(sq, kingSteps, 1)).concat(
342 this.getSlideNJumpMoves(sq, knightSteps, 1))
343 );
344 }
345
346 getPotentialR_rookMoves(sq) {
347 return this.getSlideNJumpMoves(sq, V.steps.r, 4);
348 }
349
350 getPotentialR_knightMoves(sq) {
351 return (
352 this.getSlideNJumpMoves(sq, V.steps.d, 1).concat(
353 this.getSlideNJumpMoves(sq, V.steps.w, 1))
354 );
355 }
356
357 getPotentialR_bishopMoves(sq) {
358 return (
359 this.getSlideNJumpMoves(sq, V.steps.d, 1).concat(
360 this.getSlideNJumpMoves(sq, V.steps.f, 1)).concat(
361 this.getSlideNJumpMoves(sq, V.steps.$3, 1))
362 );
363 }
364
365 getPotentialR_queenMoves(sq) {
366 return (
367 this.getSlideNJumpMoves(sq, V.steps.r).concat(
368 this.getSlideNJumpMoves(sq, V.steps.n, 1))
369 );
370 }
371
372 getCastleMoves([x, y]) {
373 const color = this.getColor(x, y);
374 let finalSquares = [ [2, 3], [V.size.y - 2, V.size.y - 3] ];
375 if (
376 (color == 'w' && this.army1 == 'C') ||
377 (color == 'b' && this.army2 == 'C')
378 ) {
379 // Colorbound castle long in an unusual way:
380 finalSquares[0] = [1, 2];
381 }
382 return super.getCastleMoves([x, y], finalSquares);
383 }
384
385 isAttacked(sq, color) {
386 if (super.isAttackedByPawn(sq, color) || super.isAttackedByKing(sq, color))
387 return true;
388 for (let army of ['C', 'N', 'R', 'F']) {
389 if (
390 [this.army1, this.army2].includes(army) &&
391 (
392 this["isAttackedBy" + army + "_rook"](sq, color) ||
393 this["isAttackedBy" + army + "_knight"](sq, color) ||
394 this["isAttackedBy" + army + "_bishop"](sq, color) ||
395 this["isAttackedBy" + army + "_queen"](sq, color)
396 )
397 ) {
398 return true;
399 }
400 }
401 return false;
402 }
403
404 isAttackedByC_rook(sq, color) {
405 return (
406 this.isAttackedBySlideNJump(sq, color, V.C_ROOK, V.steps.b) ||
407 this.isAttackedBySlideNJump(sq, color, V.C_ROOK, V.steps.d, 1)
408 );
409 }
410
411 isAttackedByC_knight(sq, color) {
412 return (
413 this.isAttackedBySlideNJump(sq, color, V.C_KNIGHT, V.steps.r, 1) ||
414 this.isAttackedBySlideNJump(sq, color, V.C_KNIGHT, V.steps.a, 1)
415 );
416 }
417
418 isAttackedByC_bishop(sq, color) {
419 return (
420 this.isAttackedBySlideNJump(sq, color, V.C_BISHOP, V.steps.d, 1) ||
421 this.isAttackedBySlideNJump(sq, color, V.C_BISHOP, V.steps.a, 1) ||
422 this.isAttackedBySlideNJump(sq, color, V.C_BISHOP, V.steps.f, 1)
423 );
424 }
425
426 isAttackedByC_queen(sq, color) {
427 return (
428 this.isAttackedBySlideNJump(sq, color, V.C_QUEEN, V.steps.b) ||
429 this.isAttackedBySlideNJump(sq, color, V.C_QUEEN, V.steps.n, 1)
430 );
431 }
432
433 isAttackedByN_rook(sq, color) {
434 const rookSteps = [ [0, -1], [0, 1], [color == 'w' ? 1 : -1, 0] ];
435 const backward = (color == 'w' ? -1 : 1);
436 const kingSteps = [ [backward, -1], [backward, 0], [backward, 1] ];
437 return (
438 this.isAttackedBySlideNJump(sq, color, V.N_ROOK, rookSteps) ||
439 this.isAttackedBySlideNJump(sq, color, V.N_ROOK, kingSteps, 1)
440 );
441 }
442
443 isAttackedByN_knight(sq, color) {
444 return (
445 this.isAttackedBySlideNJump(sq, color, V.N_KNIGHT, V.steps.$n, 1) ||
446 this.isAttackedBySlideNJump(sq, color, V.N_KNIGHT, V.steps.f, 1)
447 );
448 }
449
450 isAttackedByN_bishop(sq, color) {
451 const backward = (color == 'w' ? -1 : 1);
452 const kingSteps = [
453 [0, -1], [0, 1], [backward, -1], [backward, 0], [backward, 1]
454 ];
455 const forward = -backward;
456 const knightSteps = [
457 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
458 ];
459 return (
460 this.isAttackedBySlideNJump(sq, color, V.N_BISHOP, knightSteps, 1) ||
461 this.isAttackedBySlideNJump(sq, color, V.N_BISHOP, kingSteps, 1)
462 );
463 }
464
465 isAttackedByN_queen(sq, color) {
466 const backward = (color == 'w' ? -1 : 1);
467 const forward = -backward;
468 const kingSteps = [
469 [forward, -1], [forward, 1],
470 [backward, -1], [backward, 0], [backward, 1]
471 ];
472 const knightSteps = [
473 [2*forward, -1], [2*forward, 1], [forward, -2], [forward, 2]
474 ];
475 const rookSteps = [ [0, -1], [0, 1], [forward, 0] ];
476 return (
477 this.isAttackedBySlideNJump(sq, color, V.N_QUEEN, knightSteps, 1) ||
478 this.isAttackedBySlideNJump(sq, color, V.N_QUEEN, kingSteps, 1) ||
479 this.isAttackedBySlideNJump(sq, color, V.N_QUEEN, rookSteps)
480 );
481 }
482
483 isAttackedByR_rook(sq, color) {
484 return this.isAttackedBySlideNJump(sq, color, V.R_ROOK, V.steps.r, 4);
485 }
486
487 isAttackedByR_knight(sq, color) {
488 return (
489 this.isAttackedBySlideNJump(sq, color, V.R_KNIGHT, V.steps.d, 1) ||
490 this.isAttackedBySlideNJump(sq, color, V.R_KNIGHT, V.steps.w, 1)
491 );
492 }
493
494 isAttackedByR_bishop(sq, color) {
495 return (
496 this.isAttackedBySlideNJump(sq, color, V.R_BISHOP, V.steps.d, 1) ||
497 this.isAttackedBySlideNJump(sq, color, V.R_BISHOP, V.steps.f, 1) ||
498 this.isAttackedBySlideNJump(sq, color, V.R_BISHOP, V.steps.$3, 1)
499 );
500 }
501
502 isAttackedByR_queen(sq, color) {
503 return (
504 this.isAttackedBySlideNJump(sq, color, V.R_QUEEN, V.steps.r) ||
505 this.isAttackedBySlideNJump(sq, color, V.R_QUEEN, V.steps.n, 1)
506 );
507 }
508
509 // [HACK] So that the function above works also on Fide army:
510 isAttackedByF_rook(sq, color) {
511 return super.isAttackedByRook(sq, color);
512 }
513 isAttackedByF_knight(sq, color) {
514 return super.isAttackedByKnight(sq, color);
515 }
516 isAttackedByF_bishop(sq, color) {
517 return super.isAttackedByBishop(sq, color);
518 }
519 isAttackedByF_queen(sq, color) {
520 return super.isAttackedByQueen(sq, color);
521 }
522
523 static get VALUES() {
524 return Object.assign(
525 {
526 d: 4,
527 w: 3,
528 f: 5,
529 c: 7,
530 g: 4,
531 i: 3,
532 t: 4,
533 l: 7,
534 s: 4,
535 y: 3,
536 h: 4,
537 o: 8
538 },
539 ChessRules.VALUES
540 );
541 }
542
543 static get SEARCH_DEPTH() {
544 return 2;
545 }
546
547 };