--- /dev/null
+import { ChessRules, PiPo, Move } from "@/base_rules";
+
+export class AllmateRules extends ChessRules {
+
+ static get HasEnpassant() {
+ return false;
+ }
+
+ getCheckSquares() {
+ // No notion of check
+ return [];
+ }
+
+ static GenRandInitFen(randomness) {
+ return ChessRules.GenRandInitFen(randomness).slice(0, -2);
+ }
+
+ getPotentialMovesFrom([x, y]) {
+ let moves = super.getPotentialMovesFrom([x, y]);
+ // Remove standard captures (without removing castling):
+ moves = moves.filter(m => {
+ return m.vanish.length == 1 || m.appear.length == 2;
+ });
+
+ // Augment moves with "mate-captures":
+ // TODO: this is coded in a highly inefficient way...
+ const color = this.turn;
+ const oppCol = V.GetOppCol(this.turn);
+ moves.forEach(m => {
+ this.play(m);
+ let allAttacks = [];
+
+ // While something has been captured:
+ // remove it, and keep looking for captures
+ outerLoop: while (true) {
+
+ // 1) What is attacked?
+ let attacked = {};
+ for (let i=0; i<V.size.x; i++) {
+ for (let j=0; j<V.size.y; j++) {
+ if (this.getColor(i,j) == oppCol && this.isAttacked([i,j], color))
+ attacked[i+"_"+j] = [i,j];
+ }
+ }
+ if (Object.keys(attacked).length == 0) break outerLoop;
+
+ // 2) Among attacked pieces, which cannot escape capture?
+ // Avoid "oppMoves = this.getAllValidMoves();" => infinite recursion
+ for (let i=0; i<V.size.x; i++) {
+ for (let j=0; j<V.size.y; j++) {
+ if (this.getColor(i,j) == oppCol) {
+ let oppMoves = [];
+ switch (this.getPiece(i, j)) {
+ case V.PAWN:
+ oppMoves = this.getPotentialPawnMoves([i, j]);
+ break;
+ case V.ROOK:
+ oppMoves = this.getPotentialRookMoves([i, j]);
+ break;
+ case V.KNIGHT:
+ oppMoves = this.getPotentialKnightMoves([i, j]);
+ break;
+ case V.BISHOP:
+ oppMoves = this.getPotentialBishopMoves([i, j]);
+ break;
+ case V.QUEEN:
+ oppMoves = this.getPotentialQueenMoves([i, j]);
+ break;
+ case V.KING:
+ // Do not allow castling to escape from check
+ oppMoves = super.getSlideNJumpMoves(
+ [i, j],
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
+ break;
+ }
+ for (let om of oppMoves) {
+ V.PlayOnBoard(this.board, om);
+ Object.values(attacked).forEach(sq => {
+ const origSq = [sq[0], sq[1]];
+ if (om.start.x == sq[0] && om.start.y == sq[1])
+ // Piece moved:
+ sq = [om.appear[0].x, om.appear[0].y];
+ if (!this.isAttacked(sq, color))
+ delete attacked[origSq[0]+"_"+origSq[1]];
+ });
+ V.UndoOnBoard(this.board, om);
+ if (Object.keys(attacked).length == 0)
+ // No need to explore more moves
+ break outerLoop;
+ }
+ }
+ }
+ }
+
+ // 3) Add mate-captures and remove pieces from board:
+ Object.keys(attacked).forEach(k => {
+ allAttacks.push({
+ sq: attacked[k],
+ p: this.getPiece(attacked[k][0], attacked[k][1])
+ });
+ this.board[attacked[k][0]][attacked[k][1]] = V.EMPTY;
+ });
+ }
+
+ // Put removed pieces back on board:
+ allAttacks.forEach(v => {
+ this.board[v.sq[0]][v.sq[1]] = oppCol + v.p;
+ });
+ this.undo(m);
+ allAttacks.forEach(v => {
+ m.vanish.push(
+ new PiPo({
+ x: v.sq[0],
+ y: v.sq[1],
+ c: oppCol,
+ p: v.p
+ })
+ );
+ });
+ });
+
+ return moves;
+ }
+
+ // No "under check" conditions in castling
+ getCastleMoves(sq) {
+ return super.getCastleMoves(sq, null, "castleInCheck");
+ }
+
+ // TODO: allow pieces to "commit suicide"? (Currently yes except king)
+ filterValid(moves) {
+ // Remove moves which let the king mate-captured:
+ if (moves.length == 0) return [];
+ const color = this.turn;
+ const oppCol = V.GetOppCol(color);
+ return moves.filter(m => {
+ let res = true;
+ this.play(m);
+ if (this.underCheck(color)) {
+ res = false;
+ const attacked = this.kingPos[color];
+ // Try to find a move to escape check
+ // TODO: very inefficient method.
+ outerLoop: for (let i=0; i<V.size.x; i++) {
+ for (let j=0; j<V.size.y; j++) {
+ if (this.getColor(i,j) == color) {
+ let emoves = [];
+ // Artficial turn change to "play twice":
+ this.turn = color;
+ switch (this.getPiece(i, j)) {
+ case V.PAWN:
+ emoves = this.getPotentialPawnMoves([i, j]);
+ break;
+ case V.ROOK:
+ emoves = this.getPotentialRookMoves([i, j]);
+ break;
+ case V.KNIGHT:
+ emoves = this.getPotentialKnightMoves([i, j]);
+ break;
+ case V.BISHOP:
+ emoves = this.getPotentialBishopMoves([i, j]);
+ break;
+ case V.QUEEN:
+ emoves = this.getPotentialQueenMoves([i, j]);
+ break;
+ case V.KING:
+ emoves = this.getPotentialKingMoves([i, j]);
+ break;
+ }
+ this.turn = oppCol;
+ for (let em of emoves) {
+ V.PlayOnBoard(this.board, em);
+ let sq = attacked;
+ if (em.start.x == attacked[0] && em.start.y == attacked[1])
+ // King moved:
+ sq = [em.appear[0].x, em.appear[0].y];
+ if (!this.isAttacked(sq, oppCol))
+ res = true;
+ V.UndoOnBoard(this.board, em);
+ if (res)
+ // No need to explore more moves
+ break outerLoop;
+ }
+ }
+ }
+ }
+ }
+ this.undo(m);
+ return res;
+ });
+ }
+
+ postPlay(move) {
+ super.postPlay(move);
+ if (move.vanish.length >= 2 && move.appear.length == 1) {
+ for (let i = 1; i<move.vanish.length; i++) {
+ const v = move.vanish[i];
+ // Did opponent king disappeared?
+ if (v.p == V.KING)
+ this.kingPos[this.turn] = [-1, -1];
+ // Or maybe a rook?
+ else if (v.p == V.ROOK) {
+ if (v.y < this.kingPos[v.c][1])
+ this.castleFlags[v.c][0] = 8;
+ else
+ // v.y > this.kingPos[v.c][1]
+ this.castleFlags[v.c][1] = 8;
+ }
+ }
+ }
+ }
+
+ preUndo(move) {
+ super.preUndo(move);
+ const oppCol = this.turn;
+ if (move.vanish.length >= 2 && move.appear.length == 1) {
+ // Did opponent king disappeared?
+ const psq = move.vanish.find(v => v.p == V.KING && v.c == oppCol)
+ if (psq)
+ this.kingPos[psq.c] = [psq.x, psq.y];
+ }
+ }
+
+ 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()) return "*";
+ // Kings still there, no moves:
+ return "1/2";
+ }
+
+ static get SEARCH_DEPTH() {
+ return 1;
+ }
+
+ getNotation(move) {
+ let notation = super.getNotation(move);
+ // Add a capture mark (not describing what is captured...):
+ if (move.vanish.length > 1 && move.appear.length == 1) {
+ if (!!(notation.match(/^[a-h]x/)))
+ // Pawn capture: remove initial "b" in bxc4 for example
+ notation = notation.substr(1);
+ notation = notation.replace("x","") + "X";
+ }
+ return notation;
+ }
+
+};