Almost finished problems logic. TODO: showProblem() part
[vchess.git] / public / javascripts / variants / Ultima.js
CommitLineData
32cfcea4
BA
1class UltimaRules extends ChessRules
2{
2eef6db6
BA
3 static getPpath(b)
4 {
5 if (b[1] == "m") //'m' for Immobilizer (I is too similar to 1)
6 return "Ultima/" + b;
7 return b; //usual piece
8 }
32cfcea4 9
7931e479
BA
10 static get PIECES() {
11 return ChessRules.PIECES.concat([V.IMMOBILIZER]);
12 }
13
14 static IsGoodFlags(flags)
15 {
16 return true; //anything is good: no flags
17 }
18
2eef6db6
BA
19 initVariables(fen)
20 {
21 this.kingPos = {'w':[-1,-1], 'b':[-1,-1]};
22 const fenParts = fen.split(" ");
23 const position = fenParts[0].split("/");
24 for (let i=0; i<position.length; i++)
25 {
26 let k = 0;
27 for (let j=0; j<position[i].length; j++)
28 {
29 switch (position[i].charAt(j))
30 {
31 case 'k':
32 this.kingPos['b'] = [i,k];
33 break;
34 case 'K':
35 this.kingPos['w'] = [i,k];
36 break;
37 default:
38 let num = parseInt(position[i].charAt(j));
39 if (!isNaN(num))
40 k += (num-1);
41 }
42 k++;
43 }
44 }
45 this.epSquares = []; //no en-passant here
46 }
47
48 setFlags(fen)
49 {
50 // TODO: for compatibility?
51 this.castleFlags = {"w":[false,false], "b":[false,false]};
52 }
53
54 static get IMMOBILIZER() { return 'm'; }
55 // Although other pieces keep their names here for coding simplicity,
56 // keep in mind that:
57 // - a "rook" is a coordinator, capturing by coordinating with the king
58 // - a "knight" is a long-leaper, capturing as in draughts
59 // - a "bishop" is a chameleon, capturing as its prey
60 // - a "queen" is a withdrawer, capturing by moving away from pieces
61
2f3c8451
BA
62 // Is piece on square (x,y) immobilized?
63 isImmobilized([x,y])
2eef6db6 64 {
7688bf77
BA
65 const piece = this.getPiece(x,y);
66 const color = this.getColor(x,y);
67 const oppCol = this.getOppCol(color);
7688bf77 68 const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
9d218497 69 outerLoop:
7688bf77
BA
70 for (let step of adjacentSteps)
71 {
72 const [i,j] = [x+step[0],y+step[1]];
0b7d99ec 73 if (V.OnBoard(i,j) && this.board[i][j] != V.EMPTY
7688bf77
BA
74 && this.getColor(i,j) == oppCol)
75 {
76 const oppPiece = this.getPiece(i,j);
2316f8b8 77 if (oppPiece == V.IMMOBILIZER)
7688bf77 78 {
9d218497
BA
79 // Moving is impossible only if this immobilizer is not neutralized
80 for (let step2 of adjacentSteps)
81 {
82 const [i2,j2] = [i+step2[0],j+step2[1]];
2316f8b8
BA
83 if (i2 == x && j2 == y)
84 continue; //skip initial piece!
0b7d99ec
BA
85 if (V.OnBoard(i2,j2) && this.board[i2][j2] != V.EMPTY
86 && this.getColor(i2,j2) == color)
9d218497 87 {
f4fd6580
BA
88 if ([V.BISHOP,V.IMMOBILIZER].includes(this.getPiece(i2,j2)))
89 return false;
9d218497
BA
90 }
91 }
f4fd6580 92 return true; //immobilizer isn't neutralized
7688bf77 93 }
2316f8b8
BA
94 // Chameleons can't be immobilized twice, because there is only one immobilizer
95 if (oppPiece == V.BISHOP && piece == V.IMMOBILIZER)
96 return true;
7688bf77
BA
97 }
98 }
f4fd6580 99 return false;
2f3c8451
BA
100 }
101
102 getPotentialMovesFrom([x,y])
103 {
104 // Pre-check: is thing on this square immobilized?
105 if (this.isImmobilized([x,y]))
106 return [];
2eef6db6
BA
107 switch (this.getPiece(x,y))
108 {
0b7d99ec 109 case V.IMMOBILIZER:
2eef6db6
BA
110 return this.getPotentialImmobilizerMoves([x,y]);
111 default:
112 return super.getPotentialMovesFrom([x,y]);
113 }
2eef6db6
BA
114 }
115
116 getSlideNJumpMoves([x,y], steps, oneStep)
117 {
118 const color = this.getColor(x,y);
119 const piece = this.getPiece(x,y);
120 let moves = [];
2eef6db6
BA
121 outerLoop:
122 for (let step of steps)
123 {
124 let i = x + step[0];
125 let j = y + step[1];
0b7d99ec 126 while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
2eef6db6
BA
127 {
128 moves.push(this.getBasicMove([x,y], [i,j]));
129 if (oneStep !== undefined)
130 continue outerLoop;
131 i += step[0];
132 j += step[1];
133 }
134 // Only king can take on occupied square:
0b7d99ec 135 if (piece==V.KING && V.OnBoard(i,j) && this.canTake([x,y], [i,j]))
2eef6db6 136 moves.push(this.getBasicMove([x,y], [i,j]));
2eef6db6
BA
137 }
138 return moves;
139 }
140
a3c86ec9
BA
141 // Modify capturing moves among listed pawn moves
142 addPawnCaptures(moves, byChameleon)
143 {
0b7d99ec 144 const steps = V.steps[V.ROOK];
a3c86ec9
BA
145 const color = this.turn;
146 const oppCol = this.getOppCol(color);
147 moves.forEach(m => {
148 if (!!byChameleon && m.start.x!=m.end.x && m.start.y!=m.end.y)
149 return; //chameleon not moving as pawn
150 // Try capturing in every direction
151 for (let step of steps)
152 {
153 const sq2 = [m.end.x+2*step[0],m.end.y+2*step[1]];
0b7d99ec 154 if (V.OnBoard(sq2[0],sq2[1]) && this.board[sq2[0]][sq2[1]] != V.EMPTY
a3c86ec9
BA
155 && this.getColor(sq2[0],sq2[1]) == color)
156 {
157 // Potential capture
158 const sq1 = [m.end.x+step[0],m.end.y+step[1]];
0b7d99ec 159 if (this.board[sq1[0]][sq1[1]] != V.EMPTY
a3c86ec9
BA
160 && this.getColor(sq1[0],sq1[1]) == oppCol)
161 {
162 const piece1 = this.getPiece(sq1[0],sq1[1]);
0b7d99ec 163 if (!byChameleon || piece1 == V.PAWN)
a3c86ec9
BA
164 {
165 m.vanish.push(new PiPo({
166 x:sq1[0],
167 y:sq1[1],
168 c:oppCol,
169 p:piece1
170 }));
171 }
172 }
173 }
174 }
175 });
176 }
177
7688bf77 178 // "Pincher"
2eef6db6
BA
179 getPotentialPawnMoves([x,y])
180 {
7688bf77 181 let moves = super.getPotentialRookMoves([x,y]);
a3c86ec9
BA
182 this.addPawnCaptures(moves);
183 return moves;
2eef6db6
BA
184 }
185
a3c86ec9 186 addRookCaptures(moves, byChameleon)
2eef6db6 187 {
a3c86ec9 188 const color = this.turn;
7688bf77
BA
189 const oppCol = this.getOppCol(color);
190 const kp = this.kingPos[color];
7688bf77
BA
191 moves.forEach(m => {
192 // Check piece-king rectangle (if any) corners for enemy pieces
193 if (m.end.x == kp[0] || m.end.y == kp[1])
194 return; //"flat rectangle"
523da5d5
BA
195 const corner1 = [m.end.x, kp[1]];
196 const corner2 = [kp[0], m.end.y];
7688bf77
BA
197 for (let [i,j] of [corner1,corner2])
198 {
0b7d99ec 199 if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol)
7688bf77 200 {
a3c86ec9 201 const piece = this.getPiece(i,j);
0b7d99ec 202 if (!byChameleon || piece == V.ROOK)
a3c86ec9
BA
203 {
204 m.vanish.push( new PiPo({
205 x:i,
206 y:j,
207 p:piece,
208 c:oppCol
209 }) );
210 }
7688bf77
BA
211 }
212 }
213 });
a3c86ec9
BA
214 }
215
216 // Coordinator
217 getPotentialRookMoves(sq)
218 {
219 let moves = super.getPotentialQueenMoves(sq);
220 this.addRookCaptures(moves);
7688bf77 221 return moves;
2eef6db6
BA
222 }
223
7688bf77 224 // Long-leaper
a3c86ec9 225 getKnightCaptures(startSquare, byChameleon)
2eef6db6 226 {
7688bf77 227 // Look in every direction for captures
7688bf77 228 const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
a3c86ec9
BA
229 const color = this.turn;
230 const oppCol = this.getOppCol(color);
231 let moves = [];
232 const [x,y] = [startSquare[0],startSquare[1]];
233 const piece = this.getPiece(x,y); //might be a chameleon!
234 outerLoop:
7688bf77
BA
235 for (let step of steps)
236 {
237 let [i,j] = [x+step[0], y+step[1]];
0b7d99ec 238 while (V.OnBoard(i,j) && this.board[i][j]==V.EMPTY)
7688bf77
BA
239 {
240 i += step[0];
241 j += step[1];
242 }
0b7d99ec 243 if (!V.OnBoard(i,j) || this.getColor(i,j)==color
a3c86ec9
BA
244 || (!!byChameleon && this.getPiece(i,j)!=V.KNIGHT))
245 {
7688bf77 246 continue;
a3c86ec9
BA
247 }
248 // last(thing), cur(thing) : stop if "cur" is our color, or beyond board limits,
249 // or if "last" isn't empty and cur neither. Otherwise, if cur is empty then
250 // add move until cur square; if cur is occupied then stop if !!byChameleon and
251 // the square not occupied by a leaper.
252 let last = [i,j];
253 let cur = [i+step[0],j+step[1]];
254 let vanished = [ new PiPo({x:x,y:y,c:color,p:piece}) ];
0b7d99ec 255 while (V.OnBoard(cur[0],cur[1]))
a3c86ec9
BA
256 {
257 if (this.board[last[0]][last[1]] != V.EMPTY)
258 {
259 const oppPiece = this.getPiece(last[0],last[1]);
260 if (!!byChameleon && oppPiece != V.KNIGHT)
261 continue outerLoop;
262 // Something to eat:
263 vanished.push( new PiPo({x:last[0],y:last[1],c:oppCol,p:oppPiece}) );
264 }
265 if (this.board[cur[0]][cur[1]] != V.EMPTY)
266 {
267 if (this.getColor(cur[0],cur[1]) == color
268 || this.board[last[0]][last[1]] != V.EMPTY) //TODO: redundant test
269 {
270 continue outerLoop;
271 }
272 }
273 else
274 {
275 moves.push(new Move({
276 appear: [ new PiPo({x:cur[0],y:cur[1],c:color,p:piece}) ],
277 vanish: JSON.parse(JSON.stringify(vanished)), //TODO: required?
278 start: {x:x,y:y},
279 end: {x:cur[0],y:cur[1]}
280 }));
281 }
282 last = [last[0]+step[0],last[1]+step[1]];
283 cur = [cur[0]+step[0],cur[1]+step[1]];
284 }
7688bf77
BA
285 }
286 return moves;
2eef6db6
BA
287 }
288
a3c86ec9
BA
289 // Long-leaper
290 getPotentialKnightMoves(sq)
291 {
292 return super.getPotentialQueenMoves(sq).concat(this.getKnightCaptures(sq));
293 }
294
c28265aa 295 getPotentialBishopMoves([x,y])
2eef6db6 296 {
c28265aa
BA
297 let moves = super.getPotentialQueenMoves([x,y])
298 .concat(this.getKnightCaptures([x,y],"asChameleon"));
2f3c8451 299 // No "king capture" because king cannot remain under check
a3c86ec9
BA
300 this.addPawnCaptures(moves, "asChameleon");
301 this.addRookCaptures(moves, "asChameleon");
302 this.addQueenCaptures(moves, "asChameleon");
303 // Post-processing: merge similar moves, concatenating vanish arrays
304 let mergedMoves = {};
a3c86ec9 305 moves.forEach(m => {
0b7d99ec 306 const key = m.end.x + V.size.x * m.end.y;
a3c86ec9
BA
307 if (!mergedMoves[key])
308 mergedMoves[key] = m;
309 else
310 {
311 for (let i=1; i<m.vanish.length; i++)
312 mergedMoves[key].vanish.push(m.vanish[i]);
313 }
314 });
315 // Finally return an array
316 moves = [];
317 Object.keys(mergedMoves).forEach(k => { moves.push(mergedMoves[k]); });
318 return moves;
2eef6db6
BA
319 }
320
a3c86ec9
BA
321 // Withdrawer
322 addQueenCaptures(moves, byChameleon)
2eef6db6 323 {
a3c86ec9
BA
324 if (moves.length == 0)
325 return;
326 const [x,y] = [moves[0].start.x,moves[0].start.y];
7688bf77
BA
327 const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
328 let capturingDirections = [];
a3c86ec9 329 const color = this.turn;
7688bf77
BA
330 const oppCol = this.getOppCol(color);
331 adjacentSteps.forEach(step => {
332 const [i,j] = [x+step[0],y+step[1]];
0b7d99ec 333 if (V.OnBoard(i,j) && this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol
a3c86ec9
BA
334 && (!byChameleon || this.getPiece(i,j) == V.QUEEN))
335 {
7688bf77 336 capturingDirections.push(step);
a3c86ec9 337 }
7688bf77
BA
338 });
339 moves.forEach(m => {
340 const step = [
341 m.end.x!=x ? (m.end.x-x)/Math.abs(m.end.x-x) : 0,
342 m.end.y!=y ? (m.end.y-y)/Math.abs(m.end.y-y) : 0
343 ];
a3c86ec9 344 // NOTE: includes() and even _.isEqual() functions fail...
7688bf77 345 // TODO: this test should be done only once per direction
a3c86ec9
BA
346 if (capturingDirections.some(dir =>
347 { return (dir[0]==-step[0] && dir[1]==-step[1]); }))
7688bf77
BA
348 {
349 const [i,j] = [x-step[0],y-step[1]];
350 m.vanish.push(new PiPo({
351 x:i,
352 y:j,
353 p:this.getPiece(i,j),
354 c:oppCol
355 }));
356 }
357 });
2eef6db6
BA
358 }
359
a3c86ec9
BA
360 getPotentialQueenMoves(sq)
361 {
362 let moves = super.getPotentialQueenMoves(sq);
363 this.addQueenCaptures(moves);
364 return moves;
365 }
366
45338cdd
BA
367 getPotentialImmobilizerMoves(sq)
368 {
a3c86ec9 369 // Immobilizer doesn't capture
45338cdd
BA
370 return super.getPotentialQueenMoves(sq);
371 }
372
2eef6db6
BA
373 getPotentialKingMoves(sq)
374 {
2eef6db6
BA
375 return this.getSlideNJumpMoves(sq,
376 V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
377 }
378
2f3c8451 379 // isAttacked() is OK because the immobilizer doesn't take
9d218497 380
2f3c8451 381 isAttackedByPawn([x,y], colors)
2eef6db6 382 {
f4fd6580
BA
383 // Square (x,y) must be surroundable by two enemy pieces,
384 // and one of them at least should be a pawn (moving).
e1cce115 385 const dirs = [ [1,0],[0,1] ];
0b7d99ec 386 const steps = V.steps[V.ROOK];
2f3c8451
BA
387 for (let dir of dirs)
388 {
389 const [i1,j1] = [x-dir[0],y-dir[1]]; //"before"
390 const [i2,j2] = [x+dir[0],y+dir[1]]; //"after"
0b7d99ec 391 if (V.OnBoard(i1,j1) && V.OnBoard(i2,j2))
2f3c8451 392 {
0b7d99ec
BA
393 if ((this.board[i1][j1]!=V.EMPTY && colors.includes(this.getColor(i1,j1))
394 && this.board[i2][j2]==V.EMPTY)
f4fd6580 395 ||
0b7d99ec
BA
396 (this.board[i2][j2]!=V.EMPTY && colors.includes(this.getColor(i2,j2))
397 && this.board[i1][j1]==V.EMPTY))
f4fd6580
BA
398 {
399 // Search a movable enemy pawn landing on the empty square
400 for (let step of steps)
401 {
0b7d99ec 402 let [ii,jj] = (this.board[i1][j1]==V.EMPTY ? [i1,j1] : [i2,j2]);
f4fd6580 403 let [i3,j3] = [ii+step[0],jj+step[1]];
0b7d99ec 404 while (V.OnBoard(i3,j3) && this.board[i3][j3]==V.EMPTY)
f4fd6580
BA
405 {
406 i3 += step[0];
407 j3 += step[1];
408 }
0b7d99ec
BA
409 if (V.OnBoard(i3,j3) && colors.includes(this.getColor(i3,j3))
410 && this.getPiece(i3,j3) == V.PAWN && !this.isImmobilized([i3,j3]))
f4fd6580
BA
411 {
412 return true;
413 }
414 }
415 }
2f3c8451
BA
416 }
417 }
418 return false;
2eef6db6
BA
419 }
420
2f3c8451 421 isAttackedByRook([x,y], colors)
2eef6db6 422 {
f4fd6580
BA
423 // King must be on same column or row,
424 // and a rook should be able to reach a capturing square
f4fd6580
BA
425 // colors contains only one element, giving the oppCol and thus king position
426 const sameRow = (x == this.kingPos[colors[0]][0]);
427 const sameColumn = (y == this.kingPos[colors[0]][1]);
428 if (sameRow || sameColumn)
2f3c8451 429 {
f4fd6580 430 // Look for the enemy rook (maximum 1)
0b7d99ec 431 for (let i=0; i<V.size.x; i++)
2f3c8451 432 {
0b7d99ec 433 for (let j=0; j<V.size.y; j++)
2f3c8451 434 {
0b7d99ec
BA
435 if (this.board[i][j] != V.EMPTY && colors.includes(this.getColor(i,j))
436 && this.getPiece(i,j) == V.ROOK)
f4fd6580
BA
437 {
438 if (this.isImmobilized([i,j]))
439 return false; //because only one rook
440 // Can it reach a capturing square?
441 // Easy but quite suboptimal way (TODO): generate all moves (turn is OK)
442 const moves = this.getPotentialMovesFrom([i,j]);
443 for (let move of moves)
444 {
445 if (sameRow && move.end.y == y || sameColumn && move.end.x == x)
446 return true;
447 }
448 }
2f3c8451
BA
449 }
450 }
451 }
452 return false;
2eef6db6
BA
453 }
454
2f3c8451 455 isAttackedByKnight([x,y], colors)
2eef6db6 456 {
2f3c8451
BA
457 // Square (x,y) must be on same line as a knight,
458 // and there must be empty square(s) behind.
2f3c8451 459 const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
2f3c8451
BA
460 outerLoop:
461 for (let step of steps)
7aea7942 462 {
2f3c8451 463 const [i0,j0] = [x+step[0],y+step[1]];
0b7d99ec 464 if (V.OnBoard(i0,j0) && this.board[i0][j0] == V.EMPTY)
2f3c8451
BA
465 {
466 // Try in opposite direction:
467 let [i,j] = [x-step[0],y-step[1]];
0b7d99ec 468 while (V.OnBoard(i,j))
2f3c8451 469 {
0b7d99ec 470 while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
523da5d5
BA
471 {
472 i -= step[0];
473 j -= step[1];
474 }
0b7d99ec 475 if (V.OnBoard(i,j))
523da5d5
BA
476 {
477 if (colors.includes(this.getColor(i,j)))
478 {
479 if (this.getPiece(i,j) == V.KNIGHT && !this.isImmobilized([i,j]))
480 return true;
481 continue outerLoop;
482 }
e1cce115
BA
483 // [else] Our color, could be captured *if there was an empty space*
484 if (this.board[i+step[0]][j+step[1]] != V.EMPTY)
485 continue outerLoop;
523da5d5
BA
486 i -= step[0];
487 j -= step[1];
488 }
2f3c8451
BA
489 }
490 }
c28265aa 491 }
2f3c8451
BA
492 return false;
493 }
494
495 isAttackedByBishop([x,y], colors)
496 {
497 // We cheat a little here: since this function is used exclusively for king,
498 // it's enough to check the immediate surrounding of the square.
2f3c8451 499 const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
2f3c8451 500 for (let step of adjacentSteps)
c28265aa 501 {
2f3c8451 502 const [i,j] = [x+step[0],y+step[1]];
0b7d99ec 503 if (V.OnBoard(i,j) && this.board[i][j]!=V.EMPTY
2f3c8451 504 && colors.includes(this.getColor(i,j)) && this.getPiece(i,j) == V.BISHOP)
7aea7942 505 {
2f3c8451 506 return true; //bishops are never immobilized
7aea7942
BA
507 }
508 }
2f3c8451 509 return false;
2eef6db6
BA
510 }
511
2f3c8451 512 isAttackedByQueen([x,y], colors)
2eef6db6 513 {
2f3c8451
BA
514 // Square (x,y) must be adjacent to a queen, and the queen must have
515 // some free space in the opposite direction from (x,y)
2f3c8451 516 const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
2f3c8451 517 for (let step of adjacentSteps)
7aea7942 518 {
2f3c8451 519 const sq2 = [x+2*step[0],y+2*step[1]];
0b7d99ec 520 if (V.OnBoard(sq2[0],sq2[1]) && this.board[sq2[0]][sq2[1]] == V.EMPTY)
7aea7942 521 {
2f3c8451
BA
522 const sq1 = [x+step[0],y+step[1]];
523 if (this.board[sq1[0]][sq1[1]] != V.EMPTY
524 && colors.includes(this.getColor(sq1[0],sq1[1]))
525 && this.getPiece(sq1[0],sq1[1]) == V.QUEEN
526 && !this.isImmobilized(sq1))
7aea7942 527 {
2f3c8451 528 return true;
7aea7942
BA
529 }
530 }
531 }
2f3c8451 532 return false;
2eef6db6
BA
533 }
534
2f3c8451 535 updateVariables(move)
a3c86ec9 536 {
2f3c8451
BA
537 // Just update king(s) position(s)
538 const piece = this.getPiece(move.start.x,move.start.y);
539 const c = this.getColor(move.start.x,move.start.y);
0b7d99ec 540 if (piece == V.KING && move.appear.length > 0)
2f3c8451
BA
541 {
542 this.kingPos[c][0] = move.appear[0].x;
543 this.kingPos[c][1] = move.appear[0].y;
544 }
a3c86ec9
BA
545 }
546
2eef6db6
BA
547 static get VALUES() { //TODO: totally experimental!
548 return {
549 'p': 1,
550 'r': 2,
551 'n': 5,
552 'b': 3,
553 'q': 3,
554 'm': 5,
555 'k': 1000
556 };
557 }
558
559 static get SEARCH_DEPTH() { return 2; } //TODO?
560
561 static GenRandInitFen()
562 {
563 let pieces = { "w": new Array(8), "b": new Array(8) };
564 // Shuffle pieces on first and last rank
565 for (let c of ["w","b"])
566 {
567 let positions = _.range(8);
568 // Get random squares for every piece, totally freely
569
570 let randIndex = _.random(7);
571 const bishop1Pos = positions[randIndex];
572 positions.splice(randIndex, 1);
573
574 randIndex = _.random(6);
575 const bishop2Pos = positions[randIndex];
576 positions.splice(randIndex, 1);
577
578 randIndex = _.random(5);
579 const knight1Pos = positions[randIndex];
580 positions.splice(randIndex, 1);
581
582 randIndex = _.random(4);
583 const knight2Pos = positions[randIndex];
584 positions.splice(randIndex, 1);
585
586 randIndex = _.random(3);
587 const queenPos = positions[randIndex];
588 positions.splice(randIndex, 1);
589
590 randIndex = _.random(2);
591 const kingPos = positions[randIndex];
592 positions.splice(randIndex, 1);
593
594 randIndex = _.random(1);
595 const rookPos = positions[randIndex];
596 positions.splice(randIndex, 1);
45338cdd 597 const immobilizerPos = positions[0];
2eef6db6
BA
598
599 pieces[c][bishop1Pos] = 'b';
600 pieces[c][bishop2Pos] = 'b';
601 pieces[c][knight1Pos] = 'n';
602 pieces[c][knight2Pos] = 'n';
603 pieces[c][queenPos] = 'q';
604 pieces[c][kingPos] = 'k';
605 pieces[c][rookPos] = 'r';
606 pieces[c][immobilizerPos] = 'm';
607 }
608 return pieces["b"].join("") +
609 "/pppppppp/8/8/8/8/PPPPPPPP/" +
610 pieces["w"].join("").toUpperCase() +
611 " 0000"; //TODO: flags?!
612 }
613
614 getFlagsFen()
615 {
616 return "0000"; //TODO: or "-" ?
617 }
15c1295a
BA
618
619 getNotation(move)
620 {
621 const initialSquare =
0b7d99ec
BA
622 String.fromCharCode(97 + move.start.y) + (V.size.x-move.start.x);
623 const finalSquare = String.fromCharCode(97 + move.end.y) + (V.size.x-move.end.x);
15c1295a 624 let notation = undefined;
0b7d99ec 625 if (move.appear[0].p == V.PAWN)
15c1295a
BA
626 {
627 // Pawn: generally ambiguous short notation, so we use full description
628 notation = "P" + initialSquare + finalSquare;
629 }
0b7d99ec 630 else if (move.appear[0].p == V.KING)
15c1295a
BA
631 notation = "K" + (move.vanish.length>1 ? "x" : "") + finalSquare;
632 else
633 notation = move.appear[0].p.toUpperCase() + finalSquare;
0b7d99ec 634 if (move.vanish.length > 1 && move.appear[0].p != V.KING)
15c1295a
BA
635 notation += "X"; //capture mark (not describing what is captured...)
636 return notation;
637 }
32cfcea4 638}