+ const color = this.getColor(x, y);
+ const piece = this.getPiece(x, y);
+ const lastRank = (color == 'w' ? 0 : 7);
+ let counter = 1;
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ if (i == lastRank && piece == V.PAWN) {
+ // Promotion by push or pull
+ V.PawnSpecs.promotions.forEach(p => {
+ let move = super.getBasicMove([x, y], [i, j], { c: color, p: p });
+ moves.push(move);
+ });
+ }
+ else moves.push(super.getBasicMove([x, y], [i, j]));
+ if (++counter > nbSteps) break;
+ i += dx;
+ j += dy;
+ }
+ if (!V.OnBoard(i, j) && piece != V.KING) {
+ // Add special "exit" move, by "taking king"
+ moves.push(
+ new Move({
+ start: { x: x, y: y },
+ end: { x: this.kingPos[color][0], y: this.kingPos[color][1] },
+ appear: [],
+ vanish: [{ x: x, y: y, c: color, p: piece }]
+ })
+ );
+ }
+ return moves;
+ }
+
+ // Normalize direction to know the step
+ getNormalizedDirection([dx, dy]) {
+ const absDir = [Math.abs(dx), Math.abs(dy)];
+ let divisor = 0;
+ if (absDir[0] != 0 && absDir[1] != 0 && absDir[0] != absDir[1])
+ // Knight
+ divisor = Math.min(absDir[0], absDir[1]);
+ else
+ // Standard slider (or maybe a pawn or king: same)
+ divisor = Math.max(absDir[0], absDir[1]);
+ return [dx / divisor, dy / divisor];
+ }
+
+ // There was something on x2,y2, maybe our color, pushed or (self)pulled
+ isAprioriValidExit([x1, y1], [x2, y2], color2, piece2) {
+ const color1 = this.getColor(x1, y1);
+ const pawnShift = (color1 == 'w' ? -1 : 1);
+ const lastRank = (color1 == 'w' ? 0 : 7);
+ const deltaX = Math.abs(x1 - x2);
+ const deltaY = Math.abs(y1 - y2);
+ const checkSlider = () => {
+ const dir = this.getNormalizedDirection([x2 - x1, y2 - y1]);
+ let [i, j] = [x1 + dir[0], y1 + dir[1]];
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ i += dir[0];
+ j += dir[1];
+ }
+ return !V.OnBoard(i, j);
+ };
+ switch (piece2 || this.getPiece(x1, y1)) {
+ case V.PAWN:
+ return (
+ x1 + pawnShift == x2 &&
+ (
+ (color1 == color2 && x2 == lastRank && y1 == y2) ||
+ (
+ color1 != color2 &&
+ deltaY == 1 &&
+ !V.OnBoard(2 * x2 - x1, 2 * y2 - y1)
+ )
+ )
+ );
+ case V.ROOK:
+ if (x1 != x2 && y1 != y2) return false;
+ return checkSlider();
+ case V.KNIGHT:
+ return (
+ deltaX + deltaY == 3 &&
+ (deltaX == 1 || deltaY == 1) &&
+ !V.OnBoard(2 * x2 - x1, 2 * y2 - y1)
+ );
+ case V.BISHOP:
+ if (deltaX != deltaY) return false;
+ return checkSlider();
+ case V.QUEEN:
+ if (deltaX != 0 && deltaY != 0 && deltaX != deltaY) return false;
+ return checkSlider();
+ case V.KING:
+ return (
+ deltaX <= 1 &&
+ deltaY <= 1 &&
+ !V.OnBoard(2 * x2 - x1, 2 * y2 - y1)
+ );
+ }
+ return false;
+ }
+
+ isAprioriValidVertical([x1, y1], x2) {
+ const piece = this.getPiece(x1, y1);
+ const deltaX = Math.abs(x1 - x2);
+ const startRank = (this.getColor(x1, y1) == 'w' ? 6 : 1);
+ return (
+ [V.QUEEN, V.ROOK].includes(piece) ||