Some debug, plan several short + long term TODOs
authorBenjamin Auder <benjamin.auder@somewhere>
Thu, 27 Dec 2018 21:05:20 +0000 (22:05 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Thu, 27 Dec 2018 21:05:20 +0000 (22:05 +0100)
TODO
public/javascripts/base_rules.js
public/javascripts/components/game.js
public/javascripts/variants/Marseille.js
public/stylesheets/variant.sass
sockets.js
views/rules/Marseille/en.pug
views/rules/Marseille/fr.pug
views/translations/en.pug
views/translations/es.pug
views/translations/fr.pug

diff --git a/TODO b/TODO
index 37961d7..efdd259 100644 (file)
--- a/TODO
+++ b/TODO
@@ -5,3 +5,20 @@ Promotions: increase pieces sizes, better background.
 Code: use two spaces instead of tabs, everywhere.
 Increase code line length to 100 or more?
 (http://katafrakt.me/2017/09/16/80-characters-line-length-limit/)
+Chat button should be more apparent after game ends (color ?)
+Reinforce security for problems upload (how ?)
+The mode switch between human/computer/friend (+ problem) is a mess
+(example: finished computer game, ongoing friend game, reload, friend game is unreachable)
+
+Later:
+Let choice of time control, allow correspondance play, several games at the same time
+==> need to use indexedDB instead of localStorage. Maybe with Dexie https://dexie.org/
+Each user would have a unique identifier stored in the client DB.
+Allow to cancel games (if opponent doesn't connect again)
+Identity would be browser-based: different games on smartphone, home computer, work computer... (why not ?)
+Index might still look the same, and variant page would have another tab "Games"
+==> running, and finished (which can be deleted from local memory)
+(A true analysis mode could be implemented also, to navigate in completed games --> use a button)
+Allow challenging a specific player (by his chosen name)
+But keep the random pairings as main playing way + always playing in ZEN mode,
+except when accepting an individual challenge.
index 50766b0..8f875e4 100644 (file)
@@ -186,7 +186,8 @@ class ChessRules
                // Argument is a move:
                const move = moveOrSquare;
                const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
-               if (move.appear[0].p == V.PAWN && Math.abs(sx - ex) == 2)
+               // TODO: next conditions are first for Atomic, and third for Checkered
+               if (move.appear.length > 0 && move.appear[0].p == V.PAWN && ["w","b"].includes(move.appear[0].c) && Math.abs(sx - ex) == 2)
                {
                        return {
                                x: (sx + ex)/2,
@@ -1372,15 +1373,22 @@ class ChessRules
        getPGN(mycolor, score, fenStart, mode)
        {
                let pgn = "";
-               pgn += '[Site "vchess.club"]<br>';
+               pgn += '[Site "vchess.club"]\n';
                const opponent = mode=="human" ? "Anonymous" : "Computer";
-               pgn += '[Variant "' + variant + '"]<br>';
-               pgn += '[Date "' + getDate(new Date()) + '"]<br>';
-               pgn += '[White "' + (mycolor=='w'?'Myself':opponent) + '"]<br>';
-               pgn += '[Black "' + (mycolor=='b'?'Myself':opponent) + '"]<br>';
-               pgn += '[FenStart "' + fenStart + '"]<br>';
-               pgn += '[Fen "' + this.getFen() + '"]<br>';
-               pgn += '[Result "' + score + '"]<br><br>';
+               pgn += '[Variant "' + variant + '"]\n';
+               pgn += '[Date "' + getDate(new Date()) + '"]\n';
+               // TODO: later when users are a bit less anonymous, use better names
+               const whiteName = ["human","computer"].includes(mode)
+                       ? (mycolor=='w'?'Myself':opponent)
+                       : "analyze";
+               const blackName = ["human","computer"].includes(mode)
+                       ? (mycolor=='b'?'Myself':opponent)
+                       : "analyze";
+               pgn += '[White "' + whiteName + '"]\n';
+               pgn += '[Black "' + blackName + '"]\n';
+               pgn += '[FenStart "' + fenStart + '"]\n';
+               pgn += '[Fen "' + this.getFen() + '"]\n';
+               pgn += '[Result "' + score + '"]\n\n';
 
                // Standard PGN
                for (let i=0; i<this.moves.length; i++)
@@ -1389,7 +1397,7 @@ class ChessRules
                                pgn += ((i/2)+1) + ".";
                        pgn += this.moves[i].notation[0] + " ";
                }
-               pgn += "<br><br>";
+               pgn += "\n\n";
 
                // "Complete moves" PGN (helping in ambiguous cases)
                for (let i=0; i<this.moves.length; i++)
@@ -1399,6 +1407,6 @@ class ChessRules
                        pgn += this.moves[i].notation[1] + " ";
                }
 
-               return pgn;
+               return pgn + "\n";
        }
 }
index 4c369e4..58fd365 100644 (file)
@@ -57,7 +57,7 @@ Vue.component('my-game', {
                                        "tooltip": true,
                                        "play": true,
                                        "seek": this.seek,
-                                       "playing": this.mode == "human",
+                                       "playing": this.mode == "human" && this.score == "*",
                                },
                        },
                        [h('i', { 'class': { "material-icons": true } }, "accessibility")])
@@ -73,7 +73,7 @@ Vue.component('my-game', {
                                        'class': {
                                                "tooltip":true,
                                                "play": true,
-                                               "playing": this.mode == "computer",
+                                               "playing": this.mode == "computer" && this.score == "*",
                                                "spaceleft": true,
                                        },
                                },
@@ -865,41 +865,7 @@ Vue.component('my-game', {
                        actionArray
                );
                elementArray.push(actions);
-               if (this.score != "*" && this.pgnTxt.length > 0)
-               {
-                       elementArray.push(
-                               h('div',
-                                       {
-                                               attrs: { id: "pgn-div" },
-                                               "class": { "section-content": true },
-                                       },
-                                       [
-                                               h('a',
-                                                       {
-                                                               attrs: {
-                                                                       id: "download",
-                                                                       href: "#",
-                                                               }
-                                                       }
-                                               ),
-                                               h('p',
-                                                       {
-                                                               attrs: { id: "pgn-game" },
-                                                               domProps: { innerHTML: this.pgnTxt }
-                                                       }
-                                               ),
-                                               h('button',
-                                                       {
-                                                               attrs: { "id": "downloadBtn" },
-                                                               on: { click: this.download },
-                                                               domProps: { innerHTML: translations["Download game"] },
-                                                       }
-                                               ),
-                                       ]
-                               )
-                       );
-               }
-               else if (this.mode != "idle")
+               if (!!this.vr)
                {
                        if (this.mode == "problem")
                        {
@@ -949,6 +915,31 @@ Vue.component('my-game', {
                                        )
                                );
                        }
+                       elementArray.push(
+                               h('div',
+                                       {
+                                               attrs: { id: "pgn-div" },
+                                               "class": { "section-content": true },
+                                       },
+                                       [
+                                               h('a',
+                                                       {
+                                                               attrs: {
+                                                                       id: "download",
+                                                                       href: "#",
+                                                               }
+                                                       }
+                                               ),
+                                               h('button',
+                                                       {
+                                                               attrs: { "id": "downloadBtn" },
+                                                               on: { click: this.download },
+                                                               domProps: { innerHTML: translations["Download PGN"] },
+                                                       }
+                                               ),
+                                       ]
+                               )
+                       );
                }
                return h(
                        'div',
@@ -1008,7 +999,7 @@ Vue.component('my-game', {
                };
                const socketMessageListener = msg => {
                        const data = JSON.parse(msg.data);
-                       const L = (!!this.vr ? this.vr.moves.length : 0);
+                       let L = undefined;
                        switch (data.code)
                        {
                                case "oppname":
@@ -1028,7 +1019,7 @@ Vue.component('my-game', {
                                        break;
                                case "newgame": //opponent found
                                        // oppid: opponent socket ID
-                                       this.newGame("human", data.fen, data.color, data.oppid);
+                                       this.newGame("human", data.fen, data.color, data.oppid, data.gameid);
                                        break;
                                case "newmove": //..he played!
                                        this.play(data.move, (variant!="Dark" ? "animate" : null));
@@ -1038,6 +1029,7 @@ Vue.component('my-game', {
                                                break; //games IDs don't match: definitely over...
                                        this.oppConnected = true;
                                        // Send our "last state" informations to opponent
+                                       L = this.vr.moves.length;
                                        this.conn.send(JSON.stringify({
                                                code: "lastate",
                                                oppid: this.oppid,
@@ -1047,10 +1039,11 @@ Vue.component('my-game', {
                                        }));
                                        break;
                                case "lastate": //got opponent infos about last move
+                                       L = this.vr.moves.length;
                                        if (this.gameId != data.gameId)
                                                break; //games IDs don't match: nothing we can do...
                                        // OK, opponent still in game (which might be over)
-                                       if (this.mode != "human")
+                                       if (this.score != "*")
                                        {
                                                // We finished the game (any result possible)
                                                this.conn.send(JSON.stringify({
@@ -1068,6 +1061,7 @@ Vue.component('my-game', {
                                                this.conn.send(JSON.stringify({
                                                        code: "lastate",
                                                        oppid: this.oppid,
+                                                       gameId: this.gameId,
                                                        lastMove: this.vr.moves[L-1],
                                                        movesCount: L,
                                                }));
@@ -1164,13 +1158,12 @@ Vue.component('my-game', {
                                        : "none";
                },
                download: function() {
-                       let content = document.getElementById("pgn-game").innerHTML;
-                       content = content.replace(/<br>/g, "\n");
+                       // Variants may have special PGN structure (so next function isn't defined here)
+                       const content = this.vr.getPGN(this.mycolor, this.score, this.fenStart, this.mode);
                        // Prepare and trigger download link
                        let downloadAnchor = document.getElementById("download");
                        downloadAnchor.setAttribute("download", "game.pgn");
-                       downloadAnchor.href = "data:text/plain;charset=utf-8," +
-                               encodeURIComponent(content);
+                       downloadAnchor.href = "data:text/plain;charset=utf-8," + encodeURIComponent(content);
                        downloadAnchor.click();
                },
                showScoreMsg: function() {
@@ -1186,8 +1179,6 @@ Vue.component('my-game', {
                                localStorage.setItem(prefix+"score", score);
                        }
                        this.showScoreMsg();
-                       // Variants may have special PGN structure (so next function isn't defined here)
-                       this.pgnTxt = this.vr.getPGN(this.mycolor, this.score, this.fenStart, this.mode);
                        if (this.mode == "human" && this.oppConnected)
                        {
                                // Send our nickname to opponent
@@ -1311,7 +1302,7 @@ Vue.component('my-game', {
                        }
                        this.endGame(this.mycolor=="w"?"0-1":"1-0");
                },
-               newGame: function(mode, fenInit, color, oppId) {
+               newGame: function(mode, fenInit, color, oppId, gameId) {
                        const fen = fenInit || VariantRules.GenRandInitFen();
                        console.log(fen); //DEBUG
                        if (mode=="human" && !oppId)
@@ -1325,7 +1316,7 @@ Vue.component('my-game', {
                                }
                                // Send game request and wait..
                                try {
-                                       this.conn.send(JSON.stringify({code:"newgame", fen:fen}));
+                                       this.conn.send(JSON.stringify({code:"newgame", fen:fen, gameid: getRandString() }));
                                } catch (INVALID_STATE_ERR) {
                                        return; //nothing achieved
                                }
@@ -1374,12 +1365,10 @@ Vue.component('my-game', {
                        this.mode = mode;
                        this.incheck = [];
                        this.fenStart = V.ParseFen(fen).position; //this is enough
-                       if (mode != "problem")
-                               this.setStorage(); //store game state in case of interruptions
                        if (mode=="human")
                        {
                                // Opponent found!
-                               this.gameId = getRandString();
+                               this.gameId = gameId;
                                this.oppid = oppId;
                                this.oppConnected = true;
                                this.mycolor = color;
@@ -1398,6 +1387,8 @@ Vue.component('my-game', {
                        else if (mode == "friend")
                                this.mycolor = "w"; //convention...
                        //else: problem solving: nothing more to do
+                       if (mode != "problem")
+                               this.setStorage(); //store game state in case of interruptions
                },
                continueGame: function(mode) {
                        this.mode = mode;
index 2eb4260..f372698 100644 (file)
@@ -70,7 +70,6 @@ class MarseilleRules extends ChessRules
                const firstRank = (color == 'w' ? sizeX-1 : 0);
                const startRank = (color == "w" ? sizeX-2 : 1);
                const lastRank = (color == "w" ? 0 : sizeX-1);
-               const pawnColor = this.getColor(x,y); //can be different for checkered
                const finalPieces = x + shiftX == lastRank
                        ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
                        : [V.PAWN];
@@ -81,7 +80,7 @@ class MarseilleRules extends ChessRules
                        for (let piece of finalPieces)
                        {
                                moves.push(this.getBasicMove([x,y], [x+shiftX,y],
-                                       {c:pawnColor,p:piece}));
+                                       {c:color,p:piece}));
                        }
                        // Next condition because pawns on 1st rank can generally jump
                        if ([startRank,firstRank].includes(x)
@@ -101,7 +100,7 @@ class MarseilleRules extends ChessRules
                                for (let piece of finalPieces)
                                {
                                        moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
-                                               {c:pawnColor,p:piece}));
+                                               {c:color,p:piece}));
                                }
                        }
                }
@@ -118,6 +117,7 @@ class MarseilleRules extends ChessRules
                });
                if (epSqs.length == 0)
                        return moves;
+               const oppCol = this.getOppCol(color);
                for (let sq of epSqs)
                {
                        if (this.subTurn == 1 || (epSqs.length == 2 &&
@@ -125,14 +125,16 @@ class MarseilleRules extends ChessRules
                                // (Or maybe the opponent filled the en-passant square with a piece)
                                this.board[epSqs[0].x][epSqs[0].y] != V.EMPTY))
                        {
-                               if (sq.x == x+shiftX && Math.abs(sq.y - y) == 1)
+                               if (sq.x == x+shiftX && Math.abs(sq.y - y) == 1
+                                       // Add condition "enemy pawn must be present"
+                                       && this.getPiece(x,sq.y) == V.PAWN && this.getColor(x,sq.y) == oppCol)
                                {
                                        let epMove = this.getBasicMove([x,y], [sq.x,sq.y]);
                                        epMove.vanish.push({
                                                x: x,
                                                y: sq.y,
                                                p: 'p',
-                                               c: this.getColor(x,sq.y)
+                                               c: oppCol
                                        });
                                        moves.push(epMove);
                                }
@@ -320,15 +322,21 @@ class MarseilleRules extends ChessRules
        getPGN(mycolor, score, fenStart, mode)
        {
                let pgn = "";
-               pgn += '[Site "vchess.club"]<br>';
+               pgn += '[Site "vchess.club"]\n';
                const opponent = mode=="human" ? "Anonymous" : "Computer";
-               pgn += '[Variant "' + variant + '"]<br>';
-               pgn += '[Date "' + getDate(new Date()) + '"]<br>';
-               pgn += '[White "' + (mycolor=='w'?'Myself':opponent) + '"]<br>';
-               pgn += '[Black "' + (mycolor=='b'?'Myself':opponent) + '"]<br>';
-               pgn += '[FenStart "' + fenStart + '"]<br>';
-               pgn += '[Fen "' + this.getFen() + '"]<br>';
-               pgn += '[Result "' + score + '"]<br><br>';
+               pgn += '[Variant "' + variant + '"]\n';
+               pgn += '[Date "' + getDate(new Date()) + '"]\n';
+               const whiteName = ["human","computer"].includes(mode)
+                       ? (mycolor=='w'?'Myself':opponent)
+                       : "analyze";
+               const blackName = ["human","computer"].includes(mode)
+                       ? (mycolor=='b'?'Myself':opponent)
+                       : "analyze";
+               pgn += '[White "' + whiteName + '"]\n';
+               pgn += '[Black "' + blackName + '"]\n';
+               pgn += '[FenStart "' + fenStart + '"]\n';
+               pgn += '[Fen "' + this.getFen() + '"]\n';
+               pgn += '[Result "' + score + '"]\n\n';
 
                let counter = 1;
                let i = 0;
@@ -344,7 +352,7 @@ class MarseilleRules extends ChessRules
                                pgn += move + (i < this.moves.length-1 ? " " : "");
                        }
                }
-               pgn += "<br><br>";
+               pgn += "\n\n";
 
                // "Complete moves" PGN (helping in ambiguous cases)
                counter = 1;
@@ -362,7 +370,7 @@ class MarseilleRules extends ChessRules
                        }
                }
 
-               return pgn;
+               return pgn + "\n";
        }
 }
 
index df26d58..15c1996 100644 (file)
@@ -248,6 +248,7 @@ img.ghost
 
 #fen-string
   margin-top: 0
+  margin-bottom: 10px
 
 #pgn-game
   margin-top: 0
@@ -261,7 +262,7 @@ img.ghost
 #pgn-div > a
   display: none
 
-#fen-div > p
+//#fen-div > p
   margin-left: 0
   margin-right: 0
 
index 11fe91c..e411050 100644 (file)
@@ -100,20 +100,21 @@ module.exports = function(wss) {
                                                                        // Start a new game
                                                                        const oppId = games[page]["id"];
                                                                        const fen = games[page]["fen"];
+                                                                       const gameId = games[page]["gameid"];
                                                                        delete games[page];
-                                                                       const mycolor = Math.random() < 0.5 ? 'w' : 'b';
+                                                                       const mycolor = (Math.random() < 0.5 ? 'w' : 'b');
                                                                        socket.send(JSON.stringify(
-                                                                               {code:"newgame",fen:fen,oppid:oppId,color:mycolor}));
+                                                                               {code:"newgame",fen:fen,oppid:oppId,color:mycolor,gameid:gameId}));
                                                                        if (!!clients[page][oppId])
                                                                        {
                                                                                clients[page][oppId].send(
                                                                                        JSON.stringify(
-                                                                                               {code:"newgame",fen:fen,oppid:sid,color:mycolor=="w"?"b":"w"}),
+                                                                                               {code:"newgame",fen:fen,oppid:sid,color:mycolor=="w"?"b":"w",gameid:gameId}),
                                                                                        noop);
                                                                        }
                                                                }
                                                                else
-                                                                       games[page] = {id:sid, fen:obj.fen}; //wait for opponent
+                                                                       games[page] = {id:sid, fen:obj.fen, gameid:obj.gameid}; //wait for opponent
                                                                break;
                                                        case "cancelnewgame": //if a user cancel his seek
                                                                delete games[page];
index a390e9d..503d51e 100644 (file)
@@ -54,6 +54,8 @@ p.
        Note: if a pawn 2-squares jump was made and then a piece landed at the
        en-passant square at the second move, a pawn capture on this square
        takes only the piece.
+       And, if a pawn advanced twice then en-passant capture
+       on its first movement is impossible (the pawn is now "too far").
 
 h3 More information
 
index 51c0fcd..c805a62 100644 (file)
@@ -46,6 +46,8 @@ p.
        Note : si un pion se déplace de deux cases puis qu'une pièce occupe la case de
        prise en passant au second coup d'un tour, une capture sur cette case ne
        prendra que la pièce.
+       Et, si un pion a avancé deux fois la prise en passant sur son premier
+       déplacement est impossible (le pion est "trop loin" désormais).
 
 h3 Plus d'information
 
index f07764b..10a82cd 100644 (file)
@@ -59,7 +59,7 @@
                "Chat with ": "Chat with ",
                "Type here": "Type here",
                "Send": "Send",
-               "Download game": "Download game",
+               "Download PGN": "Download PGN",
                "Show solution": "Show solution",
                "Load previous problems": "Load previous problems",
                "Load next problems": "Load next problems",
index b589fb1..98d8b06 100644 (file)
@@ -59,7 +59,7 @@
                "Chat with ": "Hablar con ",
                "Type here": "Escribe aqui",
                "Send": "Enviar",
-               "Download game": "Descargar la partida",
+               "Download PGN": "Descargar el PGN",
                "Show solution": "Mostrar la solucion",
                "Load previous problems": "Cargar los problemas anteriores",
                "Load next problems": "Cargar los siguientes problemas",
index 8a86df3..b76e4d2 100644 (file)
@@ -59,7 +59,7 @@
                "Chat with ": "Discuter avec ",
                "Type here": "Écrivez ici",
                "Send": "Envoyer",
-               "Download game": "Télécharger la partie",
+               "Download PGN": "Télécharger le PGN",
                "Show solution": "Montrer la solution",
                "Load previous problems": "Charger les problèmes précédents",
                "Load next problems": "Charger les problèmes suivants",