Attempt to fix Alice chess
[vchess.git] / public / javascripts / variants / Alice.js
CommitLineData
270968d6 1class AliceRules extends ChessRules
5bfb0956 2{
0cd8f2bd 3 static get ALICE_PIECES()
5bfb0956 4 {
270968d6
BA
5 return {
6 's': 'p',
7 't': 'q',
8 'u': 'r',
9 'c': 'b',
10 'o': 'n',
11 'l': 'k',
12 };
13 }
14 static get ALICE_CODES()
15 {
16 return {
17 'p': 's',
18 'q': 't',
19 'r': 'u',
20 'b': 'c',
21 'n': 'o',
22 'k': 'l',
23 };
5bfb0956 24 }
a3eb4cc5 25
0cd8f2bd
BA
26 static getPpath(b)
27 {
270968d6 28 return (Object.keys(this.ALICE_PIECES).includes(b[1]) ? "Alice/" : "") + b;
0cd8f2bd
BA
29 }
30
0b5fa571
BA
31 initVariables(fen)
32 {
33 super.initVariables(fen);
34 const fenParts = fen.split(" ");
35 const position = fenParts[0].split("/");
36 if (this.kingPos["w"][0] < 0 || this.kingPos["b"][0] < 0)
37 {
38 // INIT_COL_XXX won't be used, so no need to set them for Alice kings
39 for (let i=0; i<position.length; i++)
40 {
41 let k = 0; //column index on board
42 for (let j=0; j<position[i].length; j++)
43 {
44 switch (position[i].charAt(j))
45 {
46 case 'l':
47 this.kingPos['b'] = [i,k];
48 break;
49 case 'L':
50 this.kingPos['w'] = [i,k];
51 break;
52 default:
53 let num = parseInt(position[i].charAt(j));
54 if (!isNaN(num))
55 k += (num-1);
56 }
57 k++;
58 }
59 }
60 }
61 }
62
b8121223
BA
63 // Build board of the given (mirror)side
64 getSideBoard(mirrorSide)
0cd8f2bd 65 {
270968d6 66 const V = VariantRules;
270968d6
BA
67 // Build corresponding board from complete board
68 const [sizeX,sizeY] = V.size;
69 let sideBoard = doubleArray(sizeX, sizeY, "");
0cd8f2bd
BA
70 for (let i=0; i<sizeX; i++)
71 {
72 for (let j=0; j<sizeY; j++)
73 {
74 const piece = this.getPiece(i,j);
270968d6
BA
75 if (mirrorSide==1 && Object.keys(V.ALICE_CODES).includes(piece))
76 sideBoard[i][j] = this.board[i][j];
77 else if (mirrorSide==2 && Object.keys(V.ALICE_PIECES).includes(piece))
78 sideBoard[i][j] = this.getColor(i,j) + V.ALICE_PIECES[piece];
0cd8f2bd
BA
79 }
80 }
270968d6
BA
81 return sideBoard;
82 }
0cd8f2bd 83
55eb331d
BA
84 // NOTE: castle & enPassant https://www.chessvariants.com/other.dir/alice.html
85 // --> Should be OK as is.
b8121223 86 getPotentialMovesFrom([x,y], sideBoard)
270968d6 87 {
b8121223
BA
88 const pieces = Object.keys(VariantRules.ALICE_CODES);
89 const codes = Object.keys(VariantRules.ALICE_PIECES);
90 const mirrorSide = (pieces.includes(this.getPiece(x,y)) ? 1 : 2);
0cd8f2bd 91
270968d6
BA
92 // Search valid moves on sideBoard
93 let saveBoard = this.board;
b8121223 94 this.board = sideBoard || this.getSideBoard(mirrorSide);
270968d6 95 let moves = super.getPotentialMovesFrom([x,y]);
0cd8f2bd
BA
96 this.board = saveBoard;
97
98 // Finally filter impossible moves
b8121223 99 let res = moves.filter(m => {
55eb331d 100 if (m.appear.length == 2) //castle
270968d6 101 {
55eb331d
BA
102 // If appear[i] not in vanish array, then must be empty square on other board
103 m.appear.forEach(psq => {
104 if (this.board[psq.x][psq.y] != VariantRules.EMPTY &&
105 ![m.vanish[0].y,m.vanish[1].y].includes(psq.y))
106 {
107 return false;
108 }
109 });
110 }
111 else if (this.board[m.end.x][m.end.y] != VariantRules.EMPTY)
112 {
113 // Attempt to capture
270968d6 114 const piece = this.getPiece(m.end.x,m.end.y);
06ddfe34
BA
115 if ((mirrorSide==1 && codes.includes(piece))
116 || (mirrorSide==2 && pieces.includes(piece)))
270968d6
BA
117 {
118 return false;
119 }
120 }
f6cc7faf
BA
121 // If the move is computed on board1, m.appear change for Alice pieces.
122 if (mirrorSide==1)
123 {
124 m.appear.forEach(psq => { //forEach: castling taken into account
270968d6 125 psq.p = VariantRules.ALICE_CODES[psq.p]; //goto board2
f6cc7faf
BA
126 });
127 }
0b5fa571
BA
128 else //move on board2: mark vanishing pieces as Alice
129 {
130 m.vanish.forEach(psq => {
131 psq.p = VariantRules.ALICE_CODES[psq.p];
132 });
133 }
9de73b71 134 // Fix en-passant captures
a0f5dbaa
BA
135 if (m.vanish[0].p == VariantRules.PAWN
136 && m.vanish.length == 2 && this.board[m.end.x][m.end.y] == VariantRules.EMPTY)
06ddfe34 137 {
9de73b71 138 m.vanish[1].c = this.getOppCol(this.getColor(x,y));
06ddfe34
BA
139 // In the special case of en-passant, if
140 // - board1 takes board2 : vanish[1] --> Alice
141 // - board2 takes board1 : vanish[1] --> normal
142 let van = m.vanish[1];
143 if (mirrorSide==1 && codes.includes(this.getPiece(van.x,van.y)))
144 van.p = VariantRules.ALICE_CODES[van.p];
145 else if (mirrorSide==2 && pieces.includes(this.getPiece(van.x,van.y)))
146 van.p = VariantRules.ALICE_PIECES[van.p];
147 }
270968d6
BA
148 return true;
149 });
b8121223
BA
150 return res;
151 }
152
153 // NOTE: alternative implementation, recompute sideBoard's in this function
154 filterValid(moves, sideBoard)
155 {
156 if (moves.length == 0)
157 return [];
158 const pieces = Object.keys(VariantRules.ALICE_CODES);
159 return moves.filter(m => {
160 // WARNING: for underCheck(), we need the sideBoard of the arrival world !
161 const mirrorSide = (pieces.includes(this.getPiece(m.start.x,m.start.y)) ? 2 : 1);
162 return !this.underCheck(m, !!sideBoard ? sideBoard[mirrorSide-1] : null);
163 });
0cd8f2bd
BA
164 }
165
b8121223
BA
166 getAllValidMoves()
167 {
168 const color = this.turn;
169 const oppCol = this.getOppCol(color);
170 var potentialMoves = [];
171 let [sizeX,sizeY] = VariantRules.size;
172 let sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
173 for (var i=0; i<sizeX; i++)
174 {
175 for (var j=0; j<sizeY; j++)
176 {
177 if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) == color)
178 {
179 const mirrorSide =
180 (Object.keys(VariantRules.ALICE_CODES).includes(this.getPiece(i,j)) ? 1 : 2);
181 Array.prototype.push.apply(potentialMoves,
182 this.getPotentialMovesFrom([i,j], sideBoard[mirrorSide-1]));
183 }
184 }
185 }
186 return this.filterValid(potentialMoves, sideBoard);
187 }
188
189 underCheck(move, sideBoard)
0cd8f2bd 190 {
0cd8f2bd
BA
191 const color = this.turn;
192 this.play(move);
b8121223
BA
193 const pieces = Object.keys(VariantRules.ALICE_CODES);
194 const kp = this.kingPos[color];
195 const mirrorSide = (pieces.includes(this.getPiece(kp[0],kp[1])) ? 1 : 2);
270968d6 196 let saveBoard = this.board;
b8121223 197 this.board = sideBoard || this.getSideBoard(mirrorSide);
0cd8f2bd 198 let res = this.isAttacked(this.kingPos[color], this.getOppCol(color));
270968d6 199 this.board = saveBoard;
0cd8f2bd
BA
200 this.undo(move);
201 return res;
202 }
203
270968d6 204 getCheckSquares(move)
0cd8f2bd 205 {
270968d6
BA
206 this.play(move);
207 const color = this.turn; //opponent
b8121223
BA
208 const pieces = Object.keys(VariantRules.ALICE_CODES);
209 const kp = this.kingPos[color];
210 const mirrorSide = (pieces.includes(this.getPiece(kp[0],kp[1])) ? 1 : 2);
211 let sideBoard = this.getSideBoard(mirrorSide);
270968d6
BA
212 let saveBoard = this.board;
213 this.board = sideBoard;
214 let res = this.isAttacked(this.kingPos[color], this.getOppCol(color))
215 ? [ JSON.parse(JSON.stringify(this.kingPos[color])) ]
216 : [ ];
217 this.board = saveBoard;
218 this.undo(move);
219 return res;
0cd8f2bd 220 }
270968d6 221
0b5fa571
BA
222 updateVariables(move)
223 {
224 super.updateVariables(move); //standard king
225 const piece = this.getPiece(move.start.x,move.start.y);
226 const c = this.getColor(move.start.x,move.start.y);
227 // "l" = Alice king
228 if (piece == "l")
229 {
230 this.kingPos[c][0] = move.appear[0].x;
231 this.kingPos[c][1] = move.appear[0].y;
232 this.castleFlags[c] = [false,false];
233 }
234 }
235
236 unupdateVariables(move)
237 {
238 super.unupdateVariables(move);
239 const c = this.getColor(move.start.x,move.start.y);
240 if (this.getPiece(move.start.x,move.start.y) == "l")
241 this.kingPos[c] = [move.start.x, move.start.y];
242 }
243
0cd8f2bd
BA
244 checkGameEnd()
245 {
b8121223 246 const pieces = Object.keys(VariantRules.ALICE_CODES);
0cd8f2bd 247 const color = this.turn;
b8121223
BA
248 const kp = this.kingPos[color];
249 const mirrorSide = (pieces.includes(this.getPiece(kp[0],kp[1])) ? 1 : 2);
250 let sideBoard = this.getSideBoard(mirrorSide);
270968d6
BA
251 let saveBoard = this.board;
252 this.board = sideBoard;
253 let res = "*";
0cd8f2bd 254 if (!this.isAttacked(this.kingPos[color], this.getOppCol(color)))
270968d6
BA
255 res = "1/2";
256 else
257 res = (color == "w" ? "0-1" : "1-0");
258 this.board = saveBoard;
259 return res;
0cd8f2bd 260 }
9de73b71
BA
261
262 static get VALUES() {
263 return {
264 'p': 1,
265 's': 1,
266 'r': 5,
267 'u': 5,
268 'n': 3,
269 'o': 3,
270 'b': 3,
271 'c': 3,
272 'q': 9,
273 't': 9,
274 'k': 1000,
275 'l': 1000
276 };
277 }
0f51ef98
BA
278
279 getNotation(move)
280 {
281 if (move.appear.length == 2 && move.appear[0].p == VariantRules.KING)
282 {
283 if (move.end.y < move.start.y)
284 return "0-0-0";
285 else
286 return "0-0";
287 }
288
289 const finalSquare =
290 String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
291 const piece = this.getPiece(move.start.x, move.start.y);
292
293 const captureMark = (move.vanish.length > move.appear.length ? "x" : "");
294 let pawnMark = "";
295 if (["p","s"].includes(piece) && captureMark.length == 1)
296 pawnMark = String.fromCharCode(97 + move.start.y); //start column
297
298 // Piece or pawn movement
299 let notation = piece.toUpperCase() + pawnMark + captureMark + finalSquare;
300 if (['s','p'].includes(piece) && !['s','p'].includes(move.appear[0].p))
301 {
302 // Promotion
303 notation += "=" + move.appear[0].p.toUpperCase();
304 }
305 return notation;
306 }
5bfb0956 307}