Advance on Chakart, fix Football GenRandInitFen() + try fixing newly introduced bug...
[vchess.git] / client / src / variants / Chakart.js
CommitLineData
6c7cbfed
BA
1import { ChessRules } from "@/base_rules";
2
3export class ChakartRules extends ChessRules {
4 // NOTE: getBasicMove, ajouter les bonus à vanish array
5 // + déterminer leur effet (si cavalier) ou case (si banane ou bombe)
6 // (L'effet doit être caché au joueur : devrait être OK)
7 //
8 // Saut possible par dessus bonus ou champis mais pas bananes ou bombes
90df90bc 9//==> redefinir isAttackedBySlide et getPotentialSlide...
6c7cbfed 10
90df90bc
BA
11// keep track of captured pieces: comme Grand; pieces can get back to board with toadette bonus.
12// --> pour ce bonus, passer "capture" temporairement en "reserve" pour permettre de jouer le coup.
13
6c7cbfed
BA
14 // "pièces" supplémentaires : bananes, bombes, champis, bonus --> + couleur ?
15 // (Semble mieux sans couleur => couleur spéciale indiquant que c'est pas jouable)
16 // (Attention: pas jouables cf. getPotentialMoves...)
17
90df90bc
BA
18 hoverHighlight(x, y) {
19 // TODO: exact squares
20 return this.subTurn == 2; //&& this.firstMove.donkey or wario or bonus roi boo
21 }
22
b9ce3d0f
BA
23 static get IMMOBILIZE_CODE() {
24 return {
25 'p': 's',
26 'r': 'u',
27 'n': 'o',
28 'b': 'c',
29 'q': 't',
30 'k': 'l'
31 };
32 }
33
34 static get IMMOBILIZE_DECODE() {
35 return {
36 's': 'p',
37 'u': 'r',
38 'o': 'n',
39 'c': 'b',
40 't': 'q',
41 'l': 'k'
42 };
43 }
44
45 static get INVISIBLE_QUEEN() {
46 return 'i';
47 }
48
49 getPpath(b) {
50 let prefix = "";
51 if (
52 b[1] == V.INVISIBLE_QUEEN ||
53 Object.keys(V.IMMOBILIZE_DECODE).includes(b[1])
54 ) {
55 prefix = "Chakart/";
56 }
57 return prefix + b;
58 }
59
60 static ParseFen(fen) {
61 const fenParts = fen.split(" ");
62 return Object.assign(
63 ChessRules.ParseFen(fen),
64 { captured: fenParts[5] }
65 );
66 }
67
68 // King can be l or L (immobilized) --> similar to Alice variant
90df90bc
BA
69 static IsGoodPosition(position) {
70 if (position.length == 0) return false;
71 const rows = position.split("/");
72 if (rows.length != V.size.x) return false;
b9ce3d0f 73 let kings = { "k": 0, "K": 0, 'l': 0, 'L': 0 };
90df90bc
BA
74 for (let row of rows) {
75 let sumElts = 0;
76 for (let i = 0; i < row.length; i++) {
b9ce3d0f 77 if (['K','k','L','l'].includes(row[i])) kings[row[i]]++;
90df90bc
BA
78 if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
79 else {
80 const num = parseInt(row[i]);
81 if (isNaN(num)) return false;
82 sumElts += num;
83 }
84 }
85 if (sumElts != V.size.y) return false;
86 }
b9ce3d0f
BA
87 if (kings['k'] + kings['l'] != 1 || kings['K'] + kings['L'] != 1)
88 return false;
90df90bc
BA
89 return true;
90 }
91
b9ce3d0f
BA
92 static IsGoodFlags(flags) {
93 // 4 for castle + 4 for Peach + Mario w, b
94 return !!flags.match(/^[a-z]{4,4}[01]{4,4}$/);
95 }
96
97 setFlags(fenflags) {
98 super.setFlags(fenflags); //castleFlags
99 this.powerFlags = {
100 w: [...Array(2)], //king can send red shell? Queen can be invisible?
101 b: [...Array(2)]
102 };
103 const flags = fenflags.substr(4); //skip first 4 letters, for castle
104 for (let c of ["w", "b"]) {
105 for (let i = 0; i < 2; i++)
106 this.pawnFlags[c][i] = flags.charAt((c == "w" ? 0 : 2) + i) == "1";
107 }
108 }
109
110 aggregateFlags() {
111 return [this.castleFlags, this.powerFlags];
112 }
113
114 disaggregateFlags(flags) {
115 this.castleFlags = flags[0];
116 this.powerFlags = flags[1];
117 }
118
119 getFen() {
120 return super.getFen() + " " + this.getCapturedFen();
121 }
122
123 getFenForRepeat() {
124 return super.getFenForRepeat() + "_" + this.getCapturedFen();
125 }
126
127 getCapturedFen() {
128 let counts = [...Array(10).fill(0)];
129 let i = 0;
130 for (let p of [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.PAWN])
131 counts[i] = this.captured["w"][p];
132 counts[5 + i] = this.captured["b"][p];
133 i++;
134 }
135 return counts.join("");
136 }
137
6c7cbfed
BA
138 setOtherVariables(fen) {
139 super.setOtherVariables(fen);
b9ce3d0f
BA
140 const fenParsed = V.ParseFen(fen);
141 // Initialize captured pieces' counts from FEN
142 this.captured = {
143 w: {
144 [V.ROOK]: parseInt(fenParsed.captured[0]),
145 [V.KNIGHT]: parseInt(fenParsed.captured[1]),
146 [V.BISHOP]: parseInt(fenParsed.captured[2]),
147 [V.QUEEN]: parseInt(fenParsed.captured[3]),
148 [V.PAWN]: parseInt(fenParsed.captured[4]),
149 },
150 b: {
151 [V.ROOK]: parseInt(fenParsed.captured[5]),
152 [V.KNIGHT]: parseInt(fenParsed.captured[6]),
153 [V.BISHOP]: parseInt(fenParsed.captured[7]),
154 [V.QUEEN]: parseInt(fenParsed.captured[8]),
155 [V.PAWN]: parseInt(fenParsed.captured[9]),
156 }
157 };
6c7cbfed
BA
158 this.subTurn = 1;
159 }
160
b9ce3d0f
BA
161 getFlagsFen() {
162 let fen = super.getFlagsFen();
163 // Add power flags
164 for (let c of ["w", "b"])
165 for (let i = 0; i < 2; i++) fen += (this.powerFlags[c][i] ? "1" : "0");
166 return fen;
167 }
168
6c7cbfed
BA
169 getPotentialMovesFrom([x, y]) {
170 // TODO: bananes et bombes limitent les déplacements (agissent comme un mur "capturable")
171 // bananes jaunes et rouges ?! (agissant sur une seule couleur ?) --> mauvaise idée.
172 if (this.subTurn == 2) {
173 // TODO: coup compatible avec firstMove
174 }
90df90bc
BA
175 //Détails :
176 //Si une pièce pose quelque chose sur une case ça remplace ce qui y était déjà.
b9ce3d0f 177 // TODO: un-immobilize my immobilized piece at the end of this turn, if any
6c7cbfed
BA
178 }
179
180 getPotentialPawnMoves(sq) {
181 //Toad: pion
90df90bc
BA
182 // laisse sur sa case de départ un champi turbo permettant à Peach et cavalier et autres pions d'aller
183 // un dep plus loin (evt 2 cases si pion saut initial), et aux pièces arrivant sur cette case de sauter par
184 // dessus une pièce immédiatement adjacente dans leur trajectoire (en atterissant juste derrière).
6c7cbfed
BA
185 }
186
fa74f624 187 // Coups en 2 temps (si pose possible)
6c7cbfed
BA
188 getPotentialRookMoves(sq) {
189 //Donkey : tour
190 // pose une banane (optionnel) sur une case adjacente (diagonale) à celle d'arrivée
191 // Si une pièce arrive sur la peau de banane, alors elle effectue un déplacement
192 // aléatoire d'une (2?) case (vertical ou horizontal) depuis sa position finale.
193 }
194
195 // Coups en 2 temps (si pose)
196 getPotentialBishopMoves([x, y]) {
197 //Wario: fou
198 // pose une bombe (optionnel) sur une case orthogonalement adjacente à la case d'arrivée
199 // Si une pièce arrive sur une bombe, alors elle effectue un déplacement diagonal
200 // aléatoire d'une (2?) case depuis sa position finale (juste une case si impossible).
201 }
202
203 getPotentialKnightMoves([x, y]) {
204 //Yoshi: cavalier
205 // laisse sur sa case de départ un bonus aléatoire
206 // (NOTE: certains bonus pourraient ne pas être applicables ==> pion bloqué par exemple)
207 // - i) roi boo(*E*) : échange avec n'importe quelle pièce (choix du joueur, type et/ou couleur différents)
6c7cbfed
BA
208 // - i*) koopa(*B*) : ramène sur la case initiale
209 // - ii) toadette(*R*) : permet de poser une pièce capturée sur le plateau
210 // (n'importe où sauf 8eme rangée pour les pions)
211 // - ii*) chomp(*W*) : mange la pièce ; si c'est Peach, c'est perdu
212 // - iii) daisy(*T*) : permet de rejouer un coup avec la même pièce --> cumulable si ensuite coup sur bonus Daisy.
213 // - iii*) bowser(*M*) : immobilise la pièce (marquée jaune/rouge), qui ne pourra pas jouer au tour suivant
90df90bc
BA
214 // - iv) luigi(*L*) : fait changer de camp une pièce adverse (aléatoire) (sauf le roi)
215 // - iv*) waluigi(*D*) : fait changer de camp une de nos pièces (aléatoire, sauf le roi)
6c7cbfed 216 // --> i, ii, iii en deux temps (subTurn 1 & 2)
6c7cbfed
BA
217 }
218
219 getPotentialQueenMoves(sq) {
220 //Mario: dame
90df90bc
BA
221 // pouvoir "fantôme" : peut effectuer une fois dans la partie un coup non-capturant invisible (=> choix à chaque coup, getPPpath(m) teste m.nvisible...)
222 //wg bg ghost once in the game the queen can make an invisible move --> printed as "?"
6c7cbfed
BA
223 }
224
225 getPotentialKingMoves(sq) {
226 //Peach: roi
227 // Carapace rouge (disons ^^) jouable une seule fois dans la partie,
228 // au lieu de se déplacer. Capture un ennemi au choix parmi les plus proches,
90df90bc 229 // à condition qu'ils soient visibles (suivant les directions de déplacement d'une dame).
6c7cbfed
BA
230 // Profite des accélérateurs posés par les pions (+ 1 case : obligatoire).
231 }
232
90df90bc
BA
233 atLeastOneMove() {
234 // TODO: check that
235 return true;
236 }
237
6c7cbfed 238 play(move) {
90df90bc
BA
239 // TODO: subTurn passe à 2 si arrivée sur bonus cavalier
240 // potentiellement pose (tour, fou) ou si choix (reconnaître i (ok), ii (ok) et iii (si coup normal + pas immobilisé) ?)
6c7cbfed 241 // voire +2 si plusieurs daisy...
90df90bc 242 // si pièce immobilisée de ma couleur : elle redevient utilisable (changer status fin de play)
6c7cbfed
BA
243 }
244
245 undo(move) {
246 // TODO: reconnaissance inverse si subTurn == 1 --> juste impossible ==> marquer pendant play (comme DoubleMove1 : move.turn = ...)
247 }
248
1c15969e
BA
249 doClick(square) {
250 // A click to promote a piece on subTurn 2 would trigger this.
251 // For now it would then return [NaN, NaN] because surrounding squares
252 // have no IDs in the promotion modal. TODO: improve this?
107dc1bd 253 if (isNaN(square[0])) return null;
1c15969e
BA
254 // If subTurn == 2:
255 // if square is empty && firstMove is compatible,
256 // complete the move (banana or bomb).
257 // if square not empty, just complete with empty move
258 const Lf = this.firstMove.length;
259 if (this.subTurn == 2) {
260 if (
261 this.board[square[0]][square[1]] == V.EMPTY &&
262 !this.underCheck(this.turn) &&
263 (La == 0 || !this.oppositeMoves(this.amoves[La-1], this.firstMove[Lf-1]))
264 ) {
265 return {
266 start: { x: -1, y: -1 },
267 end: { x: -1, y: -1 },
268 appear: [],
269 vanish: []
270 };
271 }
272 }
273 return null;
274 }
275
276 postPlay(move) {
277 // TODO: king may also be "chomped"
278 super.updateCastleFlags(move, piece);
279 }
b9ce3d0f
BA
280 postPlay(move) {
281 super.postPlay(move);
282 if (move.vanish.length == 2 && move.appear.length == 1)
283 // Capture: update this.captured
284 this.captured[move.vanish[1].c][move.vanish[1].p]++;
285 }
286
287 postUndo(move) {
288 super.postUndo(move);
289 if (move.vanish.length == 2 && move.appear.length == 1)
290 this.captured[move.vanish[1].c][move.vanish[1].p]--;
291 }
1c15969e 292
6c7cbfed 293 getCurrentScore() {
1c15969e
BA
294 if (this.kingPos[this.turn][0] < 0)
295 // King captured (or "chomped")
296 return this.turn == "w" ? "0-1" : "1-0";
6c7cbfed
BA
297 return '*';
298 }
299
b9ce3d0f
BA
300 static GenRandInitFen(randomness) {
301 return (
302 ChessRules.GenRandInitFen(randomness).slice(0, -2) +
303 // Add Peach + Mario flags, re-add en-passant + capture counts
304 "0000 - 0000000000"
305 );
306 }
307
1c15969e
BA
308 getComputerMove() {
309 // TODO: random mover
310 }
b9ce3d0f
BA
311
312 getNotation(move) {
313 // invisibility used? --> move notation Q??
314
315 }
6c7cbfed 316};