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