ai.js
changeset 110 e3a91b336976
parent 109 6d5a9d8b00be
child 118 7e93fb8136a3
equal deleted inserted replaced
109:6d5a9d8b00be 110:e3a91b336976
   157  * @namespace
   157  * @namespace
   158  * @property {number} scoreCoef    multiplicator for score
   158  * @property {number} scoreCoef    multiplicator for score
   159  * @property {number} maxValCoef   multiplicator for max value
   159  * @property {number} maxValCoef   multiplicator for max value
   160  * @property {number} cornerBonus  bonus for max value at board corner
   160  * @property {number} cornerBonus  bonus for max value at board corner
   161  * @property {number} edgeBonus    bonus for max value at board edge
   161  * @property {number} edgeBonus    bonus for max value at board edge
   162  * @property {number} freeBonus    bonus foe each free cell
   162  * @property {number} freeBonus    bonus for each free cell
   163  */
   163  */
   164 
   164 
   165 /** 1 step deep with * AI.
   165 /** 1 step deep with * AI.
   166  * @param {Board} brdEngine  board engine from board.js
   166  * @param {Board} brdEngine  board engine from board.js
   167  * @param {ai.OneStepAhead.cfg} cfg  configuration settings
   167  * @param {ai.OneStepAhead.cfg} cfg  configuration settings
   211 
   211 
   212 ////////////////////////////////////////////////////////////////
   212 ////////////////////////////////////////////////////////////////
   213 // N level deep on score value without random simulation.
   213 // N level deep on score value without random simulation.
   214 ////////////////////////////////////////////////////////////////
   214 ////////////////////////////////////////////////////////////////
   215 
   215 
   216 /** N level deep on score value without random simulation.
   216 /**
   217  * @param {Board} brdEngine  board engine from board.js
   217  * Defines coefficient for linear resulted weight function.
   218  * @constructor */
   218  * @name ai.StaticDeepMerges.cfg
   219 ai.DeepMaxScore = function(brdEngine) {
   219  * @namespace
   220     this.brdEngine = brdEngine;
   220  * @property {number} scoreCoef    multiplicator for score
   221 }
   221  * @property {number} maxValCoef   multiplicator for max value
   222 ai.DeepMaxScore.prototype.analyse = function(brd) {
   222  * @property {number} cornerBonus  bonus for max value at board corner
       
   223  * @property {number} edgeBonus    bonus for max value at board edge
       
   224  * @property {number} freeBonus    bonus for each free cell
       
   225  */
       
   226 
       
   227 /** Deep merges AI without random simulation.
       
   228  * @param {Board} brdEngine  board engine from board.js
       
   229  * @param {Object} cfg  configuration settings
       
   230  * @constructor */
       
   231 ai.StaticDeepMerges = function(brdEngine, cfg) {
       
   232     this.brdEngine = brdEngine;
       
   233     this.cfg = ai.copyObj(ai.OneStepAhead.bestCfg);
       
   234     ai.copyObj(cfg, this.cfg);
       
   235 }
       
   236 ai.StaticDeepMerges.bestCfg = {scoreCoef: 1, maxValCoef: 0, cornerBonus: 0, edgeBonus: 0, freeBonus: 0, weightThreshold: 10};
       
   237 ai.StaticDeepMerges.prototype.weight = function(brd) {
       
   238     var weight = 0;
       
   239     if (this.cfg.scoreCoef > 0)
       
   240         weight += this.cfg.scoreCoef * brd.score();
       
   241     var max = brd.max();
       
   242     if (this.cfg.maxValCoef > 0)
       
   243         weight += this.cfg.maxValCoef * max;
       
   244     if (this.cfg.cornerBonus > 0 && brd.atCorner(max))
       
   245         weight += this.cfg.cornerBonus;
       
   246     if (this.cfg.edgeBonus > 0 && brd.atEdge(max))
       
   247         weight += this.cfg.edgeBonus;
       
   248     if (this.cfg.freeBonus > 0)
       
   249         weight += this.cfg.freeBonus * brd.free();
       
   250     return weight;
       
   251 }
       
   252 ai.StaticDeepMerges.prototype.analyse = function(brd) {
   223     var origBrd = new this.brdEngine(brd);
   253     var origBrd = new this.brdEngine(brd);
   224     var nextBrd = new this.brdEngine();
   254     var nextBrd = new this.brdEngine();
   225     var prevScore = -1, nextScore = -1;
   255     var prevScore = -1, nextScore = -1;
   226     var maxScore = -1;
   256     var maxWeight = -1;
   227     var bestDir;
   257     var bestDir;
   228     for (var i = 0; i < ai.dirs.length; i++) {
   258     for (var i = 0; i < ai.dirs.length; i++) {
   229         var dir = ai.dirs[i];
   259         var dir = ai.dirs[i];
   230         if (origBrd[dir](nextBrd)) {
   260         if (origBrd[dir](nextBrd)) {
   231             nextScore = nextBrd.score();
   261             var weight = this.evalFn(nextBrd);
   232             var score = this.evalFn(nextBrd);
   262             var ok = (weight - maxWeight) > this.cfg.weightPriority;
   233             // console.log("dir: %o, prevScore: %o, nextScore: %o, maxScore: %o, score: %o", dir, prevScore, nextScore, maxScore, score);
   263             if ( ! ok && maxWeight <= weight) {
   234             if (maxScore < score || (maxScore === score && prevScore < nextScore)) {
   264                 nextScore = this.weight(nextBrd);
       
   265                 ok = prevScore < nextScore;
       
   266             }
       
   267             if (ok) {
   235                 prevScore = nextScore;
   268                 prevScore = nextScore;
   236                 maxScore = score;
   269                 maxWeight = weight;
   237                 bestDir = dir;
   270                 bestDir = dir;
   238             }
   271             }
   239         }
   272         }
   240     }
   273     }
   241     return bestDir;
   274     return bestDir;
   242 }
   275 }
   243 ai.DeepMaxScore.prototype.evalFn = function(brd, seenBrds) {
   276 ai.StaticDeepMerges.prototype.evalFn = function(brd) {
   244     if (seenBrds) {
       
   245         for (var i = 0; i < seenBrds.length; i++)
       
   246             if (brd.equals(seenBrds[i]))
       
   247                 return 0;
       
   248     } else {
       
   249         seenBrds = [];
       
   250     }
       
   251     seenBrds.push(brd);
       
   252     var currScore = brd.score();
   277     var currScore = brd.score();
   253     var maxScore = currScore;
   278     var maxWeight = currScore;
   254     var nextBrd = new this.brdEngine();
   279     var nextBrd = new this.brdEngine();
   255     for (var i = 0; i < ai.dirs.length; i++) {
   280     for (var i = 0; i < ai.dirs.length; i++) {
   256         if (brd[ai.dirs[i]](nextBrd)) {
   281         if (brd[ai.dirs[i]](nextBrd)) {
   257             var score = nextBrd.score();
   282             var score = nextBrd.score();
   258             if (score > currScore)
   283             if (score > currScore)
   259                 maxScore = Math.max(maxScore, this.evalFn(nextBrd, seenBrds));
   284                 maxWeight = Math.max(maxWeight, this.evalFn(nextBrd));
   260         }
   285         }
   261     }
   286     }
   262     return maxScore;
   287     return maxWeight;
   263 }
   288 }
   264 /* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */
   289 /* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */
   265 ai.DeepMaxScore.prototype.cleanup = function() { }
   290 ai.StaticDeepMerges.prototype.cleanup = function() { }
   266 
   291 
   267 
   292 
   268 
       
   269 ////////////////////////////////////////////////////////////////
       
   270 // N level deep on score value + max value prefer corner,
       
   271 // without random simulation.
       
   272 ////////////////////////////////////////////////////////////////
       
   273 
       
   274 /**
       
   275  * Defines coefficient for linear resulted weight function.
       
   276  * @name ai.DeepMaxScoreCorner.cfg
       
   277  * @namespace
       
   278  * @property {number} scoreCoef    multiplicator for score
       
   279  * @property {number} maxValCoef   multiplicator for max value
       
   280  * @property {number} cornerBonus  bonus for max value at board corner
       
   281  * @property {number} edgeBonus    bonus for max value at board edge
       
   282  * @property {number} freeBonus    bonus foe each free cell
       
   283  */
       
   284 
       
   285 /** N level deep AI without random simulation.
       
   286  * @param {Board} brdEngine  board engine from board.js
       
   287  * @param {Object} cfg  configuration settings
       
   288  * @constructor */
       
   289 ai.DeepMaxScoreCorner = function(brdEngine, cfg) {
       
   290     this.brdEngine = brdEngine;
       
   291     this.cfg = cfg || {};
       
   292     this.cfg.cornerBonus = this.cfg.cornerBonus || 20000;
       
   293     this.cfg.edgeBonus = this.cfg.edgeBonus || 100;
       
   294     this.cfg.freeBonus = this.cfg.edgeBonus || 100;
       
   295 }
       
   296 ai.DeepMaxScoreCorner.prototype.scoreCorner = function(brd) {
       
   297     var score = brd.score();
       
   298     var max = brd.max();
       
   299     if (brd.atCorner(max))
       
   300         score += this.cfg.cornerBonus;
       
   301     else if (brd.atEdge(max))
       
   302         score += this.cfg.edgeBonus;
       
   303     score += brd.free() * this.cfg.freeBonus;
       
   304     return score;
       
   305 }
       
   306 ai.DeepMaxScoreCorner.prototype.scoreEdge = function(brd) {
       
   307     var score = brd.score();
       
   308     var max = brd.max();
       
   309     if (brd.atEdge(max))
       
   310         score += this.cfg.edgeBonus;
       
   311     score += brd.free() * this.cfg.freeBonus;
       
   312     return score;
       
   313 }
       
   314 ai.DeepMaxScoreCorner.prototype.analyse = function(brd) {
       
   315     var origBrd = new this.brdEngine(brd);
       
   316     var nextBrd = new this.brdEngine();
       
   317     var prevScore = -1, nextScore = -1;
       
   318     var maxScore = -1;
       
   319     var bestDir;
       
   320     for (var i = 0; i < ai.dirs.length; i++) {
       
   321         var dir = ai.dirs[i];
       
   322         if (origBrd[dir](nextBrd)) {
       
   323             nextScore = this.scoreCorner(nextBrd);
       
   324             var score = this.evalFn(nextBrd);
       
   325             // console.log("dir: %o, prevScore: %o, nextScore: %o, maxScore: %o, score: %o", dir, prevScore, nextScore, maxScore, score);
       
   326             if (maxScore < score || (maxScore === score && prevScore < nextScore)) {
       
   327                 prevScore = nextScore;
       
   328                 maxScore = score;
       
   329                 bestDir = dir;
       
   330             }
       
   331         }
       
   332     }
       
   333     return bestDir;
       
   334 }
       
   335 ai.DeepMaxScoreCorner.prototype.evalFn = function(brd, seenBrds) {
       
   336     if (seenBrds) {
       
   337         for (var i = 0; i < seenBrds.length; i++)
       
   338             if (brd.equals(seenBrds[i]))
       
   339                 return 0;
       
   340     } else {
       
   341         seenBrds = [];
       
   342     }
       
   343     seenBrds.push(brd);
       
   344     var currScore = this.scoreEdge(brd);
       
   345     var maxScore = currScore;
       
   346     var nextBrd = new this.brdEngine();
       
   347     for (var i = 0; i < ai.dirs.length; i++) {
       
   348         if (brd[ai.dirs[i]](nextBrd)) {
       
   349             var score = this.scoreEdge(nextBrd);
       
   350             if (score > currScore)
       
   351                 maxScore = Math.max(maxScore, this.evalFn(nextBrd, seenBrds));
       
   352         }
       
   353     }
       
   354     return maxScore;
       
   355 }
       
   356 /* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */
       
   357 ai.DeepMaxScoreCorner.prototype.cleanup = function() { }
       
   358 
       
   359 
   293 
   360 ////////////////////////////////////////////////////////////////
   294 ////////////////////////////////////////////////////////////////
   361 // N level deep with random simulation.
   295 // N level deep with random simulation.
   362 ////////////////////////////////////////////////////////////////
   296 ////////////////////////////////////////////////////////////////
   363 
   297 
   367  * @namespace
   301  * @namespace
   368  * @property {number} scoreCoef    multiplicator for score
   302  * @property {number} scoreCoef    multiplicator for score
   369  * @property {number} maxValCoef   multiplicator for max value
   303  * @property {number} maxValCoef   multiplicator for max value
   370  * @property {number} cornerBonus  bonus for max value at board corner
   304  * @property {number} cornerBonus  bonus for max value at board corner
   371  * @property {number} edgeBonus    bonus for max value at board edge
   305  * @property {number} edgeBonus    bonus for max value at board edge
   372  * @property {number} freeBonus    bonus foe each free cell
   306  * @property {number} freeBonus    bonus for each free cell
   373  */
   307  */
   374 
   308 
   375 /** N level deep with random simulation.
   309 /** N level deep with random simulation.
   376  * @param {Board} brdEngine  board engine from board.js
   310  * @param {Board} brdEngine  board engine from board.js
   377  * @param {ai.expectimax.cfg} cfg  configuration settings
   311  * @param {ai.expectimax.cfg} cfg  configuration settings