1 import { ChessRules
} from "@/base_rules";
3 export class ChakartRules
extends ChessRules
{
4 static get CorrConfirm() {
5 // Because of bonus effects
9 static get CanAnalyze() {
13 hoverHighlight(x
, y
) {
15 this.firstMove
.appear
.length
== 0 ||
16 this.firstMove
.vanish
.length
== 0 ||
17 this.board
[x
][y
] != V
.EMPTY
21 const deltaX
= Math
.abs(this.firstMove
.end
.x
- x
);
22 const deltaY
= Math
.abs(this.firstMove
.end
.y
- y
);
25 // Condition: rook or bishop move, may capture, but no bonus move
26 [V
.ROOK
, V
.BISHOP
].includes(this.firstMove
.vanish
[0].p
) &&
28 this.firstMove
.vanish
.length
== 1 ||
29 ['w', 'b'].includes(this.firstMove
.vanish
[1].c
)
32 this.firstMove
.vanish
[0].p
== V
.ROOK
&& deltaX
== 1 && deltaY
== 1 ||
33 this.firstMove
.vanish
[0].p
== V
.BISHOP
&& deltaX
+ deltaY
== 1
38 static get IMMOBILIZE_CODE() {
49 static get IMMOBILIZE_DECODE() {
60 static get INVISIBLE_QUEEN() {
64 // Fictive color 'a', bomb banana mushroom egg
66 // Doesn't collide with bishop because color 'a'
75 static get MUSHROOM() {
81 ChessRules
.PIECES
.concat(
82 Object
.keys(V
.IMMOBILIZE_DECODE
)).concat(
83 [V
.BANANA
, V
.BOMB
, V
.EGG
, V
.MUSHROOM
, V
.INVISIBLE_QUEEN
])
91 b
[1] == V
.INVISIBLE_QUEEN
||
92 Object
.keys(V
.IMMOBILIZE_DECODE
).includes(b
[1])
99 static ParseFen(fen
) {
100 const fenParts
= fen
.split(" ");
101 return Object
.assign(
102 ChessRules
.ParseFen(fen
),
103 { captured: fenParts
[5] }
107 // King can be l or L (immobilized) --> similar to Alice variant
108 static IsGoodPosition(position
) {
109 if (position
.length
== 0) return false;
110 const rows
= position
.split("/");
111 if (rows
.length
!= V
.size
.x
) return false;
112 let kings
= { "k": 0, "K": 0, 'l': 0, 'L': 0 };
113 for (let row
of rows
) {
115 for (let i
= 0; i
< row
.length
; i
++) {
116 if (['K','k','L','l'].includes(row
[i
])) kings
[row
[i
]]++;
117 if (V
.PIECES
.includes(row
[i
].toLowerCase())) sumElts
++;
119 const num
= parseInt(row
[i
]);
120 if (isNaN(num
)) return false;
124 if (sumElts
!= V
.size
.y
) return false;
126 if (kings
['k'] + kings
['l'] != 1 || kings
['K'] + kings
['L'] != 1)
131 static IsGoodFlags(flags
) {
132 // 4 for castle + 4 for Peach + Mario w, b
133 return !!flags
.match(/^[a-z]{4,4}[01]{4,4}$/);
137 super.setFlags(fenflags
); //castleFlags
139 w: [...Array(2)], //king can send shell? Queen can be invisible?
142 const flags
= fenflags
.substr(4); //skip first 4 letters, for castle
143 for (let c
of ["w", "b"]) {
144 for (let i
= 0; i
< 2; i
++)
145 this.pawnFlags
[c
][i
] = flags
.charAt((c
== "w" ? 0 : 2) + i
) == "1";
150 return [this.castleFlags
, this.powerFlags
];
153 disaggregateFlags(flags
) {
154 this.castleFlags
= flags
[0];
155 this.powerFlags
= flags
[1];
159 return super.getFen() + " " + this.getCapturedFen();
163 return super.getFenForRepeat() + "_" + this.getCapturedFen();
167 let counts
= [...Array(10).fill(0)];
169 for (let p
of [V
.ROOK
, V
.KNIGHT
, V
.BISHOP
, V
.QUEEN
, V
.PAWN
]) {
170 counts
[i
] = this.captured
["w"][p
];
171 counts
[5 + i
] = this.captured
["b"][p
];
174 return counts
.join("");
177 setOtherVariables(fen
) {
178 super.setOtherVariables(fen
);
179 const fenParsed
= V
.ParseFen(fen
);
180 // Initialize captured pieces' counts from FEN
183 [V
.ROOK
]: parseInt(fenParsed
.captured
[0]),
184 [V
.KNIGHT
]: parseInt(fenParsed
.captured
[1]),
185 [V
.BISHOP
]: parseInt(fenParsed
.captured
[2]),
186 [V
.QUEEN
]: parseInt(fenParsed
.captured
[3]),
187 [V
.PAWN
]: parseInt(fenParsed
.captured
[4]),
190 [V
.ROOK
]: parseInt(fenParsed
.captured
[5]),
191 [V
.KNIGHT
]: parseInt(fenParsed
.captured
[6]),
192 [V
.BISHOP
]: parseInt(fenParsed
.captured
[7]),
193 [V
.QUEEN
]: parseInt(fenParsed
.captured
[8]),
194 [V
.PAWN
]: parseInt(fenParsed
.captured
[9]),
201 let fen
= super.getFlagsFen();
203 for (let c
of ["w", "b"])
204 for (let i
= 0; i
< 2; i
++) fen
+= (this.powerFlags
[c
][i
] ? "1" : "0");
210 // --> pour bonus toadette, passer "capture" temporairement en "reserve" pour permettre de jouer le coup.
213 getPotentialMovesFrom([x
, y
]) {
214 // TODO: si banane ou bombe ou... alors return [] ?
215 // TODO: bananes et bombes limitent les déplacements (agissent comme un mur "capturable")
216 // bananes jaunes et rouges ?! (agissant sur une seule couleur ?) --> mauvaise idée.
217 if (this.subTurn
== 2) {
218 // TODO: coup compatible avec firstMove
221 //Si une pièce pose quelque chose sur une case ça remplace ce qui y était déjà.
222 // TODO: un-immobilize my immobilized piece at the end of this turn, if any
225 getBasicMove([x1
, y1
], [x2
, y2
]) {
226 // NOTE: getBasicMove, ajouter les bonus à vanish array
227 // + déterminer leur effet (si cavalier) ou case (si banane ou bombe)
228 // (L'effet doit être caché au joueur : devrait être OK)
231 getSlideNJumpMpves(sq
, steps
, oneStep
) {
232 // Saut possible par dessus bonus ou champis mais pas bananes ou bombes
233 //==> redefinir isAttackedBySlide et getPotentialSlide...
236 getPotentialPawnMoves(sq
) {
238 // laisse sur sa case de départ un champi turbo permettant à Peach et cavalier et autres pions d'aller
239 // un dep plus loin (evt 2 cases si pion saut initial), et aux pièces arrivant sur cette case de sauter par
240 // dessus une pièce immédiatement adjacente dans leur trajectoire (en atterissant juste derrière).
243 // Coups en 2 temps (si pose possible)
244 getPotentialRookMoves(sq
) {
246 // pose une banane (optionnel) sur une case adjacente (diagonale) à celle d'arrivée
247 // Si une pièce arrive sur la peau de banane, alors elle effectue un déplacement
248 // aléatoire d'une (2?) case (vertical ou horizontal) depuis sa position finale.
251 // Coups en 2 temps (si pose)
252 getPotentialBishopMoves([x
, y
]) {
254 // pose une bombe (optionnel) sur une case orthogonalement adjacente à la case d'arrivée
255 // Si une pièce arrive sur une bombe, alors elle effectue un déplacement diagonal
256 // aléatoire d'une (2?) case depuis sa position finale (juste une case si impossible).
259 getPotentialKnightMoves([x
, y
]) {
261 // laisse sur sa case de départ un bonus aléatoire
262 // (NOTE: certains bonus pourraient ne pas être applicables ==> pion bloqué par exemple)
263 // - i) roi boo(*E*) : échange avec n'importe quelle pièce (choix du joueur, type et/ou couleur différents)
264 // - i*) koopa(*B*) : ramène sur la case initiale
265 // - ii) toadette(*R*) : permet de poser une pièce capturée sur le plateau
266 // (n'importe où sauf 8eme rangée pour les pions)
267 // - ii*) chomp(*W*) : mange la pièce ; si c'est Peach, c'est perdu
268 // - iii) daisy(*T*) : permet de rejouer un coup avec la même pièce --> cumulable si ensuite coup sur bonus Daisy.
269 // - iii*) bowser(*M*) : immobilise la pièce (marquée jaune/rouge), qui ne pourra pas jouer au tour suivant
270 // - iv) luigi(*L*) : fait changer de camp une pièce adverse (aléatoire) (sauf le roi)
271 // - iv*) waluigi(*D*) : fait changer de camp une de nos pièces (aléatoire, sauf le roi)
272 // --> i, ii, iii en deux temps (subTurn 1 & 2)
275 getPotentialQueenMoves(sq
) {
277 // pouvoir "fantôme" : peut effectuer une fois dans la partie un coup non-capturant invisible (=> choix à chaque coup, getPPpath(m) teste m.nvisible...)
278 //wg bg ghost once in the game the queen can make an invisible move --> printed as "?"
281 getPotentialKingMoves(sq
) {
283 // Carapace rouge (disons ^^) jouable une seule fois dans la partie,
284 // au lieu de se déplacer. Capture un ennemi au choix parmi les plus proches,
285 // à condition qu'ils soient visibles (suivant les directions de déplacement d'une dame).
286 // Profite des accélérateurs posés par les pions (+ 1 case : obligatoire).
289 isAttackedBySlideNJump() {
298 getAllPotentialMoves() {
299 // (Attention: objets pas jouables cf. getPotentialMoves...)
303 // TODO: subTurn passe à 2 si arrivée sur bonus cavalier
304 // potentiellement pose (tour, fou) ou si choix (reconnaître i (ok), ii (ok) et iii (si coup normal + pas immobilisé) ?)
305 // voire +2 si plusieurs daisy...
306 // si pièce immobilisée de ma couleur : elle redevient utilisable (changer status fin de play)
310 // TODO: reconnaissance inverse si subTurn == 1 --> juste impossible ==> marquer pendant play (comme DoubleMove1 : move.turn = ...)
314 if (isNaN(square
[0])) return null;
315 // TODO: If subTurn == 2:
316 // if square is empty && firstMove is compatible,
317 // complete the move (banana or bomb or piece exchange).
318 // if square not empty, just complete with empty move
319 const Lf
= this.firstMove
.length
;
320 if (this.subTurn
== 2) {
322 this.board
[square
[0]][square
[1]] == V
.EMPTY
&&
323 !this.underCheck(this.turn
) &&
324 (La
== 0 || !this.oppositeMoves(this.amoves
[La
-1], this.firstMove
[Lf
-1]))
327 start: { x: -1, y: -1 },
328 end: { x: -1, y: -1 },
338 // TODO: king may also be "chomped"
339 super.updateCastleFlags(move, piece
);
342 super.postPlay(move);
343 if (move.vanish
.length
== 2 && move.appear
.length
== 1)
344 // Capture: update this.captured
345 this.captured
[move.vanish
[1].c
][move.vanish
[1].p
]++;
349 super.postUndo(move);
350 if (move.vanish
.length
== 2 && move.appear
.length
== 1)
351 this.captured
[move.vanish
[1].c
][move.vanish
[1].p
]--;
355 if (this.kingPos
[this.turn
][0] < 0)
356 // King captured (or "chomped")
357 return this.turn
== "w" ? "0-1" : "1-0";
361 static GenRandInitFen(randomness
) {
363 ChessRules
.GenRandInitFen(randomness
).slice(0, -2) +
364 // Add Peach + Mario flags, re-add en-passant + capture counts
370 // TODO: random mover
374 // invisibility used? --> move notation Q??