From 7e8a7ea1cb66adb4a987badfb0a3c2f99a21bd0a Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Mon, 30 Nov 2020 17:13:46 +0100
Subject: [PATCH] Almost added TitanChess + EvolutionChess

---
 client/download_objects.sh                    |   6 +
 .../public/images/pieces/Coregal/castle.svg   | 106 ++++++++
 client/public/images/pieces/SOURCE            |   1 +
 client/public/images/pieces/Titan/ba.png      |   1 +
 client/public/images/pieces/Titan/bb.png      |   1 +
 client/public/images/pieces/Titan/bc.png      |   1 +
 client/public/images/pieces/Titan/bj.png      |   1 +
 client/public/images/pieces/Titan/bk.png      |   1 +
 client/public/images/pieces/Titan/bl.png      |   1 +
 client/public/images/pieces/Titan/bm.png      |   1 +
 client/public/images/pieces/Titan/bn.png      |   1 +
 client/public/images/pieces/Titan/bo.png      |   1 +
 client/public/images/pieces/Titan/bp.png      |   1 +
 client/public/images/pieces/Titan/bq.png      |   1 +
 client/public/images/pieces/Titan/br.png      |   1 +
 client/public/images/pieces/Titan/bs.png      |   1 +
 client/public/images/pieces/Titan/bt.png      |   1 +
 client/public/images/pieces/Titan/bu.png      |   1 +
 client/public/images/pieces/Titan/bv.png      |   1 +
 client/public/images/pieces/Titan/script.sh   |  13 +
 client/public/images/pieces/Titan/wa.png      |   1 +
 client/public/images/pieces/Titan/wb.png      |   1 +
 client/public/images/pieces/Titan/wc.png      |   1 +
 client/public/images/pieces/Titan/wj.png      |   1 +
 client/public/images/pieces/Titan/wk.png      |   1 +
 client/public/images/pieces/Titan/wl.png      |   1 +
 client/public/images/pieces/Titan/wm.png      |   1 +
 client/public/images/pieces/Titan/wn.png      |   1 +
 client/public/images/pieces/Titan/wo.png      |   1 +
 client/public/images/pieces/Titan/wp.png      |   1 +
 client/public/images/pieces/Titan/wq.png      |   1 +
 client/public/images/pieces/Titan/wr.png      |   1 +
 client/public/images/pieces/Titan/ws.png      |   1 +
 client/public/images/pieces/Titan/wt.png      |   1 +
 client/public/images/pieces/Titan/wu.png      |   1 +
 client/public/images/pieces/Titan/wv.png      |   1 +
 client/src/base_rules.js                      |  48 ++--
 .../src/translations/rules/Evolution/en.pug   |  25 ++
 .../src/translations/rules/Evolution/es.pug   |   2 +
 .../src/translations/rules/Evolution/fr.pug   |   5 +
 client/src/translations/rules/Titan/en.pug    |  40 +++
 client/src/translations/rules/Titan/es.pug    |  42 ++++
 client/src/translations/rules/Titan/fr.pug    |  41 ++++
 client/src/variants/Absorption.js             |   2 +
 client/src/variants/Alice.js                  |   2 +
 client/src/variants/Allmate1.js               |   8 +-
 client/src/variants/Allmate2.js               |   8 +-
 client/src/variants/Ambiguous.js              |   2 +
 client/src/variants/Antiking1.js              |   2 +
 client/src/variants/Antiking2.js              |   2 +
 client/src/variants/Antimatter.js             |   4 +-
 client/src/variants/Apocalypse.js             |   2 +
 client/src/variants/Arena.js                  |   2 +
 client/src/variants/Atomic.js                 |   2 +
 client/src/variants/Balaklava.js              |   2 +
 client/src/variants/Ball.js                   |   2 +
 client/src/variants/Baroque.js                |   2 +
 client/src/variants/Benedict.js               |   2 +
 client/src/variants/Berolina.js               |   2 +
 client/src/variants/Bicolour.js               |   2 +
 client/src/variants/Bishopawns.js             |   2 +
 client/src/variants/Cannibal.js               |   2 +
 client/src/variants/Capture.js                |   2 +
 client/src/variants/Castle.js                 |   2 +
 client/src/variants/Chakart.js                |   2 +
 client/src/variants/Checkered1.js             |   2 +
 client/src/variants/Checkered2.js             |   2 +
 client/src/variants/Checkless.js              |   2 +
 client/src/variants/Circular.js               |   2 +
 client/src/variants/Clorange.js               |   2 +
 client/src/variants/Colorbound.js             |  87 +------
 client/src/variants/Coregal.js                | 132 ++++------
 client/src/variants/Coronation.js             |   2 +
 client/src/variants/Crazyhouse.js             |   2 +
 client/src/variants/Cylinder.js               |   2 +
 client/src/variants/Dark.js                   |   2 +
 client/src/variants/Diamond.js                |   2 +
 client/src/variants/Dice.js                   |   2 +
 client/src/variants/Discoduel.js              |   2 +
 client/src/variants/Doublearmy.js             |   2 +
 client/src/variants/Doublemove1.js            |   2 +
 client/src/variants/Doublemove2.js            |   2 +
 client/src/variants/Dynamo.js                 |   2 +
 client/src/variants/Eightpieces.js            |   2 +
 client/src/variants/Enpassant.js              |   2 +
 client/src/variants/Evolution.js              |  34 +++
 client/src/variants/Extinction.js             |   2 +
 client/src/variants/Football.js               |   2 +
 client/src/variants/Forward.js                |   5 +-
 client/src/variants/Freecapture.js            |   2 +
 client/src/variants/Grand.js                  |   2 +
 client/src/variants/Grasshopper.js            |   2 +
 client/src/variants/Gridolina.js              |   2 +
 client/src/variants/Hamilton.js               |   2 +
 client/src/variants/Hidden.js                 |   2 +
 client/src/variants/Hiddenqueen.js            |   2 +
 client/src/variants/Horde.js                  |   2 +
 client/src/variants/Interweave.js             |   2 +
 client/src/variants/Kinglet.js                |   2 +
 client/src/variants/Knightmate.js             |   2 +
 client/src/variants/Knightpawns.js            |   2 +
 client/src/variants/Knightrelay1.js           |   2 +
 client/src/variants/Knightrelay2.js           |   2 +
 client/src/variants/Koopa.js                  |   7 +-
 client/src/variants/Koth.js                   |   2 +
 client/src/variants/Losers.js                 |   2 +
 client/src/variants/Madhouse.js               |   2 +
 client/src/variants/Madrasi.js                |   2 +
 client/src/variants/Magnetic.js               |   2 +
 client/src/variants/Makruk.js                 |   2 +
 client/src/variants/Maxima.js                 |   2 +
 client/src/variants/Minishogi.js              |   2 +
 client/src/variants/Monochrome.js             |   2 +
 client/src/variants/Monster.js                |   2 +
 client/src/variants/Omega.js                  |  96 +-------
 client/src/variants/Orda.js                   |   2 +
 client/src/variants/Ordamirror.js             |   2 +
 client/src/variants/Pacifist1.js              |  28 +--
 client/src/variants/Pacifist2.js              |   2 +
 client/src/variants/Parachute.js              |   2 +
 client/src/variants/Pawnmassacre.js           |   2 +
 client/src/variants/Pawns.js                  |   2 +
 client/src/variants/Pawnsking.js              |   2 +
 client/src/variants/Perfect.js                |   2 +
 client/src/variants/Pocketknight.js           |   2 +
 client/src/variants/Progressive1.js           |   2 +
 client/src/variants/Progressive2.js           |   2 +
 client/src/variants/Queenpawns.js             |   2 +
 client/src/variants/Racingkings.js            |   2 +
 client/src/variants/Rampage.js                |   2 +
 client/src/variants/Recycle.js                |   2 +
 client/src/variants/Rifle.js                  |   2 +
 client/src/variants/Rococo.js                 |   2 +
 client/src/variants/Rookpawns.js              |   2 +
 client/src/variants/Royalrace.js              |   2 +
 client/src/variants/Rugby.js                  |   2 +
 client/src/variants/Schess.js                 |   2 +
 client/src/variants/Shako.js                  |  90 +------
 client/src/variants/Shatranj.js               |   2 +
 client/src/variants/Shogi.js                  |   2 +
 client/src/variants/Sittuyin.js               |   2 +
 client/src/variants/Suction.js                |   2 +
 client/src/variants/Suicide.js                |   2 +
 client/src/variants/Swap.js                   |   2 +
 client/src/variants/Switching.js              |   4 +-
 client/src/variants/Synchrone.js              |   3 +-
 client/src/variants/Takenmake.js              |   2 +
 client/src/variants/Teleport.js               |   2 +
 client/src/variants/Tencubed.js               |   2 +
 client/src/variants/Threechecks.js            |   2 +
 client/src/variants/Titan.js                  | 230 +++++++++++++++++-
 client/src/variants/Twokings.js               |   7 +
 client/src/variants/Upsidedown.js             |   2 +
 client/src/variants/Vchess.js                 |   2 +
 client/src/variants/Wildebeest.js             |   2 +
 client/src/variants/Wormhole.js               |   2 +
 client/src/variants/Zen.js                    |   2 +
 157 files changed, 870 insertions(+), 432 deletions(-)
 create mode 100644 client/public/images/pieces/Coregal/castle.svg
 create mode 100644 client/public/images/pieces/Titan/ba.png
 create mode 100644 client/public/images/pieces/Titan/bb.png
 create mode 100644 client/public/images/pieces/Titan/bc.png
 create mode 100644 client/public/images/pieces/Titan/bj.png
 create mode 100644 client/public/images/pieces/Titan/bk.png
 create mode 100644 client/public/images/pieces/Titan/bl.png
 create mode 100644 client/public/images/pieces/Titan/bm.png
 create mode 100644 client/public/images/pieces/Titan/bn.png
 create mode 100644 client/public/images/pieces/Titan/bo.png
 create mode 120000 client/public/images/pieces/Titan/bp.png
 create mode 100644 client/public/images/pieces/Titan/bq.png
 create mode 100644 client/public/images/pieces/Titan/br.png
 create mode 100644 client/public/images/pieces/Titan/bs.png
 create mode 100644 client/public/images/pieces/Titan/bt.png
 create mode 100644 client/public/images/pieces/Titan/bu.png
 create mode 100644 client/public/images/pieces/Titan/bv.png
 create mode 100644 client/public/images/pieces/Titan/script.sh
 create mode 100644 client/public/images/pieces/Titan/wa.png
 create mode 100644 client/public/images/pieces/Titan/wb.png
 create mode 100644 client/public/images/pieces/Titan/wc.png
 create mode 100644 client/public/images/pieces/Titan/wj.png
 create mode 100644 client/public/images/pieces/Titan/wk.png
 create mode 100644 client/public/images/pieces/Titan/wl.png
 create mode 100644 client/public/images/pieces/Titan/wm.png
 create mode 100644 client/public/images/pieces/Titan/wn.png
 create mode 100644 client/public/images/pieces/Titan/wo.png
 create mode 120000 client/public/images/pieces/Titan/wp.png
 create mode 100644 client/public/images/pieces/Titan/wq.png
 create mode 100644 client/public/images/pieces/Titan/wr.png
 create mode 100644 client/public/images/pieces/Titan/ws.png
 create mode 100644 client/public/images/pieces/Titan/wt.png
 create mode 100644 client/public/images/pieces/Titan/wu.png
 create mode 100644 client/public/images/pieces/Titan/wv.png
 create mode 100644 client/src/translations/rules/Evolution/en.pug
 create mode 100644 client/src/translations/rules/Evolution/es.pug
 create mode 100644 client/src/translations/rules/Evolution/fr.pug
 create mode 100644 client/src/translations/rules/Titan/en.pug
 create mode 100644 client/src/translations/rules/Titan/es.pug
 create mode 100644 client/src/translations/rules/Titan/fr.pug
 create mode 100644 client/src/variants/Evolution.js

diff --git a/client/download_objects.sh b/client/download_objects.sh
index 3cb25775..5ad81f5b 100755
--- a/client/download_objects.sh
+++ b/client/download_objects.sh
@@ -7,6 +7,12 @@ for color in "w" "b"; do
     wget -q -O public/images/pieces/Eightpieces/tmp_png/"$color$piece".png https://vchess.club/images/pieces/Eightpieces/tmp_png/"$color$piece".png
   done
 done
+for color in "w" "b"; do
+  for piece in "a" "c" "s" "t" "u" "v" "j" "l" "m" "o" "r" "n" "b" "q" "k"; do
+    rm -f public/images/pieces/Titan/"$color$piece".png
+    wget -q -O public/images/pieces/Titan/"$color$piece".png https://vchess.club/images/pieces/Titan/"$color$piece".png
+  done
+done
 for image in "Orda" "Archer" "Lancer" "Kheshig" "Yurt"; do
   rm -f /public/images/variants/Orda/"$image".png
   wget -q -O public/images/variants/Orda/"$image".png https://vchess.club/images/variants/Orda/"$image".png
diff --git a/client/public/images/pieces/Coregal/castle.svg b/client/public/images/pieces/Coregal/castle.svg
new file mode 100644
index 00000000..f60e4c39
--- /dev/null
+++ b/client/public/images/pieces/Coregal/castle.svg
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="Capa_1"
+   x="0px"
+   y="0px"
+   viewBox="0 0 380 380"
+   style="enable-background:new 0 0 380 380;"
+   xml:space="preserve"
+   sodipodi:docname="castle.svg"
+   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"><metadata
+   id="metadata39"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+   id="defs37" /><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="10"
+   gridtolerance="10"
+   guidetolerance="10"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="1920"
+   inkscape:window-height="1060"
+   id="namedview35"
+   showgrid="false"
+   inkscape:zoom="2.3236842"
+   inkscape:cx="190"
+   inkscape:cy="155.57191"
+   inkscape:window-x="0"
+   inkscape:window-y="20"
+   inkscape:window-maximized="0"
+   inkscape:current-layer="Capa_1" />
+<path
+   d="m 358.93345,163.94827 v -15.2498 h -12.77326 v 15.2498 h -14.6918 v -15.2498 h -12.77325 v 15.2498 h -16.47997 v -15.2498 h -12.77884 v 15.2498 h -14.689 v -15.2498 h -12.77885 v 15.2498 h -2.97001 v 12.07211 h 10.63209 v 32.54831 h -41.16218 v -52.12205 h -3.54374 V 116.43308 L 193.23412,64.845953 V 40.25232 c 7.80739,-7.369721 14.39007,-7.266921 24.80611,-4.452218 10.88171,2.937517 17.56403,2.945705 25.85756,-5.475663 -19.46302,11.54538 -31.47073,-21.4787188 -50.66272,-11.522637 v -1.904974 h -4.83176 v 49.221837 l -30.91008,50.313515 v 40.01355 h -3.54557 v 52.12206 h -41.21901 v -32.54831 h 10.63211 v -12.07212 h -2.97002 v -15.2498 h -12.77046 v 15.2498 H 92.922885 v -15.2498 H 80.149634 v 15.2498 H 63.671532 v -15.2498 H 50.892691 v 15.2498 H 36.204615 v -15.2498 H 23.425774 v 15.2498 h -2.970957 v 12.07212 h 10.632114 v 186.57464 h 68.59838 13.042399 156.90285 13.04429 68.5965 V 176.02038 h 10.63212 V 163.94827 Z M 81.745009,341.22367 H 62.070568 v -30.14663 c 0,-5.30464 4.405212,-9.60765 9.837687,-9.60765 5.432473,0 9.837686,4.30301 9.837686,9.60765 v 30.14663 z M 99.900446,184.39718 h -55.9909 v -8.3768 h 55.9909 z m 91.278234,-72.36172 c 12.22935,4.89161 11.67986,12.79444 11.67986,12.79444 v 26.21204 H 191.17868 179.50254 V 124.8299 c 0,0 -0.55136,-7.90283 11.67614,-12.79444 z m 33.76274,154.32655 v 75.77957 h -33.76274 -33.76087 v -75.77957 c 0,0 -1.5842,-22.84604 33.76087,-36.98871 35.35066,14.14175 33.76274,36.98871 33.76274,36.98871 z m 95.34723,74.86166 H 300.6142 v -30.14663 c 0,-5.30464 4.40522,-9.60765 9.83582,-9.60765 5.43341,0 9.83863,4.30301 9.83863,9.60765 z m 18.16102,-156.82649 h -55.99089 v -8.3768 h 55.99089 z"
+   id="path2"
+   style="stroke-width:0.920469" />
+<g
+   id="g4"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g6"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g8"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g10"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g12"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g14"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g16"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g18"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g20"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g22"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g24"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g26"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g28"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g30"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+<g
+   id="g32"
+   transform="matrix(0.93133451,0,0,0.90972973,14.226052,16.896828)">
+</g>
+</svg>
diff --git a/client/public/images/pieces/SOURCE b/client/public/images/pieces/SOURCE
index 8854bad4..c021dfca 100644
--- a/client/public/images/pieces/SOURCE
+++ b/client/public/images/pieces/SOURCE
@@ -10,3 +10,4 @@ the black Colorbound + Sittuyin pieces set, and he sent me the wizard and
 champion SVG files as well (modified from Wikipedia)
 Letter D: https://svgsilh.com/image/2051714.html
 Mammoth: https://www.flaticon.com/free-icon/mammoth_925138
+Castle icon: https://www.flaticon.com/free-icon/castle_89009
diff --git a/client/public/images/pieces/Titan/ba.png b/client/public/images/pieces/Titan/ba.png
new file mode 100644
index 00000000..66e816dc
--- /dev/null
+++ b/client/public/images/pieces/Titan/ba.png
@@ -0,0 +1 @@
+#$# git-fat 56f92909d57649cf4e83fee222d4d31ce97db450                 6873
diff --git a/client/public/images/pieces/Titan/bb.png b/client/public/images/pieces/Titan/bb.png
new file mode 100644
index 00000000..89579a8f
--- /dev/null
+++ b/client/public/images/pieces/Titan/bb.png
@@ -0,0 +1 @@
+#$# git-fat 11885764721183414a33ca0d4f361eef52cf59ea                 7864
diff --git a/client/public/images/pieces/Titan/bc.png b/client/public/images/pieces/Titan/bc.png
new file mode 100644
index 00000000..eb8089f9
--- /dev/null
+++ b/client/public/images/pieces/Titan/bc.png
@@ -0,0 +1 @@
+#$# git-fat 58e925edb232bba20e4c6b8bcd0ba030c400522c                 6496
diff --git a/client/public/images/pieces/Titan/bj.png b/client/public/images/pieces/Titan/bj.png
new file mode 100644
index 00000000..eb916f72
--- /dev/null
+++ b/client/public/images/pieces/Titan/bj.png
@@ -0,0 +1 @@
+#$# git-fat 377da5b2300e17ca49be08a724d85080141f6677                14023
diff --git a/client/public/images/pieces/Titan/bk.png b/client/public/images/pieces/Titan/bk.png
new file mode 100644
index 00000000..899596c9
--- /dev/null
+++ b/client/public/images/pieces/Titan/bk.png
@@ -0,0 +1 @@
+#$# git-fat 492668b6b23979d939af8640a747cad8c02a57f0                16668
diff --git a/client/public/images/pieces/Titan/bl.png b/client/public/images/pieces/Titan/bl.png
new file mode 100644
index 00000000..b26315ee
--- /dev/null
+++ b/client/public/images/pieces/Titan/bl.png
@@ -0,0 +1 @@
+#$# git-fat b25f663c757f85265d1b43057d8ab3c3a74c71a3                13518
diff --git a/client/public/images/pieces/Titan/bm.png b/client/public/images/pieces/Titan/bm.png
new file mode 100644
index 00000000..399656b9
--- /dev/null
+++ b/client/public/images/pieces/Titan/bm.png
@@ -0,0 +1 @@
+#$# git-fat 09ff40b2e6cdae5e3d9c1996b63663991d8e517d                 8711
diff --git a/client/public/images/pieces/Titan/bn.png b/client/public/images/pieces/Titan/bn.png
new file mode 100644
index 00000000..a38e7722
--- /dev/null
+++ b/client/public/images/pieces/Titan/bn.png
@@ -0,0 +1 @@
+#$# git-fat 637d993756ed5916271875bfeac445aa41943838                 9702
diff --git a/client/public/images/pieces/Titan/bo.png b/client/public/images/pieces/Titan/bo.png
new file mode 100644
index 00000000..3782be67
--- /dev/null
+++ b/client/public/images/pieces/Titan/bo.png
@@ -0,0 +1 @@
+#$# git-fat 4f8983d7172fb5c940b42acc292a6f1afadc4fdc                 8166
diff --git a/client/public/images/pieces/Titan/bp.png b/client/public/images/pieces/Titan/bp.png
new file mode 120000
index 00000000..9fa31726
--- /dev/null
+++ b/client/public/images/pieces/Titan/bp.png
@@ -0,0 +1 @@
+../Eightpieces/tmp_png/bp.png
\ No newline at end of file
diff --git a/client/public/images/pieces/Titan/bq.png b/client/public/images/pieces/Titan/bq.png
new file mode 100644
index 00000000..9d6f1699
--- /dev/null
+++ b/client/public/images/pieces/Titan/bq.png
@@ -0,0 +1 @@
+#$# git-fat 25aa2c85c5fd3aaf2bce47343b39c51edf89f342                11151
diff --git a/client/public/images/pieces/Titan/br.png b/client/public/images/pieces/Titan/br.png
new file mode 100644
index 00000000..71433c7e
--- /dev/null
+++ b/client/public/images/pieces/Titan/br.png
@@ -0,0 +1 @@
+#$# git-fat 788248951c09c73e32081deae6efce7c242766ac                 7581
diff --git a/client/public/images/pieces/Titan/bs.png b/client/public/images/pieces/Titan/bs.png
new file mode 100644
index 00000000..ccfeb31a
--- /dev/null
+++ b/client/public/images/pieces/Titan/bs.png
@@ -0,0 +1 @@
+#$# git-fat bad9ab44a4634519db15e8eaec1f3402d276bb91                 9803
diff --git a/client/public/images/pieces/Titan/bt.png b/client/public/images/pieces/Titan/bt.png
new file mode 100644
index 00000000..8ade9e26
--- /dev/null
+++ b/client/public/images/pieces/Titan/bt.png
@@ -0,0 +1 @@
+#$# git-fat 3d5d5dcd30705eb5b3b94f5f0492242429d53bac                 9181
diff --git a/client/public/images/pieces/Titan/bu.png b/client/public/images/pieces/Titan/bu.png
new file mode 100644
index 00000000..f62467ac
--- /dev/null
+++ b/client/public/images/pieces/Titan/bu.png
@@ -0,0 +1 @@
+#$# git-fat d64664f3b195b0a59412918bba2c16d052913793                 7126
diff --git a/client/public/images/pieces/Titan/bv.png b/client/public/images/pieces/Titan/bv.png
new file mode 100644
index 00000000..00b59f50
--- /dev/null
+++ b/client/public/images/pieces/Titan/bv.png
@@ -0,0 +1 @@
+#$# git-fat 58d858d675e11f6fb7d8bc4884b6f425de51689b                 6636
diff --git a/client/public/images/pieces/Titan/script.sh b/client/public/images/pieces/Titan/script.sh
new file mode 100644
index 00000000..95ffa59d
--- /dev/null
+++ b/client/public/images/pieces/Titan/script.sh
@@ -0,0 +1,13 @@
+taille=64
+convert wn.png -resize "$taille"x"$taille" wn_small.png
+convert wb.png -resize "$taille"x"$taille" wb_small.png
+convert bn.png -resize "$taille"x"$taille" bn_small.png
+convert bb.png -resize "$taille"x"$taille" bb_small.png
+# GIMP: manual fill by color (yellow/red). Then:
+for color in w b; do
+  for piece in r n b q k; do
+    convert -composite -gravity center $color$piece.png "$color"n_small.png $color"$piece"_1.png
+    convert -composite -gravity center $color$piece.png "$color"b_small.png $color"$piece"_2.png
+  done
+done
+# Finally: manual renaming (TODO)
diff --git a/client/public/images/pieces/Titan/wa.png b/client/public/images/pieces/Titan/wa.png
new file mode 100644
index 00000000..74c935c4
--- /dev/null
+++ b/client/public/images/pieces/Titan/wa.png
@@ -0,0 +1 @@
+#$# git-fat 2cfeae196dcf329318e9aa32b4e8fd83743cf49f                11469
diff --git a/client/public/images/pieces/Titan/wb.png b/client/public/images/pieces/Titan/wb.png
new file mode 100644
index 00000000..4153b89f
--- /dev/null
+++ b/client/public/images/pieces/Titan/wb.png
@@ -0,0 +1 @@
+#$# git-fat 9b675b6208624b902fbbd6a433da8d1c8f295032                11911
diff --git a/client/public/images/pieces/Titan/wc.png b/client/public/images/pieces/Titan/wc.png
new file mode 100644
index 00000000..1da7092c
--- /dev/null
+++ b/client/public/images/pieces/Titan/wc.png
@@ -0,0 +1 @@
+#$# git-fat e35d4bc6f8d216ae4bd65874699dcd5eb2d4eeac                11735
diff --git a/client/public/images/pieces/Titan/wj.png b/client/public/images/pieces/Titan/wj.png
new file mode 100644
index 00000000..6d369e79
--- /dev/null
+++ b/client/public/images/pieces/Titan/wj.png
@@ -0,0 +1 @@
+#$# git-fat 313f9037f964c36f83e04345e7f5d48d033eef0a                13086
diff --git a/client/public/images/pieces/Titan/wk.png b/client/public/images/pieces/Titan/wk.png
new file mode 100644
index 00000000..86fcf430
--- /dev/null
+++ b/client/public/images/pieces/Titan/wk.png
@@ -0,0 +1 @@
+#$# git-fat dce8ad8bcb0b2b000f51e4b931815069be8cdf4f                14204
diff --git a/client/public/images/pieces/Titan/wl.png b/client/public/images/pieces/Titan/wl.png
new file mode 100644
index 00000000..a196e4e1
--- /dev/null
+++ b/client/public/images/pieces/Titan/wl.png
@@ -0,0 +1 @@
+#$# git-fat 38d8d25e99dd6579f221e1287cae92ea0f390e03                13139
diff --git a/client/public/images/pieces/Titan/wm.png b/client/public/images/pieces/Titan/wm.png
new file mode 100644
index 00000000..18dfc06b
--- /dev/null
+++ b/client/public/images/pieces/Titan/wm.png
@@ -0,0 +1 @@
+#$# git-fat 2b3afd85b70abdbd3aea594aed7c997974c75c2c                11727
diff --git a/client/public/images/pieces/Titan/wn.png b/client/public/images/pieces/Titan/wn.png
new file mode 100644
index 00000000..f8d66c93
--- /dev/null
+++ b/client/public/images/pieces/Titan/wn.png
@@ -0,0 +1 @@
+#$# git-fat 9b7b3ada1693eab7198074517a25fff77601dcc5                11975
diff --git a/client/public/images/pieces/Titan/wo.png b/client/public/images/pieces/Titan/wo.png
new file mode 100644
index 00000000..aad608f5
--- /dev/null
+++ b/client/public/images/pieces/Titan/wo.png
@@ -0,0 +1 @@
+#$# git-fat 192a9061313c6dd2c0dad24e3d4260e956452d5e                11849
diff --git a/client/public/images/pieces/Titan/wp.png b/client/public/images/pieces/Titan/wp.png
new file mode 120000
index 00000000..b1c56708
--- /dev/null
+++ b/client/public/images/pieces/Titan/wp.png
@@ -0,0 +1 @@
+../Eightpieces/tmp_png/wp.png
\ No newline at end of file
diff --git a/client/public/images/pieces/Titan/wq.png b/client/public/images/pieces/Titan/wq.png
new file mode 100644
index 00000000..da98cb76
--- /dev/null
+++ b/client/public/images/pieces/Titan/wq.png
@@ -0,0 +1 @@
+#$# git-fat 8d55cfeab39f3ffefe0249d3123f3492dce591d2                16221
diff --git a/client/public/images/pieces/Titan/wr.png b/client/public/images/pieces/Titan/wr.png
new file mode 100644
index 00000000..310782f4
--- /dev/null
+++ b/client/public/images/pieces/Titan/wr.png
@@ -0,0 +1 @@
+#$# git-fat 58fffbf851cf42da824c472c606af380b097f91f                 8644
diff --git a/client/public/images/pieces/Titan/ws.png b/client/public/images/pieces/Titan/ws.png
new file mode 100644
index 00000000..b6a224a8
--- /dev/null
+++ b/client/public/images/pieces/Titan/ws.png
@@ -0,0 +1 @@
+#$# git-fat 67b9dc06c174f688dda8a94132192089455232ae                14810
diff --git a/client/public/images/pieces/Titan/wt.png b/client/public/images/pieces/Titan/wt.png
new file mode 100644
index 00000000..b4bb52da
--- /dev/null
+++ b/client/public/images/pieces/Titan/wt.png
@@ -0,0 +1 @@
+#$# git-fat 53bf6c11b93148cf0cdb2135769bac7fbe2df52f                15043
diff --git a/client/public/images/pieces/Titan/wu.png b/client/public/images/pieces/Titan/wu.png
new file mode 100644
index 00000000..6917106e
--- /dev/null
+++ b/client/public/images/pieces/Titan/wu.png
@@ -0,0 +1 @@
+#$# git-fat 488f25c90f9f6b1e4c6b07563ab8d860edee678e                10146
diff --git a/client/public/images/pieces/Titan/wv.png b/client/public/images/pieces/Titan/wv.png
new file mode 100644
index 00000000..420aee21
--- /dev/null
+++ b/client/public/images/pieces/Titan/wv.png
@@ -0,0 +1 @@
+#$# git-fat 2a69102b1577f207dcab57a0e3d76826b9c0849a                10249
diff --git a/client/src/base_rules.js b/client/src/base_rules.js
index acf6eff0..d17ebe67 100644
--- a/client/src/base_rules.js
+++ b/client/src/base_rules.js
@@ -30,6 +30,7 @@ export const Move = class Move {
 // NOTE: x coords = top to bottom; y = left to right
 // (from white player perspective)
 export const ChessRules = class ChessRules {
+
   //////////////
   // MISC UTILS
 
@@ -518,7 +519,6 @@ export const ChessRules = class ChessRules {
 
   // Scan board for kings positions
   scanKings(fen) {
-    this.INIT_COL_KING = { w: -1, b: -1 };
     // Squares of white and black king:
     this.kingPos = { w: [-1, -1], b: [-1, -1] };
     const fenRows = V.ParseFen(fen).position.split("/");
@@ -529,11 +529,9 @@ export const ChessRules = class ChessRules {
         switch (fenRows[i].charAt(j)) {
           case "k":
             this.kingPos["b"] = [i, k];
-            this.INIT_COL_KING["b"] = k;
             break;
           case "K":
             this.kingPos["w"] = [i, k];
-            this.INIT_COL_KING["w"] = k;
             break;
           default: {
             const num = parseInt(fenRows[i].charAt(j), 10);
@@ -667,7 +665,7 @@ export const ChessRules = class ChessRules {
   // tr: transformation
   getBasicMove([sx, sy], [ex, ey], tr) {
     const initColor = this.getColor(sx, sy);
-    const initPiece = this.getPiece(sx, sy);
+    const initPiece = this.board[sx][sy].charAt(1);
     let mv = new Move({
       appear: [
         new PiPo({
@@ -694,7 +692,7 @@ export const ChessRules = class ChessRules {
           x: ex,
           y: ey,
           c: this.getColor(ex, ey),
-          p: this.getPiece(ex, ey)
+          p: this.board[ex][ey].charAt(1)
         })
       );
     }
@@ -735,8 +733,7 @@ export const ChessRules = class ChessRules {
       enpassantMove.vanish.push({
         x: x,
         y: epSquare.y,
-        // Captured piece is usually a pawn, but next line seems harmless
-        p: this.getPiece(x, epSquare.y),
+        p: this.board[x][epSquare.y].charAt(1),
         c: this.getColor(x, epSquare.y)
       });
     }
@@ -879,25 +876,22 @@ export const ChessRules = class ChessRules {
       V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
       "oneStep"
     );
-    if (V.HasCastle) moves = moves.concat(this.getCastleMoves(sq));
+    if (V.HasCastle && this.castleFlags[this.turn].some(v => v < V.size.y))
+      moves = moves.concat(this.getCastleMoves(sq));
     return moves;
   }
 
   // "castleInCheck" arg to let some variants castle under check
-  getCastleMoves([x, y], castleInCheck, castleWith) {
+  getCastleMoves([x, y], finalSquares, castleInCheck, castleWith) {
     const c = this.getColor(x, y);
-    if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c])
-      return []; //x isn't first rank, or king has moved (shortcut)
 
     // Castling ?
     const oppCol = V.GetOppCol(c);
     let moves = [];
     let i = 0;
     // King, then rook:
-    const finalSquares = [
-      [2, 3],
-      [V.size.y - 2, V.size.y - 3]
-    ];
+    finalSquares = finalSquares || [ [2, 3], [V.size.y - 2, V.size.y - 3] ];
+    const castlingKing = this.board[x][y].charAt(1);
     castlingCheck: for (
       let castleSide = 0;
       castleSide < 2;
@@ -908,30 +902,28 @@ export const ChessRules = class ChessRules {
 
       // NOTE: in some variants this is not a rook
       const rookPos = this.castleFlags[c][castleSide];
+      const castlingPiece = this.board[x][rookPos].charAt(1);
       if (
         this.board[x][rookPos] == V.EMPTY ||
         this.getColor(x, rookPos) != c ||
-        (!!castleWith && !castleWith.includes(this.getPiece(x, rookPos)))
+        (!!castleWith && !castleWith.includes(castlingPiece))
       ) {
         // Rook is not here, or changed color (see Benedict)
         continue;
       }
 
       // Nothing on the path of the king ? (and no checks)
-      const castlingPiece = this.getPiece(x, rookPos);
       const finDist = finalSquares[castleSide][0] - y;
       let step = finDist / Math.max(1, Math.abs(finDist));
       i = y;
       do {
         if (
-          // NOTE: "castling" arg is used by some variants (Monster),
-          // where "isAttacked" is overloaded in an infinite-recursive way.
-          // TODO: not used anymore (Monster + Doublemove2 are simplified).
-          (!castleInCheck && this.isAttacked([x, i], oppCol, "castling")) ||
-          (this.board[x][i] != V.EMPTY &&
+          (!castleInCheck && this.isAttacked([x, i], oppCol)) ||
+          (
+            this.board[x][i] != V.EMPTY &&
             // NOTE: next check is enough, because of chessboard constraints
-            (this.getColor(x, i) != c ||
-              ![V.KING, castlingPiece].includes(this.getPiece(x, i))))
+            (this.getColor(x, i) != c || ![y, rookPos].includes(i))
+          )
         ) {
           continue castlingCheck;
         }
@@ -950,7 +942,7 @@ export const ChessRules = class ChessRules {
           finalSquares[castleSide][i] != rookPos &&
           this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
           (
-            this.getPiece(x, finalSquares[castleSide][i]) != V.KING ||
+            finalSquares[castleSide][i] != y ||
             this.getColor(x, finalSquares[castleSide][i]) != c
           )
         ) {
@@ -965,7 +957,7 @@ export const ChessRules = class ChessRules {
             new PiPo({
               x: x,
               y: finalSquares[castleSide][0],
-              p: V.KING,
+              p: castlingKing,
               c: c
             }),
             new PiPo({
@@ -976,7 +968,8 @@ export const ChessRules = class ChessRules {
             })
           ],
           vanish: [
-            new PiPo({ x: x, y: y, p: V.KING, c: c }),
+            // King might be initially disguised (Titan...)
+            new PiPo({ x: x, y: y, p: this.board[x][y][1], c: c }),
             new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c })
           ],
           end:
@@ -1479,4 +1472,5 @@ export const ChessRules = class ChessRules {
       )
     );
   }
+
 };
diff --git a/client/src/translations/rules/Evolution/en.pug b/client/src/translations/rules/Evolution/en.pug
new file mode 100644
index 00000000..17241bfd
--- /dev/null
+++ b/client/src/translations/rules/Evolution/en.pug
@@ -0,0 +1,25 @@
+p.boxed
+  | TODO
+
+p.
+  Besides the usual game end conditions, White can win by preventing black
+  long castle. And, Black can win by castling long.
+
+p For example after 1.e4 e5 2.Bc4, Nc6?? loses immediatly: 3.Bxf7+
+
+figure.diagram-container
+  .diagram
+    | fen:r1bqkbnr/pppp1Bpp/2n5/4p3/4P3/8/PPPP1PPP/RNBQK1NR:
+  figcaption After 1.e4 e5 2.Bc4 Nc6 3.Bxf7+ 1-0
+
+h3 Source
+
+p
+  a(href="https://www.chessvariants.com/winning.dir/castle.html")
+    | Castle chess
+  | &nbsp;on chessvariants.com. See also 
+  a(href="http://cinquantesignes.blogspot.com/2020/09/castlechess.html")
+    | this post
+  | &nbsp;giving some clarifications and advices.
+
+p Inventor: Éric Angelini (1996)
diff --git a/client/src/translations/rules/Evolution/es.pug b/client/src/translations/rules/Evolution/es.pug
new file mode 100644
index 00000000..3a33838b
--- /dev/null
+++ b/client/src/translations/rules/Evolution/es.pug
@@ -0,0 +1,2 @@
+p.boxed
+  | TODO
diff --git a/client/src/translations/rules/Evolution/fr.pug b/client/src/translations/rules/Evolution/fr.pug
new file mode 100644
index 00000000..68accd76
--- /dev/null
+++ b/client/src/translations/rules/Evolution/fr.pug
@@ -0,0 +1,5 @@
+p.boxed
+  | Les pièces à longue portée peuvent sauter par dessus un obstacle
+  | lorsqu'elle se trouvent sur la première rangée.
+
+p TODO
diff --git a/client/src/translations/rules/Titan/en.pug b/client/src/translations/rules/Titan/en.pug
new file mode 100644
index 00000000..151118a4
--- /dev/null
+++ b/client/src/translations/rules/Titan/en.pug
@@ -0,0 +1,40 @@
+p.boxed
+  | Choose squares for new pieces to appear when the initial ones are moved.
+  | One extra knight (first move), and one extra bishop (second move).
+
+p.
+  Everything is the same as in orthodox rules, except that new pieces appear
+  at initially selected locations: a knight and a bishop.
+
+p.
+  The first move of the game selects the square for the knight
+  (just click on a square on the first rank),
+  and the second move picks the square for the bishop.
+  When an "augmented piece" moves, the extra piece enter into play on the
+  initial square.
+
+figure.diagram-container
+  .diagram
+    | fen:rnaqkbor/pppppppp/8/8/8/8/PPPPPPPP/RMBQKCNR:
+  figcaption Augmented pieces on b1, f1, c8 and g8.
+
+p.
+  Castling is always possible. If both pieces involved are augmented,
+  then two extra pieces appear at the end of the move.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQJ2V:
+  .diagram.diag22
+    | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQNRKB:
+  figcaption Before and after 0-0.
+
+h3 More information
+
+p
+  | The variant idea was suggested recently, and corresponds to 
+  a(href="https://musketeerchess.net/home/index.html")
+    | Musketeer Chess
+  | , using non-fairy pieces.
+
+p Inventor: Zied Haddad (2020)
diff --git a/client/src/translations/rules/Titan/es.pug b/client/src/translations/rules/Titan/es.pug
new file mode 100644
index 00000000..deaa2cfe
--- /dev/null
+++ b/client/src/translations/rules/Titan/es.pug
@@ -0,0 +1,42 @@
+p.boxed
+  | Elige espacios donde aparezcan nuevas piezas,
+  | durante un primer desplazamiento desde la primera fila.
+  | Un caballo adicional (primer movimiento) y un alfil adicional
+  | (segundo movimiento).
+
+p.
+  Todo va como el ajedrez ortodoxo, excepto las nuevas
+  piezas que aparecen en lugares designados: un caballo y un alfil.
+
+p.
+  El primer movimiento del juego selecciona una casilla para el caballo
+  (simplemente haga clic en un cuadro en la primera fila),
+  y el segundo movimiento designa una casilla para el alfil.
+  Cuando se mueve una "pieza aumentada", entra la pieza adicional
+  en el juego en la casilla de salida.
+
+figure.diagram-container
+  .diagram
+    | fen:rnaqkbor/pppppppp/8/8/8/8/PPPPPPPP/RMBQKCNR:
+  figcaption Piezas aumentadas en b1, f1, c8 y g8.
+
+p.
+  El enroque siempre es posible. Si las dos piezas involucradas son
+  aumentado, luego aparecen dos nuevas piezas después del movimiento.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQJ2V:
+  .diagram.diag22
+    | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQNRKB:
+  figcaption Antes y después de 0-0.
+
+h3 Más información
+
+p
+  | Esta idea ha sido sugerida recientemente y corresponde a 
+  a(href = "https://musketeerchess.net/home/index.html")
+    | Ajedrez Mosquetero
+  | , con piezas estándar.
+
+p Inventor: Zied Haddad (2020)
diff --git a/client/src/translations/rules/Titan/fr.pug b/client/src/translations/rules/Titan/fr.pug
new file mode 100644
index 00000000..ecaf09c3
--- /dev/null
+++ b/client/src/translations/rules/Titan/fr.pug
@@ -0,0 +1,41 @@
+p.boxed
+  | Choisissez des cases d'appartition de nouvelles pièces,
+  | lors d'un premier déplacement depuis la première rangée.
+  | Un cavalier (premier coup) et un fou supplémentaires (second coup).
+
+p.
+  Tout se déroule comme aux échecs orthodoxes, à l'exception de nouvelles
+  pièces apparaissant à des endroits désignés : un cavalier et un fou.
+
+p.
+  Le premier coup de la partie sélectionne une case pour le cavalier
+  (cliquez simplement sur une case de la première rangée),
+  et le second coup désigne une case pour le fou.
+  Quand une "pièce augmentée" se déplace, la pièce supplémentaire entre
+  dans le jeu sur la case de départ.
+
+figure.diagram-container
+  .diagram
+    | fen:rnaqkbor/pppppppp/8/8/8/8/PPPPPPPP/RMBQKCNR:
+  figcaption Pièces augmentées sur b1, f1, c8 et g8.
+
+p.
+  Le roque est toujours possible. Si les deux pièces impliquées sont
+  augmentées, alors deux nouvelles pièces apparaissent après le coup.
+
+figure.diagram-container
+  .diagram.diag12
+    | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQJ2V:
+  .diagram.diag22
+    | fen:rm1qkbor/ppp2ppp/4b3/3pp3/8/5NP1/PPPPPPBP/RNBQNRKB:
+  figcaption Avant et après 0-0.
+
+h3 Plus d'information
+
+p
+  | Cette idée a été suggérée récemment, et correspond aux 
+  a(href="https://musketeerchess.net/home/index.html")
+    | Échecs Mousquetaires
+  | , avec des pièces standard.
+
+p Inventeur : Zied Haddad (2020)
diff --git a/client/src/variants/Absorption.js b/client/src/variants/Absorption.js
index 64c03e68..c6e1de74 100644
--- a/client/src/variants/Absorption.js
+++ b/client/src/variants/Absorption.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class AbsorptionRules extends ChessRules {
+
   getPpath(b) {
     if ([V.BN, V.RN, V.QN].includes(b[1])) return "Absorption/" + b;
     return b;
@@ -147,4 +148,5 @@ export class AbsorptionRules extends ChessRules {
       notation += "=" + move.appear[0].p.toUpperCase();
     return notation;
   }
+
 };
diff --git a/client/src/variants/Alice.js b/client/src/variants/Alice.js
index 61fcd015..16ccb84d 100644
--- a/client/src/variants/Alice.js
+++ b/client/src/variants/Alice.js
@@ -5,6 +5,7 @@ import { ArrayFun } from "@/utils/array";
 // TODO? atLeastOneMove() would be more efficient if rewritten here
 // (less sideBoard computations)
 export class AliceRules extends ChessRules {
+
   static get ALICE_PIECES() {
     return {
       s: "p",
@@ -366,4 +367,5 @@ export class AliceRules extends ChessRules {
       notation += "=" + move.appear[0].p.toUpperCase();
     return notation;
   }
+
 };
diff --git a/client/src/variants/Allmate1.js b/client/src/variants/Allmate1.js
index 0e7e8ed5..88599e49 100644
--- a/client/src/variants/Allmate1.js
+++ b/client/src/variants/Allmate1.js
@@ -1,6 +1,7 @@
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 export class Allmate1Rules extends ChessRules {
+
   static get HasEnpassant() {
     return false;
   }
@@ -125,7 +126,7 @@ export class Allmate1Rules extends ChessRules {
 
   // No "under check" conditions in castling
   getCastleMoves(sq) {
-    return super.getCastleMoves(sq, "castleInCheck");
+    return super.getCastleMoves(sq, null, "castleInCheck");
   }
 
   // TODO: allow pieces to "commit suicide"? (Currently yes except king)
@@ -201,10 +202,10 @@ export class Allmate1Rules extends ChessRules {
           this.kingPos[this.turn] = [-1, -1];
         // Or maybe a rook?
         else if (v.p == V.ROOK) {
-          if (v.y < this.INIT_COL_KING[v.c])
+          if (v.y < this.kingPos[v.c][1])
             this.castleFlags[v.c][0] = 8;
           else
-            // v.y > this.INIT_COL_KING[v.c]
+            // v.y > this.kingPos[v.c][1]
             this.castleFlags[v.c][1] = 8;
         }
       }
@@ -248,4 +249,5 @@ export class Allmate1Rules extends ChessRules {
     }
     return notation;
   }
+
 };
diff --git a/client/src/variants/Allmate2.js b/client/src/variants/Allmate2.js
index 259faa74..f4abbe33 100644
--- a/client/src/variants/Allmate2.js
+++ b/client/src/variants/Allmate2.js
@@ -1,6 +1,7 @@
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 export class Allmate2Rules extends ChessRules {
+
   static get HasEnpassant() {
     return false;
   }
@@ -108,7 +109,7 @@ export class Allmate2Rules extends ChessRules {
 
   // No "under check" conditions in castling
   getCastleMoves(sq) {
-    return super.getCastleMoves(sq, "castleInCheck");
+    return super.getCastleMoves(sq, null, "castleInCheck");
   }
 
   // TODO: allow pieces to "commit suicide"? (Currently yes except king)
@@ -184,10 +185,10 @@ export class Allmate2Rules extends ChessRules {
           this.kingPos[this.turn] = [-1, -1];
         // Or maybe a rook?
         else if (v.p == V.ROOK) {
-          if (v.y < this.INIT_COL_KING[v.c])
+          if (v.y < this.kingPos[v.c][1])
             this.castleFlags[v.c][0] = 8;
           else
-            // v.y > this.INIT_COL_KING[v.c]
+            // v.y > this.kingPos[v.c][1]
             this.castleFlags[v.c][1] = 8;
         }
       }
@@ -231,4 +232,5 @@ export class Allmate2Rules extends ChessRules {
     }
     return notation;
   }
+
 };
diff --git a/client/src/variants/Ambiguous.js b/client/src/variants/Ambiguous.js
index 17e5283a..36824db8 100644
--- a/client/src/variants/Ambiguous.js
+++ b/client/src/variants/Ambiguous.js
@@ -3,6 +3,7 @@ import { randInt, shuffle } from "@/utils/alea";
 import { ArrayFun } from "@/utils/array";
 
 export class AmbiguousRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -292,4 +293,5 @@ export class AmbiguousRules extends ChessRules {
     else move.vanish[1].p = V.TARGET_CODE[move.vanish[1].p];
     return notation;
   }
+
 };
diff --git a/client/src/variants/Antiking1.js b/client/src/variants/Antiking1.js
index 07dae26c..92034a30 100644
--- a/client/src/variants/Antiking1.js
+++ b/client/src/variants/Antiking1.js
@@ -4,6 +4,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class Antiking1Rules extends BerolinaRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -221,4 +222,5 @@ export class Antiking1Rules extends BerolinaRules {
   static get SEARCH_DEPTH() {
     return 2;
   }
+
 };
diff --git a/client/src/variants/Antiking2.js b/client/src/variants/Antiking2.js
index 762ca5c8..df764583 100644
--- a/client/src/variants/Antiking2.js
+++ b/client/src/variants/Antiking2.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class Antiking2Rules extends ChessRules {
+
   static get ANTIKING() {
     return "a";
   }
@@ -232,4 +233,5 @@ export class Antiking2Rules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 2;
   }
+
 };
diff --git a/client/src/variants/Antimatter.js b/client/src/variants/Antimatter.js
index f8f4c73e..6d3d36d4 100644
--- a/client/src/variants/Antimatter.js
+++ b/client/src/variants/Antimatter.js
@@ -1,9 +1,9 @@
 import { ChessRules } from "@/base_rules";
 
 export class AntimatterRules extends ChessRules {
+
   getPotentialMovesFrom([x, y]) {
     let moves = super.getPotentialMovesFrom([x, y]);
-
     // Handle "matter collisions"
     moves.forEach(m => {
       if (
@@ -14,7 +14,7 @@ export class AntimatterRules extends ChessRules {
         m.appear.pop();
       }
     });
-
     return moves;
   }
+
 };
diff --git a/client/src/variants/Apocalypse.js b/client/src/variants/Apocalypse.js
index 77d17c0e..e817296a 100644
--- a/client/src/variants/Apocalypse.js
+++ b/client/src/variants/Apocalypse.js
@@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class ApocalypseRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -512,4 +513,5 @@ export class ApocalypseRules extends ChessRules {
       V.CoordsToSquare(move.end)
     );
   }
+
 };
diff --git a/client/src/variants/Arena.js b/client/src/variants/Arena.js
index 62d2802a..285dd4c8 100644
--- a/client/src/variants/Arena.js
+++ b/client/src/variants/Arena.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class ArenaRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -143,4 +144,5 @@ export class ArenaRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 4;
   }
+
 };
diff --git a/client/src/variants/Atomic.js b/client/src/variants/Atomic.js
index 9675c58a..e2f4bdf5 100644
--- a/client/src/variants/Atomic.js
+++ b/client/src/variants/Atomic.js
@@ -1,6 +1,7 @@
 import { ChessRules, PiPo } from "@/base_rules";
 
 export class AtomicRules extends ChessRules {
+
   getPotentialMovesFrom([x, y]) {
     let moves = super.getPotentialMovesFrom([x, y]);
 
@@ -149,4 +150,5 @@ export class AtomicRules extends ChessRules {
     if (!this.isAttacked(kp, V.GetOppCol(color))) return "1/2";
     return color == "w" ? "0-1" : "1-0"; //checkmate
   }
+
 };
diff --git a/client/src/variants/Balaklava.js b/client/src/variants/Balaklava.js
index 4a720f04..835aa965 100644
--- a/client/src/variants/Balaklava.js
+++ b/client/src/variants/Balaklava.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class BalaklavaRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -117,4 +118,5 @@ export class BalaklavaRules extends ChessRules {
       ChessRules.VALUES
     );
   }
+
 };
diff --git a/client/src/variants/Ball.js b/client/src/variants/Ball.js
index 056ec61b..04ad1feb 100644
--- a/client/src/variants/Ball.js
+++ b/client/src/variants/Ball.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { shuffle } from "@/utils/alea";
 
 export class BallRules extends ChessRules {
+
   static get Lines() {
     return [
       // White goal:
@@ -565,4 +566,5 @@ export class BallRules extends ChessRules {
       finalSquare
     );
   }
+
 };
diff --git a/client/src/variants/Baroque.js b/client/src/variants/Baroque.js
index 4ff23a00..063b1822 100644
--- a/client/src/variants/Baroque.js
+++ b/client/src/variants/Baroque.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { shuffle } from "@/utils/alea";
 
 export class BaroqueRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -586,4 +587,5 @@ export class BaroqueRules extends ChessRules {
     if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X";
     return notation;
   }
+
 };
diff --git a/client/src/variants/Benedict.js b/client/src/variants/Benedict.js
index 039aaeaf..40d7a091 100644
--- a/client/src/variants/Benedict.js
+++ b/client/src/variants/Benedict.js
@@ -1,6 +1,7 @@
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 export class BenedictRules extends ChessRules {
+
   static get HasEnpassant() {
     return false;
   }
@@ -166,4 +167,5 @@ export class BenedictRules extends ChessRules {
     };
     return super.getNotation(basicMove);
   }
+
 };
diff --git a/client/src/variants/Berolina.js b/client/src/variants/Berolina.js
index a7f7afa7..0765d4b9 100644
--- a/client/src/variants/Berolina.js
+++ b/client/src/variants/Berolina.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class BerolinaRules extends ChessRules {
+
   // En-passant after 2-sq jump
   getEpSquare(moveOrSquare) {
     if (!moveOrSquare) return undefined;
@@ -161,4 +162,5 @@ export class BerolinaRules extends ChessRules {
     }
     return super.getNotation(move); //all other pieces are orthodox
   }
+
 };
diff --git a/client/src/variants/Bicolour.js b/client/src/variants/Bicolour.js
index eaa66d17..795f4ba4 100644
--- a/client/src/variants/Bicolour.js
+++ b/client/src/variants/Bicolour.js
@@ -3,6 +3,7 @@ import { randInt } from "@/utils/alea";
 import { ArrayFun } from "@/utils/array";
 
 export class BicolourRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -108,4 +109,5 @@ export class BicolourRules extends ChessRules {
       this.isAttacked(this.kingPos[color], 'b')
     );
   }
+
 };
diff --git a/client/src/variants/Bishopawns.js b/client/src/variants/Bishopawns.js
index 67afe66c..feecd458 100644
--- a/client/src/variants/Bishopawns.js
+++ b/client/src/variants/Bishopawns.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class BishopawnsRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -52,4 +53,5 @@ export class BishopawnsRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 4;
   }
+
 };
diff --git a/client/src/variants/Cannibal.js b/client/src/variants/Cannibal.js
index bd5524f7..1d8469e2 100644
--- a/client/src/variants/Cannibal.js
+++ b/client/src/variants/Cannibal.js
@@ -1,6 +1,7 @@
 import { ChessRules, Move, PiPo } from "@/base_rules";
 
 export class CannibalRules extends ChessRules {
+
   static get KING_CODE() {
     return {
       'p': 's',
@@ -246,4 +247,5 @@ export class CannibalRules extends ChessRules {
     }
     return notation;
   }
+
 };
diff --git a/client/src/variants/Capture.js b/client/src/variants/Capture.js
index c5f86dcd..f919c60e 100644
--- a/client/src/variants/Capture.js
+++ b/client/src/variants/Capture.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class CaptureRules extends ChessRules {
+
   // Trim all non-capturing moves
   static KeepCaptures(moves) {
     return moves.filter(m => m.vanish.length == 2 && m.appear.length == 1);
@@ -43,4 +44,5 @@ export class CaptureRules extends ChessRules {
       return V.KeepCaptures(moves);
     return moves;
   }
+
 };
diff --git a/client/src/variants/Castle.js b/client/src/variants/Castle.js
index e6b0470d..cf44ba56 100644
--- a/client/src/variants/Castle.js
+++ b/client/src/variants/Castle.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class CastleRules extends ChessRules {
+
   getCurrentScore() {
     const baseScore = super.getCurrentScore();
     if (baseScore != '*') return baseScore;
@@ -11,4 +12,5 @@ export class CastleRules extends ChessRules {
     }
     return '*';
   }
+
 };
diff --git a/client/src/variants/Chakart.js b/client/src/variants/Chakart.js
index 669ade11..1795e30c 100644
--- a/client/src/variants/Chakart.js
+++ b/client/src/variants/Chakart.js
@@ -4,6 +4,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class ChakartRules extends ChessRules {
+
   static get PawnSpecs() {
     return SuicideRules.PawnSpecs;
   }
@@ -1470,4 +1471,5 @@ export class ChakartRules extends ChessRules {
     }
     return notation;
   }
+
 };
diff --git a/client/src/variants/Checkered1.js b/client/src/variants/Checkered1.js
index fd0d68df..71f9b920 100644
--- a/client/src/variants/Checkered1.js
+++ b/client/src/variants/Checkered1.js
@@ -1,6 +1,7 @@
 import { ChessRules, Move, PiPo } from "@/base_rules";
 
 export class Checkered1Rules extends ChessRules {
+
   static board2fen(b) {
     const checkered_codes = {
       p: "s",
@@ -678,4 +679,5 @@ export class Checkered1Rules extends ChessRules {
       notation += "=" + move.appear[0].p.toUpperCase();
     return notation;
   }
+
 };
diff --git a/client/src/variants/Checkered2.js b/client/src/variants/Checkered2.js
index db288766..9c883f71 100644
--- a/client/src/variants/Checkered2.js
+++ b/client/src/variants/Checkered2.js
@@ -1,6 +1,7 @@
 import { ChessRules, Move, PiPo } from "@/base_rules";
 
 export class Checkered2Rules extends ChessRules {
+
   static board2fen(b) {
     const checkered_codes = {
       p: "s",
@@ -462,4 +463,5 @@ export class Checkered2Rules extends ChessRules {
       notation += "=" + move.appear[0].p.toUpperCase();
     return notation;
   }
+
 };
diff --git a/client/src/variants/Checkless.js b/client/src/variants/Checkless.js
index b304a483..63c35c66 100644
--- a/client/src/variants/Checkless.js
+++ b/client/src/variants/Checkless.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class ChecklessRules extends ChessRules {
+
   // Cannot use super.atLeastOneMove: lead to infinite recursion
   atLeastOneMove_aux() {
     const color = this.turn;
@@ -45,4 +46,5 @@ export class ChecklessRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 2;
   }
+
 };
diff --git a/client/src/variants/Circular.js b/client/src/variants/Circular.js
index d1f8230b..aa700bb1 100644
--- a/client/src/variants/Circular.js
+++ b/client/src/variants/Circular.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { shuffle } from "@/utils/alea";
 
 export class CircularRules extends ChessRules {
+
   static get HasCastle() {
     return false;
   }
@@ -221,4 +222,5 @@ export class CircularRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 2;
   }
+
 };
diff --git a/client/src/variants/Clorange.js b/client/src/variants/Clorange.js
index e96d8514..d4ec1ef3 100644
--- a/client/src/variants/Clorange.js
+++ b/client/src/variants/Clorange.js
@@ -2,6 +2,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 
 export class ClorangeRules extends ChessRules {
+
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
@@ -314,4 +315,5 @@ export class ClorangeRules extends ChessRules {
       move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
     return piece + "@" + V.CoordsToSquare(move.end);
   }
+
 };
diff --git a/client/src/variants/Colorbound.js b/client/src/variants/Colorbound.js
index 7e0a6222..b806184c 100644
--- a/client/src/variants/Colorbound.js
+++ b/client/src/variants/Colorbound.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class ColorboundRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -184,93 +185,14 @@ export class ColorboundRules extends ChessRules {
     );
   }
 
-  // TODO: really find a way to avoid duolicating most of the castling code
-  // each time: here just the queenside castling squares change for black.
   getCastleMoves([x, y]) {
-    const c = this.getColor(x, y);
-    if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c])
-      return [];
-
-    const oppCol = V.GetOppCol(c);
-    let moves = [];
-    let i = 0;
-    // King, then rook:
+    const color = this.getColor(x, y);
     const finalSquares = [
       // Black castle long in an unusual way:
-      (c == 'w' ? [2, 3] : [1, 2]),
+      (color == 'w' ? [2, 3] : [1, 2]),
       [V.size.y - 2, V.size.y - 3]
     ];
-    castlingCheck: for (
-      let castleSide = 0;
-      castleSide < 2;
-      castleSide++ //large, then small
-    ) {
-      if (this.castleFlags[c][castleSide] >= V.size.y) continue;
-
-      const rookPos = this.castleFlags[c][castleSide];
-      const castlingPiece = this.getPiece(x, rookPos);
-      const finDist = finalSquares[castleSide][0] - y;
-      let step = finDist / Math.max(1, Math.abs(finDist));
-      i = y;
-      do {
-        if (
-          this.isAttacked([x, i], oppCol) ||
-          (this.board[x][i] != V.EMPTY &&
-            (this.getColor(x, i) != c ||
-              ![V.KING, castlingPiece].includes(this.getPiece(x, i))))
-        ) {
-          continue castlingCheck;
-        }
-        i += step;
-      } while (i != finalSquares[castleSide][0]);
-
-      step = castleSide == 0 ? -1 : 1;
-      for (i = y + step; i != rookPos; i += step) {
-        if (this.board[x][i] != V.EMPTY) continue castlingCheck;
-      }
-
-      for (i = 0; i < 2; i++) {
-        if (
-          finalSquares[castleSide][i] != rookPos &&
-          this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
-          (
-            this.getPiece(x, finalSquares[castleSide][i]) != V.KING ||
-            this.getColor(x, finalSquares[castleSide][i]) != c
-          )
-        ) {
-          continue castlingCheck;
-        }
-      }
-
-      moves.push(
-        new Move({
-          appear: [
-            new PiPo({
-              x: x,
-              y: finalSquares[castleSide][0],
-              p: V.KING,
-              c: c
-            }),
-            new PiPo({
-              x: x,
-              y: finalSquares[castleSide][1],
-              p: castlingPiece,
-              c: c
-            })
-          ],
-          vanish: [
-            new PiPo({ x: x, y: y, p: V.KING, c: c }),
-            new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c })
-          ],
-          end:
-            Math.abs(y - rookPos) <= 2
-              ? { x: x, y: rookPos }
-              : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
-        })
-      );
-    }
-
-    return moves;
+    return super.getCastleMoves([x, y], finalSquares);
   }
 
   isAttacked(sq, color) {
@@ -335,4 +257,5 @@ export class ColorboundRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 2;
   }
+
 };
diff --git a/client/src/variants/Coregal.js b/client/src/variants/Coregal.js
index 198cbfb0..cf83f1fc 100644
--- a/client/src/variants/Coregal.js
+++ b/client/src/variants/Coregal.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt, sample } from "@/utils/alea";
 
 export class CoregalRules extends ChessRules {
+
   static IsGoodPosition(position) {
     if (!ChessRules.IsGoodPosition(position)) return false;
     const rows = position.split("/");
@@ -20,8 +21,7 @@ export class CoregalRules extends ChessRules {
     return !!flags.match(/^[a-z]{8,8}$/);
   }
 
-  // Scanning king position for faster updates is still interesting,
-  // but no need for INIT_COL_KING because it's given in castle flags.
+  // Scanning king position for faster updates is still interesting.
   scanKings(fen) {
     this.kingPos = { w: [-1, -1], b: [-1, -1] };
     const fenRows = V.ParseFen(fen).position.split("/");
@@ -46,6 +46,18 @@ export class CoregalRules extends ChessRules {
     }
   }
 
+  getPPpath(m) {
+    if (
+      m.vanish.length == 2 &&
+      m.appear.length == 2 &&
+      m.vanish[0].p == V.QUEEN
+    ) {
+      // Large castle: show castle symbol
+      return "Coregal/castle";
+    }
+    return super.getPPpath(m);
+  }
+
   getCheckSquares() {
     const color = this.turn;
     let squares = [];
@@ -164,103 +176,42 @@ export class CoregalRules extends ChessRules {
     }
   }
 
-  getPotentialQueenMoves(sq) {
-    return super.getPotentialQueenMoves(sq).concat(this.getCastleMoves(sq));
+  getPotentialQueenMoves([x, y]) {
+    let moves = super.getPotentialQueenMoves([x, y]);
+    const c = this.getColor(x, y);
+    if (this.castleFlags[c].slice(1, 3).includes(y))
+      moves = moves.concat(this.getCastleMoves([x, y]));
+    return moves;
   }
 
-  getCastleMoves([x, y]) {
+  getPotentialKingMoves([x, y]) {
+    let moves = this.getSlideNJumpMoves(
+      [x, y],
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+      "oneStep"
+    );
     const c = this.getColor(x, y);
-    if (
-      x != (c == "w" ? V.size.x - 1 : 0) ||
-      !this.castleFlags[c].slice(1, 3).includes(y)
-    ) {
-      // x isn't first rank, or piece moved
-      return [];
-    }
-    const castlingPiece = this.getPiece(x, y);
+    if (this.castleFlags[c].slice(1, 3).includes(y))
+      moves = moves.concat(this.getCastleMoves([x, y]));
+    return moves;
+  }
 
+  getCastleMoves([x, y]) {
     // Relative position of the selected piece: left or right ?
     // If left: small castle left, large castle right.
     // If right: usual situation.
+    const c = this.getColor(x, y);
     const relPos = (this.castleFlags[c][1] == y ? "left" : "right");
 
-    // Castling ?
-    const oppCol = V.GetOppCol(c);
-    let moves = [];
-    let i = 0;
-    // Castling piece, then rook:
-    const finalSquares = {
-      0: (relPos == "left" ? [1, 2] : [2, 3]),
-      3: (relPos == "right" ? [6, 5] : [5, 4])
-    };
-
-    // Left, then right castle:
-    castlingCheck: for (let castleSide of [0, 3]) {
-      if (this.castleFlags[c][castleSide] >= 8) continue;
-
-      // Rook and castling piece are on initial position
-      const rookPos = this.castleFlags[c][castleSide];
-
-      // Nothing on the path of the king ? (and no checks)
-      const finDist = finalSquares[castleSide][0] - y;
-      let step = finDist / Math.max(1, Math.abs(finDist));
-      i = y;
-      do {
-        if (
-          this.isAttacked([x, i], oppCol) ||
-          (this.board[x][i] != V.EMPTY &&
-            // NOTE: next check is enough, because of chessboard constraints
-            (this.getColor(x, i) != c ||
-              ![castlingPiece, V.ROOK].includes(this.getPiece(x, i))))
-        ) {
-          continue castlingCheck;
-        }
-        i += step;
-      } while (i != finalSquares[castleSide][0]);
-
-      // Nothing on the path to the rook?
-      step = castleSide == 0 ? -1 : 1;
-      for (i = y + step; i != rookPos; i += step) {
-        if (this.board[x][i] != V.EMPTY) continue castlingCheck;
-      }
-
-      // Nothing on final squares, except maybe castling piece and rook?
-      for (i = 0; i < 2; i++) {
-        if (
-          this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
-          ![y, rookPos].includes(finalSquares[castleSide][i])
-        ) {
-          continue castlingCheck;
-        }
-      }
-
-      // If this code is reached, castle is valid
-      moves.push(
-        new Move({
-          appear: [
-            new PiPo({
-              x: x,
-              y: finalSquares[castleSide][0],
-              p: castlingPiece,
-              c: c
-            }),
-            new PiPo({
-              x: x,
-              y: finalSquares[castleSide][1],
-              p: V.ROOK,
-              c: c
-            })
-          ],
-          vanish: [
-            new PiPo({ x: x, y: y, p: castlingPiece, c: c }),
-            new PiPo({ x: x, y: rookPos, p: V.ROOK, c: c })
-          ],
-          // In this variant, always castle by playing onto the rook
-          end: { x: x, y: rookPos }
-        })
-      );
-    }
-
+    const finalSquares = [
+      relPos == "left" ? [1, 2] : [2, 3],
+      relPos == "right" ? [6, 5] : [5, 4]
+    ];
+    const saveFlags = JSON.stringify(this.castleFlags[c]);
+    // Alter flags to follow base_rules semantic
+    this.castleFlags[c] = [0, 3].map(i => this.castleFlags[c][i]);
+    const moves = super.getCastleMoves([x, y], finalSquares);
+    this.castleFlags[c] = JSON.parse(saveFlags);
     return moves;
   }
 
@@ -341,4 +292,5 @@ export class CoregalRules extends ChessRules {
     }
     return super.getNotation(move);
   }
+
 };
diff --git a/client/src/variants/Coronation.js b/client/src/variants/Coronation.js
index 9e12ea10..5077eb50 100644
--- a/client/src/variants/Coronation.js
+++ b/client/src/variants/Coronation.js
@@ -1,6 +1,7 @@
 import { ChessRules, Move, PiPo } from "@/base_rules";
 
 export class CoronationRules extends ChessRules {
+
   getPotentialMovesFrom([x, y]) {
     let moves = super.getPotentialMovesFrom([x, y]);
     // If no queen on board, allow rook+bishop fusions:
@@ -47,4 +48,5 @@ export class CoronationRules extends ChessRules {
       notation += "=Q";
     return notation;
   }
+
 };
diff --git a/client/src/variants/Crazyhouse.js b/client/src/variants/Crazyhouse.js
index ee485448..9d5834fb 100644
--- a/client/src/variants/Crazyhouse.js
+++ b/client/src/variants/Crazyhouse.js
@@ -2,6 +2,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 
 export class CrazyhouseRules extends ChessRules {
+
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
@@ -253,4 +254,5 @@ export class CrazyhouseRules extends ChessRules {
       move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
     return piece + "@" + V.CoordsToSquare(move.end);
   }
+
 };
diff --git a/client/src/variants/Cylinder.js b/client/src/variants/Cylinder.js
index 2324bdcf..8b68e9ce 100644
--- a/client/src/variants/Cylinder.js
+++ b/client/src/variants/Cylinder.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt, shuffle } from "@/utils/alea";
 
 export class CylinderRules extends ChessRules {
+
   // Output basically x % 8 (circular board)
   static ComputeY(y) {
     let res = y % V.size.y;
@@ -154,4 +155,5 @@ export class CylinderRules extends ChessRules {
       k: 1000
     };
   }
+
 };
diff --git a/client/src/variants/Dark.js b/client/src/variants/Dark.js
index 65934e69..fd6d5719 100644
--- a/client/src/variants/Dark.js
+++ b/client/src/variants/Dark.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class DarkRules extends ChessRules {
+
   // Analyse in Dark mode makes no sense
   static get CanAnalyze() {
     return false;
@@ -269,4 +270,5 @@ export class DarkRules extends ChessRules {
       candidates.push(j);
     return moves[candidates[randInt(candidates.length)]];
   }
+
 };
diff --git a/client/src/variants/Diamond.js b/client/src/variants/Diamond.js
index 2a3869ed..415a6aa3 100644
--- a/client/src/variants/Diamond.js
+++ b/client/src/variants/Diamond.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { shuffle } from "@/utils/alea";
 
 export class DiamondRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -136,4 +137,5 @@ export class DiamondRules extends ChessRules {
     }
     return super.getNotation(move); //all other pieces are orthodox
   }
+
 };
diff --git a/client/src/variants/Dice.js b/client/src/variants/Dice.js
index 682b8d50..262f273d 100644
--- a/client/src/variants/Dice.js
+++ b/client/src/variants/Dice.js
@@ -2,6 +2,7 @@ import { ChessRules, Move } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class DiceRules extends ChessRules {
+
   static get CanAnalyze() {
     return false;
   }
@@ -177,4 +178,5 @@ export class DiceRules extends ChessRules {
   getNotation(move) {
     return super.getNotation(move) + "/" + move.end.p.toUpperCase();
   }
+
 };
diff --git a/client/src/variants/Discoduel.js b/client/src/variants/Discoduel.js
index ea522df5..f5735700 100644
--- a/client/src/variants/Discoduel.js
+++ b/client/src/variants/Discoduel.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class DiscoduelRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -47,4 +48,5 @@ export class DiscoduelRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 4;
   }
+
 };
diff --git a/client/src/variants/Doublearmy.js b/client/src/variants/Doublearmy.js
index 0fafe96f..1846aa3c 100644
--- a/client/src/variants/Doublearmy.js
+++ b/client/src/variants/Doublearmy.js
@@ -6,6 +6,7 @@ import { ChessRules } from "@/base_rules";
 // ...But the middle king will get captured quickly...
 
 export class DoublearmyRules extends ChessRules {
+
   static get COMMONER() {
     return "c";
   }
@@ -78,4 +79,5 @@ export class DoublearmyRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 2;
   }
+
 };
diff --git a/client/src/variants/Doublemove1.js b/client/src/variants/Doublemove1.js
index 95d94d7a..261ca5dd 100644
--- a/client/src/variants/Doublemove1.js
+++ b/client/src/variants/Doublemove1.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class Doublemove1Rules extends ChessRules {
+
   static IsGoodEnpassant(enpassant) {
     const squares = enpassant.split(",");
     if (squares.length > 2) return false;
@@ -273,4 +274,5 @@ export class Doublemove1Rules extends ChessRules {
     }
     return doubleMove;
   }
+
 };
diff --git a/client/src/variants/Doublemove2.js b/client/src/variants/Doublemove2.js
index f9d5e4f1..287f9491 100644
--- a/client/src/variants/Doublemove2.js
+++ b/client/src/variants/Doublemove2.js
@@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class Doublemove2Rules extends ChessRules {
+
   static IsGoodEnpassant(enpassant) {
     const squares = enpassant.split(",");
     if (squares.length > 2) return false;
@@ -234,4 +235,5 @@ export class Doublemove2Rules extends ChessRules {
     // TODO: not always the best move played (why ???)
     return doubleMove;
   }
+
 };
diff --git a/client/src/variants/Dynamo.js b/client/src/variants/Dynamo.js
index dbc19c21..67065d7c 100644
--- a/client/src/variants/Dynamo.js
+++ b/client/src/variants/Dynamo.js
@@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class DynamoRules extends ChessRules {
+
   // TODO? later, allow to push out pawns on a and h files
   static get HasEnpassant() {
     return false;
@@ -873,4 +874,5 @@ export class DynamoRules extends ChessRules {
       return initialSquare + "R";
     return move.appear[0].p.toUpperCase() + initialSquare + finalSquare;
   }
+
 };
diff --git a/client/src/variants/Eightpieces.js b/client/src/variants/Eightpieces.js
index 583b1c5a..3e155009 100644
--- a/client/src/variants/Eightpieces.js
+++ b/client/src/variants/Eightpieces.js
@@ -3,6 +3,7 @@ import { randInt } from "@/utils/alea";
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 export class EightpiecesRules extends ChessRules {
+
   static get JAILER() {
     return "j";
   }
@@ -1194,4 +1195,5 @@ export class EightpiecesRules extends ChessRules {
     }
     return notation;
   }
+
 };
diff --git a/client/src/variants/Enpassant.js b/client/src/variants/Enpassant.js
index 4c6ca67d..cb218306 100644
--- a/client/src/variants/Enpassant.js
+++ b/client/src/variants/Enpassant.js
@@ -1,6 +1,7 @@
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 export class EnpassantRules extends ChessRules {
+
   static IsGoodEnpassant(enpassant) {
     if (enpassant != "-") {
       const squares = enpassant.split(",");
@@ -204,4 +205,5 @@ export class EnpassantRules extends ChessRules {
       k: 1000
     };
   }
+
 };
diff --git a/client/src/variants/Evolution.js b/client/src/variants/Evolution.js
new file mode 100644
index 00000000..5250089e
--- /dev/null
+++ b/client/src/variants/Evolution.js
@@ -0,0 +1,34 @@
+import { ChessRules } from "@/base_rules";
+
+export class EvolutionRules extends ChessRules {
+
+  getPotentialMovesFrom([x, y]) {
+    let moves = super.getPotentialMovesFrom([x, y]);
+    const c = this.getColor(x, y);
+    const piece = this.getPiece(x, y);
+    if (
+      [V.BISHOP, V.ROOK, V.QUEEN].includes(piece) &&
+      (c == 'w' && x == 7) || (c == 'b' && x == 0)
+    ) {
+      // Move from first rank
+      const forward = (c == 'w' ? -1 : 1);
+      for (let shift of [-2, 0, 2]) {
+        if (
+          (piece == V.ROOK && shift != 0) ||
+          (piece == V.BISHOP && shift == 0)
+        ) {
+          continue;
+        }
+        if (
+          V.OnBoard(x+2*forward, y+shift) &&
+          this.board[x+forward][y+shift/2] != V.EMPTY &&
+          this.getColor(x+2*forward, y+shift) != c
+        ) {
+          moves.push(this.getBasicMove([x,y], [x+2*forward,y+shift]));
+        }
+      }
+    }
+    return moves;
+  }
+
+};
diff --git a/client/src/variants/Extinction.js b/client/src/variants/Extinction.js
index db8abc06..e42e2941 100644
--- a/client/src/variants/Extinction.js
+++ b/client/src/variants/Extinction.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class ExtinctionRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -113,4 +114,5 @@ export class ExtinctionRules extends ChessRules {
     }
     return super.evalPosition();
   }
+
 };
diff --git a/client/src/variants/Football.js b/client/src/variants/Football.js
index 1ccb2a4d..bde38e53 100644
--- a/client/src/variants/Football.js
+++ b/client/src/variants/Football.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { shuffle } from "@/utils/alea";
 
 export class FootballRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -112,4 +113,5 @@ export class FootballRules extends ChessRules {
       " w 0 -"
     );
   }
+
 };
diff --git a/client/src/variants/Forward.js b/client/src/variants/Forward.js
index 8f2fc895..e0a0f9ba 100644
--- a/client/src/variants/Forward.js
+++ b/client/src/variants/Forward.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class ForwardRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -26,7 +27,6 @@ export class ForwardRules extends ChessRules {
   }
 
   scanKings(fen) {
-    this.INIT_COL_KING = { w: -1, b: -1 };
     // Squares of white and black king:
     this.kingPos = { w: [-1, -1], b: [-1, -1] };
     const fenRows = V.ParseFen(fen).position.split("/");
@@ -38,12 +38,10 @@ export class ForwardRules extends ChessRules {
           case "k":
           case "l":
             this.kingPos["b"] = [i, k];
-            this.INIT_COL_KING["b"] = k;
             break;
           case "K":
           case "L":
             this.kingPos["w"] = [i, k];
-            this.INIT_COL_KING["w"] = k;
             break;
           default: {
             const num = parseInt(fenRows[i].charAt(j), 10);
@@ -140,4 +138,5 @@ export class ForwardRules extends ChessRules {
       ChessRules.VALUES
     );
   }
+
 };
diff --git a/client/src/variants/Freecapture.js b/client/src/variants/Freecapture.js
index 19098b6d..5fe93ea4 100644
--- a/client/src/variants/Freecapture.js
+++ b/client/src/variants/Freecapture.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class FreecaptureRules extends ChessRules {
+
   canTake() {
     // Can capture both colors:
     return true;
@@ -9,4 +10,5 @@ export class FreecaptureRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 2;
   }
+
 };
diff --git a/client/src/variants/Grand.js b/client/src/variants/Grand.js
index 14c0cc96..398a96cc 100644
--- a/client/src/variants/Grand.js
+++ b/client/src/variants/Grand.js
@@ -5,6 +5,7 @@ import { randInt } from "@/utils/alea";
 // NOTE: initial setup differs from the original; see
 // https://www.chessvariants.com/large.dir/freeling.html
 export class GrandRules extends ChessRules {
+
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
@@ -391,4 +392,5 @@ export class GrandRules extends ChessRules {
       " w 0 " + flags + " - 00000000000000"
     );
   }
+
 };
diff --git a/client/src/variants/Grasshopper.js b/client/src/variants/Grasshopper.js
index c47ac2b1..90411d14 100644
--- a/client/src/variants/Grasshopper.js
+++ b/client/src/variants/Grasshopper.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class GrasshopperRules extends ChessRules {
+
   static get HasEnpassant() {
     return false;
   }
@@ -108,4 +109,5 @@ export class GrasshopperRules extends ChessRules {
         "/gggggggg/pppppppp/8/8/PPPPPPPP/GGGGGGGG/"
       );
   }
+
 };
diff --git a/client/src/variants/Gridolina.js b/client/src/variants/Gridolina.js
index 797190cd..6efff14e 100644
--- a/client/src/variants/Gridolina.js
+++ b/client/src/variants/Gridolina.js
@@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { BerolinaRules } from "@/variants/Berolina";
 
 export class GridolinaRules extends BerolinaRules {
+
   static get Lines() {
     return [
       [[2, 0], [2, 8]],
@@ -54,4 +55,5 @@ export class GridolinaRules extends BerolinaRules {
     }
     return false;
   }
+
 };
diff --git a/client/src/variants/Hamilton.js b/client/src/variants/Hamilton.js
index 69dd9e5c..88f67139 100644
--- a/client/src/variants/Hamilton.js
+++ b/client/src/variants/Hamilton.js
@@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class HamiltonRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -158,4 +159,5 @@ export class HamiltonRules extends ChessRules {
     // First game move:
     return "N@" + V.CoordsToSquare(move.end);
   }
+
 };
diff --git a/client/src/variants/Hidden.js b/client/src/variants/Hidden.js
index 2d44df5c..280062c3 100644
--- a/client/src/variants/Hidden.js
+++ b/client/src/variants/Hidden.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class HiddenRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -327,4 +328,5 @@ export class HiddenRules extends ChessRules {
       finalSquare
     );
   }
+
 };
diff --git a/client/src/variants/Hiddenqueen.js b/client/src/variants/Hiddenqueen.js
index f1607a6a..0cf09677 100644
--- a/client/src/variants/Hiddenqueen.js
+++ b/client/src/variants/Hiddenqueen.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class HiddenqueenRules extends ChessRules {
+
   // Analyse in Hiddenqueen mode makes no sense
   static get CanAnalyze() {
     return false;
@@ -230,4 +231,5 @@ export class HiddenqueenRules extends ChessRules {
     else notation = finalSquare;
     return notation;
   }
+
 };
diff --git a/client/src/variants/Horde.js b/client/src/variants/Horde.js
index c436811b..33157c94 100644
--- a/client/src/variants/Horde.js
+++ b/client/src/variants/Horde.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class HordeRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -80,4 +81,5 @@ export class HordeRules extends ChessRules {
     // From black side, just run usual checks:
     return super.getCurrentScore();
   }
+
 };
diff --git a/client/src/variants/Interweave.js b/client/src/variants/Interweave.js
index 973797d6..1a9cd94c 100644
--- a/client/src/variants/Interweave.js
+++ b/client/src/variants/Interweave.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt, shuffle } from "@/utils/alea";
 
 export class InterweaveRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -639,4 +640,5 @@ export class InterweaveRules extends ChessRules {
     if (move.vanish.length >= 2) notation += "X";
     return notation;
   }
+
 };
diff --git a/client/src/variants/Kinglet.js b/client/src/variants/Kinglet.js
index 518069f8..81c81607 100644
--- a/client/src/variants/Kinglet.js
+++ b/client/src/variants/Kinglet.js
@@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { SuicideRules } from "@/variants/Suicide";
 
 export class KingletRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -84,4 +85,5 @@ export class KingletRules extends ChessRules {
       k: 4
     };
   }
+
 };
diff --git a/client/src/variants/Knightmate.js b/client/src/variants/Knightmate.js
index 593fcc09..bc886c12 100644
--- a/client/src/variants/Knightmate.js
+++ b/client/src/variants/Knightmate.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class KnightmateRules extends ChessRules {
+
   static get COMMONER() {
     return "c";
   }
@@ -82,4 +83,5 @@ export class KnightmateRules extends ChessRules {
       k: 1000
     };
   }
+
 };
diff --git a/client/src/variants/Knightpawns.js b/client/src/variants/Knightpawns.js
index 96476535..c8c1ceaa 100644
--- a/client/src/variants/Knightpawns.js
+++ b/client/src/variants/Knightpawns.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class KnightpawnsRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -48,4 +49,5 @@ export class KnightpawnsRules extends ChessRules {
 
   postPlay() {}
   postUndo() {}
+
 };
diff --git a/client/src/variants/Knightrelay1.js b/client/src/variants/Knightrelay1.js
index 626519cd..1e7bfabb 100644
--- a/client/src/variants/Knightrelay1.js
+++ b/client/src/variants/Knightrelay1.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class Knightrelay1Rules extends ChessRules {
+
   static get HasEnpassant() {
     return false;
   }
@@ -139,4 +140,5 @@ export class Knightrelay1Rules extends ChessRules {
 
     return notation;
   }
+
 };
diff --git a/client/src/variants/Knightrelay2.js b/client/src/variants/Knightrelay2.js
index 1827accb..a8a796e8 100644
--- a/client/src/variants/Knightrelay2.js
+++ b/client/src/variants/Knightrelay2.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class Knightrelay2Rules extends ChessRules {
+
   getPotentialMovesFrom([x, y]) {
     let moves = super.getPotentialMovesFrom([x, y]);
 
@@ -120,4 +121,5 @@ export class Knightrelay2Rules extends ChessRules {
 
     return notation;
   }
+
 };
diff --git a/client/src/variants/Koopa.js b/client/src/variants/Koopa.js
index 57d19b92..a624eebb 100644
--- a/client/src/variants/Koopa.js
+++ b/client/src/variants/Koopa.js
@@ -1,6 +1,7 @@
 import { ChessRules, PiPo } from "@/base_rules";
 
 export class KoopaRules extends ChessRules {
+
   static get HasEnpassant() {
     return false;
   }
@@ -58,7 +59,6 @@ export class KoopaRules extends ChessRules {
   // stand for stunned indicator.
 
   scanKings(fen) {
-    this.INIT_COL_KING = { w: -1, b: -1 };
     // Squares of white and black king:
     this.kingPos = { w: [-1, -1], b: [-1, -1] };
     const fenRows = V.ParseFen(fen).position.split("/");
@@ -70,12 +70,10 @@ export class KoopaRules extends ChessRules {
           case "k":
           case "l":
             this.kingPos["b"] = [i, k];
-            this.INIT_COL_KING["b"] = k;
             break;
           case "K":
           case "L":
             this.kingPos["w"] = [i, k];
-            this.INIT_COL_KING["w"] = k;
             break;
           default: {
             const num = parseInt(fenRows[i].charAt(j), 10);
@@ -221,7 +219,7 @@ export class KoopaRules extends ChessRules {
         sq,
         V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
         "oneStep"
-      ).concat(super.getCastleMoves(sq, true, ['r']))
+      ).concat(super.getCastleMoves(sq, null, true, ['r']))
     );
   }
 
@@ -369,4 +367,5 @@ export class KoopaRules extends ChessRules {
     }
     return notation;
   }
+
 };
diff --git a/client/src/variants/Koth.js b/client/src/variants/Koth.js
index 42ffe7bb..38e10108 100644
--- a/client/src/variants/Koth.js
+++ b/client/src/variants/Koth.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class KothRules extends ChessRules {
+
   static get Lines() {
     return [
       [[3, 3], [3, 5]],
@@ -39,4 +40,5 @@ export class KothRules extends ChessRules {
       ) / 2
     );
   }
+
 };
diff --git a/client/src/variants/Losers.js b/client/src/variants/Losers.js
index 3ac46c2f..7bd5e541 100644
--- a/client/src/variants/Losers.js
+++ b/client/src/variants/Losers.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class LosersRules extends ChessRules {
+
   // Trim all non-capturing moves
   static KeepCaptures(moves) {
     return moves.filter(m => m.vanish.length == 2 && m.appear.length == 1);
@@ -75,4 +76,5 @@ console.log(this.atLeastOneCapture());
     // Less material is better (more subtle in fact but...)
     return -super.evalPosition();
   }
+
 };
diff --git a/client/src/variants/Madhouse.js b/client/src/variants/Madhouse.js
index c157437d..5f90904e 100644
--- a/client/src/variants/Madhouse.js
+++ b/client/src/variants/Madhouse.js
@@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class MadhouseRules extends ChessRules {
+
   hoverHighlight(x, y) {
     // Testing move validity results in an infinite update loop.
     // TODO: find a way to test validity anyway.
@@ -254,4 +255,5 @@ export class MadhouseRules extends ChessRules {
       move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
     return piece + "@" + V.CoordsToSquare(move.end);
   }
+
 };
diff --git a/client/src/variants/Madrasi.js b/client/src/variants/Madrasi.js
index ed2a8791..f0db5107 100644
--- a/client/src/variants/Madrasi.js
+++ b/client/src/variants/Madrasi.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class MadrasiRules extends ChessRules {
+
   isImmobilized(sq) {
     const oppCol = V.GetOppCol(this.getColor(sq[0], sq[1]));
     const piece = this.getPiece(sq[0], sq[1]);
@@ -61,4 +62,5 @@ export class MadrasiRules extends ChessRules {
     // Connected kings paralyze each other
     return false;
   }
+
 };
diff --git a/client/src/variants/Magnetic.js b/client/src/variants/Magnetic.js
index 9fc4180d..dc228ce1 100644
--- a/client/src/variants/Magnetic.js
+++ b/client/src/variants/Magnetic.js
@@ -1,6 +1,7 @@
 import { ChessRules, PiPo } from "@/base_rules";
 
 export class MagneticRules extends ChessRules {
+
   static get HasEnpassant() {
     return false;
   }
@@ -210,4 +211,5 @@ export class MagneticRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 2;
   }
+
 };
diff --git a/client/src/variants/Makruk.js b/client/src/variants/Makruk.js
index 6205f230..c7dd3430 100644
--- a/client/src/variants/Makruk.js
+++ b/client/src/variants/Makruk.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt, shuffle } from "@/utils/alea";
 
 export class MakrukRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -136,4 +137,5 @@ export class MakrukRules extends ChessRules {
       k: 1000
     };
   }
+
 };
diff --git a/client/src/variants/Maxima.js b/client/src/variants/Maxima.js
index e8b15d25..05df0bd9 100644
--- a/client/src/variants/Maxima.js
+++ b/client/src/variants/Maxima.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { shuffle } from "@/utils/alea";
 
 export class MaximaRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -827,4 +828,5 @@ export class MaximaRules extends ChessRules {
     if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X";
     return notation;
   }
+
 };
diff --git a/client/src/variants/Minishogi.js b/client/src/variants/Minishogi.js
index afeaa57e..d7bc0d71 100644
--- a/client/src/variants/Minishogi.js
+++ b/client/src/variants/Minishogi.js
@@ -2,6 +2,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules";
 import { ShogiRules } from "@/variants/Shogi";
 
 export class MinishogiRules extends ShogiRules {
+
   static IsGoodFen(fen) {
     if (!ChessRules.IsGoodFen(fen)) return false;
     const fenParsed = V.ParseFen(fen);
@@ -168,4 +169,5 @@ export class MinishogiRules extends ShogiRules {
   static get SEARCH_DEPTH() {
     return 3;
   }
+
 };
diff --git a/client/src/variants/Monochrome.js b/client/src/variants/Monochrome.js
index 2d5a912f..22242ed2 100644
--- a/client/src/variants/Monochrome.js
+++ b/client/src/variants/Monochrome.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class MonochromeRules extends ChessRules {
+
   static get HasEnpassant() {
     // Pawns would be on the same side
     return false;
@@ -217,4 +218,5 @@ export class MonochromeRules extends ChessRules {
     }
     return notation;
   }
+
 };
diff --git a/client/src/variants/Monster.js b/client/src/variants/Monster.js
index 6e3e10c5..cb29adae 100644
--- a/client/src/variants/Monster.js
+++ b/client/src/variants/Monster.js
@@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class MonsterRules extends ChessRules {
+
   static IsGoodFlags(flags) {
     // Only black can castle
     return !!flags.match(/^[a-z]{2,2}$/);
@@ -214,4 +215,5 @@ export class MonsterRules extends ChessRules {
     const color = this.turn;
     return (color == 'w' ? getBestWhiteMove() : getBestBlackMove());
   }
+
 };
diff --git a/client/src/variants/Omega.js b/client/src/variants/Omega.js
index 355960b7..289c05b3 100644
--- a/client/src/variants/Omega.js
+++ b/client/src/variants/Omega.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class OmegaRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -331,102 +332,12 @@ export class OmegaRules extends ChessRules {
     return this.getSlideNJumpMoves(sq, V.steps[V.WIZARD], "oneStep");
   }
 
-  getCastleMoves([x, y], castleInCheck) {
-    const c = this.getColor(x, y);
-    if (x != (c == "w" ? V.size.x - 2 : 1) || y != this.INIT_COL_KING[c])
-      return []; //x isn't first rank, or king has moved (shortcut)
-
-    // Castling ?
-    const oppCol = V.GetOppCol(c);
-    let moves = [];
-    let i = 0;
-    // King, then rook:
+  getCastleMoves([x, y]) {
     const finalSquares = [
       [4, 5],
       [8, 7]
     ];
-    castlingCheck: for (
-      let castleSide = 0;
-      castleSide < 2;
-      castleSide++ //large, then small
-    ) {
-      if (this.castleFlags[c][castleSide] >= V.size.y) continue;
-      // If this code is reached, rook and king are on initial position
-
-      // NOTE: in some variants this is not a rook
-      const rookPos = this.castleFlags[c][castleSide];
-      if (this.board[x][rookPos] == V.EMPTY || this.getColor(x, rookPos) != c)
-        // Rook is not here, or changed color (see Benedict)
-        continue;
-
-      // Nothing on the path of the king ? (and no checks)
-      const castlingPiece = this.getPiece(x, rookPos);
-      const finDist = finalSquares[castleSide][0] - y;
-      let step = finDist / Math.max(1, Math.abs(finDist));
-      i = y;
-      do {
-        if (
-          (!castleInCheck && this.isAttacked([x, i], oppCol)) ||
-          (this.board[x][i] != V.EMPTY &&
-            // NOTE: next check is enough, because of chessboard constraints
-            (this.getColor(x, i) != c ||
-              ![V.KING, castlingPiece].includes(this.getPiece(x, i))))
-        ) {
-          continue castlingCheck;
-        }
-        i += step;
-      } while (i != finalSquares[castleSide][0]);
-
-      // Nothing on the path to the rook?
-      step = castleSide == 0 ? -1 : 1;
-      for (i = y + step; i != rookPos; i += step) {
-        if (this.board[x][i] != V.EMPTY) continue castlingCheck;
-      }
-
-      // Nothing on final squares, except maybe king and castling rook?
-      for (i = 0; i < 2; i++) {
-        if (
-          finalSquares[castleSide][i] != rookPos &&
-          this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
-          (
-            this.getPiece(x, finalSquares[castleSide][i]) != V.KING ||
-            this.getColor(x, finalSquares[castleSide][i]) != c
-          )
-        ) {
-          continue castlingCheck;
-        }
-      }
-
-      // If this code is reached, castle is valid
-      moves.push(
-        new Move({
-          appear: [
-            new PiPo({
-              x: x,
-              y: finalSquares[castleSide][0],
-              p: V.KING,
-              c: c
-            }),
-            new PiPo({
-              x: x,
-              y: finalSquares[castleSide][1],
-              p: castlingPiece,
-              c: c
-            })
-          ],
-          vanish: [
-            new PiPo({ x: x, y: y, p: V.KING, c: c }),
-            new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c })
-          ],
-          end:
-            Math.abs(y - rookPos) <= 2
-              ? { x: x, y: rookPos }
-              : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
-        })
-      );
-    }
-
-    return moves;
+    return super.getCastleMoves([x, y], finalSquares);
   }
 
   isAttacked(sq, color) {
@@ -506,4 +417,5 @@ export class OmegaRules extends ChessRules {
     }
     return evaluation;
   }
+
 };
diff --git a/client/src/variants/Orda.js b/client/src/variants/Orda.js
index 1ff4898a..308ae2f4 100644
--- a/client/src/variants/Orda.js
+++ b/client/src/variants/Orda.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class OrdaRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -321,4 +322,5 @@ export class OrdaRules extends ChessRules {
       }
     );
   }
+
 };
diff --git a/client/src/variants/Ordamirror.js b/client/src/variants/Ordamirror.js
index d79d8c5f..8176b957 100644
--- a/client/src/variants/Ordamirror.js
+++ b/client/src/variants/Ordamirror.js
@@ -4,6 +4,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class OrdamirrorRules extends OrdaRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -145,4 +146,5 @@ export class OrdamirrorRules extends OrdaRules {
       k: 1000
     };
   }
+
 };
diff --git a/client/src/variants/Pacifist1.js b/client/src/variants/Pacifist1.js
index 43c81d71..37d0521f 100644
--- a/client/src/variants/Pacifist1.js
+++ b/client/src/variants/Pacifist1.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class Pacifist1Rules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -36,30 +37,8 @@ export class Pacifist1Rules extends ChessRules {
     return true;
   }
 
-  scanKings(fen) {
-    // Kings may be swapped, so they are not tracked (no kingPos)
-    this.INIT_COL_KING = { w: -1, b: -1 };
-    const fenRows = V.ParseFen(fen).position.split("/");
-    const startRow = { 'w': V.size.x - 1, 'b': 0 };
-    for (let i = 0; i < fenRows.length; i++) {
-      let k = 0; //column index on board
-      for (let j = 0; j < fenRows[i].length; j++) {
-        switch (fenRows[i].charAt(j)) {
-          case "k":
-            this.INIT_COL_KING["b"] = k;
-            break;
-          case "K":
-            this.INIT_COL_KING["w"] = k;
-            break;
-          default: {
-            const num = parseInt(fenRows[i].charAt(j), 10);
-            if (!isNaN(num)) k += num - 1;
-          }
-        }
-        k++;
-      }
-    }
-  }
+  // Kings may be swapped, so they are not tracked (no kingPos)
+  scanKings(fen) { }
 
   // Sum white pieces attacking a square, and remove black pieces count.
   sumAttacks([x, y]) {
@@ -260,4 +239,5 @@ export class Pacifist1Rules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 1;
   }
+
 };
diff --git a/client/src/variants/Pacifist2.js b/client/src/variants/Pacifist2.js
index e8128024..78d4ec84 100644
--- a/client/src/variants/Pacifist2.js
+++ b/client/src/variants/Pacifist2.js
@@ -1,6 +1,7 @@
 import { Pacifist1Rules } from "@/variants/Pacifist1";
 
 export class Pacifist2Rules extends Pacifist1Rules {
+
   // Sum values of white pieces attacking a square,
   // and remove (sum of) black pieces values.
   sumAttacks([x, y]) {
@@ -55,4 +56,5 @@ export class Pacifist2Rules extends Pacifist1Rules {
     });
     return res;
   }
+
 };
diff --git a/client/src/variants/Parachute.js b/client/src/variants/Parachute.js
index 8cd0f9c2..631301c9 100644
--- a/client/src/variants/Parachute.js
+++ b/client/src/variants/Parachute.js
@@ -1,6 +1,7 @@
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 export class ParachuteRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -234,4 +235,5 @@ export class ParachuteRules extends ChessRules {
       move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
     return piece + "@" + V.CoordsToSquare(move.end);
   }
+
 };
diff --git a/client/src/variants/Pawnmassacre.js b/client/src/variants/Pawnmassacre.js
index 45756bce..12ce1d04 100644
--- a/client/src/variants/Pawnmassacre.js
+++ b/client/src/variants/Pawnmassacre.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class PawnmassacreRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -20,4 +21,5 @@ export class PawnmassacreRules extends ChessRules {
       .concat(bFen.substr(splitIdx))
     );
   }
+
 };
diff --git a/client/src/variants/Pawns.js b/client/src/variants/Pawns.js
index 1dca994d..b3e84ea9 100644
--- a/client/src/variants/Pawns.js
+++ b/client/src/variants/Pawns.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class PawnsRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -45,4 +46,5 @@ export class PawnsRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 4;
   }
+
 };
diff --git a/client/src/variants/Pawnsking.js b/client/src/variants/Pawnsking.js
index 76d03efa..bce2fe75 100644
--- a/client/src/variants/Pawnsking.js
+++ b/client/src/variants/Pawnsking.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class PawnskingRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -53,4 +54,5 @@ export class PawnskingRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 4;
   }
+
 };
diff --git a/client/src/variants/Perfect.js b/client/src/variants/Perfect.js
index 4f65a0ed..4bd2ac92 100644
--- a/client/src/variants/Perfect.js
+++ b/client/src/variants/Perfect.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt } from "@/utils/alea";
 
 export class PerfectRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -200,4 +201,5 @@ export class PerfectRules extends ChessRules {
       " w 0 " + flags + " -"
     );
   }
+
 };
diff --git a/client/src/variants/Pocketknight.js b/client/src/variants/Pocketknight.js
index 6b92a1f5..8756ab9f 100644
--- a/client/src/variants/Pocketknight.js
+++ b/client/src/variants/Pocketknight.js
@@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class PocketknightRules extends ChessRules {
+
   hoverHighlight(x, y) {
     // Testing move validity results in an infinite update loop.
     // TODO: find a way to test validity anyway.
@@ -282,4 +283,5 @@ export class PocketknightRules extends ChessRules {
     // Knight landing:
     return "N@" + V.CoordsToSquare(move.end);
   }
+
 };
diff --git a/client/src/variants/Progressive1.js b/client/src/variants/Progressive1.js
index 3023b729..43c7c4db 100644
--- a/client/src/variants/Progressive1.js
+++ b/client/src/variants/Progressive1.js
@@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class Progressive1Rules extends ChessRules {
+
   static get HasEnpassant() {
     return false;
   }
@@ -108,4 +109,5 @@ export class Progressive1Rules extends ChessRules {
     for (let i=res.length - 1; i>= 0; i--) this.undo(res[i]);
     return res;
   }
+
 };
diff --git a/client/src/variants/Progressive2.js b/client/src/variants/Progressive2.js
index cb9911d3..d0c15e61 100644
--- a/client/src/variants/Progressive2.js
+++ b/client/src/variants/Progressive2.js
@@ -4,6 +4,7 @@ import { ChessRules } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class Progressive2Rules extends Progressive1Rules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -47,4 +48,5 @@ export class Progressive2Rules extends Progressive1Rules {
       k: 1000
     };
   }
+
 };
diff --git a/client/src/variants/Queenpawns.js b/client/src/variants/Queenpawns.js
index 8ffe111b..0ea2e45f 100644
--- a/client/src/variants/Queenpawns.js
+++ b/client/src/variants/Queenpawns.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class QueenpawnsRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -52,4 +53,5 @@ export class QueenpawnsRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 4;
   }
+
 };
diff --git a/client/src/variants/Racingkings.js b/client/src/variants/Racingkings.js
index 11457485..e898a554 100644
--- a/client/src/variants/Racingkings.js
+++ b/client/src/variants/Racingkings.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class RacingkingsRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -61,4 +62,5 @@ export class RacingkingsRules extends ChessRules {
     // Ponder with king position:
     return evaluation/5 + this.kingPos["b"][0] - this.kingPos["w"][0];
   }
+
 };
diff --git a/client/src/variants/Rampage.js b/client/src/variants/Rampage.js
index 79b8b956..12a61a18 100644
--- a/client/src/variants/Rampage.js
+++ b/client/src/variants/Rampage.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class RampageRules extends ChessRules {
+
   // Sum white pieces attacking a square, and remove black pieces count.
   sumAttacks([x, y]) {
     const getSign = (color) => {
@@ -84,4 +85,5 @@ export class RampageRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 1;
   }
+
 };
diff --git a/client/src/variants/Recycle.js b/client/src/variants/Recycle.js
index d23a78c2..eb84044e 100644
--- a/client/src/variants/Recycle.js
+++ b/client/src/variants/Recycle.js
@@ -2,6 +2,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 
 export class RecycleRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -227,4 +228,5 @@ export class RecycleRules extends ChessRules {
       move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
     return piece + "@" + V.CoordsToSquare(move.end);
   }
+
 };
diff --git a/client/src/variants/Rifle.js b/client/src/variants/Rifle.js
index 94a6d058..7d71a98a 100644
--- a/client/src/variants/Rifle.js
+++ b/client/src/variants/Rifle.js
@@ -1,6 +1,7 @@
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 export class RifleRules extends ChessRules {
+
   getBasicMove([sx, sy], [ex, ey], tr) {
     let mv = new Move({
       appear: [],
@@ -71,4 +72,5 @@ export class RifleRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 2;
   }
+
 };
diff --git a/client/src/variants/Rococo.js b/client/src/variants/Rococo.js
index e6480a8c..66d9615a 100644
--- a/client/src/variants/Rococo.js
+++ b/client/src/variants/Rococo.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { shuffle } from "@/utils/alea";
 
 export class RococoRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -721,4 +722,5 @@ export class RococoRules extends ChessRules {
     if (move.vanish.length > 1 && move.appear[0].p != V.KING) notation += "X";
     return notation;
   }
+
 };
diff --git a/client/src/variants/Rookpawns.js b/client/src/variants/Rookpawns.js
index 2d0c3a85..1daae907 100644
--- a/client/src/variants/Rookpawns.js
+++ b/client/src/variants/Rookpawns.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class RookpawnsRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -52,4 +53,5 @@ export class RookpawnsRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 4;
   }
+
 };
diff --git a/client/src/variants/Royalrace.js b/client/src/variants/Royalrace.js
index 5232cc94..5d5992c8 100644
--- a/client/src/variants/Royalrace.js
+++ b/client/src/variants/Royalrace.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt, shuffle } from "@/utils/alea";
 
 export class RoyalraceRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -219,4 +220,5 @@ export class RoyalraceRules extends ChessRules {
       V.CoordsToSquare(move.end)
     );
   }
+
 };
diff --git a/client/src/variants/Rugby.js b/client/src/variants/Rugby.js
index 209def87..bbe25cb7 100644
--- a/client/src/variants/Rugby.js
+++ b/client/src/variants/Rugby.js
@@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { ArrayFun } from "@/utils/array";
 
 export class RugbyRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -61,4 +62,5 @@ export class RugbyRules extends ChessRules {
     // Stalemate (will probably never happen)
     return "1/2";
   }
+
 };
diff --git a/client/src/variants/Schess.js b/client/src/variants/Schess.js
index 95118982..bdc93275 100644
--- a/client/src/variants/Schess.js
+++ b/client/src/variants/Schess.js
@@ -1,6 +1,7 @@
 import { ChessRules, PiPo } from "@/base_rules";
 
 export class SchessRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -349,4 +350,5 @@ export class SchessRules extends ChessRules {
     }
     return super.getNotation(move);
   }
+
 };
diff --git a/client/src/variants/Shako.js b/client/src/variants/Shako.js
index ee694d01..c3db2794 100644
--- a/client/src/variants/Shako.js
+++ b/client/src/variants/Shako.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { randInt, sample } from "@/utils/alea";
 
 export class ShakoRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -96,97 +97,11 @@ export class ShakoRules extends ChessRules {
   }
 
   getCastleMoves([x, y]) {
-    const c = this.getColor(x, y);
-    if (x != (c == "w" ? V.size.x - 2 : 1) || y != this.INIT_COL_KING[c])
-      return []; //x isn't second rank, or king has moved (shortcut)
-
-    // Castling ?
-    const oppCol = V.GetOppCol(c);
-    let moves = [];
-    let i = 0;
-    // King, then rook:
     const finalSquares = [
       [3, 4],
       [7, 6]
     ];
-    castlingCheck: for (
-      let castleSide = 0;
-      castleSide < 2;
-      castleSide++ //large, then small
-    ) {
-      if (this.castleFlags[c][castleSide] >= V.size.y) continue;
-      // If this code is reached, rook and king are on initial position
-
-      const rookPos = this.castleFlags[c][castleSide];
-
-      // Nothing on the path of the king ? (and no checks)
-      const castlingPiece = this.getPiece(x, rookPos);
-      const finDist = finalSquares[castleSide][0] - y;
-      let step = finDist / Math.max(1, Math.abs(finDist));
-      i = y;
-      do {
-        if (
-          this.isAttacked([x, i], oppCol) ||
-          (this.board[x][i] != V.EMPTY &&
-            // NOTE: next check is enough, because of chessboard constraints
-            (this.getColor(x, i) != c ||
-              ![V.KING, castlingPiece].includes(this.getPiece(x, i))))
-        ) {
-          continue castlingCheck;
-        }
-        i += step;
-      } while (i != finalSquares[castleSide][0]);
-
-      // Nothing on the path to the rook?
-      step = castleSide == 0 ? -1 : 1;
-      for (i = y + step; i != rookPos; i += step) {
-        if (this.board[x][i] != V.EMPTY) continue castlingCheck;
-      }
-
-      // Nothing on final squares, except maybe king and castling rook?
-      for (i = 0; i < 2; i++) {
-        if (
-          finalSquares[castleSide][i] != rookPos &&
-          this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
-          (
-            this.getPiece(x, finalSquares[castleSide][i]) != V.KING ||
-            this.getColor(x, finalSquares[castleSide][i]) != c
-          )
-        ) {
-          continue castlingCheck;
-        }
-      }
-
-      // If this code is reached, castle is valid
-      moves.push(
-        new Move({
-          appear: [
-            new PiPo({
-              x: x,
-              y: finalSquares[castleSide][0],
-              p: V.KING,
-              c: c
-            }),
-            new PiPo({
-              x: x,
-              y: finalSquares[castleSide][1],
-              p: castlingPiece,
-              c: c
-            })
-          ],
-          vanish: [
-            new PiPo({ x: x, y: y, p: V.KING, c: c }),
-            new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c })
-          ],
-          end:
-            Math.abs(y - rookPos) <= 2
-              ? { x: x, y: rookPos }
-              : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) }
-        })
-      );
-    }
-
-    return moves;
+    return super.getCastleMoves([x, y], finalSquares);
   }
 
   isAttacked(sq, color) {
@@ -337,4 +252,5 @@ export class ShakoRules extends ChessRules {
       " w 0 " + flags + " -"
     );
   }
+
 };
diff --git a/client/src/variants/Shatranj.js b/client/src/variants/Shatranj.js
index 7aab305e..0569f713 100644
--- a/client/src/variants/Shatranj.js
+++ b/client/src/variants/Shatranj.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class ShatranjRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -151,4 +152,5 @@ export class ShatranjRules extends ChessRules {
       k: 1000
     };
   }
+
 };
diff --git a/client/src/variants/Shogi.js b/client/src/variants/Shogi.js
index 6c522dc6..e8b17918 100644
--- a/client/src/variants/Shogi.js
+++ b/client/src/variants/Shogi.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { sample, shuffle } from "@/utils/alea";
 
 export class ShogiRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -656,4 +657,5 @@ export class ShogiRules extends ChessRules {
       )
     );
   }
+
 };
diff --git a/client/src/variants/Sittuyin.js b/client/src/variants/Sittuyin.js
index 0ddf74c9..5efd8cda 100644
--- a/client/src/variants/Sittuyin.js
+++ b/client/src/variants/Sittuyin.js
@@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class SittuyinRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -402,4 +403,5 @@ export class SittuyinRules extends ChessRules {
     }
     return super.getNotation(move);
   }
+
 };
diff --git a/client/src/variants/Suction.js b/client/src/variants/Suction.js
index 7fff939e..d12f83d2 100644
--- a/client/src/variants/Suction.js
+++ b/client/src/variants/Suction.js
@@ -2,6 +2,7 @@ import { ChessRules, PiPo, Move } from "@/base_rules";
 import { SuicideRules } from "@/variants/Suicide";
 
 export class SuctionRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -235,4 +236,5 @@ export class SuctionRules extends ChessRules {
       finalSquare
     );
   }
+
 };
diff --git a/client/src/variants/Suicide.js b/client/src/variants/Suicide.js
index e3630bb4..05fb19c3 100644
--- a/client/src/variants/Suicide.js
+++ b/client/src/variants/Suicide.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { shuffle } from "@/utils/alea";
 
 export class SuicideRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -165,4 +166,5 @@ export class SuicideRules extends ChessRules {
       " w 0 -"
     );
   }
+
 };
diff --git a/client/src/variants/Swap.js b/client/src/variants/Swap.js
index 4d97ae93..9c3407b7 100644
--- a/client/src/variants/Swap.js
+++ b/client/src/variants/Swap.js
@@ -2,6 +2,7 @@ import { ChessRules, PiPo } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class SwapRules extends ChessRules {
+
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     // Local stack of swaps
@@ -341,4 +342,5 @@ export class SwapRules extends ChessRules {
     // Swap
     return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end);
   }
+
 };
diff --git a/client/src/variants/Switching.js b/client/src/variants/Switching.js
index b940d0a6..8cb3141d 100644
--- a/client/src/variants/Switching.js
+++ b/client/src/variants/Switching.js
@@ -1,6 +1,7 @@
 import { ChessRules, Move, PiPo } from "@/base_rules";
 
 export class SwitchingRules extends ChessRules {
+
   // Build switch move between squares x1,y1 and x2,y2
 	getSwitchMove_s([x1, y1], [x2, y2]) {
 		const c = this.getColor(x1, y1); //same as color at square 2
@@ -123,4 +124,5 @@ export class SwitchingRules extends ChessRules {
     // Switch
     return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end);
   }
-}
+
+};
diff --git a/client/src/variants/Synchrone.js b/client/src/variants/Synchrone.js
index fea63378..365dd9aa 100644
--- a/client/src/variants/Synchrone.js
+++ b/client/src/variants/Synchrone.js
@@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class SynchroneRules extends ChessRules {
+
   static get CanAnalyze() {
     return false;
   }
@@ -78,7 +79,6 @@ export class SynchroneRules extends ChessRules {
         : null;
   }
 
-  // After undo(): no need to re-set INIT_COL_KING
   scanKings() {
     this.kingPos = { w: [-1, -1], b: [-1, -1] };
     for (let i = 0; i < V.size.x; i++) {
@@ -516,4 +516,5 @@ export class SynchroneRules extends ChessRules {
       V.CoordsToSquare(move.end)
     );
   }
+
 };
diff --git a/client/src/variants/Takenmake.js b/client/src/variants/Takenmake.js
index 6cea420c..5c0ac08e 100644
--- a/client/src/variants/Takenmake.js
+++ b/client/src/variants/Takenmake.js
@@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class TakenmakeRules extends ChessRules {
+
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     // Stack of "last move" only for intermediate captures
@@ -197,4 +198,5 @@ export class TakenmakeRules extends ChessRules {
     delete moves[mIdx]["next"];
     return [moves[mIdx], move2];
   }
+
 };
diff --git a/client/src/variants/Teleport.js b/client/src/variants/Teleport.js
index eadaf13b..52c7fd6e 100644
--- a/client/src/variants/Teleport.js
+++ b/client/src/variants/Teleport.js
@@ -2,6 +2,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules";
 import { randInt } from "@/utils/alea";
 
 export class TeleportRules extends ChessRules {
+
   hoverHighlight(x, y) {
     // Testing move validity results in an infinite update loop.
     // TODO: find a way to test validity anyway.
@@ -319,4 +320,5 @@ export class TeleportRules extends ChessRules {
       move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "";
     return piece + "@" + V.CoordsToSquare(move.end);
   }
+
 };
diff --git a/client/src/variants/Tencubed.js b/client/src/variants/Tencubed.js
index 33166254..af48977e 100644
--- a/client/src/variants/Tencubed.js
+++ b/client/src/variants/Tencubed.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { shuffle } from "@/utils/alea";
 
 export class TencubedRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -242,4 +243,5 @@ export class TencubedRules extends ChessRules {
       { c: 4, w: 3, a: 6, m: 8 }
     );
   }
+
 };
diff --git a/client/src/variants/Threechecks.js b/client/src/variants/Threechecks.js
index 05250596..c297d0a4 100644
--- a/client/src/variants/Threechecks.js
+++ b/client/src/variants/Threechecks.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class ThreechecksRules extends ChessRules {
+
   static IsGoodFlags(flags) {
     // 4 for castle + 2 for checks (0,1 or 2)
     return !!flags.match(/^[01]{4,4}[012]{2,2}$/);
@@ -64,4 +65,5 @@ export class ThreechecksRules extends ChessRules {
     // Take number of checks into account
     return baseEval/5 - this.checkFlags["w"] + this.checkFlags["b"];
   }
+
 };
diff --git a/client/src/variants/Titan.js b/client/src/variants/Titan.js
index 2ed54992..aadc3e70 100644
--- a/client/src/variants/Titan.js
+++ b/client/src/variants/Titan.js
@@ -1,13 +1,15 @@
-import { ChessRules } from "@/base_rules";
+import { ChessRules, Move, PiPo } from "@/base_rules";
 
 export class TitanRules extends ChessRules {
-  // Idea: yellow = bishop, orange = knight (for white)
-  // and, red = bishop + purple = knight (black side)
-  // (avoid using a bigger board, or complicated drawings)
+
+  static get IMAGE_EXTENSION() {
+    // Temporarily, for the time SVG pieces are being designed:
+    return ".png";
+  }
 
   // Decode if normal piece, or + bishop or knight
-  getPiece(i, j) {
-    const piece = this.board[i][j].charAt(1);
+  getPiece(x, y) {
+    const piece = this.board[x][y].charAt(1);
     if (ChessRules.PIECES.includes(piece)) return piece;
     // Augmented piece:
     switch (piece) {
@@ -29,8 +31,6 @@ export class TitanRules extends ChessRules {
     }
   }
 
-  // TODO: subtelty, castle forbidden if 
-
   // Code: a/c = bishop + knight/bishop j/l for king,
   // m/o for knight, s/t for queen, u/v for rook
   static get AUGMENTED_PIECES() {
@@ -48,6 +48,10 @@ export class TitanRules extends ChessRules {
     ];
   }
 
+  getPpath(b) {
+    return "Titan/" + b;
+  }
+
   // Decode above notation into additional piece
   getExtraPiece(symbol) {
     if (['a','j','m','s','u'].includes(symbol))
@@ -55,18 +59,98 @@ export class TitanRules extends ChessRules {
     return 'b';
   }
 
+  // Inverse operation: augment piece
+  getAugmented(piece) {
+    const knight = this.movesCount <= 1;
+    switch (piece) {
+      case V.ROOK: return (knight ? 'u' : 'v');
+      case V.KNIGHT: return (knight ? 'm' : 'o');
+      case V.BISHOP: return (knight ? 'a' : 'c');
+      case V.QUEEN: return (knight ? 's' : 't');
+      case V.KING: return (knight ? 'j' : 'l');
+    }
+    return '_'; //never reached
+  }
+
+  static IsGoodPosition(position) {
+    if (position.length == 0) return false;
+    const rows = position.split("/");
+    if (rows.length != V.size.x) return false;
+    let kings = { "w": 0, "b": 0 };
+    const allPiecesCodes = V.PIECES.concat(V.AUGMENTED_PIECES);
+    const kingBlackCodes = ['j','k','l'];
+    const kingWhiteCodes = ['J','K','L'];
+    for (let row of rows) {
+      let sumElts = 0;
+      for (let i = 0; i < row.length; i++) {
+        if (kingBlackCodes.includes(row[i])) kings['b']++;
+        else if (kingWhiteCodes.includes(row[i])) kings['w']++;
+        if (allPiecesCodes.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;
+    }
+    // Both kings should be on board, only one of each color:
+    if (Object.values(kings).some(v => v != 1)) return false;
+    return true;
+  }
+
+  // Kings may be augmented:
+  scanKings(fen) {
+    this.kingPos = { w: [-1, -1], b: [-1, -1] };
+    const rows = V.ParseFen(fen).position.split("/");
+    for (let i = 0; i < rows.length; i++) {
+      let k = 0; //column index on board
+      for (let j = 0; j < rows[i].length; j++) {
+        const piece = rows[i].charAt(j);
+        if (['j','k','l'].includes(piece.toLowerCase())) {
+          const color = (piece.charCodeAt(0) <= 90 ? 'w' : 'b');
+          this.kingPos[color] = [i, k];
+        }
+        else {
+          const num = parseInt(rows[i].charAt(j), 10);
+          if (!isNaN(num)) k += num - 1;
+        }
+        k++;
+      }
+    }
+  }
+
   // If piece not in usual list, bishop or knight appears.
   getPotentialMovesFrom([x, y]) {
-    let moves = super.getPotentialMovesFrom(sq);
+    if (this.movesCount <= 3) {
+      // Setup stage
+      const color = this.getColor(x, y);
+      const firstRank = (color == 'w' ? 7 : 0);
+      if (x != firstRank || V.AUGMENTED_PIECES.includes(this.board[x][y][1]))
+        return [];
+      const piece = this.getPiece(x, y);
+      const move = new Move({
+        appear: [
+          new PiPo({ x: x, y: y, c: color, p: this.getAugmented(piece) })
+        ],
+        vanish: [
+          new PiPo({ x: x, y: y, c: color, p: piece })
+        ],
+        start: { x: x, y: y },
+        end: { x: x, y: y }
+      });
+      return [move];
+    }
+    let moves = super.getPotentialMovesFrom([x, y]);
+    const initialPiece = this.getPiece(x, y);
     const color = this.turn;
-    
-// treat castle case here (both pieces appear!)
     if (
       V.AUGMENTED_PIECES.includes(this.board[x][y][1]) &&
       ((color == 'w' && x == 7) || (color == "b" && x == 0))
     ) {
       const newPiece = this.getExtraPiece(this.board[x][y][1]);
       moves.forEach(m => {
+        m.appear[0].p = initialPiece;
         m.appear.push(
           new PiPo({
             p: newPiece,
@@ -77,9 +161,129 @@ export class TitanRules extends ChessRules {
         );
       });
     }
+    moves.forEach(m => {
+      if (m.vanish.length <= 1) return;
+      const [vx, vy] = [m.vanish[1].x, m.vanish[1].y];
+      if (
+        m.appear.length >= 2 && //3 if the king was also augmented
+        m.vanish.length == 2 &&
+        m.vanish[1].c == color &&
+        V.AUGMENTED_PIECES.includes(this.board[vx][vy][1])
+      ) {
+        // Castle, rook is an "augmented piece"
+        m.appear[1].p = V.ROOK;
+        m.appear.push(
+          new PiPo({
+            p: this.getExtraPiece(this.board[vx][vy][1]),
+            c: color,
+            x: vx,
+            y: vy
+          })
+        );
+      }
+    });
     return moves;
   }
 
-  // TODO: special case of move 1 = choose squares, knight first, then bishop
-  // (just click ?)
+  // Special case of move 1 = choose squares, knight first, then bishop
+  doClick(square) {
+    if (this.movesCount >= 4) return null;
+    const color = this.turn;
+    const [x, y] = [square[0], square[1]];
+    if ((color == 'w' && x != 7) || (color == 'b' && x != 0)) return null;
+    const selectedPiece = this.board[x][y][1];
+    return new Move({
+      appear: [
+        new PiPo({
+          x: x,
+          y: y,
+          c: color,
+          p: this.getAugmented(selectedPiece)
+        })
+      ],
+      vanish: [
+        new PiPo({
+          x: x,
+          y: y,
+          c: color,
+          p: selectedPiece
+        })
+      ],
+      start: { x: x, y: y },
+      end: { x: x, y: y }
+    });
+  }
+
+  postPlay(move) {
+    if (this.movesCount > 4) super.postPlay(move);
+  }
+
+  postUndo(move) {
+    if (this.movesCount >= 4) super.postUndo(move);
+  }
+
+  evalPosition() {
+    let evaluation = 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) {
+          const sign = this.getColor(i, j) == "w" ? 1 : -1;
+          const piece = this.getPiece(i, j);
+          evaluation += sign * V.VALUES[piece];
+          const symbol = this.board[i][j][1];
+          if (V.AUGMENTED_PIECES.includes(symbol)) {
+            const extraPiece = this.getExtraPiece(symbol);
+            evaluation += sign * V.VALUES[extraPiece]
+          }
+        }
+      }
+    }
+    return evaluation;
+  }
+
+  getNotation(move) {
+    if (
+      move.appear[0].x != move.vanish[0].x ||
+      move.appear[0].y != move.vanish[0].y
+    ) {
+      if (
+        V.AUGMENTED_PIECES.includes(move.vanish[0].p) ||
+        (
+          move.vanish.length >= 2 &&
+          V.AUGMENTED_PIECES.includes(move.vanish[1].p)
+        )
+      ) {
+        // Simplify move before calling super.getNotation()
+        let smove = JSON.parse(JSON.stringify(move));
+        if (ChessRules.PIECES.includes(move.vanish[0].p)) {
+          // Castle with an augmented rook
+          smove.appear.pop();
+          smove.vanish[1].p = smove.appear[1].p;
+        }
+        else {
+          // Moving an augmented piece
+          smove.appear.pop();
+          smove.vanish[0].p = smove.appear[0].p;
+          if (
+            smove.vanish.length == 2 &&
+            smove.vanish[0].c == smove.vanish[1].c &&
+            V.AUGMENTED_PIECES.includes(move.vanish[1].p)
+          ) {
+            // Castle with an augmented rook
+            smove.appear.pop();
+            smove.vanish[1].p = smove.appear[1].p;
+          }
+        }
+        return super.getNotation(smove);
+      }
+      // Else, more common case:
+      return super.getNotation(move);
+    }
+    // First moves in game, placements:
+    const square = V.CoordsToSquare(move.appear[0]);
+    const reserve =
+      (['a','j','m','s','u'].includes(move.appear[0].p) ? 'N' : 'B');
+    return '+' + reserve + '@' + square;
+  }
+
 };
diff --git a/client/src/variants/Twokings.js b/client/src/variants/Twokings.js
index d56d0951..a5bd76af 100644
--- a/client/src/variants/Twokings.js
+++ b/client/src/variants/Twokings.js
@@ -2,6 +2,7 @@ import { ChessRules } from "@/base_rules";
 import { CoregalRules } from "@/variants/Coregal";
 
 export class TwokingsRules extends CoregalRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -101,6 +102,11 @@ export class TwokingsRules extends CoregalRules {
     );
   }
 
+  getPotentialQueenMoves(sq) {
+    return this.getSlideNJumpMoves(sq,
+      V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
+  }
+
   underCheck(color) {
     const oppCol = V.GetOppCol(color);
     for (let i=0; i<V.size.x; i++) {
@@ -123,4 +129,5 @@ export class TwokingsRules extends CoregalRules {
   }
 
   postUndo() {}
+
 };
diff --git a/client/src/variants/Upsidedown.js b/client/src/variants/Upsidedown.js
index abffd8f9..43f38257 100644
--- a/client/src/variants/Upsidedown.js
+++ b/client/src/variants/Upsidedown.js
@@ -3,6 +3,7 @@ import { randInt } from "@/utils/alea";
 import { ArrayFun } from "@/utils/array";
 
 export class UpsidedownRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -77,4 +78,5 @@ export class UpsidedownRules extends ChessRules {
   static get SEARCH_DEPTH() {
     return 2;
   }
+
 };
diff --git a/client/src/variants/Vchess.js b/client/src/variants/Vchess.js
index c11cce4e..c12435c5 100644
--- a/client/src/variants/Vchess.js
+++ b/client/src/variants/Vchess.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class VchessRules extends ChessRules {
+
   static get PawnSpecs() {
     return Object.assign(
       {},
@@ -33,4 +34,5 @@ export class VchessRules extends ChessRules {
     }
     return notation;
   }
+
 };
diff --git a/client/src/variants/Wildebeest.js b/client/src/variants/Wildebeest.js
index 526da1a7..6317c4bb 100644
--- a/client/src/variants/Wildebeest.js
+++ b/client/src/variants/Wildebeest.js
@@ -3,6 +3,7 @@ import { ArrayFun } from "@/utils/array";
 import { sample, randInt } from "@/utils/alea";
 
 export class WildebeestRules extends ChessRules {
+
   static get size() {
     return { x: 10, y: 11 };
   }
@@ -315,4 +316,5 @@ export class WildebeestRules extends ChessRules {
       " w 0 " + flags + " -"
     );
   }
+
 };
diff --git a/client/src/variants/Wormhole.js b/client/src/variants/Wormhole.js
index 1f3ecd2a..fcf63b4e 100644
--- a/client/src/variants/Wormhole.js
+++ b/client/src/variants/Wormhole.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class WormholeRules extends ChessRules {
+
   static get HasFlags() {
     return false;
   }
@@ -327,4 +328,5 @@ export class WormholeRules extends ChessRules {
       notation += "=" + move.appear[0].p.toUpperCase();
     return notation;
   }
+
 };
diff --git a/client/src/variants/Zen.js b/client/src/variants/Zen.js
index 808d5436..eb6748d0 100644
--- a/client/src/variants/Zen.js
+++ b/client/src/variants/Zen.js
@@ -1,6 +1,7 @@
 import { ChessRules } from "@/base_rules";
 
 export class ZenRules extends ChessRules {
+
   getEpSquare(moveOrSquare) {
     if (!moveOrSquare) return undefined;
     if (typeof moveOrSquare === "string") {
@@ -161,4 +162,5 @@ export class ZenRules extends ChessRules {
       k: 1000
     };
   }
+
 };
-- 
2.44.0