--- /dev/null
+../Alice/bc.svg
\ No newline at end of file
--- /dev/null
+../Alice/bl.svg
\ No newline at end of file
--- /dev/null
+../Alice/bo.svg
\ No newline at end of file
--- /dev/null
+../Alice/bs.svg
\ No newline at end of file
--- /dev/null
+../Alice/bt.svg
\ No newline at end of file
--- /dev/null
+../Alice/bu.svg
\ No newline at end of file
--- /dev/null
+../Alice/wc.svg
\ No newline at end of file
--- /dev/null
+../Alice/wl.svg
\ No newline at end of file
--- /dev/null
+../Alice/wo.svg
\ No newline at end of file
--- /dev/null
+../Alice/ws.svg
\ No newline at end of file
--- /dev/null
+../Alice/wt.svg
\ No newline at end of file
--- /dev/null
+../Alice/wu.svg
\ No newline at end of file
#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
#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
#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
"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",
"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",
"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",
--- /dev/null
+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 :-)
--- /dev/null
+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? :-)
--- /dev/null
+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 :-)
"Dark",
"Hidden",
"Hiddenqueen",
+ "Stealthbomb",
"Synchrone1",
"Synchrone2"
]
"Dark",
"Hidden",
"Hiddenqueen",
+ "Stealthbomb",
"Synchrone1",
"Synchrone2"
]
"Dark",
"Hidden",
"Hiddenqueen",
+ "Stealthbomb",
"Synchrone1",
"Synchrone2"
]
vanish: [
new PiPo({ x: square[0], y: square[1], c: c, p: V.UNDEFINED })
],
- start: { x: -1, y: -1 },
+ start: { x: -1, y: -1 }
});
}
--- /dev/null
+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);
+ }
+
+};
('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'),