Add detecting value at edge or corner, free cell count. Added AI that count
authorOleksandr Gavenko <gavenkoa@gmail.com>
Mon, 08 Sep 2014 17:43:10 +0300
changeset 20 ab294e8db00c
parent 19 94a4201d27a3
child 21 ed0292f0c7c6
Add detecting value at edge or corner, free cell count. Added AI that count max value at edges or corner and free cell count.
2048.html
ai.js
board.js
--- a/2048.html	Mon Sep 08 02:04:16 2014 +0300
+++ b/2048.html	Mon Sep 08 17:43:10 2014 +0300
@@ -88,6 +88,7 @@
       <button id="ai-next-max-score">next max score</button>
       <button id="ai-next-max-value">next max value</button>
       <button id="ai-deep-max-score">deep max score</button>
+      <button id="ai-deep-max-score-corner">deep max score corner</button>
     </div>
   </div>
 
@@ -354,6 +355,9 @@
     document.getElementById("ai-deep-max-score").addEventListener("click", function() {
       ui.ai = new ai.deepMaxScore(ui.brdEngine);
     });
+    document.getElementById("ai-deep-max-score-corner").addEventListener("click", function() {
+      ui.ai = new ai.deepMaxScoreCorner(ui.brdEngine);
+    });
 
   </script>
   
--- a/ai.js	Mon Sep 08 02:04:16 2014 +0300
+++ b/ai.js	Mon Sep 08 17:43:10 2014 +0300
@@ -137,3 +137,81 @@
 /* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */
 ai.deepMaxScore.prototype.cleanup = function() { }
 
+
+////////////////////////////////////////////////////////////////
+// N level deep on score value + max value prefer corner,
+// without random simulation.
+////////////////////////////////////////////////////////////////
+
+/* cfg.cornerBonus - value to add if max value at corner. */
+/* cfg.edgeBonus - value to add if max value at edge. */
+ai.deepMaxScoreCorner = function(brdEngine, cfg) {
+    this.brdEngine = brdEngine;
+    this.cfg = cfg || {};
+    this.cfg.cornerBonus = this.cfg.cornerBonus || 20000;
+    this.cfg.edgeBonus = this.cfg.edgeBonus || 100;
+    this.cfg.freeBonus = this.cfg.edgeBonus || 100;
+}
+ai.deepMaxScoreCorner.prototype.scoreCorner = 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;
+    return score;
+}
+ai.deepMaxScoreCorner.prototype.scoreEdge = function(brd) {
+    var score = brd.score();
+    var max = brd.max();
+    if (brd.atEdge(max))
+        score += this.cfg.edgeBonus;
+    score += brd.free() * this.cfg.freeBonus;
+    return score;
+}
+ai.deepMaxScoreCorner.prototype.analyse = function(brd) {
+    var origBrd = new this.brdEngine(brd);
+    var nextBrd = new this.brdEngine();
+    var prevScore = -1, nextScore = -1;
+    var maxScore = -1;
+    var bestDir;
+    for (var i = 0; i < ai.dirs.length; i++) {
+        var dir = ai.dirs[i];
+        if (origBrd[dir](nextBrd)) {
+            nextScore = this.scoreCorner(nextBrd);
+            var score = this.bestScore(nextBrd);
+            // console.log("dir: %o, prevScore: %o, nextScore: %o, maxScore: %o, score: %o", dir, prevScore, nextScore, maxScore, score);
+            if (maxScore < score || (maxScore === score && prevScore < nextScore)) {
+                prevScore = nextScore;
+                maxScore = score;
+                bestDir = dir;
+            }
+        }
+    }
+    return bestDir;
+}
+ai.deepMaxScoreCorner.prototype.bestScore = function(brd, seenBrds) {
+    if (seenBrds) {
+        for (var i = 0; i < seenBrds.length; i++)
+            if (brd.equals(seenBrds[i]))
+                return 0;
+    } else {
+        seenBrds = [];
+    }
+    seenBrds.push(brd);
+    var currScore = this.scoreEdge(brd);
+    var maxScore = currScore;
+    var nextBrd = new this.brdEngine();
+    for (var i = 0; i < ai.dirs.length; i++) {
+        if (brd[ai.dirs[i]](nextBrd)) {
+            var score = this.scoreEdge(nextBrd);
+            if (score > currScore)
+                maxScore = Math.max(maxScore, this.bestScore(nextBrd, seenBrds));
+        }
+    }
+    return maxScore;
+}
+/* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */
+ai.deepMaxScoreCorner.prototype.cleanup = function() { }
+
--- a/board.js	Mon Sep 08 02:04:16 2014 +0300
+++ b/board.js	Mon Sep 08 17:43:10 2014 +0300
@@ -81,6 +81,14 @@
             brd.brd[i][j] = this.brd[i][j];
     return brd;
 }
+BoardArr2d.prototype.free = function() {
+    var cnt = 0;
+    for (var i = 0; i < 4; i++)
+        for (var j = 0; j < 4; j++)
+            if (this.brd[i][j] === 0)
+                cnt++;
+    return cnt;
+}
 BoardArr2d.prototype.score = function() {
     var score = 0;
     for (var i = 0; i < 4; i++) {
@@ -101,6 +109,27 @@
     }
     return max;
 }
+BoardArr2d.prototype.find = function(val) {
+    var xy = [];
+    for (var i = 0; i < 4; i++) {
+        for (var j = 0; j < 4; j++) {
+            if (this.brd[i][j] === val)
+            xy.push([i,j]);
+        }
+    }
+    return xy;
+}
+BoardArr2d.prototype.atEdge = function(val) {
+    var brd = this.brd;
+    return (brd[0][0] === val) || (brd[0][1] === val) || (brd[0][2] === val) || (brd[0][3] === val)
+        || (brd[1][0] === val) || (brd[1][3] === val)
+        || (brd[2][0] === val) || (brd[2][3] === val)
+        || (brd[3][0] === val) || (brd[3][1] === val) || (brd[3][2] === val) || (brd[3][3] === val);
+}
+BoardArr2d.prototype.atCorner = function(val) {
+    var brd = this.brd;
+    return (brd[0][0] === val) || (brd[0][3] === val) || (brd[3][0] === val) || (brd[3][3] === val);
+}
 
 BoardArr2d.prototype.canRight = function() {
     for (var i = 0; i < 4; i++) {