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