From: Benjamin Auder Date: Thu, 21 Jan 2021 18:18:09 +0000 (+0100) Subject: Fix Emergo. Add Avalam 1 & 2 X-Git-Url: https://git.auder.net/js/pieces/scripts/current/doc/screen_timer.png?a=commitdiff_plain;h=b27300c2951adcc67acbf4b822267dc102895ce3;p=vchess.git Fix Emergo. Add Avalam 1 & 2 --- diff --git a/.gitattributes b/.gitattributes index ac66ad09..6ce89528 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ *.ico filter=fat *.pdf filter=fat *.png filter=fat +*.jpg filter=fat *.gif filter=fat *.flac filter=fat diff --git a/client/public/images/pieces/Avalam/.gitignore b/client/public/images/pieces/Avalam/.gitignore new file mode 100644 index 00000000..fa112253 --- /dev/null +++ b/client/public/images/pieces/Avalam/.gitignore @@ -0,0 +1,3 @@ +* +!generateSVG.py +!.gitignore diff --git a/client/public/images/pieces/Avalam/generateSVG.py b/client/public/images/pieces/Avalam/generateSVG.py new file mode 100755 index 00000000..d400d79b --- /dev/null +++ b/client/public/images/pieces/Avalam/generateSVG.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +# Compose each piece SVG with numbers +# https://travishorn.com/removing-parts-of-shapes-in-svg-b539a89e5649 +# https://developer.mozilla.org/fr/docs/Web/SVG/Tutoriel/Paths + +preamble = """ + +""" + +black = '' +white = '' + +digits = [ + # 1 + '') + f.write("\n") + f.write(final) + f.close() diff --git a/client/public/images/pieces/Emergo/generateSVG_composite.py b/client/public/images/pieces/Emergo/generateSVG_composite.py index 87eb55f6..915404b6 100755 --- a/client/public/images/pieces/Emergo/generateSVG_composite.py +++ b/client/public/images/pieces/Emergo/generateSVG_composite.py @@ -22,55 +22,55 @@ black_right = '' digits = { "left": [ # 1 - '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '' + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '' + '') + f.write("\n") + f.write(digits["right"][right] + ' fill="none" stroke-width="4" ' + ('stroke="red"' if colorLeft == "black" else 'stroke="orange"') + '/>') + f.write("\n") f.write(final) f.close() diff --git a/client/public/images/pieces/Emergo/generateSVG_simple.py b/client/public/images/pieces/Emergo/generateSVG_simple.py index 5cb53c9e..64533624 100755 --- a/client/public/images/pieces/Emergo/generateSVG_simple.py +++ b/client/public/images/pieces/Emergo/generateSVG_simple.py @@ -13,29 +13,29 @@ white = '' digits = [ # 1 - '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '' + '') + f.write("\n") f.write(final) f.close() diff --git a/client/public/variants/Emergo/match_2004_U-con.jpg b/client/public/variants/Emergo/match_2004_U-con.jpg new file mode 100644 index 00000000..b24969cb --- /dev/null +++ b/client/public/variants/Emergo/match_2004_U-con.jpg @@ -0,0 +1 @@ +#$# git-fat 761b5f04a60c2c2438cf2777453ba42aceb16da3 80995 diff --git a/client/src/translations/rules/Avalam1/en.pug b/client/src/translations/rules/Avalam1/en.pug index 3a33838b..64364b48 100644 --- a/client/src/translations/rules/Avalam1/en.pug +++ b/client/src/translations/rules/Avalam1/en.pug @@ -1,2 +1,33 @@ p.boxed - | TODO + | Stack towers freely, so that your color appears on top. + +p. + There are initially 48 pieces placed on a 49 squares irregular board. + "White" takes yellow pieces, and "black" gets the red ones. + +figure.diagram-container + .diagram + | fen:xxBbxxxxx/xBbBbxxxx/xbBbBbBxx/xBbBbBbBb/BbBb1bBbB/bBbBbBbBx/xxBbBbBbx/xxxxbBbBx/xxxxxbBxx: + figcaption Deterministic initial position. + +p. + At each turn, take any tower of less than 5 pieces (the number + printed on it) and put it on an adjacent tower, such that + the resulting construction isn't more than 5 units tall. + +p. + When no moves are possible anymore, the game is over. + The winner is the player having the largest number of towers + of his color (on top) + +h3 More information + +p + | Few online resources about this game. The rules are explained + | for example on + a(href="http://jeuxstrategie.free.fr/Avalam_complet.php") this page + | . See also the Avalam page on + a(href="https://www.jeuxdenim.be/jeu-Avalam") jeuxdenim.be + | . + +p Inventor: Philippe Deweys (1995) diff --git a/client/src/translations/rules/Avalam1/es.pug b/client/src/translations/rules/Avalam1/es.pug index 3a33838b..dbf46227 100644 --- a/client/src/translations/rules/Avalam1/es.pug +++ b/client/src/translations/rules/Avalam1/es.pug @@ -1,2 +1,34 @@ p.boxed - | TODO + | Apila libremente las torres, para que tu color + | aparece en la parte superior. + +p. + Inicialmente se distribuyen 48 piezas en un tablero irregular de 49 casillas. + Las "blancas" toman las piezas amarillas, + y las "negras" juegan con las rojas. + +figure.diagram-container + .diagram + | fen:xxBbxxxxx/xBbBbxxxx/xbBbBbBxx/xBbBbBbBb/BbBb1bBbB/bBbBbBbBx/xxBbBbBbx/xxxxbBbBx/xxxxxbBxx: + figcaption Posición inicial determinista. + +p. + En cada turno, tome cualquier torre con menos de 5 piezas (el número + indicado arriba) y colóquelo en una torre adyacente, de modo que + la construcción resultante no tiene más de 5 de altura. + +p. + Cuando no es posible realizar más movimientos, el juego termina. + El ganador es el jugador con más torres de su color (arriba). + +h3 Más información + +p + | Hay pocos recursos en línea sobre este juego. Se explican las reglas + | por ejemplo en + a(href="http://jeuxstrategie.free.fr/Avalam_complet.php") cette page + | . Consulte también la página de Avalam en + a(href="https://www.jeuxdenim.be/jeu-Avalam") jeuxdenim.be + | . + +p Inventor: Philippe Deweys (1995) diff --git a/client/src/translations/rules/Avalam1/fr.pug b/client/src/translations/rules/Avalam1/fr.pug index 3a33838b..3dd876ea 100644 --- a/client/src/translations/rules/Avalam1/fr.pug +++ b/client/src/translations/rules/Avalam1/fr.pug @@ -1,2 +1,34 @@ p.boxed - | TODO + | Empilez librement les tours, de manière à ce que votre couleur + | apparaisse en haut. + +p. + 48 pièces sont initialement réparties sur un plateau irrégulier de 49 cases. + Les "blancs" prennent les pièces jaunes, + et les "noirs" jouent avec les rouges. + +figure.diagram-container + .diagram + | fen:xxBbxxxxx/xBbBbxxxx/xbBbBbBxx/xBbBbBbBb/BbBb1bBbB/bBbBbBbBx/xxBbBbBbx/xxxxbBbBx/xxxxxbBxx: + figcaption Position initiale déterministe. + +p. + À chaque tour, prenez n'importe quelle tour de moins de 5 pièces (le nombre + indiqué dessus) et placez la sur une tour adjacente, de manière à ce que + la construction résultante soit de hauteur au plus 5. + +p. + Quand plus aucun coup n'est possible, la partie s'achève. + Le gagnant est le joueur ayant le plus de tours de sa couleur (en haut). + +h3 Plus d'information + +p + | Peu de ressources en ligne à propos de ce jeu. Les règles sont expliquées + | par exemple sur + a(href="http://jeuxstrategie.free.fr/Avalam_complet.php") cette page + | . Voir aussi la page Avalam sur + a(href="https://www.jeuxdenim.be/jeu-Avalam") jeuxdenim.be + | . + +p Inventeur : Philippe Deweys (1995) diff --git a/client/src/translations/rules/Avalam2/en.pug b/client/src/translations/rules/Avalam2/en.pug index 3a33838b..2126bac4 100644 --- a/client/src/translations/rules/Avalam2/en.pug +++ b/client/src/translations/rules/Avalam2/en.pug @@ -1,2 +1,7 @@ p.boxed - | TODO + | Move towers freely on top of each others, + | so that your color appears on top. + +p + a(href="/#/variants/Avalam1") Avalam1 + |  rules, on a regular 8x8 chess board. diff --git a/client/src/translations/rules/Avalam2/es.pug b/client/src/translations/rules/Avalam2/es.pug index 3a33838b..9b7311d5 100644 --- a/client/src/translations/rules/Avalam2/es.pug +++ b/client/src/translations/rules/Avalam2/es.pug @@ -1,2 +1,8 @@ p.boxed - | TODO + | Apila libremente las torres, para que tu color + | aparece en la parte superior. + +p + | Reglas de + a(href="/#/variants/Avalam1") Avalam1 + | , en un tablero de Ajedrez 8x8 estándar. diff --git a/client/src/translations/rules/Avalam2/fr.pug b/client/src/translations/rules/Avalam2/fr.pug index 3a33838b..028741ab 100644 --- a/client/src/translations/rules/Avalam2/fr.pug +++ b/client/src/translations/rules/Avalam2/fr.pug @@ -1,2 +1,8 @@ p.boxed - | TODO + | Empilez librement les tours, de manière à ce que votre couleur + | apparaisse en haut. + +p + | Règles d' + a(href="/#/variants/Avalam1") Avalam1 + | , sur un échiquier 8x8 standard. diff --git a/client/src/translations/rules/Emergo/en.pug b/client/src/translations/rules/Emergo/en.pug index 8c24bd6a..202082e3 100644 --- a/client/src/translations/rules/Emergo/en.pug +++ b/client/src/translations/rules/Emergo/en.pug @@ -1,62 +1,96 @@ p.boxed | Similar to Checkers, with prisoners stacked below capturers. +figure.diagram-container + img.img-center(src="/variants/Emergo/match_2004_U-con.jpg") + figcaption.text-center. + Game played on an orthogonal depiction of a standard 9x9 Emergo board. + +p + | Nothing disappears in this game: captured pieces become part of the + | capturing unit. Therefore, a piece or unit will always be understood + | as a combination of W white (elementary) pieces and B (elementary) black + | ones, with + br + | W >= 0, B >= 0 and W + B >= 1. + p | The 9x9 board is initially empty. | Each player receives 12 stackable pieces, "in hand". - | At each turn, a player must either + | At each turn, a player must pick an action among the followings: ul - li. - Enter a new piece on the board such that the opponent cannot capture it. - However, if a capture is already possible before the move, then - the piece can be dropped anywhere. - White cannot place a piece in the center at move 1. - li Play a move on the board, along diagonals. + li Enter a new piece (W + B = 1) on the board. + li Move a unit along diagonals, by one square. + li Capture something. + +h3 Entering moves + +p. + Introducing a piece such that the opponent can take it on next turn is + forbidden, unless another capture is already available. + +p At first move, white cannot place a piece at the central point. + +p. + If the opponent already placed all his pieces in hand, then a + "shadow piece" enters, formed by all the remaining units available + (thus in this case W + B > 1). + +h3 Captures p. - Simple moves are Ferz moves: one step diagonally. Captures work exactly as in Checkers: by jumping over a diagonally adjacent piece to land on a free square just behind. - However, the resulting situation is more complex. See below. If a capture is possible, then it must be played; in this case no piece can be introduced on the board. + If after a capture another is possible with the same piece, it must also be + played — except if that implies turning at 180 degrees. -p TODO: diagram +figure.diagram-container + .diagram.diag12 + | fen:9/9/2a@1a@4/9/4a@4/3D@5/9/5A@3/6b@2 e3: + .diagram.diag22 + | fen:9/9/2a@1a@4/9/4a@4/3D@5/4ba4/9/9: + figcaption. + Before and after black captures, jumping at the marked location. + The next white piece must be captured too. p. - Let us consider each unit as a compound entity containing W white pieces - and B black ones (initially W = 1 and B = 0 for white units, - and vice-versa for black). - Captures can then be described formally as follows. - -p. - As white: - If W1/B1 jumps over W2/B2 at square S2 to land on S1', then - W1/(B1+1) arrives on S1' and W2/(B2-1) remains on S2. + Captures can be described formally as follows. + If, as white, W1/B1 jumps over W2/B2 at square S2 to land on S1', then + W1/(B1+1) arrives on S1' while W2/(B2-1) stays on S2. If W2 = B2 - 1 = 0, nothing remains at the captured unit location. - As black: exchange W and B above. + As black: exchange W and B. p. In other words, each unit is a stack of friendly and enemy pieces, with friendly pieces on top. After each capture, the prisoners part of the - stack is incremented, while the "jailers" counterpart at the captured + stack is incremented, while the jailers quantity at the captured location decreases by one. p. When several capturing chains are available, - the player has to select one of the longest (as in Checkers). + the player must select one of the longest (as in Checkers): + +figure.diagram-container + .diagram + | fen:9/9/2a@1a@4/9/2bb1a@4/3C@5/9/9/9: + figcaption. + From https://www.mindsports.nl/: + White's only option is to capture clockwise. -p TODO: diagram (from mindsports.nl) +p. + A piece can be jumped over several times, as long as it contains + at least one enemy unit (controling it). h3 More information p - | See the + | You are invited to visit the a(href="https://www.mindsports.nl/index.php/arena/emergo/88-rules") - | Emergo page - |  on the author's website. - | Rules are also described on + | authors' website + | . The rules are also described on a(href="http://www.iggamecenter.com/info/en/emergo.html") iggamecenter - | , where you can also play this game. + | , where you can play Emergo. p Inventors: Christian Freeling and Ed van Zon (1986) diff --git a/client/src/translations/rules/Emergo/es.pug b/client/src/translations/rules/Emergo/es.pug index 3a33838b..24ad1e8b 100644 --- a/client/src/translations/rules/Emergo/es.pug +++ b/client/src/translations/rules/Emergo/es.pug @@ -1,2 +1,101 @@ p.boxed - | TODO + | Similar a las Damas, con prisioneros apilados debajo de los captores. + +figure.diagram-container + img.img-center(src="/variants/Emergo/match_2004_U-con.jpg") + figcaption.text-center. + Juego jugado en una representación ortogonal + de un tablero de Emergo estándar 9x9. + +p + | Nada desaparece en este juego: las piezas capturadas forman parte de las + | capturando unidades. Por lo tanto, una pieza o unidad siempre estará + | entendido como una combinación de W piezas blancas (elementales) y + | B piezas negras (elementales), con + br + | W >= 0, B >= 0 y W + B >= 1. + +p + | El tablero de 9x9 está inicialmente vacío. + | Cada jugador recibe 12 piezas apilables, "en la mano". + | En cada turno, un jugador debe elegir una de las siguientes acciones: + ul + li Introduzca una nueva pieza (W + B = 1) en el tablero. + li Mueve una unidad un cuadrado a lo largo de las diagonales. + li Capture algo. + +h3 Jugadas de introducción + +p. + Está prohibido introducir una pieza de forma que el oponente pueda + capturarlo en el siguiente turno, a menos que una captura ya fuera posible. + +p. + En el primer movimiento, las blancas no pueden colocar una pieza al + punto central. + +p. + Si el oponente ya ha colocado todas sus piezas en la mano, entonces + "pieza sombra" entra en el juego, que consta de todas las unidades + disponibles (así, en este caso, W + B > 1). + +h3 Capturas + +p. + Las capturas se realizan exactamente como en las Damas: saltando una pieza + diagonalmente adyacente para caer en una casilla vacía justo detrás. + Si una captura es posible, entonces debe jugarse; en este caso + ninguna pieza puede colocarse en el tablero. + Si después de una captura es posible otra con la misma pieza, entonces + debe ser jugado — a menos que implique un giro de 180 grados. + +figure.diagram-container + .diagram.diag12 + | fen:9/9/2a@1a@4/9/4a@4/3D@5/9/5A@3/6b@2 e3: + .diagram.diag22 + | fen:9/9/2a@1a@4/9/4a@4/3D@5/4ba4/9/9: + figcaption. + Antes y después de las capturas negras, + saltando a la ubicación marcada. + Tambien hay que capturar la unidad blanca siguente. + +p. + Las capturas pueden describirse formalmente como sigue. + Si, controlado por las blancas, W1/B1 salta sobre W2/B2 ubicado en S2 para + aterriza en S1', luego W1/(B1+1) llega a S1' mientras W2/(B2-1) permanece + en S2. Si W2 = B2 - 1 = 0, entonces no queda nada en la ubicación del + pieza capturada. + Para una pieza negra, invierta W y B. + +p. + En otras palabras, cada unidad está formada por una pila de piezas + (elementales) amigas o enemigas, las amigas se encuentran arriba. + Después de cada captura, los prisioneros en la pila aumentan en uno, + mientras que el número de carceleros en el lugar de captura se reduce en uno. + +p. + Cuando un jugador tiene varias cadenas de capturas disponibles, debe + seleccione uno de los más largos (como en Damas): + +figure.diagram-container + .diagram + | fen:9/9/2a@1a@4/9/2bb1a@4/3C@5/9/9/9: + figcaption. + En https://www.mindsports.nl/: + las blancas deben capturar en sentido horario. + +p. + Puede saltar sobre una pieza varias veces, siempre que + contiene al menos una unidad enemiga (que la controla). + +h3 Más información + +p + | Le invitamos a visitar el + a(href="https://www.mindsports.nl/index.php/arena/emergo/88-rules") + | sitio de autores + | . Las reglas también se describen en + a(href="http://www.iggamecenter.com/info/en/emergo.html") iggamecenter + | , donde puedes jugar Emergo. + +p Inventores: Christian Freeling y Ed van Zon (1986) diff --git a/client/src/translations/rules/Emergo/fr.pug b/client/src/translations/rules/Emergo/fr.pug index 3a33838b..e614b343 100644 --- a/client/src/translations/rules/Emergo/fr.pug +++ b/client/src/translations/rules/Emergo/fr.pug @@ -1,2 +1,100 @@ p.boxed - | TODO + | Similaires aux Dames, avec des prisonniers empilés sous les captureurs. + +figure.diagram-container + img.img-center(src="/variants/Emergo/match_2004_U-con.jpg") + figcaption.text-center. + Partie jouée sur une représentation orthogonale + d'un plateau d'Emergo standard 9x9. + +p + | Rien ne disparaît dans ce jeu : les pièces capturées font ensuite partie + | des unités capturantes. Par conséquent, une pièce ou unité sera toujours + | compris comme une combinaison de W pièces (élémentaires) blanches et de + | B pièces noires (élémentaires), avec + br + | W >= 0, B >= 0 et W + B >= 1. + +p + | Le plateau 9x9 est initialement vide. + | Chaque joueur reçoit 12 pièces empilables, "en main". + | À chaque tour, un jour doit choisir une action parmi les suivantes : + ul + li Entrer une nouvelle pièce (W + B = 1) sur le plateau. + li Déplacer une unité d'une case le long des diagonales. + li Capturer quelque chose. + +h3 Coups d'introduction + +p. + Il est interdit d'entrer une pièce de telle façon que l'adversaire puisse + la capturer au tour suivant, à moins qu'une capture était déjà possible. + +p Au premier coup, les blancs ne peuvent pas poser de pièce au point central. + +p. + Si l'adversaire a déjà posé toutes ses pièces en main, alors une + "pièce ombre" entre dans le jeu, constituée de toutes les unités disponibles + (ainsi dans ce cas W + B > 1). + +h3 Captures + +p. + Les captures se déroulent exactement comme aux Dames : en sautant par dessus + une pièce diagonalement adjacente pour atterrir sur une case vide juste + derrière. + Si une capture est possible, alors elle doit être jouée ; dans ce cas + aucune pièce ne peut être introduite sur le plateau. + Si après une capture une autre est possible avec la même pièce, alors elle + doit être jouée — sauf si cela implique un virage à 180 degrés. + +figure.diagram-container + .diagram.diag12 + | fen:9/9/2a@1a@4/9/4a@4/3D@5/9/5A@3/6b@2 e3: + .diagram.diag22 + | fen:9/9/2a@1a@4/9/4a@4/3D@5/4ba4/9/9: + figcaption. + Avant et après que les noirs capturent, + sautant à l'emplacement marqué. + Il faut aussi capturer la pièce blanche suivante. + +p. + Les captures peuvent être décrites formellement comme suit. + Si, contrôlée par les blancs, W1/B1 saute par dessus W2/B2 situé en S2 pour + atterrir en S1', alors W1/(B1+1) arrive en S1' tandis que W2/(B2-1) reste + en S2. Si W2 = B2 - 1 = 0, alors rien ne subsiste à l'emplacement de la + pièce capturée. + Pour une pièce noire, inverser W et B. + +p. + En d'autres termes, chaque unité est composée d'une pile de pièces + (élémentaires) amies ou ennemies, les amies étant situées au dessus. + Après chaque capture, les prisonniers dans la pile sont augmentés d'un, + tandis que le nombre de geôliers à l'emplacement de la capture diminue d'un. + +p. + Quand un joueur a à disposition plusieurs chaînes de captures, il doit + sélectionner l'une des plus longues (comme aux Dames) : + +figure.diagram-container + .diagram + | fen:9/9/2a@1a@4/9/2bb1a@4/3C@5/9/9/9: + figcaption. + Sur https://www.mindsports.nl/ : + les blancs doivent capturer dans le sens horaire. + +p. + On peut sauter par dessus une pièce plusieurs fois, du moment qu'elle + contient au moins une unité ennemie (qui la contrôle). + +h3 Plus d'information + +p + | Vous êtes invités à visiter le + a(href="https://www.mindsports.nl/index.php/arena/emergo/88-rules") + | site des auteurs + | . Les règles sont également décrites sur + a(href="http://www.iggamecenter.com/info/en/emergo.html") iggamecenter + | , où vous pouvez jouer à Emergo. + +p Inventeurs : Christian Freeling et Ed van Zon (1986) diff --git a/client/src/variants/Avalam1.js b/client/src/variants/Avalam1.js new file mode 100644 index 00000000..248b9c39 --- /dev/null +++ b/client/src/variants/Avalam1.js @@ -0,0 +1,52 @@ +import { ChessRules } from "@/base_rules"; +import { Avalam2Rules } from "@/variants/Avalam2"; + +export class Avalam1Rules extends Avalam2Rules { + + 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 "Avalam/" + b; + } + + static GenRandInitFen() { + return ( + "xxBbxxxxx/xBbBbxxxx/xbBbBbBxx/xBbBbBbBb/BbBb1bBbB/" + + "bBbBbBbBx/xxBbBbBbx/xxxxbBbBx/xxxxxbBxx w 0" + ); + } + + static get size() { + return { x: 9, y: 9 }; + } + + static OnBoard(x, y) { + if (!ChessRules.OnBoard(x, y)) return false; + switch (x) { + case 0: return [2, 3].includes(y); + case 1: return [1, 2, 3, 4].includes(y); + case 2: return [1, 2, 3, 4, 5, 6].includes(y); + case 3: return y >= 1; + case 4: return y != 4; + case 5: return y <= 7; + case 6: return [2, 3, 4, 5, 6, 7].includes(y); + case 7: return [4, 5, 6, 7].includes(y); + case 8: return [5, 6].includes(y); + } + return false; //never reached + } + +}; diff --git a/client/src/variants/Avalam2.js b/client/src/variants/Avalam2.js new file mode 100644 index 00000000..6c39e94c --- /dev/null +++ b/client/src/variants/Avalam2.js @@ -0,0 +1,130 @@ +import { ChessRules, Move, PiPo } from "@/base_rules"; +import { randInt } from "@/utils/alea"; + +export class Avalam2Rules extends ChessRules { + + static get HasFlags() { + return false; + } + + static get HasEnpassant() { + return false; + } + + static get Monochrome() { + return true; + } + + get showFirstTurn() { + return true; + } + + getPpath(b) { + return "Avalam/" + b; + } + + static get PIECES() { + // Towers of 1, 2, 3, 4 and 5 + return ['b', 'c', 'd', 'e', 'f']; + } + + static IsGoodPosition(position) { + if (position.length == 0) return false; + const rows = position.split("/"); + if (rows.length != V.size.x) return false; + for (let row of rows) { + let sumElts = 0; + for (let i = 0; i < row.length; i++) { + if (['x'].concat(V.PIECES).includes(row[i].toLowerCase())) sumElts++; + else { + const num = parseInt(row[i], 10); + if (isNaN(num)) return false; + sumElts += num; + } + } + if (sumElts != V.size.y) return false; + } + return true; + } + + static GenRandInitFen() { + return ( + "BbBbBbBb/bBbBbBbB/BbBbBbBb/bBbBbBbB/" + + "BbBbBbBb/bBbBbBbB/BbBbBbBb/bBbBbBbB w 0" + ); + } + + canIplay(side) { + return this.turn == side; + } + + getColor() { + return this.turn; //:-) + } + + getBasicMove([x1, y1], [x2, y2]) { + const cp1 = this.board[x1][y1], + cp2 = this.board[x2][y2]; + const newPiece = + String.fromCharCode(cp1.charCodeAt(1) + cp2.charCodeAt(1) - 97); + return ( + new Move({ + vanish: [ + new PiPo({ x: x1, y: y1, c: cp1[0], p: cp1[1] }), + new PiPo({ x: x2, y: y2, c: cp2[0], p: cp2[1] }) + ], + appear: [ + new PiPo({ x: x2, y: y2, c: cp1[0], p: newPiece }) + ] + }) + ); + } + + getPotentialMovesFrom([x, y]) { + const height = this.board[x][y].charCodeAt(1) - 97; + if (height == 5) return []; + let moves = []; + for (let s of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) { + const [i, j] = [x + s[0], y + s[1]]; + if ( + V.OnBoard(i, j) && + this.board[i][j] != V.EMPTY && + (height + this.board[i][j].charCodeAt(1) - 97 <= 5) + ) { + moves.push(this.getBasicMove([x, y], [i, j])); + } + } + return moves; + } + + filterValid(moves) { + return moves; + } + + getCheckSquares() { + return []; + } + + getCurrentScore() { + let towersCount = { w: 0, b: 0 }; + 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) { + if (this.getPotentialMovesFrom([i, j]).length > 0) return '*'; + towersCount[ this.board[i][j][0] ]++; + } + } + } + if (towersCount['w'] > towersCount['b']) return "1-0"; + if (towersCount['b'] > towersCount['w']) return "0-1"; + return "1/2"; + } + + getComputerMove() { + // Random mover (TODO) + const moves = super.getAllValidMoves(); + if (moves.length == 0) return null; + return moves[randInt(moves.length)]; + } + +}; diff --git a/client/src/variants/Dobutsu.js b/client/src/variants/Dobutsu.js index 60d0017d..da632b83 100644 --- a/client/src/variants/Dobutsu.js +++ b/client/src/variants/Dobutsu.js @@ -16,6 +16,10 @@ export class DobutsuRules extends ChessRules { return true; } + get showFirstTurn() { + return true; + } + static IsGoodFen(fen) { if (!ChessRules.IsGoodFen(fen)) return false; const fenParsed = V.ParseFen(fen); diff --git a/client/src/variants/Emergo.js b/client/src/variants/Emergo.js index caa2e982..98d9d71a 100644 --- a/client/src/variants/Emergo.js +++ b/client/src/variants/Emergo.js @@ -135,24 +135,28 @@ export class EmergoRules extends ChessRules { setOtherVariables(fen) { const reserve = V.ParseFen(fen).reserve.split(",").map(x => parseInt(x, 10)); - this.reserve = { - w: { [V.PAWN]: reserve[0] }, - b: { [V.PAWN]: reserve[1] } - }; + this.reserve = { w: null, b: null }; + if (reserve[0] > 0) this.reserve['w'] = { [V.PAWN]: reserve[0] }; + if (reserve[1] > 0) this.reserve['b'] = { [V.PAWN]: reserve[1] }; // Local stack of captures during a turn (squares + directions) this.captures = [ [] ]; } - atLeastOneCaptureFrom([x, y], color) { + atLeastOneCaptureFrom([x, y], color, forbiddenStep) { for (let s of V.steps[V.BISHOP]) { - const [i, j] = [x + s[0], y + s[1]]; if ( - V.OnBoard(i + s[0], j + s[1]) && - this.board[i][j] != V.EMPTY && - this.getColor(i, j) != color && - this.board[i + s[0]][j + s[1]] == V.EMPTY + !forbiddenStep || + (s[0] != -forbiddenStep[0] || s[1] != -forbiddenStep[1]) ) { - return true; + const [i, j] = [x + s[0], y + s[1]]; + if ( + V.OnBoard(i + s[0], j + s[1]) && + this.board[i][j] != V.EMPTY && + this.getColor(i, j) != color && + this.board[i + s[0]][j + s[1]] == V.EMPTY + ) { + return true; + } } } return false; @@ -162,7 +166,12 @@ export class EmergoRules extends ChessRules { const L0 = this.captures.length; const captures = this.captures[L0 - 1]; const L = captures.length; - if (L > 0) return this.atLeastOneCaptureFrom(captures[L-1].square, color); + if (L > 0) { + return ( + this.atLeastOneCaptureFrom( + captures[L-1].square, color, captures[L-1].step) + ); + } for (let i = 0; i < V.size.x; i++) { for (let j=0; j< V.size.y; j++) { if ( @@ -190,12 +199,34 @@ export class EmergoRules extends ChessRules { return res; }; + getLongestCaptures_aux([x, y], color, locSteps) { + let res = []; + const L = locSteps.length; + const lastStep = (L > 0 ? locSteps[L-1] : null); + for (let s of V.steps[V.BISHOP]) { + if (!!lastStep && s[0] == -lastStep[0] && s[1] == -lastStep[1]) continue; + const [i, j] = [x + s[0], y + s[1]]; + if ( + V.OnBoard(i + s[0], j + s[1]) && + this.board[i + s[0]][j + s[1]] == V.EMPTY && + this.board[i][j] != V.EMPTY && + this.getColor(i, j) != color + ) { + const move = this.getBasicMove([x, y], [i + s[0], j + s[1]], [i, j]); + locSteps.push(s); + V.PlayOnBoard(this.board, move); + const nextRes = + this.getLongestCaptures_aux([i + s[0], j + s[1]], color, locSteps); + res.push(1 + nextRes); + locSteps.pop(); + V.UndoOnBoard(this.board, move); + } + } + if (res.length == 0) return 0; + return Math.max(...res); + } + getLongestCapturesFrom([x, y], color, locSteps) { - // - // TODO: debug here, from - // 9/9/2a@1a@4/5A@3/9/3aa1A@3/9/9/8A@ w 10 8,9 - // White to move, double capture. - // let res = []; const L = locSteps.length; const lastStep = (L > 0 ? locSteps[L-1] : null); @@ -211,50 +242,47 @@ export class EmergoRules extends ChessRules { const move = this.getBasicMove([x, y], [i + s[0], j + s[1]], [i, j]); locSteps.push(s); V.PlayOnBoard(this.board, move); - const sRes = this.getLongestCapturesFrom( - [i + s[0], j + s[1]], color, locSteps); - res.push({ - step: s, - length: 1 + (sRes.length == 0 ? 0 : sRes[0].length) - }); + const stepRes = + this.getLongestCaptures_aux([i + s[0], j + s[1]], color, locSteps); + res.push({ step: s, length: 1 + stepRes }); locSteps.pop(); V.UndoOnBoard(this.board, move); } } - return this.maxLengthIndices(res).map(i => res[i]); + return this.maxLengthIndices(res).map(i => res[i]);; } getAllLongestCaptures(color) { const L0 = this.captures.length; const captures = this.captures[L0 - 1]; const L = captures.length; + let caps = []; if (L > 0) { - let locSteps = []; - const caps = Object.assign( - { square: captures[L-1].square }, - this.getLongestCapturesFrom(captures[L-1].square, color, locSteps) + let locSteps = [ captures[L-1].step ]; + let res = + this.getLongestCapturesFrom(captures[L-1].square, color, locSteps); + Array.prototype.push.apply( + caps, + res.map(r => Object.assign({ square: captures[L-1].square }, r)) ); - return this.maxLengthIndices(caps).map(i => caps[i]); } - let caps = []; - 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 && - this.getColor(i, j) == color - ) { - let locSteps = []; - let res = this.getLongestCapturesFrom([i, j], color, locSteps); - Array.prototype.push.apply( - caps, - res.map(r => Object.assign({ square: [i, j] }, r)) - ); + else { + 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 && + this.getColor(i, j) == color + ) { + let locSteps = []; + let res = this.getLongestCapturesFrom([i, j], color, locSteps); + Array.prototype.push.apply( + caps, + res.map(r => Object.assign({ square: [i, j] }, r)) + ); + } } } } - -console.log(caps); - return this.maxLengthIndices(caps).map(i => caps[i]); } @@ -267,20 +295,32 @@ console.log(caps); }); } // Compute resulting types based on jumped + jumping pieces + const color = this.getColor(x1, y1); + const firstCodes = (color == 'w' ? [65, 97] : [97, 65]); const cpCapt = this.board[capt[0]][capt[1]]; - const newAtCapt = cpCapt.charCodeAt(0) - 1; - const newAtDest = - cp1[1] == '@' - ? (cp1.charCodeAt(0) < 97 ? 65 : 97) - : (cp1.charCodeAt(1) + 1); - const color = this.turn; + let count1 = [cp1.charCodeAt(0) - firstCodes[0], -1]; + if (cp1[1] != '@') count1[1] = cp1.charCodeAt(1) - firstCodes[0]; + let countC = [cpCapt.charCodeAt(0) - firstCodes[1], -1]; + if (cpCapt[1] != '@') countC[1] = cpCapt.charCodeAt(1) - firstCodes[1]; + count1[1]++; + countC[0]--; + let colorChange = false, + captVanish = false; + if (countC[0] < 0) { + if (countC[1] >= 0) { + colorChange = true; + countC = [countC[1], -1]; + } + else captVanish = true; + } + const incPrisoners = String.fromCharCode(firstCodes[0] + count1[1]); let mv = new Move({ appear: [ new PiPo({ x: x2, y: y2, c: cp1[0], - p: String.fromCharCode(newAtDest) + p: incPrisoners }) ], vanish: [ @@ -288,27 +328,14 @@ console.log(caps); new PiPo({ x: capt[0], y: capt[1], c: cpCapt[0], p: cpCapt[1] }) ] }); - if ([64, 96].includes(newAtCapt)) { - // Enemy units vanish from capturing square - if (cpCapt.charAt(1) != '@') { - // Out units remain: - mv.appear.push( - new PiPo({ - x: capt[0], - y: capt[1], - c: cpCapt[0], - p: '@' - }) - ); - } - } - else { + if (!captVanish) { mv.appear.push( new PiPo({ x: capt[0], y: capt[1], - c: String.fromCharCode(newAtCapt), - p: cpCapt[1] + c: String.fromCharCode( + firstCodes[(colorChange ? 0 : 1)] + countC[0]), + p: (colorChange ? '@' : cpCapt[1]), }) ); } @@ -443,11 +470,11 @@ console.log(caps); const L0 = this.captures.length; let captures = this.captures[L0 - 1]; captures.push({ - square: [move.start.x, move.start.y], - step: [move.end.x - move.start.x, move.end.y - move.start.y] + square: [move.end.x, move.end.y], + step: [(move.end.x - move.start.x)/2, (move.end.y - move.start.y)/2] }); - if (this.atLeastOneCapture()) - // There could be other captures (optional) + if (this.atLeastOneCapture(color)) + // There could be other captures (mandatory) move.notTheEnd = true; } else if (move.vanish == 0) { @@ -480,8 +507,8 @@ console.log(caps); } atLeastOneMove() { - if (this.atLeastOneCapture()) return true; const color = this.turn; + if (this.atLeastOneCapture(color)) return true; 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 && this.getColor(i, j) == color) { @@ -498,16 +525,17 @@ console.log(caps); getCurrentScore() { const color = this.turn; // If no pieces on board + reserve, I lose - if ( - !this.reserve[color] && - this.board.every(b => { - return b.every(cell => { - return (cell == "" || cell[0] != color); - }); - }) - ) { - return (color == 'w' ? "0-1" : "1-0"); + if (!!this.reserve[color]) return "*"; + let atLeastOnePiece = false; + 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 && this.getColor(i, j) == color) { + atLeastOnePiece = true; + break outerLoop; + } + } } + if (!atLeastOnePiece) return (color == 'w' ? "0-1" : "1-0"); if (!this.atLeastOneMove()) return "1/2"; return "*"; } @@ -529,6 +557,8 @@ console.log(caps); getNotation(move) { if (move.vanish.length == 0) return "@" + V.CoordsToSquare(move.end); + const L0 = this.captures.length; + if (this.captures[L0 - 1].length > 0) return V.CoordsToSquare(move.end); return V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end); }