Play computer move in webworker to not freeze interface
authorBenjamin Auder <benjamin.auder@somewhere>
Tue, 18 Dec 2018 18:08:14 +0000 (19:08 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Tue, 18 Dec 2018 18:08:14 +0000 (19:08 +0100)
22 files changed:
TODO [deleted file]
db/create.sql
public/javascripts/components/game.js
public/javascripts/playCompMove.js [new file with mode: 0644]
public/javascripts/variant.js
public/javascripts/variants/Alice.js
public/javascripts/variants/Antiking.js
public/javascripts/variants/Atomic.js
public/javascripts/variants/Checkered.js
public/javascripts/variants/Chess960.js
public/javascripts/variants/Crazyhouse.js
public/javascripts/variants/Extinction.js
public/javascripts/variants/Grand.js
public/javascripts/variants/Loser.js
public/javascripts/variants/Magnetic.js
public/javascripts/variants/Switching.js
public/javascripts/variants/Ultima.js
public/javascripts/variants/Wildebeest.js
public/javascripts/variants/Zen.js
public/stylesheets/variant.sass
views/rules/Ultima.pug
views/variant.pug

diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index 4b311b0..0000000
--- a/TODO
+++ /dev/null
@@ -1,2 +0,0 @@
-global lang cookie, + display (remember in each variant what is shown...)
-translations (how ? switch on index page only, then find ideas...)
index 36bda8a..033df4b 100644 (file)
@@ -21,6 +21,7 @@ insert into Variants values
        ('Ultima', 'Exotic captures');
 
 create table Problems (
+       num integer primary key,
        added datetime,
        variant varchar,
        fen varchar,
index 186c027..b242ee2 100644 (file)
@@ -22,6 +22,9 @@ Vue.component('my-game', {
                        color: getCookie("color", "lichess"), //lichess, chesscom or chesstempo
                        // sound level: 0 = no sound, 1 = sound only on newgame, 2 = always
                        sound: parseInt(getCookie("sound", "2")),
+                       // Web worker to play computer moves without freezing interface:
+                       compWorker: new Worker('/javascripts/playCompMove.js'),
+                       timeStart: undefined, //time when computer starts thinking
                };
        },
        watch: {
@@ -146,7 +149,7 @@ Vue.component('my-game', {
                                        'class': {
                                                "tooltip": true,
                                                "topindicator": true,
-                                               "indic-right": true,
+                                               "indic-left": true,
                                                "settings-btn": !smallScreen,
                                                "settings-btn-small": smallScreen,
                                        },
@@ -965,6 +968,19 @@ Vue.component('my-game', {
                                        this.play();
                        }
                };
+               // Computer moves web worker logic:
+               this.compWorker.postMessage(["scripts",variant]);
+               const self = this;
+               this.compWorker.onmessage = function(e) {
+                       const compMove = e.data;
+                       // (first move) HACK: small delay to avoid selecting elements
+                       // before they appear on page:
+                       const delay = Math.max(500-(Date.now()-self.timeStart), 0);
+                       setTimeout(() => {
+                               if (self.mode == "computer") //Warning: mode could have changed!
+                                       self.play(compMove, "animate")
+                       }, delay);
+               }
        },
        methods: {
                toggleShowSolution: function() {
@@ -1161,23 +1177,16 @@ Vue.component('my-game', {
                        }
                        else if (mode == "computer")
                        {
+                               this.compWorker.postMessage(["init",this.vr.getFen()]);
                                this.mycolor = Math.random() < 0.5 ? 'w' : 'b';
                                if (this.mycolor == 'b')
-                                       setTimeout(this.playComputerMove, 100); //small delay for drawing board
+                                       this.playComputerMove();
                        }
                        //else: against a (IRL) friend or problem solving: nothing more to do
                },
                playComputerMove: function() {
-                       const timeStart = Date.now();
-                       // TODO: next call asynchronous (avoid freezing interface while computer "think").
-                       // This would also allow to remove some artificial setTimeouts
-                       const compMove = this.vr.getComputerMove();
-                       // (first move) HACK: avoid selecting elements before they appear on page:
-                       const delay = Math.max(250-(Date.now()-timeStart), 0);
-                       setTimeout(() => {
-                               if (this.mode == "computer") //Warning: mode could have changed!
-                                       this.play(compMove, "animate")
-                       }, delay);
+                       this.timeStart = Date.now();
+                       this.compWorker.postMessage(["askmove"]);
                },
                // Get the identifier of a HTML table cell from its numeric coordinates o.x,o.y.
                getSquareId: function(o) {
@@ -1339,6 +1348,11 @@ Vue.component('my-game', {
                        {
                                this.incheck = this.vr.getCheckSquares(move); //is opponent in check?
                                this.vr.play(move, "ingame");
+                               if (this.mode == "computer")
+                               {
+                                       // Send the move to web worker
+                                       this.compWorker.postMessage(["newmove",move]);
+                               }
                        }
                        else
                        {
@@ -1363,7 +1377,7 @@ Vue.component('my-game', {
                                }
                        }
                        if (this.mode == "computer" && this.vr.turn != this.mycolor)
-                               setTimeout(this.playComputerMove, 250); //small delay for animation
+                               this.playComputerMove();
                },
                undo: function() {
                        // Navigate after game is over
diff --git a/public/javascripts/playCompMove.js b/public/javascripts/playCompMove.js
new file mode 100644 (file)
index 0000000..fc69ce2
--- /dev/null
@@ -0,0 +1,26 @@
+// For asynchronous computer move search
+onmessage = function(e)
+{
+       switch (e.data[0])
+       {
+               case "scripts":
+                       self.importScripts(
+                               '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js',
+                               '/javascripts/base_rules.js',
+                               '/javascripts/utils/array.js',
+                               '/javascripts/variants/' + e.data[1] + '.js');
+                       self.V = VariantRules;
+                       break;
+               case "init":
+                       const fen = e.data[1];
+                       self.vr = new VariantRules(fen, []);
+                       break;
+               case "newmove":
+                       self.vr.play(e.data[1]);
+                       break;
+               case "askmove":
+                       const compMove = self.vr.getComputerMove();
+                       postMessage(compMove);
+                       break;
+       }
+}
index 3aa2f68..e9e956c 100644 (file)
@@ -1,15 +1,21 @@
 new Vue({
        el: "#variantPage",
        data: {
-               display: "", //do not show anything...
+               display: getCookie("display-"+variant,""), //default: do not show anything...
                problem: undefined, //current problem in view
        },
        methods: {
                toggleDisplay: function(elt) {
                        if (this.display == elt)
+                       {
                                this.display = ""; //hide
+                               setCookie("display-"+variant, "");
+                       }
                        else
+                       {
                                this.display = elt; //show
+                               setCookie("display-"+variant, elt);
+                       }
                },
                showProblem: function(problemTxt) {
                        this.problem = JSON.parse(problemTxt);
index 00103a7..f7d2fa1 100644 (file)
@@ -336,3 +336,5 @@ class AliceRules extends ChessRules
                return notation;
        }
 }
+
+const VariantRules = AliceRules;
index 38cadf2..0d34271 100644 (file)
@@ -204,3 +204,5 @@ class AntikingRules extends ChessRules
                        " w 1111";
        }
 }
+
+const VariantRules = AntikingRules;
index e2f8ea3..0275805 100644 (file)
@@ -147,3 +147,5 @@ class AtomicRules extends ChessRules
                return color == "w" ? "0-1" : "1-0"; //checkmate
        }
 }
+
+const VariantRules = AtomicRules;
index 314cd8e..d73cef4 100644 (file)
@@ -303,3 +303,5 @@ class CheckeredRules extends ChessRules
                }
        }
 }
+
+const VariantRules = CheckeredRules;
index 1c8292c..529ad99 100644 (file)
@@ -2,3 +2,5 @@ class Chess960Rules extends ChessRules
 {
        // Standard rules
 }
+
+const VariantRules = Chess960Rules;
index 00452cb..afebcc3 100644 (file)
@@ -266,3 +266,5 @@ class CrazyhouseRules extends ChessRules
                return "@" + V.CoordsToSquare(move.end);
        }
 }
+
+const VariantRules = CrazyhouseRules;
index 49d61b2..bc06162 100644 (file)
@@ -135,3 +135,5 @@ class ExtinctionRules extends ChessRules
                return super.evalPosition();
        }
 }
+
+const VariantRules = ExtinctionRules;
index b598f2d..ec2afd8 100644 (file)
@@ -358,3 +358,5 @@ class GrandRules extends ChessRules
                        " w 1111 -";
        }
 }
+
+const VariantRules = GrandRules;
index 5f15612..6731d32 100644 (file)
@@ -193,3 +193,5 @@ class LoserRules extends ChessRules
                        " w -"; //no en-passant
        }
 }
+
+const VariantRules = LoserRules;
index 225489e..17efeaa 100644 (file)
@@ -281,3 +281,5 @@ class MagneticRules extends ChessRules
                return 500; //checkmates evals may be slightly below 1000
        }
 }
+
+const VariantRules = MagneticRules;
index ee84e4c..e53ab69 100644 (file)
@@ -137,3 +137,5 @@ class SwitchingRules extends ChessRules
                return "S" + startSquare + finalSquare;
        }
 }
+
+const VariantRules = SwitchingRules;
index 7f4a7eb..04d62b7 100644 (file)
@@ -1,8 +1,8 @@
 class UltimaRules extends ChessRules
 {
-       static get HasFlags { return false; }
+       static get HasFlags() { return false; }
 
-       static get HasEnpassant { return false; }
+       static get HasEnpassant() { return false; }
 
        static getPpath(b)
        {
@@ -626,3 +626,5 @@ class UltimaRules extends ChessRules
                return notation;
        }
 }
+
+const VariantRules = UltimaRules;
index 4194521..783eb44 100644 (file)
@@ -281,3 +281,5 @@ class WildebeestRules extends ChessRules
                        " w 1111 -";
        }
 }
+
+const VariantRules = WildebeestRules;
index da4dd7a..b090a29 100644 (file)
@@ -226,3 +226,5 @@ class ZenRules extends ChessRules
                }
        }
 }
+
+const VariantRules = ZenRules;
index 856f22b..a0d4f2a 100644 (file)
@@ -280,3 +280,6 @@ ul:not(.browser-default) > li
 
 #solution-div h3
   cursor: pointer
+
+.newproblem-form, .newproblem-preview
+  max-width: 90%
index cf5da24..8255a5e 100644 (file)
@@ -103,7 +103,7 @@ p.
 
 figure.diagram-container
        .diagram
-               | fen:2n4k/3r4/5b2/3p4/1m6/3b4/3N4/K7 d4,d6,d8,a5:
+               | fen:2n4k/3r4/5b2/3p4/1m6/3b4/3N4/K7 d4,d6,d8,a5:
        figcaption All marked squares captures are playable from d2.
 
 h4 Withdrawer (queen)
@@ -114,7 +114,7 @@ p.
 
 figure.diagram-container
        .diagram
-               | fen:7k/8/8/3Qr3/8/8/8/K7 a5,b5,c5:
+               | fen:7k/8/8/3Qr3/8/8/8/K7 a5,b5,c5:
        figcaption 1.Qa5, 1.Qb5 or 1.Qc5 captures the black rook.
 
 h4 Chameleon (bishop)
@@ -129,7 +129,7 @@ p ...and these captures can be combined.
 
 figure.diagram-container
        .diagram
-               | fen:7k/8/8/r3pP2/2n5/8/B7/K7 a5,c4,e5:
+               | fen:7k/8/8/r3pP2/2n5/8/B7/K7 a5,c4,e5:
        figcaption 1.Bd5 captures all marked pieces.
 
 p.
@@ -150,7 +150,7 @@ p.
 
 figure.diagram-container
        .diagram
-               | fen:7k/8/8/p4r/4K3/8/8/8 e5:
+               | fen:7k/8/8/p4r/4K3/8/8/8 e5:
        figcaption 1.Ke5 is impossible
 
 h3 Credits
index b1caabd..d3581ae 100644 (file)
@@ -32,7 +32,6 @@ block javascripts
        script(src="/javascripts/base_rules.js")
        script(src="/javascripts/variants/" + variant + ".js")
        script.
-               const VariantRules = #{variant}Rules;
                const V = VariantRules; //because this variable is often used
                const variant = "#{variant}";
                const problemArray = !{JSON.stringify(problemArray)};