1 import { ChessRules
} from "@/base_rules";
2 import { SuicideRules
} from "@/variants/Suicide";
4 export class ChakartRules
extends ChessRules
{
5 static get PawnSpecs() {
6 return SuicideRules
.PawnSpecs
;
9 static get HasCastle() {
13 static get HasEnpassant() {
14 // TODO: maybe enable them later, but then the capturing pawn take the
15 // mushroom and continue diagonally?!
19 static get CorrConfirm() {
20 // Because of bonus effects
24 static get CanAnalyze() {
28 hoverHighlight(x
, y
) {
30 this.firstMove
.appear
.length
== 0 ||
31 this.firstMove
.vanish
.length
== 0 ||
32 this.board
[x
][y
] != V
.EMPTY
36 const deltaX
= Math
.abs(this.firstMove
.end
.x
- x
);
37 const deltaY
= Math
.abs(this.firstMove
.end
.y
- y
);
40 // Condition: rook or bishop move, may capture, but no bonus move
41 [V
.ROOK
, V
.BISHOP
].includes(this.firstMove
.vanish
[0].p
) &&
43 this.firstMove
.vanish
.length
== 1 ||
44 ['w', 'b'].includes(this.firstMove
.vanish
[1].c
)
47 this.firstMove
.vanish
[0].p
== V
.ROOK
&& deltaX
== 1 && deltaY
== 1 ||
48 this.firstMove
.vanish
[0].p
== V
.BISHOP
&& deltaX
+ deltaY
== 1
53 static get IMMOBILIZE_CODE() {
64 static get IMMOBILIZE_DECODE() {
75 static get INVISIBLE_QUEEN() {
79 // Fictive color 'a', bomb banana mushroom egg
81 // Doesn't collide with bishop because color 'a'
90 static get MUSHROOM() {
96 ChessRules
.PIECES
.concat(
97 Object
.keys(V
.IMMOBILIZE_DECODE
)).concat(
98 [V
.BANANA
, V
.BOMB
, V
.EGG
, V
.MUSHROOM
, V
.INVISIBLE_QUEEN
])
106 b
[1] == V
.INVISIBLE_QUEEN
||
107 Object
.keys(V
.IMMOBILIZE_DECODE
).includes(b
[1])
114 static ParseFen(fen
) {
115 const fenParts
= fen
.split(" ");
116 return Object
.assign(
117 ChessRules
.ParseFen(fen
),
118 { captured: fenParts
[4] }
122 // King can be l or L (immobilized) --> similar to Alice variant
123 static IsGoodPosition(position
) {
124 if (position
.length
== 0) return false;
125 const rows
= position
.split("/");
126 if (rows
.length
!= V
.size
.x
) return false;
127 let kings
= { "k": 0, "K": 0, 'l': 0, 'L': 0 };
128 for (let row
of rows
) {
130 for (let i
= 0; i
< row
.length
; i
++) {
131 if (['K','k','L','l'].includes(row
[i
])) kings
[row
[i
]]++;
132 if (V
.PIECES
.includes(row
[i
].toLowerCase())) sumElts
++;
134 const num
= parseInt(row
[i
]);
135 if (isNaN(num
)) return false;
139 if (sumElts
!= V
.size
.y
) return false;
141 if (kings
['k'] + kings
['l'] != 1 || kings
['K'] + kings
['L'] != 1)
146 static IsGoodFlags(flags
) {
147 // 4 for Peach + Mario w, b
148 return !!flags
.match(/^[01]{4,4}$/);
153 w: [...Array(2)], //king can send shell? Queen can be invisible?
156 for (let c
of ["w", "b"]) {
157 for (let i
= 0; i
< 2; i
++)
158 this.pawnFlags
[c
][i
] = fenFlags
.charAt((c
== "w" ? 0 : 2) + i
) == "1";
163 return this.powerFlags
;
166 disaggregateFlags(flags
) {
167 this.powerFlags
= flags
;
171 return super.getFen() + " " + this.getCapturedFen();
175 return super.getFenForRepeat() + "_" + this.getCapturedFen();
179 let counts
= [...Array(10).fill(0)];
181 for (let p
of [V
.ROOK
, V
.KNIGHT
, V
.BISHOP
, V
.QUEEN
, V
.PAWN
]) {
182 counts
[i
] = this.captured
["w"][p
];
183 counts
[5 + i
] = this.captured
["b"][p
];
186 return counts
.join("");
189 setOtherVariables(fen
) {
190 const fenParsed
= V
.ParseFen(fen
);
191 // Initialize captured pieces' counts from FEN
194 [V
.ROOK
]: parseInt(fenParsed
.captured
[0]),
195 [V
.KNIGHT
]: parseInt(fenParsed
.captured
[1]),
196 [V
.BISHOP
]: parseInt(fenParsed
.captured
[2]),
197 [V
.QUEEN
]: parseInt(fenParsed
.captured
[3]),
198 [V
.PAWN
]: parseInt(fenParsed
.captured
[4]),
201 [V
.ROOK
]: parseInt(fenParsed
.captured
[5]),
202 [V
.KNIGHT
]: parseInt(fenParsed
.captured
[6]),
203 [V
.BISHOP
]: parseInt(fenParsed
.captured
[7]),
204 [V
.QUEEN
]: parseInt(fenParsed
.captured
[8]),
205 [V
.PAWN
]: parseInt(fenParsed
.captured
[9]),
214 for (let c
of ["w", "b"])
215 for (let i
= 0; i
< 2; i
++) fen
+= (this.powerFlags
[c
][i
] ? "1" : "0");
219 getPotentialMovesFrom(sq
) {
220 if (this.subTurn
== 1) return super.getPotentialMovesFrom(sq
);
221 if (this.subTurn
== 2) {
222 // TODO: coup compatible avec firstMove
226 getBasicMove([x1
, y1
], [x2
, y2
]) {
227 // Apply mushroom, bomb or banana effect (hidden to the player).
228 // Determine egg effect, too, and apply its first part if possible.
229 // add egg + add mushroom for pawns.
230 let move = super.getBasicMove([x1
, y1
], [x2
, y2
]);
233 // Infer move type based on its effects (used to decide subTurn 1 --> 2)
234 // --> impossible étant donné juste first part (egg --> effect?)
235 // => stocker l'effet (i, ii ou iii) dans le coup directement,
236 // Pas terrible, mais y'aura pas 36 variantes comme ça. Disons end.effect == null, 0, 1, 2 ou 3
237 // 0 => tour ou fou, pose potentielle.
238 // If queen can be invisible, add move same start + end but final type changes
241 getPotentialKingMoves([x
, y
]) {
242 let moves
= super.getPotentialKingMoves([x
, y
]);
243 // TODO: if flags allows it, add 'remote shell captures'
247 getSlideNJumpMoves([x
, y
], steps
, oneStep
) {
249 outerLoop: for (let step
of steps
) {
255 this.board
[i
][j
] == V
.EMPTY
||
257 this.getColor(i
, j
) == 'a' &&
258 [V
.EGG
, V
.MUSHROOM
].includes(this.getPiece(i
, j
))
262 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
263 if (oneStep
) continue outerLoop
;
267 if (V
.OnBoard(i
, j
) && this.canTake([x
, y
], [i
, j
]))
268 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
273 getAllPotentialMoves() {
274 if (this.subTurn
== 1) return super.getAllPotentialMoves();
275 // TODO: subTurn == 2, switch on firstMove.end.effect --> lack firstMove, setOtherVariables, play/undo, see Dynamo
279 if (isNaN(square
[0])) return null;
280 // TODO: If subTurn == 2:
281 // if square is empty && firstMove is compatible,
282 // complete the move (banana or bomb or piece exchange).
283 // if square not empty, just complete with empty move
284 const Lf
= this.firstMove
.length
;
285 if (this.subTurn
== 2) {
287 this.board
[square
[0]][square
[1]] == V
.EMPTY
&&
288 (La
== 0 || !this.oppositeMoves(this.amoves
[La
-1], this.firstMove
[Lf
-1]))
291 start: { x: -1, y: -1 },
292 end: { x: -1, y: -1 },
303 // --> pour bonus toadette, passer "capture" temporairement en "reserve" pour permettre de jouer le coup.
304 // il faut alors mettre à jour 'captured'
305 // TODO: subTurn passe à 2 si arrivée sur bonus cavalier + effect == 1, 2 ou 3 ou si coup de tour ou fou (non cumulables)
309 // TODO: if effect = resurect a piece, then this.reserve = this.captured;
310 if (move.vanish
.length
== 2 && move.vanish
[1].c
!= 'a')
311 // Capture: update this.captured
312 this.captured
[move.vanish
[1].c
][move.vanish
[1].p
]++;
313 else if (move.vanish
.length
== 0) {
314 // A piece is back on board
315 this.captured
[move.vanish
[1].c
][move.vanish
[1].p
]++;
318 // si pièce immobilisée de ma couleur : elle redevient utilisable (changer status fin de play)
319 // TODO: un-immobilize my formerly immobilized piece, if any.
320 // Make invisible queen visible again, if any opponent invisible queen.
324 // TODO: should be easy once end.effect is set in getBasicMove()
328 if (move.vanish
.length
== 2 && move.vanish
[1].c
!= 'a')
329 this.captured
[move.vanish
[1].c
][move.vanish
[1].p
]--;
337 // Find kings (not tracked in this variant)
338 let kingThere
= { w: false, b: false };
339 for (let i
=0; i
<8; i
++) {
340 for (let j
=0; j
<8; j
++) {
341 if (this.board
[i
][j
] != V
.EMPTY
&& this.getPiece(i
, j
) == V
.KING
)
342 kingThere
[this.getColor(i
, j
)] = true;
345 if (!kingThere
['w']) return "0-1";
346 if (!kingThere
['b']) return "1-0";
350 static GenRandInitFen(randomness
) {
352 SuicideRules
.GenRandInitFen(randomness
).slice(0, -1) +
353 // Add Peach + Mario flags, re-add en-passant + capture counts
363 const moves
= this.getAllValidMoves();
364 // TODO: random mover
370 // invisibility used? --> move notation Q??