Commit | Line | Data |
---|---|---|
7f0711a8 | 1 | class MagneticRules extends ChessRules |
1af36beb BA |
2 | { |
3 | getEpSquare(move) | |
4 | { | |
5 | return undefined; //no en-passant | |
6 | } | |
7 | ||
8 | // Complete a move with magnetic actions | |
9 | applyMagneticLaws([x,y], move) | |
10 | { | |
7f0711a8 BA |
11 | const standardMove = JSON.parse(JSON.stringify(move)); |
12 | this.play(standardMove); | |
13 | const color = this.getColor(x,y); | |
14 | const [sizeX,sizeY] = VariantRules.size; | |
15 | for (let step of [[-1,0],[1,0],[0,-1],[0,1]]) | |
16 | { | |
17 | let [i,j] = [x+step[0],y+step[1]]; | |
18 | while (i>=0 && i<sizeX && j>=0 && j<sizeY) | |
19 | { | |
20 | if (this.board[i][j] != VariantRules.EMPTY) | |
21 | { | |
22 | // Found something. Same color or not? | |
23 | if (this.getColor(i,j) != color) | |
24 | { | |
25 | // Attraction | |
26 | if ((Math.abs(i-x)>=2 || Math.abs(j-y)>=2) | |
27 | && this.getPiece(i,j) != VariantRules.KING) | |
28 | { | |
29 | move.vanish.push( | |
30 | new PiPo({ | |
31 | p:this.getPiece(i,j), | |
32 | c:this.getColor(i,j), | |
33 | x:i, | |
34 | y:j | |
35 | }) | |
36 | ); | |
37 | move.appear.push( | |
38 | new PiPo({ | |
39 | p:this.getPiece(i,j), | |
40 | c:this.getColor(i,j), | |
41 | x:x+step[0], | |
42 | y:y+step[1] | |
43 | }) | |
44 | ); | |
45 | } | |
46 | } | |
47 | else | |
48 | { | |
49 | // Repulsion | |
50 | if (this.getPiece(i,j) != VariantRules.KING) | |
51 | { | |
52 | // Push it until we meet an obstacle or edge of the board | |
53 | let [ii,jj] = [i+step[0],j+step[1]]; | |
54 | while (ii>=0 && ii<sizeX && jj>=0 && jj<sizeY) | |
55 | { | |
56 | if (this.board[ii][jj] != VariantRules.EMPTY) | |
57 | break; | |
58 | ii += step[0]; | |
59 | jj += step[1]; | |
60 | } | |
61 | ii -= step[0]; | |
62 | jj -= step[1]; | |
63 | if (Math.abs(ii-i)>=1 || Math.abs(jj-j)>=1) | |
64 | { | |
65 | move.vanish.push( | |
66 | new PiPo({ | |
67 | p:this.getPiece(i,j), | |
68 | c:this.getColor(i,j), | |
69 | x:i, | |
70 | y:j | |
71 | }) | |
72 | ); | |
73 | move.appear.push( | |
74 | new PiPo({ | |
75 | p:this.getPiece(i,j), | |
76 | c:this.getColor(i,j), | |
77 | x:ii, | |
78 | y:jj | |
79 | }) | |
80 | ); | |
81 | } | |
82 | } | |
83 | } | |
84 | break; | |
85 | } | |
86 | i += step[0]; | |
87 | j += step[1]; | |
88 | } | |
89 | } | |
90 | this.undo(standardMove); | |
1af36beb BA |
91 | } |
92 | ||
93 | getBasicMove([sx,sy], [ex,ey], tr) | |
94 | { | |
95 | var mv = new Move({ | |
96 | appear: [ | |
97 | new PiPo({ | |
98 | x: ex, | |
99 | y: ey, | |
100 | c: !!tr ? tr.c : this.getColor(sx,sy), | |
101 | p: !!tr ? tr.p : this.getPiece(sx,sy) | |
102 | }) | |
103 | ], | |
104 | vanish: [ | |
105 | new PiPo({ | |
106 | x: sx, | |
107 | y: sy, | |
108 | c: this.getColor(sx,sy), | |
109 | p: this.getPiece(sx,sy) | |
110 | }) | |
111 | ] | |
112 | }); | |
113 | ||
114 | if (this.board[ex][ey] != VariantRules.EMPTY) | |
115 | { | |
116 | mv.vanish.push( | |
117 | new PiPo({ | |
118 | x: ex, | |
119 | y: ey, | |
120 | c: this.getColor(ex,ey), | |
121 | p: this.getPiece(ex,ey) | |
122 | }) | |
123 | ); | |
124 | } | |
125 | this.applyMagneticLaws([ex,ey], mv); | |
126 | return mv; | |
127 | } | |
128 | ||
7f0711a8 BA |
129 | getPotentialPawnMoves([x,y]) |
130 | { | |
131 | const color = this.getColor(x,y); | |
132 | var moves = []; | |
133 | var V = VariantRules; | |
134 | const [sizeX,sizeY] = VariantRules.size; | |
135 | let shift = (color == "w" ? -1 : 1); | |
136 | let startRank = (color == "w" ? sizeY-2 : 1); | |
137 | let firstRank = (color == 'w' ? sizeY-1 : 0); | |
138 | let lastRank = (color == "w" ? 0 : sizeY-1); | |
139 | ||
140 | if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank) | |
141 | { | |
142 | // Normal moves | |
143 | if (this.board[x+shift][y] == V.EMPTY) | |
144 | { | |
145 | moves.push(this.getBasicMove([x,y], [x+shift,y])); | |
146 | if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY) | |
147 | { | |
148 | // Two squares jump | |
149 | moves.push(this.getBasicMove([x,y], [x+2*shift,y])); | |
150 | } | |
151 | } | |
152 | // Captures | |
153 | if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY) | |
154 | moves.push(this.getBasicMove([x,y], [x+shift,y-1])); | |
155 | if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY) | |
156 | moves.push(this.getBasicMove([x,y], [x+shift,y+1])); | |
157 | } | |
158 | ||
159 | if (x+shift == lastRank) | |
160 | { | |
161 | // Promotion | |
162 | let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]; | |
163 | promotionPieces.forEach(p => { | |
164 | // Normal move | |
165 | if (this.board[x+shift][y] == V.EMPTY) | |
166 | moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p})); | |
167 | // Captures | |
168 | if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY) | |
169 | moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p})); | |
170 | if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY) | |
171 | moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p})); | |
172 | }); | |
173 | } | |
174 | ||
175 | // No en passant | |
176 | ||
177 | return moves; | |
178 | } | |
179 | ||
1af36beb BA |
180 | getCastleMoves([x,y]) |
181 | { | |
182 | const c = this.getColor(x,y); | |
183 | if (x != (c=="w" ? 7 : 0) || y != this.INIT_COL_KING[c]) | |
184 | return []; //x isn't first rank, or king has moved (shortcut) | |
185 | ||
186 | const V = VariantRules; | |
187 | ||
188 | // Castling ? | |
189 | const oppCol = this.getOppCol(c); | |
190 | let moves = []; | |
191 | let i = 0; | |
192 | const finalSquares = [ [2,3], [6,5] ]; //king, then rook | |
193 | castlingCheck: | |
194 | for (let castleSide=0; castleSide < 2; castleSide++) //large, then small | |
195 | { | |
196 | if (!this.flags[c][castleSide]) | |
197 | continue; | |
198 | // If this code is reached, rooks and king are on initial position | |
199 | ||
200 | // Nothing on the path of the king (and no checks; OK also if y==finalSquare)? | |
201 | let step = finalSquares[castleSide][0] < y ? -1 : 1; | |
202 | for (i=y; i!=finalSquares[castleSide][0]; i+=step) | |
203 | { | |
204 | if (this.isAttacked([x,i], oppCol) || (this.board[x][i] != V.EMPTY && | |
205 | // NOTE: next check is enough, because of chessboard constraints | |
206 | (this.getColor(x,i) != c || ![V.KING,V.ROOK].includes(this.getPiece(x,i))))) | |
207 | { | |
208 | continue castlingCheck; | |
209 | } | |
210 | } | |
211 | ||
212 | // Nothing on the path to the rook? | |
213 | step = castleSide == 0 ? -1 : 1; | |
214 | for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step) | |
215 | { | |
216 | if (this.board[x][i] != V.EMPTY) | |
217 | continue castlingCheck; | |
218 | } | |
219 | const rookPos = this.INIT_COL_ROOK[c][castleSide]; | |
220 | ||
221 | // Nothing on final squares, except maybe king and castling rook? | |
222 | for (i=0; i<2; i++) | |
223 | { | |
224 | if (this.board[x][finalSquares[castleSide][i]] != V.EMPTY && | |
225 | this.getPiece(x,finalSquares[castleSide][i]) != V.KING && | |
226 | finalSquares[castleSide][i] != rookPos) | |
227 | { | |
228 | continue castlingCheck; | |
229 | } | |
230 | } | |
231 | ||
232 | // If this code is reached, castle is valid | |
233 | let cmove = new Move({ | |
234 | appear: [ | |
235 | new PiPo({x:x,y:finalSquares[castleSide][0],p:V.KING,c:c}), | |
236 | new PiPo({x:x,y:finalSquares[castleSide][1],p:V.ROOK,c:c})], | |
237 | vanish: [ | |
238 | new PiPo({x:x,y:y,p:V.KING,c:c}), | |
239 | new PiPo({x:x,y:rookPos,p:V.ROOK,c:c})], | |
240 | end: Math.abs(y - rookPos) <= 2 | |
241 | ? {x:x, y:rookPos} | |
242 | : {x:x, y:y + 2 * (castleSide==0 ? -1 : 1)} | |
243 | }); | |
244 | this.applyMagneticLaws([x,finalSquares[castleSide][1]], cmove); | |
245 | moves.push(cmove); | |
246 | } | |
247 | ||
248 | return moves; | |
249 | } | |
250 | ||
251 | // TODO: verify this assertion | |
252 | // atLeastOneMove() | |
253 | // { | |
254 | // return true; //always at least one possible move | |
255 | // } | |
256 | ||
257 | underCheck(move) | |
258 | { | |
259 | return false; //there is no check | |
260 | } | |
261 | ||
262 | getCheckSquares(move) | |
263 | { | |
264 | const c = this.getOppCol(this.turn); //opponent | |
265 | const saveKingPos = this.kingPos[c]; //king might be taken | |
266 | this.play(move); | |
267 | // The only way to be "under check" is to have lost the king (thus game over) | |
7f0711a8 | 268 | let res = this.kingPos[c][0] < 0 |
1af36beb BA |
269 | ? [ JSON.parse(JSON.stringify(saveKingPos)) ] |
270 | : [ ]; | |
271 | this.undo(move); | |
272 | return res; | |
273 | } | |
274 | ||
275 | updateVariables(move) | |
276 | { | |
277 | super.updateVariables(move); | |
278 | const c = this.getColor(move.start.x,move.start.y); | |
279 | if (c != this.getColor(move.end.x,move.end.y) | |
280 | && this.board[move.end.x][move.end.y] != VariantRules.EMPTY | |
281 | && this.getPiece(move.end.x,move.end.y) == VariantRules.KING) | |
282 | { | |
283 | // We took opponent king ! | |
284 | const oppCol = this.getOppCol(c); | |
285 | this.kingPos[oppCol] = [-1,-1]; | |
286 | this.flags[oppCol] = [false,false]; | |
287 | } | |
288 | } | |
289 | ||
290 | unupdateVariables(move) | |
291 | { | |
292 | super.unupdateVariables(move); | |
293 | const c = this.getColor(move.start.x,move.start.y); | |
294 | const oppCol = this.getOppCol(c); | |
295 | if (this.kingPos[oppCol][0] < 0) | |
296 | { | |
297 | // Last move took opponent's king | |
298 | for (let psq of move.vanish) | |
299 | { | |
300 | if (psq.p == 'k') | |
301 | { | |
302 | this.kingPos[oppCol] = [psq.x, psq.y]; | |
303 | break; | |
304 | } | |
305 | } | |
306 | } | |
307 | } | |
308 | ||
309 | checkGameOver() | |
310 | { | |
311 | if (this.checkRepetition()) | |
312 | return "1/2"; | |
313 | ||
314 | const color = this.turn; | |
315 | // TODO: do we need "atLeastOneMove()"? | |
316 | if (this.atLeastOneMove() && this.kingPos[color][0] >= 0) | |
317 | return "*"; | |
318 | ||
319 | return this.checkGameEnd(); | |
320 | } | |
321 | ||
322 | checkGameEnd() | |
323 | { | |
324 | // No valid move: our king disappeared | |
325 | return this.turn == "w" ? "0-1" : "1-0"; | |
326 | } | |
327 | } |