+ // 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;
+ if (this.subTurn == 1) {
+ const getMoveHash = (m) => {
+ return V.CoordsToSquare(m.start) + V.CoordsToSquare(m.end);
+ };
+ 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):
+ const moves =
+ this.getColor(x, y) == color
+ ? super.getPotentialMovesFrom([x, y])
+ : [];
+ const pawnShift = (color == 'w' ? -1 : 1);
+ const pawnStartRank = (color == 'w' ? 6 : 1);
+ // 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'
+ for (let step of V.steps[V.KNIGHT]) {
+ const [i, j] = [x + step[0], y + step[1]];
+ if (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == color &&
+ this.getPiece(i, j) == V.KNIGHT
+ ) {
+ addMoves(step, 1);
+ }
+ }
+ for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
+ let [i, j] = [x + step[0], y + step[1]];
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ i += step[0];
+ j += step[1];
+ }
+ if (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == color
+ ) {
+ const deltaX = Math.abs(i - x);
+ const deltaY = Math.abs(j - y);
+ // Can a priori go both ways, except with pawns
+ switch (this.getPiece(i, j)) {
+ case V.PAWN:
+ if (
+ (x - i) / deltaX == pawnShift &&
+ deltaX <= 2 &&
+ deltaY <= 1
+ ) {
+ const pColor = this.getColor(x, y);
+ if (pColor == color && deltaY == 0) {
+ // Pushed forward
+ const maxSteps = (i == pawnStartRank && deltaX == 1 ? 2 : 1);
+ addMoves(step, maxSteps);
+ }
+ else if (pColor != color && deltaY == 1 && deltaX == 1)
+ // Pushed diagonally
+ addMoves(step, 1);
+ }
+ break;
+ case V.ROOK:
+ if (deltaX == 0 || deltaY == 0) addMoves(step);
+ break;
+ case V.BISHOP:
+ if (deltaX == deltaY) addMoves(step);
+ break;
+ case V.QUEEN:
+ if (deltaX == 0 || deltaY == 0 || deltaX == deltaY)
+ addMoves(step);
+ break;
+ case V.KING:
+ if (deltaX <= 1 && deltaY <= 1) addMoves(step, 1);
+ break;
+ }
+ }
+ }
+ return moves;
+ }
+ // If subTurn == 2 then we should have a first move,
+ // which restrict what we can play now: only in the first move direction
+ // NOTE: no need for knight or pawn checks, because the move will be
+ // naturally limited in those cases.
+ const L = this.firstMove.length;
+ const fm = this.firstMove[L-1];
+ if (fm.appear.length == 2 && fm.vanish.length == 2)
+ // Castle: no real move playable then.
+ return [];
+ if (fm.appear.length == 0) {
+ // Piece at subTurn 1 just exited the board.
+ // Can I be a piece which caused the exit?
+ if (
+ this.isAprioriValidExit(
+ [x, y],
+ [fm.start.x, fm.start.y],
+ fm.vanish[0].c
+ )
+ ) {
+ // Seems so:
+ const dir = this.getNormalizedDirection(
+ [fm.start.x - x, fm.start.y - y]);
+ return this.getMovesInDirection([x, y], dir);
+ }
+ }