From a548cb4e3ad8099e977da9bb4a4184973beb56e3 Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Thu, 30 Jun 2022 17:26:07 +0200 Subject: [PATCH] Draft Antiking1/2. Changed underCheck/searchKingPos (TODO: impact on some other variants) --- base_rules.js | 54 ++++++++++++-------- variants/AbstractAntiking.js | 69 ++++++++++++++++++++++++++ variants/Antiking1/class.js | 93 +++++++++++++++++++++++++++++++++++ variants/Antiking1/rules.html | 1 + variants/Antiking2/class.js | 33 +++++++++++++ variants/Antiking2/rules.html | 1 + 6 files changed, 230 insertions(+), 21 deletions(-) create mode 100644 variants/AbstractAntiking.js create mode 100644 variants/Antiking1/class.js create mode 100644 variants/Antiking1/rules.html create mode 100644 variants/Antiking2/class.js create mode 100644 variants/Antiking2/rules.html diff --git a/base_rules.js b/base_rules.js index 54c1220..e3a34fb 100644 --- a/base_rules.js +++ b/base_rules.js @@ -1511,7 +1511,7 @@ export default class ChessRules { if (piece == "p" && this.hasEnpassant && this.epSquare) Array.prototype.push.apply(moves, this.getEnpassantCaptures([x, y])); if ( - piece == "k" && this.hasCastle && + this.isKing(0, 0, piece) && this.hasCastle && this.castleFlags[color || this.turn].some(v => v < this.size.y) ) { Array.prototype.push.apply(moves, this.getCastleMoves([x, y])); @@ -2162,21 +2162,24 @@ export default class ChessRules { ); } - underCheck([x, y], oppCol) { + underCheck(square_s, oppCol) { if (this.options["taking"] || this.options["dark"]) return false; - return this.underAttack([x, y], oppCol); + if (!Array.isArray(square_s)) + square_s = [square_s]; + return square_s.some(sq => this.underAttack(sq, oppCol)); } - // Stop at first king found (TODO: multi-kings) + // Scan board for king(s) searchKingPos(color) { + let res = []; for (let i=0; i < this.size.x; i++) { for (let j=0; j < this.size.y; j++) { if (this.getColor(i, j) == color && this.isKing(i, j)) - return [i, j]; + res.push([i, j]); } } - return [-1, -1]; //king not found + return res; } // 'color' arg because some variants (e.g. Refusal) check opponent moves @@ -2184,24 +2187,30 @@ export default class ChessRules { if (!color) color = this.turn; const oppCol = C.GetOppCol(color); - const kingPos = this.searchKingPos(color); + let kingPos = this.searchKingPos(color); let filtered = {}; //avoid re-checking similar moves (promotions...) return moves.filter(m => { const key = m.start.x + m.start.y + '.' + m.end.x + m.end.y; if (!filtered[key]) { this.playOnBoard(m); - let square = kingPos, + let newKingPP = null, + sqIdx = 0, res = true; //a priori valid - if (m.vanish.some(v => this.isKing(0, 0, v.p) && v.c == color)) { + const oldKingPP = m.vanish.find(v => this.isKing(0, 0, v.p) && v.c == color); + if (oldKingPP) { // Search king in appear array: - const newKingIdx = - m.appear.findIndex(a => this.isKing(0, 0, a.p) && a.c == color); - if (newKingIdx >= 0) - square = [m.appear[newKingIdx].x, m.appear[newKingIdx].y]; + newKingPP = + m.appear.find(a => this.isKing(0, 0, a.p) && a.c == color); + if (newKingPP) { + sqIdx = kingPos.findIndex(kp => kp[0] == oldKingPP.x && kp[1] == oldKingPP[.y); + kingPos[sqIdx] = [newKingPP.x, newKingPP.y]; + } else - res = false; + res = false; //king vanished } - res &&= !this.underCheck(square, oppCol); + res &&= !this.underCheck(square_s, oppCol); + if (oldKingPP && newKingPP) + kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y]; this.undoOnBoard(m); filtered[key] = res; return res; @@ -2348,7 +2357,7 @@ export default class ChessRules { return false; const color = this.turn; const oppKingPos = this.searchKingPos(C.GetOppCol(color)); - if (oppKingPos[0] < 0 || this.underCheck(oppKingPos, color)) + if (oppKingPos.length == 0 || this.underCheck(oppKingPos, color)) return true; return ( ( @@ -2397,17 +2406,20 @@ export default class ChessRules { getCurrentScore(move) { const color = this.turn; const oppCol = C.GetOppCol(color); - const kingPos = [this.searchKingPos(color), this.searchKingPos(oppCol)]; - if (kingPos[0][0] < 0 && kingPos[1][0] < 0) + const kingPos = { + [color]: this.searchKingPos(color), + [oppCol]: this.searchKingPos(oppCol) + }; + if (kingPos[color].length == 0 && kingPos[oppCol].length == 0) return "1/2"; - if (kingPos[0][0] < 0) + if (kingPos[color].length == 0) return (color == "w" ? "0-1" : "1-0"); - if (kingPos[1][0] < 0) + if (kingPos[oppCol].length == 0) return (color == "w" ? "1-0" : "0-1"); if (this.atLeastOneMove(color)) return "*"; // No valid move: stalemate or checkmate? - if (!this.underCheck(kingPos[0], oppCol)) + if (!this.underCheck(kingPos[color], oppCol)) return "1/2"; // OK, checkmate return (color == "w" ? "0-1" : "1-0"); diff --git a/variants/AbstractAntiking.js b/variants/AbstractAntiking.js new file mode 100644 index 0000000..7846add --- /dev/null +++ b/variants/AbstractAntiking.js @@ -0,0 +1,69 @@ +import ChessRules from "/base_rules.js"; + +export class AbstractAntikingRules extends ChessRules { + + static get Options() { + return { + styles: [ + "atomic", + "balance", + "cannibal", + "capture", + "crazyhouse", + "doublemove", + "madrasi", + "progressive", + "recycle", + "rifle", + "teleport", + "zen" + ] + }; + } + + pieces(color, x, y) { + "a": { + // Move like a king, no attacks + "class": "antiking", + moves: super.pieces(color, x, y)['k'].moves, + attack: [] + } + } + + isKing(x, y, p) { + if (!p) + p = this.getPiece(x, y); + return ['k', 'a'].includes(p); + } + + 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) + ) + ); + } + + underCheck(squares, color) { + const oppCol = C.GetOppCol(color); + let res = false; + squares.forEach(sq => { + switch (this.getPiece(sq[0], sq[1])) { + case 'k': + res ||= super.underAttack(sq, oppCol); + break; + case 'a': + res ||= !super.underAttack(sq, oppCol); + break; + } + }); + return res; + } + +}; diff --git a/variants/Antiking1/class.js b/variants/Antiking1/class.js new file mode 100644 index 0000000..f8406b7 --- /dev/null +++ b/variants/Antiking1/class.js @@ -0,0 +1,93 @@ +import ChessRules from "/base_rules.js"; +import AbstractAntikingRules from "/variants/AbstractAntiking.js"; + +export class Antiking1Rules extends AbstractAntikingRules { + + static get Options() { + return { + styles: [ + "atomic", + "balance", + "cannibal", + "capture", + "crazyhouse", + "doublemove", + "madrasi", + "progressive", + "recycle", + "rifle", + "teleport", + "zen" + ] + }; + } + + get hasCastle() { + return false; + } + + pieces(color, x, y) { + const pawnShift = (color == "w" ? -1 : 1); + let res = super.pieces(color, x, y); + res['p'].moves = [ + { + steps: [[pawnShift, 1], [pawnShift, -1]], + range: 1 + } + ]; + res['p'].attack = [ + { + steps: [[pawnShift, 0]], + range: 1 + } + ]; + return res; + } + + genRandInitFen() { + // Always deterministic setup + return ( + '2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2 w 0 ' + + '{"flags":"KAka"}' + ); + } + + // (Anti)King flags at 1 (true) if they can knight-jump + setFlags(fenflags) { + this.kingFlags = { w: {}, b: {} }; + for (let i=0; i Object.keys(this.kingFlags[c])) + ).join("") + ); + } + + getPotentialMovesFrom([x, y]) { + const color = this.turn; + let moves = super.getPotentialMovesFrom([x, y]); + if (this.kingFlags[color][piece]) { + // Allow knight jump (king or antiking) + const knightMoves = super.getPotentialMovesOf('n', [x, y]); + // Remove captures (TODO: could be done more efficiently...) + moves = moves.concat(knightJumps.filter(m => m.vanish.length == 1)); + } + return moves; + } + + prePlay(move) { + super.prePlay(move); + // Update king+antiking flags + const piece = move.vanish[0].p; + if (this.isKing(0, 0, piece)) + delete this.kingFlags[move.vanish[0].c][piece]; + } + +}; diff --git a/variants/Antiking1/rules.html b/variants/Antiking1/rules.html new file mode 100644 index 0000000..fee723a --- /dev/null +++ b/variants/Antiking1/rules.html @@ -0,0 +1 @@ +https://www.chessvariants.com/diffobjective.dir/anti-king-chess.html diff --git a/variants/Antiking2/class.js b/variants/Antiking2/class.js new file mode 100644 index 0000000..0e6a3e5 --- /dev/null +++ b/variants/Antiking2/class.js @@ -0,0 +1,33 @@ +import ChessRules from "/base_rules.js"; +import AbstractAntikingRules from "/variants/AbstractAntiking.js"; +import { Random } from "/utils/alea.js"; + +export class Antiking2Rules extends AbstractAntikingRules { + + static get Aliases() { + return Object.assign({'A': AbstractAntikingRules}, ChessRules.Aliases); + } + + static get Options() { + return { + styles: A.Options.styles.concat("cylinder") + }; + } + + genRandInitFen(seed) { + const baseFen = super.genRandInitFen(seed); + // Just add an antiking on 3rd ranks + let akPos = [3, 3]; + if (this.options.randomness >= 1) { + akPos[0] = Random.randInt(this.size.y); + if (this.options.randomness == 2) + akPos[1] = Random.randInt(this.size.y); + else + akPos[1] = akPos[0]; + } + const whiteLine = (akPos[0] > 0 ? akPos[0] : "") + 'A' + (akPos < this.size.y - 1 ? ...); + const blackLine = ... + return baseFen.replace(...) + } + +}; diff --git a/variants/Antiking2/rules.html b/variants/Antiking2/rules.html new file mode 100644 index 0000000..fee723a --- /dev/null +++ b/variants/Antiking2/rules.html @@ -0,0 +1 @@ +https://www.chessvariants.com/diffobjective.dir/anti-king-chess.html -- 2.44.0