1 import { ChessRules
} from "@/base_rules";
3 export class CwdaRules
extends ChessRules
{
7 select: ChessRules
.Options
.select
.concat([
13 { label: "Colorbound Clobberers", value: 'C' },
14 { label: "Nutty Knights", value: 'N' },
15 { label: "Remarkable Rookies", value: 'R' },
16 { label: "Fide", value: 'F' }
24 { label: "Colorbound Clobberers", value: 'C' },
25 { label: "Nutty Knights", value: 'N' },
26 { label: "Remarkable Rookies", value: 'R' },
27 { label: "Fide", value: 'F' }
34 static AbbreviateOptions(opts
) {
35 return opts
["army1"] + opts
["army2"];
38 static IsValidOptions(opts
) {
39 // Both armies filled, avoid Fide vs Fide
41 opts
.army1
&& opts
.army2
&&
42 (opts
.army1
!= 'F' || opts
.army2
!= 'F')
47 return (ChessRules
.PIECES
.includes(b
[1]) ? "" : "Cwda/") + b
;
50 static get PiecesMap() {
52 // Colorbound Clobberers
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);
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);
98 blackLine
+ "/" + blackPawns
+
99 baseFen
.substring(17, 26) +
100 whitePawns
+ "/" + whiteLine
+
101 baseFen
.substr(43) + " " + options
.army1
+ options
.army2
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);
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
++) {
117 for (let j
= 0; j
< fenRows
[i
].length
; j
++) {
118 const newChar
= fenRows
[i
].charAt(j
);
119 if (['a', 'e', 'k', 'm'].includes(newChar
))
120 this.kingPos
["b"] = [i
, k
];
121 else if (['A', 'E', 'K', 'M'].includes(newChar
))
122 this.kingPos
["w"] = [i
, k
];
124 const num
= parseInt(fenRows
[i
].charAt(j
), 10);
125 if (!isNaN(num
)) k
+= num
- 1;
132 static ParseFen(fen
) {
133 return Object
.assign(
134 { armies: fen
.split(" ")[5] },
135 ChessRules
.ParseFen(fen
)
139 static IsGoodFen(fen
) {
140 if (!ChessRules
.IsGoodFen(fen
)) return false;
141 const armies
= V
.ParseFen(fen
).armies
;
142 return (!!armies
&& armies
.match(/^[CNRF]{2,2}$/));
146 return super.getFen() + " " + this.army1
+ this.army2
;
149 static get C_ROOK() {
152 static get C_KNIGHT() {
155 static get C_BISHOP() {
158 static get C_QUEEN() {
161 static get C_KING() {
164 static get C_PAWN() {
167 static get N_ROOK() {
170 static get N_KNIGHT() {
173 static get N_BISHOP() {
176 static get N_QUEEN() {
179 static get N_KING() {
182 static get N_PAWN() {
185 static get R_ROOK() {
188 static get R_KNIGHT() {
191 static get R_BISHOP() {
194 static get R_QUEEN() {
197 static get R_KING() {
200 static get R_PAWN() {
205 const p
= this.board
[x
][y
][1];
206 if (['u', 'v', 'z'].includes(p
)) return 'p';
207 if (['a', 'e', 'm'].includes(p
)) return 'k';
211 static get PIECES() {
212 return ChessRules
.PIECES
.concat(
214 V
.C_ROOK
, V
.C_KNIGHT
, V
.C_BISHOP
, V
.C_QUEEN
, V
.C_KING
, V
.C_PAWN
,
215 V
.N_ROOK
, V
.N_KNIGHT
, V
.N_BISHOP
, V
.N_QUEEN
, V
.N_KING
, V
.N_PAWN
,
216 V
.R_ROOK
, V
.R_KNIGHT
, V
.R_BISHOP
, V
.R_QUEEN
, V
.R_KING
, V
.R_PAWN
221 getEpSquare(moveOrSquare
) {
222 if (!moveOrSquare
) return undefined; //TODO: necessary line?!
223 if (typeof moveOrSquare
=== "string") {
224 const square
= moveOrSquare
;
225 if (square
== "-") return undefined;
226 return V
.SquareToCoords(square
);
228 // Argument is a move:
229 const move = moveOrSquare
;
230 const s
= move.start
,
234 Math
.abs(s
.x
- e
.x
) == 2 &&
235 ['p', 'u', 'v'].includes(move.appear
[0].p
)
242 return undefined; //default
245 getPotentialMovesFrom(sq
) {
246 switch (this.getPiece(sq
[0], sq
[1])) {
247 case V
.C_ROOK: return this.getPotentialC_rookMoves(sq
);
248 case V
.C_KNIGHT: return this.getPotentialC_knightMoves(sq
);
249 case V
.C_BISHOP: return this.getPotentialC_bishopMoves(sq
);
250 case V
.C_QUEEN: return this.getPotentialC_queenMoves(sq
);
251 case V
.N_ROOK: return this.getPotentialN_rookMoves(sq
);
252 case V
.N_KNIGHT: return this.getPotentialN_knightMoves(sq
);
253 case V
.N_BISHOP: return this.getPotentialN_bishopMoves(sq
);
254 case V
.N_QUEEN: return this.getPotentialN_queenMoves(sq
);
255 case V
.R_ROOK: return this.getPotentialR_rookMoves(sq
);
256 case V
.R_KNIGHT: return this.getPotentialR_knightMoves(sq
);
257 case V
.R_BISHOP: return this.getPotentialR_bishopMoves(sq
);
258 case V
.R_QUEEN: return this.getPotentialR_queenMoves(sq
);
260 // Can promote in anything from the two current armies
262 for (let army
of ["army1", "army2"]) {
263 if (army
== "army2" && this.army2
== this.army1
) break;
264 switch (this[army
]) {
266 Array
.prototype.push
.apply(promotions
,
267 [V
.C_ROOK
, V
.C_KNIGHT
, V
.C_BISHOP
, V
.C_QUEEN
]);
271 Array
.prototype.push
.apply(promotions
,
272 [V
.N_ROOK
, V
.N_KNIGHT
, V
.N_BISHOP
, V
.N_QUEEN
]);
276 Array
.prototype.push
.apply(promotions
,
277 [V
.R_ROOK
, V
.R_KNIGHT
, V
.R_BISHOP
, V
.R_QUEEN
]);
281 Array
.prototype.push
.apply(promotions
,
282 [V
.ROOK
, V
.KNIGHT
, V
.BISHOP
, V
.QUEEN
]);
287 return super.getPotentialPawnMoves(sq
, promotions
);
289 default: return super.getPotentialMovesFrom(sq
);
295 return Object
.assign(
344 getPotentialC_rookMoves(sq
) {
346 this.getSlideNJumpMoves(sq
, V
.steps
.b
).concat(
347 this.getSlideNJumpMoves(sq
, V
.steps
.d
, 1))
351 getPotentialC_knightMoves(sq
) {
353 this.getSlideNJumpMoves(sq
, V
.steps
.a
, 1).concat(
354 this.getSlideNJumpMoves(sq
, V
.steps
.r
, 1))
358 getPotentialC_bishopMoves(sq
) {
360 this.getSlideNJumpMoves(sq
, V
.steps
.d
, 1).concat(
361 this.getSlideNJumpMoves(sq
, V
.steps
.a
, 1)).concat(
362 this.getSlideNJumpMoves(sq
, V
.steps
.b
, 1))
366 getPotentialC_queenMoves(sq
) {
368 this.getSlideNJumpMoves(sq
, V
.steps
.b
).concat(
369 this.getSlideNJumpMoves(sq
, V
.steps
.n
, 1))
373 getPotentialN_rookMoves(sq
) {
375 const rookSteps
= [ [0, -1], [0, 1], [c
== 'w' ? -1 : 1, 0] ];
376 const backward
= (c
== 'w' ? 1 : -1);
377 const kingSteps
= [ [backward
, -1], [backward
, 0], [backward
, 1] ];
379 this.getSlideNJumpMoves(sq
, rookSteps
).concat(
380 this.getSlideNJumpMoves(sq
, kingSteps
, 1))
384 getPotentialN_knightMoves(sq
) {
386 this.getSlideNJumpMoves(sq
, V
.steps
.$n
, 1).concat(
387 this.getSlideNJumpMoves(sq
, V
.steps
.f
, 1))
391 getPotentialN_bishopMoves(sq
) {
392 const backward
= (this.turn
== 'w' ? 1 : -1);
394 [0, -1], [0, 1], [backward
, -1], [backward
, 0], [backward
, 1]
396 const forward
= -backward
;
397 const knightSteps
= [
398 [2*forward
, -1], [2*forward
, 1], [forward
, -2], [forward
, 2]
401 this.getSlideNJumpMoves(sq
, knightSteps
, 1).concat(
402 this.getSlideNJumpMoves(sq
, kingSteps
, 1))
406 getPotentialN_queenMoves(sq
) {
407 const backward
= (this.turn
== 'w' ? 1 : -1);
408 const forward
= -backward
;
410 [forward
, -1], [forward
, 1],
411 [backward
, -1], [backward
, 0], [backward
, 1]
413 const knightSteps
= [
414 [2*forward
, -1], [2*forward
, 1], [forward
, -2], [forward
, 2]
416 const rookSteps
= [ [0, -1], [0, 1], [forward
, 0] ];
418 this.getSlideNJumpMoves(sq
, rookSteps
).concat(
419 this.getSlideNJumpMoves(sq
, kingSteps
, 1)).concat(
420 this.getSlideNJumpMoves(sq
, knightSteps
, 1))
424 getPotentialR_rookMoves(sq
) {
425 return this.getSlideNJumpMoves(sq
, V
.steps
.r
, 4);
428 getPotentialR_knightMoves(sq
) {
430 this.getSlideNJumpMoves(sq
, V
.steps
.d
, 1).concat(
431 this.getSlideNJumpMoves(sq
, V
.steps
.w
, 1))
435 getPotentialR_bishopMoves(sq
) {
437 this.getSlideNJumpMoves(sq
, V
.steps
.d
, 1).concat(
438 this.getSlideNJumpMoves(sq
, V
.steps
.f
, 1)).concat(
439 this.getSlideNJumpMoves(sq
, V
.steps
.$3, 1))
443 getPotentialR_queenMoves(sq
) {
445 this.getSlideNJumpMoves(sq
, V
.steps
.r
).concat(
446 this.getSlideNJumpMoves(sq
, V
.steps
.n
, 1))
450 getCastleMoves([x
, y
]) {
451 const color
= this.getColor(x
, y
);
452 let finalSquares
= [ [2, 3], [V
.size
.y
- 2, V
.size
.y
- 3] ];
454 (color
== 'w' && this.army1
== 'C') ||
455 (color
== 'b' && this.army2
== 'C')
457 // Colorbound castle long in an unusual way:
458 finalSquares
[0] = [1, 2];
460 return super.getCastleMoves([x
, y
], finalSquares
);
463 isAttacked(sq
, color
) {
464 if (super.isAttackedByPawn(sq
, color
) || super.isAttackedByKing(sq
, color
))
466 for (let army
of ['C', 'N', 'R', 'F']) {
468 [this.army1
, this.army2
].includes(army
) &&
470 this["isAttackedBy" + army
+ "_rook"](sq
, color
) ||
471 this["isAttackedBy" + army
+ "_knight"](sq
, color
) ||
472 this["isAttackedBy" + army
+ "_bishop"](sq
, color
) ||
473 this["isAttackedBy" + army
+ "_queen"](sq
, color
)
482 isAttackedByC_rook(sq
, color
) {
484 this.isAttackedBySlideNJump(sq
, color
, V
.C_ROOK
, V
.steps
.b
) ||
485 this.isAttackedBySlideNJump(sq
, color
, V
.C_ROOK
, V
.steps
.d
, 1)
489 isAttackedByC_knight(sq
, color
) {
491 this.isAttackedBySlideNJump(sq
, color
, V
.C_KNIGHT
, V
.steps
.r
, 1) ||
492 this.isAttackedBySlideNJump(sq
, color
, V
.C_KNIGHT
, V
.steps
.a
, 1)
496 isAttackedByC_bishop(sq
, color
) {
498 this.isAttackedBySlideNJump(sq
, color
, V
.C_BISHOP
, V
.steps
.d
, 1) ||
499 this.isAttackedBySlideNJump(sq
, color
, V
.C_BISHOP
, V
.steps
.a
, 1) ||
500 this.isAttackedBySlideNJump(sq
, color
, V
.C_BISHOP
, V
.steps
.f
, 1)
504 isAttackedByC_queen(sq
, color
) {
506 this.isAttackedBySlideNJump(sq
, color
, V
.C_QUEEN
, V
.steps
.b
) ||
507 this.isAttackedBySlideNJump(sq
, color
, V
.C_QUEEN
, V
.steps
.n
, 1)
511 isAttackedByN_rook(sq
, color
) {
512 const rookSteps
= [ [0, -1], [0, 1], [color
== 'w' ? 1 : -1, 0] ];
513 const backward
= (color
== 'w' ? -1 : 1);
514 const kingSteps
= [ [backward
, -1], [backward
, 0], [backward
, 1] ];
516 this.isAttackedBySlideNJump(sq
, color
, V
.N_ROOK
, rookSteps
) ||
517 this.isAttackedBySlideNJump(sq
, color
, V
.N_ROOK
, kingSteps
, 1)
521 isAttackedByN_knight(sq
, color
) {
523 this.isAttackedBySlideNJump(sq
, color
, V
.N_KNIGHT
, V
.steps
.$n
, 1) ||
524 this.isAttackedBySlideNJump(sq
, color
, V
.N_KNIGHT
, V
.steps
.f
, 1)
528 isAttackedByN_bishop(sq
, color
) {
529 const backward
= (color
== 'w' ? -1 : 1);
531 [0, -1], [0, 1], [backward
, -1], [backward
, 0], [backward
, 1]
533 const forward
= -backward
;
534 const knightSteps
= [
535 [2*forward
, -1], [2*forward
, 1], [forward
, -2], [forward
, 2]
538 this.isAttackedBySlideNJump(sq
, color
, V
.N_BISHOP
, knightSteps
, 1) ||
539 this.isAttackedBySlideNJump(sq
, color
, V
.N_BISHOP
, kingSteps
, 1)
543 isAttackedByN_queen(sq
, color
) {
544 const backward
= (color
== 'w' ? -1 : 1);
545 const forward
= -backward
;
547 [forward
, -1], [forward
, 1],
548 [backward
, -1], [backward
, 0], [backward
, 1]
550 const knightSteps
= [
551 [2*forward
, -1], [2*forward
, 1], [forward
, -2], [forward
, 2]
553 const rookSteps
= [ [0, -1], [0, 1], [forward
, 0] ];
555 this.isAttackedBySlideNJump(sq
, color
, V
.N_QUEEN
, knightSteps
, 1) ||
556 this.isAttackedBySlideNJump(sq
, color
, V
.N_QUEEN
, kingSteps
, 1) ||
557 this.isAttackedBySlideNJump(sq
, color
, V
.N_QUEEN
, rookSteps
)
561 isAttackedByR_rook(sq
, color
) {
562 return this.isAttackedBySlideNJump(sq
, color
, V
.R_ROOK
, V
.steps
.r
, 4);
565 isAttackedByR_knight(sq
, color
) {
567 this.isAttackedBySlideNJump(sq
, color
, V
.R_KNIGHT
, V
.steps
.d
, 1) ||
568 this.isAttackedBySlideNJump(sq
, color
, V
.R_KNIGHT
, V
.steps
.w
, 1)
572 isAttackedByR_bishop(sq
, color
) {
574 this.isAttackedBySlideNJump(sq
, color
, V
.R_BISHOP
, V
.steps
.d
, 1) ||
575 this.isAttackedBySlideNJump(sq
, color
, V
.R_BISHOP
, V
.steps
.f
, 1) ||
576 this.isAttackedBySlideNJump(sq
, color
, V
.R_BISHOP
, V
.steps
.$3, 1)
580 isAttackedByR_queen(sq
, color
) {
582 this.isAttackedBySlideNJump(sq
, color
, V
.R_QUEEN
, V
.steps
.r
) ||
583 this.isAttackedBySlideNJump(sq
, color
, V
.R_QUEEN
, V
.steps
.n
, 1)
587 // [HACK] So that the function above works also on Fide army:
588 isAttackedByF_rook(sq
, color
) {
589 return super.isAttackedByRook(sq
, color
);
591 isAttackedByF_knight(sq
, color
) {
592 return super.isAttackedByKnight(sq
, color
);
594 isAttackedByF_bishop(sq
, color
) {
595 return super.isAttackedByBishop(sq
, color
);
597 isAttackedByF_queen(sq
, color
) {
598 return super.isAttackedByQueen(sq
, color
);
602 const c
= V
.GetOppCol(this.turn
);
603 const piece
= move.appear
[0].p
;
604 // Update king position + flags
605 if (['k', 'a', 'e', 'm'].includes(piece
)) {
606 this.kingPos
[c
][0] = move.appear
[0].x
;
607 this.kingPos
[c
][1] = move.appear
[0].y
;
608 this.castleFlags
[c
] = [V
.size
.y
, V
.size
.y
];
610 // Next call is still required because the king may eat an opponent's rook
611 // TODO: castleFlags will be turned off twice then.
612 super.updateCastleFlags(move, piece
);
616 // (Potentially) Reset king position
617 const c
= this.getColor(move.start
.x
, move.start
.y
);
618 const piece
= move.appear
[0].p
;
619 if (['k', 'a', 'e', 'm'].includes(piece
))
620 this.kingPos
[c
] = [move.start
.x
, move.start
.y
];
623 static get VALUES() {
624 return Object
.assign(
643 static get SEARCH_DEPTH() {
648 let notation
= super.getNotation(move);
649 if (['u', 'v', 'z'].includes(move.appear
[0].p
))
650 notation
= notation
.slice(0, -2);