From 1c15969ecec2a86ee7dffe570e53dfd61fd06b22 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Thu, 16 Apr 2020 16:53:12 +0200
Subject: [PATCH] Games open in a new tab from main Hall (to not lose
 challenges)

---
 client/src/variants/Alice.js       | 26 ++++++++++++++++
 client/src/variants/Chakart.js     | 49 ++++++++++++++++++++++++++++++
 client/src/variants/Grasshopper.js |  5 +--
 client/src/variants/Makruk.js      |  5 +--
 client/src/variants/Sittuyin.js    |  1 -
 client/src/views/Analyse.vue       | 14 ++++-----
 client/src/views/Game.vue          | 16 +++++-----
 client/src/views/Hall.vue          |  9 +++---
 8 files changed, 97 insertions(+), 28 deletions(-)

diff --git a/client/src/variants/Alice.js b/client/src/variants/Alice.js
index d6fb0ac6..3fd02a32 100644
--- a/client/src/variants/Alice.js
+++ b/client/src/variants/Alice.js
@@ -34,6 +34,32 @@ export class AliceRules extends ChessRules {
     return (Object.keys(V.ALICE_PIECES).includes(b[1]) ? "Alice/" : "") + b;
   }
 
+  getEpSquare(moveOrSquare) {
+    if (!moveOrSquare) return undefined;
+    if (typeof moveOrSquare === "string") {
+      const square = moveOrSquare;
+      if (square == "-") return undefined;
+      return V.SquareToCoords(square);
+    }
+    // Argument is a move:
+    const move = moveOrSquare;
+    const s = move.start,
+          e = move.end;
+    if (
+      s.y == e.y &&
+      Math.abs(s.x - e.x) == 2 &&
+      // Special conditions: a pawn can be on the other side
+      ['p','s'].includes(move.appear[0].p) &&
+      ['p','s'].includes(move.vanish[0].p)
+    ) {
+      return {
+        x: (s.x + e.x) / 2,
+        y: s.y
+      };
+    }
+    return undefined; //default
+  }
+
   setOtherVariables(fen) {
     super.setOtherVariables(fen);
     const rows = V.ParseFen(fen).position.split("/");
diff --git a/client/src/variants/Chakart.js b/client/src/variants/Chakart.js
index 7b2e77e2..e8b95cb1 100644
--- a/client/src/variants/Chakart.js
+++ b/client/src/variants/Chakart.js
@@ -90,12 +90,61 @@ export class ChakartRules extends ChessRules {
 
   //atLeastOneMove() should be OK
 
+  doClick(square) {
+    // A click to promote a piece on subTurn 2 would trigger this.
+    // For now it would then return [NaN, NaN] because surrounding squares
+    // have no IDs in the promotion modal. TODO: improve this?
+    if (!square[0]) return null;
+    // If subTurn == 2:
+    // if square is empty && firstMove is compatible,
+    // complete the move (banana or bomb).
+    // if square not empty, just complete with empty move
+    const Lf = this.firstMove.length;
+    if (this.subTurn == 2) {
+      if (
+        this.board[square[0]][square[1]] == V.EMPTY &&
+        !this.underCheck(this.turn) &&
+        (La == 0 || !this.oppositeMoves(this.amoves[La-1], this.firstMove[Lf-1]))
+      ) {
+        return {
+          start: { x: -1, y: -1 },
+          end: { x: -1, y: -1 },
+          appear: [],
+          vanish: []
+        };
+      }
+    }
+    return null;
+  }
+
+  postPlay(move) {
+    // TODO: king may also be "chomped"
+    super.updateCastleFlags(move, piece);
+  }
+
   getCurrentScore() {
+    if (this.kingPos[this.turn][0] < 0)
+      // King captured (or "chomped")
+      return this.turn == "w" ? "0-1" : "1-0";
+    const color = V.GetOppCol(this.turn);
+    const lastRank = (color == 'w' ? 0 : 7);
+    if (this.kingPos[color][0] == lastRank)
+      // The opposing edge is reached!
+      return color == "w" ? "1-0" : "0-1";
+    if (this.atLeastOneMove()) return "*";
+    // Game over
+    const oppCol = this.turn;
+    if (!this.underCheck(oppCol)) return "1/2";
+    return (oppCol == "w" ? "0-1" : "1-0");
     //TODO: But = capturer la princesse adverse. Variante : but = la princesse arrive de l'autre côté de l'échiquier.
     //==> On peut mixer ces deux conditions : arriver au bout du plateau ou capturer la princesse adverse.
     return '*';
   }
 
+  getComputerMove() {
+    // TODO: random mover
+  }
+
   //Détails :
   //Si une pièce pose quelque chose sur une case ça remplace ce qui y était déjà.
   //Pas de condition de pat, puisque l'objectif est la capture de la reine :)
diff --git a/client/src/variants/Grasshopper.js b/client/src/variants/Grasshopper.js
index 5069e231..c47ac2b1 100644
--- a/client/src/variants/Grasshopper.js
+++ b/client/src/variants/Grasshopper.js
@@ -11,10 +11,7 @@ export class GrasshopperRules extends ChessRules {
     return Object.assign(
       {},
       ChessRules.PawnSpecs,
-      {
-        twoSquares: false,
-        promotions: ChessRules.PawnSpecs.promotions.concat([V.GRASSHOPPER])
-      }
+      { promotions: ChessRules.PawnSpecs.promotions.concat([V.GRASSHOPPER]) }
     );
   }
 
diff --git a/client/src/variants/Makruk.js b/client/src/variants/Makruk.js
index 9f9c37b6..2eeeaeaf 100644
--- a/client/src/variants/Makruk.js
+++ b/client/src/variants/Makruk.js
@@ -15,10 +15,7 @@ export class MakrukRules extends ChessRules {
     return Object.assign(
       {},
       ChessRules.PawnSpecs,
-      {
-        twoSquares: false,
-        promotions: [V.QUEEN]
-      }
+      { promotions: [V.QUEEN] }
     );
   }
 
diff --git a/client/src/variants/Sittuyin.js b/client/src/variants/Sittuyin.js
index aae1890e..c93c3f07 100644
--- a/client/src/variants/Sittuyin.js
+++ b/client/src/variants/Sittuyin.js
@@ -15,7 +15,6 @@ export class SittuyinRules extends ChessRules {
       {},
       ChessRules.PawnSpecs,
       {
-        twoSquares: false,
         // Promotions are handled differently here
         promotions: [V.QUEEN]
       }
diff --git a/client/src/views/Analyse.vue b/client/src/views/Analyse.vue
index bc88c757..9dcf033a 100644
--- a/client/src/views/Analyse.vue
+++ b/client/src/views/Analyse.vue
@@ -7,7 +7,8 @@ main
   )
     .card
       label.modal-close(for="modalRules")
-      h4#variantNameInAnalyze(@click="gotoRules()") {{ game.vname }}
+      a#variantNameInAnalyze(:href="'/#/variants/'+game.vname")
+        | {{ game.vname }}
       div(v-html="rulesContent")
   .row
     .col-sm-12
@@ -111,9 +112,6 @@ export default {
         .replace(/"$/, "")
         .replace(/(fen:)([^:]*):/g, replaceByDiag);
     },
-    gotoRules: function() {
-      this.$router.push("/variants/" + this.gameRef.vname);
-    },
     loadGame: function(orientation) {
       this.game.vname = this.gameRef.vname;
       this.game.fenStart = this.gameRef.fen;
@@ -149,11 +147,13 @@ export default {
 </style>
 
 <style lang="sass" scoped>
-h4#variantNameInAnalyze
-  cursor: pointer
+a#variantNameInAnalyze
+  color: var(--card-fore-color)
   text-align: center
-  text-decoration: underline
   font-weight: bold
+  font-size: calc(1rem * var(--heading-ratio))
+  line-height: 1.2
+  margin: calc(1.5 * var(--universal-margin))
 
 #rulesDiv > .card
   padding: 5px 0
diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue
index e0eb1be0..99b1f22e 100644
--- a/client/src/views/Game.vue
+++ b/client/src/views/Game.vue
@@ -7,7 +7,7 @@ main
   )
     .card
       label.modal-close(for="modalRules")
-      h4#variantNameInGame(@click="gotoRules") {{ game.vname }}
+      a#variantNameInGame(:href="'/#/variants/'+game.vname") {{ game.vname }}
       div(v-html="rulesContent")
   input#modalScore.modal(type="checkbox")
   div#scoreDiv(
@@ -252,6 +252,9 @@ export default {
   },
   // NOTE: some redundant code with Hall.vue (mostly related to people array)
   created: function() {
+    if (this.$route.query["focus"] === "false")
+      // Focus explicitely set to false from Hall (live game)
+      this.focus = false;
     this.atCreation();
   },
   mounted: function() {
@@ -310,9 +313,6 @@ export default {
         )
       );
     },
-    gotoRules: function() {
-      this.$router.push("/variants/" + this.game.vname);
-    },
     participateInChat: function(p) {
       return Object.keys(p.tmpIds).some(x => p.tmpIds[x].focus) && !!p.name;
     },
@@ -1801,11 +1801,13 @@ button.acceptBtn
 button.refuseBtn
   background-color: red
 
-h4#variantNameInGame
-  cursor: pointer
+a#variantNameInGame
+  color: var(--card-fore-color)
   text-align: center
-  text-decoration: underline
   font-weight: bold
+  font-size: calc(1rem * var(--heading-ratio))
+  line-height: 1.2
+  margin: calc(1.5 * var(--universal-margin))
 </style>
 
 <style lang="sass">
diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue
index 01f588f2..50da3810 100644
--- a/client/src/views/Hall.vue
+++ b/client/src/views/Hall.vue
@@ -572,14 +572,12 @@ export default {
         }
       });
       const gid = gids[Math.floor(Math.random() * gids.length)];
-      const game = this.games.find(g => g.id == gid);
-      if (!!game) this.showGame(game);
-      else this.$router.push("/game/" + gid); //game vs. me
+      window.open("/#/game/" + gid, "_blank");
     },
     showGame: function(g) {
       // NOTE: we are an observer, since only games I don't play are shown here
       // ==> Moves sent by connected remote player(s) if live game
-      this.$router.push("/game/" + g.id);
+      window.open("/#/game/" + g.id, "_blank");
     },
     toggleSocialColor: function(action) {
       if (!action && document.getElementById("modalPeople").checked)
@@ -1302,7 +1300,8 @@ export default {
                 { body: "vs " + game.players[1-myIdx].name || "@nonymous" }
               );
             }
-            this.$router.push("/game/" + gameInfo.id);
+            this.$router.push(
+              "/game/" + gameInfo.id + "/?focus=" + this.focus);
           });
         },
         this.focus ? 500 + 1000 * Math.random() : 0
-- 
2.44.0