Enable config for expectimax. Fix weight function for expectimax.
authorOleksandr Gavenko <gavenkoa@gmail.com>
Thu, 18 Sep 2014 00:39:41 +0300
changeset 74 93cb48b73b39
parent 73 a50828ac954d
child 75 ab74c80beffd
Enable config for expectimax. Fix weight function for expectimax.
2048.html
ai.js
--- a/2048.html	Wed Sep 17 23:30:02 2014 +0300
+++ b/2048.html	Thu Sep 18 00:39:41 2014 +0300
@@ -103,6 +103,9 @@
       border: 1px solid green;
       padding: 2px;
     }
+    table.report-by-maxval > tr > td:nth-child(3) {
+      background-color: yellow;
+    }
   </style>
 </head>
 <body>
@@ -231,6 +234,28 @@
       <div class="ai" id="ai-expectimax">
         <button class="ai">enable</button>
         <h5>expectimax</h5>
+        <div class="option">
+          <input type="text" name="depth" class="positive" pattern="[0-9]*" value="3"/> recursion depth
+        </div>
+        <div class="option">
+          <input type="text" name="balance" class="positive" pattern="[0-9]*[.]?[0-9]*" value=".9"/> probability of 2
+        </div>
+        <div class="option">
+          <input type="text" name="scoreCoef" class="positive" pattern="[0-9]*[.]?[0-9]*" value="1"/> score weight
+        </div>
+        <div class="option">
+          <input type="text" name="maxValCoef" class="positive" pattern="[0-9]*[.]?[0-9]*" value="0"/> max value weight
+        </div>
+        <div class="option">
+          <input type="text" name="cornerBonus" class="positive" pattern="[0-9]*[.]?[0-9]*" value="100"/> max value at corner bonus
+        </div>
+        <div class="option">
+          <input type="text" name="edgeBonus" class="positive" pattern="[0-9]*[.]?[0-9]*" value="0"/> max value at edge bonus
+        </div>
+        <div class="option">
+          <input type="text" name="freeBonus" class="positive" pattern="[0-9]*[.]?[0-9]*" value="10"/> free cell coefficient
+        </div>
+        <div class="clearfix"></div>
       </div>
     </div>
   </div>
@@ -624,8 +649,10 @@
       "ai-deep-max-score-corner": function() {
         return new ai.DeepMaxScoreCorner(ui.brdEngine);
       },
-      "ai-expectimax": function() {
-        return new ai.expectimax(ui.brdEngine);
+      "ai-expectimax": function(aiDom) {
+        var cfg = {};
+        ui.ai.parseCfg(aiDom, cfg);
+        return new ai.expectimax(ui.brdEngine, cfg);
       },
       // "": function() {
       //   return new ai.(ui.brdEngine);
--- a/ai.js	Wed Sep 17 23:30:02 2014 +0300
+++ b/ai.js	Thu Sep 18 00:39:41 2014 +0300
@@ -378,33 +378,31 @@
  * @constructor */
 ai.expectimax = function(brdEngine, cfg) {
     this.brdEngine = brdEngine;
-    this.cfg = cfg || {};
-    this.cfg.balance = this.cfg.balance || .9;
+    this.cfg = ai.copyObj(ai.expectimax.bestCfg);
+    ai.copyObj(cfg, this.cfg);
+    if (this.cfg.balance <= 0)
+        this.cfg.balance = ai.expectimax.bestCfg.balance;
+    if ( this.cfg.balance > 1)
+        this.cfg.balance = 1;
     if (!this.cfg.depth || this.cfg.depth < 0 || 8 <= this.cfg.depth)
-        this.cfg.depth = 5;
-    this.cfg.cornerBonus = this.cfg.cornerBonus || 20000;
-    this.cfg.edgeBonus = this.cfg.edgeBonus || 100;
-    this.cfg.freeBonus = this.cfg.edgeBonus || 100;
-    this.cfg.weightPriority = this.cfg.weightPriority || 10;
+        this.cfg.depth = ai.expectimax.bestCfg.depth;
 }
-ai.expectimax.prototype.lvl1Score = function(brd) {
-    var score = brd.score();
+ai.expectimax.bestCfg = {balance: .9, depth: 3, scoreCoef: 1, maxValCoef: 0, cornerBonus: 0, edgeBonus: 0, freeBonus: 0, weightPriority: 10};
+ai.expectimax.prototype.weight = function(brd) {
+    var score = 0;
+    if (this.cfg.scoreCoef > 0)
+        score += this.cfg.scoreCoef * brd.score();
     var max = brd.max();
-    if (brd.atCorner(max))
-        score += this.cfg.cornerBonus;
-    else if (brd.atEdge(max))
-        score += this.cfg.edgeBonus;
-    score += brd.free() * this.cfg.freeBonus;
-    return score;
-}
-ai.expectimax.prototype.lvlnScore = function(brd) {
-    var score = brd.score();
-    // var max = brd.max();
-    // if (brd.atCorner(max))
-    //     score += this.cfg.cornerBonus;
-    // else if (brd.atEdge(max))
-    //     score += this.cfg.edgeBonus;
-    // score += brd.free() * this.cfg.freeBonus;
+    if (this.cfg.maxValCoef > 0)
+        score += this.cfg.maxValCoef * max;
+    if (this.cfg.cornerBonus > 0)
+        if (brd.atCorner(max))
+            score += this.cfg.cornerBonus;
+    if (this.cfg.edgeBonus > 0)
+        if (brd.atEdge(max))
+            score += this.cfg.edgeBonus;
+    if (this.cfg.freeBonus > 0)
+        score += this.cfg.freeBonus * brd.free();
     return score;
 }
 ai.expectimax.prototype.analyse = function(brd) {
@@ -417,10 +415,13 @@
     for (var i = 0; i < ai.dirs.length; i++) {
         var dir = ai.dirs[i];
         if (origBrd[dir](nextBrd)) {
-            nextScore = this.lvl1Score(nextBrd);
-            var weight = this.weight(nextBrd, 0);
-            // console.log("dir: %o, prevScore: %o, nextScore: %o, maxWeight: %o, weight: %o", dir, prevScore, nextScore, maxWeight, weight);
-            if (maxWeight + this.cfg.weightPriority < weight || (maxWeight <= weight && prevScore < nextScore)) {
+            var weight = this.recWeight(nextBrd, 1);
+            var ok = (weight - maxWeight) > this.cfg.weightPriority;
+            if ( ! ok && maxWeight <= weight) {
+                nextScore = this.weight(nextBrd);
+                ok = prevScore < nextScore;
+            }
+            if (ok) {
                 prevScore = nextScore;
                 maxWeight = weight;
                 bestDir = dir;
@@ -430,9 +431,9 @@
     this.cleanup();
     return bestDir;
 }
-ai.expectimax.prototype.weight = function(brd, depth) {
-    if (depth === this.cfg.depth)
-        return this.lvlnScore(brd);
+ai.expectimax.prototype.recWeight = function(brd, depth) {
+    if (depth >= this.cfg.depth)
+        return this.weight(brd);
     if (this.cache[depth]) {
         var cache = this.cache[depth];
         for (var i = cache.length-1; i >= 0; i--) {
@@ -453,24 +454,26 @@
                 var n = 0, w = 0;
                 for (var diri = 0; diri < ai.dirs.length; diri++) {
                     if (randBoard[ai.dirs[diri]](nextBrd)) {
-                        w += this.weight(nextBrd, depth+1);
+                        w += this.recWeight(nextBrd, depth+1);
                         n++;
                     }
                 }
                 if (n > 0)
                     w = w / n;
                 weight += this.cfg.balance * w;
-                randBoard.set(i, j, 2);
-                var n = 0, w = 0;
-                for (var diri = 0; diri < ai.dirs.length; diri++) {
-                    if (randBoard[ai.dirs[diri]](nextBrd)) {
-                        w += this.weight(nextBrd, depth+1);
-                        n++;
+                if (this.cfg.balance < 1) {
+                    randBoard.set(i, j, 2);
+                    var n = 0, w = 0;
+                    for (var diri = 0; diri < ai.dirs.length; diri++) {
+                        if (randBoard[ai.dirs[diri]](nextBrd)) {
+                            w += this.recWeight(nextBrd, depth+1);
+                            n++;
+                        }
                     }
+                    if (n > 0)
+                        w = w / n;
+                    weight += this.cfg.balance * w;
                 }
-                if (n > 0)
-                    w = w / n;
-                weight += this.cfg.balance * w;
                 free++;
             }
         }