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
+*.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 (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
-        '<path d="M90,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M90,95 v40"',
         # 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
-        '<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
-        '<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
-        '<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
-        '<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
-        '<path d="M70,95 h20 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M70,95 h20 v40"',
         # 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
-        '<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
-        '<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
-        '<path d="M60,95 v40 M90,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M60,95 v40 M90,95 v40"',
         # 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
-        '<path d="M180,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M180,95 v40"',
         # 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
-        '<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
-        '<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
-        '<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
-        '<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
-        '<path d="M160,95 h20 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M160,95 h20 v40"',
         # 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
-        '<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
-        '<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
-        '<path d="M150,95 v40 M180,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+        '<path d="M150,95 v40 M180,95 v40"',
         # 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)
-            f.write("\n");
+            f.write("\n")
             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("\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()
index 5cb53c9..6453362 100755 (executable)
@@ -13,29 +13,29 @@ white = '<circle cx="115" cy="115" r="100" fill="whitesmoke" stroke="orange"/>'
 
 digits = [
     # 1
-    '<path d="M125,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M125,95 v40"',
     # 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
-    '<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
-    '<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
-    '<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
-    '<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
-    '<path d="M105,95 h20 v40" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M105,95 h20 v40"',
     # 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
-    '<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
-    '<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
-    '<path d="M100,95 v40 M130,95 v40" stroke="red" fill="none" stroke-width="2"/>',
+    '<path d="M100,95 v40 M130,95 v40"',
     # 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>"
@@ -46,10 +46,10 @@ for color in ["white", "black"]:
         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("\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()
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
-  | 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
-  | 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
-  | 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
-  | 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
-  | 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
-  | 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.
 
+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 &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.
-  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
-  | &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
-  | , where you can also play this game.
+  | , where you can play Emergo.
 
 p Inventors: Christian Freeling and Ed van Zon (1986)
index 3a33838..24ad1e8 100644 (file)
@@ -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 &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
-  | 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;
   }
 
+  get showFirstTurn() {
+    return true;
+  }
+
   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));
-    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);
   }