Join N level deep on score value without random simulation AIs and add
parameter customisation.
--- a/2048.html Mon Sep 22 01:44:48 2014 +0300
+++ b/2048.html Mon Sep 22 22:35:41 2014 +0300
@@ -235,13 +235,28 @@
</div>
<div class="clearfix"></div>
</div>
- <div class="ai" id="ai-deep-max-score">
+ <div class="ai" id="ai-static-deep-merges">
<button class="ai">enable</button>
- <h5>deep merges without simulation make max score</h5>
- </div>
- <div class="ai" id="ai-deep-max-score-corner">
- <button class="ai">enable</button>
- <h5>deep merges without simulation make max score + bonus if max value at corner/edge</h5>
+ <h5>deep merges without random simulation</h5>
+ <div class="option">
+ <input type="text" name="scoreCoef" class="positive" pattern="[0-9]*[.]?[0-9]*" value="1"/> score weight
+ </div>
+ <div class="option">
+ <input type="text" name="maxValCoef" class="positive" pattern="[0-9]*[.]?[0-9]*" value="0"/> max value weight
+ </div>
+ <div class="option">
+ <input type="text" name="cornerBonus" class="positive" pattern="[0-9]*[.]?[0-9]*" value="100"/> max value at corner bonus
+ </div>
+ <div class="option">
+ <input type="text" name="edgeBonus" class="positive" pattern="[0-9]*[.]?[0-9]*" value="0"/> max value at edge bonus
+ </div>
+ <div class="option">
+ <input type="text" name="freeBonus" class="positive" pattern="[0-9]*[.]?[0-9]*" value="10"/> free cell coefficient
+ </div>
+ <div class="option">
+ <input type="text" name="weightThreshold" class="positive" pattern="[0-9]*[.]?[0-9]*" value="10"/> score threshold
+ </div>
+ <div class="clearfix"></div>
</div>
<div class="ai" id="ai-expectimax">
<button class="ai">enable</button>
@@ -732,11 +747,9 @@
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);
--- a/ai.js Mon Sep 22 01:44:48 2014 +0300
+++ b/ai.js Mon Sep 22 22:35:41 2014 +0300
@@ -159,7 +159,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
*/
/** 1 step deep with * AI.
@@ -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.evalFn(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.evalFn = 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.evalFn(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.evalFn(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.evalFn = 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.evalFn(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.