+ const L0 = this.captures.length;
+ const captures = this.captures[L0 - 1];
+ const L = captures.length;
+ if (L > 0) {
+ var c = captures[L-1];
+ if (x != c.square.x + c.step[0] || y != c.square.y + c.step[1])
+ return [];
+ }
+ const oppCol = V.GetOppCol(this.turn);
+ let steps = V.steps[V.ROOK];
+ if ((x + y) % 2 == 0) steps = steps.concat(V.steps[V.BISHOP]);
+ let moves = [];
+ for (let s of steps) {
+ if (L > 0 && c.step[0] == s[0] && c.step[1] == s[1]) {
+ // Add a move to say "I'm done capturing"
+ moves.push(
+ new Move({
+ appear: [],
+ vanish: [],
+ start: { x: x, y: y },
+ end: { x: x - s[0], y: y - s[1] }
+ })
+ );
+ continue;
+ }
+ let [i, j] = [x + s[0], y + s[1]];
+ if (captures.some(c => c.square.x == i && c.square.y == j)) continue;
+ if (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ // The move is potentially allowed. Might lead to 2 different captures
+ let mv = super.getBasicMove([x, y], [i, j]);
+ const capt = this.addCapture([i, j], s, mv);
+ if (capt) {
+ moves.push(mv);
+ mv = super.getBasicMove([x, y], [i, j]);
+ }
+ const capt_bw = this.addCapture([x, y], [-s[0], -s[1]], mv);
+ if (capt_bw) moves.push(mv);
+ // Captures take priority (if available)
+ if (!capt && !capt_bw && L == 0) moves.push(mv);
+ }
+ }
+ return moves;
+ }
+
+ atLeastOneCapture() {
+ const color = this.turn;
+ const oppCol = V.GetOppCol(color);
+ const L0 = this.captures.length;
+ const captures = this.captures[L0 - 1];
+ const L = captures.length;
+ if (L > 0) {
+ // If some adjacent enemy stone, with free space to capture it,
+ // toward a square not already visited, through a different step
+ // from last one: then yes.
+ const c = captures[L-1];
+ const [x, y] = [c.square.x + c.step[0], c.square.y + c.step[1]];
+ let steps = V.steps[V.ROOK];
+ if ((x + y) % 2 == 0) steps = steps.concat(V.steps[V.BISHOP]);
+ // TODO: half of the steps explored are redundant
+ for (let s of steps) {
+ if (s[0] == c.step[0] && s[1] == c.step[1]) continue;
+ const [i, j] = [x + s[0], y + s[1]];
+ if (
+ !V.OnBoard(i, j) ||
+ this.board[i][j] != V.EMPTY ||
+ captures.some(c => c.square.x == i && c.square.y == j)
+ ) {
+ continue;
+ }
+ if (
+ V.OnBoard(i + s[0], j + s[1]) &&
+ this.board[i + s[0]][j + s[1]] != V.EMPTY &&
+ this.getColor(i + s[0], j + s[1]) == oppCol
+ ) {
+ return true;
+ }
+ if (
+ V.OnBoard(x - s[0], y - s[1]) &&
+ this.board[x - s[0]][y - s[1]] != V.EMPTY &&
+ this.getColor(x - s[0], y - s[1]) == oppCol
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
+ if (
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == color &&
+ // TODO: this could be more efficient
+ this.getPotentialMovesFrom([i, j]).some(m => m.vanish.length >= 2)
+ ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ static KeepCaptures(moves) {
+ return moves.filter(m => m.vanish.length >= 2);
+ }
+
+ getPossibleMovesFrom(sq) {
+ let moves = this.getPotentialMovesFrom(sq);
+ const L0 = this.captures.length;
+ const captures = this.captures[L0 - 1];
+ if (captures.length > 0) return this.getPotentialMovesFrom(sq);
+ const captureMoves = V.KeepCaptures(moves);
+ if (captureMoves.length > 0) return captureMoves;
+ if (this.atLeastOneCapture()) return [];
+ return moves;
+ }
+
+ getAllValidMoves() {
+ const moves = super.getAllValidMoves();
+ if (moves.some(m => m.vanish.length >= 2)) return V.KeepCaptures(moves);
+ return moves;