);
}
- isAttacked(sq, color, castling) {
- const singleMoveAttack = super.isAttacked(sq, color);
- if (singleMoveAttack) return true;
- if (color == 'b' || !!castling) return singleMoveAttack;
- // Attacks by white: double-move allowed
- const curTurn = this.turn;
- this.turn = 'w';
- const w1Moves = super.getAllPotentialMoves();
- this.turn = curTurn;
- for (let move of w1Moves) {
- this.play(move);
- const res = super.isAttacked(sq, 'w');
- this.undo(move);
- if (res) return res;
- }
+ isAttacked() {
+ // Goal is king capture => no checks
return false;
}
+ filterValid(moves) {
+ return moves;
+ }
+
+ getCheckSquares() {
+ return [];
+ }
+
+ getCurrentScore() {
+ const color = this.turn;
+ if (this.kingPos[color][0] < 0) return (color == 'w' ? "0-1" : "1-0");
+ return "*";
+ }
+
play(move) {
move.flags = JSON.stringify(this.aggregateFlags());
if (this.turn == 'b' || this.subTurn == 2)
V.PlayOnBoard(this.board, move);
if (this.turn == 'w') {
if (this.subTurn == 1) this.movesCount++;
- else this.turn = 'b';
- this.subTurn = 3 - this.subTurn;
- } else {
+ if (
+ this.subTurn == 2 ||
+ // King captured
+ (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+ ) {
+ this.turn = 'b';
+ this.subTurn = 1;
+ }
+ else this.subTurn = 2;
+ }
+ else {
this.turn = 'w';
this.movesCount++;
}
// Definition of 'c' in base class doesn't work:
const c = move.vanish[0].c;
const piece = move.vanish[0].p;
- if (piece == V.KING) {
- this.kingPos[c][0] = move.appear[0].x;
- this.kingPos[c][1] = move.appear[0].y;
+ if (piece == V.KING)
+ this.kingPos[c] = [move.appear[0].x, move.appear[0].y];
+ if (move.vanish.length == 2 && move.vanish[1].p == V.KING) {
+ // Opponent's king is captured, game over
+ this.kingPos[move.vanish[1].c] = [-1, -1];
+ move.captureKing = true; //for undo
}
this.updateCastleFlags(move, piece);
}
if (this.subTurn == 2) this.subTurn = 1;
else this.turn = 'b';
this.movesCount--;
- } else {
+ }
+ else {
this.turn = 'w';
- this.subTurn = 2;
+ this.subTurn = (!move.captureKing ? 2 : 1);
}
this.postUndo(move);
}
- filterValid(moves) {
- if (this.turn == 'w' && this.subTurn == 1) {
- return moves.filter(m1 => {
- this.play(m1);
- // NOTE: no recursion because next call will see subTurn == 2
- const res = super.atLeastOneMove();
- this.undo(m1);
- return res;
- });
- }
- return super.filterValid(moves);
- }
-
- static get SEARCH_DEPTH() {
- return 1;
+ postUndo(move) {
+ if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+ // Opponent's king was captured
+ this.kingPos[move.vanish[1].c] = [move.vanish[1].x, move.vanish[1].y];
+ super.postUndo(move);
}
+ // Custom search at depth 1(+1)
getComputerMove() {
- const color = this.turn;
- if (color == 'w') {
+ const getBestWhiteMove = (terminal) => {
// Generate all sequences of 2-moves
- const moves1 = this.getAllValidMoves();
+ let moves1 = this.getAllValidMoves();
moves1.forEach(m1 => {
m1.eval = -V.INFINITY;
m1.move2 = null;
this.play(m1);
- const moves2 = this.getAllValidMoves();
- moves2.forEach(m2 => {
- this.play(m2);
- const eval2 = this.evalPosition();
- this.undo(m2);
- if (eval2 > m1.eval) {
- m1.eval = eval2;
- m1.move2 = m2;
- }
- });
+ if (!!terminal) m1.eval = this.evalPosition();
+ else {
+ const moves2 = this.getAllValidMoves();
+ moves2.forEach(m2 => {
+ this.play(m2);
+ const eval2 = this.evalPosition() + 0.05 - Math.random() / 10;
+ this.undo(m2);
+ if (eval2 > m1.eval) {
+ m1.eval = eval2;
+ m1.move2 = m2;
+ }
+ });
+ }
this.undo(m1);
});
moves1.sort((a, b) => b.eval - a.eval);
+ if (!!terminal)
+ // The move itself doesn't matter, only its eval:
+ return moves1[0];
let candidates = [0];
for (
let i = 1;
const move2 = moves1[idx].move2;
delete moves1[idx]["move2"];
return [moves1[idx], move2];
- }
- // For black at depth 1, super method is fine:
- return super.getComputerMove();
+ };
+
+ const getBestBlackMove = () => {
+ let moves = this.getAllValidMoves();
+ moves.forEach(m => {
+ m.eval = V.INFINITY;
+ this.play(m);
+ const evalM = getBestWhiteMove("terminal").eval
+ this.undo(m);
+ if (evalM < m.eval) m.eval = evalM;
+ });
+ moves.sort((a, b) => a.eval - b.eval);
+ let candidates = [0];
+ for (
+ let i = 1;
+ i < moves.length && moves[i].eval == moves[0].eval;
+ i++
+ ) {
+ candidates.push(i);
+ }
+ const idx = candidates[randInt(candidates.length)];
+ return moves[idx];
+ };
+
+ const color = this.turn;
+ return (color == 'w' ? getBestWhiteMove() : getBestBlackMove());
}
};