From f31de5e46015a93dca20765da61670035ce8f491 Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Fri, 1 Jul 2022 11:12:01 +0200 Subject: [PATCH] Cleaner fen generation + first draft of Apocalypse + a few fixes --- TODO | 2 + base_rules.js | 90 ++++++++-------- variants/Alapo/class.js | 104 +++++++++--------- variants/Align4/class.js | 16 +-- variants/Ambiguous/class.js | 16 +-- variants/Antiking1/class.js | 23 ++-- variants/Antiking2/class.js | 25 +++-- variants/Apocalypse/class.js | 199 +++++++++++++++++++++++++++++++++++ variants/Chakart/class.js | 11 +- variants/Giveaway/class.js | 70 ++++++------ variants/Hex/class.js | 9 +- variants/Refusal/class.js | 16 ++- variants/Suction/class.js | 17 ++- variants/_Antiking/class.js | 6 +- 14 files changed, 406 insertions(+), 198 deletions(-) create mode 100644 variants/Apocalypse/class.js diff --git a/TODO b/TODO index 1e4eda3..c2ab9af 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ +Animate castle by both movements (and generalize? match vanishes with appears?) + add variants : Dark Racing Kings ? Checkered-Teleport ? diff --git a/base_rules.js b/base_rules.js index b98c724..beac0bb 100644 --- a/base_rules.js +++ b/base_rules.js @@ -197,8 +197,19 @@ export default class ChessRules { return (f.charCodeAt(0) <= 90 ? "w" + f.toLowerCase() : "b" + f); } - // Setup the initial random-or-not (asymmetric-or-not) position genRandInitFen(seed) { + Random.setSeed(seed); //may be unused + let baseFen = this.genRandInitBaseFen(); + baseFen.o = Object.assign({init: true}, baseFen.o); + const parts = this.getPartFen(baseFen.o); + return ( + baseFen.fen + + (Object.keys(parts).length > 0 ? (" " + JSON.stringify(parts)) : "") + ); + } + + // Setup the initial random-or-not (asymmetric-or-not) position + genRandInitBaseFen() { let fen, flags = "0707"; if (!this.options.randomness) // Deterministic: @@ -206,7 +217,6 @@ export default class ChessRules { else { // Randomize - Random.setSeed(seed); let pieces = {w: new Array(8), b: new Array(8)}; flags = ""; // Shuffle pieces on first (and last rank if randomness == 2) @@ -216,9 +226,7 @@ export default class ChessRules { flags += flags; break; } - let positions = ArrayFun.range(8); - // Get random squares for bishops let randIndex = 2 * Random.randInt(4); const bishop1Pos = positions[randIndex]; @@ -228,7 +236,6 @@ export default class ChessRules { // Remove chosen squares positions.splice(Math.max(randIndex, randIndex_tmp), 1); positions.splice(Math.min(randIndex, randIndex_tmp), 1); - // Get random squares for knights randIndex = Random.randInt(6); const knight1Pos = positions[randIndex]; @@ -236,18 +243,15 @@ export default class ChessRules { randIndex = Random.randInt(5); const knight2Pos = positions[randIndex]; positions.splice(randIndex, 1); - // Get random square for queen randIndex = Random.randInt(4); const queenPos = 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]; - // Finally put the shuffled pieces in the board array pieces[c][rook1Pos] = "r"; pieces[c][knight1Pos] = "n"; @@ -266,19 +270,7 @@ export default class ChessRules { " w 0" ); } - // Add turn + flags + enpassant (+ reserve) - let parts = []; - if (this.hasFlags) - parts.push(`"flags":"${flags}"`); - if (this.hasEnpassant) - parts.push('"enpassant":"-"'); - if (this.hasReserveFen) - parts.push('"reserve":"000000000000"'); - if (this.options["crazyhouse"]) - parts.push('"ispawn":"-"'); - if (parts.length >= 1) - fen += " {" + parts.join(",") + "}"; - return fen; + return { fen: fen, o: {flags: flags} }; } // "Parse" FEN: just return untransformed string data @@ -296,23 +288,28 @@ export default class ChessRules { // Return current fen (game state) getFen() { - let fen = ( - this.getPosition() + " " + - this.getTurnFen() + " " + - this.movesCount + const parts = this.getPartFen({}); + return ( + this.getBaseFen() + + (Object.keys(parts).length > 0 ? (" " + JSON.stringify(parts)) : "") ); - let parts = []; + } + + getBaseFen() { + return this.getPosition() + " " + this.turn + " " + this.movesCount; + } + + getPartFen(o) { + let parts = {}; if (this.hasFlags) - parts.push(`"flags":"${this.getFlagsFen()}"`); + parts["flags"] = o.init ? o.flags : this.getFlagsFen(); if (this.hasEnpassant) - parts.push(`"enpassant":"${this.getEnpassantFen()}"`); + parts["enpassant"] = o.init ? "-" : this.getEnpassantFen(); if (this.hasReserveFen) - parts.push(`"reserve":"${this.getReserveFen()}"`); + parts["reserve"] = this.getReserveFen(o); if (this.options["crazyhouse"]) - parts.push(`"ispawn":"${this.getIspawnFen()}"`); - if (parts.length >= 1) - fen += " {" + parts.join(",") + "}"; - return fen; + parts["ispawn"] = this.getIspawnFen(o); + return parts; } static FenEmptySquares(count) { @@ -353,10 +350,6 @@ export default class ChessRules { return position; } - getTurnFen() { - return this.turn; - } - // Flags part of the FEN string getFlagsFen() { return ["w", "b"].map(c => { @@ -367,17 +360,22 @@ export default class ChessRules { // Enpassant part of the FEN string getEnpassantFen() { if (!this.epSquare) - return "-"; //no en-passant + return "-"; return C.CoordsToSquare(this.epSquare); } - getReserveFen() { + getReserveFen(o) { + if (o.init) + return "000000000000"; return ( ["w","b"].map(c => Object.values(this.reserve[c]).join("")).join("") ); } - getIspawnFen() { + getIspawnFen(o) { + if (o.init) + // NOTE: cannot merge because this.ispawn doesn't exist yet + return "-"; const squares = Object.keys(this.ispawn); if (squares.length == 0) return "-"; @@ -405,18 +403,18 @@ export default class ChessRules { if (o.genFenOnly) // This object will be used only for initial FEN generation return; + + // Some variables this.playerColor = o.color; this.afterPlay = o.afterPlay; //trigger some actions after playing a move + this.containerId = o.element; + this.isDiagram = o.diagram; + this.marks = o.marks; - // Fen string fully describes the game state + // Initializations if (!o.fen) o.fen = this.genRandInitFen(o.seed); this.re_initFromFen(o.fen); - - // Graphical (can use variables defined above) - this.containerId = o.element; - this.isDiagram = o.diagram; - this.marks = o.marks; this.graphicalInit(); } diff --git a/variants/Alapo/class.js b/variants/Alapo/class.js index 4836413..1c7c048 100644 --- a/variants/Alapo/class.js +++ b/variants/Alapo/class.js @@ -28,64 +28,58 @@ export default class AlapoRules extends ChessRules { return board; } - genRandInitFen(seed) { + genRandInitBaseFen() { + let fen = ""; if (this.options["randomness"] == 0) - return "rbqqbr/tcssct/6/6/TCSSCT/RBQQBR w 0"; - - Random.setSeed(seed); - - const piece2pawn = { - r: 't', - q: 's', - b: 'c' - }; - - let pieces = { w: new Array(6), b: new Array(6) }; - // Shuffle pieces on first (and last rank if randomness == 2) - for (let c of ["w", "b"]) { - if (c == 'b' && this.options["randomness"] == 1) { - pieces['b'] = pieces['w']; - break; + fen = "rbqqbr/tcssct/6/6/TCSSCT/RBQQBR w 0"; + else { + const piece2pawn = { + r: 't', + q: 's', + b: 'c' + }; + let pieces = { w: new Array(6), b: new Array(6) }; + // Shuffle pieces on first (and last rank if randomness == 2) + for (let c of ["w", "b"]) { + if (c == 'b' && this.options["randomness"] == 1) { + pieces['b'] = pieces['w']; + break; + } + let positions = ArrayFun.range(6); + // Get random squares for bishops + let randIndex = 2 * Random.randInt(3); + const bishop1Pos = positions[randIndex]; + let randIndex_tmp = 2 * Random.randInt(3) + 1; + const bishop2Pos = positions[randIndex_tmp]; + positions.splice(Math.max(randIndex, randIndex_tmp), 1); + positions.splice(Math.min(randIndex, randIndex_tmp), 1); + // Get random square for queens + randIndex = Random.randInt(4); + const queen1Pos = positions[randIndex]; + positions.splice(randIndex, 1); + randIndex = Random.randInt(3); + const queen2Pos = positions[randIndex]; + positions.splice(randIndex, 1); + // Rooks positions are now fixed, + const rook1Pos = positions[0]; + const rook2Pos = positions[1]; + pieces[c][rook1Pos] = "r"; + pieces[c][bishop1Pos] = "b"; + pieces[c][queen1Pos] = "q"; + pieces[c][queen2Pos] = "q"; + pieces[c][bishop2Pos] = "b"; + pieces[c][rook2Pos] = "r"; } - - let positions = ArrayFun.range(6); - - // Get random squares for bishops - let randIndex = 2 * Random.randInt(3); - const bishop1Pos = positions[randIndex]; - let randIndex_tmp = 2 * Random.randInt(3) + 1; - const bishop2Pos = positions[randIndex_tmp]; - positions.splice(Math.max(randIndex, randIndex_tmp), 1); - positions.splice(Math.min(randIndex, randIndex_tmp), 1); - - // Get random square for queens - randIndex = Random.randInt(4); - const queen1Pos = positions[randIndex]; - positions.splice(randIndex, 1); - randIndex = Random.randInt(3); - const queen2Pos = positions[randIndex]; - positions.splice(randIndex, 1); - - // Rooks positions are now fixed, - const rook1Pos = positions[0]; - const rook2Pos = positions[1]; - - pieces[c][rook1Pos] = "r"; - pieces[c][bishop1Pos] = "b"; - pieces[c][queen1Pos] = "q"; - pieces[c][queen2Pos] = "q"; - pieces[c][bishop2Pos] = "b"; - pieces[c][rook2Pos] = "r"; + fen = ( + pieces["b"].join("") + "/" + + pieces["b"].map(p => piece2pawn[p]).join("") + + "/6/6/" + + pieces["w"].map(p => piece2pawn[p].toUpperCase()).join("") + "/" + + pieces["w"].join("").toUpperCase() + + " w 0" + ); } - - return ( - pieces["b"].join("") + "/" + - pieces["b"].map(p => piece2pawn[p]).join("") + - "/6/6/" + - pieces["w"].map(p => piece2pawn[p].toUpperCase()).join("") + "/" + - pieces["w"].join("").toUpperCase() + - " w 0" - ); + return { fen: fen, o: {} }; } // Triangles are rotated from opponent viewpoint (=> suffix "_inv") diff --git a/variants/Align4/class.js b/variants/Align4/class.js index f603c51..4c191db 100644 --- a/variants/Align4/class.js +++ b/variants/Align4/class.js @@ -24,13 +24,15 @@ export default class Align4Rules extends ChessRules { return false; } - genRandInitFen(seed) { - const baseFen = super.genRandInitFen(seed); - const fen = baseFen.replace("rnbqkbnr/pppppppp", "4k3/8"); - const fenParts = fen.split(" "); - let others = JSON.parse(fenParts[3]); - others["flags"] = others["flags"].substr(0, 2) + "88"; - return fenParts.slice(0, 3).join(" ") + " " + JSON.stringify(others); + genRandInitBaseFen() { + let baseFen = super.genRandInitBaseFen(); + return { fen: baseFen.fen.replace("rnbqkbnr/pppppppp", "4k3/8"), o: {} }; + } + + getPartFen(o) { + let parts = super.getPartFen(o); + parts["flags"] = parts["flags"].substr(0, 2) + "88"; + return parts; } initReserves() { diff --git a/variants/Ambiguous/class.js b/variants/Ambiguous/class.js index db24d0e..4311c4d 100644 --- a/variants/Ambiguous/class.js +++ b/variants/Ambiguous/class.js @@ -22,10 +22,10 @@ export default class AmbiguousRules extends ChessRules { this.subTurn = 1; } - genRandInitFen(seed) { + genRandInitBaseFen() { const options = Object.assign({mode: "suicide"}, this.options); const gr = new GiveawayRules({options: options, genFenOnly: true}); - return gr.genRandInitFen(seed); + return gr.genRandInitBaseFen(); } canStepOver(x, y) { @@ -57,15 +57,17 @@ export default class AmbiguousRules extends ChessRules { .map(m => { if (m.vanish.length == 1) m.appear[0].p = V.GOAL; - else + else { m.appear[0].p = V.TARGET_CODE[m.vanish[1].p]; + m.appear[0].c = m.vanish[1].c; + } m.vanish.shift(); return m; }) ); } // At subTurn == 1, play a targeted move for the opponent. - // Search for target (we could also have it in a stack...) + // Search for target: let target = {x: -1, y: -1}; outerLoop: for (let i = 0; i < this.size.x; i++) { for (let j = 0; j < this.size.y; j++) { @@ -142,8 +144,10 @@ export default class AmbiguousRules extends ChessRules { return moves; } - isKing(symbol) { - return ['k', 'l'].includes(symbol); + isKing(x, y, p) { + if (!p) + p = this.getPiece(x, y); + return ['k', 'l'].includes(p); } getCurrentScore() { diff --git a/variants/Antiking1/class.js b/variants/Antiking1/class.js index 9b8af78..9d981e8 100644 --- a/variants/Antiking1/class.js +++ b/variants/Antiking1/class.js @@ -28,20 +28,20 @@ export default class Antiking1Rules extends AbstractAntikingRules { return res; } - genRandInitFen() { + genRandInitBaseFen() { // Always deterministic setup - return ( - '2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2 w 0 ' + - '{"flags":"KAka"}' - ); + return { + fen: "2prbkqA/2p1nnbr/2pppppp/8/8/PPPPPP2/RBNN1P2/aQKBRP2 w 0", + o: {"flags": "KAka"} + }; } // (Anti)King flags at 1 (true) if they can knight-jump - setFlags(fenFlags) { + setFlags(fenflags) { this.kingFlags = { w: {}, b: {} }; - for (let i=0; i Object.keys(this.kingFlags[c])) + ['w', 'b'].map(c => { + const res = Object.keys(this.kingFlags[c]).join(""); + return (c == 'w' ? res.toUpperCase() : res); + }) ).join("") ); } diff --git a/variants/Antiking2/class.js b/variants/Antiking2/class.js index ee1030a..55c9229 100644 --- a/variants/Antiking2/class.js +++ b/variants/Antiking2/class.js @@ -11,8 +11,8 @@ export default class Antiking2Rules extends AbstractAntikingRules { }; } - genRandInitFen(seed) { - const baseFen = super.genRandInitFen(seed); + genRandInitBaseFen() { + const baseFen = super.genRandInitBaseFen(); // Just add an antiking on 3rd ranks let akPos = [3, 3]; if (this.options.randomness >= 1) { @@ -31,10 +31,23 @@ export default class Antiking2Rules extends AbstractAntikingRules { : "") ); }; - return ( - baseFen.replace("p/8", "p/" + antikingLine('b')) - .replace("8/P", antikingLine('w') + "/P") - ); + return { + fen: baseFen.fen.replace("p/8", "p/" + antikingLine('b')) + .replace("8/P", antikingLine('w') + "/P"), + o: baseFen.o + }; + } + + getCastleMoves([x, y]) { + if (this.getPiece(x, y) == 'a') + return []; + return super.getCastleMoves([x, y]); + } + + updateCastleFlags(move) { + if (move.vanish.length > 0 && move.vanish[0].p == 'a') + return; + super.updateCastleFlags(move); } }; diff --git a/variants/Apocalypse/class.js b/variants/Apocalypse/class.js new file mode 100644 index 0000000..e91cf81 --- /dev/null +++ b/variants/Apocalypse/class.js @@ -0,0 +1,199 @@ +import ChessRules from "/base_rules.js"; + +export class ApocalypseRules extends ChessRules { + + static get Options() { + return {}; + } + + get pawnPromotions() { + return ['n', 'p']; + } + + get size() { + return {x: 5, y: 5}; + } + + genRandInitBaseFen() { + return { + fen: "npppn/p3p/5/P3P/NPPPN w 0", + o: {"flags":"00"} + }; + } + + getPartFen(o) { + let parts = super.getPartFen(o); + parts["whiteMove"] = this.whiteMove || "-"; + return parts; + } + + getFlagsFen() { + return ( + this.penaltyFlags['w'].toString() + this.penaltyFlags['b'].toString() + ); + } + + setOtherVariables(fen) { + const parsedFen = V.ParseFen(fen); + this.setFlags(parsedFen.flags); + // Also init whiteMove + this.whiteMove = + parsedFen.whiteMove != "-" + ? JSON.parse(parsedFen.whiteMove) + : null; + } + + setFlags(fenflags) { + this.penaltyFlags = { + 'w': parseInt(fenflags[0], 10), + 'b': parseInt(fenflags[1], 10) + }; + } + + // TODO: could be a stack of 2 (pawn promote + relocation) + getWhitemoveFen() { + if (!this.whiteMove) return "-"; + return JSON.stringify({ + start: this.whiteMove.start, + end: this.whiteMove.end, + appear: this.whiteMove.appear, + vanish: this.whiteMove.vanish + }); + } + + // allow pawns to move diagonally and capture vertically --> only purpose illegal + // allow capture self --> same purpose + // ---> MARK such moves : move.illegal = true + + // simpler: allow all moves, including "capturing nothing" + // flag every pawn capture as "illegal" (potentially) + + pieces(color, x, y) { + const pawnShift = (color == "w" ? -1 : 1); + return { + 'p': { + "class": "pawn", + moves: [ + { + steps: [[pawnShift, 0], [pawnShift, -1], [pawnShift, 1]], + range: 1 + } + ], + }, + 'n': super.pieces(color, x, y)['n'] + }; + } + + canTake() { + return true; + } + + getPotentialMovesFrom([x, y]) { + let moves = []; + if (this.subTurn == 2) { + const start = this.moveStack[0].end; + if (x == start.x && y == start.y) { + // Move the pawn to any empty square not on last rank (== x) + for (let i=0; i { + if ( + // Self-capture test: + (m.vanish.length == 2 && m.vanish[1].c == m.vanish[0].c) || + // Pawn going diagonaly to empty square, or vertically to occupied + ( + m.vanish[0].p == 'p' && + ( + (m.end.y == m.start.y && m.vanish.length == 2) || + (m.end.y != m.start.y && m.vanish.length == 1) + ) + ) + ) { + m.illegal = true; + } + }); + } + return moves; + } + + filterValid(moves) { + // No checks: + return moves; + } + + // White and black (partial) moves were played: merge + // + animate both at the same time ! + resolveSynchroneMove(move) { + // TODO + } + + play(move) { + // Do not play on board (would reveal the move...) + this.turn = V.GetOppCol(this.turn); + this.movesCount++; + this.postPlay(move); + } + + postPlay(move) { + if (pawn promotion into pawn) { + this.curMove move; //TODO: animate both move at same time + effects AFTER ! + this.subTurn = 2 + } + else if (this.turn == 'b') + // NOTE: whiteMove is used read-only, so no need to copy + this.whiteMove = move; + } + else { + // A full turn just ended: + const smove = this.resolveSynchroneMove(move); + V.PlayOnBoard(this.board, smove); //----> ici : animate both ! + this.whiteMove = null; + } + } + + atLeastOneLegalMove(...) { + // TODO + } + + getCurrentScore() { + if (this.turn == 'b') + // Turn (white + black) not over yet. Could be stalemate if black cannot move (legally) + // TODO: check. If so, return "1/2". + return "*"; + // Count footmen: if a side has none, it loses + let fmCount = { 'w': 0, 'b': 0 }; + for (let i=0; i<5; i++) { + for (let j=0; j<5; j++) { + if (this.board[i][j] != V.EMPTY && this.getPiece(i, j) == V.PAWN) + fmCount[this.getColor(i, j)]++; + } + } + if (Object.values(fmCount).some(v => v == 0)) { + if (fmCount['w'] == 0 && fmCount['b'] == 0) + // Everyone died + return "1/2"; + if (fmCount['w'] == 0) return "0-1"; + return "1-0"; //fmCount['b'] == 0 + } + // Check penaltyFlags: if a side has 2 or more, it loses + if (Object.values(this.penaltyFlags).every(v => v == 2)) return "1/2"; + if (this.penaltyFlags['w'] == 2) return "0-1"; + if (this.penaltyFlags['b'] == 2) return "1-0"; + if (!this.atLeastOneLegalMove('w') || !this.atLeastOneLegalMove('b')) + // Stalemate (should be very rare) + return "1/2"; + return "*"; + } + +}; diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js index 02dd16e..815be74 100644 --- a/variants/Chakart/class.js +++ b/variants/Chakart/class.js @@ -131,15 +131,12 @@ export default class ChakartRules extends ChessRules { ); } - genRandInitFen(seed) { + genRandInitBaseFen() { const options = Object.assign({mode: "suicide"}, this.options); const gr = new GiveawayRules({options: options, genFenOnly: true}); - const baseFen = gr.genRandInitFen(seed); - const fenParts = baseFen.split(" "); - let others = JSON.parse(fenParts[3]); - delete others["enpassant"]; - others["flags"] = "1111"; //Peach + Mario flags - return fenParts.slice(0, 3).join(" ") + " " + JSON.stringify(others); + let res = gr.genRandInitBaseFen(); + res.o["flags"] = "1111"; //Peach + Mario flags + return res; } fen2board(f) { diff --git a/variants/Giveaway/class.js b/variants/Giveaway/class.js index 69addf2..15a8998 100644 --- a/variants/Giveaway/class.js +++ b/variants/Giveaway/class.js @@ -36,47 +36,44 @@ export default class GiveawayRules extends ChessRules { return res; } - genRandInitFen(seed) { + genRandInitBaseFen() { if (this.options["mode"] == "losers") - return super.genRandInitFen(seed); + return super.genRandInitBaseFen(); - if (this.options["randomness"] == 0) { - return ( - 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 {"enpassant":"-"}' - ); - } - - Random.setSeed(seed); - let pieces = { w: new Array(8), b: new Array(8) }; - for (let c of ["w", "b"]) { - if (c == 'b' && this.options["randomness"] == 1) { - pieces['b'] = pieces['w']; - break; - } - - // Get random squares for every piece, totally freely - let positions = Random.shuffle(ArrayFun.range(8)); - const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q']; - const rem2 = positions[0] % 2; - if (rem2 == positions[1] % 2) { - // Fix bishops (on different colors) - for (let i=2; i<8; i++) { - if (positions[i] % 2 != rem2) { - [positions[1], positions[i]] = [positions[i], positions[1]]; - break; + let fen = ""; + if (this.options["randomness"] == 0) + fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0"; + else { + let pieces = { w: new Array(8), b: new Array(8) }; + for (let c of ["w", "b"]) { + if (c == 'b' && this.options["randomness"] == 1) { + pieces['b'] = pieces['w']; + break; + } + // Get random squares for every piece, totally freely + let positions = Random.shuffle(ArrayFun.range(8)); + const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q']; + const rem2 = positions[0] % 2; + if (rem2 == positions[1] % 2) { + // Fix bishops (on different colors) + for (let i=2; i<8; i++) { + if (positions[i] % 2 != rem2) { + [positions[1], positions[i]] = [positions[i], positions[1]]; + break; + } } } + for (let i = 0; i < 8; i++) + pieces[c][positions[i]] = composition[i]; } - for (let i = 0; i < 8; i++) - pieces[c][positions[i]] = composition[i]; + fen = ( + pieces["b"].join("") + + "/pppppppp/8/8/8/8/PPPPPPPP/" + + pieces["w"].join("").toUpperCase() + + " w 0" + ); } - return ( - pieces["b"].join("") + - "/pppppppp/8/8/8/8/PPPPPPPP/" + - pieces["w"].join("").toUpperCase() + - // En-passant allowed, but no flags - ' w 0 {"enpassant":"-"}' - ); + return { fen: fen, o: {} }; } constructor(o) { @@ -91,7 +88,8 @@ export default class GiveawayRules extends ChessRules { } getCurrentScore() { - if (this.atLeastOneMove()) return "*"; + if (this.atLeastOneMove(this.turn)) + return "*"; // No valid move: the side who cannot move wins return (this.turn == "w" ? "1-0" : "0-1"); } diff --git a/variants/Hex/class.js b/variants/Hex/class.js index 80d4501..0797c91 100644 --- a/variants/Hex/class.js +++ b/variants/Hex/class.js @@ -74,10 +74,13 @@ export default class HexRules extends ChessRules { return res; } - genRandInitFen() { + genRandInitBaseFen() { // NOTE: size.x == size.y (square boards) const emptyCount = C.FenEmptySquares(this.size.x); - return (emptyCount + "/").repeat(this.size.x).slice(0, -1) + " w 0"; + return { + fen: (emptyCount + "/").repeat(this.size.x).slice(0, -1) + " w 0", + o: {} + }; } getSvgChessboard() { @@ -166,7 +169,7 @@ export default class HexRules extends ChessRules { get size() { const baseRatio = 1.6191907514450865; //2801.2 / 1730, "widescreen" const rc = - document.getElementById(this.containerid).getBoundingClientRect(); + document.getElementById(this.containerId).getBoundingClientRect(); const rotate = rc.width < rc.height; //"vertical screen" return { x: this.options["bsize"], diff --git a/variants/Refusal/class.js b/variants/Refusal/class.js index 2cd9ca6..04301d7 100644 --- a/variants/Refusal/class.js +++ b/variants/Refusal/class.js @@ -21,14 +21,10 @@ export default class RefusalRules extends ChessRules { return false; } - genRandInitFen(seed) { - return super.genRandInitFen(seed).slice(0, -1) + ',"lastmove":"null"}'; - } - - getFen() { - return ( - super.getFen().slice(0, -1) + ',"lastmove":"' + - JSON.stringify(this.lastMove) + '"}'); + getPartFen(o) { + let parts = super.getPartFen(o); + parts["lastmove"] = o.init ? null : this.lastMove; + return parts; } setOtherVariables(fenParsed) { @@ -119,10 +115,10 @@ export default class RefusalRules extends ChessRules { super.postPlay(move); } - atLeastOneMove() { + atLeastOneMove(color) { if (!this.lastMove.noRef) return true; - return super.atLeastOneMove(); + return super.atLeastOneMove(color); } }; diff --git a/variants/Suction/class.js b/variants/Suction/class.js index cf5ae16..889a37f 100644 --- a/variants/Suction/class.js +++ b/variants/Suction/class.js @@ -41,22 +41,19 @@ export default class SuctionRules extends ChessRules { } } - genRandInitFen(seed) { + genRandInitBaseFen() { const options = Object.assign({mode: "suicide"}, this.options); const gr = new GiveawayRules({options: options, genFenOnly: true}); - const baseFen = gr.genRandInitFen(seed); - // Add empty cmove: - const fenParts = baseFen.split(" "); - let others = JSON.parse(fenParts[3]); - others["cmove"] = "-"; - return fenParts.slice(0, 3).join(" ") + " " + JSON.stringify(others); + return gr.genRandInitBaseFen(); } - getFen() { - const cmoveFen = !this.cmove + getPartFen(o) { + let parts = super.getPartFen(o); + const cmoveFen = o.init || !this.cmove ? "-" : C.CoordsToSquare(this.cmove.start) + C.CoordsToSquare(this.cmove.end); - return super.getFen().slice(0, -1) + ',"cmove":"' + cmoveFen + '"}'; + parts["cmove"] = cmoveFen; + return parts; } getBasicMove([sx, sy], [ex, ey]) { diff --git a/variants/_Antiking/class.js b/variants/_Antiking/class.js index 8e2173a..941bd82 100644 --- a/variants/_Antiking/class.js +++ b/variants/_Antiking/class.js @@ -55,9 +55,11 @@ export default class AbstractAntikingRules extends ChessRules { m.vanish.length == 1 || m.vanish[1].p != 'a'); } - underCheck(squares, color) { + underCheck(square_s, color) { let res = false; - squares.forEach(sq => { + if (!Array.isArray(square_s[0])) + square_s = [square_s]; + square_s.forEach(sq => { switch (this.getPiece(sq[0], sq[1])) { case 'k': res ||= super.underAttack(sq, color); -- 2.44.0