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 | ||
43c0f1d8 | 93 | // TODO: when pawn is pushed to 8th rank, apply promotions (similar change as in Checkered) |
1af36beb BA |
94 | getBasicMove([sx,sy], [ex,ey], tr) |
95 | { | |
96 | var mv = new Move({ | |
97 | appear: [ | |
98 | new PiPo({ | |
99 | x: ex, | |
100 | y: ey, | |
101 | c: !!tr ? tr.c : this.getColor(sx,sy), | |
102 | p: !!tr ? tr.p : this.getPiece(sx,sy) | |
103 | }) | |
104 | ], | |
105 | vanish: [ | |
106 | new PiPo({ | |
107 | x: sx, | |
108 | y: sy, | |
109 | c: this.getColor(sx,sy), | |
110 | p: this.getPiece(sx,sy) | |
111 | }) | |
112 | ] | |
113 | }); | |
114 | ||
115 | if (this.board[ex][ey] != VariantRules.EMPTY) | |
116 | { | |
117 | mv.vanish.push( | |
118 | new PiPo({ | |
119 | x: ex, | |
120 | y: ey, | |
121 | c: this.getColor(ex,ey), | |
122 | p: this.getPiece(ex,ey) | |
123 | }) | |
124 | ); | |
125 | } | |
126 | this.applyMagneticLaws([ex,ey], mv); | |
127 | return mv; | |
128 | } | |
129 | ||
7f0711a8 BA |
130 | getPotentialPawnMoves([x,y]) |
131 | { | |
132 | const color = this.getColor(x,y); | |
133 | var moves = []; | |
134 | var V = VariantRules; | |
135 | const [sizeX,sizeY] = VariantRules.size; | |
136 | let shift = (color == "w" ? -1 : 1); | |
137 | let startRank = (color == "w" ? sizeY-2 : 1); | |
138 | let firstRank = (color == 'w' ? sizeY-1 : 0); | |
139 | let lastRank = (color == "w" ? 0 : sizeY-1); | |
140 | ||
141 | if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank) | |
142 | { | |
143 | // Normal moves | |
144 | if (this.board[x+shift][y] == V.EMPTY) | |
145 | { | |
146 | moves.push(this.getBasicMove([x,y], [x+shift,y])); | |
147 | if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY) | |
148 | { | |
149 | // Two squares jump | |
150 | moves.push(this.getBasicMove([x,y], [x+2*shift,y])); | |
151 | } | |
152 | } | |
153 | // Captures | |
154 | if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY) | |
155 | moves.push(this.getBasicMove([x,y], [x+shift,y-1])); | |
156 | if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY) | |
157 | moves.push(this.getBasicMove([x,y], [x+shift,y+1])); | |
158 | } | |
159 | ||
160 | if (x+shift == lastRank) | |
161 | { | |
162 | // Promotion | |
163 | let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]; | |
164 | promotionPieces.forEach(p => { | |
165 | // Normal move | |
166 | if (this.board[x+shift][y] == V.EMPTY) | |
167 | moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p})); | |
168 | // Captures | |
169 | if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY) | |
170 | moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p})); | |
171 | if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY) | |
172 | moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p})); | |
173 | }); | |
174 | } | |
175 | ||
176 | // No en passant | |
177 | ||
178 | return moves; | |
179 | } | |
180 | ||
1af36beb BA |
181 | getCastleMoves([x,y]) |
182 | { | |
183 | const c = this.getColor(x,y); | |
184 | if (x != (c=="w" ? 7 : 0) || y != this.INIT_COL_KING[c]) | |
185 | return []; //x isn't first rank, or king has moved (shortcut) | |
186 | ||
187 | const V = VariantRules; | |
188 | ||
189 | // Castling ? | |
190 | const oppCol = this.getOppCol(c); | |
191 | let moves = []; | |
192 | let i = 0; | |
193 | const finalSquares = [ [2,3], [6,5] ]; //king, then rook | |
194 | castlingCheck: | |
195 | for (let castleSide=0; castleSide < 2; castleSide++) //large, then small | |
196 | { | |
197 | if (!this.flags[c][castleSide]) | |
198 | continue; | |
199 | // If this code is reached, rooks and king are on initial position | |
200 | ||
201 | // Nothing on the path of the king (and no checks; OK also if y==finalSquare)? | |
202 | let step = finalSquares[castleSide][0] < y ? -1 : 1; | |
203 | for (i=y; i!=finalSquares[castleSide][0]; i+=step) | |
204 | { | |
205 | if (this.isAttacked([x,i], oppCol) || (this.board[x][i] != V.EMPTY && | |
206 | // NOTE: next check is enough, because of chessboard constraints | |
207 | (this.getColor(x,i) != c || ![V.KING,V.ROOK].includes(this.getPiece(x,i))))) | |
208 | { | |
209 | continue castlingCheck; | |
210 | } | |
211 | } | |
212 | ||
213 | // Nothing on the path to the rook? | |
214 | step = castleSide == 0 ? -1 : 1; | |
215 | for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step) | |
216 | { | |
217 | if (this.board[x][i] != V.EMPTY) | |
218 | continue castlingCheck; | |
219 | } | |
220 | const rookPos = this.INIT_COL_ROOK[c][castleSide]; | |
221 | ||
222 | // Nothing on final squares, except maybe king and castling rook? | |
223 | for (i=0; i<2; i++) | |
224 | { | |
225 | if (this.board[x][finalSquares[castleSide][i]] != V.EMPTY && | |
226 | this.getPiece(x,finalSquares[castleSide][i]) != V.KING && | |
227 | finalSquares[castleSide][i] != rookPos) | |
228 | { | |
229 | continue castlingCheck; | |
230 | } | |
231 | } | |
232 | ||
233 | // If this code is reached, castle is valid | |
234 | let cmove = new Move({ | |
235 | appear: [ | |
236 | new PiPo({x:x,y:finalSquares[castleSide][0],p:V.KING,c:c}), | |
237 | new PiPo({x:x,y:finalSquares[castleSide][1],p:V.ROOK,c:c})], | |
238 | vanish: [ | |
239 | new PiPo({x:x,y:y,p:V.KING,c:c}), | |
240 | new PiPo({x:x,y:rookPos,p:V.ROOK,c:c})], | |
241 | end: Math.abs(y - rookPos) <= 2 | |
242 | ? {x:x, y:rookPos} | |
243 | : {x:x, y:y + 2 * (castleSide==0 ? -1 : 1)} | |
244 | }); | |
245 | this.applyMagneticLaws([x,finalSquares[castleSide][1]], cmove); | |
246 | moves.push(cmove); | |
247 | } | |
248 | ||
249 | return moves; | |
250 | } | |
251 | ||
252 | // TODO: verify this assertion | |
253 | // atLeastOneMove() | |
254 | // { | |
255 | // return true; //always at least one possible move | |
256 | // } | |
257 | ||
258 | underCheck(move) | |
259 | { | |
260 | return false; //there is no check | |
261 | } | |
262 | ||
263 | getCheckSquares(move) | |
264 | { | |
265 | const c = this.getOppCol(this.turn); //opponent | |
266 | const saveKingPos = this.kingPos[c]; //king might be taken | |
267 | this.play(move); | |
268 | // The only way to be "under check" is to have lost the king (thus game over) | |
7f0711a8 | 269 | let res = this.kingPos[c][0] < 0 |
1af36beb BA |
270 | ? [ JSON.parse(JSON.stringify(saveKingPos)) ] |
271 | : [ ]; | |
272 | this.undo(move); | |
273 | return res; | |
274 | } | |
275 | ||
276 | updateVariables(move) | |
277 | { | |
278 | super.updateVariables(move); | |
279 | const c = this.getColor(move.start.x,move.start.y); | |
280 | if (c != this.getColor(move.end.x,move.end.y) | |
281 | && this.board[move.end.x][move.end.y] != VariantRules.EMPTY | |
282 | && this.getPiece(move.end.x,move.end.y) == VariantRules.KING) | |
283 | { | |
284 | // We took opponent king ! | |
285 | const oppCol = this.getOppCol(c); | |
286 | this.kingPos[oppCol] = [-1,-1]; | |
287 | this.flags[oppCol] = [false,false]; | |
288 | } | |
289 | } | |
290 | ||
291 | unupdateVariables(move) | |
292 | { | |
293 | super.unupdateVariables(move); | |
294 | const c = this.getColor(move.start.x,move.start.y); | |
295 | const oppCol = this.getOppCol(c); | |
296 | if (this.kingPos[oppCol][0] < 0) | |
297 | { | |
298 | // Last move took opponent's king | |
299 | for (let psq of move.vanish) | |
300 | { | |
301 | if (psq.p == 'k') | |
302 | { | |
303 | this.kingPos[oppCol] = [psq.x, psq.y]; | |
304 | break; | |
305 | } | |
306 | } | |
307 | } | |
308 | } | |
309 | ||
310 | checkGameOver() | |
311 | { | |
312 | if (this.checkRepetition()) | |
313 | return "1/2"; | |
314 | ||
315 | const color = this.turn; | |
316 | // TODO: do we need "atLeastOneMove()"? | |
317 | if (this.atLeastOneMove() && this.kingPos[color][0] >= 0) | |
318 | return "*"; | |
319 | ||
320 | return this.checkGameEnd(); | |
321 | } | |
322 | ||
323 | checkGameEnd() | |
324 | { | |
325 | // No valid move: our king disappeared | |
326 | return this.turn == "w" ? "0-1" : "1-0"; | |
327 | } | |
328 | } |