From 13227d6124e1cf465d329373e32c407d6fd351d3 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Thu, 21 May 2020 21:48:47 +0200
Subject: [PATCH] Experimental support for challenges from URL + ask specific
 color

---
 client/src/translations/en.js |  6 +++
 client/src/translations/es.js |  6 +++
 client/src/translations/fr.js |  6 +++
 client/src/variants/Koopa.js  | 10 +++--
 client/src/views/Hall.vue     | 73 +++++++++++++++++++++++++++++------
 5 files changed, 85 insertions(+), 16 deletions(-)

diff --git a/client/src/translations/en.js b/client/src/translations/en.js
index d223dc14..13869e9f 100644
--- a/client/src/translations/en.js
+++ b/client/src/translations/en.js
@@ -14,6 +14,7 @@ export const translations = {
   "Asymmetric random": "Asymmetric random",
   "Authentication successful!": "Authentication successful!",
   "Back to list": "Back to list",
+  "Black": "Black",
   "Black to move": "Black to move",
   "Black surrender": "Black surrender",
   "Black win": "Black win",
@@ -29,6 +30,8 @@ export const translations = {
   "Chat here": "Chat here",
   "Clear history": "Clear history",
   Code: "Code",
+  Color: "Color",
+  "Color option only for targeted challenge": "Color option only for targeted challenge",
   "Connection token sent. Check your emails!": "Connection token sent. Check your emails!",
   Contact: "Contact",
   "Correspondence challenges": "Correspondence challenges",
@@ -146,13 +149,16 @@ export const translations = {
   Variant: "Variant",
   Variants: "Variants",
   Versus: "Versus",
+  White: "White",
   "White to move": "White to move",
   "White surrender": "White surrender",
   "White win": "White win",
   "Who's there?": "Who's there?",
   With: "With",
   with: "with",
+  "Wrong color": "Wrong color",
   "Wrong time control": "Wrong time control",
+  "Your color:": "Your color:",
   "Your message": "Your message",
 
   // Variants boxes:
diff --git a/client/src/translations/es.js b/client/src/translations/es.js
index fe610f2d..2036ad53 100644
--- a/client/src/translations/es.js
+++ b/client/src/translations/es.js
@@ -14,6 +14,7 @@ export const translations = {
   "Asymmetric random": "Aleatorio asimétrico",
   "Authentication successful!": "¡Autenticación exitosa!",
   "Back to list": "Volver a la lista",
+  Black: "Negras",
   "Black to move": "Juegan las negras",
   "Black surrender": "Las negras abandonan",
   "Black win": "Las negras gagnan",
@@ -29,6 +30,8 @@ export const translations = {
   "Chat here": "Chat aquí",
   "Clear history": "Clara historia",
   Code: "Código",
+  Color: "Color",
+  "Color option only for targeted challenge": "Elección de color solo para desafíos específicos",
   "Connection token sent. Check your emails!": "Token de conexión enviado. ¡Revisa tus correos!",
   Contact: "Contacto",
   "Correspondence challenges": "Desafíos por correspondencia",
@@ -146,13 +149,16 @@ export const translations = {
   Variant: "Variante",
   Variants: "Variantes",
   Versus: "Contra",
+  White: "Blancas",
   "White to move": "Juegan las blancas",
   "White surrender": "Las blancas abandonan",
   "White win": "Las blancas gagnan",
   "Who's there?": "¿Quién está ahí?",
   With: "Con",
   with: "con",
+  "Wrong color": "Color incorrecto",
   "Wrong time control": "Cadencia errónea",
+  "Your color:": "Tu color:",
   "Your message": "Tu mensaje",
 
   // Variants boxes:
diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js
index 980beac5..2e34016a 100644
--- a/client/src/translations/fr.js
+++ b/client/src/translations/fr.js
@@ -14,6 +14,7 @@ export const translations = {
   "Are you sure?": "Étes vous sûr?",
   "Asymmetric random": "Aléatoire asymétrique",
   "Back to list": "Retour à la liste",
+  "Black": "Noirs",
   "Black to move": "Trait aux noirs",
   "Black surrender": "Les noirs abandonnent",
   "Black win": "Les noirs gagnent",
@@ -29,6 +30,8 @@ export const translations = {
   "Chat here": "Chattez ici",
   "Clear history": "Effacer l'historique",
   Code: "Code",
+  Color: "Couleur",
+  "Color option only for targeted challenge": "Choix de la couleur seulement pour défis ciblés",
   "Connection token sent. Check your emails!": "Token de connection envoyé. Allez voir vos emails !",
   Contact: "Contact",
   "Correspondence challenges": "Défis par correspondance",
@@ -146,13 +149,16 @@ export const translations = {
   Variant: "Variante",
   Variants: "Variantes",
   Versus: "Contre",
+  White: "Blancs",
   "White to move": "Trait aux blancs",
   "White surrender": "Les blancs abandonnent",
   "White win": "Les blancs gagnent",
   "Who's there?": "Qui est là ?",
   With: "Avec",
   with: "avec",
+  "Wrong color": "Mauvaise couleur",
   "Wrong time control": "Cadence erronée",
+  "Your color:": "Votre couleur :",
   "Your message": "Votre message",
 
   // Variants boxes:
diff --git a/client/src/variants/Koopa.js b/client/src/variants/Koopa.js
index c7b0859f..bc72d6df 100644
--- a/client/src/variants/Koopa.js
+++ b/client/src/variants/Koopa.js
@@ -235,9 +235,10 @@ export class KoopaRules extends ChessRules {
     // Base method is fine because a stunned king (which won't be detected)
     // can still castle after going back to normal.
     super.postPlay(move);
-    const kIdx = move.vanish.findIndex(v => v.p == "l");
+    const kIdx = move.vanish.findIndex(
+      (v,i) => v.p == 'l' || (i >= 1 && v.p == 'k'));
     if (kIdx >= 0)
-      // A stunned king vanish (game over)
+      // A (stunned or not) king vanish: game over
       this.kingPos[move.vanish[kIdx].c] = [-1, -1];
     move.stunned = JSON.stringify(this.stunned);
     // Array of stunned stage 1 pieces (just back to normal then)
@@ -268,9 +269,10 @@ export class KoopaRules extends ChessRules {
 
   postUndo(move) {
     super.postUndo(move);
-    const kIdx = move.vanish.findIndex(v => v.p == "l");
+    const kIdx = move.vanish.findIndex(
+      (v,i) => v.p == 'l' || (i >= 1 && v.p == 'k'));
     if (kIdx >= 0) {
-      // A stunned king vanished
+      // A king vanished
       this.kingPos[move.vanish[kIdx].c] =
         [move.vanish[kIdx].x, move.vanish[kIdx].y];
     }
diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue
index 451299b7..5b537d85 100644
--- a/client/src/views/Hall.vue
+++ b/client/src/views/Hall.vue
@@ -15,6 +15,8 @@ main
         span.variantName {{ curChallToAccept.vname }} 
         span {{ curChallToAccept.cadence }} 
         span {{ st.tr["with"] + " " + curChallToAccept.from.name }}
+      p.text-center(v-if="!!curChallToAccept.color")
+        | {{ st.tr["Your color:"] + " " + invColor(curChallToAccept.color) }}
       .diagram(
         v-if="!!curChallToAccept.fen"
         v-html="tchallDiag"
@@ -88,7 +90,13 @@ main
             type="text"
             v-model="newchallenge.to"
           )
-        fieldset(v-if="st.user.id > 0 && newchallenge.to.length > 0")
+        fieldset(v-show="st.user.id > 0 && newchallenge.to.length > 0")
+          label(for="selectColor") {{ st.tr["Color"] }}
+          select#selectColor(v-model="newchallenge.color")
+            option(value='') 
+            option(value='w') {{ st.tr["White"] }}
+            option(value='b') {{ st.tr["Black"] }}
+          br
           input#inputFen(
             placeholder="FEN"
             @input="trySetNewchallDiag()"
@@ -242,6 +250,7 @@ export default {
         fen: "",
         vid: parseInt(localStorage.getItem("vid"), 10) || 0,
         to: "", //name of challenged player (if any)
+        color: '',
         cadence: localStorage.getItem("cadence") || "",
         randomness:
           // Warning: randomness can be 0, then !!randomness is false
@@ -255,7 +264,7 @@ export default {
       },
       focus: true,
       tchallDiag: "",
-      curChallToAccept: {from: {}},
+      curChallToAccept: { from: {} },
       presetChalls: JSON.parse(localStorage.getItem("presetChalls") || "[]"),
       conn: null,
       connexionString: "",
@@ -301,6 +310,28 @@ export default {
     const connectAndPoll = () => {
       this.send("connect");
       this.send("pollclientsandgamers");
+      if (!!this.$route.query["challenge"]) {
+        // Automatic challenge sending, for tournaments
+        this.loadNewchallVariant(
+          () => {
+            this.newchallenge = {
+              fen: "",
+              vid:
+                this.st.variants
+                .find(v => v.name == this.$route.query["variant"])
+                .id,
+              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,
+              memorize: false
+            };
+            this.issueNewChallenge();
+          },
+          this.$route.query["variant"]
+        );
+      }
     };
     // Initialize connection
     this.connexionString =
@@ -454,9 +485,14 @@ export default {
       this.focus = false;
       this.send("losefocus");
     },
+    invColor: function(c) {
+      if (c == 'w') return this.st.tr["Black"];
+      return this.tr.tr["White"];
+    },
     partialResetNewchallenge: function() {
       // Reset potential target and custom FEN:
       this.newchallenge.to = "";
+      this.newchallenge.color = '';
       this.newchallenge.fen = "";
       this.newchallenge.diag = "";
       this.newchallenge.memorize = false;
@@ -499,6 +535,7 @@ export default {
       if (!this.newchallenge.to) {
         // Reset potential FEN + diagram
         this.newchallenge.fen = "";
+        this.newchallenge.color = '';
         this.newchallenge.diag = "";
       }
     },
@@ -952,8 +989,8 @@ export default {
         }
       }
     },
-    loadNewchallVariant: async function(cb) {
-      const vname = this.getVname(this.newchallenge.vid);
+    loadNewchallVariant: async function(cb, vname) {
+      vname = vname || this.getVname(this.newchallenge.vid);
       await import("@/variants/" + vname + ".js")
       .then((vModule) => {
         window.V = vModule[vname + "Rules"];
@@ -976,8 +1013,8 @@ export default {
       ) {
         const parsedFen = V.ParseFen(this.newchallenge.fen);
         this.newchallenge.diag = getDiagram({
-          position: parsedFen.position,
-          orientation: parsedFen.turn
+          position: parsedFen.position
+          //,orientation: parsedFen.turn
         });
       } else this.newchallenge.diag = "";
     },
@@ -998,6 +1035,8 @@ export default {
         error = this.st.tr["Please select a variant"];
       else if (ctype == "corr" && this.st.user.id <= 0)
         error = this.st.tr["Please log in to play correspondence games"];
+      else if (!this.newchallenge.to && !!this.newchallenge.color)
+        error = this.st.tr["Color option only for targeted challenge"];
       else if (!!this.newchallenge.to) {
         if (this.newchallenge.to == this.st.user.name)
           error = this.st.tr["Self-challenge is forbidden"];
@@ -1007,8 +1046,14 @@ export default {
         ) {
           error = this.newchallenge.to + " " + this.st.tr["is not online"];
         }
+        if (
+          !!this.newchallenge.color &&
+          !['w', 'b'].includes(this.newchallenge.color)
+        ) {
+          error = this.st.tr["Wrong color"];
+        }
       }
-      if (error) {
+      if (!!error) {
         alert(error);
         return;
       }
@@ -1020,13 +1065,18 @@ export default {
       }
       // NOTE: "from" information is not required here
       let chall = Object.assign({}, this.newchallenge);
-      // Add only if not already issued (not counting target or FEN):
+      // Add only if not already issued (not counting FEN):
       if (this.challenges.some(c =>
         (
           c.from.sid == this.st.user.sid ||
           (c.from.id > 0 && c.from.id == this.st.user.id)
         )
         &&
+        (
+          (!c.to && !chall.to) ||
+          c.to == chall.to
+        )
+        &&
         c.vid == chall.vid &&
         c.cadence == chall.cadence &&
         c.randomness == chall.randomness
@@ -1175,10 +1225,9 @@ export default {
             // c.to == this.st.user.name (connected)
             if (!!c.fen) {
               const parsedFen = V.ParseFen(c.fen);
-              c.mycolor = V.GetOppCol(parsedFen.turn);
               this.tchallDiag = getDiagram({
                 position: parsedFen.position,
-                orientation: c.mycolor
+                orientation: parsedFen.turn
               });
             }
             this.curChallToAccept = c;
@@ -1205,8 +1254,8 @@ export default {
     launchGame: function(c) {
       // White player index 0, black player index 1:
       let players =
-        !!c.mycolor
-          ? (c.mycolor == "w" ? [c.seat, c.from] : [c.from, c.seat])
+        !!c.color
+          ? (c.color == "w" ? [c.from, c.seat] : [c.seat, c.from])
           : shuffle([c.from, c.seat]);
       players.forEach(p => {
         if (!!p["tmpIds"]) delete p["tmpIds"];
-- 
2.44.0