Enable config for expectimax. Fix weight function for expectimax.
--- a/2048.html Wed Sep 17 23:30:02 2014 +0300
+++ b/2048.html Thu Sep 18 00:39:41 2014 +0300
@@ -103,6 +103,9 @@
border: 1px solid green;
padding: 2px;
}
+ table.report-by-maxval > tr > td:nth-child(3) {
+ background-color: yellow;
+ }
</style>
</head>
<body>
@@ -231,6 +234,28 @@
<div class="ai" id="ai-expectimax">
<button class="ai">enable</button>
<h5>expectimax</h5>
+ <div class="option">
+ <input type="text" name="depth" class="positive" pattern="[0-9]*" value="3"/> recursion depth
+ </div>
+ <div class="option">
+ <input type="text" name="balance" class="positive" pattern="[0-9]*[.]?[0-9]*" value=".9"/> probability of 2
+ </div>
+ <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="clearfix"></div>
</div>
</div>
</div>
@@ -624,8 +649,10 @@
"ai-deep-max-score-corner": function() {
return new ai.DeepMaxScoreCorner(ui.brdEngine);
},
- "ai-expectimax": function() {
- return new ai.expectimax(ui.brdEngine);
+ "ai-expectimax": function(aiDom) {
+ var cfg = {};
+ ui.ai.parseCfg(aiDom, cfg);
+ return new ai.expectimax(ui.brdEngine, cfg);
},
// "": function() {
// return new ai.(ui.brdEngine);
--- a/ai.js Wed Sep 17 23:30:02 2014 +0300
+++ b/ai.js Thu Sep 18 00:39:41 2014 +0300
@@ -378,33 +378,31 @@
* @constructor */
ai.expectimax = function(brdEngine, cfg) {
this.brdEngine = brdEngine;
- this.cfg = cfg || {};
- this.cfg.balance = this.cfg.balance || .9;
+ this.cfg = ai.copyObj(ai.expectimax.bestCfg);
+ ai.copyObj(cfg, this.cfg);
+ if (this.cfg.balance <= 0)
+ this.cfg.balance = ai.expectimax.bestCfg.balance;
+ if ( this.cfg.balance > 1)
+ this.cfg.balance = 1;
if (!this.cfg.depth || this.cfg.depth < 0 || 8 <= this.cfg.depth)
- this.cfg.depth = 5;
- this.cfg.cornerBonus = this.cfg.cornerBonus || 20000;
- this.cfg.edgeBonus = this.cfg.edgeBonus || 100;
- this.cfg.freeBonus = this.cfg.edgeBonus || 100;
- this.cfg.weightPriority = this.cfg.weightPriority || 10;
+ this.cfg.depth = ai.expectimax.bestCfg.depth;
}
-ai.expectimax.prototype.lvl1Score = function(brd) {
- var score = brd.score();
+ai.expectimax.bestCfg = {balance: .9, depth: 3, scoreCoef: 1, maxValCoef: 0, cornerBonus: 0, edgeBonus: 0, freeBonus: 0, weightPriority: 10};
+ai.expectimax.prototype.weight = function(brd) {
+ var score = 0;
+ if (this.cfg.scoreCoef > 0)
+ score += this.cfg.scoreCoef * 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.expectimax.prototype.lvlnScore = 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;
+ if (this.cfg.maxValCoef > 0)
+ score += this.cfg.maxValCoef * max;
+ if (this.cfg.cornerBonus > 0)
+ if (brd.atCorner(max))
+ score += this.cfg.cornerBonus;
+ if (this.cfg.edgeBonus > 0)
+ if (brd.atEdge(max))
+ score += this.cfg.edgeBonus;
+ if (this.cfg.freeBonus > 0)
+ score += this.cfg.freeBonus * brd.free();
return score;
}
ai.expectimax.prototype.analyse = function(brd) {
@@ -417,10 +415,13 @@
for (var i = 0; i < ai.dirs.length; i++) {
var dir = ai.dirs[i];
if (origBrd[dir](nextBrd)) {
- nextScore = this.lvl1Score(nextBrd);
- var weight = this.weight(nextBrd, 0);
- // console.log("dir: %o, prevScore: %o, nextScore: %o, maxWeight: %o, weight: %o", dir, prevScore, nextScore, maxWeight, weight);
- if (maxWeight + this.cfg.weightPriority < weight || (maxWeight <= weight && prevScore < nextScore)) {
+ var weight = this.recWeight(nextBrd, 1);
+ var ok = (weight - maxWeight) > this.cfg.weightPriority;
+ if ( ! ok && maxWeight <= weight) {
+ nextScore = this.weight(nextBrd);
+ ok = prevScore < nextScore;
+ }
+ if (ok) {
prevScore = nextScore;
maxWeight = weight;
bestDir = dir;
@@ -430,9 +431,9 @@
this.cleanup();
return bestDir;
}
-ai.expectimax.prototype.weight = function(brd, depth) {
- if (depth === this.cfg.depth)
- return this.lvlnScore(brd);
+ai.expectimax.prototype.recWeight = function(brd, depth) {
+ if (depth >= this.cfg.depth)
+ return this.weight(brd);
if (this.cache[depth]) {
var cache = this.cache[depth];
for (var i = cache.length-1; i >= 0; i--) {
@@ -453,24 +454,26 @@
var n = 0, w = 0;
for (var diri = 0; diri < ai.dirs.length; diri++) {
if (randBoard[ai.dirs[diri]](nextBrd)) {
- w += this.weight(nextBrd, depth+1);
+ w += this.recWeight(nextBrd, depth+1);
n++;
}
}
if (n > 0)
w = w / n;
weight += this.cfg.balance * w;
- randBoard.set(i, j, 2);
- var n = 0, w = 0;
- for (var diri = 0; diri < ai.dirs.length; diri++) {
- if (randBoard[ai.dirs[diri]](nextBrd)) {
- w += this.weight(nextBrd, depth+1);
- n++;
+ if (this.cfg.balance < 1) {
+ randBoard.set(i, j, 2);
+ 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);
+ n++;
+ }
}
+ if (n > 0)
+ w = w / n;
+ weight += this.cfg.balance * w;
}
- if (n > 0)
- w = w / n;
- weight += this.cfg.balance * w;
free++;
}
}