From: Benjamin Auder Date: Wed, 24 Mar 2021 18:04:16 +0000 (+0100) Subject: Add Stealthbomb X-Git-Url: https://git.auder.net/doc/current/%7B%7B%20asset%28%27mixstore/css/static/%7B%7B?a=commitdiff_plain;h=fbc3e6f984492143625e438436f9c0a94e42c713;p=vchess.git Add Stealthbomb --- diff --git a/client/public/images/pieces/Stealthbomb/bc.svg b/client/public/images/pieces/Stealthbomb/bc.svg new file mode 120000 index 00000000..b30a26ad --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/bc.svg @@ -0,0 +1 @@ +../Alice/bc.svg \ No newline at end of file diff --git a/client/public/images/pieces/Stealthbomb/bl.svg b/client/public/images/pieces/Stealthbomb/bl.svg new file mode 120000 index 00000000..a10d9e0a --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/bl.svg @@ -0,0 +1 @@ +../Alice/bl.svg \ No newline at end of file diff --git a/client/public/images/pieces/Stealthbomb/bo.svg b/client/public/images/pieces/Stealthbomb/bo.svg new file mode 120000 index 00000000..1200186b --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/bo.svg @@ -0,0 +1 @@ +../Alice/bo.svg \ No newline at end of file diff --git a/client/public/images/pieces/Stealthbomb/bs.svg b/client/public/images/pieces/Stealthbomb/bs.svg new file mode 120000 index 00000000..e8cf23a8 --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/bs.svg @@ -0,0 +1 @@ +../Alice/bs.svg \ No newline at end of file diff --git a/client/public/images/pieces/Stealthbomb/bt.svg b/client/public/images/pieces/Stealthbomb/bt.svg new file mode 120000 index 00000000..c517549b --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/bt.svg @@ -0,0 +1 @@ +../Alice/bt.svg \ No newline at end of file diff --git a/client/public/images/pieces/Stealthbomb/bu.svg b/client/public/images/pieces/Stealthbomb/bu.svg new file mode 120000 index 00000000..09e6ea3e --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/bu.svg @@ -0,0 +1 @@ +../Alice/bu.svg \ No newline at end of file diff --git a/client/public/images/pieces/Stealthbomb/wc.svg b/client/public/images/pieces/Stealthbomb/wc.svg new file mode 120000 index 00000000..d23af91d --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/wc.svg @@ -0,0 +1 @@ +../Alice/wc.svg \ No newline at end of file diff --git a/client/public/images/pieces/Stealthbomb/wl.svg b/client/public/images/pieces/Stealthbomb/wl.svg new file mode 120000 index 00000000..51c1893a --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/wl.svg @@ -0,0 +1 @@ +../Alice/wl.svg \ No newline at end of file diff --git a/client/public/images/pieces/Stealthbomb/wo.svg b/client/public/images/pieces/Stealthbomb/wo.svg new file mode 120000 index 00000000..4a85712d --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/wo.svg @@ -0,0 +1 @@ +../Alice/wo.svg \ No newline at end of file diff --git a/client/public/images/pieces/Stealthbomb/ws.svg b/client/public/images/pieces/Stealthbomb/ws.svg new file mode 120000 index 00000000..659b2de0 --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/ws.svg @@ -0,0 +1 @@ +../Alice/ws.svg \ No newline at end of file diff --git a/client/public/images/pieces/Stealthbomb/wt.svg b/client/public/images/pieces/Stealthbomb/wt.svg new file mode 120000 index 00000000..447fc4fe --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/wt.svg @@ -0,0 +1 @@ +../Alice/wt.svg \ No newline at end of file diff --git a/client/public/images/pieces/Stealthbomb/wu.svg b/client/public/images/pieces/Stealthbomb/wu.svg new file mode 120000 index 00000000..c1403b33 --- /dev/null +++ b/client/public/images/pieces/Stealthbomb/wu.svg @@ -0,0 +1 @@ +../Alice/wu.svg \ No newline at end of file diff --git a/client/src/translations/about/en.pug b/client/src/translations/about/en.pug index f1e0c93a..49795754 100644 --- a/client/src/translations/about/en.pug +++ b/client/src/translations/about/en.pug @@ -44,7 +44,8 @@ h3 Related links #links a(href="https://www.chessvariants.com/") chessvariants.com a(href="https://greenchess.net/") greenchess.net - a(href="http://pychess-variants.herokuapp.com/") pychess-variants.com + a(href="https://www.pychess.org/") pychess.org + a(href="https://fishrandom.io/") fishrandom.io a(href="https://glukkazan.github.io/") Dagaz demo + server a(href="https://mindsports.nl/index.php") mindsports.nl a(href="https://www.jocly.com/#/games") jocly.com diff --git a/client/src/translations/about/es.pug b/client/src/translations/about/es.pug index bcc6ff0c..4b08b48c 100644 --- a/client/src/translations/about/es.pug +++ b/client/src/translations/about/es.pug @@ -43,7 +43,8 @@ h3 Enlaces relacionados #links a(href="https://www.chessvariants.com/") chessvariants.com a(href="https://greenchess.net/") greenchess.net - a(href="http://pychess-variants.herokuapp.com/") pychess-variants.com + a(href="https://www.pychess.org/") pychess.org + a(href="https://fishrandom.io/") fishrandom.io a(href="https://glukkazan.github.io/") Dagaz demo + servidor a(href="https://mindsports.nl/index.php") mindsports.nl a(href="https://www.jocly.com/#/games") jocly.com diff --git a/client/src/translations/about/fr.pug b/client/src/translations/about/fr.pug index 537a79ba..cf58ca77 100644 --- a/client/src/translations/about/fr.pug +++ b/client/src/translations/about/fr.pug @@ -44,7 +44,8 @@ h3 Liens connexes #links a(href="https://www.chessvariants.com/") chessvariants.com a(href="https://greenchess.net/") greenchess.net - a(href="http://pychess-variants.herokuapp.com/") pychess-variants.com + a(href="https://www.pychess.org/") pychess.org + a(href="https://fishrandom.io/") fishrandom.io a(href="https://glukkazan.github.io/") Dagaz demo + serveur a(href="https://mindsports.nl/index.php") mindsports.nl a(href="https://www.jocly.com/#/games") jocly.com diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 604fbf8e..a86b6e1d 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -181,6 +181,7 @@ export const translations = { "Augmented Queens": "Augmented Queens", "Balanced sliders & leapers": "Balanced sliders & leapers", "Baroque Music": "Baroque Music", + "Beware the bomb": "Beware the bomb", "Big board": "Big board", "Bishop versus pawns": "Bishop versus pawns", "Board upside down": "Board upside down", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 9a099a2c..a5cd67b5 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -181,6 +181,7 @@ export const translations = { "Augmented Queens": "Damas aumentadas", "Balanced sliders & leapers": "Modos de desplazamiento equilibrados", "Baroque Music": "Música Barroca", + "Beware the bomb": "Cuidado con la bomba", "Big board": "Gran tablero", "Bishop versus pawns": "Alfil contra peones", "Board upside down": "Tablero al revés", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 5bd4aea6..9ca47171 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -181,6 +181,7 @@ export const translations = { "Augmented Queens": "Dames augmentées", "Balanced sliders & leapers": "Modes de déplacement équilibrés", "Baroque Music": "Musique Baroque", + "Beware the bomb": "Attention à la bombe", "Big board": "Grand échiquier", "Bishop versus pawns": "Fou contre pions", "Board upside down": "Échiquier à l'envers", diff --git a/client/src/translations/rules/Stealthbomb/en.pug b/client/src/translations/rules/Stealthbomb/en.pug new file mode 100644 index 00000000..09ba3bdd --- /dev/null +++ b/client/src/translations/rules/Stealthbomb/en.pug @@ -0,0 +1,31 @@ +p.boxed. + One piece of each side hides a bomb, + which can be triggered instead of playing a move. + +p. + Each player secretly decides of a piece which will carry a bomb. + (Click on any piece at move 1). + +p. + At any time, instead of playing a move you may detonate the bomb: + bring the loaded piece onto your king. + On the diagrams below, bombs are placed at c2 and e4. + +figure.diagram-container + .diagram.diag12 + | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/2N5/PPoP1PPP/R1BK2NR: + .diagram.diag22 + | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/8/P4PPP/R5NR: + figcaption Not checkmate: explosion on c2 follows. + +p. + An explosion destroys everything a king step away from the bomb, + including the loaded piece. Explosion has priority over checkmate. + +h3 Source + +p + a(href="https://en.wikipedia.org/wiki/Beirut_chess") Beirut Chess + | . This variant is Stealthbomber on + a(href="fishrandom.io") fishrandom.io + | , and "Stealthbomb" here... why not :-) diff --git a/client/src/translations/rules/Stealthbomb/es.pug b/client/src/translations/rules/Stealthbomb/es.pug new file mode 100644 index 00000000..758e7e9a --- /dev/null +++ b/client/src/translations/rules/Stealthbomb/es.pug @@ -0,0 +1,31 @@ +p.boxed. + Una pieza de cada campamento esconde una bomba, + que se puede activar en lugar de ejecutar un movimiento. + +p. + Cada jugador decide en secreto qué pieza llevará una bomba. + (Haga clic en una pieza en el primer movimiento). + +p. + En cualquier momento, puede detonar la bomba en lugar de hacer una jugada: + lleva la pieza cargada a tu rey. + En los siguientes diagramas, las bombas se colocan en c2 y e4. + +figure.diagram-container + .diagram.diag12 + | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/2N5/PPoP1PPP/R1BK2NR: + .diagram.diag22 + | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/8/P4PPP/R5NR: + figcaption No jaque mate: sigue la explosión en c2. + +p. + Una explosión destruye todo a un paso de la bomba, incluyendo + la pieza cargada. La explosión tiene prioridad sobre el jaque mate. + +h3 Fuente + +p + a(href="https://en.wikipedia.org/wiki/Beirut_chess") Beirut Chess + | . Esta variante es Stealthbomber en + a(href="fishrandom.io") fishrandom.io + | , y "Stealthbomb" aquí... ¿Por qué no? :-) diff --git a/client/src/translations/rules/Stealthbomb/fr.pug b/client/src/translations/rules/Stealthbomb/fr.pug new file mode 100644 index 00000000..ecf61cc1 --- /dev/null +++ b/client/src/translations/rules/Stealthbomb/fr.pug @@ -0,0 +1,31 @@ +p.boxed. + Une pièce de chaque camp cache une bombe, + qui peut être déclenchée au lieu de jouer un coup. + +p. + Chaque joueur décide secrètement d'une pièce qui portera une bombe. + (Cliquez sur une pièce au premier coup). + +p. + À tout moment, vous pouvez faire exploser la bombe au lieu de jouer un coup : + amenez la pièce chargée sur votre roi. + Dans les diagrammes ci-dessous, les bombes sont placées en c2 et e4. + +figure.diagram-container + .diagram.diag12 + | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/2N5/PPoP1PPP/R1BK2NR: + .diagram.diag22 + | fen:r1bqk1nr/pppp1Qpp/8/2b1p3/2B1S3/8/P4PPP/R5NR: + figcaption Pas mat : l'explosion en c2 suit. + +p. + Une explosion détruit tout à un pas de roi depuis la bombe, incluant + la pièce chargée. L'explosion a priorité sur le mat. + +h3 Source + +p + a(href="https://en.wikipedia.org/wiki/Beirut_chess") Beirut Chess + | . Cette variante est Stealthbomber sur + a(href="fishrandom.io") fishrandom.io + | , et "Stealthbomb" ici... pourquoi pas :-) diff --git a/client/src/translations/variants/en.pug b/client/src/translations/variants/en.pug index 5c131086..c933d9c7 100644 --- a/client/src/translations/variants/en.pug +++ b/client/src/translations/variants/en.pug @@ -222,6 +222,7 @@ p. "Dark", "Hidden", "Hiddenqueen", + "Stealthbomb", "Synchrone1", "Synchrone2" ] diff --git a/client/src/translations/variants/es.pug b/client/src/translations/variants/es.pug index fa4bab94..81fad93f 100644 --- a/client/src/translations/variants/es.pug +++ b/client/src/translations/variants/es.pug @@ -229,6 +229,7 @@ p. "Dark", "Hidden", "Hiddenqueen", + "Stealthbomb", "Synchrone1", "Synchrone2" ] diff --git a/client/src/translations/variants/fr.pug b/client/src/translations/variants/fr.pug index a2dccaef..6743ce25 100644 --- a/client/src/translations/variants/fr.pug +++ b/client/src/translations/variants/fr.pug @@ -228,6 +228,7 @@ p. "Dark", "Hidden", "Hiddenqueen", + "Stealthbomb", "Synchrone1", "Synchrone2" ] diff --git a/client/src/variants/Bario.js b/client/src/variants/Bario.js index e10bb3d4..3564e748 100644 --- a/client/src/variants/Bario.js +++ b/client/src/variants/Bario.js @@ -73,7 +73,7 @@ export class BarioRules extends ChessRules { vanish: [ new PiPo({ x: square[0], y: square[1], c: c, p: V.UNDEFINED }) ], - start: { x: -1, y: -1 }, + start: { x: -1, y: -1 } }); } diff --git a/client/src/variants/Stealthbomb.js b/client/src/variants/Stealthbomb.js new file mode 100644 index 00000000..ee73ef54 --- /dev/null +++ b/client/src/variants/Stealthbomb.js @@ -0,0 +1,228 @@ +import { ChessRules, Move, PiPo } from "@/base_rules"; + +export class StealthbombRules extends ChessRules { + + static get CanAnalyze() { + return false; + } + + static get SomeHiddenMoves() { + return true; + } + + static get BOMB_DECODE() { + return { + s: "p", + t: "q", + u: "r", + c: "b", + o: "n", + l: "k" + }; + } + static get BOMB_CODE() { + return { + p: "s", + q: "t", + r: "u", + b: "c", + n: "o", + k: "l" + }; + } + + static get PIECES() { + return ChessRules.PIECES.concat(Object.keys(V.BOMB_DECODE)); + } + + getPiece(i, j) { + const piece = this.board[i][j].charAt(1); + if ( + ChessRules.PIECES.includes(piece) || + // 'side' is used to determine what I see: normal or "loaded" piece? + this.getColor(i, j) == this.side + ) { + return piece; + } + // Loaded piece, but no right to view it + return V.BOMB_DECODE[piece]; + } + + getPpath(b, color, score) { + if (Object.keys(V.BOMB_DECODE).includes(b[1])) { + // Supposed to be hidden. + if (score == "*" && (!color || color != b[0])) + return b[0] + V.BOMB_DECODE[b[1]]; + return "Stealthbomb/" + b; + } + return b; + } + + hoverHighlight([x, y]) { + const c = this.turn; + return ( + this.movesCount <= 1 && + ( + (c == 'w' && x >= 6) || + (c == 'b' && x <= 1) + ) + ); + } + + onlyClick([x, y]) { + return ( + this.movesCount <= 1 || + // TODO: next line theoretically shouldn't be required... + (this.movesCount == 2 && this.getColor(x, y) != this.turn) + ); + } + + // Initiate the game by choosing a square for the bomb: + doClick(square) { + const c = this.turn; + if ( + this.movesCount >= 2 || + ( + (c == 'w' && square[0] < 6) || + (c == 'b' && square[0] > 2) + ) + ) { + return null; + } + const [x, y] = square; + const piece = super.getPiece(x, y); + return new Move({ + appear: [ new PiPo({ x: x, y: y, c: c, p: V.BOMB_CODE[piece] }) ], + vanish: [ new PiPo({ x: x, y: y, c: c, p: piece }) ], + start: { x: -1, y: -1 } + }); + } + + getPotentialMovesFrom([x, y]) { + if (this.movesCount <= 1) { + const setup = this.doClick([x, y]); + return (!setup ? [] : [setup]); + } + let moves = super.getPotentialMovesFrom([x, y]); + const c = this.turn; + // Add bomb explosion + if (Object.keys(V.BOMB_DECODE).includes(this.board[x][y][1])) { + let mv = new Move({ + appear: [ ], + vanish: [ new PiPo({ x: x, y: y, c: c, p: this.board[x][y][1] }) ], + end: { x: this.kingPos[c][0], y: this.kingPos[c][1] } + }); + for (let s of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) { + let [i, j] = [x + s[0], y + s[1]]; + if (V.OnBoard(i, j) && this.board[i][j] != V.EMPTY) { + mv.vanish.push( + new PiPo({ + x: i, + y: j, + c: this.getColor(i, j), + p: this.board[i][j][1] + }) + ); + } + } + moves.push(mv); + } + return moves; + } + + // NOTE: a lot of copy-paste from Atomic from here. + postPlay(move) { + if (this.movesCount >= 3) { + super.postPlay(move); + if (move.appear.length == 0) { + // Explosion + const firstRank = { w: 7, b: 0 }; + for (let c of ["w", "b"]) { + // Did we explode king of color c ? + if ( + Math.abs(this.kingPos[c][0] - move.start.x) <= 1 && + Math.abs(this.kingPos[c][1] - move.start.y) <= 1 + ) { + this.kingPos[c] = [-1, -1]; + this.castleFlags[c] = [8, 8]; + } + else { + // Now check if init rook(s) exploded + if (Math.abs(move.start.x - firstRank[c]) <= 1) { + if (Math.abs(move.start.y - this.castleFlags[c][0]) <= 1) + this.castleFlags[c][0] = 8; + if (Math.abs(move.start.y - this.castleFlags[c][1]) <= 1) + this.castleFlags[c][1] = 8; + } + } + } + } + } + } + + postUndo(move) { + if (this.movesCount >= 2) { + super.postUndo(move); + const c = this.turn; + const oppCol = V.GetOppCol(c); + if ([this.kingPos[c][0], this.kingPos[oppCol][0]].some(e => e < 0)) { + // Last move exploded some king.. + for (let psq of move.vanish) { + if (psq.p == "k") + this.kingPos[psq.c == c ? c : oppCol] = [psq.x, psq.y]; + } + } + } + } + + underCheck(color) { + const oppCol = V.GetOppCol(color); + let res = undefined; + // If our king disappeared, move is not valid + if (this.kingPos[color][0] < 0) res = true; + // If opponent king disappeared, move is valid + else if (this.kingPos[oppCol][0] < 0) res = false; + // Otherwise, if we remain under check, move is not valid + else res = this.isAttacked(this.kingPos[color], oppCol); + return res; + } + + getCheckSquares() { + const color = this.turn; + let res = []; + if ( + this.kingPos[color][0] >= 0 && //king might have exploded + this.isAttacked(this.kingPos[color], V.GetOppCol(color)) + ) { + res = [JSON.parse(JSON.stringify(this.kingPos[color]))]; + } + return res; + } + + getCurrentScore() { + const color = this.turn; + const kp = this.kingPos[color]; + if (kp[0] < 0) + // King disappeared + return color == "w" ? "0-1" : "1-0"; + if (this.atLeastOneMove()) return "*"; + if (!this.isAttacked(kp, V.GetOppCol(color))) return "1/2"; + return color == "w" ? "0-1" : "1-0"; //checkmate + } + + getNotation(move) { + if (this.movesCount <= 1) return "Bomb?"; + const c = this.turn; + if (move.end.x == this.kingPos[c][0] && move.end.y == this.kingPos[c][1]) + return V.CoordsToSquare(move.start) + "~X"; + if (Object.keys(V.BOMB_DECODE).includes(move.vanish[0].p)) { + let cpMove = JSON.parse(JSON.stringify(move)); + cpMove.vanish[0].p = V.BOMB_DECODE[move.vanish[0].p]; + if (Object.keys(V.BOMB_DECODE).includes(move.appear[0].p)) + cpMove.appear[0].p = V.BOMB_DECODE[move.appear[0].p]; + return super.getNotation(cpMove); + } + return super.getNotation(move); + } + +}; diff --git a/server/db/populate.sql b/server/db/populate.sql index 1a13014b..13a4fb8a 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -156,6 +156,7 @@ insert or ignore into Variants (name, description) values ('Spartan', 'Spartan versus Persians'), ('Squatter1', 'Squat last rank (v1)'), ('Squatter2', 'Squat last rank (v2)'), + ('Stealthbomb', 'Beware the bomb'), ('Suicide', 'Lose all pieces'), ('Suction', 'Attract opposite king'), ('Swap', 'Dangerous captures'),