export default class ConvertRules extends ChessRules {
- // TODO
+ // TODO: options ? (balance progressive ok it seems?)
static get Options() {
return {
select: C.Options.select,
setOtherVariables(fenParsed, pieceArray) {
super.setOtherVariables(fenParsed, pieceArray);
// Stack of "last move" only for intermediate chaining
- this.lastMoveEnd = [null];
+ this.lastMoveEnd = [];
}
genRandInitBaseFen() {
return mv;
}
-// TODO from here
+ getPiece(x, y) {
+ const L = this.lastMoveEnd.length;
+ if (L >= 1 && this.lastMoveEnd[L-1].x == x && this.lastMoveEnd[L-1].y == y)
+ return this.lastMoveEnd[L-1].p;
+ return super.getPiece(x, y);
+ }
+
+ getPotentialMovesFrom([x, y], color) {
+ const L = this.lastMoveEnd.length;
+ if (
+ L >= 1 &&
+ (x != this.lastMoveEnd[L-1].x || y != this.lastMoveEnd[L-1].y)
+ ) {
+ // A capture was played: wrong square
+ return [];
+ }
+ return super.getPotentialMovesFrom([x, y], color);
+ }
+
+ underAttack_aux([x, y], color, explored) {
+ if (explored.some(sq => sq[0] == x && sq[1] == y))
+ // Start of an infinite loop: exit
+ return false;
+ explored.push([x, y]);
+ if (super.underAttack([x, y], [color]))
+ return true;
+ // Maybe indirect "chaining" attack:
+ const myColor = this.turn;
+ let res = false;
+ let toCheck = []; //check all but king (no need)
+ // Pawns:
+ const shiftToPawn = (myColor == 'w' ? -1 : 1);
+ for (let yShift of [-1, 1]) {
+ const [i, j] = [x + shiftToPawn, y + yShift];
+ if (
+ this.onBoard(i, j) &&
+ this.board[i][j] != "" &&
+ // NOTE: no need to check color (no enemy pawn can take directly)
+ this.getPiece(i, j) == 'p'
+ ) {
+ toCheck.push([i, j]);
+ }
+ }
+ // Knights:
+ this.pieces()['n'].both[0].steps.forEach(s => {
+ const [i, j] = [x + s[0], y + s[1]];
+ if (
+ this.onBoard(i, j) &&
+ this.board[i][j] != "" &&
+ this.getPiece(i, j) == 'n'
+ ) {
+ toCheck.push([i, j]);
+ }
+ });
+ // Sliders:
+ this.pieces()['q'].both[0].steps.forEach(s => {
+ let [i, j] = [x + s[0], y + s[1]];
+ while (this.onBoard(i, j) && this.board[i][j] == "") {
+ i += s[0];
+ j += s[1];
+ }
+ if (!this.onBoard(i, j))
+ return;
+ const piece = this.getPiece(i, j);
+ if (
+ piece == 'q' ||
+ (piece == 'r' && (s[0] == 0 || s[1] == 0)) ||
+ (piece == 'b' && (s[0] != 0 && s[1] != 0))
+ ) {
+ toCheck.push([i, j]);
+ }
+ });
+ for (let ij of toCheck) {
+ if (this.underAttack_aux(ij, color, explored))
+ return true;
+ }
+ return false;
+ }
+
+ underAttack([x, y], color) {
+ let explored = [];
+ return this.underAttack_aux([x, y], color, explored);
+ }
+
+ filterValid(moves) {
+ // No "checks" (except to forbid castle)
+ return moves;
+ }
+
+ isLastMove(move) {
+ return (
+ super.isLastMove(move) ||
+ move.vanish.length <= 1 ||
+ move.vanish[1].c != move.vanish[0].c ||
+ move.appear.length == 2 //castle!
+ );
+ }
+
+ postPlay(move) {
+ super.postPlay(move);
+ if (!this.isLastMove(move)) {
+ this.lastMoveEnd.push({
+ x: move.end.x,
+ y: move.end.y,
+ p: move.vanish[1].p //TODO: check this
+ });
+ }
+ }
+
+};
+
+// TODO: wrong rules! mismatch Convert (taking opponent pieces) and chaining (tend to be this) taking own units (with normal initial position).
+// Initial Convert:
+
+import { ChessRules, PiPo, Move } from "@/base_rules";
+import { randInt } from "@/utils/alea";
+
+export class ConvertRules extends ChessRules {
+
+ static get HasEnpassant() {
+ return false;
+ }
+
+ setOtherVariables(fen) {
+ super.setOtherVariables(fen);
+ // Stack of "last move" only for intermediate chaining
+ this.lastMoveEnd = [null];
+ }
+
+ static GenRandInitFen(options) {
+ const baseFen = ChessRules.GenRandInitFen(options);
+ return (
+ baseFen.substr(0, 8) +
+ "/8/pppppppp/8/8/PPPPPPPP/8/" +
+ baseFen.substr(35, 17)
+ );
+ }
+
+ getBasicMove([sx, sy], [ex, ey], tr) {
+ const L = this.lastMoveEnd.length;
+ const lm = this.lastMoveEnd[L-1];
+ const piece = (!!lm ? lm.p : null);
+ const c = this.turn;
+ if (this.board[ex][ey] == V.EMPTY) {
+ if (!!piece && !tr) tr = { c: c, p: piece }
+ let mv = super.getBasicMove([sx, sy], [ex, ey], tr);
+ if (!!piece) mv.vanish.pop();
+ return mv;
+ }
+ // Capture: initial, or inside a chain
+ const initPiece = (piece || this.getPiece(sx, sy));
+ const oppCol = V.GetOppCol(c);
+ const oppPiece = this.getPiece(ex, ey);
+ let mv = new Move({
+ start: { x: sx, y: sy },
+ end: { x: ex, y: ey },
+ appear: [
+ new PiPo({
+ x: ex,
+ y: ey,
+ c: c,
+ p: (!!tr ? tr.p : initPiece)
+ })
+ ],
+ vanish: [
+ new PiPo({
+ x: ex,
+ y: ey,
+ c: oppCol,
+ p: oppPiece
+ })
+ ]
+ });
+ if (!piece) {
+ // Initial capture
+ mv.vanish.unshift(
+ new PiPo({
+ x: sx,
+ y: sy,
+ c: c,
+ p: initPiece
+ })
+ );
+ }
+ // TODO: This "converted" indication isn't needed in fact,
+ // because it can be deduced from the move itself.
+ mv.end.converted = oppPiece;
+ return mv;
+ }
getPotentialMovesFrom([x, y], asA) {
const L = this.lastMoveEnd.length;
return moves;
}
+ getCheckSquares() {
+ return [];
+ }
+
prePlay(move) {
const c = this.turn;
// Extra conditions to avoid tracking converted kings:
super.updateCastleFlags(move, move.appear[0].p, c);
}
+ undo(move) {
+ this.disaggregateFlags(JSON.parse(move.flags));
+ this.lastMoveEnd.pop();
+ V.UndoOnBoard(this.board, move);
+ if (!move.end.converted) {
+ this.turn = V.GetOppCol(this.turn);
+ this.movesCount--;
+ }
+ this.postUndo(move);
+ }
+
+ postUndo(move) {
+ const c = this.getColor(move.start.x, move.start.y);
+ if (
+ move.appear[0].p == V.KING &&
+ move.vanish.length >= 1 &&
+ move.vanish[0].p == V.KING
+ ) {
+ this.kingPos[c] = [move.start.x, move.start.y];
+ }
+ }
+
+ getCurrentScore() {
+ const color = this.turn;
+ const kp = this.kingPos[color];
+ if (this.getColor(kp[0], kp[1]) != color)
+ return (color == "w" ? "0-1" : "1-0");
+ if (!super.atLeastOneMove()) return "1/2";
+ return "*";
+ }
+
+ getComputerMove() {
+ let initMoves = this.getAllValidMoves();
+ if (initMoves.length == 0) return null;
+ // Loop until valid move is found (no blocked pawn conversion...)
+ while (true) {
+ let moves = JSON.parse(JSON.stringify(initMoves));
+ let mvArray = [];
+ let mv = null;
+ // Just play random moves (for now at least. TODO?)
+ while (moves.length > 0) {
+ mv = moves[randInt(moves.length)];
+ mvArray.push(mv);
+ this.play(mv);
+ if (!!mv.end.converted)
+ // A piece was just converted
+ moves = this.getPotentialMovesFrom([mv.end.x, mv.end.y]);
+ else break;
+ }
+ for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]);
+ if (!mv.end.converted) return (mvArray.length > 1 ? mvArray : mvArray[0]);
+ }
+ return null; //never reached
+ }
+
+ getNotation(move) {
+ if (move.appear.length == 2 && move.appear[0].p == V.KING)
+ return (move.end.y < move.start.y ? "0-0-0" : "0-0");
+ const c = this.turn;
+ const L = this.lastMoveEnd.length;
+ const lm = this.lastMoveEnd[L-1];
+ const piece = (!lm ? move.appear[0].p : lm.p);
+ // Basic move notation:
+ let notation = piece.toUpperCase();
+ if (
+ this.board[move.end.x][move.end.y] != V.EMPTY ||
+ (piece == V.PAWN && move.start.y != move.end.y)
+ ) {
+ notation += "x";
+ }
+ const finalSquare = V.CoordsToSquare(move.end);
+ notation += finalSquare;
+
+ // Add potential promotion indications:
+ const firstLastRank = (c == 'w' ? [7, 0] : [0, 7]);
+ if (move.end.x == firstLastRank[1] && piece == V.PAWN)
+ notation += "=" + move.appear[0].p.toUpperCase();
+ return notation;
+ }
+
};