X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FAntiking1.js;fp=client%2Fsrc%2Fvariants%2FAntiking1.js;h=42450d1725adddc64caf8fe1afa97399b1699a74;hb=c583ef1c1dfd19aee88b22c2175202fbdf4dc1c0;hp=0000000000000000000000000000000000000000;hpb=f1c9d7072d253ba55a941f97a99eee7e394577ed;p=vchess.git diff --git a/client/src/variants/Antiking1.js b/client/src/variants/Antiking1.js new file mode 100644 index 00000000..42450d17 --- /dev/null +++ b/client/src/variants/Antiking1.js @@ -0,0 +1,270 @@ +import { ChessRules } from "@/base_rules"; +import { ArrayFun } from "@/utils/array"; +import { randInt } from "@/utils/alea"; + +export const VariantRules = class Antiking1Rules extends ChessRules { + static get HasEnpassant() { + return false; + } + + static get HasCastle() { + return false; + } + + static get ANTIKING() { + return "a"; + } + + static get PIECES() { + return ChessRules.PIECES.concat([V.ANTIKING]); + } + + getPpath(b) { + return b[1] == "a" ? "Antiking/" + b : b; + } + + setOtherVariables(fen) { + super.setOtherVariables(fen); + this.antikingPos = { w: [-1, -1], b: [-1, -1] }; + const rows = V.ParseFen(fen).position.split("/"); + for (let i = 0; i < rows.length; i++) { + let k = 0; + for (let j = 0; j < rows[i].length; j++) { + switch (rows[i].charAt(j)) { + case "a": + this.antikingPos["b"] = [i, k]; + break; + case "A": + this.antikingPos["w"] = [i, k]; + break; + default: { + const num = parseInt(rows[i].charAt(j)); + if (!isNaN(num)) k += num - 1; + } + } + k++; + } + } + } + + // (Anti)King flags at 1 (true) if they can knight-jump + setFlags(fenflags) { + this.kingFlags = { + // King then antiking + w: [...Array(2).fill(false)], + b: [...Array(2).fill(false)] + }; + for (let c of ["w", "b"]) { + for (let i = 0; i < 2; i++) + this.kingFlags[c][i] = fenflags.charAt((c == "w" ? 0 : 2) + i) == "1"; + } + } + + aggregateFlags() { + return this.kingFlags; + } + + disaggregateFlags(flags) { + this.kingFlags = flags; + } + + getFlagsFen() { + // Return kings flags + let flags = ""; + for (let c of ["w", "b"]) { + for (let i = 0; i < 2; i++) flags += this.kingFlags[c][i] ? "1" : "0"; + } + return flags; + } + + canTake([x1, y1], [x2, y2]) { + const piece1 = this.getPiece(x1, y1); + const piece2 = this.getPiece(x2, y2); + const color1 = this.getColor(x1, y1); + const color2 = this.getColor(x2, y2); + return ( + piece2 != "a" && + ((piece1 != "a" && color1 != color2) || + (piece1 == "a" && color1 == color2)) + ); + } + + getPotentialMovesFrom([x, y]) { + let moves = []; + let addKnightJumps = false; + const piece = this.getPiece(x, y); + const color = this.getColor(x, y); + if (piece == V.ANTIKING) { + moves = this.getPotentialAntikingMoves([x, y]); + addKnightJumps = this.kingFlags[color][1]; + } else { + moves = super.getPotentialMovesFrom([x, y]); + if (piece == V.KING) addKnightJumps = this.kingFlags[color][0]; + } + if (addKnightJumps) { + // Add potential knight jump to (anti)kings + const knightJumps = super.getPotentialKnightMoves([x, y]); + // Remove captures (TODO: could be done more efficiently...) + moves = moves.concat(knightJumps.filter(m => m.vanish.length == 1)); + } + return moves; + } + + getPotentialPawnMoves([x, y]) { + const color = this.turn; + let moves = []; + const [sizeX, sizeY] = [V.size.x, V.size.y]; + const shiftX = color == "w" ? -1 : 1; + const startRank = color == "w" ? sizeX - 2 : 1; + const lastRank = color == "w" ? 0 : sizeX - 1; + const finalPieces = + x + shiftX == lastRank ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.PAWN]; + + // One square diagonally + for (let shiftY of [-1, 1]) { + if (this.board[x + shiftX][y + shiftY] == V.EMPTY) { + for (let piece of finalPieces) { + moves.push( + this.getBasicMove([x, y], [x + shiftX, y + shiftY], { + c: color, + p: piece + }) + ); + } + } + } + // Capture + if ( + this.board[x + shiftX][y] != V.EMPTY && + this.canTake([x, y], [x + shiftX, y]) + ) { + for (let piece of finalPieces) + moves.push( + this.getBasicMove([x, y], [x + shiftX, y], { c: color, p: piece }) + ); + } + + return moves; + } + + getPotentialAntikingMoves(sq) { + // The antiking moves like a king (only captured colors differ) + return this.getSlideNJumpMoves( + sq, + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), + "oneStep" + ); + } + + isAttacked(sq, color) { + return ( + super.isAttacked(sq, color) || + this.isAttackedByAntiking(sq, color) + ); + } + + isAttackedByPawn([x, y], color) { + let pawnShift = (color == "w" ? 1 : -1); + if (x + pawnShift >= 0 && x + pawnShift < V.size.x) { + if ( + this.getPiece(x + pawnShift, y) == V.PAWN && + this.getColor(x + pawnShift, y) == color + ) { + return true; + } + } + return false; + } + + isAttackedByKing([x, y], color) { + // Antiking is not attacked by king: + if (this.getPiece(x, y) == V.ANTIKING) return false; + return this.isAttackedBySlideNJump( + [x, y], + color, + V.KING, + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), + "oneStep" + ); + } + + isAttackedByAntiking([x, y], color) { + // (Anti)King is not attacked by antiking + if ([V.KING, V.ANTIKING].includes(this.getPiece(x, y))) return false; + return this.isAttackedBySlideNJump( + [x, y], + color, + V.ANTIKING, + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), + "oneStep" + ); + } + + underCheck(color) { + const oppCol = V.GetOppCol(color); + let res = + this.isAttacked(this.kingPos[color], oppCol) || + !this.isAttacked(this.antikingPos[color], oppCol); + return res; + } + + getCheckSquares(color) { + let res = []; + const oppCol = V.GetOppCol(color); + if (this.isAttacked(this.kingPos[color], oppCol)) + res.push(JSON.parse(JSON.stringify(this.kingPos[color]))); + if (!this.isAttacked(this.antikingPos[color], oppCol)) + res.push(JSON.parse(JSON.stringify(this.antikingPos[color]))); + return res; + } + + postPlay(move) { + super.postPlay(move); + const piece = move.vanish[0].p; + const c = move.vanish[0].c; + // Update antiking position, and kings flags + if (piece == V.ANTIKING) { + this.antikingPos[c][0] = move.appear[0].x; + this.antikingPos[c][1] = move.appear[0].y; + this.kingFlags[c][1] = false; + } else if (piece == V.KING) this.kingFlags[c][0] = false; + } + + postUndo(move) { + super.postUndo(move); + const c = move.vanish[0].c; + if (move.vanish[0].p == V.ANTIKING) + this.antikingPos[c] = [move.start.x, move.start.y]; + } + + getCurrentScore() { + if (this.atLeastOneMove()) + return "*"; + + const color = this.turn; + const oppCol = V.GetOppCol(color); + if ( + !this.isAttacked(this.kingPos[color], oppCol) && + this.isAttacked(this.antikingPos[color], oppCol) + ) { + return "1/2"; + } + return color == "w" ? "0-1" : "1-0"; + } + + static get VALUES() { + return Object.assign( + { a: 1000 }, + ChessRules.VALUES + ); + } + + static GenRandInitFen() { + // Always deterministic setup + return "2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2 w 0 1111"; + } + + static get SEARCH_DEPTH() { + return 2; + } +};