# HG changeset patch # User Oleksandr Gavenko # Date 1411414563 -10800 # Node ID 7b9d1bb9c471d91726ab6f10ddbe7d54234b8328 # Parent e3a91b336976d3a618e6f9bf23a0b69ebc578361# Parent c2bf15c3b80be22812bc6547aac827eaa9e1ad58 merged diff -r c2bf15c3b80b -r 7b9d1bb9c471 .hgignore --- a/.hgignore Mon Sep 22 21:29:21 2014 +0300 +++ b/.hgignore Mon Sep 22 22:36:03 2014 +0300 @@ -6,3 +6,4 @@ CHANGES.html README.html HACKING.html +AIs.html diff -r c2bf15c3b80b -r 7b9d1bb9c471 .hgtags --- a/.hgtags Mon Sep 22 21:29:21 2014 +0300 +++ b/.hgtags Mon Sep 22 22:36:03 2014 +0300 @@ -4,3 +4,4 @@ 58adeb5e426478540b5116518f1db2b0dd60f024 v0.2 58adeb5e426478540b5116518f1db2b0dd60f024 v0.2 97acd9f4288b5cf8f90ef483f23bcb349cee4ade v0.2 +6a0997efaa532ddfb9e6f94a5e34e2fbd0d6b436 v0.3 diff -r c2bf15c3b80b -r 7b9d1bb9c471 2048.html --- a/2048.html Mon Sep 22 21:29:21 2014 +0300 +++ b/2048.html Mon Sep 22 22:36:03 2014 +0300 @@ -66,20 +66,22 @@ left: 1em; top: -1em; } - button#statistic { + button#statistic, button#statistic-clean { display: inline-block; float: left; margin: 3px 4px; } div.ai > div.option, div.setting, div.report { display: inline-block; - float: left; margin: 1px 4px; padding: 2px; border: 1px solid tan; border-radius: 4px; } - div.ai > div.option > input.positive, div.settings > div.setting > input { + div.ai > div.option, div.setting { + float: left; + } + div.ai > div.option > input.positive, div.settings > div.setting > input, div.ai-control input.positive { text-align: right; max-width: 4em; margin-right: 2px; @@ -162,6 +164,11 @@ +
+ + until score + and max value +
@@ -187,13 +194,13 @@ left weight
- right weight + right weight
- up weight + up weight
- down weight + down weight
@@ -208,9 +215,9 @@
-
+
-
next merge makes max value
+
one step ahead
score weight
@@ -228,13 +235,28 @@
-
+
-
deep merges without simulation make max score
-
-
- -
deep merges without simulation make max score + bonus if max value at corner/edge
+
deep merges without random simulation
+
+ score weight +
+
+ max value weight +
+
+ max value at corner bonus +
+
+ max value at edge bonus +
+
+ free cell coefficient +
+
+ score threshold +
+
@@ -265,12 +287,13 @@
-
+

Reports

times
limit to sec
+
score
turn
speed
@@ -290,10 +313,17 @@ // UI widgets. ui.dom = {}; + ui.dom.clearfix = function() { + var divDom = document.createElement('div'); + divDom.classList.add("clearfix"); + return divDom; + } ui.dom.table = function(tbl, cols, cfg) { var tableDom = document.createElement('table'); - if (typeof cfg.tableClass === 'string') - tableDom.classList.add(cfg.tableClass); + if (typeof cfg.tblClass === 'string') + tableDom.classList.add(cfg.tblClass); + if (typeof cfg.tblTitle === 'string') + tableDom.title = cfg.tblTitle; var trDom = document.createElement('tr'); for (var i = 0; i < cols.length; i++) { var thDom = document.createElement('td'); @@ -423,9 +453,8 @@ maxDom.innerHTML = '' + ui.board.val2048(score.max); turnDom.innerHTML = '' + turn; } - ui.score.speed = function(speed, turn) { + ui.score.speed = function(speed) { speedDom.innerHTML = '' + speed; - turnDom.innerHTML = '' + turn; } //////////////////////////////////////////////////////////////// @@ -484,14 +513,17 @@ return false; return true; } - ui.game.finishStep = function() { - board.putRandom(ui.board.position); - ui.board.turn++; + ui.game.refresh = function() { ui.board.update(ui.board.position); ui.score.update(ui.board.position, ui.board.turn); localStorage.savedBoard = JSON.stringify(ui.board.position); localStorage.savedTurn = ui.board.turn; } + ui.game.finishStep = function() { + board.putRandom(ui.board.position); + ui.board.turn++; + ui.game.refresh(); + } //////////////////////////////////////////////////////////////// // Actions. @@ -599,6 +631,62 @@ if (updated) { board.putRandom(ui.board.position); } else { + ui.board.turn += step; + ui.game.refresh(); + ui.game.setMessage("Wrong move!"); + return; + } + step++; + } + var tsTo = new Date().getTime(); + ui.board.turn += step; + ui.game.finishStep(); + ui.score.speed(parseFloat((step*1000.0/(tsTo-tsFrom)).toPrecision(3))); + ui.game.setMessage("Game over!"); + ui.ai.current.cleanup(); + } + document.getElementById("finish").addEventListener("click", ui.action.finish, false); + + ui.action.until = function() { + if ( ! ui.ai.current) { + ui.game.setMessage('Select AI!'); + return; + } + if ( ! ui.game.beginStep()) + return; + var step = 0; + var safeBdr = board.create(); + var tsFrom = new Date().getTime(); + var scoreLimit = parseInt(document.getElementById("until-score").value); + if (!isFinite(scoreLimit) || scoreLimit < 0) { + scoreLimit = 1; + document.getElementById("until-score").value = scoreLimit; + } + var maxValLimit = parseInt(document.getElementById("until-max-value").value); + if (!isFinite(maxValLimit) || maxValLimit < 0 || maxValLimit > 13) { + maxValLimit = 1; + document.getElementById("until-max-value").value = maxValLimit; + } + localStorage.untilScore = scoreLimit; + localStorage.untilMaxVal = maxValLimit; + while (true) { + if (board.gameOver(ui.board.position)) { + ui.game.setMessage("Game over!"); + break; + } + var stat = board.score(ui.board.position); + if (stat.score >= scoreLimit && stat.max >= maxValLimit) + break; + board.copy(ui.board.position, safeBdr); + var move = ui.ai.current.analyse(safeBdr); + if (typeof move === 'undefined') { + ui.game.setMessage("I don't know how to move!"); + return; + } + var updated = board.move[move].call(null, ui.board.position); + if (updated) { + board.putRandom(ui.board.position); + } else { ui.game.finishStep(); ui.game.setMessage("Wrong move!"); return; @@ -606,12 +694,12 @@ step++; } var tsTo = new Date().getTime(); - ui.game.finishStep(); - ui.score.speed(parseFloat((step*1000.0/(tsTo-tsFrom)).toPrecision(3)), step); - ui.game.setMessage("Game over!"); - ui.ai.current && ui.ai.current.cleanup(); + ui.board.turn += step; + ui.game.refresh(); + ui.score.speed(parseFloat((step*1000.0/(tsTo-tsFrom)).toPrecision(3))); + ui.ai.current.cleanup(); } - document.getElementById("finish").addEventListener("click", ui.action.finish, false); + document.getElementById("until").addEventListener("click", ui.action.until, false); //////////////////////////////////////////////////////////////// // Register AIs. @@ -619,6 +707,7 @@ ui.ai = {}; ui.ai.current = null; ui.ai.parseCfg = function(aiDom, cfg) { + cfg = cfg || {}; var optDoms = aiDom.querySelectorAll("div.option > input.positive[type='text']"); for (var i = 0; i < optDoms.length; i++) { var val = parseFloat(optDoms[i].value); @@ -629,6 +718,17 @@ } cfg[optDoms[i].name] = val; } + var optDoms = aiDom.querySelectorAll("div.option > input[type='checkbox']"); + for (var i = 0; i < optDoms.length; i++) { + cfg[optDoms[i].name] = optDoms[i].checked; + } + return cfg; + } + ui.ai.cfgTitle = function(aiName) { + var title = JSON.stringify(ui.ai.parseCfg(document.getElementById(aiName))).replace(":", ": ", "g").replace(",", "\n", "g"); + title = title.substr(1); + title = title.substr(0, title.length-1); + return title; } ui.ai.algList = { @@ -636,30 +736,23 @@ return new ai.BlindRandom(ui.brdEngine); }, "ai-blind-weight-random": function(aiDom) { - var cfg = {}; - ui.ai.parseCfg(aiDom, cfg); + var cfg = ui.ai.parseCfg(aiDom); return new ai.BlindWeightRandom(ui.brdEngine, cfg); }, "ai-blind-cycle": function(aiDom) { - var cfg = {}; - cfg.clockwise = aiDom.querySelectorAll("input[name='clockwise']")[0].checked; - cfg.whilePossible = aiDom.querySelectorAll("input[name='whilePossible']")[0].checked; + var cfg = ui.ai.parseCfg(aiDom); return new ai.BlindCycle(ui.brdEngine, cfg); }, - "ai-one-step-deep": function(aiDom) { - var cfg = {}; - ui.ai.parseCfg(aiDom, cfg); - return new ai.OneStepDeep(ui.brdEngine, cfg); + "ai-one-step-ahead": function(aiDom) { + var cfg = ui.ai.parseCfg(aiDom); + return new ai.OneStepAhead(ui.brdEngine, cfg); }, - "ai-deep-max-score": function() { - return new ai.DeepMaxScore(ui.brdEngine); - }, - "ai-deep-max-score-corner": function() { - return new ai.DeepMaxScoreCorner(ui.brdEngine); + "ai-static-deep-merges": function(aiDom) { + var cfg = ui.ai.parseCfg(aiDom); + return new ai.StaticDeepMerges(ui.brdEngine, cfg); }, "ai-expectimax": function(aiDom) { - var cfg = {}; - ui.ai.parseCfg(aiDom, cfg); + var cfg = ui.ai.parseCfg(aiDom); return new ai.expectimax(ui.brdEngine, cfg); }, // "": function() { @@ -697,6 +790,7 @@ var reportsDom = document.getElementById('reports'); ui.report = {}; + ui.report.statNo = 1; ui.report.stat = function() { /* console.profile(); */ @@ -779,7 +873,7 @@ var reportDom = document.createElement('div'); reportDom.classList.add('report'); var h5Dom = document.createElement('h5'); - h5Dom.appendChild(document.createTextNode(ui.ai.currentName)); + h5Dom.appendChild(document.createTextNode(ui.ai.currentName + " #" + ui.report.statNo++)); reportDom.appendChild(h5Dom); var maxVals = Object.keys(histo).sort(function(a,b){return a - b}); var tbl = []; @@ -822,15 +916,22 @@ tblCols.push('mean speed'); tblCols.push('max speed'); } - var tableDom = ui.dom.table(tbl, tblCols, { tableClass: 'report-by-maxval' }); + var tableDom = ui.dom.table(tbl, tblCols, { tblClass: 'report-by-maxval', tblTitle: ui.ai.cfgTitle(ui.ai.currentName) }); reportDom.appendChild(tableDom); reportsDom.insertBefore(reportDom, reportsDom.firstChild); /* console.profileEnd(); */ } - var statisticBtn = document.getElementById('statistic'); statisticBtn.addEventListener("click", ui.report.stat, false); + ui.report.statClean = function() { + while (reportsDom.hasChildNodes()) { + reportsDom.removeChild(reportsDom.lastChild); + } + reportsDom.appendChild(ui.dom.clearfix()); + } + var statisticCleanBtn = document.getElementById('statistic-clean'); + statisticCleanBtn.addEventListener("click", ui.report.statClean, false); //////////////////////////////////////////////////////////////// // Initialise game. @@ -838,6 +939,17 @@ ui.brdEngine = BoardArr2d; // TODO make user selectable // ui.brdEngine = BoardObj; // TODO make user selectable + if (localStorage.untilScore) { + var scoreLimit = parseInt(localStorage.untilScore); + if (isFinite(scoreLimit) && scoreLimit > 0) + document.getElementById("until-score").value = scoreLimit; + } + if (localStorage.untilMaxVal) { + var maxValLimit = parseInt(localStorage.untilMaxVal); + if (isFinite(maxValLimit) && maxValLimit > 0 && maxValLimit <= 13) + document.getElementById("until-max-value").value = maxValLimit; + } + if (localStorage.val2048) { try { ui.board.val2048Dom.checked = JSON.parse(localStorage.val2048); diff -r c2bf15c3b80b -r 7b9d1bb9c471 AIs.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AIs.rst Mon Sep 22 22:36:03 2014 +0300 @@ -0,0 +1,34 @@ +.. -*- mode: rst; coding: utf-8; fill-column: 80 -*- + +.. include:: header.rst + +====== + AIs. +====== +.. contents:: + +Document version. +================= + +.. include:: VERSION.rst + +AI discussion. +============== + + * http://math.stackexchange.com/questions/727076/probability-that-random-moves-in-the-game-2048-will-win + * http://stackoverflow.com/questions/22342854/what-is-the-optimal-algorithm-for-the-game-2048 + * http://codegolf.stackexchange.com/questions/25226/2048-king-on-the-hill + +Blind strategies. +================= + +Blind random moves with shifted probabilities of moves (weighted random) are +better then random moves. + +Blind moves with repeating pattern (like switching direction in clock order) +better then weighted random moves. + +See: + + * http://math.stackexchange.com/questions/727076/probability-that-random-moves-in-the-game-2048-will-win/ + diff -r c2bf15c3b80b -r 7b9d1bb9c471 CHANGES.rst --- a/CHANGES.rst Mon Sep 22 21:29:21 2014 +0300 +++ b/CHANGES.rst Mon Sep 22 22:36:03 2014 +0300 @@ -12,6 +12,17 @@ .. include:: VERSION.rst +v0.4, 2014-xx-xx. +================= + + * Add AI descriptions. + +v0.3, 2014-09-20. +================= + + * Add AI looping until condition meet. + * Improve statistic UI. + v0.2, 2014-09-18. ================= diff -r c2bf15c3b80b -r 7b9d1bb9c471 README.rst --- a/README.rst Mon Sep 22 21:29:21 2014 +0300 +++ b/README.rst Mon Sep 22 22:36:03 2014 +0300 @@ -13,14 +13,16 @@ Game rules. =========== -TODO +4x4 board tiles filled by nambers or empty. + +During each move one of free tiles was filled by value 1 or 2 (2 with probability 10%). -AI discussion. -============== +You can move tiles in one of four possible direction: left/right/up/dowm. - * http://math.stackexchange.com/questions/727076/probability-that-random-moves-in-the-game-2048-will-win - * http://stackoverflow.com/questions/22342854/what-is-the-optimal-algorithm-for-the-game-2048 - * http://codegolf.stackexchange.com/questions/25226/2048-king-on-the-hill +Each move every tiles fill empty space in selected direction. If two tiles have same numbers ``N`` +them was merged into tile with number ``N+1``. Merges performed from head of direction. + +Your goal is to reach tile with number ``11`` or better. Alternatives. ============= diff -r c2bf15c3b80b -r 7b9d1bb9c471 ai.js --- a/ai.js Mon Sep 22 21:29:21 2014 +0300 +++ b/ai.js Mon Sep 22 22:36:03 2014 +0300 @@ -76,7 +76,7 @@ this.threshold2 = (this.cfg.left + this.cfg.down)/total; this.threshold3 = (this.cfg.left + this.cfg.down + this.cfg.right)/total; } -ai.BlindWeightRandom.bestCfg = { left: 1, down: 10, right: 5, up: 1 }; +ai.BlindWeightRandom.bestCfg = { left: 1, right: 16, up: 4, down: 8 }; ai.BlindWeightRandom.prototype.analyse = function(brd) { var origBrd = new this.brdEngine(brd); while (true) { @@ -153,26 +153,26 @@ /** * Defines coefficient for linear resulted weight function. - * @name ai.OneStepDeep.cfg + * @name ai.OneStepAhead.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 foe each free cell + * @property {number} freeBonus bonus for each free cell */ /** 1 step deep with * AI. * @param {Board} brdEngine board engine from board.js - * @param {ai.OneStepDeep.cfg} cfg configuration settings + * @param {ai.OneStepAhead.cfg} cfg configuration settings * @constructor */ -ai.OneStepDeep = function(brdEngine, cfg) { +ai.OneStepAhead = function(brdEngine, cfg) { this.brdEngine = brdEngine; - this.cfg = ai.copyObj(ai.OneStepDeep.bestCfg); + this.cfg = ai.copyObj(ai.OneStepAhead.bestCfg); ai.copyObj(cfg, this.cfg); } -ai.OneStepDeep.bestCfg = {scoreCoef: 1, maxValCoef: 0, cornerBonus: 0, edgeBonus: 0, freeBonus: 0}; -ai.OneStepDeep.prototype.weight = function(brd) { +ai.OneStepAhead.bestCfg = {scoreCoef: 1, maxValCoef: 0, cornerBonus: 0, edgeBonus: 0, freeBonus: 0}; +ai.OneStepAhead.prototype.weight = function(brd) { var weight = 0; if (this.cfg.scoreCoef > 0) weight += this.cfg.scoreCoef * brd.score(); @@ -187,7 +187,7 @@ weight += this.cfg.freeBonus * brd.free(); return weight; } -ai.OneStepDeep.prototype.analyse = function(brd) { +ai.OneStepAhead.prototype.analyse = function(brd) { var origBrd = new this.brdEngine(brd); var nextBrd = new this.brdEngine(); var maxWeight = -1; @@ -205,7 +205,7 @@ return bestDir; } /* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */ -ai.OneStepDeep.prototype.cleanup = function() { } +ai.OneStepAhead.prototype.cleanup = function() { } @@ -213,151 +213,85 @@ // N level deep on score value without random simulation. //////////////////////////////////////////////////////////////// -/** N level deep on score value without random simulation. +/** + * Defines coefficient for linear resulted weight function. + * @name ai.StaticDeepMerges.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 + */ + +/** Deep merges AI without random simulation. * @param {Board} brdEngine board engine from board.js + * @param {Object} cfg configuration settings * @constructor */ -ai.DeepMaxScore = function(brdEngine) { +ai.StaticDeepMerges = function(brdEngine, cfg) { this.brdEngine = brdEngine; + this.cfg = ai.copyObj(ai.OneStepAhead.bestCfg); + ai.copyObj(cfg, this.cfg); } -ai.DeepMaxScore.prototype.analyse = function(brd) { +ai.StaticDeepMerges.bestCfg = {scoreCoef: 1, maxValCoef: 0, cornerBonus: 0, edgeBonus: 0, freeBonus: 0, weightThreshold: 10}; +ai.StaticDeepMerges.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.StaticDeepMerges.prototype.analyse = function(brd) { var origBrd = new this.brdEngine(brd); var nextBrd = new this.brdEngine(); var prevScore = -1, nextScore = -1; - 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)) { - nextScore = nextBrd.score(); - 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)) { + var weight = this.evalFn(nextBrd); + var ok = (weight - maxWeight) > this.cfg.weightPriority; + if ( ! ok && maxWeight <= weight) { + nextScore = this.weight(nextBrd); + ok = prevScore < nextScore; + } + if (ok) { prevScore = nextScore; - maxScore = score; + maxWeight = weight; bestDir = dir; } } } return bestDir; } -ai.DeepMaxScore.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); +ai.StaticDeepMerges.prototype.evalFn = function(brd) { var currScore = brd.score(); - var maxScore = currScore; + var maxWeight = currScore; var nextBrd = new this.brdEngine(); for (var i = 0; i < ai.dirs.length; i++) { if (brd[ai.dirs[i]](nextBrd)) { var score = nextBrd.score(); if (score > currScore) - maxScore = Math.max(maxScore, this.bestScore(nextBrd, seenBrds)); + maxWeight = Math.max(maxWeight, this.evalFn(nextBrd)); } } - return maxScore; + return maxWeight; } /* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */ -ai.DeepMaxScore.prototype.cleanup = function() { } +ai.StaticDeepMerges.prototype.cleanup = function() { } //////////////////////////////////////////////////////////////// -// N level deep on score value + max value prefer corner, -// without random simulation. -//////////////////////////////////////////////////////////////// - -/** - * Defines coefficient for linear resulted weight function. - * @name ai.DeepMaxScoreCorner.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 foe each free cell - */ - -/** N level deep AI without random simulation. - * @param {Board} brdEngine board engine from board.js - * @param {Object} cfg configuration settings - * @constructor */ -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() { } - - -//////////////////////////////////////////////////////////////// // N level deep with random simulation. //////////////////////////////////////////////////////////////// @@ -369,7 +303,7 @@ * @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 foe each free cell + * @property {number} freeBonus bonus for each free cell */ /** N level deep with random simulation. @@ -415,7 +349,7 @@ for (var i = 0; i < ai.dirs.length; i++) { var dir = ai.dirs[i]; if (origBrd[dir](nextBrd)) { - var weight = this.recWeight(nextBrd, 1); + var weight = this.evalFn(nextBrd, 1); var ok = (weight - maxWeight) > this.cfg.weightPriority; if ( ! ok && maxWeight <= weight) { nextScore = this.weight(nextBrd); @@ -431,7 +365,7 @@ this.cleanup(); return bestDir; } -ai.expectimax.prototype.recWeight = function(brd, depth) { +ai.expectimax.prototype.evalFn = function(brd, depth) { if (depth >= this.cfg.depth) return this.weight(brd); if (this.cache[depth]) { @@ -454,7 +388,7 @@ var n = 0, w = 0; for (var diri = 0; diri < ai.dirs.length; diri++) { if (randBoard[ai.dirs[diri]](nextBrd)) { - w += this.recWeight(nextBrd, depth+1); + w += this.evalFn(nextBrd, depth+1); n++; } } @@ -466,7 +400,7 @@ var n = 0, w = 0; for (var diri = 0; diri < ai.dirs.length; diri++) { if (randBoard[ai.dirs[diri]](nextBrd)) { - w += this.recWeight(nextBrd, depth+1); + w += this.evalFn(nextBrd, depth+1); n++; } } diff -r c2bf15c3b80b -r 7b9d1bb9c471 header.rst --- a/header.rst Mon Sep 22 21:29:21 2014 +0300 +++ b/header.rst Mon Sep 22 22:36:03 2014 +0300 @@ -5,8 +5,9 @@ .. _README: README.html .. _CHANGES: CHANGES.html .. _Changes: CHANGES.html +.. _AIs: AIs.html .. _Hacking: HACKING.html .. _Authors: AUTHORS.html -[ Play_ | About_ | Changes_ | Hacking_ | Authors_ ] +[ Play_ | About_ | Changes_ | AIs_ | Hacking_ | Authors_ ]