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