ai.js
changeset 53 ee53cd2cb69a
parent 36 a18fc2601ce8
child 54 2c389325d13b
--- a/ai.js	Thu Sep 11 18:12:24 2014 +0300
+++ b/ai.js	Thu Sep 11 19:22:04 2014 +0300
@@ -4,6 +4,19 @@
 ai.dirs = ["up", "right", "down", "left"];
 ai.canDirs = ["canUp", "canRight", "canDown", "canLeft"];
 
+/* Create empty 'to' if argument missing. */
+ai.copyObj = function(from, to) {
+    if (to == null || typeof to !== "object")
+        to = {};
+    if (from == null || typeof obj !== "object")
+        return to;
+    for (var attr in cfg) {
+        if (from.hasOwnProperty(attr))
+            to[attr] = from[attr];
+    }
+    return to;
+}
+
 // Each strategy is a function that except current board position as 2d array and context from
 // previous call to share state/precomputed values between calls.
 
@@ -109,60 +122,51 @@
 
 
 ////////////////////////////////////////////////////////////////
-// 1 level deep on max scores.
+// 1 step deep with linear weight function on score, max value,
+// bonuses for max value stay at corner or edge and bonuses
+// for each free field.
 ////////////////////////////////////////////////////////////////
 
-ai.nextMaxScore = function(brdEngine) {
+ai.oneStepDeep = function(brdEngine, cfg) {
     this.brdEngine = brdEngine;
+    this.cfg = ai.copyObj(ai.oneStepDeep.bestCfg);
+    ai.copyObj(cfg, this.cfg);
 }
-ai.nextMaxScore.prototype.analyse = function(brd) {
+ai.oneStepDeep.bestCfg = {scoreCoef: 1, maxValCoef: 0, cornerBonus: 0, edgeBonus: 0, freeBonus: 0};
+ai.oneStepDeep.prototype.weight = function(brd) {
+    var weight = 0;
+    if (this.cfg.scoreCoef > 0)
+        weight += this.cfg.scoreCoef * brd.score();
+    var max = brd.max();
+    if (this.cfg.maxValCoef > 0)
+        weight += this.cfg.maxValCoef * max;
+    if (this.cfg.cornerBonus > 0 && brd.atCorner(max))
+        weight += this.cfg.cornerBonus;
+    if (this.cfg.edgeBonus > 0 && brd.atEdge(max))
+        weight += this.cfg.edgeBonus;
+    if (this.cfg.freeBonus > 0)
+        weight += this.cfg.freeBonus * brd.free();
+    return weight;
+}
+ai.oneStepDeep.prototype.analyse = function(brd) {
     var origBrd = new this.brdEngine(brd);
     var nextBrd = new this.brdEngine();
-    var maxScore = -1;
+    var maxWeight = -1;
     var bestDir;
     for (var i = 0; i < ai.dirs.length; i++) {
         var dir = ai.dirs[i];
         if (origBrd[dir](nextBrd)) {
-            var score = nextBrd.score();
-            if (maxScore < score) {
+            var weight = this.weight(nextBrd);
+            if (maxWeight < weight) {
                 bestDir = dir;
-                maxScore = score;
+                maxWeight = weight;
             }
         }
     }
     return bestDir;
 }
 /* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */
-ai.nextMaxScore.prototype.cleanup = function() { }
-
-
-
-////////////////////////////////////////////////////////////////
-// 1 level deep on max value.
-////////////////////////////////////////////////////////////////
-
-ai.nextMaxValue = function(brdEngine) {
-    this.brdEngine = brdEngine;
-}
-ai.nextMaxValue.prototype.analyse = function(brd) {
-    var origBrd = new this.brdEngine(brd);
-    var nextBrd = new this.brdEngine();
-    var maxMax = -1;
-    var bestDir;
-    for (var i = 0; i < ai.dirs.length; i++) {
-        var dir = ai.dirs[i];
-        if (origBrd[dir](nextBrd)) {
-            var max = nextBrd.score();
-            if (maxMax < max) {
-                maxMax = max;
-                bestDir = dir;
-            }
-        }
-    }
-    return bestDir;
-}
-/* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */
-ai.nextMaxValue.prototype.cleanup = function() { }
+ai.oneStepDeep.prototype.cleanup = function() { }