# HG changeset patch # User Oleksandr Gavenko # Date 1410452524 -10800 # Node ID ee53cd2cb69a2ef0ee6ec50aebdb3d82d392cc27 # Parent e4e21d2fcbe7c1bd586e8deac6eb2745c579a4dc Join all one step deep AIs into one with linear weight function. diff -r e4e21d2fcbe7 -r ee53cd2cb69a 2048.html --- a/2048.html Thu Sep 11 18:12:24 2014 +0300 +++ b/2048.html Thu Sep 11 19:22:04 2014 +0300 @@ -185,9 +185,25 @@
-
+
next merge makes max value
+
+ score weight +
+
+ max value weight +
+
+ max value at corner bonus +
+
+ max value at edge bonus +
+
+ free cell coefficient +
+
@@ -528,6 +544,17 @@ ui.ai = {}; ui.ai.current = null; + ui.ai.parseCfg = function(aiDom, cfg) { + var optDoms = aiDom.querySelectorAll("div.option > input.int[type='text']"); + for (var i = 0; i < optDoms.length; i++) { + var val = parseFloat(optDoms[i].value); + if (val === NaN) { + ui.game.setMessage('' + optDoms[i].name + ' setting is not a number!'); + continue; + } + cfg[optDoms[i].name] = val; + } + } ui.ai.algList = { "ai-blind-random": function() { return new ai.blindRandom(ui.brdEngine); @@ -546,11 +573,11 @@ cfg.whilePossible = aiDom.querySelectorAll("input[name='whilePossible']")[0].checked; return new ai.blindCycle(ui.brdEngine, cfg); }, - "ai-next-max-score": function() { - return new ai.nextMaxScore(ui.brdEngine); - }, - "ai-next-max-value": function() { - return new ai.nextMaxValue(ui.brdEngine); + "ai-one-step-deep": function(aiDom) { + var cfg = {}; + ui.ai.parseCfg(aiDom, cfg); + console.log(cfg); + return new ai.oneStepDeep(ui.brdEngine, cfg); }, "ai-deep-max-score": function() { return new ai.deepMaxScore(ui.brdEngine); diff -r e4e21d2fcbe7 -r ee53cd2cb69a ai.js --- 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() { }