import { ChessRules } from "@/base_rules";
-import { ArrayFun} from "@/utils/array";
+import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class DarkRules extends ChessRules
-{
- // Standard rules, in the shadow
- setOtherVariables(fen)
- {
+export class DarkRules extends ChessRules {
+
+ // Analyse in Dark mode makes no sense
+ static get CanAnalyze() {
+ return false;
+ }
+
+ // Moves are revealed only when game ends
+ static get ShowMoves() {
+ return "none";
+ }
+
+ static get SomeHiddenMoves() {
+ return true;
+ }
+
+ setOtherVariables(fen) {
super.setOtherVariables(fen);
- const [sizeX,sizeY] = [V.size.x,V.size.y];
+ const [sizeX, sizeY] = [V.size.x, V.size.y];
this.enlightened = {
- "w": ArrayFun.init(sizeX,sizeY),
- "b": ArrayFun.init(sizeX,sizeY)
+ w: ArrayFun.init(sizeX, sizeY),
+ b: ArrayFun.init(sizeX, sizeY)
};
// Setup enlightened: squares reachable by each side
// (TODO: one side would be enough ?)
this.updateEnlightened();
}
- updateEnlightened()
- {
- for (let i=0; i<V.size.x; i++)
- {
- for (let j=0; j<V.size.y; j++)
- {
+ updateEnlightened() {
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
this.enlightened["w"][i][j] = false;
this.enlightened["b"][i][j] = false;
}
}
- const pawnShift = {"w":-1, "b":1};
+ const pawnShift = { w: -1, b: 1 };
// Initialize with pieces positions (which are seen)
- 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)
- {
- const color = this.getColor(i,j);
- this.enlightened[color][i][j] = true;
+ 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) {
+ const c = this.getColor(i, j);
+ this.enlightened[c][i][j] = true;
// Add potential squares visible by "impossible pawn capture"
- if (this.getPiece(i,j) == V.PAWN)
- {
- for (let shiftY of [-1,1])
- {
- if (V.OnBoard(i+pawnShift[color],j+shiftY)
- && this.board[i+pawnShift[color]][j+shiftY] == V.EMPTY)
- {
- this.enlightened[color][i+pawnShift[color]][j+shiftY] = true;
+ if (this.getPiece(i, j) == V.PAWN) {
+ for (let shiftY of [-1, 1]) {
+ if (
+ V.OnBoard(i + pawnShift[c], j + shiftY) &&
+ this.board[i + pawnShift[c]][j + shiftY] == V.EMPTY
+ ) {
+ this.enlightened[c][i + pawnShift[c]][j + shiftY] = true;
}
}
}
this.enlightened["w"][move.end.x][move.end.y] = true;
for (let move of movesBlack)
this.enlightened["b"][move.end.x][move.end.y] = true;
+ // Include en-passant capturing square if any:
+ let moves = currentTurn == "w" ? movesWhite : movesBlack;
+ for (let m of moves) {
+ if (
+ m.appear[0].p == V.PAWN &&
+ m.vanish.length == 2 &&
+ m.vanish[1].x != m.end.x
+ ) {
+ const psq = m.vanish[1];
+ this.enlightened[currentTurn][psq.x][psq.y] = true;
+ break;
+ }
+ }
+ }
+
+ // To always allow castling:
+ isAttacked() {
+ return false;
+ }
+
+ filterValid(moves) {
+ // Used in the interface
+ return moves;
}
// Has to be redefined to avoid an infinite loop
- getAllValidMoves()
- {
+ getAllValidMoves() {
const color = this.turn;
- const oppCol = V.GetOppCol(color);
let potentialMoves = [];
- 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)
- Array.prototype.push.apply(potentialMoves, this.getPotentialMovesFrom([i,j]));
+ 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)
+ Array.prototype.push.apply(
+ potentialMoves,
+ this.getPotentialMovesFrom([i, j])
+ );
}
}
return potentialMoves; //because there are no checks
}
- atLeastOneMove()
- {
- if (this.kingPos[this.turn][0] < 0)
- return false;
- return true; //TODO: is it right?
- }
-
- underCheck(color)
- {
- return false; //there is no check
- }
-
- getCheckSquares(color)
- {
+ getCheckSquares() {
return [];
}
- updateVariables(move)
- {
- super.updateVariables(move);
- if (move.vanish.length >= 2 && move.vanish[1].p == V.KING)
- {
- // We took opponent king ! (because if castle vanish[1] is a rook)
- this.kingPos[this.turn] = [-1,-1];
- }
+ postPlay(move) {
+ super.postPlay(move);
+ if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+ // We took opponent king (because if castle vanish[1] is a rook)
+ this.kingPos[this.turn] = [-1, -1];
// Update lights for both colors:
this.updateEnlightened();
}
- unupdateVariables(move)
- {
- super.unupdateVariables(move);
- const c = move.vanish[0].c;
- const oppCol = V.GetOppCol(c);
- if (this.kingPos[oppCol][0] < 0)
- {
- // Last move took opponent's king
- for (let psq of move.vanish)
- {
- if (psq.p == 'k')
- {
- this.kingPos[oppCol] = [psq.x, psq.y];
- break;
- }
- }
- }
+ postUndo(move) {
+ super.postUndo(move);
+ if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+ // Last move took opponent's king:
+ this.kingPos[move.vanish[1].c] = [move.vanish[1].x, move.vanish[1].y];
// Update lights for both colors:
this.updateEnlightened();
}
- getCurrentScore()
- {
+ getCurrentScore() {
const color = this.turn;
const kp = this.kingPos[color];
- if (kp[0] < 0) //king disappeared
- return (color == "w" ? "0-1" : "1-0");
- if (this.atLeastOneMove()) // game not over
- return "*";
- return "1/2"; //no moves but kings still there (seems impossible)
+ if (kp[0] < 0)
+ // King disappeared
+ return color == "w" ? "0-1" : "1-0";
+ // Assume that stalemate is impossible (I think so. Would need proof...)
+ return "*";
}
- static get THRESHOLD_MATE()
- {
+ static get THRESHOLD_MATE() {
return 500; //checkmates evals may be slightly below 1000
}
// In this special situation, we just look 1 half move ahead
- getComputerMove()
- {
+ getComputerMove() {
const maxeval = V.INFINITY;
const color = this.turn;
const oppCol = V.GetOppCol(color);
- const pawnShift = (color == "w" ? -1 : 1);
+ const pawnShift = color == "w" ? -1 : 1;
// Do not cheat: the current enlightment is all we can see
const myLight = JSON.parse(JSON.stringify(this.enlightened[color]));
// Can a slider on (i,j) apparently take my king?
// NOTE: inaccurate because assume yes if some squares are shadowed
- const sliderTake = ([i,j], piece) => {
+ const sliderTake = ([i, j], piece) => {
const kp = this.kingPos[color];
let step = undefined;
- if (piece == V.BISHOP)
- {
- if (Math.abs(kp[0] - i) == Math.abs(kp[1] - j))
- {
- step =
- [
- (i-kp[0]) / Math.abs(i-kp[0]),
- (j-kp[1]) / Math.abs(j-kp[1])
+ if (piece == V.BISHOP) {
+ if (Math.abs(kp[0] - i) == Math.abs(kp[1] - j)) {
+ step = [
+ (i - kp[0]) / Math.abs(i - kp[0]),
+ (j - kp[1]) / Math.abs(j - kp[1])
];
}
+ } else if (piece == V.ROOK) {
+ if (kp[0] == i) step = [0, (j - kp[1]) / Math.abs(j - kp[1])];
+ else if (kp[1] == j) step = [(i - kp[0]) / Math.abs(i - kp[0]), 0];
}
- else if (piece == V.ROOK)
- {
- if (kp[0] == i)
- step = [0, (j-kp[1]) / Math.abs(j-kp[1])];
- else if (kp[1] == j)
- step = [(i-kp[0]) / Math.abs(i-kp[0]), 0];
- }
- if (!step)
- return false;
+ if (!step) return false;
// Check for obstacles
let obstacle = false;
for (
- let x=kp[0]+step[0], y=kp[1]+step[1];
+ let x = kp[0] + step[0], y = kp[1] + step[1];
x != i && y != j;
- x += step[0], y += step[1])
- {
- if (myLight[x][y] && this.board[x][y] != V.EMPTY)
- {
+ x += step[0], y += step[1]
+ ) {
+ if (myLight[x][y] && this.board[x][y] != V.EMPTY) {
obstacle = true;
break;
}
}
- if (!obstacle)
- return true;
+ if (!obstacle) return true;
return false;
};
// Do I see something which can take my king ?
const kingThreats = () => {
const kp = this.kingPos[color];
- for (let i=0; i<V.size.x; i++)
- {
- for (let j=0; j<V.size.y; j++)
- {
- if (myLight[i][j] && this.board[i][j] != V.EMPTY
- && this.getColor(i,j) != color)
- {
- switch (this.getPiece(i,j))
- {
+ for (let i = 0; i < V.size.x; i++) {
+ for (let j = 0; j < V.size.y; j++) {
+ if (
+ myLight[i][j] &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i, j) != color
+ ) {
+ switch (this.getPiece(i, j)) {
case V.PAWN:
- if (kp[0] + pawnShift == i && Math.abs(kp[1]-j) == 1)
+ if (kp[0] + pawnShift == i && Math.abs(kp[1] - j) == 1)
return true;
break;
case V.KNIGHT:
- if ((Math.abs(kp[0] - i) == 2 && Math.abs(kp[1] - j) == 1) ||
- (Math.abs(kp[0] - i) == 1 && Math.abs(kp[1] - j) == 2))
- {
+ if (
+ (Math.abs(kp[0] - i) == 2 && Math.abs(kp[1] - j) == 1) ||
+ (Math.abs(kp[0] - i) == 1 && Math.abs(kp[1] - j) == 2)
+ ) {
return true;
}
break;
return true;
break;
case V.BISHOP:
- if (sliderTake([i,j], V.BISHOP))
- return true;
+ if (sliderTake([i, j], V.BISHOP)) return true;
break;
case V.ROOK:
- if (sliderTake([i,j], V.ROOK))
- return true;
+ if (sliderTake([i, j], V.ROOK)) return true;
break;
case V.QUEEN:
- if (sliderTake([i,j], V.BISHOP) || sliderTake([i,j], V.ROOK))
+ if (sliderTake([i, j], V.BISHOP) || sliderTake([i, j], V.ROOK))
return true;
break;
}
};
let moves = this.getAllValidMoves();
- for (let move of moves)
- {
+ for (let move of moves) {
this.play(move);
- if (this.kingPos[oppCol][0] >= 0 && kingThreats())
- {
+ if (this.kingPos[oppCol][0] >= 0 && kingThreats()) {
// We didn't take opponent king, and our king will be captured: bad
move.eval = -maxeval;
}
this.undo(move);
- if (!!move.eval)
- continue;
+ if (move.eval) continue;
move.eval = 0; //a priori...
// Can I take something ? If yes, do it if it seems good...
- if (move.vanish.length == 2 && move.vanish[1].c != color) //avoid castle
- {
+ if (move.vanish.length == 2 && move.vanish[1].c != color) {
+ // OK this isn't a castling move
const myPieceVal = V.VALUES[move.appear[0].p];
const hisPieceVal = V.VALUES[move.vanish[1].p];
+ // Favor captures
if (myPieceVal <= hisPieceVal)
- move.eval = hisPieceVal - myPieceVal + 2; //favor captures
- else
- {
+ move.eval = hisPieceVal - myPieceVal + 1;
+ else {
// Taking a pawn with minor piece,
// or minor piece or pawn with a rook,
// or anything but a queen with a queen,
// or anything with a king.
- // ==> Do it at random, although
- // this is clearly inferior to what a human can deduce...
- move.eval = (Math.random() < 0.5 ? 1 : -1);
+ move.eval = hisPieceVal - myPieceVal;
+ //Math.random() < 0.5 ? 1 : -1;
}
}
}
// TODO: also need to implement the case when an opponent piece (in light)
// is threatening something - maybe not the king, but e.g. pawn takes rook.
- moves.sort((a,b) => b.eval - a.eval);
+ moves.sort((a, b) => b.eval - a.eval);
let candidates = [0];
- for (let j=1; j<moves.length && moves[j].eval == moves[0].eval; j++)
+ for (let j = 1; j < moves.length && moves[j].eval == moves[0].eval; j++)
candidates.push(j);
return moves[candidates[randInt(candidates.length)]];
}
-}
+
+};