# HG changeset patch # User Oleksandr Gavenko # Date 1413670422 -10800 # Node ID 1f8df90bd338a31a95226cf201c3cc2358d00299 # Parent c6d9b84d9391365fdbfc4baa4fc0d8e04c2db842 AI: Survive as long as possible. diff -r c6d9b84d9391 -r 1f8df90bd338 2048.html --- a/2048.html Sun Oct 19 01:04:19 2014 +0300 +++ b/2048.html Sun Oct 19 01:13:42 2014 +0300 @@ -281,6 +281,16 @@ free cell coefficient +
+ +
survive
+
+ depth +
+
+ free cells +
+
@@ -785,6 +795,10 @@ var cfg = ui.ai.parseCfg(aiDom); return new ai.expectimax(ui.brdEngine, cfg); }, + "ai-survive": function(aiDom) { + var cfg = ui.ai.parseCfg(aiDom); + return new ai.survive(ui.brdEngine, cfg); + }, // "": function() { // return new ai.(ui.brdEngine); // }, diff -r c6d9b84d9391 -r 1f8df90bd338 ai.js --- 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() { +} +