-import { ChessRules } from "@/base_rules";
+import { ChessRules, PiPo, Move } from "@/base_rules";
import { ArrayFun } from "@/utils/array";
import { randInt } from "@/utils/alea";
-export const VariantRules = class HiddenRules extends ChessRules {
+export class HiddenRules extends ChessRules {
+
static get HasFlags() {
return false;
}
return false;
}
+ static get SomeHiddenMoves() {
+ return true;
+ }
+
// Analyse in Hidden mode makes no sense
static get CanAnalyze() {
return false;
}
- // Moves are revealed only when game ends
+ // Moves are revealed only when game ends, but are highlighted on board
static get ShowMoves() {
- return "none";
+ return "highlight";
}
static get HIDDEN_DECODE() {
};
}
+ // Turn a hidden piece or revealed piece into revealed piece:
+ static Decode(p) {
+ if (Object.keys(V.HIDDEN_DECODE).includes(p))
+ return V.HIDDEN_DECODE[p];
+ return p;
+ }
+
static get PIECES() {
- return ChessRules.PIECES.concat(Object.values(V.HIDDEN_CODE));
+ return ChessRules.PIECES.concat(Object.keys(V.HIDDEN_DECODE));
+ }
+
+ // Pieces can be hidden :)
+ getPiece(i, j) {
+ const piece = this.board[i][j].charAt(1);
+ if (Object.keys(V.HIDDEN_DECODE).includes(piece))
+ return V.HIDDEN_DECODE[piece];
+ return piece;
+ }
+
+ getPpath(b, color, score) {
+ if (Object.keys(V.HIDDEN_DECODE).includes(b[1])) {
+ // Supposed to be hidden.
+ if (score == "*" && (!color || color != b[0]))
+ return "Hidden/" + b[0] + "p";
+ // Else: condition OK to show the piece
+ return b[0] + V.HIDDEN_DECODE[b[1]];
+ }
+ // The piece is already not supposed to be hidden:
+ return b;
}
// Scan board for kings positions (no castling)
- scanKingsRooks(fen) {
+ scanKings(fen) {
this.kingPos = { w: [-1, -1], b: [-1, -1] };
const fenRows = V.ParseFen(fen).position.split("/");
for (let i = 0; i < fenRows.length; i++) {
this.kingPos["w"] = [i, k];
break;
default: {
- const num = parseInt(fenRows[i].charAt(j));
+ const num = parseInt(fenRows[i].charAt(j), 10);
if (!isNaN(num)) k += num - 1;
}
}
}
}
- getPpath(b, color, score) {
- if (Object.keys(V.HIDDEN_DECODE).includes(b[1])) {
- // Supposed to be hidden.
- if (score == "*" && (!color || color != b[0]))
- return "Hidden/" + b[0] + "p";
- // Else: condition OK to show the piece
- return b[0] + V.HIDDEN_DECODE[b[1]];
+ getBasicMove([sx, sy], [ex, ey], tr) {
+ if (
+ tr &&
+ Object.keys(V.HIDDEN_DECODE).includes(this.board[sx][sy].charAt(1))
+ ) {
+ // The transformed piece is a priori hidden
+ tr.p = V.HIDDEN_CODE[tr.p];
}
- // The piece is already not supposed to be hidden:
- return b;
+ let mv = new Move({
+ appear: [
+ new PiPo({
+ x: ex,
+ y: ey,
+ c: tr ? tr.c : this.getColor(sx, sy),
+ p: tr ? tr.p : this.board[sx][sy].charAt(1)
+ })
+ ],
+ vanish: [
+ new PiPo({
+ x: sx,
+ y: sy,
+ c: this.getColor(sx, sy),
+ p: this.board[sx][sy].charAt(1)
+ })
+ ]
+ });
+
+ // The opponent piece disappears if we take it
+ if (this.board[ex][ey] != V.EMPTY) {
+ mv.vanish.push(
+ new PiPo({
+ x: ex,
+ y: ey,
+ c: this.getColor(ex, ey),
+ p: this.board[ex][ey].charAt(1)
+ })
+ );
+ // Pieces are revealed when they capture
+ mv.appear[0].p = V.Decode(mv.appear[0].p);
+ }
+
+ return mv;
}
- //getPotentialMovesFrom: TODO: write
+ filterValid(moves) {
+ return moves;
+ }
- // TODO: bishops on different colors, a1 --> h1, h2 --> a2 ?
+ // Ignore randomness here: placement is always random asymmetric
static GenRandInitFen() {
let pieces = { w: new Array(8), b: new Array(8) };
// Shuffle pieces + pawns on two first ranks
const knight2Pos = positions[randIndex];
positions.splice(randIndex, 1);
- // Get random square for queen
+ // Get random squares for rooks
randIndex = randInt(12);
+ const rook1Pos = positions[randIndex];
+ positions.splice(randIndex, 1);
+ randIndex = randInt(11);
+ const rook2Pos = positions[randIndex];
+ positions.splice(randIndex, 1);
+
+ // Get random square for queen
+ randIndex = randInt(10);
const queenPos = positions[randIndex];
positions.splice(randIndex, 1);
- // Get random squares for pawns
- // TODO...
+ // Get random square for king
+ randIndex = randInt(9);
+ const kingPos = positions[randIndex];
+ positions.splice(randIndex, 1);
- // Rooks and king positions are now fixed,
- // because of the ordering rook-king-rook
- const rook1Pos = positions[0];
- const kingPos = positions[1];
- const rook2Pos = positions[2];
+ // Pawns position are all remaining slots:
+ for (let p of positions)
+ pieces[c][p] = "s";
// Finally put the shuffled pieces in the board array
- pieces[c][rook1Pos] = "r";
- pieces[c][knight1Pos] = "n";
- pieces[c][bishop1Pos] = "b";
- pieces[c][queenPos] = "q";
- pieces[c][kingPos] = "k";
- pieces[c][bishop2Pos] = "b";
- pieces[c][knight2Pos] = "n";
- pieces[c][rook2Pos] = "r";
+ pieces[c][rook1Pos] = "u";
+ pieces[c][knight1Pos] = "o";
+ pieces[c][bishop1Pos] = "c";
+ pieces[c][queenPos] = "t";
+ pieces[c][kingPos] = "l";
+ pieces[c][bishop2Pos] = "c";
+ pieces[c][knight2Pos] = "o";
+ pieces[c][rook2Pos] = "u";
}
- return (
- pieces["b"].join("") +
- "/pppppppp/8/8/8/8/PPPPPPPP/" +
- pieces["w"].join("").toUpperCase() +
- " w 0"
- );
+ let upFen = pieces["b"].join("");
+ upFen = upFen.substr(0,8) + "/" +
+ upFen.substr(8).split("").reverse().join("");
+ let downFen = pieces["b"].join("").toUpperCase();
+ downFen = downFen.substr(0,8) + "/" +
+ downFen.substr(8).split("").reverse().join("");
+ return upFen + "/8/8/8/8/" + downFen + " w 0";
}
getCheckSquares() {
return [];
}
- updateVariables(move) {
- super.updateVariables(move);
+ postPlay(move) {
+ super.postPlay(move);
if (
move.vanish.length >= 2 &&
[V.KING,V.HIDDEN_CODE[V.KING]].includes(move.vanish[1].p)
}
}
- unupdateVariables(move) {
- super.unupdateVariables(move);
+ postUndo(move) {
+ super.postUndo(move);
const c = move.vanish[0].c;
const oppCol = V.GetOppCol(c);
if (this.kingPos[oppCol][0] < 0)
}
getComputerMove() {
- // Just return a random move. TODO: something smarter...
- const moves = this.getAllValidMoves();
- return moves[randInt(moves.length)];
+ const color = this.turn;
+ let moves = this.getAllValidMoves();
+ for (let move of moves) {
+ move.eval = 0; //a priori...
+
+ // Can I take something ? If yes, do it with some probability
+ 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 =
+ Object.keys(V.HIDDEN_DECODE).includes(move.vanish[1].p)
+ ? undefined
+ : V.VALUES[move.vanish[1].p];
+ if (!hisPieceVal) {
+ // Opponent's piece is unknown: do not take too much risk
+ move.eval = -myPieceVal + 1.5; //so that pawns always take
+ }
+ // Favor captures
+ else if (myPieceVal <= hisPieceVal)
+ 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.
+ move.eval = hisPieceVal - myPieceVal;
+ }
+ } else {
+ // If no capture, favor small step moves,
+ // but sometimes move the knight anyway
+ const penalty = V.Decode(move.vanish[0].p) != V.KNIGHT
+ ? Math.abs(move.end.x - move.start.x) +
+ Math.abs(move.end.y - move.start.y)
+ : (Math.random() < 0.5 ? 3 : 1);
+ move.eval -= penalty / (V.size.x + V.size.y - 1);
+ }
+
+ // TODO: also favor movements toward the center?
+ }
+
+ 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++)
+ candidates.push(j);
+ return moves[candidates[randInt(candidates.length)]];
+ }
+
+ getNotation(move) {
+ // Translate final square
+ const finalSquare = V.CoordsToSquare(move.end);
+
+ const piece = this.getPiece(move.start.x, move.start.y);
+ if (piece == V.PAWN) {
+ // Pawn move
+ let notation = "";
+ if (move.vanish.length > move.appear.length) {
+ // Capture
+ const startColumn = V.CoordToColumn(move.start.y);
+ notation = startColumn + "x" + finalSquare;
+ }
+ else notation = finalSquare;
+ if (move.appear.length > 0 && !["p","s"].includes(move.appear[0].p)) {
+ // Promotion
+ const appearPiece = V.Decode(move.appear[0].p);
+ notation += "=" + appearPiece.toUpperCase();
+ }
+ return notation;
+ }
+ // Piece movement
+ return (
+ piece.toUpperCase() +
+ (move.vanish.length > move.appear.length ? "x" : "") +
+ finalSquare
+ );
}
+
};