From: Benjamin Auder Date: Mon, 25 Jul 2022 17:02:57 +0000 (+0200) Subject: Untested draft refactor both/moves/attack for pieces specs X-Git-Url: https://git.auder.net/images/assets/doc/html/%7B%7B%20pkg.url%20%7D%7D?a=commitdiff_plain;h=33b427488bb6ee5c505c3a024bccedbef763f80e;p=xogo.git Untested draft refactor both/moves/attack for pieces specs --- diff --git a/base_rules.js b/base_rules.js index 4ee389f..5b1a013 100644 --- a/base_rules.js +++ b/base_rules.js @@ -1241,13 +1241,13 @@ export default class ChessRules { }, 'r': { "class": "rook", - moves: [ + both: [ {steps: [[0, 1], [0, -1], [1, 0], [-1, 0]]} ] }, 'n': { "class": "knight", - moves: [ + both: [ { steps: [ [1, 2], [1, -2], [-1, 2], [-1, -2], @@ -1259,13 +1259,13 @@ export default class ChessRules { }, 'b': { "class": "bishop", - moves: [ + both: [ {steps: [[1, 1], [1, -1], [-1, 1], [-1, -1]]} ] }, 'q': { "class": "queen", - moves: [ + both: [ { steps: [ [0, 1], [0, -1], [1, 0], [-1, 0], @@ -1276,7 +1276,7 @@ export default class ChessRules { }, 'k': { "class": "king", - moves: [ + both: [ { steps: [ [0, 1], [0, -1], [1, 0], [-1, 0], @@ -1341,12 +1341,19 @@ export default class ChessRules { getStepSpec(color, x, y, piece) { let pieceType = piece; - const allSpecs = this.pieces(color, x, y); + let allSpecs = this.pieces(color, x, y); if (!piece) pieceType = this.getPieceType(x, y); else if (allSpecs[piece].moveas) pieceType = allSpecs[piece].moveas; - return allSpecs[pieceType]; + let res = allSpecs[pieceType]; + if (!res["both"]) + res.both = []; + if (!res["moves"]) + res.moves = []; + if (!res["attack"]) + res.attack = []; + return res; } // Can thing on square1 capture thing on square2? @@ -1380,7 +1387,7 @@ export default class ChessRules { const oppCol = C.GetOppCol(color); const piece = this.getPieceType(x, y); const stepSpec = this.getStepSpec(color, x, y, piece); - const attacks = stepSpec.attack || stepSpec.moves; + const attacks = stepSpec.both.concat(stepSpec.attack); for (let a of attacks) { outerLoop: for (let step of a.steps) { let [i, j] = [x + step[0], y + step[1]]; @@ -1779,7 +1786,7 @@ export default class ChessRules { elt.segments = this.getSegments(segments, segStart, end); res.push(elt); }; - const exploreSteps = (stepArray) => { + const exploreSteps = (stepArray, mode) => { for (let s of stepArray) { outerLoop: for (let step of s.steps) { if (o.segments) { @@ -1798,9 +1805,9 @@ export default class ChessRules { !o.captureTarget || (o.captureTarget[0] == i && o.captureTarget[1] == j) ) { - if (o.one && !o.attackOnly) + if (o.one && mode != "attack") return true; - if (!o.attackOnly) + if (mode != "attack") addSquare(!o.captureTarget ? [i, j] : [x, y]); if (o.captureTarget) return res[0]; @@ -1823,9 +1830,9 @@ export default class ChessRules { if (!explored[i + "." + j]) { explored[i + "." + j] = true; if (allowed([x, y], [i, j])) { - if (o.one && !o.moveOnly) + if (o.one && mode != "moves") return true; - if (!o.moveOnly) + if (mode != "moves") addSquare(!o.captureTarget ? [i, j] : [x, y]); if ( o.captureTarget && @@ -1840,17 +1847,15 @@ export default class ChessRules { return undefined; //default, but let's explicit it }; if (o.captureTarget) - return exploreSteps(o.captureSteps) + return exploreSteps(o.captureSteps, "attack"); else { const stepSpec = o.stepSpec || this.getStepSpec(this.getColor(x, y), x, y); let outOne = false; - if (!o.attackOnly || !stepSpec.attack) - outOne = exploreSteps(stepSpec.moves); - if (!outOne && !o.moveOnly && !!stepSpec.attack) { - o.attackOnly = true; //ok because o is always a temporary object - outOne = exploreSteps(stepSpec.attack); - } + if (!o.attackOnly) + outOne = exploreSteps(stepSpec.both.concat(stepSpec.moves), "moves"); + if (!outOne && !o.moveOnly) + outOne = exploreSteps(stepSpec.both.concat(stepSpec.attack), "attack"); return (o.one ? outOne : res); } } @@ -1873,7 +1878,7 @@ export default class ChessRules { if (this.canStepOver(x, y, apparentPiece)) continue; const stepSpec = this.getStepSpec(colIJ, i, j); - const attacks = stepSpec.attack || stepSpec.moves; + const attacks = stepSpec.attack.concat(stepSpec.both); for (let a of attacks) { for (let s of a.steps) { // Quick check: if step isn't compatible, don't even try diff --git a/utils/alea.js b/utils/alea.js index 751d347..077970d 100644 --- a/utils/alea.js +++ b/utils/alea.js @@ -22,7 +22,7 @@ export const Random = { min = 0; } if (!Random.rand) - Random.setSeed(Math.floor(Math.random() * 1984)); + Random.setSeed(Math.floor(Math.random() * 19840)); return Math.floor(Random.rand() * (max - min)) + min; }, diff --git a/variants.js b/variants.js index 7c51fcd..ffe77ac 100644 --- a/variants.js +++ b/variants.js @@ -14,14 +14,14 @@ const variants = [ {name: 'Atomic', desc: 'Explosive captures'}, {name: 'Avalam', desc: 'Build towers'}, {name: 'Avalanche', desc: 'Pawnfalls'}, -// {name: 'Balaklava', desc: 'Meet the Mammoth'}, + {name: 'Balaklava', desc: 'Meet the Mammoth'}, {name: 'Bario', desc: 'A quantum story'}, {name: "Balanced", desc: "balanced chess"}, {name: 'Baroque', desc: 'Exotic captures'}, {name: "Benedict", desc: "Change colors"}, {name: 'Berolina', desc: 'Pawns move diagonally'}, -// {name: 'Bicolour', desc: 'Harassed kings'}, -// {name: 'Brotherhood', desc: 'Friendly pieces'}, + {name: 'Bicolour', desc: 'Harassed kings'}, + {name: 'Brotherhood', desc: 'Friendly pieces'}, {name: 'Cannibal', desc: 'Capture powers'}, // {name: 'Capablanca', desc: 'Capablanca Chess', disp: 'Capablanca Chess'}, {name: 'Capture', desc: 'Mandatory captures'}, diff --git a/variants/Absorption/class.js b/variants/Absorption/class.js index 66278db..5f58c32 100644 --- a/variants/Absorption/class.js +++ b/variants/Absorption/class.js @@ -26,7 +26,7 @@ export default class AbsorptionRules extends ChessRules { // amazon 'a': { "class": "amazon", - moves: [ + both: [ { steps: [ [0, 1], [0, -1], [1, 0], [-1, 0], @@ -45,7 +45,7 @@ export default class AbsorptionRules extends ChessRules { // empress 'e': { "class": "empress", - moves: [ + both: [ { steps: [ [1, 0], [-1, 0], [0, 1], [0, -1] @@ -63,7 +63,7 @@ export default class AbsorptionRules extends ChessRules { // princess 's': { "class": "princess", - moves: [ + both: [ { steps: [ [1, 1], [1, -1], [-1, 1], [-1, -1] diff --git a/variants/Alapo/class.js b/variants/Alapo/class.js index 199a169..e613140 100644 --- a/variants/Alapo/class.js +++ b/variants/Alapo/class.js @@ -91,7 +91,7 @@ export default class AlapoRules extends ChessRules { {"class": "bishop" + (this.playerColor != color ? "_inv" : "")}), 's': { //"square" "class": "babyrook", - moves: [ + both: [ { steps: [[0, 1], [0, -1], [1, 0], [-1, 0]], range: 1 @@ -100,7 +100,7 @@ export default class AlapoRules extends ChessRules { }, 'c': { //"circle" "class": "babyqueen", - moves: [ + both: [ { steps: [ [0, 1], [0, -1], [1, 0], [-1, 0], @@ -112,7 +112,7 @@ export default class AlapoRules extends ChessRules { }, 't': { //"triangle" "class": "babybishop" + (this.playerColor != color ? "_inv" : ""), - moves: [ + both: [ { steps: [[1, 1], [1, -1], [-1, 1], [-1, -1]], range: 1 diff --git a/variants/Ambiguous/class.js b/variants/Ambiguous/class.js index 4311c4d..6d9ac7e 100644 --- a/variants/Ambiguous/class.js +++ b/variants/Ambiguous/class.js @@ -131,7 +131,7 @@ export default class AmbiguousRules extends ChessRules { 't': {"class": "target-queen", moves: []}, 'l': {"class": "target-king", moves: []} }; - return Object.assign({ 'g': {"class": "target", moves: []} }, + return Object.assign({ 'g': {"class": "target"} }, targets, super.pieces(color, x, y)); } diff --git a/variants/Antiking1/class.js b/variants/Antiking1/class.js index 4ef66f2..d536680 100644 --- a/variants/Antiking1/class.js +++ b/variants/Antiking1/class.js @@ -1,5 +1,6 @@ import ChessRules from "/base_rules.js"; import AbstractAntikingRules from "/variants/_Antiking/class.js"; +import BerolinaPawnSpec from "/variants/_Berolina/pawnSpec.js"; export default class Antiking1Rules extends AbstractAntikingRules { @@ -11,20 +12,8 @@ export default class Antiking1Rules extends AbstractAntikingRules { } 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 - } - ]; + res['p'] = BerolinaPawnSpec(color); return res; } diff --git a/variants/Arena/class.js b/variants/Arena/class.js index 801469a..5a53086 100644 --- a/variants/Arena/class.js +++ b/variants/Arena/class.js @@ -28,8 +28,8 @@ export default class ArenaRules extends ChessRules { const pawnShift = (color == "w" ? -1 : 1); Array.prototype.push.apply(pawnSpec.attack[0].steps, [[-pawnShift, 1], [-pawnShift, -1]]); - queenSpec.moves[0].range = 3; - kingSpec.moves[0].range = 3; + queenSpec.both[0].range = 3; + kingSpec.both[0].range = 3; return Object.assign({}, allSpecs, { diff --git a/variants/Atomic/class.js b/variants/Atomic/class.js index 5c73de2..4bd2712 100644 --- a/variants/Atomic/class.js +++ b/variants/Atomic/class.js @@ -45,8 +45,8 @@ export default class AtomicRules extends ChessRules { c: c }) ], - start: { x: x, y: y }, - end: { x: x, y: y } + start: {x: x, y: y}, + end: {x: x, y: y} }) ]; } diff --git a/variants/Avalam/class.js b/variants/Avalam/class.js index 95993e0..aaa2cf3 100644 --- a/variants/Avalam/class.js +++ b/variants/Avalam/class.js @@ -42,7 +42,7 @@ export default class AvalamRules extends ChessRules { return { 'b': { "class": "stack", - moves: [{steps: steps, range: 1}] + both: [{steps: steps, range: 1}] }, 'c': { "class": "stack2", diff --git a/variants/Balaklava/class.js b/variants/Balaklava/class.js new file mode 100644 index 0000000..dd3cf62 --- /dev/null +++ b/variants/Balaklava/class.js @@ -0,0 +1,59 @@ +import ChessRules from "/base_rules.js"; + +export default class BalaklavaRules extends ChessRules { + + get pawnPromotions() { + return ['r', 'm', 'b', 'q']; + } + + get hasEnpassant() { + return false; + } + + pieces(color, x, y) { + let res = super.pieces(color, x, y); + const knightSpec = res['n']; + delete res['n']; + res['m'] = { + "class": "mammoth", + both: [ + { + steps: [ + [-2, -2], [-2, 0], [-2, 2], + [0, -2], [0, 2], [2, -2], + [2, 0], [2, 2] + ], + range: 1 + } + ] + }; + ['r', 'b', 'm', 'q'].forEach(p => res[p].moves = knightSpec.moves); + return res; + } + + genRandInitBaseFen() { + const baseFen = super.genRandInitBaseFen(); + return { + fen: baseFen.replace(/n/g, 'm').replace(/N/g, 'M'), + o: baseFen.o + }; + } + + pawnPostProcess(moves) { + if (moves.length == 0) + return []; + const color = moves[0].vanish[0].c; + const lastRank = (color == 'w' ? 0 : this.size.x - 1); + const noKnightPromotions = moves.filter(m => { + return ( + m.end.x != lastRank || + ( + Math.abs(m.start.x - m.end.x) <= 1 && + Math.abs(m.start.y - m.end.y) <= 1 + ) + ); + }); + return super.pawnPostProcess(noKnightPromotions); + } + +}; diff --git a/variants/Balaklava/rules.html b/variants/Balaklava/rules.html new file mode 100644 index 0000000..c65158e --- /dev/null +++ b/variants/Balaklava/rules.html @@ -0,0 +1 @@ +

TODO

diff --git a/variants/Balaklava/style.css b/variants/Balaklava/style.css new file mode 100644 index 0000000..a3550bc --- /dev/null +++ b/variants/Balaklava/style.css @@ -0,0 +1 @@ +@import url("/base_pieces.css"); diff --git a/variants/Bario/class.js b/variants/Bario/class.js index 01cb3d7..1874383 100644 --- a/variants/Bario/class.js +++ b/variants/Bario/class.js @@ -26,12 +26,7 @@ export default class BarioRules extends ChessRules { pieces(color, x, y) { return Object.assign( - { - 'u': { - "class": "undefined", - moves: [] - } - }, + { 'u': {"class": "undefined"} }, super.pieces(color, x, y) ); } @@ -165,8 +160,10 @@ export default class BarioRules extends ChessRules { if (super.underCheck(square_s, oppCol)) return true; // Check potential specializations of undefined using reserve: + const inReserve = Object.keys(this.reserve[oppCol]) + .filter(k => this.reserve[oppCol][k] >= 1); const allAttacks = Array.prototype.concat.apply( - ['r', 'n', 'b', 'q'].map(p => this.pieces()[p].moves[0])); + inReserve.map(p => this.pieces()[p].both[0])); const [x, y] = square_s[0]; for (let i=0; i { - this.g_pieces[f.x][f.y].classList.toggle("white"); - this.g_pieces[f.x][f.y].classList.toggle("black"); - }); - } - }; diff --git a/variants/Berolina/class.js b/variants/Berolina/class.js index beabdc6..ef0ba89 100644 --- a/variants/Berolina/class.js +++ b/variants/Berolina/class.js @@ -1,3 +1,12 @@ -import AbstractBerolinaRules from "/variants/_Berolina/class.js"; +import ChessRules from "/base_rules.js"; +import BerolinaPawnSpec from "/variants/_Berolina/pawnSpec.js"; -export default class BerolinaRules extends AbstractBerolinaRules {}; +export default class BerolinaRules extends ChessRules { + + pieces(color, x, y) { + let res = super.pieces(color, x, y); + res['p'] = BerolinaPawnSpec(color); + return res; + } + +}; diff --git a/variants/Bicolour/class.js b/variants/Bicolour/class.js new file mode 100644 index 0000000..fd2cdb0 --- /dev/null +++ b/variants/Bicolour/class.js @@ -0,0 +1,111 @@ +import ChessRules from "/base_rules.js"; +import {Random} from "/utils/alea.js"; +import {ArrayFun} from "/utils/array.js"; + +export class BicolourRules extends ChessRules { + + // TODO: Options + + get hasFlags() { + return false; + } + + canTake([x1, y1], [x2, y2]) { + return (this.getPiece(x2, y2) == 'k' || super.canTake([x1, y1], [x2, y2])); + } + + genRandInitBaseFen() { + if (this.options["randomness"] == 0) + return { fen: "rqbnkbnr/pppppppp/8/8/8/8/PPPPPPPP/RQBNKBNR", o: {} }; + + // Place pieces at random but the king cannot be next to a rook or queen. + // => One bishop and one knight should surround the king. + let pieces = {w: new Array(8), b: new Array(8)}; + let flags = ""; + for (let c of ["w", "b"]) { + if (c == 'b' && this.options["randomness"] == 1) { + pieces['b'] = pieces['w']; + break; + } + let positions = ArrayFun.range(8); + const kingPos = randInt(8); + let toRemove = [kingPos]; + let knight1Pos = undefined; + let bishop1Pos = undefined; + if (kingPos == 0) { + if (Random.randBool()) + knight1Pos = 1; + else + bishop1Pos = 1; + toRemove.push(1); + } + else if (kingPos == V.size.y - 1) { + if (Random.randBool()) + knight1Pos = V.size.y - 2; + else + bishop1Pos = V.size.y - 2; + toRemove.push(V.size.y - 2); + } + else { + knight1Pos = kingPos + (Random.randBool() ? 1 : -1); + bishop1Pos = kingPos + (knight1Pos < kingPos ? 1 : -1); + toRemove.push(knight1Pos, bishop1Pos); + } + const firstPieces = [kingPos, knight1Pos, bishop1Pos] + .filter(elt => elt !== undefined); + firstPieces + .sort((a, b) => b - a) + .forEach(elt => positions.splice(elt, 1)); + let randIndex = undefined; + if (bishop1Pos === undefined) { + const posWithIdx = positions.map((e,i) => { return {e: e, i: i}; }); + let availableSquares = posWithIdx.filter(p => p.e % 2 == 0); + randIndex = randInt(availableSquares.length); + bishop1Pos = availableSquares[randIndex].e; + positions.splice(availableSquares[randIndex].i, 1); + } + const posWithIdx = positions.map((e,i) => { return {e: e, i: i}; }); + const rem1B = bishop1Pos % 2; + let availableSquares = posWithIdx.filter(p => p.e % 2 == 1 - rem1B); + randIndex = randInt(availableSquares.length); + const bishop2Pos = availableSquares[randIndex].e; + positions.splice(availableSquares[randIndex].i, 1); + if (knight1Pos === undefined) { + randIndex = randInt(5); + knight1Pos = positions[randIndex]; + positions.splice(randIndex, 1); + } + randIndex = randInt(4); + const knight2Pos = positions[randIndex]; + positions.splice(randIndex, 1); + randIndex = randInt(3); + const queenPos = positions[randIndex]; + positions.splice(randIndex, 1); + const rook1Pos = positions[0]; + const rook2Pos = positions[1]; + pieces[c][rook1Pos] = "r"; + pieces[c][knight1Pos] = "n"; + pieces[c][bishop1Pos] = "b"; + pieces[c][queenPos] = "q"; + pieces[c][kingPos] = "k"; + pieces[c][bishop2Pos] = "b"; + pieces[c][knight2Pos] = "n"; + pieces[c][rook2Pos] = "r"; + } + + return { + fen: ( + pieces["b"].join("") + + "/pppppppp/8/8/8/8/PPPPPPPP/" + + pieces["w"].join("").toUpperCase() + ), + o: {} + }; + } + + underCheck(color) { + const kingPos = this.searchKingPos(color)[0], + return (this.underAttack(kingPos, 'w') || this.underAttack(kingPos, 'b')); + } + +}; diff --git a/variants/Bicolour/rules.html b/variants/Bicolour/rules.html new file mode 100644 index 0000000..c65158e --- /dev/null +++ b/variants/Bicolour/rules.html @@ -0,0 +1 @@ +

TODO

diff --git a/variants/Bicolour/style.css b/variants/Bicolour/style.css new file mode 100644 index 0000000..a3550bc --- /dev/null +++ b/variants/Bicolour/style.css @@ -0,0 +1 @@ +@import url("/base_pieces.css"); diff --git a/variants/Brotherhood/class.js b/variants/Brotherhood/class.js new file mode 100644 index 0000000..94150c2 --- /dev/null +++ b/variants/Brotherhood/class.js @@ -0,0 +1,20 @@ +import ChessRules from "/base_rules.js"; + +export default class BrotherhoodRules extends ChessRules { + + canTake([x1, y1], [x2, y2]) { + if (!super.canTake([x1, y1], [x2, y2])) + return false; + const p1 = this.getPiece(x1, y1), + p2 = this.getPiece(x2, y2); + return (p1 != p2 || ['p', 'k'].some(symb => [p1, p2].includes(symb))); + } + + getCurrentScore() { + if (this.atLeastOneMove(this.turn)) + return "*"; + // Stalemate = loss + return (this.turn == 'w' ? "0-1" : "1-0"); + } + +}; diff --git a/variants/Brotherhood/rules.html b/variants/Brotherhood/rules.html new file mode 100644 index 0000000..b65fb61 --- /dev/null +++ b/variants/Brotherhood/rules.html @@ -0,0 +1,6 @@ +

+ Rooks, knights, bishops and queens cannot + capture enemy pieces of the same nature. +

+ +

Gianluca Vecchi (1993).

diff --git a/variants/Brotherhood/style.css b/variants/Brotherhood/style.css new file mode 100644 index 0000000..a3550bc --- /dev/null +++ b/variants/Brotherhood/style.css @@ -0,0 +1 @@ +@import url("/base_pieces.css"); diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js index 5202b48..96a53f3 100644 --- a/variants/Chakart/class.js +++ b/variants/Chakart/class.js @@ -116,7 +116,6 @@ export default class ChakartRules extends ChessRules { { 'y': { // Virtual piece for "king remote shell captures" - moves: [], attack: [ { steps: [ diff --git a/variants/_Berolina/class.js b/variants/_Berolina/class.js deleted file mode 100644 index 6066e51..0000000 --- a/variants/_Berolina/class.js +++ /dev/null @@ -1,23 +0,0 @@ -import ChessRules from "/base_rules.js"; - -export default class BerolinaRules extends ChessRules { - - 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; - } - -}; diff --git a/variants/_Berolina/pawnSpec.js b/variants/_Berolina/pawnSpec.js new file mode 100644 index 0000000..088e7df --- /dev/null +++ b/variants/_Berolina/pawnSpec.js @@ -0,0 +1,19 @@ +export default BerolinaPawnSpec = (color) => { + + const pawnShift = (color == "w" ? -1 : 1); + return { + moves: [ + { + steps: [[pawnShift, 1], [pawnShift, -1]], + range: 1 + } + ], + attack: [ + { + steps: [[pawnShift, 0]], + range: 1 + } + ] + }; + +}; diff --git a/variants/_Flip/class.js b/variants/_Flip/class.js new file mode 100644 index 0000000..e2e2bf5 --- /dev/null +++ b/variants/_Flip/class.js @@ -0,0 +1,43 @@ +import ChessRules from "/base_rules.js"; + +export default class AbstractFlipRules extends ChessRules { + + // For games without captures (replaced by flips) + get hasEnpassant() { + return false; + } + canTake() { + return false; + } + filterValid(moves) { + return moves; + } + underCheck() { + return false; + } + + playOnBoard(move) { + super.playOnBoard(move); + this.flipColorOf(move.flips); + } + undoOnBoard(move) { + super.undoOnBoard(move); + this.flipColorOf(move.flips); + } + + flipColorOf(flips) { + for (let xy of flips) { + const newColor = C.GetOppCol(this.getColor(xy.x, xy.y)); + this.board[xy.x][xy.y] = newColor + this.board[xy.x][xy.y][1]; + } + } + + playVisual(move, r) { + super.playVisual(move, r); + move.flips.forEach(f => { + this.g_pieces[f.x][f.y].classList.toggle("white"); + this.g_pieces[f.x][f.y].classList.toggle("black"); + }); + } + +};