c4995dd3003d9fd0ac36f7780374e455e189703d
[vchess.git] / public / javascripts / variants / Magnetic.js
1 class MagneticRules extends ChessRules
2 {
3 static get HasEnpassant() { return false; }
4
5 getPotentialMovesFrom([x,y])
6 {
7 let standardMoves = super.getPotentialMovesFrom([x,y]);
8 let moves = [];
9 standardMoves.forEach(m => {
10 let newMove_s = this.applyMagneticLaws(m);
11 if (newMove_s.length == 1)
12 moves.push(newMove_s[0]);
13 else //promotion
14 moves = moves.concat(newMove_s);
15 });
16 return moves;
17 }
18
19 getPotentialPawnMoves([x,y])
20 {
21 const color = this.turn;
22 let moves = [];
23 const [sizeX,sizeY] = [V.size.x,V.size.y];
24 const shift = (color == "w" ? -1 : 1);
25 const firstRank = (color == 'w' ? sizeX-1 : 0);
26 const startRank = (color == "w" ? sizeX-2 : 1);
27 const lastRank = (color == "w" ? 0 : sizeX-1);
28
29 if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank)
30 {
31 // Normal moves
32 if (this.board[x+shift][y] == V.EMPTY)
33 {
34 moves.push(this.getBasicMove([x,y], [x+shift,y]));
35 // Next condition because variants with pawns on 1st rank allow them to jump
36 if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
37 {
38 // Two squares jump
39 moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
40 }
41 }
42 // Captures
43 if (y>0 && this.board[x+shift][y-1] != V.EMPTY
44 && this.canTake([x,y], [x+shift,y-1]))
45 {
46 moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
47 }
48 if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
49 && this.canTake([x,y], [x+shift,y+1]))
50 {
51 moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
52 }
53 }
54
55 if (x+shift == lastRank)
56 {
57 // Promotion
58 const pawnColor = this.getColor(x,y); //can be different for checkered
59 let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
60 promotionPieces.forEach(p => {
61 // Normal move
62 if (this.board[x+shift][y] == V.EMPTY)
63 moves.push(this.getBasicMove([x,y], [x+shift,y], {c:pawnColor,p:p}));
64 // Captures
65 if (y>0 && this.board[x+shift][y-1] != V.EMPTY
66 && this.canTake([x,y], [x+shift,y-1]))
67 {
68 moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:pawnColor,p:p}));
69 }
70 if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
71 && this.canTake([x,y], [x+shift,y+1]))
72 {
73 moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:pawnColor,p:p}));
74 }
75 });
76 }
77 return moves; //no en-passant
78 }
79
80 // Complete a move with magnetic actions
81 // TODO: job is done multiple times for (normal) promotions.
82 applyMagneticLaws(move)
83 {
84 if (move.appear[0].p == V.KING && move.appear.length==1)
85 return [move]; //kings are not charged
86 const aIdx = (move.appear[0].p != V.KING ? 0 : 1); //if castling, rook is charged
87 const [x,y] = [move.appear[aIdx].x, move.appear[aIdx].y];
88 const color = this.turn;
89 const lastRank = (color=="w" ? 0 : 7);
90 const standardMove = JSON.parse(JSON.stringify(move));
91 this.play(standardMove);
92 for (let step of [[-1,0],[1,0],[0,-1],[0,1]])
93 {
94 let [i,j] = [x+step[0],y+step[1]];
95 while (V.OnBoard(i,j))
96 {
97 if (this.board[i][j] != V.EMPTY)
98 {
99 // Found something. Same color or not?
100 if (this.getColor(i,j) != color)
101 {
102 // Attraction
103 if ((Math.abs(i-x)>=2 || Math.abs(j-y)>=2) && this.getPiece(i,j) != V.KING)
104 {
105 move.vanish.push(
106 new PiPo({
107 p:this.getPiece(i,j),
108 c:this.getColor(i,j),
109 x:i,
110 y:j
111 })
112 );
113 move.appear.push(
114 new PiPo({
115 p:this.getPiece(i,j),
116 c:this.getColor(i,j),
117 x:x+step[0],
118 y:y+step[1]
119 })
120 );
121 }
122 }
123 else
124 {
125 // Repulsion
126 if (this.getPiece(i,j) != V.KING)
127 {
128 // Push it until we meet an obstacle or edge of the board
129 let [ii,jj] = [i+step[0],j+step[1]];
130 while (V.OnBoard(ii,jj))
131 {
132 if (this.board[ii][jj] != V.EMPTY)
133 break;
134 ii += step[0];
135 jj += step[1];
136 }
137 ii -= step[0];
138 jj -= step[1];
139 if (Math.abs(ii-i)>=1 || Math.abs(jj-j)>=1)
140 {
141 move.vanish.push(
142 new PiPo({
143 p:this.getPiece(i,j),
144 c:this.getColor(i,j),
145 x:i,
146 y:j
147 })
148 );
149 move.appear.push(
150 new PiPo({
151 p:this.getPiece(i,j),
152 c:this.getColor(i,j),
153 x:ii,
154 y:jj
155 })
156 );
157 }
158 }
159 }
160 break;
161 }
162 i += step[0];
163 j += step[1];
164 }
165 }
166 this.undo(standardMove);
167 let moves = [];
168 // Scan move for pawn (max 1) on 8th rank
169 for (let i=1; i<move.appear.length; i++)
170 {
171 if (move.appear[i].p==V.PAWN && move.appear[i].c==color
172 && move.appear[i].x==lastRank)
173 {
174 move.appear[i].p = V.ROOK;
175 moves.push(move);
176 for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN])
177 {
178 let cmove = JSON.parse(JSON.stringify(move));
179 cmove.appear[i].p = piece;
180 moves.push(cmove);
181 }
182 // Swap appear[i] and appear[0] for moves presentation (TODO: this is awkward)
183 moves.forEach(m => {
184 let tmp = m.appear[0];
185 m.appear[0] = m.appear[i];
186 m.appear[i] = tmp;
187 });
188 break;
189 }
190 }
191 if (moves.length == 0) //no pawn on 8th rank
192 moves.push(move);
193 return moves;
194 }
195
196 atLeastOneMove()
197 {
198 if (this.kingPos[this.turn][0] < 0)
199 return false;
200 return true; //TODO: is it right?
201 }
202
203 underCheck(move)
204 {
205 return false; //there is no check
206 }
207
208 getCheckSquares(move)
209 {
210 const c = this.getOppCol(this.turn); //opponent
211 const saveKingPos = this.kingPos[c]; //king might be taken
212 this.play(move);
213 // The only way to be "under check" is to have lost the king (thus game over)
214 let res = this.kingPos[c][0] < 0
215 ? [JSON.parse(JSON.stringify(saveKingPos))]
216 : [];
217 this.undo(move);
218 return res;
219 }
220
221 updateVariables(move)
222 {
223 super.updateVariables(move);
224 const c = this.getColor(move.start.x,move.start.y);
225 if (this.board[move.end.x][move.end.y] != V.EMPTY
226 && c != this.getColor(move.end.x,move.end.y)
227 && this.getPiece(move.end.x,move.end.y) == V.KING)
228 {
229 // We took opponent king !
230 const oppCol = this.getOppCol(c);
231 this.kingPos[oppCol] = [-1,-1];
232 this.castleFlags[oppCol] = [false,false];
233 }
234 // Did we magnetically move our (init) rooks or opponents' ones ?
235 const firstRank = (c == "w" ? 7 : 0);
236 const oppFirstRank = 7 - firstRank;
237 const oppCol = this.getOppCol(c);
238 move.vanish.forEach(psq => {
239 if (psq.x == firstRank && this.INIT_COL_ROOK[c].includes(psq.y))
240 this.castleFlags[c][psq.y==this.INIT_COL_ROOK[c][0] ? 0 : 1] = false;
241 else if (psq.x == oppFirstRank && this.INIT_COL_ROOK[oppCol].includes(psq.y))
242 this.castleFlags[oppCol][psq.y==this.INIT_COL_ROOK[oppCol][0] ? 0 : 1] = false;
243 });
244 }
245
246 unupdateVariables(move)
247 {
248 super.unupdateVariables(move);
249 const c = this.getColor(move.start.x,move.start.y);
250 const oppCol = this.getOppCol(c);
251 if (this.kingPos[oppCol][0] < 0)
252 {
253 // Last move took opponent's king
254 for (let psq of move.vanish)
255 {
256 if (psq.p == 'k')
257 {
258 this.kingPos[oppCol] = [psq.x, psq.y];
259 break;
260 }
261 }
262 }
263 }
264
265 checkGameEnd()
266 {
267 // No valid move: our king disappeared
268 return this.turn == "w" ? "0-1" : "1-0";
269 }
270
271 static get THRESHOLD_MATE()
272 {
273 return 500; //checkmates evals may be slightly below 1000
274 }
275 }
276
277 const VariantRules = MagneticRules;