Fix Emergo. Add Avalam 1 & 2
authorBenjamin Auder <benjamin.auder@somewhere>
Thu, 21 Jan 2021 18:18:09 +0000 (19:18 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Thu, 21 Jan 2021 18:18:09 +0000 (19:18 +0100)
19 files changed:
.gitattributes
client/public/images/pieces/Avalam/.gitignore [new file with mode: 0644]
client/public/images/pieces/Avalam/generateSVG.py [new file with mode: 0755]
client/public/images/pieces/Emergo/generateSVG_composite.py
client/public/images/pieces/Emergo/generateSVG_simple.py
client/public/variants/Emergo/match_2004_U-con.jpg [new file with mode: 0644]
client/src/translations/rules/Avalam1/en.pug
client/src/translations/rules/Avalam1/es.pug
client/src/translations/rules/Avalam1/fr.pug
client/src/translations/rules/Avalam2/en.pug
client/src/translations/rules/Avalam2/es.pug
client/src/translations/rules/Avalam2/fr.pug
client/src/translations/rules/Emergo/en.pug
client/src/translations/rules/Emergo/es.pug
client/src/translations/rules/Emergo/fr.pug
client/src/variants/Avalam1.js [new file with mode: 0644]
client/src/variants/Avalam2.js [new file with mode: 0644]
client/src/variants/Dobutsu.js
client/src/variants/Emergo.js

index ac66ad0..6ce8952 100644 (file)
@@ -1,5 +1,6 @@
 *.ico filter=fat
 *.pdf filter=fat
 *.png filter=fat
 *.ico filter=fat
 *.pdf filter=fat
 *.png filter=fat
+*.jpg filter=fat
 *.gif filter=fat
 *.flac 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 (file)
index 0000000..fa11225
--- /dev/null
@@ -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 (executable)
index 0000000..d400d79
--- /dev/null
@@ -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 = """<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" version="1.0" width="230" height="230">"""
+
+black = '<circle cx="115" cy="115" r="100" fill="red" stroke="darkslategray"/>'
+white = '<circle cx="115" cy="115" r="100" fill="yellow" stroke="darkslategray"/>'
+
+digits = [
+    # 1
+    '<path d="M125,95 v40"',
+    # 2
+    '<path d="M105,95 h20 v20 h-20 v20 h20"',
+    # 3
+    '<path d="M105,95 h20 v20 h-20 M125,115 v20 h-20"',
+    # 4
+    '<path d="M105,95 v20 h20 v20 M125,95 v20"',
+    # 5
+    '<path d="M125,95 h-20 v20 h20 v20 h-20"'
+]
+
+final = "</svg>"
+
+for color in ["white", "black"]:
+    for number in range(5):
+        filename = ('w' if color == "white" else 'b') + chr(97 + number + 1) + ".svg"
+        f = open(filename, "w")
+        f.write(preamble)
+        f.write("\n")
+        f.write(white if color == "white" else black)
+        f.write("\n")
+        f.write(digits[number] + ' fill="none" stroke-width="4" ' + ('stroke="red"' if color == "white" else 'stroke="yellow"') + '/>')
+        f.write("\n")
+        f.write(final)
+        f.close()
index 87eb55f..915404b 100755 (executable)
@@ -22,55 +22,55 @@ black_right = '<circle cx="115" cy="115" r="100" fill="black"/>'
 digits = {
     "left": [
         # 1
 digits = {
     "left": [
         # 1
-        '<path d="M90,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M90,95 v40"',
         # 2
         # 2
-        '<path d="M70,95 h20 v20 h-20 v20 h20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M70,95 h20 v20 h-20 v20 h20"',
         # 3
         # 3
-        '<path d="M70,95 h20 v20 h-20 M90,115 v20 h-20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M70,95 h20 v20 h-20 M90,115 v20 h-20"',
         # 4
         # 4
-        '<path d="M70,95 v20 h20 v20 M90,95 v20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M70,95 v20 h20 v20 M90,95 v20"',
         # 5
         # 5
-        '<path d="M90,95 h-20 v20 h20 v20 h-20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M90,95 h-20 v20 h20 v20 h-20"',
         # 6
         # 6
-        '<path d="M90,95 h-20 v40 h20 v-20 h-20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M90,95 h-20 v40 h20 v-20 h-20"',
         # 7
         # 7
-        '<path d="M70,95 h20 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M70,95 h20 v40"',
         # 8
         # 8
-        '<path d="M70,95 h20 v40 h-20 z M70,115 h20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M70,95 h20 v40 h-20 z M70,115 h20"',
         # 9
         # 9
-        '<path d="M70,135 h20 v-40 h-20 v20 h20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M70,135 h20 v-40 h-20 v20 h20"',
         # 10
         # 10
-        '<path d="M60,95 v40 M70,95 h20 v40 h-20 v-40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M60,95 v40 M70,95 h20 v40 h-20 v-40"',
         # 11
         # 11
-        '<path d="M60,95 v40 M90,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M60,95 v40 M90,95 v40"',
         # 12
         # 12
-        '<path d="M60,95 v40 M70,95 h20 v20 h-20 M90,115 v20 h-20" stroke="red" fill="none" stroke-width="2"/>'
+        '<path d="M60,95 v40 M70,95 h20 v20 h-20 M90,115 v20 h-20"'
     ],
     "right": [
         # 1
     ],
     "right": [
         # 1
-        '<path d="M180,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M180,95 v40"',
         # 2
         # 2
-        '<path d="M160,95 h20 v20 h-20 v20 h20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M160,95 h20 v20 h-20 v20 h20"',
         # 3
         # 3
-        '<path d="M160,95 h20 v20 h-20 M180,115 v20 h-20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M160,95 h20 v20 h-20 M180,115 v20 h-20"',
         # 4
         # 4
-        '<path d="M160,95 v20 h20 v20 M180,95 v20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M160,95 v20 h20 v20 M180,95 v20"',
         # 5
         # 5
-        '<path d="M180,95 h-20 v20 h20 v20 h-20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M180,95 h-20 v20 h20 v20 h-20"',
         # 6
         # 6
-        '<path d="M180,95 h-20 v40 h20 v-20 h-20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M180,95 h-20 v40 h20 v-20 h-20"',
         # 7
         # 7
-        '<path d="M160,95 h20 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M160,95 h20 v40"',
         # 8
         # 8
-        '<path d="M160,95 h20 v40 h-20 z M160,115 h20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M160,95 h20 v40 h-20 z M160,115 h20"',
         # 9
         # 9
-        '<path d="M160,135 h20 v-40 h-20 v20 h20" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M160,135 h20 v-40 h-20 v20 h20"',
         # 10
         # 10
-        '<path d="M150,95 v40 M160,95 h20 v40 h-20 v-40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M150,95 v40 M160,95 h20 v40 h-20 v-40"',
         # 11
         # 11
-        '<path d="M150,95 v40 M180,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M150,95 v40 M180,95 v40"',
         # 12
         # 12
-        '<path d="M150,95 v40 M160,95 h20 v20 h-20 M180,115 v20 h-20" stroke="red" fill="none" stroke-width="2"/>'
+        '<path d="M150,95 v40 M160,95 h20 v20 h-20 M180,115 v20 h-20"'
     ]
 }
 
     ]
 }
 
@@ -83,14 +83,14 @@ for colorLeft in ["white", "black"]:
             filename = chr(65 + left + chrShift) + chr(65 + right + chrShift) + ".svg"
             f = open(filename, "w")
             f.write(preamble)
             filename = chr(65 + left + chrShift) + chr(65 + right + chrShift) + ".svg"
             f = open(filename, "w")
             f.write(preamble)
-            f.write("\n");
+            f.write("\n")
             f.write(black_right if colorLeft == "white" else white_right)
             f.write(black_right if colorLeft == "white" else white_right)
-            f.write("\n");
+            f.write("\n")
             f.write(white_left if colorLeft == "white" else black_left)
             f.write(white_left if colorLeft == "white" else black_left)
-            f.write("\n");
-            f.write(digits["left"][left])
-            f.write("\n");
-            f.write(digits["right"][right])
-            f.write("\n");
+            f.write("\n")
+            f.write(digits["left"][left] + ' fill="none" stroke-width="4" ' + ('stroke="red"' if colorLeft == "white" else 'stroke="orange"') + '/>')
+            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()
             f.write(final)
             f.close()
index 5cb53c9..6453362 100755 (executable)
@@ -13,29 +13,29 @@ white = '<circle cx="115" cy="115" r="100" fill="whitesmoke" stroke="orange"/>'
 
 digits = [
     # 1
 
 digits = [
     # 1
-    '<path d="M125,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M125,95 v40"',
     # 2
     # 2
-    '<path d="M105,95 h20 v20 h-20 v20 h20" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M105,95 h20 v20 h-20 v20 h20"',
     # 3
     # 3
-    '<path d="M105,95 h20 v20 h-20 M125,115 v20 h-20" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M105,95 h20 v20 h-20 M125,115 v20 h-20"',
     # 4
     # 4
-    '<path d="M105,95 v20 h20 v20 M125,95 v20" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M105,95 v20 h20 v20 M125,95 v20"',
     # 5
     # 5
-    '<path d="M125,95 h-20 v20 h20 v20 h-20" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M125,95 h-20 v20 h20 v20 h-20"',
     # 6
     # 6
-    '<path d="M125,95 h-20 v40 h20 v-20 h-20" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M125,95 h-20 v40 h20 v-20 h-20"',
     # 7
     # 7
-    '<path d="M105,95 h20 v40" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M105,95 h20 v40"',
     # 8
     # 8
-    '<path d="M105,95 h20 v40 h-20 z M105,115 h20" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M105,95 h20 v40 h-20 z M105,115 h20"',
     # 9
     # 9
-    '<path d="M105,135 h20 v-40 h-20 v20 h20" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M105,135 h20 v-40 h-20 v20 h20"',
     # 10
     # 10
-    '<path d="M100,95 v40 M110,95 h20 v40 h-20 v-40" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M100,95 v40 M110,95 h20 v40 h-20 v-40"',
     # 11
     # 11
-    '<path d="M100,95 v40 M130,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M100,95 v40 M130,95 v40"',
     # 12
     # 12
-    '<path d="M100,95 v40 M110,95 h20 v20 h-20 M130,115 v20 h-20" stroke="red" fill="none" stroke-width="2"/>'
+    '<path d="M100,95 v40 M110,95 h20 v20 h-20 M130,115 v20 h-20"'
 ]
 
 final = "</svg>"
 ]
 
 final = "</svg>"
@@ -46,10 +46,10 @@ for color in ["white", "black"]:
         filename = chr(65 + number + chrShift) + "@.svg"
         f = open(filename, "w")
         f.write(preamble)
         filename = chr(65 + number + chrShift) + "@.svg"
         f = open(filename, "w")
         f.write(preamble)
-        f.write("\n");
+        f.write("\n")
         f.write(white if color == "white" else black)
         f.write(white if color == "white" else black)
-        f.write("\n");
-        f.write(digits[number])
-        f.write("\n");
+        f.write("\n")
+        f.write(digits[number] + ' fill="none" stroke-width="4" ' + ('stroke="red"' if color == "white" else 'stroke="orange"') + '/>')
+        f.write("\n")
         f.write(final)
         f.close()
         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 (file)
index 0000000..b24969c
--- /dev/null
@@ -0,0 +1 @@
+#$# git-fat 761b5f04a60c2c2438cf2777453ba42aceb16da3                80995
index 3a33838..64364b4 100644 (file)
@@ -1,2 +1,33 @@
 p.boxed
 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)
index 3a33838..dbf4622 100644 (file)
@@ -1,2 +1,34 @@
 p.boxed
 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)
index 3a33838..3dd876e 100644 (file)
@@ -1,2 +1,34 @@
 p.boxed
 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)
index 3a33838..2126bac 100644 (file)
@@ -1,2 +1,7 @@
 p.boxed
 p.boxed
-  | TODO
+  | Move towers freely on top of each others,
+  | so that your color appears on top.
+
+p
+  a(href="/#/variants/Avalam1") Avalam1
+  | &nbsp;rules, on a regular 8x8 chess board.
index 3a33838..9b7311d 100644 (file)
@@ -1,2 +1,8 @@
 p.boxed
 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.
index 3a33838..028741a 100644 (file)
@@ -1,2 +1,8 @@
 p.boxed
 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.
index 8c24bd6..202082e 100644 (file)
@@ -1,62 +1,96 @@
 p.boxed
   | Similar to Checkers, with prisoners stacked below capturers.
 
 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".
 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
   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.
 
 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.
   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 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 &mdash; 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.
 
 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.
   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
 
 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,
   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
 
 h3 More information
 
 p
-  | See the 
+  | You are invited to visit the 
   a(href="https://www.mindsports.nl/index.php/arena/emergo/88-rules")
   a(href="https://www.mindsports.nl/index.php/arena/emergo/88-rules")
-    | Emergo page
-  | &nbsp;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
   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)
 
 p Inventors: Christian Freeling and Ed van Zon (1986)
index 3a33838..24ad1e8 100644 (file)
@@ -1,2 +1,101 @@
 p.boxed
 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 &mdash; 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)
index 3a33838..e614b34 100644 (file)
@@ -1,2 +1,100 @@
 p.boxed
 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 &mdash; 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 (file)
index 0000000..248b9c3
--- /dev/null
@@ -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 (file)
index 0000000..6c39e94
--- /dev/null
@@ -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)];
+  }
+
+};
index 60d0017..da632b8 100644 (file)
@@ -16,6 +16,10 @@ export class DobutsuRules extends ChessRules {
     return true;
   }
 
     return true;
   }
 
+  get showFirstTurn() {
+    return true;
+  }
+
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
index caa2e98..98d9d71 100644 (file)
@@ -135,24 +135,28 @@ export class EmergoRules extends ChessRules {
   setOtherVariables(fen) {
     const reserve =
       V.ParseFen(fen).reserve.split(",").map(x => parseInt(x, 10));
   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 = [ [] ];
   }
 
     // 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]) {
     for (let s of V.steps[V.BISHOP]) {
-      const [i, j] = [x + s[0], y + s[1]];
       if (
       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;
       }
     }
     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;
     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 (
     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;
   };
 
     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) {
   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);
     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 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);
       }
     }
         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;
   }
 
   getAllLongestCaptures(color) {
     const L0 = this.captures.length;
     const captures = this.captures[L0 - 1];
     const L = captures.length;
+    let caps = [];
     if (L > 0) {
     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]);
   }
 
     return this.maxLengthIndices(caps).map(i => caps[i]);
   }
 
@@ -267,20 +295,32 @@ console.log(caps);
       });
     }
     // Compute resulting types based on jumped + jumping pieces
       });
     }
     // 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 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],
     let mv = new Move({
       appear: [
         new PiPo({
           x: x2,
           y: y2,
           c: cp1[0],
-          p: String.fromCharCode(newAtDest)
+          p: incPrisoners
         })
       ],
       vanish: [
         })
       ],
       vanish: [
@@ -288,27 +328,14 @@ console.log(caps);
         new PiPo({ x: capt[0], y: capt[1], c: cpCapt[0], p: cpCapt[1] })
       ]
     });
         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],
       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({
       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) {
         move.notTheEnd = true;
     }
     else if (move.vanish == 0) {
@@ -480,8 +507,8 @@ console.log(caps);
   }
 
   atLeastOneMove() {
   }
 
   atLeastOneMove() {
-    if (this.atLeastOneCapture()) return true;
     const color = this.turn;
     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) {
     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
   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 "*";
   }
     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);
 
   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);
   }
 
     return V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end);
   }