Simplify Game logic a bit + some advances on Chakart
authorBenjamin Auder <benjamin.auder@somewhere>
Fri, 24 Apr 2020 18:55:30 +0000 (20:55 +0200)
committerBenjamin Auder <benjamin.auder@somewhere>
Fri, 24 Apr 2020 18:55:30 +0000 (20:55 +0200)
TODO
client/src/base_rules.js
client/src/components/BaseGame.vue
client/src/translations/faq/en.pug
client/src/variants/Chakart.js
client/src/variants/Football.js
client/src/variants/Monochrome.js
client/src/variants/Teleport.js
client/src/views/Game.vue

diff --git a/TODO b/TODO
index ec7d975..b2fd215 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,6 +1,7 @@
 Issue: embedded rules language not updated when language is set (in Analyse, Game and Problems)
 Also: if new live game starts in background, "new game" notify OK but not first move (not too serious however)
-On smartphone for Teleport, Chakart, Weiqi and some others: wait re-click to confirm "touch" move (in Board.vue)
+On smartphone for Teleport, Chakart, Weiqi and some others: option "confirm moves on touch screen"
+(=> comme pour corr) + option "confirm moves in corr games"?
 
 https://www.chessvariants.com/crossover.dir/koopachess.html
 --> Can a stunned piece capture? Maybe not. ...recover? After 5 moves? Never?
index 1795b5f..cfb8f84 100644 (file)
@@ -80,6 +80,11 @@ export const ChessRules = class ChessRules {
     return V.ShowMoves;
   }
 
+  // Generally true, unless the variant includes random effects
+  static get CorrConfirm() {
+    return true;
+  }
+
   // Used for Monochrome variant (TODO: harmonize: !canFlip ==> showFirstTurn)
   get showFirstTurn() {
     return false;
index 70a304b..f476064 100644 (file)
@@ -461,7 +461,7 @@ export default {
           // To play a received move, cursor must be at the end of the game:
           this.gotoEnd();
       }
-      // The board may show some the possible moves: (TODO: bad solution)
+      // The board may show some possible moves: (TODO: bad solution)
       this.$refs["board"].resetCurrentAttempt();
       const playSubmove = (smove) => {
         smove.notation = this.vr.getNotation(smove);
index 0d5ae9e..2f74462 100644 (file)
   | long live games, like maybe 1h30 + 30s.
 
 .question.
-  I bookmarked a fascinating observed game, but the URL no longer works.
+  I bookmarked a fascinating game, but the URL no longer works.
 .answer
   | Live games are stored locally, on browsers only.
   | If you are only an observer, your browser doesn't even store it, so later
index 5d79f4d..12a84bc 100644 (file)
@@ -1,23 +1,38 @@
 import { ChessRules } from "@/base_rules";
 
 export class ChakartRules extends ChessRules {
-  // NOTE: getBasicMove, ajouter les bonus à vanish array
-  // + déterminer leur effet (si cavalier) ou case (si banane ou bombe)
-  // (L'effet doit être caché au joueur : devrait être OK)
-  //
-  // Saut possible par dessus bonus ou champis mais pas bananes ou bombes
-//==> redefinir isAttackedBySlide et getPotentialSlide...
-
-// keep track of captured pieces: comme Grand; pieces can get back to board with toadette bonus.
-// --> pour ce bonus, passer "capture" temporairement en "reserve" pour permettre de jouer le coup.
+  static get CorrConfirm() {
+    // Because of bonus effects
+    return false;
+  }
 
-  // "pièces" supplémentaires : bananes, bombes, champis, bonus --> + couleur ?
-  //   (Semble mieux sans couleur => couleur spéciale indiquant que c'est pas jouable)
-  // (Attention: pas jouables cf. getPotentialMoves...)
+  static get CanAnalyze() {
+    return false;
+  }
 
   hoverHighlight(x, y) {
-    // TODO: exact squares
-    return this.subTurn == 2; //&& this.firstMove.donkey or wario or bonus roi boo
+    if (
+      this.firstMove.appear.length == 0 ||
+      this.firstMove.vanish.length == 0 ||
+      this.board[x][y] != V.EMPTY
+    ) {
+      return false;
+    }
+    const deltaX = Math.abs(this.firstMove.end.x - x);
+    const deltaY = Math.abs(this.firstMove.end.y - y);
+    return (
+      this.subTurn == 2 &&
+      // Condition: rook or bishop move, may capture, but no bonus move
+      [V.ROOK, V.BISHOP].includes(this.firstMove.vanish[0].p) &&
+      (
+        this.firstMove.vanish.length == 1 ||
+        ['w', 'b'].includes(this.firstMove.vanish[1].c)
+      ) &&
+      (
+        this.firstMove.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1 ||
+        this.firstMove.vanish[0].p == V.BISHOP && deltaX + deltaY == 1
+      )
+    );
   }
 
   static get IMMOBILIZE_CODE() {
@@ -46,9 +61,33 @@ export class ChakartRules extends ChessRules {
     return 'i';
   }
 
+  // Fictive color 'a', bomb banana mushroom egg
+  static get BOMB() {
+    // Doesn't collide with bishop because color 'a'
+    return 'b';
+  }
+  static get BANANA() {
+    return 'n';
+  }
+  static get EGG() {
+    return 'e';
+  }
+  static get MUSHROOM() {
+    return 'm';
+  }
+
+  static get PIECES() {
+    return (
+      ChessRules.PIECES.concat(
+      Object.keys(V.IMMOBILIZE_DECODE)).concat(
+      [V.BANANA, V.BOMB, V.EGG, V.MUSHROOM, V.INVISIBLE_QUEEN])
+    );
+  }
+
   getPpath(b) {
     let prefix = "";
     if (
+      b[0] == 'a' ||
       b[1] == V.INVISIBLE_QUEEN ||
       Object.keys(V.IMMOBILIZE_DECODE).includes(b[1])
     ) {
@@ -97,7 +136,7 @@ export class ChakartRules extends ChessRules {
   setFlags(fenflags) {
     super.setFlags(fenflags); //castleFlags
     this.powerFlags = {
-      w: [...Array(2)], //king can send red shell? Queen can be invisible?
+      w: [...Array(2)], //king can send shell? Queen can be invisible?
       b: [...Array(2)]
     };
     const flags = fenflags.substr(4); //skip first 4 letters, for castle
@@ -166,7 +205,13 @@ export class ChakartRules extends ChessRules {
     return fen;
   }
 
+  addBonusYoshi() {
+    // TODO
+// --> pour bonus toadette, passer "capture" temporairement en "reserve" pour permettre de jouer le coup.
+  }
+
   getPotentialMovesFrom([x, y]) {
+    // TODO: si banane ou bombe ou... alors return [] ?
     // TODO: bananes et bombes limitent les déplacements (agissent comme un mur "capturable")
     // bananes jaunes et rouges ?! (agissant sur une seule couleur ?) --> mauvaise idée.
     if (this.subTurn == 2) {
@@ -177,6 +222,17 @@ export class ChakartRules extends ChessRules {
   // TODO: un-immobilize my immobilized piece at the end of this turn, if any
   }
 
+  getBasicMove([x1, y1], [x2, y2]) {
+  // NOTE: getBasicMove, ajouter les bonus à vanish array
+  // + déterminer leur effet (si cavalier) ou case (si banane ou bombe)
+  // (L'effet doit être caché au joueur : devrait être OK)
+  }
+
+  getSlideNJumpMpves(sq, steps, oneStep) {
+  // Saut possible par dessus bonus ou champis mais pas bananes ou bombes
+//==> redefinir isAttackedBySlide et getPotentialSlide...
+  }
+
   getPotentialPawnMoves(sq) {
     //Toad: pion
     //  laisse sur sa case de départ un champi turbo permettant à Peach et cavalier et autres pions d'aller
@@ -230,11 +286,19 @@ export class ChakartRules extends ChessRules {
     //  Profite des accélérateurs posés par les pions (+ 1 case : obligatoire).
   }
 
+  isAttackedBySlideNJump() {
+    // TODO:
+  }
+
   atLeastOneMove() {
     // TODO: check that
     return true;
   }
 
+  getAllPotentialMoves() {
+    // (Attention: objets pas jouables cf. getPotentialMoves...)
+  }
+
   play(move) {
     // TODO: subTurn passe à 2 si arrivée sur bonus cavalier
     // potentiellement pose (tour, fou) ou si choix (reconnaître i (ok), ii (ok) et iii (si coup normal + pas immobilisé) ?)
index b0678e1..e7e6513 100644 (file)
@@ -1,4 +1,6 @@
 import { ChessRules } from "@/base_rules";
+import { ArrayFun } from "@/utils/array";
+import { shuffle } from "@/utils/alea";
 
 export class FootballRules extends ChessRules {
   static get HasFlags() {
index 5bd2ce0..b17b6f1 100644 (file)
@@ -6,6 +6,10 @@ export class MonochromeRules extends ChessRules {
     return false;
   }
 
+  static get Lines() {
+    return [ [[4, 0], [4, 8]] ];
+  }
+
   get showFirstTurn() {
     return true;
   }
index 93648c4..f5422c7 100644 (file)
@@ -3,8 +3,27 @@ import { randInt } from "@/utils/alea";
 
 export class TeleportRules extends ChessRules {
   hoverHighlight(x, y) {
-    // TODO: only highlight if the move is legal
-    return (this.subTurn == 2 && this.board[x][y] == V.EMPTY);
+    if (this.subTurn == 1 || this.board[x][y] != V.EMPTY)
+      return false;
+    // Only highlight if the move is legal
+    const color = this.turn;
+    const tMove = new Move({
+      appear: [
+        new PiPo({
+          x: x,
+          y: y,
+          c: color,
+          // The dropped piece nature has no importance:
+          p: V.KNIGHT
+        })
+      ],
+      vanish: [],
+      start: { x: -1, y: -1 }
+    });
+    this.play(tMove);
+    const moveOk = !this.underCheck(color);
+    this.undo(tMove);
+    return moveOk;
   }
 
   setOtherVariables(fen) {
index 2beb10c..0735b76 100644 (file)
@@ -212,9 +212,6 @@ export default {
       curDiag: "", //for corr moves confirmation
       conn: null,
       roomInitialized: false,
-      // If newmove has wrong index: ask fullgame again:
-      askGameTime: 0,
-      gameIsLoading: false,
       // If asklastate got no reply, ask again:
       gotLastate: false,
       gotMoveIdx: -1, //last move index received
@@ -357,8 +354,6 @@ export default {
       this.rematchOffer = "";
       this.lastate = undefined;
       this.roomInitialized = false;
-      this.askGameTime = 0;
-      this.gameIsLoading = false;
       this.gotLastate = false;
       this.gotMoveIdx = -1;
       this.opponentGotMove = false;
@@ -536,27 +531,6 @@ export default {
       this.$router.push(
         "/game/" + nextGid + "/?next=" + JSON.stringify(this.nextIds));
     },
-    askGameAgain: function() {
-      this.gameIsLoading = true;
-      const currentUrl = document.location.href;
-      const doAskGame = () => {
-        if (document.location.href != currentUrl) return; //page change
-        this.fetchGame((game) => {
-          if (!!game)
-            // This is my game: just reload.
-            this.loadGame(game);
-          else
-            // Just ask fullgame again (once!), this is much simpler.
-            // If this fails, the user could just reload page :/
-            this.send("askfullgame");
-        });
-      };
-      // Delay of at least 2s between two game requests
-      const now = Date.now();
-      const delay = Math.max(2000 - (now - this.askGameTime), 0);
-      this.askGameTime = now;
-      setTimeout(doAskGame, delay);
-    },
     socketMessageListener: function(msg) {
       if (!this.conn) return;
       const data = JSON.parse(msg.data);
@@ -770,76 +744,69 @@ console.log(data.data);
 
           const movePlus = data.data;
           const movesCount = this.game.moves.length;
-          if (movePlus.index > movesCount) {
-            // This can only happen if I'm an observer and missed a move.
-            if (this.gotMoveIdx < movePlus.index)
-              this.gotMoveIdx = movePlus.index;
-            if (!this.gameIsLoading) this.askGameAgain();
+          if (
+            movePlus.index < movesCount ||
+            this.gotMoveIdx >= movePlus.index
+          ) {
+            // Opponent re-send but we already have the move:
+            // (maybe he didn't receive our pingback...)
+            this.send("gotmove", {data: movePlus.index, target: data.from});
           }
           else {
-            if (
-              movePlus.index < movesCount ||
-              this.gotMoveIdx >= movePlus.index
-            ) {
-              // Opponent re-send but we already have the move:
-              // (maybe he didn't receive our pingback...)
-              this.send("gotmove", {data: movePlus.index, target: data.from});
-            } else {
-              this.gotMoveIdx = movePlus.index;
-              const receiveMyMove = (movePlus.color == this.game.mycolor);
-              const moveColIdx = ["w", "b"].indexOf(movePlus.color);
-              if (!receiveMyMove && !!this.game.mycolor) {
-                // Notify opponent that I got the move:
-                this.send(
-                  "gotmove",
-                  { data: movePlus.index, target: data.from }
+            this.gotMoveIdx = movePlus.index;
+            const receiveMyMove = (movePlus.color == this.game.mycolor);
+            const moveColIdx = ["w", "b"].indexOf(movePlus.color);
+            if (!receiveMyMove && !!this.game.mycolor) {
+              // Notify opponent that I got the move:
+              this.send(
+                "gotmove",
+                { data: movePlus.index, target: data.from }
+              );
+              // And myself if I'm elsewhere:
+              if (!this.focus) {
+                notify(
+                  "New move",
+                  {
+                    body:
+                      (this.game.players[moveColIdx].name || "@nonymous") +
+                      " just played."
+                  }
                 );
-                // And myself if I'm elsewhere:
-                if (!this.focus) {
-                  notify(
-                    "New move",
-                    {
-                      body:
-                        (this.game.players[moveColIdx].name || "@nonymous") +
-                        " just played."
-                    }
-                  );
-                }
               }
-              if (movePlus.cancelDrawOffer) {
-                // Opponent refuses draw
-                this.drawOffer = "";
-                // NOTE for corr games: drawOffer reset by player in turn
-                if (
-                  this.game.type == "live" &&
-                  !!this.game.mycolor &&
-                  !receiveMyMove
-                ) {
-                  GameStorage.update(this.gameRef, { drawOffer: "" });
-                }
+            }
+            if (movePlus.cancelDrawOffer) {
+              // Opponent refuses draw
+              this.drawOffer = "";
+              // NOTE for corr games: drawOffer reset by player in turn
+              if (
+                this.game.type == "live" &&
+                !!this.game.mycolor &&
+                !receiveMyMove
+              ) {
+                GameStorage.update(this.gameRef, { drawOffer: "" });
               }
-              this.$refs["basegame"].play(movePlus.move, "received");
-              // Freeze time while the move is being play
-              // (TODO: a callback would be cleaner here)
-              clearInterval(this.clockUpdate);
-              this.clockUpdate = null;
-              const freezeDuration = ["all", "highlight"].includes(V.ShowMoves)
-                // 250 = length of animation, 500 = delay between sub-moves
-                ? 250 + 750 *
-                  (Array.isArray(movePlus.move) ? movePlus.move.length - 1 : 0)
-                // Incomplete information: no move animation
-                : 0;
-              setTimeout(
-                () => {
-                  this.game.clocks[moveColIdx] = movePlus.clock;
-                  this.processMove(
-                    movePlus.move,
-                    { receiveMyMove: receiveMyMove }
-                  );
-                },
-                freezeDuration
-              );
             }
+            this.$refs["basegame"].play(movePlus.move, "received");
+            // Freeze time while the move is being play
+            // (TODO: a callback would be cleaner here)
+            clearInterval(this.clockUpdate);
+            this.clockUpdate = null;
+            const freezeDuration = ["all", "highlight"].includes(V.ShowMoves)
+              // 250 = length of animation, 500 = delay between sub-moves
+              ? 250 + 750 *
+                (Array.isArray(movePlus.move) ? movePlus.move.length - 1 : 0)
+              // Incomplete information: no move animation
+              : 0;
+            setTimeout(
+              () => {
+                this.game.clocks[moveColIdx] = movePlus.clock;
+                this.processMove(
+                  movePlus.move,
+                  { receiveMyMove: receiveMyMove }
+                );
+              },
+              freezeDuration
+            );
           }
           break;
         }
@@ -1261,14 +1228,12 @@ console.log(data.data);
         game
       );
       this.$refs["basegame"].re_setVariables(this.game);
-      if (!this.gameIsLoading) {
-        // Initial loading:
-        this.gotMoveIdx = game.moves.length - 1;
-        // If we arrive here after 'nextGame' action, the board might be hidden
-        let boardDiv = document.querySelector(".game");
-        if (!!boardDiv && boardDiv.style.visibility == "hidden")
-          boardDiv.style.visibility = "visible";
-      }
+      // Initial loading:
+      this.gotMoveIdx = game.moves.length - 1;
+      // If we arrive here after 'nextGame' action, the board might be hidden
+      let boardDiv = document.querySelector(".game");
+      if (!!boardDiv && boardDiv.style.visibility == "hidden")
+        boardDiv.style.visibility = "visible";
       this.re_setClocks();
       this.$nextTick(() => {
         this.game.rendered = true;
@@ -1279,12 +1244,6 @@ console.log(data.data);
         this.lastateAsked = false;
         this.sendLastate(game.oppsid);
       }
-      if (this.gameIsLoading) {
-        this.gameIsLoading = false;
-        if (this.gotMoveIdx >= game.moves.length)
-          // Some moves arrived meanwhile...
-          this.askGameAgain();
-      }
       if (!!callback) callback();
     },
     loadVariantThenGame: async function(game, callback) {
@@ -1512,7 +1471,7 @@ console.log(data.data);
           this.opponentGotMove = false;
           this.send("newmove", {data: sendMove});
           // If the opponent doesn't reply gotmove soon enough, re-send move:
-          // Do this at most 2 times, because mpore would mean network issues,
+          // Do this at most 2 times, because more would mean network issues,
           // opponent would then be expected to disconnect/reconnect.
           let counter = 1;
           const currentUrl = document.location.href;
@@ -1544,6 +1503,7 @@ console.log(data.data);
       };
       if (
         this.game.type == "corr" &&
+        V.CorrConfirm &&
         moveCol == this.game.mycolor &&
         !data.receiveMyMove
       ) {
@@ -1587,7 +1547,8 @@ console.log(data.data);
           });
           document.querySelector("#confirmDiv > .card").style.width =
             boardDiv.offsetWidth + "px";
-        } else {
+        }
+        else {
           // Incomplete information: just ask confirmation
           // Hide the board, because otherwise it could reveal infos
           boardDiv.style.visibility = "hidden";