ai.js
changeset 155 1f8df90bd338
parent 152 07814b979a8a
child 156 e6a4bc888b72
--- a/ai.js	Sun Oct 19 01:04:19 2014 +0300
+++ b/ai.js	Sun Oct 19 01:13:42 2014 +0300
@@ -435,3 +435,99 @@
 ai.expectimax.prototype.cleanup = function() {
 }
 
+
+
+////////////////////////////////////////////////////////////////
+// Survive as long as possible.
+////////////////////////////////////////////////////////////////
+
+/**
+ * Defines coefficient for linear resulted weight function.
+ * @name ai.survive.cfg
+ * @namespace
+ * @property {number} scoreCoef    multiplicator for score
+ * @property {number} maxValCoef   multiplicator for max value
+ * @property {number} cornerBonus  bonus for max value at board corner
+ * @property {number} edgeBonus    bonus for max value at board edge
+ * @property {number} freeBonus    bonus for each free cell
+ */
+
+/** N level deep with random simulation.
+ * @param {Board} brdEngine  board engine from board.js
+ * @param {ai.survive.cfg} cfg  configuration settings
+ * @constructor */
+ai.survive = function(brdEngine, cfg) {
+    this.brdEngine = brdEngine;
+    this.cfg = ai.copyObj(ai.survive.bestCfg);
+    ai.copyObj(cfg, this.cfg);
+    if (this.cfg.freeCells <= 0)
+        this.cfg.freeCells = ai.survive.bestCfg.freeCells;
+    if (!this.cfg.maxDepth || this.cfg.maxDepth < 0 || 20 <= this.cfg.maxDepth)
+        this.cfg.maxDepth = ai.survive.bestCfg.maxDepth;
+    this.cfg.altAI = new ai.StaticDeepMerges(brdEngine, ai.survive.altAICfg);
+}
+ai.survive.bestCfg = {freeCells: 8, maxDepth: 5};
+ai.survive.altAICfg = {scoreCoef: 1, maxValCoef: 0, cornerBonus: 0, edgeBonus: 0, freeBonus: 0, weightThreshold: 0};
+/** Select best direction for next step. */
+ai.survive.prototype.analyse = function(brd2d) {
+    var origBrd = new this.brdEngine(brd2d);
+    var nextBrd = new this.brdEngine();
+    var bestW = -1;
+    var bestDir;
+    var freeCnt = origBrd.freeCnt();
+    if (freeCnt >= this.cfg.freeCells)
+        return this.cfg.altAI.analyse(brd2d);
+    for (var i = 0; i < ai.dirs.length; i++) {
+        var dir = ai.dirs[i];
+        if (origBrd[dir](nextBrd)) {
+            var w = this.evalFn(nextBrd, 1);
+            console.log("dir: %s, w: %d", dir, w);
+            if (w > bestW) {
+                bestW = w;
+                bestDir = dir;
+            }
+        }
+    }
+    return bestDir;
+}
+ai.survive.prototype.evalFn = function(brd, depth) {
+    if (brd.freeCnt() >= this.cfg.freeCells)
+        return 1;
+    if (depth >= this.cfg.maxDepth)
+        return 0;
+    var wMin = +Infinity;
+    var randBoard = new this.brdEngine();
+    var nextBrd = new this.brdEngine();
+exit:
+    for (var i = 0; i < 3; i++) {
+        for (var j = 0; j < 3; j++) {
+            if (brd.get(i, j) !== 0)
+                continue;
+            brd.copy(randBoard);
+            randBoard.set(i, j, 1);
+            var wMax = -1;
+            for (var diri = 0; diri < ai.dirs.length; diri++) {
+                if (randBoard[ai.dirs[diri]](nextBrd)) {
+                    var w = this.evalFn(nextBrd, depth+1);
+                    if (w === 1) {
+                        wMax = 1;
+                        break;
+                    }
+                    wMax = Math.max(wMax, w);
+                }
+            }
+            if (wMax === -1) {
+                wMin = -1;
+                break exit;
+            }
+            wMin = Math.min(wMin, wMax);
+        }
+    }
+    if (wMin === +Infinity)
+        return -1;
+    return wMin;
+}
+/* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */
+ai.survive.prototype.cleanup = function() {
+}
+