Experimental change: options replacing randomness (more general)
authorBenjamin Auder <benjamin.auder@somewhere>
Thu, 8 Apr 2021 19:41:58 +0000 (21:41 +0200)
committerBenjamin Auder <benjamin.auder@somewhere>
Thu, 8 Apr 2021 19:41:58 +0000 (21:41 +0200)
47 files changed:
TODO
client/public/images/pieces/Fugue/wl.svg
client/src/App.vue
client/src/base_rules.js
client/src/components/ChallengeList.vue
client/src/components/ComputerGame.vue
client/src/components/GameList.vue
client/src/components/Settings.vue
client/src/main.js
client/src/store.js
client/src/translations/about/en.pug
client/src/translations/about/es.pug
client/src/translations/about/fr.pug
client/src/translations/en.js
client/src/translations/es.js
client/src/translations/fr.js
client/src/translations/rules/Allmate/en.pug [moved from client/src/translations/rules/Allmate1/en.pug with 95% similarity]
client/src/translations/rules/Allmate/es.pug [moved from client/src/translations/rules/Allmate1/es.pug with 99% similarity]
client/src/translations/rules/Allmate/fr.pug [moved from client/src/translations/rules/Allmate1/fr.pug with 96% similarity]
client/src/translations/rules/Allmate2/en.pug [deleted file]
client/src/translations/rules/Allmate2/es.pug [deleted file]
client/src/translations/rules/Allmate2/fr.pug [deleted file]
client/src/translations/rules/Checkered/en.pug [moved from client/src/translations/rules/Checkered1/en.pug with 97% similarity]
client/src/translations/rules/Checkered/es.pug [moved from client/src/translations/rules/Checkered1/es.pug with 97% similarity]
client/src/translations/rules/Checkered/fr.pug [moved from client/src/translations/rules/Checkered1/fr.pug with 97% similarity]
client/src/translations/rules/Checkered2/en.pug [deleted file]
client/src/translations/rules/Checkered2/es.pug [deleted file]
client/src/translations/rules/Checkered2/fr.pug [deleted file]
client/src/translations/rules/Chess960/en.pug [moved from client/src/translations/rules/Chess/en.pug with 99% similarity]
client/src/translations/rules/Chess960/es.pug [moved from client/src/translations/rules/Chess/es.pug with 99% similarity]
client/src/translations/rules/Chess960/fr.pug [moved from client/src/translations/rules/Chess/fr.pug with 99% similarity]
client/src/variants/Allmate.js [moved from client/src/variants/Allmate1.js with 99% similarity]
client/src/variants/Allmate2.js [deleted file]
client/src/variants/Checkered.js [moved from client/src/variants/Checkered1.js with 95% similarity]
client/src/variants/Checkered2.js [deleted file]
client/src/variants/Chess.js [deleted symlink]
client/src/variants/Chess960.js [new file with mode: 0644]
client/src/views/Analyse.vue
client/src/views/Game.vue
client/src/views/Hall.vue
client/src/views/Rules.vue
client/src/views/Variants.vue
server/db/create.sql
server/db/populate.sql
server/models/Challenge.js
server/models/Game.js
server/routes/challenges.js

diff --git a/TODO b/TODO
index d3b6227..924b6d0 100644 (file)
--- a/TODO
+++ b/TODO
@@ -20,4 +20,15 @@ CWDA : need game options (also useful at least for Monster)
 PizzaKings https://www.chessvariants.com/unequal.dir/pizza-kings.html
 https://en.m.wikipedia.org/wiki/Chess_with_different_armies#Pizza_Kings%5B11%5D_(John_Lawson)
 
 PizzaKings https://www.chessvariants.com/unequal.dir/pizza-kings.html
 https://en.m.wikipedia.org/wiki/Chess_with_different_armies#Pizza_Kings%5B11%5D_(John_Lawson)
 
+Coin Chess
+https://msoworld.com/product/chess-variants/
+Background: Unknown inventor
+Rules:
+Normal rules apply except for the introduction of a coin (or counter)
+Black starts by placing the coin on any unoccupied square. Play then continues with players alternating turns as in normal chess.
+On their turn, a player makes any legal move but may not move onto the square where the coin has been placed. The player may move over (but not onto), the coin square.
+The player ends their turn by leaving the coin where it stands or moving the coin to a different unoccupied square.
+The coin can never be placed on an occupied square, and therefore cannot be used to protect a piece from capture
+A player wins by checkmating the opponent. Note that the coin can be used to remove escape squares from the king.
+
 https://www.chessvariants.com/other.dir/nemoroth.html :-)
 https://www.chessvariants.com/other.dir/nemoroth.html :-)
index d73d3bd..cc2b974 100644 (file)
@@ -13,7 +13,7 @@
    version="1.1"
    id="svg12"
    sodipodi:docname="wl.svg"
    version="1.1"
    id="svg12"
    sodipodi:docname="wl.svg"
-   inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07, custom)">
+   inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
   <metadata
      id="metadata18">
     <rdf:RDF>
   <metadata
      id="metadata18">
     <rdf:RDF>
      guidetolerance="10"
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
      guidetolerance="10"
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
-     inkscape:window-width="1920"
+     inkscape:window-width="960"
      inkscape:window-height="1060"
      id="namedview14"
      showgrid="false"
      inkscape:zoom="1.7246094"
      inkscape:cx="256"
      inkscape:window-height="1060"
      id="namedview14"
      showgrid="false"
      inkscape:zoom="1.7246094"
      inkscape:cx="256"
-     inkscape:cy="255.42016"
+     inkscape:cy="254.84032"
      inkscape:window-x="0"
      inkscape:window-y="20"
      inkscape:window-maximized="0"
      inkscape:window-x="0"
      inkscape:window-y="20"
      inkscape:window-maximized="0"
-     inkscape:current-layer="svg12" />
+     inkscape:current-layer="svg12"
+     inkscape:document-rotation="0" />
   <g
      id="010---Frog"
      transform="matrix(0.8,0,0,0.8,6.0000003,6)">
   <g
      id="010---Frog"
      transform="matrix(0.8,0,0,0.8,6.0000003,6)">
      d="m 337.81831,445.85023 c -10.4388,-2.80716 -18.34008,-11.95076 -19.75585,-22.86208 -0.3099,-2.38844 -0.57152,-7.14629 -0.58136,-10.573 l -0.0179,-6.23038 2.94262,-3.62692 c 3.89557,-4.80148 8.76783,-12.99552 11.31926,-19.03639 3.94316,-9.33604 6.61242,-22.77207 6.61242,-33.28449 0,-3.51978 0.11123,-3.78972 3.5586,-8.63619 5.97066,-8.39384 12.04985,-15.26538 22.90012,-25.88492 18.58016,-18.18504 33.22365,-27.66968 49.66778,-32.16997 2.66295,-0.72877 3.59096,-1.30322 4.49161,-2.78035 2.51574,-4.12599 0.70172,-9.36723 -3.75061,-10.83664 -4.93204,-1.62772 -21.05317,4.81069 -34.44901,13.75814 -9.69646,6.47653 -16.23253,12.10004 -29.43403,25.32449 l -12.81849,12.84076 -0.37277,-7.36534 c -0.20502,-4.05093 -0.51019,-7.72345 -0.67815,-8.16114 -0.67475,-1.75837 15.29792,-17.9046 26.1136,-26.39736 18.48353,-14.51375 38.21698,-23.34861 52.15124,-23.34861 6.54713,0 10.13163,1.11515 13.08827,4.07179 2.89575,2.89576 4.15016,6.83571 4.13303,12.98141 -0.0522,18.7298 -14.70341,45.11564 -38.04471,68.51594 -9.49806,9.52206 -16.97344,15.60664 -28.544,23.23334 -8.35929,5.51 -10.87871,7.93729 -12.7369,12.2711 -4.26923,9.95703 0.0894,22.23736 9.5458,26.89528 l 2.72108,1.34031 37.97962,0.28993 c 36.08008,0.27541 38.06085,0.34497 39.60381,1.39075 3.07868,2.08662 4.02373,5.39369 2.42488,8.48553 -1.82084,3.52113 -1.18172,3.46007 -36.22011,3.46007 -35.03251,0 -35.03094,-1.5e-4 -36.79159,3.40457 -2.21954,4.29214 -0.22935,9.26049 4.20261,10.49143 1.27878,0.35517 10.71904,0.59487 23.5043,0.5968 l 21.36715,0.003 1.93729,1.93728 c 2.46615,2.46616 2.95099,4.36523 1.75687,6.88163 -1.96428,4.13942 0.28881,3.94194 -44.48849,3.89944 -33.22344,-0.0315 -40.75306,-0.18432 -43.33798,-0.87945 z"
      id="path26"
      transform="scale(0.1171875)" />
      d="m 337.81831,445.85023 c -10.4388,-2.80716 -18.34008,-11.95076 -19.75585,-22.86208 -0.3099,-2.38844 -0.57152,-7.14629 -0.58136,-10.573 l -0.0179,-6.23038 2.94262,-3.62692 c 3.89557,-4.80148 8.76783,-12.99552 11.31926,-19.03639 3.94316,-9.33604 6.61242,-22.77207 6.61242,-33.28449 0,-3.51978 0.11123,-3.78972 3.5586,-8.63619 5.97066,-8.39384 12.04985,-15.26538 22.90012,-25.88492 18.58016,-18.18504 33.22365,-27.66968 49.66778,-32.16997 2.66295,-0.72877 3.59096,-1.30322 4.49161,-2.78035 2.51574,-4.12599 0.70172,-9.36723 -3.75061,-10.83664 -4.93204,-1.62772 -21.05317,4.81069 -34.44901,13.75814 -9.69646,6.47653 -16.23253,12.10004 -29.43403,25.32449 l -12.81849,12.84076 -0.37277,-7.36534 c -0.20502,-4.05093 -0.51019,-7.72345 -0.67815,-8.16114 -0.67475,-1.75837 15.29792,-17.9046 26.1136,-26.39736 18.48353,-14.51375 38.21698,-23.34861 52.15124,-23.34861 6.54713,0 10.13163,1.11515 13.08827,4.07179 2.89575,2.89576 4.15016,6.83571 4.13303,12.98141 -0.0522,18.7298 -14.70341,45.11564 -38.04471,68.51594 -9.49806,9.52206 -16.97344,15.60664 -28.544,23.23334 -8.35929,5.51 -10.87871,7.93729 -12.7369,12.2711 -4.26923,9.95703 0.0894,22.23736 9.5458,26.89528 l 2.72108,1.34031 37.97962,0.28993 c 36.08008,0.27541 38.06085,0.34497 39.60381,1.39075 3.07868,2.08662 4.02373,5.39369 2.42488,8.48553 -1.82084,3.52113 -1.18172,3.46007 -36.22011,3.46007 -35.03251,0 -35.03094,-1.5e-4 -36.79159,3.40457 -2.21954,4.29214 -0.22935,9.26049 4.20261,10.49143 1.27878,0.35517 10.71904,0.59487 23.5043,0.5968 l 21.36715,0.003 1.93729,1.93728 c 2.46615,2.46616 2.95099,4.36523 1.75687,6.88163 -1.96428,4.13942 0.28881,3.94194 -44.48849,3.89944 -33.22344,-0.0315 -40.75306,-0.18432 -43.33798,-0.87945 z"
      id="path26"
      transform="scale(0.1171875)" />
+  <path
+     style="fill:#ffffff;stroke:#000000;stroke-width:0.579841"
+     d="m 194.60655,118.19666 c -2.41356,-1.22724 -5.07782,-4.80585 -5.93454,-7.97119 -1.03527,-3.82506 -0.3344,-9.24725 1.59667,-12.35241 2.34703,-3.774029 4.34459,-5.098424 7.68987,-5.098424 3.50397,0 6.34607,2.039401 8.26069,5.927609 1.92065,3.900445 1.91919,10.324345 -0.003,14.237645 -2.51645,5.12252 -7.46596,7.36366 -11.60946,5.25677 z"
+     id="path16"
+     transform="scale(0.1171875)" />
+  <path
+     style="fill:#ffffff;stroke:#000000;stroke-width:0.579841"
+     d="m 310.57484,118.19666 c -5.83545,-2.96721 -8.15717,-12.49955 -4.75579,-19.525881 1.86272,-3.84785 4.71914,-5.896143 8.2224,-5.896143 3.37415,0 5.34355,1.325238 7.77055,5.228919 1.58018,2.541625 1.73953,3.253555 1.73953,7.772045 0,4.38385 -0.18949,5.29646 -1.5968,7.69048 -2.88247,4.90343 -7.3707,6.76917 -11.37989,4.73058 z"
+     id="path18"
+     transform="scale(0.1171875)" />
 </svg>
 </svg>
index 67dc225..b17133d 100644 (file)
           a.menuitem(href="https://github.com/yagu0/vchess")
             span {{ st.tr["Code"] }}
             img(src="/images/icons/github.svg")
           a.menuitem(href="https://github.com/yagu0/vchess")
             span {{ st.tr["Code"] }}
             img(src="/images/icons/github.svg")
-          //a.menuitem(href="https://www.facebook.com/Variants-Chess-Club-112565840437886")
-            img(src="/images/icons/facebook.svg")
-          //a.menuitem(href="https://twitter.com/VchessC")
-            img(src="/images/icons/twitter.svg")
 </template>
 
 <script>
 </template>
 
 <script>
index f7a2fee..43f9f26 100644 (file)
@@ -34,6 +34,30 @@ export const ChessRules = class ChessRules {
   //////////////
   // MISC UTILS
 
   //////////////
   // MISC UTILS
 
+  static get Options() {
+    return {
+      select: [
+        {
+          label: "Randomness",
+          variable: "randomness",
+          defaut: 2,
+          options: [
+            { label: "Deterministic", value: 0 },
+            { label: "Symmetric random", value: 1 },
+            { label: "Asymmetric random", value: 2 }
+          ]
+        }
+      ],
+      check: []
+    };
+  }
+
+  static AbbreviateOptions(opts) {
+    return "";
+    // Randomness is a special option: (TODO?)
+    //return "R" + opts.randomness;
+  }
+
   // Some variants don't have flags:
   static get HasFlags() {
     return true;
   // Some variants don't have flags:
   static get HasFlags() {
     return true;
@@ -343,8 +367,8 @@ export const ChessRules = class ChessRules {
   // FEN UTILS
 
   // Setup the initial random (asymmetric) position
   // FEN UTILS
 
   // Setup the initial random (asymmetric) position
-  static GenRandInitFen(randomness) {
-    if (randomness == 0)
+  static GenRandInitFen(options) {
+    if (!options.randomness || options.randomness == 0)
       // Deterministic:
       return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 ahah -";
 
       // Deterministic:
       return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 ahah -";
 
@@ -352,7 +376,7 @@ export const ChessRules = class ChessRules {
     let flags = "";
     // Shuffle pieces on first (and last rank if randomness == 2)
     for (let c of ["w", "b"]) {
     let flags = "";
     // Shuffle pieces on first (and last rank if randomness == 2)
     for (let c of ["w", "b"]) {
-      if (c == 'b' && randomness == 1) {
+      if (c == 'b' && options.randomness == 1) {
         pieces['b'] = pieces['w'];
         flags += flags;
         break;
         pieces['b'] = pieces['w'];
         flags += flags;
         break;
index 1107b58..a354203 100644 (file)
@@ -6,7 +6,7 @@ div
         th {{ st.tr["Variant"] }}
         th {{ st.tr["With"] }}
         th {{ st.tr["Cadence"] }}
         th {{ st.tr["Variant"] }}
         th {{ st.tr["With"] }}
         th {{ st.tr["Cadence"] }}
-        th {{ st.tr["Random?"] }}
+        th {{ st.tr["Options"] }}
     tbody
       tr(
         v-for="c in sortedChallenges"
     tbody
       tr(
         v-for="c in sortedChallenges"
@@ -16,7 +16,7 @@ div
         td {{ c.vname }}
         td {{ withWho(c) }}
         td {{ c.cadence }}
         td {{ c.vname }}
         td {{ withWho(c) }}
         td {{ c.cadence }}
-        td(:class="getRandomnessClass(c)")
+        td(:class="getRandomnessClass(c)") {{ c.options.abridged }}
   p(v-else)
     | {{ st.tr["No challenges found :( Click on 'New game'!"] }}
 </template>
   p(v-else)
     | {{ st.tr["No challenges found :( Click on 'New game'!"] }}
 </template>
@@ -63,8 +63,9 @@ export default {
       return c.from.name || "@nonymous";
     },
     getRandomnessClass: function(c) {
       return c.from.name || "@nonymous";
     },
     getRandomnessClass: function(c) {
+      if (!c.options.randomness) return {};
       return {
       return {
-        ["random-" + c.randomness]: true
+        ["random-" + c.options.randomness]: true
       };
     }
   }
       };
     }
   }
@@ -84,9 +85,9 @@ tr.toyou > td
 
 tr > td:last-child
   &.random-0
 
 tr > td:last-child
   &.random-0
-    background-color: #FF5733
+    background-color: #FEAF9E
   &.random-1
   &.random-1
-    background-color: #2B63B4
+    background-color: #9EB2FE
   &.random-2
   &.random-2
-    background-color: #33B42B
+    background-color: #A5FE9E
 </style>
 </style>
index 9168922..b212d0f 100644 (file)
@@ -63,12 +63,12 @@ export default {
     };
   },
   methods: {
     };
   },
   methods: {
-    launchGame: function(game) {
+    launchGame: function(game, options) {
       this.compWorker.postMessage(["scripts", this.gameInfo.vname]);
       if (!game) {
         game = {
           vname: this.gameInfo.vname,
       this.compWorker.postMessage(["scripts", this.gameInfo.vname]);
       if (!game) {
         game = {
           vname: this.gameInfo.vname,
-          fenStart: V.GenRandInitFen(this.st.settings.randomness),
+          fenStart: V.GenRandInitFen(options),
           moves: []
         };
         game.fen = game.fenStart;
           moves: []
         };
         game.fen = game.fenStart;
index d81a1de..ed6b99f 100644 (file)
@@ -13,7 +13,7 @@ div
         @click="$emit('show-game',g)"
         :class="{'my-turn': !!g.myTurn}"
       )
         @click="$emit('show-game',g)"
         :class="{'my-turn': !!g.myTurn}"
       )
-        td {{ g.vname }}
+        td {{ g.vname + '-' + g.options.abridged }}
         td {{ player_s(g) }}
         td(v-if="showCadence") {{ g.cadence }}
         td(
         td {{ player_s(g) }}
         td(v-if="showCadence") {{ g.cadence }}
         td(
index 415e9d4..514e704 100644 (file)
@@ -60,12 +60,6 @@ div
             type="checkbox"
             v-model="st.settings.gotonext"
           )
             type="checkbox"
             v-model="st.settings.gotonext"
           )
-        fieldset
-          label(for="setRandomness") {{ st.tr["Randomness"] }}
-          select#setRandomness(v-model="st.settings.randomness")
-            option(value="0") {{ st.tr["Deterministic"] }}
-            option(value="1") {{ st.tr["Symmetric random"] }}
-            option(value="2") {{ st.tr["Asymmetric random"] }}
 </template>
 
 <script>
 </template>
 
 <script>
@@ -100,7 +94,7 @@ export default {
       const propName = event.target.id
         .substr(3)
         .replace(/^\w/, c => c.toLowerCase());
       const propName = event.target.id
         .substr(3)
         .replace(/^\w/, c => c.toLowerCase());
-      const value = ["bcolor","randomness"].includes(propName)
+      const value = propName == "bcolor"
         ? event.target.value
         : event.target.checked;
       store.updateSetting(propName, value);
         ? event.target.value
         : event.target.checked;
       store.updateSetting(propName, value);
index 3577b15..b0f9ddc 100644 (file)
@@ -22,8 +22,8 @@ new Vue({
         modalBoxes.forEach(m => {
           if (
             m.checked &&
         modalBoxes.forEach(m => {
           if (
             m.checked &&
-            !["modalAccept", "modalConfirm", "modalChat", "modalPeople"]
-              .includes(m.id)
+            !["Accept", "Confirm", "Chat", "People"]
+              .includes(m.id.substr(5)) //modalThing --> Thing
           ) {
             m.checked = false;
           }
           ) {
             m.checked = false;
           }
index 45535f8..13c600b 100644 (file)
@@ -90,11 +90,7 @@ export const store = {
       hints: getItemDefaultTrue("hints"),
       highlight: getItemDefaultTrue("highlight"),
       gotonext: getItemDefaultTrue("gotonext"),
       hints: getItemDefaultTrue("hints"),
       highlight: getItemDefaultTrue("highlight"),
       gotonext: getItemDefaultTrue("gotonext"),
-      randomness: parseInt(localStorage.getItem("randomness"), 10)
     };
     };
-    if (isNaN(this.state.settings.randomness))
-      // Default: random asymmetric
-      this.state.settings.randomness = 2;
     const supportedLangs = ["en", "es", "fr"];
     const navLanguage = navigator.language.substr(0, 2);
     this.state.lang =
     const supportedLangs = ["en", "es", "fr"];
     const navLanguage = navigator.language.substr(0, 2);
     this.state.lang =
index 4979575..82fabd0 100644 (file)
@@ -50,8 +50,10 @@ h3 Related links
   a(href="https://mindsports.nl/index.php") mindsports.nl
   a(href="https://www.jocly.com/#/games") jocly.com
   a(href="http://www.iggamecenter.com/") iggamecenter.com
   a(href="https://mindsports.nl/index.php") mindsports.nl
   a(href="https://www.jocly.com/#/games") jocly.com
   a(href="http://www.iggamecenter.com/") iggamecenter.com
+  a(href="https://boardspace.net/english/index.shtml") boardspace.net
   a(href="https://musketeerchess.net/home/index.html") musketeerchess.net
   a(href="https://schemingmind.com/") schemingmind.com
   a(href="https://musketeerchess.net/home/index.html") musketeerchess.net
   a(href="https://schemingmind.com/") schemingmind.com
+  a(href="https://boardgamearena.com/") boardgamearena.com
   a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
   a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
   a(href="https://brainking.com/") brainking.com
   a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
   a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
   a(href="https://brainking.com/") brainking.com
index 4b08b48..95f69c0 100644 (file)
@@ -49,8 +49,10 @@ h3 Enlaces relacionados
   a(href="https://mindsports.nl/index.php") mindsports.nl
   a(href="https://www.jocly.com/#/games") jocly.com
   a(href="http://www.iggamecenter.com/") iggamecenter.com
   a(href="https://mindsports.nl/index.php") mindsports.nl
   a(href="https://www.jocly.com/#/games") jocly.com
   a(href="http://www.iggamecenter.com/") iggamecenter.com
+  a(href="https://boardspace.net/english/index.shtml") boardspace.net
   a(href="https://musketeerchess.net/home/index.html") musketeerchess.net
   a(href="https://schemingmind.com/") schemingmind.com
   a(href="https://musketeerchess.net/home/index.html") musketeerchess.net
   a(href="https://schemingmind.com/") schemingmind.com
+  a(href="https://boardgamearena.com/") boardgamearena.com
   a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
   a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
   a(href="https://brainking.com/") brainking.com
   a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
   a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
   a(href="https://brainking.com/") brainking.com
index cf58ca7..eb47ccd 100644 (file)
@@ -50,8 +50,10 @@ h3 Liens connexes
   a(href="https://mindsports.nl/index.php") mindsports.nl
   a(href="https://www.jocly.com/#/games") jocly.com
   a(href="http://www.iggamecenter.com/") iggamecenter.com
   a(href="https://mindsports.nl/index.php") mindsports.nl
   a(href="https://www.jocly.com/#/games") jocly.com
   a(href="http://www.iggamecenter.com/") iggamecenter.com
+  a(href="https://boardspace.net/english/index.shtml") boardspace.net
   a(href="https://musketeerchess.net/home/index.html") musketeerchess.net
   a(href="https://schemingmind.com/") schemingmind.com
   a(href="https://musketeerchess.net/home/index.html") musketeerchess.net
   a(href="https://schemingmind.com/") schemingmind.com
+  a(href="https://boardgamearena.com/") boardgamearena.com
   a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
   a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
   a(href="https://brainking.com/") brainking.com
   a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr
   a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net
   a(href="https://brainking.com/") brainking.com
index b8c1cd9..133a0de 100644 (file)
@@ -90,7 +90,7 @@ export const translations = {
   "No more problems": "No more problems",
   "No subject. Send anyway?": "No subject. Send anyway?",
   "Notifications by email": "Notifications by email",
   "No more problems": "No more problems",
   "No subject. Send anyway?": "No subject. Send anyway?",
   "Notifications by email": "Notifications by email",
-  Number: "Number",
+  "Number": "Number",
   Observe: "Observe",
   "Offer draw?": "Offer draw?",
   "Opponent action": "Opponent action",
   Observe: "Observe",
   "Offer draw?": "Offer draw?",
   "Opponent action": "Opponent action",
@@ -108,8 +108,6 @@ export const translations = {
   Previous_p: "Previous",
   "Processing... Please wait": "Processing... Please wait",
   Problems: "Problems",
   Previous_p: "Previous",
   "Processing... Please wait": "Processing... Please wait",
   Problems: "Problems",
-  "Random?": "Random?",
-  "Randomness": "Randomness",
   Refuse: "Refuse",
   Register: "Register",
   "Registration complete! Please check your emails now": "Registration complete! Please check your emails now",
   Refuse: "Refuse",
   Register: "Register",
   "Registration complete! Please check your emails now": "Registration complete! Please check your emails now",
@@ -260,8 +258,7 @@ export const translations = {
   "Lose all pieces": "Lose all pieces",
   "Malagasy Draughts": "Malagasy Draughts",
   "Mandatory captures": "Mandatory captures",
   "Lose all pieces": "Lose all pieces",
   "Malagasy Draughts": "Malagasy Draughts",
   "Mandatory captures": "Mandatory captures",
-  "Mate any piece (v1)": "Mate any piece (v1)",
-  "Mate any piece (v2)": "Mate any piece (v2)",
+  "Mate any piece": "Mate any piece",
   "Mate the knight (v1)": "Mate the knight (v1)",
   "Mate the knight (v2)": "Mate the knight (v2)",
   "Meet the Mammoth": "Meet the Mammoth",
   "Mate the knight (v1)": "Mate the knight (v1)",
   "Mate the knight (v2)": "Mate the knight (v2)",
   "Meet the Mammoth": "Meet the Mammoth",
@@ -313,8 +310,7 @@ export const translations = {
   "Run forward": "Run forward",
   "Score a goal": "Score a goal",
   "Seirawan-Harper Chess": "Seirawan-Harper Chess",
   "Run forward": "Run forward",
   "Score a goal": "Score a goal",
   "Seirawan-Harper Chess": "Seirawan-Harper Chess",
-  "Shared pieces (v1)": "Shared pieces (v1)",
-  "Shared pieces (v2)": "Shared pieces (v2)",
+  "Shared pieces": "Shared pieces",
   "Shogi 5 x 5": "Shogi 5 x 5",
   "Shoot pieces": "Shoot pieces",
   "Spartan versus Persians": "Spartan versus Persians",
   "Shogi 5 x 5": "Shogi 5 x 5",
   "Shoot pieces": "Shoot pieces",
   "Spartan versus Persians": "Spartan versus Persians",
@@ -343,7 +339,7 @@ export const translations = {
   // Variants by categories:
   "What is a chess variant?": "What is a chess variant?",
   "Why play chess variants?": "Why play chess variants?",
   // Variants by categories:
   "What is a chess variant?": "What is a chess variant?",
   "Why play chess variants?": "Why play chess variants?",
-  "chess_v": ": to play under standard rules, with a random (or not) symmetric (or not) initial position.",
+  "chess960_v": ": to play under standard rules, with a random symmetric (or not) initial position.",
   "vt0": "Simplified games to learn chess",
   "vg0": "Variants with very few different pieces, and a simplified goal.",
   "vt1": "Forced captures",
   "vt0": "Simplified games to learn chess",
   "vg0": "Variants with very few different pieces, and a simplified goal.",
   "vt1": "Forced captures",
@@ -408,4 +404,9 @@ export const translations = {
   "vg30": "Pieces can temporarily borrow powers from others.",
   "vt31": "Miscelleanous",
   "vg31": "These variants are not classified yet, generally because they are the only one of their kind on this website.",
   "vg30": "Pieces can temporarily borrow powers from others.",
   "vt31": "Miscelleanous",
   "vg31": "These variants are not classified yet, generally because they are the only one of their kind on this website.",
+
+  // Variants' options
+  "Options": "Options",
+  "Randomness": "Randomness",
+  "With switch": "With switch",
 };
 };
index 55dd304..d3be2c1 100644 (file)
@@ -90,7 +90,7 @@ export const translations = {
   "No more problems": "No mas problemas",
   "No subject. Send anyway?": "Sin asunto. Â¿Enviar sin embargo?",
   "Notifications by email": "Notificaciones por email",
   "No more problems": "No mas problemas",
   "No subject. Send anyway?": "Sin asunto. Â¿Enviar sin embargo?",
   "Notifications by email": "Notificaciones por email",
-  Number: "Número",
+  "Number": "Número",
   "Offer draw?": "¿Ofrecer tablas?",
   Observe: "Observar",
   "Opponent action": "Acción del adversario",
   "Offer draw?": "¿Ofrecer tablas?",
   Observe: "Observar",
   "Opponent action": "Acción del adversario",
@@ -108,8 +108,6 @@ export const translations = {
   Previous_n: "Anterior",
   "Processing... Please wait": "Procesando... por favor espere",
   Problems: "Problemas",
   Previous_n: "Anterior",
   "Processing... Please wait": "Procesando... por favor espere",
   Problems: "Problemas",
-  "Random?": "Aleatorio?",
-  "Randomness": "Grado de azar",
   Refuse: "Rechazar",
   Register: "Inscribirse",
   "Registration complete! Please check your emails now": "¡Registro completo! Revise sus correos electrónicos ahora",
   Refuse: "Rechazar",
   Register: "Inscribirse",
   "Registration complete! Please check your emails now": "¡Registro completo! Revise sus correos electrónicos ahora",
@@ -260,8 +258,7 @@ export const translations = {
   "Lose all pieces": "Perder todas las piezas",
   "Malagasy Draughts": "Damas malgaches",
   "Mandatory captures": "Capturas obligatorias",
   "Lose all pieces": "Perder todas las piezas",
   "Malagasy Draughts": "Damas malgaches",
   "Mandatory captures": "Capturas obligatorias",
-  "Mate any piece (v1)": "Matar cualquier pieza (v1)",
-  "Mate any piece (v2)": "Matar cualquier pieza (v2)",
+  "Mate any piece": "Matar cualquier pieza",
   "Mate the knight (v1)": "Matar el caballo (v1)",
   "Mate the knight (v2)": "Matar el caballo (v2)",
   "Meet the Mammoth": "Conoce al Mamut",
   "Mate the knight (v1)": "Matar el caballo (v1)",
   "Mate the knight (v2)": "Matar el caballo (v2)",
   "Meet the Mammoth": "Conoce al Mamut",
@@ -313,8 +310,7 @@ export const translations = {
   "Run forward": "Correr hacia adelante",
   "Score a goal": "Marcar una meta",
   "Seirawan-Harper Chess": "Ajedrez Seirawan-Harper",
   "Run forward": "Correr hacia adelante",
   "Score a goal": "Marcar una meta",
   "Seirawan-Harper Chess": "Ajedrez Seirawan-Harper",
-  "Shared pieces (v1)": "Piezas compartidas (v1)",
-  "Shared pieces (v2)": "Piezas compartidas (v2)",
+  "Shared pieces": "Piezas compartidas",
   "Shogi 5 x 5": "Shogi 5 x 5",
   "Shoot pieces": "Tirar de las piezas",
   "Spartan versus Persians": "Espartanos contra Persas",
   "Shogi 5 x 5": "Shogi 5 x 5",
   "Shoot pieces": "Tirar de las piezas",
   "Spartan versus Persians": "Espartanos contra Persas",
@@ -343,7 +339,7 @@ export const translations = {
   // Variants by categories:
   "What is a chess variant?": "¿Qué es una variante?",
   "Why play chess variants?": "¿Por qué jugar las variantes?",
   // Variants by categories:
   "What is a chess variant?": "¿Qué es una variante?",
   "Why play chess variants?": "¿Por qué jugar las variantes?",
-  "chess_v": ": para jugar con reglas estándar, desde una posición inicial aleatorio (o no) simétrico (o no).",
+  "chess960_v": ": para jugar con reglas estándar, desde una posición inicial aleatorio simétrico (o no).",
   "vt0": "Juegos simplificados para aprender ajedrez",
   "vg0": "Variantes con muy pocas piezas diferentes y un propósito simplificado.",
   "vt1": "Capturas obligatorias",
   "vt0": "Juegos simplificados para aprender ajedrez",
   "vg0": "Variantes con muy pocas piezas diferentes y un propósito simplificado.",
   "vt1": "Capturas obligatorias",
@@ -408,4 +404,9 @@ export const translations = {
   "vg30": "Las piezas pueden temporalmente tomar prestados poderes de otras",
   "vt31": "Varios",
   "vg31": "Estas variantes aún no están clasificadas, en general porque son el Ãºnico representante de su tipo en este sitio.",
   "vg30": "Las piezas pueden temporalmente tomar prestados poderes de otras",
   "vt31": "Varios",
   "vg31": "Estas variantes aún no están clasificadas, en general porque son el Ãºnico representante de su tipo en este sitio.",
+
+  // Variants' options
+  "Options": "Optiones",
+  "Randomness": "Grado de azar",
+  "With switch": "Con switch",
 };
 };
index 7f5f8fd..3cef19e 100644 (file)
@@ -90,7 +90,7 @@ export const translations = {
   "No more problems": "Plus de problèmes",
   "No subject. Send anyway?": "Pas de sujet. Envoyer quand-même ??",
   "Notifications by email": "Notifications par email",
   "No more problems": "Plus de problèmes",
   "No subject. Send anyway?": "Pas de sujet. Envoyer quand-même ??",
   "Notifications by email": "Notifications par email",
-  Number: "Numéro",
+  "Number": "Numéro",
   "Offer draw?": "Proposer nulle ?",
   Observe: "Observer",
   "Opponent action": "Action de l'adversaire",
   "Offer draw?": "Proposer nulle ?",
   Observe: "Observer",
   "Opponent action": "Action de l'adversaire",
@@ -108,8 +108,6 @@ export const translations = {
   Previous_n: "Précédente",
   "Processing... Please wait": "Traitement en cours... Attendez SVP",
   Problems: "Problèmes",
   Previous_n: "Précédente",
   "Processing... Please wait": "Traitement en cours... Attendez SVP",
   Problems: "Problèmes",
-  "Random?": "Aléatoire?",
-  "Randomness": "Degré d'aléa",
   Refuse: "Refuser",
   Register: "S'inscrire",
   "Registration complete! Please check your emails now": "Enregistrement terminé ! Allez voir vos emails maintenant",
   Refuse: "Refuser",
   Register: "S'inscrire",
   "Registration complete! Please check your emails now": "Enregistrement terminé ! Allez voir vos emails maintenant",
@@ -260,8 +258,7 @@ export const translations = {
   "Lose all pieces": "Perdez toutes les pièces",
   "Malagasy Draughts": "Dames malgaches",
   "Mandatory captures": "Captures obligatoires",
   "Lose all pieces": "Perdez toutes les pièces",
   "Malagasy Draughts": "Dames malgaches",
   "Mandatory captures": "Captures obligatoires",
-  "Mate any piece (v1)": "Matez n'importe quelle pièce (v1)",
-  "Mate any piece (v2)": "Matez n'importe quelle pièce (v2)",
+  "Mate any piece": "Matez n'importe quelle pièce",
   "Mate the knight (v1)": "Matez le cavalier (v1)",
   "Mate the knight (v2)": "Matez le cavalier (v2)",
   "Meet the Mammoth": "Rencontrez le Mammouth",
   "Mate the knight (v1)": "Matez le cavalier (v1)",
   "Mate the knight (v2)": "Matez le cavalier (v2)",
   "Meet the Mammoth": "Rencontrez le Mammouth",
@@ -313,8 +310,7 @@ export const translations = {
   "Run forward": "Courir vers l'avant",
   "Score a goal": "Marquer un but",
   "Seirawan-Harper Chess": "Échecs Seirawan-Harper",
   "Run forward": "Courir vers l'avant",
   "Score a goal": "Marquer un but",
   "Seirawan-Harper Chess": "Échecs Seirawan-Harper",
-  "Shared pieces (v1)": "Pièces partagées (v1)",
-  "Shared pieces (v2)": "Pièces partagées (v2)",
+  "Shared pieces": "Pièces partagées",
   "Shogi 5 x 5": "Shogi 5 x 5",
   "Shoot pieces": "Tirez sur les pièces",
   "Spartan versus Persians": "Spartiates contre Perses",
   "Shogi 5 x 5": "Shogi 5 x 5",
   "Shoot pieces": "Tirez sur les pièces",
   "Spartan versus Persians": "Spartiates contre Perses",
@@ -343,7 +339,7 @@ export const translations = {
   // Variants by categories:
   "What is a chess variant?": "Qu'est-ce qu'une variante ?",
   "Why play chess variants?": "Pourquoi jouer aux variantes ?",
   // Variants by categories:
   "What is a chess variant?": "Qu'est-ce qu'une variante ?",
   "Why play chess variants?": "Pourquoi jouer aux variantes ?",
-  "chess_v": " : pour jouer avec les règles standard, depuis une position initiale aléatoire (ou non) symétrique (ou non).",
+  "chess960_v": " : pour jouer avec les règles standard, depuis une position initiale aléatoire symétrique (ou non).",
   "vt0": "Jeux simplifiés pour apprendre les Ã©checs",
   "vg0": "Variantes avec très peu de pièces différentes, et un but simplifié.",
   "vt1": "Captures obligatoires",
   "vt0": "Jeux simplifiés pour apprendre les Ã©checs",
   "vg0": "Variantes avec très peu de pièces différentes, et un but simplifié.",
   "vt1": "Captures obligatoires",
@@ -408,4 +404,9 @@ export const translations = {
   "vg30": "Les pièces peuvent temporairement emprunter des pouvoir aux autres",
   "vt31": "Divers",
   "vg31": "Ces variantes ne sont pas encore classées, en général car elles sont l'unique représentant de leur type sur ce site.",
   "vg30": "Les pièces peuvent temporairement emprunter des pouvoir aux autres",
   "vt31": "Divers",
   "vg31": "Ces variantes ne sont pas encore classées, en général car elles sont l'unique représentant de leur type sur ce site.",
+
+  // Variants' options
+  "Options": "Options",
+  "Randomness": "Degré d'aléa",
+  "With switch": "Avec switch",
 };
 };
similarity index 95%
rename from client/src/translations/rules/Allmate1/en.pug
rename to client/src/translations/rules/Allmate/en.pug
index fabc8af..adf8d0d 100644 (file)
@@ -36,7 +36,7 @@ figure.diagram-container
 p.
   Note about moves notation: since captures do not occur on final squares,
   an "X" mark is appended to the move to indicate a capture.
 p.
   Note about moves notation: since captures do not occur on final squares,
   an "X" mark is appended to the move to indicate a capture.
-  To keep notation short the potential list of captured squares is not written.
+  To keep notation short the list of captured squares is not written.
 
 h3 "Suicide" note
 
 
 h3 "Suicide" note
 
similarity index 99%
rename from client/src/translations/rules/Allmate1/es.pug
rename to client/src/translations/rules/Allmate/es.pug
index d6ba11b..4365f4f 100644 (file)
@@ -37,7 +37,7 @@ figure.diagram-container
 p.
   Nota sobre la notación de las jugadas: ya que las capturas no se realizan
   en la casilla de llegada, se agrega una marca "X" al movimiento para indicar
 p.
   Nota sobre la notación de las jugadas: ya que las capturas no se realizan
   en la casilla de llegada, se agrega una marca "X" al movimiento para indicar
-  la captura. Para evitar las notaciones demasiado largas, la lista potencial
+  la captura. Para evitar las notaciones demasiado largas, la lista
   de casillas de captura no está escrita.
 
 h3 Nota "suicidio"
   de casillas de captura no está escrita.
 
 h3 Nota "suicidio"
similarity index 96%
rename from client/src/translations/rules/Allmate1/fr.pug
rename to client/src/translations/rules/Allmate/fr.pug
index d396233..8b6619f 100644 (file)
@@ -38,7 +38,7 @@ figure.diagram-container
 p.
   Note sur la notation des coups: puisque les captures ne s'effectuent pas
   sur la case d'arrivée, une marque "X" est ajoutée au coup pour indiquer la
 p.
   Note sur la notation des coups: puisque les captures ne s'effectuent pas
   sur la case d'arrivée, une marque "X" est ajoutée au coup pour indiquer la
-  capture. Afin d'éviter les notations Ã  rallonge la liste potentielle des
+  capture. Afin d'éviter les notations Ã  rallonge la liste des
   cases de capture n'est pas Ã©crite.
 
 h3 Note "suicide"
   cases de capture n'est pas Ã©crite.
 
 h3 Note "suicide"
diff --git a/client/src/translations/rules/Allmate2/en.pug b/client/src/translations/rules/Allmate2/en.pug
deleted file mode 100644 (file)
index 70fdafe..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-p.boxed
-  | An attacked piece is taken if the capture cannot
-  | be prevented with non-capturing moves.
-
-p.
-  This is the Allmate1 variant, with a weaker mating condition:
-  capturing moves to escape from checkmate are not considered.
-  (Mate-)Capturing is thus easier: on the next diagram, 1.Qe6 captures
-  the d7, e7 and f7 pawns.
-
-figure.diagram-container
-  .diagram.diag12
-    | fen:4k3/pppppppp/8/8/4Q3/8/PPP2PPP/4K3:
-  .diagram.diag22
-    | fen:4k3/ppp3pp/4Q3/8/8/8/PPP2PPP/4K3:
-  figcaption Before and after 1.Qe6X
-
-p Since captures are much easier, they don't cascade as in Allmate1.
-
-h3 Source
-
-p See Allmate1 variant.
diff --git a/client/src/translations/rules/Allmate2/es.pug b/client/src/translations/rules/Allmate2/es.pug
deleted file mode 100644 (file)
index df2defd..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-p.boxed
-  | Se captura una pieza atacada si no se puede evitar
-  | la toma por movimientos sin captura.
-
-p.
-  Esta es la variante Allmate1, con una condición de mate más
-  débil: no se consideran las capturas para escapar de un jaque mate.
-  Por lo tanto, las (mate-)capturas son más fáciles: en el siguiente diagrama,
-  1.Qe6 toma los peones d7, e7 y f7.
-
-figure.diagram-container
-  .diagram.diag12
-    | fen:4K3/pppppppp/8/8/4Q3/8/PPP2PPP/4K3:
-  .diagram.diag22
-    | fen:4K3/ppp3pp/4Q3/8/8/8/PPP2PPP/4K3:
-  figcaption Antes y después 1.Qe6X
-
-p.
-  Como las capturas son mucho más fáciles, no tienen lugar
-  en cascada como en Allmate1.
-
-h3 Fuente
-
-p Ver la variante Allmate1.
diff --git a/client/src/translations/rules/Allmate2/fr.pug b/client/src/translations/rules/Allmate2/fr.pug
deleted file mode 100644 (file)
index 1ea1a05..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-p.boxed
-  | Une pièce attaquée est capturée si la prise ne peut Ãªtre
-  | empêchée par des coups non capturants.
-
-p.
-  C'est la variante Allmate1, avec une condition de mat plus
-  faible : les coups capturants pour Ã©chapper Ã  un mat ne sont pas considérés.
-  Les (mat-)captures sont donc plus faciles : sur le diagramme suivant,
-  1.Qe6 prend les pions d7, e7 et f7.
-
-figure.diagram-container
-  .diagram.diag12
-    | fen:4k3/pppppppp/8/8/4Q3/8/PPP2PPP/4K3:
-  .diagram.diag22
-    | fen:4k3/ppp3pp/4Q3/8/8/8/PPP2PPP/4K3:
-  figcaption Avant et après 1.Qe6X
-
-p.
-  Puisque les captures sont beaucoup plus faciles, elles ne s'effectuent pas
-  en cascade comme dans Allmate1.
-
-h3 Source
-
-p Voir la variante Allmate1.
@@ -77,6 +77,9 @@ p.
 
 h2.stageDelimiter Stage 2
 
 
 h2.stageDelimiter Stage 2
 
+p.italic.
+  This stage can be disabled by unselecting "With switch" at game creation.
+
 p.
   During the game one of the two players can decide to take control of the
   checkered pieces.
 p.
   During the game one of the two players can decide to take control of the
   checkered pieces.
@@ -75,6 +75,10 @@ p.
 
 h2.stageDelimiter Fase 2
 
 
 h2.stageDelimiter Fase 2
 
+p.italic.
+  Si anula la selección de "Con switch" al crear la partida, entonces
+  el juego permanecerá en la fase 1.
+
 p.
   Durante el juego, uno de los dos jugadores puede decidir tomar
   las piezas a cuadros. Estos luego se vuelven autónomos
 p.
   Durante el juego, uno de los dos jugadores puede decidir tomar
   las piezas a cuadros. Estos luego se vuelven autónomos
@@ -79,6 +79,10 @@ p.
 
 h2.stageDelimiter Phase 2
 
 
 h2.stageDelimiter Phase 2
 
+p.italic.
+  Si vous désélectionnez "Avec switch" lors de la création de la partie,
+  alors le jeu restera en phase 1.
+
 p.
   Au cours de la partie l'un des deux joueurs peut décider de prendre le
   contrôle des pièces Ã©chiquetées. Celles-ci deviennent alors autonomes
 p.
   Au cours de la partie l'un des deux joueurs peut décider de prendre le
   contrôle des pièces Ã©chiquetées. Celles-ci deviennent alors autonomes
diff --git a/client/src/translations/rules/Checkered2/en.pug b/client/src/translations/rules/Checkered2/en.pug
deleted file mode 100644 (file)
index 0d0af19..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-p.boxed
-  | The capture of an enemy piece produces a new "checkered" piece belonging
-  | to both players.
-
-p
-  | This is the 
-  a(href="/#/variants/Checkered1") Checkered1 variant
-  | , without the stage 2.
-  | Probably more drawish, but also quite different.
-  | I think both are interesting.
diff --git a/client/src/translations/rules/Checkered2/es.pug b/client/src/translations/rules/Checkered2/es.pug
deleted file mode 100644 (file)
index a38eaf2..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-p.boxed
-  | La captura de una pieza enemiga da lugar al nacimiento de una pieza
-  | "a cuadros", que pertenece a ambos campos.
-
-p
-  | Esta la 
-  a(href="/#/variants/Checkered1") variante Checkered1
-  | , sin fase 2.
-  | Probablemente más canceladoras, pero también bastante diferente.
-  | Creo que ambos tienen interés.
diff --git a/client/src/translations/rules/Checkered2/fr.pug b/client/src/translations/rules/Checkered2/fr.pug
deleted file mode 100644 (file)
index 4b0b92a..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-p.boxed
-  | La capture d'une pièce ennemie donne lieu Ã  la naissance d'une pièce
-  | "échiquetée", qui appartient aux deux camps.
-
-p
-  | C'est la 
-  a(href="/#/variants/Checkered1") variante Checkered1
-  | , sans la phase 2.
-  | Sans doute plus annulant, mais aussi assez différente.
-  | Je pense que les deux ont un intérêt.
similarity index 99%
rename from client/src/translations/rules/Chess/en.pug
rename to client/src/translations/rules/Chess960/en.pug
index e7645ef..f978917 100644 (file)
@@ -1,5 +1,5 @@
 p.boxed
 p.boxed
-  | Orthodox rules (with potentially shuffled starting position).
+  | Orthodox rules with shuffled starting position.
 
 p.
   Chess is played between two players, one moving the white pieces and the
 
 p.
   Chess is played between two players, one moving the white pieces and the
similarity index 99%
rename from client/src/translations/rules/Chess/es.pug
rename to client/src/translations/rules/Chess960/es.pug
index b60b7b3..205285b 100644 (file)
@@ -1,5 +1,5 @@
 p.boxed
 p.boxed
-  | Juego ortodoxo (con una posición inicial potencialmente aleatoria).
+  | Juego ortodoxo con una posición inicial aleatoria.
 
 p.
   El ajedrez es un juego entre dos jugadores, uno que mueve las piezas blancas
 
 p.
   El ajedrez es un juego entre dos jugadores, uno que mueve las piezas blancas
similarity index 99%
rename from client/src/translations/rules/Chess/fr.pug
rename to client/src/translations/rules/Chess960/fr.pug
index 9a54a91..fdbd2cf 100644 (file)
@@ -1,5 +1,5 @@
 p.boxed
 p.boxed
-  | Jeu orthodoxe (avec une position de départ potentiellement aléatoire).
+  | Jeu orthodoxe avec une position de départ aléatoire.
 
 p.
   Les Ã©checs sont un jeu entre deux joueurs, l'un déplaçant les pièces blanches
 
 p.
   Les Ã©checs sont un jeu entre deux joueurs, l'un déplaçant les pièces blanches
similarity index 99%
rename from client/src/variants/Allmate1.js
rename to client/src/variants/Allmate.js
index 88599e4..86409c2 100644 (file)
@@ -1,6 +1,6 @@
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
 import { ChessRules, PiPo, Move } from "@/base_rules";
 
-export class Allmate1Rules extends ChessRules {
+export class AllmateRules extends ChessRules {
 
   static get HasEnpassant() {
     return false;
 
   static get HasEnpassant() {
     return false;
diff --git a/client/src/variants/Allmate2.js b/client/src/variants/Allmate2.js
deleted file mode 100644 (file)
index f4abbe3..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-import { ChessRules, PiPo, Move } from "@/base_rules";
-
-export class Allmate2Rules extends ChessRules {
-
-  static get HasEnpassant() {
-    return false;
-  }
-
-  getCheckSquares() {
-    // No notion of check
-    return [];
-  }
-
-  static GenRandInitFen(randomness) {
-    return ChessRules.GenRandInitFen(randomness).slice(0, -2);
-  }
-
-  getPotentialMovesFrom([x, y]) {
-    let moves = super.getPotentialMovesFrom([x, y]);
-    // Remove standard captures (without removing castling):
-    moves = moves.filter(m => {
-      return m.vanish.length == 1 || m.appear.length == 2;
-    });
-
-    // Augment moves with "mate-captures":
-    // TODO: this is coded in a highly inefficient way...
-    const color = this.turn;
-    const oppCol = V.GetOppCol(this.turn);
-    moves.forEach(m => {
-      this.play(m);
-
-      // 1) What is attacked?
-      let attacked = {};
-      for (let i=0; i<V.size.x; i++) {
-        for (let j=0; j<V.size.y; j++) {
-          if (this.getColor(i,j) == oppCol && this.isAttacked([i,j], color))
-            attacked[i+"_"+j] = [i,j];
-        }
-      }
-
-      // 2) Among attacked pieces, which cannot escape capture?
-      // --> without (normal-)capturing: difference with Allmate1 variant
-      // Avoid "oppMoves = this.getAllValidMoves();" => infinite recursion
-      outerLoop: for (let i=0; i<V.size.x; i++) {
-        for (let j=0; j<V.size.y; j++) {
-          if (this.getColor(i,j) == oppCol) {
-            let oppMoves = [];
-            switch (this.getPiece(i, j)) {
-              case V.PAWN:
-                oppMoves = this.getPotentialPawnMoves([i, j]);
-                break;
-              case V.ROOK:
-                oppMoves = this.getPotentialRookMoves([i, j]);
-                break;
-              case V.KNIGHT:
-                oppMoves = this.getPotentialKnightMoves([i, j]);
-                break;
-              case V.BISHOP:
-                oppMoves = this.getPotentialBishopMoves([i, j]);
-                break;
-              case V.QUEEN:
-                oppMoves = this.getPotentialQueenMoves([i, j]);
-                break;
-              case V.KING:
-                // Do not allow castling to escape from check
-                oppMoves = super.getSlideNJumpMoves(
-                  [i, j],
-                  V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-                  "oneStep"
-                );
-                break;
-            }
-            for (let om of oppMoves) {
-              if (om.vanish.length == 2)
-                // Skip captures: forbidden in this mode
-                continue;
-              V.PlayOnBoard(this.board, om);
-              Object.values(attacked).forEach(sq => {
-                const origSq = [sq[0], sq[1]];
-                if (om.start.x == sq[0] && om.start.y == sq[1])
-                  // Piece moved:
-                  sq = [om.appear[0].x, om.appear[0].y];
-                if (!this.isAttacked(sq, color))
-                  delete attacked[origSq[0]+"_"+origSq[1]];
-              });
-              V.UndoOnBoard(this.board, om);
-              if (Object.keys(attacked).length == 0)
-                // No need to explore more moves
-                break outerLoop;
-            }
-          }
-        }
-      }
-      this.undo(m);
-
-      // 3) Add mate-captures:
-      Object.values(attacked).forEach(sq => {
-        m.vanish.push(new PiPo({
-          x: sq[0],
-          y: sq[1],
-          c: oppCol,
-          p: this.getPiece(sq[0], sq[1])
-        }));
-      });
-    });
-
-    return moves;
-  }
-
-  // No "under check" conditions in castling
-  getCastleMoves(sq) {
-    return super.getCastleMoves(sq, null, "castleInCheck");
-  }
-
-  // TODO: allow pieces to "commit suicide"? (Currently yes except king)
-  filterValid(moves) {
-    // Remove moves which let the king mate-captured:
-    if (moves.length == 0) return [];
-    const color = this.turn;
-    const oppCol = V.GetOppCol(color);
-    return moves.filter(m => {
-      let res = true;
-      this.play(m);
-      if (this.underCheck(color)) {
-        res = false;
-        const attacked = this.kingPos[color];
-        // Try to find a move to escape check
-        // TODO: very inefficient method.
-        outerLoop: for (let i=0; i<V.size.x; i++) {
-          for (let j=0; j<V.size.y; j++) {
-            if (this.getColor(i,j) == color) {
-              let emoves = [];
-              // Artficial turn change to "play twice":
-              this.turn = color;
-              switch (this.getPiece(i, j)) {
-                case V.PAWN:
-                  emoves = this.getPotentialPawnMoves([i, j]);
-                  break;
-                case V.ROOK:
-                  emoves = this.getPotentialRookMoves([i, j]);
-                  break;
-                case V.KNIGHT:
-                  emoves = this.getPotentialKnightMoves([i, j]);
-                  break;
-                case V.BISHOP:
-                  emoves = this.getPotentialBishopMoves([i, j]);
-                  break;
-                case V.QUEEN:
-                  emoves = this.getPotentialQueenMoves([i, j]);
-                  break;
-                case V.KING:
-                  emoves = this.getPotentialKingMoves([i, j]);
-                  break;
-              }
-              this.turn = oppCol;
-              for (let em of emoves) {
-                V.PlayOnBoard(this.board, em);
-                let sq = attacked;
-                if (em.start.x == attacked[0] && em.start.y == attacked[1])
-                  // King moved:
-                  sq = [em.appear[0].x, em.appear[0].y];
-                if (!this.isAttacked(sq, oppCol))
-                  res = true;
-                V.UndoOnBoard(this.board, em);
-                if (res)
-                  // No need to explore more moves
-                  break outerLoop;
-              }
-            }
-          }
-        }
-      }
-      this.undo(m);
-      return res;
-    });
-  }
-
-  postPlay(move) {
-    super.postPlay(move);
-    if (move.vanish.length >= 2 && move.appear.length == 1) {
-      for (let i = 1; i<move.vanish.length; i++) {
-        const v = move.vanish[i];
-        // Did opponent king disappeared?
-        if (v.p == V.KING)
-          this.kingPos[this.turn] = [-1, -1];
-        // Or maybe a rook?
-        else if (v.p == V.ROOK) {
-          if (v.y < this.kingPos[v.c][1])
-            this.castleFlags[v.c][0] = 8;
-          else
-            // v.y > this.kingPos[v.c][1]
-            this.castleFlags[v.c][1] = 8;
-        }
-      }
-    }
-  }
-
-  preUndo(move) {
-    super.preUndo(move);
-    const oppCol = this.turn;
-    if (move.vanish.length >= 2 && move.appear.length == 1) {
-      // Did opponent king disappeared?
-      const psq = move.vanish.find(v => v.p == V.KING && v.c == oppCol)
-      if (psq)
-        this.kingPos[psq.c] = [psq.x, psq.y];
-    }
-  }
-
-  getCurrentScore() {
-    const color = this.turn;
-    const kp = this.kingPos[color];
-    if (kp[0] < 0)
-      // King disappeared
-      return color == "w" ? "0-1" : "1-0";
-    if (this.atLeastOneMove()) return "*";
-    // Kings still there, no moves:
-    return "1/2";
-  }
-
-  static get SEARCH_DEPTH() {
-    return 1;
-  }
-
-  getNotation(move) {
-    let notation = super.getNotation(move);
-    // Add a capture mark (not describing what is captured...):
-    if (move.vanish.length > 1 && move.appear.length == 1) {
-      if (!!(notation.match(/^[a-h]x/)))
-        // Pawn capture: remove initial "b" in bxc4 for example
-        notation = notation.substr(1);
-      notation = notation.replace("x","") + "X";
-    }
-    return notation;
-  }
-
-};
similarity index 95%
rename from client/src/variants/Checkered1.js
rename to client/src/variants/Checkered.js
index 2451f9b..5036a82 100644 (file)
@@ -1,6 +1,6 @@
 import { ChessRules, Move, PiPo } from "@/base_rules";
 
 import { ChessRules, Move, PiPo } from "@/base_rules";
 
-export class Checkered1Rules extends ChessRules {
+export class CheckeredRules extends ChessRules {
 
   static board2fen(b) {
     const checkered_codes = {
 
   static board2fen(b) {
     const checkered_codes = {
@@ -41,6 +41,26 @@ export class Checkered1Rules extends ChessRules {
     return (b[0] == "c" ? "Checkered/" : "") + b;
   }
 
     return (b[0] == "c" ? "Checkered/" : "") + b;
   }
 
+  static get Options() {
+    return Object.assign(
+      {},
+      ChessRules.Options,
+      {
+        check: [
+          {
+            label: "With switch",
+            defaut: true,
+            variable: "switch"
+          }
+        ]
+      }
+    );
+  }
+
+  static AbbreviateOptions(opts) {
+    return (!opts["switch"] ? "NS" : "");
+  }
+
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     // Local stack of non-capturing checkered moves:
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     // Local stack of non-capturing checkered moves:
@@ -56,6 +76,7 @@ export class Checkered1Rules extends ChessRules {
     // Stage 1: as Checkered2. Stage 2: checkered pieces are autonomous
     const stageInfo = V.ParseFen(fen).stage;
     this.stage = parseInt(stageInfo[0], 10);
     // Stage 1: as Checkered2. Stage 2: checkered pieces are autonomous
     const stageInfo = V.ParseFen(fen).stage;
     this.stage = parseInt(stageInfo[0], 10);
+    this.canSwitch = (this.stage == 1 && stageInfo[1] != '-');
     this.sideCheckered = (this.stage == 2 ? stageInfo[1] : undefined);
   }
 
     this.sideCheckered = (this.stage == 2 ? stageInfo[1] : undefined);
   }
 
@@ -65,7 +86,7 @@ export class Checkered1Rules extends ChessRules {
     if (fenParts.length != 7) return false;
     if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/))
       return false;
     if (fenParts.length != 7) return false;
     if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/))
       return false;
-    if (!fenParts[6].match(/^[12][wb]?$/)) return false;
+    if (!fenParts[6].match(/^[12][wb-]?$/)) return false;
     return true;
   }
 
     return true;
   }
 
@@ -143,7 +164,7 @@ export class Checkered1Rules extends ChessRules {
       if (this.getPiece(x, y) == V.KING) {
         // If at least one checkered piece, allow switching:
         if (
       if (this.getPiece(x, y) == V.KING) {
         // If at least one checkered piece, allow switching:
         if (
-          !noswitch &&
+          this.canSwitch && !noswitch &&
           this.board.some(b => b.some(cell => cell[0] == 'c'))
         ) {
           const oppCol = V.GetOppCol(color);
           this.board.some(b => b.some(cell => cell[0] == 'c'))
         ) {
           const oppCol = V.GetOppCol(color);
@@ -592,10 +613,13 @@ export class Checkered1Rules extends ChessRules {
     return evaluation;
   }
 
     return evaluation;
   }
 
-  static GenRandInitFen(randomness) {
-    // Add 16 pawns flags + empty cmove + stage == 1:
-    return ChessRules.GenRandInitFen(randomness)
-      .slice(0, -2) + "1111111111111111 - - 1";
+  static GenRandInitFen(options) {
+    const baseFen = ChessRules.GenRandInitFen(options.randomness);
+    return (
+      // Add 16 pawns flags + empty cmove + stage == 1:
+      baseFen.slice(0, -2) + "1111111111111111 - - 1" +
+      (!options["switch"] ? '-' : "")
+    );
   }
 
   static ParseFen(fen) {
   }
 
   static ParseFen(fen) {
@@ -620,7 +644,9 @@ export class Checkered1Rules extends ChessRules {
   }
 
   getStageFen() {
   }
 
   getStageFen() {
-    return (this.stage == 1 ? "1" : "2" + this.sideCheckered);
+    if (this.stage == 1) return "1" + (!this.canSwitch ? '-' : "");
+    // Stage == 2:
+    return "2" + this.sideCheckered;
   }
 
   getFen() {
   }
 
   getFen() {
diff --git a/client/src/variants/Checkered2.js b/client/src/variants/Checkered2.js
deleted file mode 100644 (file)
index 997c1c7..0000000
+++ /dev/null
@@ -1,472 +0,0 @@
-import { ChessRules, Move, PiPo } from "@/base_rules";
-
-export class Checkered2Rules extends ChessRules {
-
-  static board2fen(b) {
-    const checkered_codes = {
-      p: "s",
-      q: "t",
-      r: "u",
-      b: "c",
-      n: "o"
-    };
-    if (b[0] == "c") return checkered_codes[b[1]];
-    return ChessRules.board2fen(b);
-  }
-
-  static fen2board(f) {
-    // Tolerate upper-case versions of checkered pieces (why not?)
-    const checkered_pieces = {
-      s: "p",
-      S: "p",
-      t: "q",
-      T: "q",
-      u: "r",
-      U: "r",
-      c: "b",
-      C: "b",
-      o: "n",
-      O: "n"
-    };
-    if (Object.keys(checkered_pieces).includes(f))
-      return "c" + checkered_pieces[f];
-    return ChessRules.fen2board(f);
-  }
-
-  static get PIECES() {
-    return ChessRules.PIECES.concat(["s", "t", "u", "c", "o"]);
-  }
-
-  getPpath(b) {
-    return (b[0] == "c" ? "Checkered/" : "") + b;
-  }
-
-  setOtherVariables(fen) {
-    super.setOtherVariables(fen);
-    // Local stack of non-capturing checkered moves:
-    this.cmoves = [];
-    const cmove = V.ParseFen(fen).cmove;
-    if (cmove == "-") this.cmoves.push(null);
-    else {
-      this.cmoves.push({
-        start: ChessRules.SquareToCoords(cmove.substr(0, 2)),
-        end: ChessRules.SquareToCoords(cmove.substr(2))
-      });
-    }
-  }
-
-  static IsGoodFen(fen) {
-    if (!ChessRules.IsGoodFen(fen)) return false;
-    const fenParts = fen.split(" ");
-    if (fenParts.length != 6) return false;
-    if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/))
-      return false;
-    return true;
-  }
-
-  static IsGoodFlags(flags) {
-    // 4 for castle + 16 for pawns
-    return !!flags.match(/^[a-z]{4,4}[01]{16,16}$/);
-  }
-
-  setFlags(fenflags) {
-    super.setFlags(fenflags); //castleFlags
-    this.pawnFlags = {
-      w: [...Array(8)], //pawns can move 2 squares?
-      b: [...Array(8)]
-    };
-    const flags = fenflags.substr(4); //skip first 4 letters, for castle
-    for (let c of ["w", "b"]) {
-      for (let i = 0; i < 8; i++)
-        this.pawnFlags[c][i] = flags.charAt((c == "w" ? 0 : 8) + i) == "1";
-    }
-  }
-
-  aggregateFlags() {
-    return [this.castleFlags, this.pawnFlags];
-  }
-
-  disaggregateFlags(flags) {
-    this.castleFlags = flags[0];
-    this.pawnFlags = flags[1];
-  }
-
-  getEpSquare(moveOrSquare) {
-    if (typeof moveOrSquare !== "object" || moveOrSquare.appear[0].c != 'c')
-      return super.getEpSquare(moveOrSquare);
-    // Checkered move: no en-passant
-    return undefined;
-  }
-
-  getCmove(move) {
-    if (move.appear[0].c == "c" && move.vanish.length == 1)
-      return { start: move.start, end: move.end };
-    return null;
-  }
-
-  canTake([x1, y1], [x2, y2]) {
-    const color1 = this.getColor(x1, y1);
-    const color2 = this.getColor(x2, y2);
-    // Checkered aren't captured
-    return (
-      color1 != color2 &&
-      color2 != "c" &&
-      (color1 != "c" || color2 != this.turn)
-    );
-  }
-
-  // Post-processing: apply "checkerization" of standard moves
-  getPotentialMovesFrom([x, y]) {
-    let standardMoves = super.getPotentialMovesFrom([x, y]);
-    const lastRank = this.turn == "w" ? 0 : 7;
-    // King is treated differently: it never turn checkered
-    if (this.getPiece(x, y) == V.KING) return standardMoves;
-    let moves = [];
-    standardMoves.forEach(m => {
-      if (m.vanish[0].p == V.PAWN) {
-        if (
-          Math.abs(m.end.x - m.start.x) == 2 &&
-          !this.pawnFlags[this.turn][m.start.y]
-        ) {
-          return; //skip forbidden 2-squares jumps
-        }
-        if (
-          this.board[m.end.x][m.end.y] == V.EMPTY &&
-          m.vanish.length == 2 &&
-          this.getColor(m.start.x, m.start.y) == "c"
-        ) {
-          return; //checkered pawns cannot take en-passant
-        }
-      }
-      if (m.vanish.length == 1)
-        // No capture
-        moves.push(m);
-      else {
-        // A capture occured (m.vanish.length == 2)
-        m.appear[0].c = "c";
-        moves.push(m);
-        if (
-          // Avoid promotions (already treated):
-          m.appear[0].p != m.vanish[1].p &&
-          (m.vanish[0].p != V.PAWN || m.end.x != lastRank)
-        ) {
-          // Add transformation into captured piece
-          let m2 = JSON.parse(JSON.stringify(m));
-          m2.appear[0].p = m.vanish[1].p;
-          moves.push(m2);
-        }
-      }
-    });
-    return moves;
-  }
-
-  getPotentialPawnMoves([x, y]) {
-    let moves = super.getPotentialPawnMoves([x, y]);
-    // Post-process: set right color for checkered moves
-    if (this.getColor(x, y) == 'c') {
-      moves.forEach(m => {
-        m.appear[0].c = 'c'; //may be done twice if capture
-        m.vanish[0].c = 'c';
-      });
-    }
-    return moves;
-  }
-
-  canIplay(side, [x, y]) {
-    return side == this.turn && [side, "c"].includes(this.getColor(x, y));
-  }
-
-  // Does m2 un-do m1 ? (to disallow undoing checkered moves)
-  oppositeMoves(m1, m2) {
-    return (
-      !!m1 &&
-      m2.appear[0].c == "c" &&
-      m2.appear.length == 1 &&
-      m2.vanish.length == 1 &&
-      m1.start.x == m2.end.x &&
-      m1.end.x == m2.start.x &&
-      m1.start.y == m2.end.y &&
-      m1.end.y == m2.start.y
-    );
-  }
-
-  filterValid(moves) {
-    if (moves.length == 0) return [];
-    const color = this.turn;
-    const L = this.cmoves.length; //at least 1: init from FEN
-    return moves.filter(m => {
-      if (this.oppositeMoves(this.cmoves[L - 1], m)) return false;
-      this.play(m);
-      const res = !this.underCheck(color);
-      this.undo(m);
-      return res;
-    });
-  }
-
-  getAllValidMoves() {
-    const oppCol = V.GetOppCol(this.turn);
-    let potentialMoves = [];
-    for (let i = 0; i < V.size.x; i++) {
-      for (let j = 0; j < V.size.y; j++) {
-        // NOTE: just testing == color isn't enough because of checkered pieces
-        if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) {
-          Array.prototype.push.apply(
-            potentialMoves,
-            this.getPotentialMovesFrom([i, j])
-          );
-        }
-      }
-    }
-    return this.filterValid(potentialMoves);
-  }
-
-  atLeastOneMove() {
-    const oppCol = V.GetOppCol(this.turn);
-    for (let i = 0; i < V.size.x; i++) {
-      for (let j = 0; j < V.size.y; j++) {
-        // NOTE: just testing == color isn't enough because of checkered pieces
-        if (this.board[i][j] != V.EMPTY && this.getColor(i, j) != oppCol) {
-          const moves = this.getPotentialMovesFrom([i, j]);
-          if (moves.length > 0) {
-            for (let k = 0; k < moves.length; k++) {
-              if (this.filterValid([moves[k]]).length > 0) return true;
-            }
-          }
-        }
-      }
-    }
-    return false;
-  }
-
-  // colors: array, generally 'w' and 'c' or 'b' and 'c'
-  isAttacked(sq, colors) {
-    if (!Array.isArray(colors)) colors = [colors];
-    return (
-      this.isAttackedByPawn(sq, colors) ||
-      this.isAttackedByRook(sq, colors) ||
-      this.isAttackedByKnight(sq, colors) ||
-      this.isAttackedByBishop(sq, colors) ||
-      this.isAttackedByQueen(sq, colors) ||
-      this.isAttackedByKing(sq, colors)
-    );
-  }
-
-  isAttackedByPawn([x, y], colors) {
-    for (let c of colors) {
-      const color = (c == "c" ? this.turn : c);
-      let pawnShift = color == "w" ? 1 : -1;
-      if (x + pawnShift >= 0 && x + pawnShift < 8) {
-        for (let i of [-1, 1]) {
-          if (
-            y + i >= 0 &&
-            y + i < 8 &&
-            this.getPiece(x + pawnShift, y + i) == V.PAWN &&
-            this.getColor(x + pawnShift, y + i) == c
-          ) {
-            return true;
-          }
-        }
-      }
-    }
-    return false;
-  }
-
-  isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) {
-    for (let step of steps) {
-      let rx = x + step[0],
-          ry = y + step[1];
-      while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
-        rx += step[0];
-        ry += step[1];
-      }
-      if (
-        V.OnBoard(rx, ry) &&
-        this.getPiece(rx, ry) === piece &&
-        colors.includes(this.getColor(rx, ry))
-      ) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  isAttackedByRook(sq, colors) {
-    return this.isAttackedBySlideNJump(sq, colors, V.ROOK, V.steps[V.ROOK]);
-  }
-
-  isAttackedByKnight(sq, colors) {
-    return this.isAttackedBySlideNJump(
-      sq,
-      colors,
-      V.KNIGHT,
-      V.steps[V.KNIGHT],
-      "oneStep"
-    );
-  }
-
-  isAttackedByBishop(sq, colors) {
-    return this.isAttackedBySlideNJump(
-      sq, colors, V.BISHOP, V.steps[V.BISHOP]);
-  }
-
-  isAttackedByQueen(sq, colors) {
-    return this.isAttackedBySlideNJump(
-      sq,
-      colors,
-      V.QUEEN,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP])
-    );
-  }
-
-  isAttackedByKing(sq, colors) {
-    return this.isAttackedBySlideNJump(
-      sq,
-      colors,
-      V.KING,
-      V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
-      "oneStep"
-    );
-  }
-
-  underCheck(color) {
-    return this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"]);
-  }
-
-  getCheckSquares() {
-    const color = this.turn;
-    // Artifically change turn, for checkered pawns
-    this.turn = V.GetOppCol(color);
-    const kingAttacked =
-      this.isAttacked(
-        this.kingPos[color],
-        [this.turn, 'c']
-      );
-    let res = kingAttacked
-      ? [JSON.parse(JSON.stringify(this.kingPos[color]))]
-      : [];
-    this.turn = color;
-    return res;
-  }
-
-  postPlay(move) {
-    super.postPlay(move);
-    // Does this move turn off a 2-squares pawn flag?
-    if (
-      [1, 6].includes(move.start.x) &&
-      move.vanish[0].p == V.PAWN &&
-      Math.abs(move.end.x - move.start.x) == 2
-    ) {
-      this.pawnFlags[move.start.x == 6 ? "w" : "b"][move.start.y] = false;
-    }
-    this.cmoves.push(this.getCmove(move));
-  }
-
-  postUndo(move) {
-    super.postUndo(move);
-    this.cmoves.pop();
-  }
-
-  getCurrentScore() {
-    if (this.atLeastOneMove()) return "*";
-    const color = this.turn;
-    // Artifically change turn, for checkered pawns
-    this.turn = V.GetOppCol(this.turn);
-    const res = this.isAttacked(this.kingPos[color], [V.GetOppCol(color), "c"])
-      ? color == "w"
-        ? "0-1"
-        : "1-0"
-      : "1/2";
-    this.turn = V.GetOppCol(this.turn);
-    return res;
-  }
-
-  evalPosition() {
-    let evaluation = 0;
-    // Just count material for now, considering checkered neutral (...)
-    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 sqColor = this.getColor(i, j);
-          if (["w","b"].includes(sqColor)) {
-            const sign = sqColor == "w" ? 1 : -1;
-            evaluation += sign * V.VALUES[this.getPiece(i, j)];
-          }
-        }
-      }
-    }
-    return evaluation;
-  }
-
-  static GenRandInitFen(randomness) {
-    // Add 16 pawns flags + empty cmove:
-    return ChessRules.GenRandInitFen(randomness)
-      .slice(0, -2) + "1111111111111111 - -";
-  }
-
-  static ParseFen(fen) {
-    return Object.assign(
-      ChessRules.ParseFen(fen),
-      { cmove: fen.split(" ")[5] }
-    );
-  }
-
-  getCmoveFen() {
-    const L = this.cmoves.length;
-    return (
-      !this.cmoves[L - 1]
-        ? "-"
-        : ChessRules.CoordsToSquare(this.cmoves[L - 1].start) +
-          ChessRules.CoordsToSquare(this.cmoves[L - 1].end)
-    );
-  }
-
-  getFen() {
-    return super.getFen() + " " + this.getCmoveFen();
-  }
-
-  getFenForRepeat() {
-    return super.getFenForRepeat() + "_" + this.getCmoveFen();
-  }
-
-  getFlagsFen() {
-    let fen = super.getFlagsFen();
-    // Add pawns flags
-    for (let c of ["w", "b"])
-      for (let i = 0; i < 8; i++) fen += (this.pawnFlags[c][i] ? "1" : "0");
-    return fen;
-  }
-
-  static get SEARCH_DEPTH() {
-    return 2;
-  }
-
-  getNotation(move) {
-    if (move.appear.length == 2) {
-      // Castle
-      if (move.end.y < move.start.y) return "0-0-0";
-      return "0-0";
-    }
-
-    const finalSquare = V.CoordsToSquare(move.end);
-    const piece = this.getPiece(move.start.x, move.start.y);
-    let notation = "";
-    if (piece == V.PAWN) {
-      // Pawn move
-      if (move.vanish.length > 1) {
-        // Capture
-        const startColumn = V.CoordToColumn(move.start.y);
-        notation = startColumn + "x" + finalSquare;
-      } else notation = finalSquare;
-    } else {
-      // Piece movement
-      notation =
-        piece.toUpperCase() +
-        (move.vanish.length > 1 ? "x" : "") +
-        finalSquare;
-    }
-    if (move.appear[0].p != move.vanish[0].p)
-      notation += "=" + move.appear[0].p.toUpperCase();
-    return notation;
-  }
-
-};
diff --git a/client/src/variants/Chess.js b/client/src/variants/Chess.js
deleted file mode 120000 (symlink)
index 8e6b27c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../base_rules.js
\ No newline at end of file
diff --git a/client/src/variants/Chess960.js b/client/src/variants/Chess960.js
new file mode 100644 (file)
index 0000000..968ec1c
--- /dev/null
@@ -0,0 +1,23 @@
+import { ChessRules } from "@/base_rules";
+
+export class Chess960Rules extends ChessRules {
+
+  // Do not allow standard chess:
+  static get Options() {
+    return {
+      select: [
+        {
+          label: "Randomness",
+          variable: "randomness",
+          defaut: 2,
+          options: [
+            { label: "Symmetric random", value: 1 },
+            { label: "Asymmetric random", value: 2 }
+          ]
+        }
+      ],
+      check: []
+    };
+  }
+
+};
index 06ef790..5c525a3 100644 (file)
@@ -44,7 +44,8 @@ export default {
       rulesContent: "",
       gameRef: {
         vname: "",
       rulesContent: "",
       gameRef: {
         vname: "",
-        fen: ""
+        fen: "",
+        options: {}
       },
       game: {
         players: [{ name: "Analyse" }, { name: "Analyse" }],
       },
       game: {
         players: [{ name: "Analyse" }, { name: "Analyse" }],
index 3d73893..b13e383 100644 (file)
@@ -78,7 +78,10 @@ main
   .row
     #aboveBoard.col-sm-12
       span.variant-cadence(v-if="game.type!='import'") {{ game.cadence }}
   .row
     #aboveBoard.col-sm-12
       span.variant-cadence(v-if="game.type!='import'") {{ game.cadence }}
-      span.variant-name {{ game.vname }}
+      span.variant-name
+        | {{ game.vname }}
+        | -
+        | {{ vr.constructor.AbbreviateOptions(game.options) }}
       span#nextGame(
         v-if="nextIds.length > 0"
         @click="showNextGame()"
       span#nextGame(
         v-if="nextIds.length > 0"
         @click="showNextGame()"
@@ -408,8 +411,15 @@ export default {
           this.conn.onopen = () => callback();
       };
       this.fetchGame((game) => {
           this.conn.onopen = () => callback();
       };
       this.fetchGame((game) => {
-        if (!!game)
+        if (!!game) {
+          if (!game.options) {
+            // Patch for retro-compatibility (TODO: remove it)
+            game.options = { randomness: game.randomness };
+            delete game["randomness"];
+          }
+          else game.options = JSON.parse(game.options);
           this.loadVariantThenGame(game, () => socketInit(this.roomInit));
           this.loadVariantThenGame(game, () => socketInit(this.roomInit));
+        }
         else
           // Live game stored remotely: need socket to retrieve it
           // NOTE: the callback "roomInit" will be lost, so it's not provided.
         else
           // Live game stored remotely: need socket to retrieve it
           // NOTE: the callback "roomInit" will be lost, so it's not provided.
@@ -707,7 +717,7 @@ export default {
           const gameToSend = Object.keys(this.game)
             .filter(k =>
               [
           const gameToSend = Object.keys(this.game)
             .filter(k =>
               [
-                "id","fen","players","vid","cadence","fenStart",
+                "id","fen","players","vid","cadence","fenStart","options",
                 "moves","clocks","score","drawOffer","rematchOffer"
               ].includes(k))
             .reduce(
                 "moves","clocks","score","drawOffer","rematchOffer"
               ].includes(k))
             .reduce(
@@ -1048,8 +1058,8 @@ export default {
         // Start a new game!
         let gameInfo = {
           id: getRandString(), //ignored if corr
         // Start a new game!
         let gameInfo = {
           id: getRandString(), //ignored if corr
-          fen: V.GenRandInitFen(this.game.randomness),
-          randomness: this.game.randomness,
+          fen: V.GenRandInitFen(this.game.options),
+          options: this.game.options,
           players: [this.game.players[1], this.game.players[0]],
           vid: this.game.vid,
           cadence: this.game.cadence
           players: [this.game.players[1], this.game.players[0]],
           vid: this.game.vid,
           cadence: this.game.cadence
@@ -1082,7 +1092,11 @@ export default {
             "/games",
             "POST",
             {
             "/games",
             "POST",
             {
-              data: { gameInfo: gameInfo },
+              data: Object.assign(
+                {},
+                gameInfo,
+                { options: JSON.stringify(this.game.options) }
+              ),
               success: (response) => {
                 gameInfo.id = response.id;
                 notifyNewGame();
               success: (response) => {
                 gameInfo.id = response.id;
                 notifyNewGame();
index 7749dfa..6d675a1 100644 (file)
@@ -12,7 +12,9 @@ main
   div#acceptDiv(role="dialog")
     .card
       p.text-center
   div#acceptDiv(role="dialog")
     .card
       p.text-center
-        span.variantName {{ curChallToAccept.vname }} 
+        span.variantName
+          | {{ curChallToAccept.vname }}
+          | {{ curChallToAccept.options.abridged }} 
         span {{ curChallToAccept.cadence }} 
         span {{ st.tr["with"] + " " + curChallToAccept.from.name }}
       p.text-center(v-if="!!curChallToAccept.color")
         span {{ curChallToAccept.cadence }} 
         span {{ st.tr["with"] + " " + curChallToAccept.from.name }}
       p.text-center(v-if="!!curChallToAccept.color")
@@ -49,6 +51,23 @@ main
               :selected="newchallenge.vid==v.id"
             )
               | {{ v.display }}
               :selected="newchallenge.vid==v.id"
             )
               | {{ v.display }}
+        // Variant-specific options (often at least randomness)
+        fieldset(v-if="!!newchallenge.V")
+          div(v-for="select of newchallenge.V.Options.select")
+            label(:for="select.variable + '_opt'") {{ st.tr[select.label] }} *
+            select(:id="select.variable + '_opt'")
+              option(
+                v-for="o of select.options"
+                :value="o.value"
+                :selected="o.value == select.defaut"
+              )
+                | {{ st.tr[o.label] }}
+          div(v-for="check of newchallenge.V.Options.check")
+            label(:for="check.variable + '_opt'") {{ st.tr[check.label] }} *
+            input(
+              :id="check.variable + '_opt'"
+              type="checkbox"
+              :checked="check.defaut")
         fieldset
           label(for="cadence") {{ st.tr["Cadence"] }} *
           div#predefinedCadences
         fieldset
           label(for="cadence") {{ st.tr["Cadence"] }} *
           div#predefinedCadences
@@ -61,12 +80,6 @@ main
             v-model="newchallenge.cadence"
             placeholder="5+0, 1h+30s, 5d ..."
           )
             v-model="newchallenge.cadence"
             placeholder="5+0, 1h+30s, 5d ..."
           )
-        fieldset
-          label(for="selectRandomLevel") {{ st.tr["Randomness"] }} *
-          select#selectRandomLevel(v-model="newchallenge.randomness")
-            option(value="0") {{ st.tr["Deterministic"] }}
-            option(value="1") {{ st.tr["Symmetric random"] }}
-            option(value="2") {{ st.tr["Asymmetric random"] }}
         fieldset
           label(for="memorizeChall") {{ st.tr["Memorize"] }}
           input#memorizeChall(
         fieldset
           label(for="memorizeChall") {{ st.tr["Memorize"] }}
           input#memorizeChall(
@@ -156,7 +169,7 @@ main
           tr
             th {{ st.tr["Variant"] }}
             th {{ st.tr["Cadence"] }}
           tr
             th {{ st.tr["Variant"] }}
             th {{ st.tr["Cadence"] }}
-            th {{ st.tr["Random?"] }}
+            th {{ st.tr["Options"] }}
             th
         tbody
           tr(
             th
         tbody
           tr(
@@ -165,7 +178,7 @@ main
           )
             td {{ pc.vname }}
             td {{ pc.cadence }}
           )
             td {{ pc.vname }}
             td {{ pc.cadence }}
-            td(:class="getRandomnessClass(pc)")
+            td(:class="getRandomnessClass(pc)") {{ pc.options.abridged }}
             td.remove-preset(@click="removePresetChall($event, pc)")
               img(src="/images/icons/delete.svg")
   .row
             td.remove-preset(@click="removePresetChall($event, pc)")
               img(src="/images/icons/delete.svg")
   .row
@@ -253,11 +266,9 @@ export default {
         to: "", //name of challenged player (if any)
         color: '',
         cadence: localStorage.getItem("cadence") || "",
         to: "", //name of challenged player (if any)
         color: '',
         cadence: localStorage.getItem("cadence") || "",
-        randomness:
-          // Warning: randomness can be 0, then !!randomness is false
-          (parseInt(localStorage.getItem("challRandomness"),10)+1 || 3) - 1,
+        options: {},
         // VariantRules object, stored to not interfere with
         // VariantRules object, stored to not interfere with
-        // diagrams of targetted challenges:
+        // diagrams of targeted challenges:
         V: null,
         vname: "",
         diag: "", //visualizing FEN
         V: null,
         vname: "",
         diag: "", //visualizing FEN
@@ -265,7 +276,7 @@ export default {
       },
       focus: true,
       tchallDiag: "",
       },
       focus: true,
       tchallDiag: "",
-      curChallToAccept: { from: {} },
+      curChallToAccept: { from: {}, options: {} },
       presetChalls: JSON.parse(localStorage.getItem("presetChalls") || "[]"),
       conn: null,
       connexionString: "",
       presetChalls: JSON.parse(localStorage.getItem("presetChalls") || "[]"),
       conn: null,
       connexionString: "",
@@ -326,13 +337,11 @@ export default {
                 to: this.$route.query["challenge"],
                 color: this.$route.query["color"] || '',
                 cadence: this.$route.query["cadence"],
                 to: this.$route.query["challenge"],
                 color: this.$route.query["color"] || '',
                 cadence: this.$route.query["cadence"],
-                // Tournament: no randomness (TODO: for now at least)
-                randomness: 0,
+                options: {},
                 memorize: false
               }
             );
             window.doClick("modalNewgame");
                 memorize: false
               }
             );
             window.doClick("modalNewgame");
-            //this.issueNewChallenge(); //NOTE: doesn't work yet.
           },
           this.$route.query["variant"]
         );
           },
           this.$route.query["variant"]
         );
@@ -417,15 +426,16 @@ export default {
               response.challenges.map(c => {
                 const from = { name: names[c.uid], id: c.uid }; //or just name
                 const type = this.classifyObject(c);
               response.challenges.map(c => {
                 const from = { name: names[c.uid], id: c.uid }; //or just name
                 const type = this.classifyObject(c);
-                const vname = this.getVname(c.vid);
+                this.setVname(c);
                 return Object.assign(
                 return Object.assign(
+                  {},
+                  c,
                   {
                     type: type,
                   {
                     type: type,
-                    vname: vname,
                     from: from,
                     from: from,
-                    to: c.target ? names[c.target] : ""
-                  },
-                  c
+                    to: c.target ? names[c.target] : "",
+                    options: JSON.parse(c.options)
+                  }
                 );
               })
             );
                 );
               })
             );
@@ -465,8 +475,9 @@ export default {
       this.conn = null;
     },
     getRandomnessClass: function(pc) {
       this.conn = null;
     },
     getRandomnessClass: function(pc) {
+      if (!pc.options.randomness) return {};
       return {
       return {
-        ["random-" + pc.randomness]: true
+        ["random-" + pc.options.randomness]: true
       };
     },
     anonymousCount: function() {
       };
     },
     anonymousCount: function() {
@@ -506,12 +517,22 @@ export default {
       this.partialResetNewchallenge();
       window.doClick("modalNewgame");
     },
       this.partialResetNewchallenge();
       window.doClick("modalNewgame");
     },
+    sameOptions: function(opt1, opt2) {
+      const keys1 = Object.keys(opt1),
+            keys2 = Object.keys(opt2);
+      if (keys1.length != keys2.length) return false;
+      for (const key1 of keys1) {
+        if (!keys2.includes(key1)) return false;
+        if (opt1[key1] != opt2[key1]) return false;
+      }
+      return true;
+    },
     addPresetChall: function(chall) {
       // Add only if not already existing:
       if (this.presetChalls.some(c =>
         c.vid == chall.vid &&
         c.cadence == chall.cadence &&
     addPresetChall: function(chall) {
       // Add only if not already existing:
       if (this.presetChalls.some(c =>
         c.vid == chall.vid &&
         c.cadence == chall.cadence &&
-        c.randomness == chall.randomness
+        this.sameOptions(c.options, chall.options)
       )) {
         return;
       }
       )) {
         return;
       }
@@ -521,7 +542,7 @@ export default {
         vid: chall.vid,
         vname: chall.vname, //redundant, but easier
         cadence: chall.cadence,
         vid: chall.vid,
         vname: chall.vname, //redundant, but easier
         cadence: chall.cadence,
-        randomness: chall.randomness
+        options: chall.options
       });
       localStorage.setItem("presetChalls", JSON.stringify(this.presetChalls));
     },
       });
       localStorage.setItem("presetChalls", JSON.stringify(this.presetChalls));
     },
@@ -825,7 +846,7 @@ export default {
                 id: c.id,
                 from: this.st.user.sid,
                 to: c.to,
                 id: c.id,
                 from: this.st.user.sid,
                 to: c.to,
-                randomness: c.randomness,
+                options: c.options,
                 fen: c.fen,
                 vid: c.vid,
                 cadence: c.cadence,
                 fen: c.fen,
                 vid: c.vid,
                 cadence: c.cadence,
@@ -969,7 +990,7 @@ export default {
       ) {
         let newChall = Object.assign({}, chall);
         newChall.type = this.classifyObject(chall);
       ) {
         let newChall = Object.assign({}, chall);
         newChall.type = this.classifyObject(chall);
-        newChall.randomness = chall.randomness;
+        newChall.options = chall.options;
         newChall.added = Date.now();
         let fromValues = Object.assign({}, this.people[chall.from]);
         delete fromValues["pages"]; //irrelevant in this context
         newChall.added = Date.now();
         let fromValues = Object.assign({}, this.people[chall.from]);
         delete fromValues["pages"]; //irrelevant in this context
@@ -1028,7 +1049,7 @@ export default {
       this.partialResetNewchallenge();
       this.newchallenge.vid = pchall.vid;
       this.newchallenge.cadence = pchall.cadence;
       this.partialResetNewchallenge();
       this.newchallenge.vid = pchall.vid;
       this.newchallenge.cadence = pchall.cadence;
-      this.newchallenge.randomness = pchall.randomness;
+      this.newchallenge.options = pchall.options;
       this.loadNewchallVariant(this.issueNewChallenge);
     },
     issueNewChallenge: async function() {
       this.loadNewchallVariant(this.issueNewChallenge);
     },
     issueNewChallenge: async function() {
@@ -1071,6 +1092,17 @@ export default {
       }
       // NOTE: "from" information is not required here
       let chall = Object.assign({}, this.newchallenge);
       }
       // NOTE: "from" information is not required here
       let chall = Object.assign({}, this.newchallenge);
+      chall.options = {};
+      // Get/set options variables (if any) / TODO: v-model?!
+      for (const check of this.newchallenge.V.Options.check) {
+        const elt = document.getElementById(check.variable + "_opt");
+        if (elt.checked) chall.options[check.variable] = true;
+      }
+      for (const select of this.newchallenge.V.Options.select) {
+        const elt = document.getElementById(select.variable + "_opt");
+        chall.options[select.variable] = elt.value;
+      }
+      chall.options.abridged = V.AbbreviateOptions(chall.options);
       // Add only if not already issued (not counting FEN):
       if (this.challenges.some(c =>
         (
       // Add only if not already issued (not counting FEN):
       if (this.challenges.some(c =>
         (
@@ -1085,7 +1117,7 @@ export default {
         &&
         c.vid == chall.vid &&
         c.cadence == chall.cadence &&
         &&
         c.vid == chall.vid &&
         c.cadence == chall.cadence &&
-        c.randomness == chall.randomness
+        this.sameOptions(c.options, chall.options)
       )) {
         alert(this.st.tr["Challenge already exists"]);
         return;
       )) {
         alert(this.st.tr["Challenge already exists"]);
         return;
@@ -1146,10 +1178,9 @@ export default {
         chall.type = ctype;
         chall.vname = this.newchallenge.vname;
         this.challenges.push(chall);
         chall.type = ctype;
         chall.vname = this.newchallenge.vname;
         this.challenges.push(chall);
-        // Remember cadence  + vid for quicker further challenges:
+        // Remember cadence + vid for quicker further challenges:
         localStorage.setItem("cadence", chall.cadence);
         localStorage.setItem("vid", chall.vid);
         localStorage.setItem("cadence", chall.cadence);
         localStorage.setItem("vid", chall.vid);
-        localStorage.setItem("challRandomness", chall.randomness);
         document.getElementById("modalNewgame").checked = false;
         // Show the challenge if not on current display
         if (
         document.getElementById("modalNewgame").checked = false;
         // Show the challenge if not on current display
         if (
@@ -1169,7 +1200,13 @@ export default {
           "/challenges",
           "POST",
           {
           "/challenges",
           "POST",
           {
-            data: { chall: chall },
+            data: {
+              chall: Object.assign(
+                {},
+                chall,
+                { options: JSON.stringify(chall.options) }
+              )
+            },
             success: (response) => {
               finishAddChallenge(response.id);
             }
             success: (response) => {
               finishAddChallenge(response.id);
             }
@@ -1275,8 +1312,8 @@ export default {
       // These game informations will be shared
       let gameInfo = {
         id: getRandString(),
       // These game informations will be shared
       let gameInfo = {
         id: getRandString(),
-        fen: c.fen || V.GenRandInitFen(c.randomness),
-        randomness: c.randomness, //for rematch
+        fen: c.fen || V.GenRandInitFen(c.options),
+        options: c.options, //for rematch
         players: players,
         vid: c.vid,
         cadence: c.cadence
         players: players,
         vid: c.vid,
         cadence: c.cadence
@@ -1326,7 +1363,11 @@ export default {
           {
             // cid is useful to delete the challenge:
             data: {
           {
             // cid is useful to delete the challenge:
             data: {
-              gameInfo: gameInfo,
+              gameInfo: Object.assign(
+                {},
+                gameInfo,
+                { options: JSON.stringify(gameInfo.options) }
+              ),
               cid: c.id
             },
             success: (response) => {
               cid: c.id
             },
             success: (response) => {
@@ -1478,11 +1519,11 @@ button.refuseBtn
 
 tr > td
   &.random-0
 
 tr > td
   &.random-0
-    background-color: #FF5733
+    background-color: #FEAF9E
   &.random-1
   &.random-1
-    background-color: #2B63B4
+    background-color: #9EB2FE
   &.random-2
   &.random-2
-    background-color: #33B42B
+    background-color: #A5FE9E
 
 @media screen and (max-width: 767px)
   h4
 
 @media screen and (max-width: 767px)
   h4
index 09d5e06..fc3a0cd 100644 (file)
@@ -1,5 +1,30 @@
 <template lang="pug">
 main
 <template lang="pug">
 main
+  input#modalOptions.modal(type="checkbox")
+  div#optionsDiv(
+    role="dialog"
+    data-checkbox="modalOptions"
+  )
+    .card
+      label.modal-close(for="modalOptions")
+      h3 {{ st.tr["Options"] }}
+      fieldset(v-if="!!V")
+        div(v-for="select of V.Options.select")
+          label(:for="select.variable + '_opt'") {{ st.tr[select.label] }} *
+          select(:id="select.variable + '_opt'")
+            option(
+              v-for="o of select.options"
+              :value="o.value"
+              :selected="o.value == select.defaut"
+            )
+              | {{ st.tr[o.label] }}
+        div(v-for="check of V.Options.check")
+          label(:for="check.variable + '_opt'") {{ st.tr[check.label] }} *
+          input(
+            :id="check.variable + '_opt'"
+            type="checkbox"
+            :checked="check.defaut")
+      button(@click="setOptions()") {{ st.tr["Validate"] }}
   .row
     .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
       .button-group
   .row
     .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
       .button-group
@@ -44,6 +69,7 @@ import ComputerGame from "@/components/ComputerGame.vue";
 import { store } from "@/store";
 import { replaceByDiag } from "@/utils/printDiagram";
 import { CompgameStorage } from "@/utils/compgameStorage";
 import { store } from "@/store";
 import { replaceByDiag } from "@/utils/printDiagram";
 import { CompgameStorage } from "@/utils/compgameStorage";
+import { processModalClick } from "@/utils/modalClick";
 import afterRawLoad from "@/utils/afterRawLoad";
 export default {
   name: "my-rules",
 import afterRawLoad from "@/utils/afterRawLoad";
 export default {
   name: "my-rules",
@@ -58,9 +84,9 @@ export default {
       // variables passed to ComputerGame:
       gameInfo: {
         vname: "",
       // variables passed to ComputerGame:
       gameInfo: {
         vname: "",
-        mode: "versus",
+        mode: "versus"
       },
       },
-      V: null,
+      V: null
     };
   },
   watch: {
     };
   },
   watch: {
@@ -72,6 +98,10 @@ export default {
     // NOTE: variant cannot be set before store is initialized
     this.re_setVariant(this.$route.params["vname"]);
   },
     // NOTE: variant cannot be set before store is initialized
     this.re_setVariant(this.$route.params["vname"]);
   },
+  mounted: function() {
+    document.getElementById("optionsDiv")
+      .addEventListener("click", processModalClick);
+  },
   computed: {
     showAnalyzeBtn: function() {
       return !!this.V && this.V.CanAnalyze;
   computed: {
     showAnalyzeBtn: function() {
       return !!this.V && this.V.CanAnalyze;
@@ -117,12 +147,38 @@ export default {
         }, 500);
       });
     },
         }, 500);
       });
     },
-    startGame: function(mode) {
+    setOptions: function() {
+      let options = {};
+      // Get/set options variables / TODO: v-model?!
+      for (const check of this.V.Options.check) {
+        const elt = document.getElementById(check.variable + "_opt");
+        if (elt.checked) options[check.variable] = true;
+      }
+      for (const select of this.V.Options.select) {
+        const elt = document.getElementById(select.variable + "_opt");
+        options[select.variable] = elt.value;
+      }
+      document.getElementById("modalOptions").checked = false;
+      if (this.whatNext == "analyze") this.gotoAnalyze(options);
+      else this.startGame(this.whatNext, options);
+    },
+    startGame: function(mode, options) {
       if (this.gameInProgress) return;
       if (this.gameInProgress) return;
-      this.gameInProgress = true;
-      this.display = "computer";
-      this.gameInfo.mode = mode;
-      if (this.gameInfo.mode == "versus") {
+      const next = (game, options) => {
+        this.gameInProgress = true;
+        this.display = "computer";
+        this.gameInfo.mode = mode;
+        this.$refs["compgame"].launchGame(game, options);
+      };
+      if (!!options) {
+        next(null, options);
+        return;
+      }
+      const askOptions = () => {
+        this.whatNext = mode;
+        doClick("modalOptions");
+      };
+      if (mode == "versus") {
         CompgameStorage.get(this.gameInfo.vname, (game) => {
           // NOTE: game might be null (if none stored yet)
           if (!!game && !V.IsGoodFen(game.fen)) {
         CompgameStorage.get(this.gameInfo.vname, (game) => {
           // NOTE: game might be null (if none stored yet)
           if (!!game && !V.IsGoodFen(game.fen)) {
@@ -130,10 +186,14 @@ export default {
             CompgameStorage.remove(game.vname);
             game = null;
           }
             CompgameStorage.remove(game.vname);
             game = null;
           }
-          this.$refs["compgame"].launchGame(game);
+          if (!!game || Object.keys(V.Options).length == 0) next(game);
+          else askOptions();
         });
       }
         });
       }
-      else this.$refs["compgame"].launchGame();
+      else {
+        if (Object.keys(V.Options).length == 0) next();
+        else askOptions();
+      }
     },
     // The user wants to stop the game:
     stopGame: function() {
     },
     // The user wants to stop the game:
     stopGame: function() {
@@ -145,11 +205,17 @@ export default {
       if (this.gameInfo.mode == "versus")
         CompgameStorage.remove(this.gameInfo.vname);
     },
       if (this.gameInfo.mode == "versus")
         CompgameStorage.remove(this.gameInfo.vname);
     },
-    gotoAnalyze: function() {
-      this.$router.push(
-        "/analyse/" + this.gameInfo.vname +
-        "/?fen=" + V.GenRandInitFen(this.st.settings.randomness)
-      );
+    gotoAnalyze: function(options) {
+      if (!options && Object.keys(V.Options).length > 0) {
+        this.whatNext = "analyze";
+        doClick("modalOptions");
+      }
+      else {
+        this.$router.push(
+          "/analyse/" + this.gameInfo.vname +
+          "/?fen=" + V.GenRandInitFen(options)
+        );
+      }
     }
   }
 };
     }
   }
 };
index 8e28e50..f60e5f1 100644 (file)
@@ -10,8 +10,8 @@ main
         a(href="https://www.chessvariants.com/why.html")
           | {{ st.tr["Why play chess variants?"] }}
       p
         a(href="https://www.chessvariants.com/why.html")
           | {{ st.tr["Why play chess variants?"] }}
       p
-        a(href="/#/variants/Chess") Chess
-        | {{ st.tr["chess_v"] }}
+        a(href="/#/variants/Chess960") Chess960
+        | {{ st.tr["chess960_v"] }}
       div(v-for="g of sortedGroups")
         h3 {{ st.tr["vt" + g] }}
         p {{ st.tr["vg" + g] }}
       div(v-for="g of sortedGroups")
         h3 {{ st.tr["vt" + g] }}
         p {{ st.tr["vg" + g] }}
index e26b03b..248e597 100644 (file)
@@ -39,7 +39,7 @@ create table Challenges (
   uid integer,
   target integer,
   vid integer,
   uid integer,
   target integer,
   vid integer,
-  randomness integer,
+  options varchar,
   fen varchar,
   cadence varchar,
   foreign key (uid) references Users(id),
   fen varchar,
   cadence varchar,
   foreign key (uid) references Users(id),
@@ -62,7 +62,7 @@ create table Games (
   score varchar default '*',
   scoreMsg varchar,
   cadence varchar,
   score varchar default '*',
   scoreMsg varchar,
   cadence varchar,
-  randomness integer, --for rematch
+  options varchar, --for rematch
   created datetime,
   drawOffer character default '',
   rematchOffer character default '',
   created datetime,
   drawOffer character default '',
   rematchOffer character default '',
index 6f67cc1..1cb54e6 100644 (file)
@@ -17,8 +17,7 @@ insert or ignore into Variants (name, description, groupe, display) values
   ('Alapo', 'Geometric Chess', 27, 'Alapo'),
   ('Alice', 'Both sides of the mirror', 31, 'Alice Chess'),
   ('Align4', 'Align four pawns', 31, 'Align4'),
   ('Alapo', 'Geometric Chess', 27, 'Alapo'),
   ('Alice', 'Both sides of the mirror', 31, 'Alice Chess'),
   ('Align4', 'Align four pawns', 31, 'Align4'),
-  ('Allmate1', 'Mate any piece (v1)', 11, 'Allmate1'),
-  ('Allmate2', 'Mate any piece (v2)', 11, 'Allmate2'),
+  ('Allmate', 'Mate any piece', 11, 'Allmate'),
   ('Ambiguous', 'Play opponent''s pieces', 29, 'Ambiguous'),
   ('Antiking1', 'Keep antiking in check (v1)', 9, 'Anti-King 1'),
   ('Antiking2', 'Keep antiking in check (v2)', 9, 'Anti-King 2'),
   ('Ambiguous', 'Play opponent''s pieces', 29, 'Ambiguous'),
   ('Antiking1', 'Keep antiking in check (v1)', 9, 'Anti-King 1'),
   ('Antiking2', 'Keep antiking in check (v2)', 9, 'Anti-King 2'),
@@ -44,10 +43,9 @@ insert or ignore into Variants (name, description, groupe, display) values
   ('Capablanca', 'Capablanca Chess', 7, 'Capablanca Chess'),
   ('Capture', 'Mandatory captures', 1, 'Capture'),
   ('Castle', 'Win by castling long', 27, 'Castle'),
   ('Capablanca', 'Capablanca Chess', 7, 'Capablanca Chess'),
   ('Capture', 'Mandatory captures', 1, 'Capture'),
   ('Castle', 'Win by castling long', 27, 'Castle'),
-  ('Checkered1', 'Shared pieces (v1)', 12, 'Checkered 1'),
-  ('Checkered2', 'Shared pieces (v2)', 12, 'Checkered 2'),
+  ('Checkered', 'Shared pieces', 12, 'Checkered'),
   ('Checkless', 'No-check mode', 18, 'Checkless'),
   ('Checkless', 'No-check mode', 18, 'Checkless'),
-  ('Chess', 'Standard rules', -1, 'Chess'),
+  ('Chess960', 'Standard rules', -1, 'Chess960'),
   ('Circular', 'Run forward', 3, 'Circular Chess'),
   ('Clorange', 'A Clockwork Orange', 20, 'Clockwork Orange'),
   ('Colorbound', 'The colorbound clobberers', 5, 'Colorbound Clobberers'),
   ('Circular', 'Run forward', 3, 'Circular Chess'),
   ('Clorange', 'A Clockwork Orange', 20, 'Clockwork Orange'),
   ('Colorbound', 'The colorbound clobberers', 5, 'Colorbound Clobberers'),
index a528ca6..e32cd68 100644 (file)
@@ -8,9 +8,10 @@ const UserModel = require("./User");
  *   uid: user id (int)
  *   target: recipient id (optional)
  *   vid: variant id (int)
  *   uid: user id (int)
  *   target: recipient id (optional)
  *   vid: variant id (int)
- *   randomness: integer in 0..2
+ *   options: varchar
  *   fen: varchar (optional)
  *   cadence: string (3m+2s, 7d ...)
  *   fen: varchar (optional)
  *   cadence: string (3m+2s, 7d ...)
+ *   options: string (js object)
  */
 
 const ChallengeModel = {
  */
 
 const ChallengeModel = {
@@ -19,7 +20,6 @@ const ChallengeModel = {
     return (
       c.vid.toString().match(/^[0-9]+$/) &&
       c.cadence.match(/^[0-9dhms +]+$/) &&
     return (
       c.vid.toString().match(/^[0-9]+$/) &&
       c.cadence.match(/^[0-9dhms +]+$/) &&
-      c.randomness.toString().match(/^[0-2]$/) &&
       c.fen.match(/^[a-zA-Z0-9, /-]*$/) &&
       (!c.to || UserModel.checkNameEmail({ name: c.to }))
     );
       c.fen.match(/^[a-zA-Z0-9, /-]*$/) &&
       (!c.to || UserModel.checkNameEmail({ name: c.to }))
     );
@@ -30,11 +30,10 @@ const ChallengeModel = {
       const query =
         "INSERT INTO Challenges " +
           "(added, uid, " + (c.to ? "target, " : "") +
       const query =
         "INSERT INTO Challenges " +
           "(added, uid, " + (c.to ? "target, " : "") +
-          "vid, randomness, fen, cadence) " +
-        "VALUES " +
-          "(" + Date.now() + "," + c.uid + "," + (c.to ? c.to + "," : "") +
-          c.vid + "," + c.randomness + ",'" + c.fen + "','" + c.cadence + "')";
-      db.run(query, function(err) {
+          "vid, options, fen, cadence) " +
+        "VALUES (" + Date.now() + "," + c.uid + "," + (c.to ? c.to + "," : "")
+          + c.vid + ",?,'" + c.fen + "','" + c.cadence + "')";
+      db.run(query, c.options, function(err) {
         cb(err, { id: this.lastID });
       });
     });
         cb(err, { id: this.lastID });
       });
     });
index 5304693..8cc526f 100644 (file)
@@ -15,7 +15,7 @@ const UserModel = require("./User");
  *   created: datetime
  *   drawOffer: char ('w','b' or '' for none)
  *   rematchOffer: char (similar to drawOffer)
  *   created: datetime
  *   drawOffer: char ('w','b' or '' for none)
  *   rematchOffer: char (similar to drawOffer)
- *   randomness: integer
+ *   options: varchar
  *   deletedByWhite: boolean
  *   deletedByBlack: boolean
  *   chatReadWhite: datetime
  *   deletedByWhite: boolean
  *   deletedByBlack: boolean
  *   chatReadWhite: datetime
@@ -40,7 +40,6 @@ const GameModel = {
     return (
       g.vid.toString().match(/^[0-9]+$/) &&
       g.cadence.match(/^[0-9dhms +]+$/) &&
     return (
       g.vid.toString().match(/^[0-9]+$/) &&
       g.cadence.match(/^[0-9dhms +]+$/) &&
-      g.randomness.toString().match(/^[0-2]$/) &&
       g.fen.match(/^[a-zA-Z0-9, /-]*$/) &&
       g.players.length == 2 &&
       g.players.every(p => p.id.toString().match(/^[0-9]+$/))
       g.fen.match(/^[a-zA-Z0-9, /-]*$/) &&
       g.players.length == 2 &&
       g.players.every(p => p.id.toString().match(/^[0-9]+$/))
@@ -57,22 +56,22 @@ const GameModel = {
     });
   },
 
     });
   },
 
-  create: function(vid, fen, randomness, cadence, players, cb) {
+  create: function(vid, fen, options, cadence, players, cb) {
     db.serialize(function() {
       let query =
         "INSERT INTO Games " +
         "(" +
     db.serialize(function() {
       let query =
         "INSERT INTO Games " +
         "(" +
-          "vid, fenStart, fen, randomness, " +
+          "vid, fenStart, fen, options, " +
           "white, black, " +
           "cadence, created" +
         ") " +
         "VALUES " +
         "(" +
           "white, black, " +
           "cadence, created" +
         ") " +
         "VALUES " +
         "(" +
-          vid + ",'" + fen + "','" + fen + "'," + randomness + "," +
+          vid + ",'" + fen + "','" + fen + "',?," +
           players[0].id + "," + players[1].id + "," +
           "'" + cadence + "'," + Date.now() +
         ")";
           players[0].id + "," + players[1].id + "," +
           "'" + cadence + "'," + Date.now() +
         ")";
-      db.run(query, function(err) {
+      db.run(query, options, function(err) {
         cb(err, { id: this.lastID });
       });
     });
         cb(err, { id: this.lastID });
       });
     });
@@ -85,7 +84,7 @@ const GameModel = {
       let query =
         "SELECT " +
           "id, vid, fen, fenStart, cadence, created, " +
       let query =
         "SELECT " +
           "id, vid, fen, fenStart, cadence, created, " +
-          "white, black, randomness, score, scoreMsg, " +
+          "white, black, options, score, scoreMsg, " +
           "chatReadWhite, chatReadBlack, drawOffer, rematchOffer " +
         "FROM Games " +
         "WHERE id = " + id;
           "chatReadWhite, chatReadBlack, drawOffer, rematchOffer " +
         "FROM Games " +
         "WHERE id = " + id;
index fe10ee3..1f626f2 100644 (file)
@@ -9,6 +9,7 @@ router.post("/challenges", access.logged, access.ajax, (req,res) => {
     let challenge = {
       fen: req.body.chall.fen,
       cadence: req.body.chall.cadence,
     let challenge = {
       fen: req.body.chall.fen,
       cadence: req.body.chall.cadence,
+      options: req.body.chall.options,
       randomness: req.body.chall.randomness,
       vid: req.body.chall.vid,
       uid: req.userId,
       randomness: req.body.chall.randomness,
       vid: req.body.chall.vid,
       uid: req.userId,