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