+ // Any capture?
+ const captures = moves.filter(m => m.vanish.length >= 1);
+ if (captures.length > 0) return captures[randInt(captures.length)];
+ // Any group in immediate danger?
+ const color = this.turn;
+ let explored = ArrayFun.init(V.size.x, V.size.y, 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 &&
+ !explored[i][j]
+ ) {
+ // Before this search, reset liberties,
+ // because two groups might share them.
+ for (let ii = 0; ii < V.size.x; ii++) {
+ for (let jj = 0; jj < V.size.y; jj++) {
+ if (explored[ii][jj] && this.board[ii][jj] == V.EMPTY)
+ explored[ii][jj] = false;
+ }
+ }
+ const liberties = this.countEmptySpaces([i, j], color, explored);
+ if (liberties.length == 1) {
+ const L = liberties[0];
+ const toPlay = moves.find(m => m.end.x == L[0] && m.end.y == L[1]);
+ if (!!toPlay) return toPlay;
+ }
+ }
+ }
+ }
+ // At this point, pick a random move not far from current stones (TODO)
+ const candidates = moves.filter(m => {
+ const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
+ return (
+ steps.some(s => {
+ const [i, j] = [m.end.x + s[0], m.end.y + s[1]];
+ return (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) == color
+ );
+ })
+ );
+ });
+ if (candidates.length > 0) return candidates[randInt(candidates.length)];