Bugs fixing, finalization of rules in french+english
[vchess.git] / public / javascripts / variants / Dark.js
CommitLineData
388e4c40 1class DarkRules extends ChessRules
375ecdd1
BA
2{
3 // Standard rules, in the shadow
4 setOtherVariables(fen)
5 {
6 super.setOtherVariables(fen);
388e4c40 7 const [sizeX,sizeY] = [V.size.x,V.size.y];
375ecdd1 8 this.enlightened = {
388e4c40
BA
9 "w": doubleArray(sizeX,sizeY),
10 "b": doubleArray(sizeX,sizeY)
375ecdd1 11 };
388e4c40
BA
12 // Setup enlightened: squares reachable by each side
13 // (TODO: one side would be enough ?)
14 this.updateEnlightened();
375ecdd1
BA
15 }
16
388e4c40 17 updateEnlightened()
375ecdd1 18 {
69f3d801 19 const pawnShift = {"w":-1, "b":1};
388e4c40
BA
20 // Initialize with pieces positions (which are seen)
21 for (let i=0; i<V.size.x; i++)
22 {
23 for (let j=0; j<V.size.y; j++)
24 {
25 this.enlightened["w"][i][j] = false;
26 this.enlightened["b"][i][j] = false;
27 if (this.board[i][j] != V.EMPTY)
69f3d801
BA
28 {
29 const color = this.getColor(i,j);
30 this.enlightened[color][i][j] = true;
31 // Add potential squares visible by "impossible pawn capture"
32 if (this.getPiece(i,j) == V.PAWN)
33 {
34 for (let shiftY of [-1,1])
35 {
36 if (V.OnBoard(i+pawnShift[color],j+shiftY)
37 && this.board[i+pawnShift[color]][j+shiftY] == V.EMPTY)
38 {
39 this.enlightened[color][i+pawnShift[color]][j+shiftY] = true;
40 }
41 }
42 }
43 }
388e4c40
BA
44 }
45 }
46 const currentTurn = this.turn;
47 this.turn = "w";
48 const movesWhite = this.getAllValidMoves();
49 this.turn = "b";
50 const movesBlack = this.getAllValidMoves();
51 this.turn = currentTurn;
52 for (let move of movesWhite)
53 this.enlightened["w"][move.end.x][move.end.y] = true;
54 for (let move of movesBlack)
55 this.enlightened["b"][move.end.x][move.end.y] = true;
375ecdd1
BA
56 }
57
f6dbe8e3
BA
58 // Has to be redefined to avoid an infinite loop
59 getAllValidMoves()
60 {
61 const color = this.turn;
62 const oppCol = this.getOppCol(color);
63 let potentialMoves = [];
64 for (let i=0; i<V.size.x; i++)
65 {
66 for (let j=0; j<V.size.y; j++)
67 {
68 if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == color)
69 Array.prototype.push.apply(potentialMoves, this.getPotentialMovesFrom([i,j]));
70 }
71 }
72 return potentialMoves; //because there are no checks
73 }
74
375ecdd1
BA
75 atLeastOneMove()
76 {
77 if (this.kingPos[this.turn][0] < 0)
78 return false;
79 return true; //TODO: is it right?
80 }
81
f6dbe8e3 82 underCheck(color)
375ecdd1
BA
83 {
84 return false; //there is no check
85 }
86
f6dbe8e3 87 getCheckSquares(color)
375ecdd1 88 {
f6dbe8e3 89 return [];
375ecdd1
BA
90 }
91
388e4c40
BA
92 updateVariables(move)
93 {
69f3d801 94 super.updateVariables(move);
388e4c40
BA
95 if (move.vanish.length >= 2 && move.vanish[1].p == V.KING)
96 {
97 // We took opponent king !
69f3d801 98 this.kingPos[this.turn] = [-1,-1];
388e4c40
BA
99 }
100
101 // Update moves for both colors:
102 this.updateEnlightened();
103 }
104
105 unupdateVariables(move)
106 {
107 super.unupdateVariables(move);
108 const c = move.vanish[0].c;
109 const oppCol = this.getOppCol(c);
110 if (this.kingPos[oppCol][0] < 0)
111 {
112 // Last move took opponent's king
113 for (let psq of move.vanish)
114 {
115 if (psq.p == 'k')
116 {
117 this.kingPos[oppCol] = [psq.x, psq.y];
118 break;
119 }
120 }
121 }
122
123 // Update moves for both colors:
124 this.updateEnlightened();
125 }
375ecdd1
BA
126
127 checkGameEnd()
128 {
129 // No valid move: our king disappeared
130 return this.turn == "w" ? "0-1" : "1-0";
131 }
132
133 static get THRESHOLD_MATE()
134 {
135 return 500; //checkmates evals may be slightly below 1000
136 }
5915f720
BA
137
138 // In this special situation, we just look 1 half move ahead
139 getComputerMove()
140 {
141 const maxeval = V.INFINITY;
142 const color = this.turn;
143 const oppCol = this.getOppCol(color);
144 const pawnShift = (color == "w" ? -1 : 1);
145 const kp = this.kingPos[color];
146
147 // Do not cheat: the current enlightment is all we can see
148 const myLight = JSON.parse(JSON.stringify(this.enlightened[color]));
149
150 // Can a slider on (i,j) apparently take my king?
151 // NOTE: inaccurate because assume yes if some squares are shadowed
152 const sliderTake = ([i,j], piece) => {
153 let step = undefined;
154 if (piece == V.BISHOP)
155 {
156 if (Math.abs(kp[0] - i) == Math.abs(kp[1] - j))
157 {
158 step =
159 [
160 (i-kp[0]) / Math.abs(i-kp[0]),
161 (j-kp[1]) / Math.abs(j-kp[1])
162 ];
163 }
164 }
165 else if (piece == V.ROOK)
166 {
167 if (kp[0] == i)
168 step = [0, (j-kp[1]) / Math.abs(j-kp[1])];
169 else if (kp[1] == j)
170 step = [(i-kp[0]) / Math.abs(i-kp[0]), 0];
171 }
172 if (!step)
173 return false;
174 // Check for obstacles
175 let obstacle = false;
176 for (
177 let x=kp[0]+step[0], y=kp[1]+step[1];
178 x != i && y != j;
179 x += step[0], y+= step[1])
180 {
181 if (myLight[x][y] && this.board[x][y] != V.EMPTY)
182 {
183 obstacle = true;
184 break;
185 }
186 }
187 if (!obstacle)
188 return true;
189 return false;
190 };
191
192 // Do I see something which can take my king ?
193 const kingThreats = () => {
194 for (let i=0; i<V.size.x; i++)
195 {
196 for (let j=0; j<V.size.y; j++)
197 {
198 if (myLight[i][j] && this.board[i][j] != V.EMPTY
199 && this.getColor(i,j) != color)
200 {
201 switch (this.getPiece(i,j))
202 {
203 case V.PAWN:
204 if (kp[0] + pawnShift == i && Math.abs(kp[1]-j) == 1)
205 return true;
206 break;
207 case V.KNIGHT:
208 if ((Math.abs(kp[0] - i) == 2 && Math.abs(kp[1] - j) == 1) ||
209 (Math.abs(kp[0] - i) == 1 && Math.abs(kp[1] - j) == 2))
210 {
211 return true;
212 }
213 break;
214 case V.KING:
215 if (Math.abs(kp[0] - i) == 1 && Math.abs(kp[1] - j) == 1)
216 return true;
217 break;
218 case V.BISHOP:
219 if (sliderTake([i,j], V.BISHOP))
220 return true;
221 break;
222 case V.ROOK:
223 if (sliderTake([i,j], V.ROOK))
224 return true;
225 break;
226 case V.QUEEN:
227 if (sliderTake([i,j], V.BISHOP) || sliderTake([i,j], V.ROOK))
228 return true;
229 break;
230 }
231 }
232 }
233 }
234 return false;
235 };
236
237 let moves = this.getAllValidMoves();
238 for (let move of moves)
239 {
240 this.play(move);
241 if (this.kingPos[oppCol][0] >= 0 && kingThreats())
242 {
243 // We didn't take opponent king, and our king will be captured: bad
244 move.eval = -maxeval;
245 }
246 this.undo(move);
247 if (!!move.eval)
248 continue;
249
250 move.eval = 0; //a priori...
251
252 // Can I take something ? If yes, do it if it seems good...
253 if (move.vanish.length == 2 && move.vanish[1].c != color) //avoid castle
254 {
255 const myPieceVal = V.VALUES[move.appear[0].p];
256 const hisPieceVal = V.VALUES[move.vanish[1].p];
257 if (myPieceVal <= hisPieceVal)
258 move.eval = hisPieceVal - myPieceVal + 2; //favor captures
259 else
260 {
261 // Taking a pawn with minor piece,
262 // or minor piece or pawn with a rook,
263 // or anything but a queen with a queen,
264 // or anything with a king.
265 // ==> Do it at random, although
266 // this is clearly inferior to what a human can deduce...
267 move.eval = (Math.random() < 0.5 ? 1 : -1);
268 }
269 }
270 }
271
272 // TODO: also need to implement the case when an opponent piece (in light)
273 // is threatening something - maybe not the king, but e.g. pawn takes rook.
274
275 moves.sort((a,b) => b.eval - a.eval);
276 let candidates = [0];
277 for (let j=1; j<moves.length && moves[j].eval == moves[0].eval; j++)
278 candidates.push(j);
279 return moves[_.sample(candidates, 1)];
280 }
375ecdd1
BA
281}
282
283const VariantRules = DarkRules;