From 554e3ad3773a3123701bd894db1df4c1843283b8 Mon Sep 17 00:00:00 2001 From: Benjamin Auder <benjamin.auder@somewhere> Date: Tue, 21 Jun 2022 14:25:59 +0200 Subject: [PATCH] Add Ambiguous. Fix a few issues with FEN generation / options --- app.js | 5 +- pieces/ambiguous_target.svg | 191 ++++++++++++++++++++++++++++++++++ server.js | 16 ++- variants.js | 2 +- variants/Ambiguous/class.js | 107 ++++++++++--------- variants/Ambiguous/rules.html | 11 ++ variants/Ambiguous/style.css | 43 ++++++++ variants/Chakart/class.js | 6 +- variants/Suction/class.js | 4 +- 9 files changed, 316 insertions(+), 69 deletions(-) create mode 100644 pieces/ambiguous_target.svg create mode 100644 variants/Ambiguous/rules.html create mode 100644 variants/Ambiguous/style.css diff --git a/app.js b/app.js index 7d387c5..4c0b5e4 100644 --- a/app.js +++ b/app.js @@ -545,8 +545,9 @@ function initializeGame(obj) { break; } } - fillGameInfos(obj, playerColor == "w" ? 1 : 0); - if (obj.randvar) + const playerIndex = (playerColor == "w" ? 0 : 1); + fillGameInfos(obj, 1 - playerIndex); + if (obj.players[playerIndex].randvar) toggleVisible("gameInfos"); else toggleVisible("boardContainer"); diff --git a/pieces/ambiguous_target.svg b/pieces/ambiguous_target.svg new file mode 100644 index 0000000..53f0ccc --- /dev/null +++ b/pieces/ambiguous_target.svg @@ -0,0 +1,191 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://www.w3.org/2000/svg" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:ns1="http://sozi.baierouge.fr" + xmlns:xlink="http://www.w3.org/1999/xlink" + id="svg12417" + sodipodi:docname="target.svg" + viewBox="0 0 200.13 200.02" + version="1.1" + inkscape:version="0.48+devel r10958" + > + <title + id="title825" + >Target</title + > + <sodipodi:namedview + id="base" + fit-margin-left="10" + inkscape:zoom="1" + height="0px" + borderopacity="1.0" + inkscape:current-layer="layer1" + inkscape:cx="89.904323" + inkscape:cy="99.565238" + fit-margin-right="10" + inkscape:window-maximized="1" + showgrid="false" + width="0px" + inkscape:guide-bbox="true" + showguides="true" + bordercolor="#666666" + inkscape:window-x="-8" + inkscape:window-y="-8" + fit-margin-bottom="10" + inkscape:window-width="1280" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + pagecolor="#ffffff" + inkscape:document-units="px" + inkscape:window-height="962" + fit-margin-top="10" + /> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer" + transform="translate(-248.44 -442.39)" + > + <g + id="g818" + transform="translate(-.96350 -1.0044)" + > + <path + id="path12427-9-3-9" + sodipodi:rx="180.62735" + sodipodi:ry="180.62735" + style="color:#000000;stroke:#000000;stroke-width:24.084;fill:none" + sodipodi:type="arc" + d="m531.77 557.68c0 99.758-80.87 180.63-180.63 180.63-99.758 0-180.63-80.87-180.63-180.63 0-99.758 80.87-180.63 180.63-180.63 99.758 0 180.63 80.87 180.63 180.63z" + transform="matrix(.41522 0 0 .41522 203.67 311.84)" + sodipodi:cy="557.68408" + sodipodi:cx="351.13956" + /> + <path + id="path12427-9-3-9-8-5" + sodipodi:rx="180.62735" + sodipodi:ry="180.62735" + style="color:#000000;stroke:#000000;stroke-width:40.139;fill:none" + sodipodi:type="arc" + d="m531.77 557.68c0 99.758-80.87 180.63-180.63 180.63-99.758 0-180.63-80.87-180.63-180.63 0-99.758 80.87-180.63 180.63-180.63 99.758 0 180.63 80.87 180.63 180.63z" + transform="matrix(.24913 0 0 .24913 261.99 404.46)" + sodipodi:cy="557.68408" + sodipodi:cx="351.13956" + /> + <path + id="path12427-9-3-9-8-2-3" + sodipodi:rx="180.62735" + sodipodi:ry="180.62735" + style="color:#000000;fill:#000000" + sodipodi:type="arc" + d="m531.77 557.68c0 99.758-80.87 180.63-180.63 180.63-99.758 0-180.63-80.87-180.63-180.63 0-99.758 80.87-180.63 180.63-180.63 99.758 0 180.63 80.87 180.63 180.63z" + transform="matrix(.12121 0 0 .12121 306.91 475.8)" + sodipodi:cy="557.68408" + sodipodi:cx="351.13956" + /> + <path + id="path13189-0" + d="m349.48 455.93v174.95" + style="stroke:#000000;stroke-linecap:round;stroke-width:5;fill:none" + inkscape:connector-curvature="0" + /> + <path + id="path45" + style="stroke:#000000;stroke-linecap:round;stroke-width:5;fill:none" + inkscape:connector-curvature="0" + d="m436.98 543.41h-174.95" + /> + </g + > + </g + > + <metadata + > + <rdf:RDF + > + <cc:Work + > + <dc:format + >image/svg+xml</dc:format + > + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" + /> + <cc:license + rdf:resource="http://creativecommons.org/licenses/publicdomain/" + /> + <dc:publisher + > + <cc:Agent + rdf:about="http://openclipart.org/" + > + <dc:title + >Openclipart</dc:title + > + </cc:Agent + > + </dc:publisher + > + <dc:title + >Target</dc:title + > + <dc:date + >2012-02-15T07:37:04</dc:date + > + <dc:description + >Target symbol</dc:description + > + <dc:source + >https://openclipart.org/detail/168253/target-by-fanda@cz</dc:source + > + <dc:creator + > + <cc:Agent + > + <dc:title + >Fanda@CZ</dc:title + > + </cc:Agent + > + </dc:creator + > + <dc:subject + > + <rdf:Bag + > + <rdf:li + >target</rdf:li + > + </rdf:Bag + > + </dc:subject + > + </cc:Work + > + <cc:License + rdf:about="http://creativecommons.org/licenses/publicdomain/" + > + <cc:permits + rdf:resource="http://creativecommons.org/ns#Reproduction" + /> + <cc:permits + rdf:resource="http://creativecommons.org/ns#Distribution" + /> + <cc:permits + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" + /> + </cc:License + > + </rdf:RDF + > + </metadata + > +</svg +> diff --git a/server.js b/server.js index 7e1a99b..dc8bd97 100644 --- a/server.js +++ b/server.js @@ -37,15 +37,12 @@ function initializeGame(vname, players, options) { function launchGame(gid) { moveHash[gid] = {}; const gameInfo = Object.assign( - {seed: Math.floor(Math.random() * 1984), gid: gid}, + {seed: Math.floor(Math.random() * 19840), gid: gid}, games[gid] ); // players array is supposed to be full: - for (const p of games[gid].players) { - send(p.sid, - "gamestart", - Object.assign({randvar: p.randvar}, gameInfo)); - } + for (const p of games[gid].players) + send(p.sid, "gamestart", gameInfo); } function getRandomVariant() { @@ -137,11 +134,10 @@ wss.on("connection", (socket, req) => { const allrand = games[obj.gid].rematch.every(r => r == 2); if (allrand) vname = getRandomVariant(); - games[obj.gid].players.forEach(p => - p.randvar = allrand ? true : false); + games[obj.gid].players.forEach(p => p.randvar = allrand); const gid = initializeGame(vname, - games[obj.gid].players.reverse(), - games[obj.gid].options); + games[obj.gid].players.reverse(), + games[obj.gid].options); launchGame(gid); } } diff --git a/variants.js b/variants.js index aafd02a..c9af9e4 100644 --- a/variants.js +++ b/variants.js @@ -5,7 +5,7 @@ const variants = [ // {name: 'Alice', desc: 'Both sides of the mirror'}, // {name: 'Align4', desc: 'Align four pawns'}, // {name: 'Allmate', desc: 'Mate any piece'}, -// {name: 'Ambiguous', desc: "Play opponent's pieces"}, + {name: 'Ambiguous', desc: "Play opponent's pieces"}, // {name: 'Antiking1', desc: 'Keep antiking in check', disp: 'Anti-King'}, // {name: 'Antimatter', desc: 'Dangerous collisions'}, // {name: 'Apocalypse', desc: 'The end of the world'}, diff --git a/variants/Ambiguous/class.js b/variants/Ambiguous/class.js index 78d3d63..6a001a6 100644 --- a/variants/Ambiguous/class.js +++ b/variants/Ambiguous/class.js @@ -1,10 +1,14 @@ import ChessRules from "/base_rules.js"; -import { randInt, shuffle } from "@/utils/alea"; -import { ArrayFun } from "@/utils/array"; +import GiveawayRules from "/variants/Giveaway/class.js"; export default class AmbiguousRules extends ChessRules { - // TODO: options + static get Options() { + return { + select: C.Options.select, + styles: ["cylinder"] + }; + } get hasFlags() { return false; @@ -19,25 +23,29 @@ export default class AmbiguousRules extends ChessRules { } genRandInitFen(seed) { - const gr = new GiveawayRules( - {mode: "suicide", options: this.options, genFenOnly: true}); + const options = Object.assign({mode: "suicide"}, this.options); + const gr = new GiveawayRules({options: options, genFenOnly: true}); return gr.genRandInitFen(seed); } + canStepOver(x, y) { + return this.board[x][y] == "" || this.getPiece(x, y) == V.GOAL; + } + // Subturn 1: play a move for the opponent on the designated square. // Subturn 2: play a move for me (which just indicate a square). getPotentialMovesFrom([x, y]) { const color = this.turn; - const oppCol = V.GetOppCol(color); + const oppCol = C.GetOppCol(color); if (this.subTurn == 2) { // Just play a normal move (which in fact only indicate a square) let movesHash = {}; return ( super.getPotentialMovesFrom([x, y]) .filter(m => { - // Filter promotions: keep only one, since no choice now. + // Filter promotions: keep only one, since no choice for now. if (m.appear[0].p != m.vanish[0].p) { - const hash = V.CoordsToSquare(m.start) + V.CoordsToSquare(m.end); + const hash = C.CoordsToSquare(m.start) + C.CoordsToSquare(m.end); if (!movesHash[hash]) { movesHash[hash] = true; return true; @@ -47,48 +55,37 @@ export default class AmbiguousRules extends ChessRules { return true; }) .map(m => { - if (m.vanish.length == 1) m.appear[0].p = V.GOAL; - else m.appear[0].p = V.TARGET_CODE[m.vanish[1].p]; - m.appear[0].c = oppCol; + if (m.vanish.length == 1) { + m.appear[0].c = 'a'; //a-color + m.appear[0].p = V.GOAL; + } + else { + m.appear[0].p = V.TARGET_CODE[m.vanish[1].p]; + m.appear[0].c = oppCol; + } m.vanish.shift(); return m; }) ); } - // At subTurn == 1, play a targeted move for opponent + // At subTurn == 1, play a targeted move for the opponent. // Search for target (we could also have it in a stack...) - let target = { x: -1, y: -1 }; - outerLoop: for (let i = 0; i < V.size.x; i++) { - for (let j = 0; j < V.size.y; j++) { - if (this.board[i][j] != V.EMPTY) { - const piece = this.board[i][j][1]; + 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++) { + if (this.board[i][j] != "") { + const piece = this.getPiece(i, j); if ( piece == V.GOAL || Object.keys(V.TARGET_DECODE).includes(piece) ) { - target = { x: i, y: j}; + target = {x: i, y:j}; break outerLoop; } } } } - // TODO: could be more efficient than generating all moves. - this.turn = oppCol; - const emptyTarget = (this.board[target.x][target.y][1] == V.GOAL); - if (emptyTarget) this.board[target.x][target.y] = V.EMPTY; - let moves = super.getPotentialMovesFrom([x, y]); - if (emptyTarget) { - this.board[target.x][target.y] = color + V.GOAL; - moves.forEach(m => { - m.vanish.push({ - x: target.x, - y: target.y, - c: color, - p: V.GOAL - }); - }); - } - this.turn = color; + const moves = super.getPotentialMovesFrom([x, y], oppCol); return moves.filter(m => m.end.x == target.x && m.end.y == target.y); } @@ -127,8 +124,17 @@ export default class AmbiguousRules extends ChessRules { }; } - pieces() { - // ......... + pieces(color, x, y) { + const targets = { + 's': {"class": "target-pawn", moves: []}, + 'u': {"class": "target-rook", moves: []}, + 'o': {"class": "target-knight", moves: []}, + 'c': {"class": "target-bishop", moves: []}, + 't': {"class": "target-queen", moves: []}, + 'l': {"class": "target-king", moves: []} + }; + return Object.assign( + { 'g': {"class": "target"} }, targets, super.pieces(color, x, y)); } atLeastOneMove() { @@ -140,27 +146,26 @@ export default class AmbiguousRules extends ChessRules { return moves; } + isKing(symbol) { + return ['k', 'l'].includes(symbol); + } + getCurrentScore() { // This function is only called at subTurn 1 - const color = V.GetOppCol(this.turn); - if (this.kingPos[color][0] < 0) return (color == 'w' ? "0-1" : "1-0"); + const color = C.GetOppCol(this.turn); + const kingPos = this.searchKingPos(color); + if (kingPos[0] < 0) + return (color == 'w' ? "0-1" : "1-0"); return "*"; } - play(move) { - let kingCaptured = false; - if (this.subTurn == 1) { - this.prePlay(move); - this.epSquares.push(this.getEpSquare(move)); - kingCaptured = this.kingPos[this.turn][0] < 0; - } - if (kingCaptured) move.kingCaptured = true; - V.PlayOnBoard(this.board, move); - if (this.subTurn == 2 || kingCaptured) { - this.turn = V.GetOppCol(this.turn); + postPlay(move) { + const color = this.turn; + if (this.subTurn == 2 || this.searchKingPos(color)[0] < 0) { + this.turn = C.GetOppCol(color); this.movesCount++; } - if (!kingCaptured) this.subTurn = 3 - this.subTurn; + this.subTurn = 3 - this.subTurn; } }; diff --git a/variants/Ambiguous/rules.html b/variants/Ambiguous/rules.html new file mode 100644 index 0000000..b51c747 --- /dev/null +++ b/variants/Ambiguous/rules.html @@ -0,0 +1,11 @@ +<p> + Every move you play can be changed by your opponent by a move arriving + on the same square. +</p> + +<p> + Consequently, you play twice on each turn: first to select a move for + your opponent, then to choose one for you - which could be altered. +</p> + +<p class="author">Fabrice Liardet (2005).</p> diff --git a/variants/Ambiguous/style.css b/variants/Ambiguous/style.css new file mode 100644 index 0000000..e31d810 --- /dev/null +++ b/variants/Ambiguous/style.css @@ -0,0 +1,43 @@ +@import url("/base_pieces.css"); + +piece.target { + background-image: url('/pieces/ambiguous_target.svg'); +} + +piece.white.target-pawn { + background-image: url('/pieces/yellow_pawn.svg'); +} +piece.white.target-rook { + background-image: url('/pieces/yellow_rook.svg'); +} +piece.white.target-knight { + background-image: url('/pieces/yellow_knight.svg'); +} +piece.white.target-bishop { + background-image: url('/pieces/yellow_bishop.svg'); +} +piece.white.target-queen { + background-image: url('/pieces/yellow_queen.svg'); +} +piece.white.target-king { + background-image: url('/pieces/yellow_king.svg'); +} + +piece.black.target-pawn { + background-image: url('/pieces/red_pawn.svg'); +} +piece.black.target-rook { + background-image: url('/pieces/red_rook.svg'); +} +piece.black.target-knight { + background-image: url('/pieces/red_knight.svg'); +} +piece.black.target-bishop { + background-image: url('/pieces/red_bishop.svg'); +} +piece.black.target-queen { + background-image: url('/pieces/red_queen.svg'); +} +piece.black.target-king { + background-image: url('/pieces/red_king.svg'); +} diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js index 29fdee9..37261e4 100644 --- a/variants/Chakart/class.js +++ b/variants/Chakart/class.js @@ -131,8 +131,8 @@ export default class ChakartRules extends ChessRules { } genRandInitFen(seed) { - const gr = new GiveawayRules( - {mode: "suicide", options: this.options, genFenOnly: true}); + const options = Object.assign({mode: "suicide"}, this.options); + const gr = new GiveawayRules({options: options, genFenOnly: true}); // Add Peach + mario flags return gr.genRandInitFen(seed).slice(0, -17) + '{"flags":"1111"}'; } @@ -180,7 +180,7 @@ export default class ChakartRules extends ChessRules { this.moveStack = []; // Change seed (after FEN generation!!) // so that further calls differ between players: - Random.setSeed(Math.floor(10000 * Math.random())); + Random.setSeed(Math.floor(19840 * Math.random())); } // For Toadette bonus diff --git a/variants/Suction/class.js b/variants/Suction/class.js index f9cf26d..e2e36c8 100644 --- a/variants/Suction/class.js +++ b/variants/Suction/class.js @@ -42,8 +42,8 @@ export default class SuctionRules extends ChessRules { } genRandInitFen(seed) { - const gr = new GiveawayRules( - {mode: "suicide", options: this.options, genFenOnly: true}); + const options = Object.assign({mode: "suicide"}, this.options); + const gr = new GiveawayRules({options: options, genFenOnly: true}); // Add empty cmove: return ( gr.genRandInitFen(seed).slice(0, -17) + '{"enpassant":"-","cmove":"-"}'); -- 2.44.0