+ 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) ||
+ (
+ [V.KING, V.PAWN].includes(piece) &&
+ (
+ deltaX == 1 ||
+ (deltaX == 2 && piece == V.PAWN && x1 == startRank)
+ )
+ )
+ );
+ }
+
+ // NOTE: for pushes, play the pushed piece first.
+ // for pulls: play the piece doing the action first
+ // NOTE: to push a piece out of the board, make it slide until its king
+ getPotentialMovesFrom([x, y]) {
+ const color = this.turn;
+ const sqCol = this.getColor(x, y);
+ const pawnShift = (color == 'w' ? -1 : 1);
+ const pawnStartRank = (color == 'w' ? 6 : 1);
+ const getMoveHash = (m) => {
+ return V.CoordsToSquare(m.start) + V.CoordsToSquare(m.end);
+ };
+ if (this.subTurn == 1) {
+ const addMoves = (dir, nbSteps) => {
+ const newMoves =
+ this.getMovesInDirection([x, y], [-dir[0], -dir[1]], nbSteps)
+ .filter(m => !movesHash[getMoveHash(m)]);
+ newMoves.forEach(m => { movesHash[getMoveHash(m)] = true; });
+ Array.prototype.push.apply(moves, newMoves);
+ };
+ // Free to play any move (if piece of my color):
+ let moves =
+ sqCol == color
+ ? super.getPotentialMovesFrom([x, y])
+ : [];
+ // There may be several suicide moves: keep only one
+ let hasExit = false;
+ moves = moves.filter(m => {
+ const suicide = (m.appear.length == 0);
+ if (suicide) {
+ if (hasExit) return false;
+ hasExit = true;
+ }
+ return true;
+ });
+ // Structure to avoid adding moves twice (can be action & move)
+ let movesHash = {};
+ moves.forEach(m => { movesHash[getMoveHash(m)] = true; });
+ // [x, y] is pushed by 'color'