From 472c0c4f5aa29d96e080873ebfce2a04f664d852 Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Sun, 29 Mar 2020 16:09:14 +0200 Subject: [PATCH] Add Tencubed and Omega variants + some fixes (updateCastleFlags()) + cleaner FEN for Enpassant variant --- client/public/images/pieces/Omega/bc.svg | 97 ++++ client/public/images/pieces/Omega/bw.svg | 91 ++++ client/public/images/pieces/Omega/nothing.svg | 56 +++ client/public/images/pieces/Omega/wc.svg | 62 +++ client/public/images/pieces/Omega/ww.svg | 97 ++++ client/public/images/pieces/Tencubed/ba.svg | 156 ++++++ client/public/images/pieces/Tencubed/bc.svg | 97 ++++ client/public/images/pieces/Tencubed/bm.svg | 205 ++++++++ client/public/images/pieces/Tencubed/bw.svg | 91 ++++ client/public/images/pieces/Tencubed/wa.svg | 152 ++++++ client/public/images/pieces/Tencubed/wc.svg | 62 +++ client/public/images/pieces/Tencubed/wm.svg | 175 +++++++ client/public/images/pieces/Tencubed/ww.svg | 97 ++++ client/src/App.vue | 6 +- client/src/base_rules.js | 25 +- client/src/translations/about/en.pug | 1 + client/src/translations/about/es.pug | 1 + client/src/translations/about/fr.pug | 1 + client/src/translations/en.js | 2 + client/src/translations/es.js | 2 + client/src/translations/fr.js | 2 + client/src/translations/rules/Omega/en.pug | 48 ++ client/src/translations/rules/Omega/es.pug | 51 ++ client/src/translations/rules/Omega/fr.pug | 51 ++ client/src/translations/rules/Tencubed/en.pug | 38 ++ client/src/translations/rules/Tencubed/es.pug | 41 ++ client/src/translations/rules/Tencubed/fr.pug | 41 ++ client/src/variants/Cannibal.js | 3 +- client/src/variants/Enpassant.js | 45 +- client/src/variants/Grand.js | 15 +- client/src/variants/Monster.js | 3 +- client/src/variants/Omega.js | 450 ++++++++++++++++++ client/src/variants/Schess.js | 1 - client/src/variants/Tencubed.js | 245 ++++++++++ client/src/variants/Wildebeest.js | 11 +- server/db/populate.sql | 2 + 36 files changed, 2481 insertions(+), 42 deletions(-) create mode 100644 client/public/images/pieces/Omega/bc.svg create mode 100644 client/public/images/pieces/Omega/bw.svg create mode 100644 client/public/images/pieces/Omega/nothing.svg create mode 100644 client/public/images/pieces/Omega/wc.svg create mode 100644 client/public/images/pieces/Omega/ww.svg create mode 100644 client/public/images/pieces/Tencubed/ba.svg create mode 100644 client/public/images/pieces/Tencubed/bc.svg create mode 100644 client/public/images/pieces/Tencubed/bm.svg create mode 100644 client/public/images/pieces/Tencubed/bw.svg create mode 100644 client/public/images/pieces/Tencubed/wa.svg create mode 100644 client/public/images/pieces/Tencubed/wc.svg create mode 100644 client/public/images/pieces/Tencubed/wm.svg create mode 100644 client/public/images/pieces/Tencubed/ww.svg create mode 100644 client/src/translations/rules/Omega/en.pug create mode 100644 client/src/translations/rules/Omega/es.pug create mode 100644 client/src/translations/rules/Omega/fr.pug create mode 100644 client/src/translations/rules/Tencubed/en.pug create mode 100644 client/src/translations/rules/Tencubed/es.pug create mode 100644 client/src/translations/rules/Tencubed/fr.pug create mode 100644 client/src/variants/Omega.js create mode 100644 client/src/variants/Tencubed.js diff --git a/client/public/images/pieces/Omega/bc.svg b/client/public/images/pieces/Omega/bc.svg new file mode 100644 index 00000000..e3d2eb68 --- /dev/null +++ b/client/public/images/pieces/Omega/bc.svg @@ -0,0 +1,97 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Omega/bw.svg b/client/public/images/pieces/Omega/bw.svg new file mode 100644 index 00000000..5c6b2bb8 --- /dev/null +++ b/client/public/images/pieces/Omega/bw.svg @@ -0,0 +1,91 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Omega/nothing.svg b/client/public/images/pieces/Omega/nothing.svg new file mode 100644 index 00000000..affd020e --- /dev/null +++ b/client/public/images/pieces/Omega/nothing.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/client/public/images/pieces/Omega/wc.svg b/client/public/images/pieces/Omega/wc.svg new file mode 100644 index 00000000..2dfaffa5 --- /dev/null +++ b/client/public/images/pieces/Omega/wc.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/client/public/images/pieces/Omega/ww.svg b/client/public/images/pieces/Omega/ww.svg new file mode 100644 index 00000000..f40e874a --- /dev/null +++ b/client/public/images/pieces/Omega/ww.svg @@ -0,0 +1,97 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Tencubed/ba.svg b/client/public/images/pieces/Tencubed/ba.svg new file mode 100644 index 00000000..afc27f05 --- /dev/null +++ b/client/public/images/pieces/Tencubed/ba.svg @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Tencubed/bc.svg b/client/public/images/pieces/Tencubed/bc.svg new file mode 100644 index 00000000..e3d2eb68 --- /dev/null +++ b/client/public/images/pieces/Tencubed/bc.svg @@ -0,0 +1,97 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Tencubed/bm.svg b/client/public/images/pieces/Tencubed/bm.svg new file mode 100644 index 00000000..fd548016 --- /dev/null +++ b/client/public/images/pieces/Tencubed/bm.svg @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Tencubed/bw.svg b/client/public/images/pieces/Tencubed/bw.svg new file mode 100644 index 00000000..5c6b2bb8 --- /dev/null +++ b/client/public/images/pieces/Tencubed/bw.svg @@ -0,0 +1,91 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Tencubed/wa.svg b/client/public/images/pieces/Tencubed/wa.svg new file mode 100644 index 00000000..b45ea509 --- /dev/null +++ b/client/public/images/pieces/Tencubed/wa.svg @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Tencubed/wc.svg b/client/public/images/pieces/Tencubed/wc.svg new file mode 100644 index 00000000..2dfaffa5 --- /dev/null +++ b/client/public/images/pieces/Tencubed/wc.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/client/public/images/pieces/Tencubed/wm.svg b/client/public/images/pieces/Tencubed/wm.svg new file mode 100644 index 00000000..fd0288db --- /dev/null +++ b/client/public/images/pieces/Tencubed/wm.svg @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Tencubed/ww.svg b/client/public/images/pieces/Tencubed/ww.svg new file mode 100644 index 00000000..f40e874a --- /dev/null +++ b/client/public/images/pieces/Tencubed/ww.svg @@ -0,0 +1,97 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/client/src/App.vue b/client/src/App.vue index ed8d1520..369391e7 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -327,7 +327,11 @@ div.board10 div.board11 width: 9.09% - padding-bottom: 9.1% + padding-bottom: 9.09% + +div.board12 + width: 8.33% + padding-bottom: 8.33% img.piece width: 100% diff --git a/client/src/base_rules.js b/client/src/base_rules.js index 22f57d5b..1a4a32d9 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -47,7 +47,9 @@ export const ChessRules = class ChessRules { static get PawnSpecs() { return { directions: { 'w': -1, 'b': 1 }, + initShift: { w: 1, b: 1 }, twoSquares: true, + threeSquares: false, promotions: [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN], canCapture: true, captureBackward: false, @@ -164,6 +166,7 @@ export const ChessRules = class ChessRules { return !!flags.match(/^[a-z]{4,4}$/); } + // NOTE: not with regexp to adapt to different board sizes. (TODO?) static IsGoodEnpassant(enpassant) { if (enpassant != "-") { const ep = V.SquareToCoords(enpassant); @@ -721,7 +724,6 @@ export const ChessRules = class ChessRules { const [sizeX, sizeY] = [V.size.x, V.size.y]; const pawnShiftX = V.PawnSpecs.directions[color]; const firstRank = (color == "w" ? sizeX - 1 : 0); - const startRank = (color == "w" ? sizeX - 2 : 1); // Pawn movements in shiftX direction: const getPawnMoves = (shiftX) => { @@ -734,11 +736,23 @@ export const ChessRules = class ChessRules { // Next condition because pawns on 1st rank can generally jump if ( V.PawnSpecs.twoSquares && - [startRank, firstRank].includes(x) && - this.board[x + 2 * shiftX][y] == V.EMPTY + ( + (color == 'w' && x >= V.size.x - 1 - V.PawnSpecs.initShift['w']) + || + (color == 'b' && x <= V.PawnSpecs.initShift['b']) + ) ) { - // Two squares jump - moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y])); + if (this.board[x + 2 * shiftX][y] == V.EMPTY) { + // Two squares jump + moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y])); + if ( + V.PawnSpecs.threeSquares && + this.board[x + 3 * shiftX][y] == V.EMPTY + ) { + // Three squares jump + moves.push(this.getBasicMove([x, y], [x + 3 * shiftX, y])); + } + } } } // Captures @@ -1158,7 +1172,6 @@ export const ChessRules = class ChessRules { if (piece == V.KING && move.appear.length > 0) { this.kingPos[c][0] = move.appear[0].x; this.kingPos[c][1] = move.appear[0].y; - return; } if (V.HasCastle) this.updateCastleFlags(move, piece); } diff --git a/client/src/translations/about/en.pug b/client/src/translations/about/en.pug index 6d5472c2..daccd8ff 100644 --- a/client/src/translations/about/en.pug +++ b/client/src/translations/about/en.pug @@ -53,3 +53,4 @@ h3 Related links a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net a(href="https://brainking.com/") brainking.com a(href="https://www.facebook.com/groups/592562551198628") A Facebook group + a(href="http://www.zillions-of-games.com/") zillions-of-games.com diff --git a/client/src/translations/about/es.pug b/client/src/translations/about/es.pug index c9036c80..cd84f982 100644 --- a/client/src/translations/about/es.pug +++ b/client/src/translations/about/es.pug @@ -50,3 +50,4 @@ h3 Enlaces relacionados a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net a(href="https://brainking.com/") brainking.com a(href="https://www.facebook.com/groups/592562551198628") Un grupo Facebook + a(href="http://www.zillions-of-games.com/") zillions-of-games.com diff --git a/client/src/translations/about/fr.pug b/client/src/translations/about/fr.pug index af12edaa..70ddc0ff 100644 --- a/client/src/translations/about/fr.pug +++ b/client/src/translations/about/fr.pug @@ -49,3 +49,4 @@ h3 Liens connexes a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net a(href="https://brainking.com/") brainking.com a(href="https://www.facebook.com/groups/592562551198628") Un groupe Facebook + a(href="http://www.zillions-of-games.com/") zillions-of-games.com diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 86b75dde..7de0b9c1 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -157,6 +157,7 @@ export const translations = { // Variants boxes: "64 pieces on the board": "64 pieces on the board", "A pawns cloud": "A pawns cloud", + "A wizard in the corner": "A wizard in the corner", "Ancient rules": "Ancient rules", "Attract opposite king": "Attract opposite king", "Balanced sliders & leapers": "Balanced sliders & leapers", @@ -173,6 +174,7 @@ export const translations = { "Each piece is unique": "Each piece is unique", "Exotic captures": "Exotic captures", "Explosive captures": "Explosive captures", + "Four new pieces": "Four new pieces", "In the shadow": "In the shadow", "Get strong at self-mate": "Get strong at self-mate", "Give three checks": "Give three checks", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 1cd0b527..3a4ece50 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -157,6 +157,7 @@ export const translations = { // Variants boxes: "64 pieces on the board": "64 piezas en el tablero", "A pawns cloud": "Une nube de peones", + "A wizard in the corner": "Un mago en la esquina", "Ancient rules": "Viejas reglas", "Attract opposite king": "Atraer al rey contrario", "Balanced sliders & leapers": "Modos de desplazamiento equilibrados", @@ -173,6 +174,7 @@ export const translations = { "Each piece is unique": "Cada pieza es única", "Exotic captures": "Capturas exóticas", "Explosive captures": "Capturas explosivas", + "Four new pieces": "Quatro nuevas piezas", "In the shadow": "En la sombra", "Get strong at self-mate": "Progreso en mates asistidos", "Give three checks": "Dar tres jaques", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index f4c956fa..3ed54e76 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -157,6 +157,7 @@ export const translations = { // Variants boxes: "64 pieces on the board": "64 pièces sur l'échiquier", "A pawns cloud": "Une nuée de pions", + "A wizard in the corner": "Un sorcier dans le coin", "Ancient rules": "Règles anciennes", "Attract opposite king": "Attirer le roi adverse", "Balanced sliders & leapers": "Modes de déplacement équilibrés", @@ -173,6 +174,7 @@ export const translations = { "Each piece is unique": "Chaque pièce est unique", "Exotic captures": "Captures exotiques", "Explosive captures": "Captures explosives", + "Four new pieces": "Quatre nouvelles pièces", "In the shadow": "Dans l'ombre", "Get strong at self-mate": "Progressez en mats aidés", "Give three checks": "Donnez trois échecs", diff --git a/client/src/translations/rules/Omega/en.pug b/client/src/translations/rules/Omega/en.pug new file mode 100644 index 00000000..8575b101 --- /dev/null +++ b/client/src/translations/rules/Omega/en.pug @@ -0,0 +1,48 @@ +p.boxed. + 10x10 board with four extra corners and two new piece. + Orthodox rules with a few exceptions. + +figure.diagram-container + .diagram + | fen:wxxxxxxxxxxw/xcrnbqkbnrcx/xppppppppppx/x91x/x91x/x91x/x91x/x91x/x91x/xPPPPPPPPPPx/xCRNBQKBNRCx/WxxxxxxxxxxW: + figcaption Initial deterministic position + +p. + The board is unusually shaped: it's a 10x10 square with four extra corners + added. This open new tactical possibilites: a king can hide there for + example. + +p There are two new pieces in the corner and on the first rank: +ul + li Champion (C) = dabbabah + alfil + wazir, + li Wizard (W) = camel + ferz. +p. + Since the pieces mentioned to describe champion and wizard's movements might + be unknown, here is how they move: + +figure.diagram-container + .diagram.diag12 + | fen:1xxxxxxxxxx1/x91x/x91x/x91x/x91x/x91x/x4C5x/x91x/x91x/x91x/x91x/1xxxxxxxxxx1 d8,f8,h8,f7,d6,e6,g6,h6,f5,d4,f4,h4: + .diagram.diag22 + | fen:1xxxxxxxxxx1/x91x/x91x/x91x/x91x/x4W5x/x91x/x91x/x91x/x91x/x91x/1xxxxxxxxxx1 e10,g10,c8,i8,c6,i6,e4,g4,e8,g8,e6,g6: + figcaption Left: movements of the champion. Right: movements of the wizard. + +p. + The pawns behave as in orthodox chess, with a possible initial two or three + squares jump. They promote in any piece. + Castling is permitted: the king moves two squares to the right for a small + castle or to the left for a large castle. + +h3 Source + +p + | The + a(href="https://omegachess.com/") official website + | , which has a + a(href="http://www.omegachess.ru/omega/") playing area + | , although the Omega chess team purpose is seemingly to develop the game + | with real board and pieces - which they sell on the website. See also the + a(href="https://en.wikipedia.org/wiki/Omega_Chess") Wikipedia page + | . + +p Inventor: Daniel MacDonald (1995) diff --git a/client/src/translations/rules/Omega/es.pug b/client/src/translations/rules/Omega/es.pug new file mode 100644 index 00000000..f754b6cb --- /dev/null +++ b/client/src/translations/rules/Omega/es.pug @@ -0,0 +1,51 @@ +p.boxed. + Tablero 10x10 con cuatro esquinas adicionales. + Reglas ortodoxas se aplican con algunas excepciones. + +figure.diagram-container + .diagram + | fen:wxxxxxxxxxxw/xcrnbqkbnrcx/xppppppppppx/x91x/x91x/x91x/x91x/x91x/x91x/xPPPPPPPPPPx/xCRNBQKBNRCx/WxxxxxxxxxxW: + figcaption Posición determinista inicial + +p. + El tablero de ajedrez tiene una forma inusual: es un cuadrado de 10x10 que + tenemos agregó cuatro esquinas. Esto abre nuevas posibilidades tácticas: un + rey puede esconderse allí, por ejemplo. + +p Dos piezas nuevas están en las esquinas y en la primera fila: +ul + li Campeón (C) = dabbabah + alfil + wazir, + li brujo (W) = camello + ferz. +p. + Desde las piezas mencionadas para describir los movimientos del campeón y + del hechicero pueden ser desconocido, así es como se mueven: + +figure.diagram-container + .diagram.diag12 + | fen:1xxxxxxxxxx1/x91x/x91x/x91x/x91x/x91x/x4C5x/x91x/x91x/x91x/x91x/1xxxxxxxxxx1 d8,f8,h8,f7,d6,e6,g6,h6,f5,d4,f4,h4: + .diagram.diag22 + | fen:1xxxxxxxxxx1/x91x/x91x/x91x/x91x/x4W5x/x91x/x91x/x91x/x91x/x91x/1xxxxxxxxxx1 e10,g10,c8,i8,c6,i6,e4,g4,e8,g8,e6,g6: + figcaption. + Izquierda: movimiento del campeón. Derecha: movimiento del hechicero. + +p. + Los peones se comportan como en el ajedrez ortodoxo, con un posible salto + iniciales dos o tres cajas. Se promocionan en cualquier habitación. + El enroque es posible: el rey mueve dos casillas a la derecha para + un enroque pequeño, o hacia la izquierda para un enroque grande. + +h3 Fuente + +p + | El + a(href="https://www.chessvariants.com/contests/10/tencubedchess.html") + | sitio web oficial + | , quien tiene un + a(href="http://www.omegachess.ru/omega/") área de juego + | , aunque el objetivo del equipo de ajedrez Omega parece ser desarrollar + | el juego con tableros de ajedrez y piezas reales, que venden en el sitio. + | Ver también la + a(href="https://en.wikipedia.org/wiki/Omega_Chess") página Wikipedia + | . + +p Inventor: Daniel MacDonald (1995) diff --git a/client/src/translations/rules/Omega/fr.pug b/client/src/translations/rules/Omega/fr.pug new file mode 100644 index 00000000..723dc628 --- /dev/null +++ b/client/src/translations/rules/Omega/fr.pug @@ -0,0 +1,51 @@ +p.boxed. + Échiquier 10x10 avec quatre coins supplémentaires. + Les règles orthodoxes s'appliquent avec quelques exceptions. + +figure.diagram-container + .diagram + | fen:wxxxxxxxxxxw/xcrnbqkbnrcx/xppppppppppx/x91x/x91x/x91x/x91x/x91x/x91x/xPPPPPPPPPPx/xCRNBQKBNRCx/WxxxxxxxxxxW: + figcaption Position initiale déterministe + +p. + L'échiquier a une forme inhébituelle : c'est un carré 10x10 auquel on a + ajouté quatre coins. Cela ouvre de nouvelles possibilités tactiques : un roi + peut venir s'y cacher, par exemple. + +p Deux nouvelles pièces se trouvent dans les coins et sur la première rangée : +ul + li Champion (C) = dabbabah + alfil + wazir, + li Sorcier (W) = chameau + ferz. +p. + Puisque les pièces mentionnées pour décrire les déplacements du champion et + du sorcier peuvent être inconnues, voici comment elles se déplacent : + +figure.diagram-container + .diagram.diag12 + | fen:1xxxxxxxxxx1/x91x/x91x/x91x/x91x/x91x/x4C5x/x91x/x91x/x91x/x91x/1xxxxxxxxxx1 d8,f8,h8,f7,d6,e6,g6,h6,f5,d4,f4,h4: + .diagram.diag22 + | fen:1xxxxxxxxxx1/x91x/x91x/x91x/x91x/x4W5x/x91x/x91x/x91x/x91x/x91x/1xxxxxxxxxx1 e10,g10,c8,i8,c6,i6,e4,g4,e8,g8,e6,g6: + figcaption. + Gauche : déplacements du champion. Droite : déplacements du sorcier. + +p. + Les pions se comportent comme aux échecs orthodoxes, avec un éventuel saut + initial de deux ou trois cases. Ils se promeuvent en n'importe quelle pièce. + Le roque est possible : le roi se déplace de deux cases vers la droite pour + un petit roque, ou vers la gauche pour un grand roque. + +h3 Source + +p + | Le + a(href="https://www.chessvariants.com/contests/10/tencubedchess.html") + | site officiel + | , qui a une + a(href="http://www.omegachess.ru/omega/") zone de jeu + | , bien que l'objectif de l'équipe d'Omega chess semble être de développer + | le jeu avec des vrais échiquiers et pièces - qu'ils vendent sur le site. + | Voir aussi la + a(href="https://en.wikipedia.org/wiki/Omega_Chess") page Wikipedia + | . + +p Inventeur : Daniel MacDonald (1995) diff --git a/client/src/translations/rules/Tencubed/en.pug b/client/src/translations/rules/Tencubed/en.pug new file mode 100644 index 00000000..fb5dbd8c --- /dev/null +++ b/client/src/translations/rules/Tencubed/en.pug @@ -0,0 +1,38 @@ +p.boxed. + 10x10 board with four new pieces. Orthodox rules with a few exceptions. + +figure.diagram-container + .diagram + | fen:2cwamwc2/1rnbqkbnr1/pppppppppp/91/91/91/91/PPPPPPPPPP/1RNBQKBNR1/2CWAMWC2: + figcaption Initial deterministic position + +p There are four new pieces on the first rank: +ul + li Marshall (M) = rook + knight, + li Archbishop (A) = bishop + knight, + li Champion (C) = dabbabah + alfil + wazir, + li Wizard (W) = camel + ferz. +p. + Since the pieces mentioned to describe champion and wizard's movements might + be unknown, here is how they move: + +figure.diagram-container + .diagram.diag12 + | fen:91/91/91/91/91/4C5/91/91/91/91 c7,e7,g7,e6,c5,d5,f5,g5,e4,c3,e3,g3: + .diagram.diag22 + | fen:91/91/91/91/4W5/91/91/91/91/91 d9,f9,b7,h7,b5,h5,d3,f3,d7,f7,d5,f5: + figcaption Left: movements of the champion. Right: movements of the wizard. + +p. + The pawns behave as in orthodox chess, with a possible initial two-squares + jump. They promote in queen, marshall or archbishop. + There is no castling. + +h3 Source + +p + a(href="https://www.chessvariants.com/contests/10/tencubedchess.html") + | Tencubed chess + |  on chessvariants.com. + +p Inventor: David Paulowich (2005) diff --git a/client/src/translations/rules/Tencubed/es.pug b/client/src/translations/rules/Tencubed/es.pug new file mode 100644 index 00000000..9a36c48d --- /dev/null +++ b/client/src/translations/rules/Tencubed/es.pug @@ -0,0 +1,41 @@ +p.boxed. + Tablero de 10x10 con cuatro piezas nuevas. Reglas ortodoxas se aplican con + algunas excepciones. + +figure.diagram-container + .diagram + | fen:2cwamwc2/1rnbqkbnr1/pppppppppp/91/91/91/91/PPPPPPPPPP/1RNBQKBNR1/2CWAMWC2: + figcaption Posición determinista inicial + +p Cuatro piezas nuevas están en la primera fila: +ul + li Mariscal (M) = torre + jinete, + li Arzobispo (A) = loco + jinete, + li Campeón (C) = dabbabah + alfil + wazir, + li brujo (W) = camello + ferz. +p. + Desde las piezas mencionadas para describir los movimientos del campeón y + del hechicero pueden ser desconocido, así es como se mueven: + +figure.diagram-container + .diagram.diag12 + | fen:91/91/91/91/91/4C5/91/91/91/91 c7,e7,g7,e6,c5,d5,f5,g5,e4,c3,e3,g3: + .diagram.diag22 + | fen:91/91/91/91/4W5/91/91/91/91/91 d9,f9,b7,h7,b5,h5,d3,f3,d7,f7,d5,f5: + figcaption. + Izquierda: movimiento del campeón. Derecha: movimiento del hechicero. + +p. + Los peones se comportan como en el ajedrez ortodoxo, con un posible salto + inicial de dos cajas. Se promocionan como dama, mariscal o arzobispo. + No hay enroque. + +h3 Fuente + +p + | La + a(href="https://www.chessvariants.com/contests/10/tencubedchess.html") + | variante Tencubed + |  en chessvariants.com. + +p Inventor: David Paulowich (2005) diff --git a/client/src/translations/rules/Tencubed/fr.pug b/client/src/translations/rules/Tencubed/fr.pug new file mode 100644 index 00000000..216b4fa3 --- /dev/null +++ b/client/src/translations/rules/Tencubed/fr.pug @@ -0,0 +1,41 @@ +p.boxed. + Échiquier 10x10 avec quatre nouvelles pièces. Les règles orthodoxes + s'appliquent avec quelques exceptions. + +figure.diagram-container + .diagram + | fen:2cwamwc2/1rnbqkbnr1/pppppppppp/91/91/91/91/PPPPPPPPPP/1RNBQKBNR1/2CWAMWC2: + figcaption Position initiale déterministe + +p Quatre nouvelles pièces se trouvent sur la première rangée : +ul + li Maréchal (M) = tour + cavalier, + li Archevêque (A) = fou + cavalier, + li Champion (C) = dabbabah + alfil + wazir, + li Sorcier (W) = chameau + ferz. +p. + Puisque les pièces mentionnées pour décrire les déplacements du champion et + du sorcier peuvent être inconnues, voici comment elles se déplacent : + +figure.diagram-container + .diagram.diag12 + | fen:91/91/91/91/91/4C5/91/91/91/91 c7,e7,g7,e6,c5,d5,f5,g5,e4,c3,e3,g3: + .diagram.diag22 + | fen:91/91/91/91/4W5/91/91/91/91/91 d9,f9,b7,h7,b5,h5,d3,f3,d7,f7,d5,f5: + figcaption. + Gauche : déplacements du champion. Droite : déplacements du sorcier. + +p. + Les pions se comportent comme aux échecs orthodoxes, avec un éventuel saut + initial de deux cases. Ils se promeuvent en dame, maréchal ou archevêque. + Il n'y a pas de roque. + +h3 Source + +p + | La + a(href="https://www.chessvariants.com/contests/10/tencubedchess.html") + | variante Tencubed + |  sur chessvariants.com. + +p Inventeur : David Paulowich (2005) diff --git a/client/src/variants/Cannibal.js b/client/src/variants/Cannibal.js index 3a87a723..f7a95149 100644 --- a/client/src/variants/Cannibal.js +++ b/client/src/variants/Cannibal.js @@ -207,8 +207,9 @@ export class CannibalRules extends ChessRules { this.kingPos[c][0] = move.appear[0].x; this.kingPos[c][1] = move.appear[0].y; this.castleFlags[c] = [V.size.y, V.size.y]; - return; } + // Next call is still required because the king may eat an opponent's rook + // TODO: castleFlags will be turned off twice then. super.updateCastleFlags(move, piece); } diff --git a/client/src/variants/Enpassant.js b/client/src/variants/Enpassant.js index 5ff4c956..53f5c4fb 100644 --- a/client/src/variants/Enpassant.js +++ b/client/src/variants/Enpassant.js @@ -4,6 +4,7 @@ export class EnpassantRules extends ChessRules { static IsGoodEnpassant(enpassant) { if (enpassant != "-") { const squares = enpassant.split(","); + if (squares.length > 2) return false; for (let sq of squares) { const ep = V.SquareToCoords(sq); if (isNaN(ep.x) || !V.OnBoard(ep)) return false; @@ -17,11 +18,34 @@ export class EnpassantRules extends ChessRules { if (typeof moveOrSquare === "string") { const square = moveOrSquare; if (square == "-") return undefined; - let res = []; - square.split(",").forEach(sq => { - res.push(V.SquareToCoords(sq)); - }); - return res; + // Expand init + dest squares into a full path: + const init = V.SquareToCoords(square.substr(0, 2)); + let newPath = [init]; + if (square.length == 2) return newPath; + const dest = V.SquareToCoords(square.substr(2)); + const delta = ['x', 'y'].map(i => Math.abs(dest[i] - init[i])); + // Check if it's a knight(rider) movement: + let step = [0, 0]; + if (delta[0] > 0 && delta[1] > 0 && delta[0] != delta[1]) { + // Knightrider + const minShift = Math.min(delta[0], delta[1]); + step[0] = (dest.x - init.x) / minShift; + step[1] = (dest.y - init.y) / minShift; + } else { + // "Sliders" + step = ['x', 'y'].map((i, idx) => { + return (dest[i] - init[i]) / delta[idx] || 0 + }); + } + let x = init.x + step[0], + y = init.y + step[1]; + while (x != dest.x || y != dest.y) { + newPath.push({ x: x, y: y }); + x += step[0]; + y += step[1]; + } + newPath.push(dest); + return newPath; } // Argument is a move: all intermediate squares are en-passant candidates, // except if the moving piece is a king. @@ -52,7 +76,7 @@ export class EnpassantRules extends ChessRules { x != move.end.x || y != move.end.y; x += step[0], y += step[1] ) { - res.push({x:x, y:y}); + res.push({ x: x, y: y }); } // Add final square to know which piece is taken en passant: res.push(move.end); @@ -62,11 +86,10 @@ export class EnpassantRules extends ChessRules { getEnpassantFen() { const L = this.epSquares.length; if (!this.epSquares[L - 1]) return "-"; //no en-passant - let res = ""; - this.epSquares[L - 1].forEach(sq => { - res += V.CoordsToSquare(sq) + ","; - }); - return res.slice(0, -1); //remove last comma + const epsq = this.epSquares[L - 1]; + if (epsq.length <= 2) return epsq.map(V.CoordsToSquare).join(""); + // Condensate path: just need initial and final squares: + return V.CoordsToSquare(epsq[0]) + V.CoordsToSquare(epsq[epsq.length - 1]); } getPotentialMovesFrom([x, y]) { diff --git a/client/src/variants/Grand.js b/client/src/variants/Grand.js index e92896b8..aeb7b6f4 100644 --- a/client/src/variants/Grand.js +++ b/client/src/variants/Grand.js @@ -9,20 +9,13 @@ export class GrandRules extends ChessRules { if (!ChessRules.IsGoodFen(fen)) return false; const fenParsed = V.ParseFen(fen); // 5) Check captures - if (!fenParsed.captured || !fenParsed.captured.match(/^[0-9]{14,14}$/)) + if (!fenParsed.captured || !fenParsed.captured.match(/^[0-9]{12,12}$/)) return false; return true; } static IsGoodEnpassant(enpassant) { - if (enpassant != "-") { - const squares = enpassant.split(","); - if (squares.length > 2) return false; - for (let sq of squares) { - const ep = V.SquareToCoords(sq); - if (isNaN(ep.x) || !V.OnBoard(ep)) return false; - } - } + if (enpassant != "-") return !!enpassant.match(/^([a-j][0-9]{1,2},?)+$/); return true; } @@ -139,7 +132,7 @@ export class GrandRules extends ChessRules { } ]; if (sx + 2 * step != ex) { - //3-squares move + // 3-squares jump res.push({ x: sx + 2 * step, y: sy @@ -225,7 +218,7 @@ export class GrandRules extends ChessRules { // En passant const Lep = this.epSquares.length; const epSquare = this.epSquares[Lep - 1]; - if (epSquare) { + if (!!epSquare) { for (let epsq of epSquare) { // TODO: some redundant checks if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) { diff --git a/client/src/variants/Monster.js b/client/src/variants/Monster.js index 177f71d1..cffc49e7 100644 --- a/client/src/variants/Monster.js +++ b/client/src/variants/Monster.js @@ -105,10 +105,9 @@ export class MonsterRules extends ChessRules { // Definition of 'c' in base class doesn't work: const c = move.vanish[0].c; const piece = move.vanish[0].p; - if (piece == V.KING && move.appear.length > 0) { + if (piece == V.KING) { this.kingPos[c][0] = move.appear[0].x; this.kingPos[c][1] = move.appear[0].y; - return; } this.updateCastleFlags(move, piece); } diff --git a/client/src/variants/Omega.js b/client/src/variants/Omega.js new file mode 100644 index 00000000..fa95f5e3 --- /dev/null +++ b/client/src/variants/Omega.js @@ -0,0 +1,450 @@ +import { ChessRules, Move, PiPo } from "@/base_rules"; +import { ArrayFun } from "@/utils/array"; +import { randInt } from "@/utils/alea"; + +export class OmegaRules extends ChessRules { + static get PawnSpecs() { + return Object.assign( + {}, + ChessRules.PawnSpecs, + { + initShift: { w: 2, b: 2 }, + threeSquares: true, + promotions: + ChessRules.PawnSpecs.promotions.concat([V.CHAMPION, V.WIZARD]) + } + ); + } + + // For space between corners: + static get NOTHING() { + return "xx"; + } + + static board2fen(b) { + if (b[0] == 'x') return 'x'; + return ChessRules.board2fen(b); + } + + static fen2board(f) { + if (f == 'x') return V.NOTHING; + return ChessRules.fen2board(f); + } + + getPpath(b) { + if (b[0] == 'x') return "Omega/nothing"; + return ([V.CHAMPION, V.WIZARD].includes(b[1]) ? "Omega/" : "") + b; + } + + // NOTE: keep this extensive check because the board has holes + static IsGoodEnpassant(enpassant) { + if (enpassant != "-") { + const squares = enpassant.split(","); + if (squares.length > 2) return false; + for (let sq of squares) { + const ep = V.SquareToCoords(sq); + if (isNaN(ep.x) || !V.OnBoard(ep)) return false; + } + } + return true; + } + + static get size() { + return { x: 12, y: 12 }; + } + + static OnBoard(x, y) { + return ( + (x >= 1 && x <= 10 && y >= 1 && y <= 10) || + (x == 11 && [0, 11].includes(y)) || + (x == 0 && [0, 11].includes(y)) + ); + } + + // Dabbabah + alfil + wazir + static get CHAMPION() { + return "c"; + } + + // Camel + ferz + static get WIZARD() { + return "w"; + } + + static get PIECES() { + return ChessRules.PIECES.concat([V.CHAMPION, V.WIZARD]); + } + + static get steps() { + return Object.assign( + {}, + ChessRules.steps, + { + w: [ + [-3, -1], + [-3, 1], + [-1, -3], + [-1, 3], + [1, -3], + [1, 3], + [3, -1], + [3, 1], + [-1, -1], + [-1, 1], + [1, -1], + [1, 1] + ], + c: [ + [1, 0], + [-1, 0], + [0, 1], + [0, -1], + [2, 2], + [2, -2], + [-2, 2], + [-2, -2], + [-2, 0], + [0, -2], + [2, 0], + [0, 2] + ] + } + ); + } + + static GenRandInitFen(randomness) { + if (randomness == 0) { + return ( + "wxxxxxxxxxxw/xcrnbqkbnrcx/xppppppppppx/x91x/x91x/x91x/" + + "x91x/x91x/x91x/xPPPPPPPPPPx/xCRNBQKBNRCx/WxxxxxxxxxxW " + + "w 0 cjcj -" + ); + } + + let pieces = { w: new Array(10), b: new Array(10) }; + let flags = ""; + // Shuffle pieces on first (and last rank if randomness == 2) + for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + flags += flags; + break; + } + + let positions = ArrayFun.range(10); + + // Get random squares for bishops + let randIndex = 2 * randInt(5); + const bishop1Pos = positions[randIndex]; + // The second bishop must be on a square of different color + let randIndex_tmp = 2 * randInt(5) + 1; + const bishop2Pos = positions[randIndex_tmp]; + positions.splice(Math.max(randIndex, randIndex_tmp), 1); + positions.splice(Math.min(randIndex, randIndex_tmp), 1); + + // Get random squares for champions + randIndex = 2 * randInt(4); + let bishopSameColorPos = (bishop1Pos % 2 == 0 ? bishop1Pos : bishop2Pos); + if (randIndex >= bishopSameColorPos) randIndex += 2; + const champion1Pos = positions[randIndex]; + // The second champion must be on a square of different color + randIndex_tmp = 2 * randInt(4) + 1; + bishopSameColorPos = (bishop1Pos % 2 == 0 ? bishop1Pos : bishop2Pos); + if (randIndex_tmp >= bishopSameColorPos) randIndex_tmp += 2; + const champion2Pos = positions[randIndex_tmp]; + positions.splice(Math.max(randIndex, randIndex_tmp), 1); + positions.splice(Math.min(randIndex, randIndex_tmp), 1); + + // Get random squares for other pieces + randIndex = randInt(6); + const knight1Pos = positions[randIndex]; + positions.splice(randIndex, 1); + randIndex = randInt(5); + const knight2Pos = positions[randIndex]; + positions.splice(randIndex, 1); + + randIndex = randInt(4); + const queenPos = positions[randIndex]; + positions.splice(randIndex, 1); + + // Rooks and king positions are now fixed + const rook1Pos = positions[0]; + const kingPos = positions[1]; + const rook2Pos = positions[2]; + + pieces[c][champion1Pos] = "c"; + 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"; + pieces[c][champion2Pos] = "c"; + flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos); + } + // Add turn + flags + enpassant + return ( + "wxxxxxxxxxxw/" + + "x" + pieces["b"].join("") + + "x/xppppppppppx/x91x/x91x/x91x/x91x/x91x/x91x/xPPPPPPPPPPx/x" + + pieces["w"].join("").toUpperCase() + "x" + + "/WxxxxxxxxxxW " + + "w 0 " + flags + " -" + ); + } + + // There may be 2 enPassant squares (if pawn jump 3 squares) + getEnpassantFen() { + const L = this.epSquares.length; + if (!this.epSquares[L - 1]) return "-"; //no en-passant + let res = ""; + this.epSquares[L - 1].forEach(sq => { + res += V.CoordsToSquare(sq) + ","; + }); + return res.slice(0, -1); //remove last comma + } + + // En-passant after 2-sq or 3-sq jumps + getEpSquare(moveOrSquare) { + if (!moveOrSquare) return undefined; + if (typeof moveOrSquare === "string") { + const square = moveOrSquare; + if (square == "-") return undefined; + let res = []; + square.split(",").forEach(sq => { + res.push(V.SquareToCoords(sq)); + }); + return res; + } + // Argument is a move: + const move = moveOrSquare; + const [sx, sy, ex] = [move.start.x, move.start.y, move.end.x]; + if (this.getPiece(sx, sy) == V.PAWN && Math.abs(sx - ex) >= 2) { + const step = (ex - sx) / Math.abs(ex - sx); + let res = [ + { + x: sx + step, + y: sy + } + ]; + if (sx + 2 * step != ex) { + // 3-squares jump + res.push({ + x: sx + 2 * step, + y: sy + }); + } + return res; + } + return undefined; //default + } + + getPotentialMovesFrom([x, y]) { + switch (this.getPiece(x, y)) { + case V.CHAMPION: + return this.getPotentialChampionMoves([x, y]); + case V.WIZARD: + return this.getPotentialWizardMoves([x, y]); + default: + return super.getPotentialMovesFrom([x, y]); + } + } + + getEnpassanCaptures([x, y], shiftX) { + const Lep = this.epSquares.length; + const epSquare = this.epSquares[Lep - 1]; + let moves = []; + if (!!epSquare) { + for (let epsq of epSquare) { + // TODO: some redundant checks + if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) { + let enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]); + // WARNING: the captured pawn may be diagonally behind us, + // if it's a 3-squares jump and we take on 1st passing square + const px = this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX; + enpassantMove.vanish.push({ + x: px, + y: epsq.y, + p: "p", + c: this.getColor(px, epsq.y) + }); + moves.push(enpassantMove); + } + } + } + return moves; + } + + getPotentialChampionMoves(sq) { + return this.getSlideNJumpMoves(sq, V.steps[V.CHAMPION], "oneStep"); + } + + getPotentialWizardMoves(sq) { + return this.getSlideNJumpMoves(sq, V.steps[V.WIZARD], "oneStep"); + } + + getCastleMoves([x, y], castleInCheck) { + const c = this.getColor(x, y); + if (x != (c == "w" ? V.size.x - 2 : 1) || y != this.INIT_COL_KING[c]) + return []; //x isn't first rank, or king has moved (shortcut) + + // Castling ? + const oppCol = V.GetOppCol(c); + let moves = []; + let i = 0; + // King, then rook: + const finalSquares = [ + [4, 5], + [8, 7] + ]; + castlingCheck: for ( + let castleSide = 0; + castleSide < 2; + castleSide++ //large, then small + ) { + if (this.castleFlags[c][castleSide] >= V.size.y) continue; + // If this code is reached, rook and king are on initial position + + // NOTE: in some variants this is not a rook + const rookPos = this.castleFlags[c][castleSide]; + if (this.board[x][rookPos] == V.EMPTY || this.getColor(x, rookPos) != c) + // Rook is not here, or changed color (see Benedict) + continue; + + // Nothing on the path of the king ? (and no checks) + const castlingPiece = this.getPiece(x, rookPos); + const finDist = finalSquares[castleSide][0] - y; + let step = finDist / Math.max(1, Math.abs(finDist)); + i = y; + do { + if ( + (!castleInCheck && this.isAttacked([x, i], oppCol)) || + (this.board[x][i] != V.EMPTY && + // NOTE: next check is enough, because of chessboard constraints + (this.getColor(x, i) != c || + ![V.KING, castlingPiece].includes(this.getPiece(x, i)))) + ) { + continue castlingCheck; + } + i += step; + } while (i != finalSquares[castleSide][0]); + + // Nothing on the path to the rook? + step = castleSide == 0 ? -1 : 1; + for (i = y + step; i != rookPos; i += step) { + if (this.board[x][i] != V.EMPTY) continue castlingCheck; + } + + // Nothing on final squares, except maybe king and castling rook? + for (i = 0; i < 2; i++) { + if ( + finalSquares[castleSide][i] != rookPos && + this.board[x][finalSquares[castleSide][i]] != V.EMPTY && + ( + this.getPiece(x, finalSquares[castleSide][i]) != V.KING || + this.getColor(x, finalSquares[castleSide][i]) != c + ) + ) { + continue castlingCheck; + } + } + + // If this code is reached, castle is valid + moves.push( + new Move({ + appear: [ + new PiPo({ + x: x, + y: finalSquares[castleSide][0], + p: V.KING, + c: c + }), + new PiPo({ + x: x, + y: finalSquares[castleSide][1], + p: castlingPiece, + c: c + }) + ], + vanish: [ + new PiPo({ x: x, y: y, p: V.KING, c: c }), + new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c }) + ], + end: + Math.abs(y - rookPos) <= 2 + ? { x: x, y: rookPos } + : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) } + }) + ); + } + + return moves; + } + + isAttacked(sq, color) { + return ( + super.isAttacked(sq, color) || + this.isAttackedByChampion(sq, color) || + this.isAttackedByWizard(sq, color) + ); + } + + isAttackedByWizard(sq, color) { + return ( + this.isAttackedBySlideNJump( + sq, color, V.WIZARD, V.steps[V.WIZARD], "oneStep") + ); + } + + isAttackedByChampion(sq, color) { + return ( + this.isAttackedBySlideNJump( + sq, color, V.CHAMPION, V.steps[V.CHAMPION], "oneStep") + ); + } + + updateCastleFlags(move, piece) { + const c = V.GetOppCol(this.turn); + const firstRank = (c == "w" ? V.size.x - 2 : 1); + // Update castling flags if rooks are moved + const oppCol = this.turn; + const oppFirstRank = V.size.x - 1 - firstRank; + if (piece == V.KING) + this.castleFlags[c] = [V.size.y, V.size.y]; + else if ( + move.start.x == firstRank && //our rook moves? + this.castleFlags[c].includes(move.start.y) + ) { + const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1); + this.castleFlags[c][flagIdx] = V.size.y; + } + // NOTE: not "else if" because a rook could take an opposing rook + if ( + move.end.x == oppFirstRank && //we took opponent rook? + this.castleFlags[oppCol].includes(move.end.y) + ) { + const flagIdx = (move.end.y == this.castleFlags[oppCol][0] ? 0 : 1); + this.castleFlags[oppCol][flagIdx] = V.size.y; + } + } + + static get SEARCH_DEPTH() { + return 2; + } + + // Values taken from https://omegachess.com/strategy.htm + static get VALUES() { + return { + p: 1, + n: 2, + b: 4, + r: 6, + q: 12, + w: 4, + c: 4, + k: 1000 + }; + } +}; diff --git a/client/src/variants/Schess.js b/client/src/variants/Schess.js index 0d12b749..06644c7e 100644 --- a/client/src/variants/Schess.js +++ b/client/src/variants/Schess.js @@ -291,7 +291,6 @@ export class SchessRules extends ChessRules { ([V.HAWK, V.ELEPHANT, V.NOTHING].includes(move.appear[0].p) ? 1 : 0); this.kingPos[color][0] = move.appear[shift].x; this.kingPos[color][1] = move.appear[shift].y; - return; } this.updateCastleFlags(move, piece); diff --git a/client/src/variants/Tencubed.js b/client/src/variants/Tencubed.js new file mode 100644 index 00000000..99bff135 --- /dev/null +++ b/client/src/variants/Tencubed.js @@ -0,0 +1,245 @@ +import { ChessRules } from "@/base_rules"; +import { ArrayFun } from "@/utils/array"; +import { shuffle } from "@/utils/alea"; + +export class TencubedRules extends ChessRules { + static get PawnSpecs() { + return Object.assign( + {}, + ChessRules.PawnSpecs, + { + initShift: { w: 2, b: 2 }, + promotions: [V.QUEEN, V.MARSHALL, V.ARCHBISHOP] + } + ); + } + + static get HasFlags() { + return false; + } + + getPpath(b) { + return ( + [V.MARSHALL, V.ARCHBISHOP, V.CHAMPION, V.WIZARD].includes(b[1]) + ? "Tencubed/" + : "" + ) + b; + } + + static get size() { + return { x: 10, y: 10 }; + } + + // Rook + knight: + static get MARSHALL() { + return "m"; + } + + // Bishop + knight + static get ARCHBISHOP() { + return "a"; + } + + // Dabbabah + alfil + wazir + static get CHAMPION() { + return "c"; + } + + // Camel + ferz + static get WIZARD() { + return "w"; + } + + static get PIECES() { + return ( + ChessRules.PIECES + .concat([V.MARSHALL, V.ARCHBISHOP, V.CHAMPION, V.WIZARD]) + ); + } + + static get steps() { + return Object.assign( + {}, + ChessRules.steps, + { + w: [ + [-3, -1], + [-3, 1], + [-1, -3], + [-1, 3], + [1, -3], + [1, 3], + [3, -1], + [3, 1], + [-1, -1], + [-1, 1], + [1, -1], + [1, 1] + ], + c: [ + [1, 0], + [-1, 0], + [0, 1], + [0, -1], + [2, 2], + [2, -2], + [-2, 2], + [-2, -2], + [-2, 0], + [0, -2], + [2, 0], + [0, 2] + ] + } + ); + } + + static GenRandInitFen(randomness) { + if (randomness == 0) { + return ( + "2cwamwc2/1rnbqkbnr1/pppppppppp/91/91/" + + "91/91/PPPPPPPPPP/1RNBQKBNR1/2CWAMWC2/ " + + "w 0 bibi -" + ); + } + + const baseFen = V.ParseFen(ChessRules.GenRandInitFen(randomness)); + const positionParts = baseFen.position.split("/"); + const bFen = ( + "1" + positionParts[0] + + "1/pppppppppp/91/91/91/91/PPPPPPPPPP/1" + + positionParts[7] + "1" + ); + // Now just obtain randomized new pieces placements: + let pieces = { w: new Array(6), b: new Array(6) }; + for (let c of ["w", "b"]) { + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + break; + } + + let positions = shuffle(ArrayFun.range(6)); + const composition = ['w', 'w', 'c', 'c', 'a', 'm']; + let rem2 = positions[0] % 2; + if (rem2 == positions[1] % 2) { + // Fix wizards (on different colors) + for (let i=4; i<6; i++) { + if (positions[i] % 2 != rem2) + [positions[1], positions[i]] = [positions[i], positions[1]]; + } + } + rem2 = positions[2] % 2; + if (rem2 == positions[3] % 2) { + // Fix champions too: [NOTE: positions[4] & [5] should do] + for (let i=4; i<6; i++) { + if (positions[i] % 2 != rem2) + [positions[3], positions[i]] = [positions[i], positions[3]]; + } + } + for (let i = 0; i < 9; i++) pieces[c][positions[i]] = composition[i]; + } + return ( + "2" + pieces["b"].join("") + "2/" + + bFen + + "/2" + pieces["w"].join("").toUpperCase() + "2" + + " w 0 -" + ); + } + + getPotentialMovesFrom([x, y]) { + switch (this.getPiece(x, y)) { + case V.MARSHALL: + return this.getPotentialMarshallMoves([x, y]); + case V.ARCHBISHOP: + return this.getPotentialArchbishopMoves([x, y]); + case V.CHAMPION: + return this.getPotentialChampionMoves([x, y]); + case V.WIZARD: + return this.getPotentialWizardMoves([x, y]); + default: + return super.getPotentialMovesFrom([x, y]); + } + } + + getPotentialMarshallMoves(sq) { + return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat( + this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep") + ); + } + + getPotentialArchbishopMoves(sq) { + return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat( + this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep") + ); + } + + getPotentialChampionMoves(sq) { + return this.getSlideNJumpMoves(sq, V.steps[V.CHAMPION], "oneStep"); + } + + getPotentialWizardMoves(sq) { + return this.getSlideNJumpMoves(sq, V.steps[V.WIZARD], "oneStep"); + } + + isAttacked(sq, color) { + return ( + super.isAttacked(sq, color) || + this.isAttackedByMarshall(sq, color) || + this.isAttackedByArchbishop(sq, color) || + this.isAttackedByChampion(sq, color) || + this.isAttackedByWizard(sq, color) + ); + } + + isAttackedByMarshall(sq, color) { + return ( + this.isAttackedBySlideNJump(sq, color, V.MARSHALL, V.steps[V.ROOK]) || + this.isAttackedBySlideNJump( + sq, + color, + V.MARSHALL, + V.steps[V.KNIGHT], + "oneStep" + ) + ); + } + + isAttackedByArchbishop(sq, color) { + return ( + this.isAttackedBySlideNJump(sq, color, V.CARDINAL, V.steps[V.BISHOP]) || + this.isAttackedBySlideNJump( + sq, + color, + V.CARDINAL, + V.steps[V.KNIGHT], + "oneStep" + ) + ); + } + + isAttackedByWizard(sq, color) { + return ( + this.isAttackedBySlideNJump( + sq, color, V.WIZARD, V.steps[V.WIZARD], "oneStep") + ); + } + + isAttackedByChampion(sq, color) { + return ( + this.isAttackedBySlideNJump( + sq, color, V.CHAMPION, V.steps[V.CHAMPION], "oneStep") + ); + } + + static get SEARCH_DEPTH() { + return 2; + } + + static get VALUES() { + return Object.assign( + {}, + ChessRules.VALUES, + { c: 4, w: 3, a: 6, m: 8 } + ); + } +}; diff --git a/client/src/variants/Wildebeest.js b/client/src/variants/Wildebeest.js index 8664e5ed..526da1a7 100644 --- a/client/src/variants/Wildebeest.js +++ b/client/src/variants/Wildebeest.js @@ -39,14 +39,7 @@ export class WildebeestRules extends ChessRules { } static IsGoodEnpassant(enpassant) { - if (enpassant != "-") { - const squares = enpassant.split(","); - if (squares.length > 2) return false; - for (let sq of squares) { - const ep = V.SquareToCoords(sq); - if (isNaN(ep.x) || !V.OnBoard(ep)) return false; - } - } + if (enpassant != "-") return !!enpassant.match(/^([a-j][0-9]{1,2},?)+$/); return true; } @@ -162,7 +155,7 @@ export class WildebeestRules extends ChessRules { // En passant const Lep = this.epSquares.length; const epSquare = this.epSquares[Lep - 1]; - if (epSquare) { + if (!!epSquare) { for (let epsq of epSquare) { // TODO: some redundant checks if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) { diff --git a/server/db/populate.sql b/server/db/populate.sql index 83ba4cc0..2fb1b0d0 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -44,6 +44,7 @@ insert or ignore into Variants (name, description) values ('Magnetic', 'Laws of attraction'), ('Marseille', 'Double moves'), ('Monster', 'White move twice'), + ('Omega', 'A wizard in the corner'), ('Orda', 'Mongolian Horde'), ('Parachute', 'Landing on the board'), ('Perfect', 'Powerful pieces'), @@ -56,6 +57,7 @@ insert or ignore into Variants (name, description) values ('Shatranj', 'Ancient rules'), ('Suicide', 'Lose all pieces'), ('Suction', 'Attract opposite king'), + ('Tencubed', 'Four new pieces'), ('Threechecks', 'Give three checks'), ('Twokings', 'Two kings'), ('Upsidedown', 'Board upside down'), -- 2.44.0