433 } |
433 } |
434 /* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */ |
434 /* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */ |
435 ai.expectimax.prototype.cleanup = function() { |
435 ai.expectimax.prototype.cleanup = function() { |
436 } |
436 } |
437 |
437 |
|
438 |
|
439 |
|
440 //////////////////////////////////////////////////////////////// |
|
441 // Survive as long as possible. |
|
442 //////////////////////////////////////////////////////////////// |
|
443 |
|
444 /** |
|
445 * Defines coefficient for linear resulted weight function. |
|
446 * @name ai.survive.cfg |
|
447 * @namespace |
|
448 * @property {number} scoreCoef multiplicator for score |
|
449 * @property {number} maxValCoef multiplicator for max value |
|
450 * @property {number} cornerBonus bonus for max value at board corner |
|
451 * @property {number} edgeBonus bonus for max value at board edge |
|
452 * @property {number} freeBonus bonus for each free cell |
|
453 */ |
|
454 |
|
455 /** N level deep with random simulation. |
|
456 * @param {Board} brdEngine board engine from board.js |
|
457 * @param {ai.survive.cfg} cfg configuration settings |
|
458 * @constructor */ |
|
459 ai.survive = function(brdEngine, cfg) { |
|
460 this.brdEngine = brdEngine; |
|
461 this.cfg = ai.copyObj(ai.survive.bestCfg); |
|
462 ai.copyObj(cfg, this.cfg); |
|
463 if (this.cfg.freeCells <= 0) |
|
464 this.cfg.freeCells = ai.survive.bestCfg.freeCells; |
|
465 if (!this.cfg.maxDepth || this.cfg.maxDepth < 0 || 20 <= this.cfg.maxDepth) |
|
466 this.cfg.maxDepth = ai.survive.bestCfg.maxDepth; |
|
467 this.cfg.altAI = new ai.StaticDeepMerges(brdEngine, ai.survive.altAICfg); |
|
468 } |
|
469 ai.survive.bestCfg = {freeCells: 8, maxDepth: 5}; |
|
470 ai.survive.altAICfg = {scoreCoef: 1, maxValCoef: 0, cornerBonus: 0, edgeBonus: 0, freeBonus: 0, weightThreshold: 0}; |
|
471 /** Select best direction for next step. */ |
|
472 ai.survive.prototype.analyse = function(brd2d) { |
|
473 var origBrd = new this.brdEngine(brd2d); |
|
474 var nextBrd = new this.brdEngine(); |
|
475 var bestW = -1; |
|
476 var bestDir; |
|
477 var freeCnt = origBrd.freeCnt(); |
|
478 if (freeCnt >= this.cfg.freeCells) |
|
479 return this.cfg.altAI.analyse(brd2d); |
|
480 for (var i = 0; i < ai.dirs.length; i++) { |
|
481 var dir = ai.dirs[i]; |
|
482 if (origBrd[dir](nextBrd)) { |
|
483 var w = this.evalFn(nextBrd, 1); |
|
484 console.log("dir: %s, w: %d", dir, w); |
|
485 if (w > bestW) { |
|
486 bestW = w; |
|
487 bestDir = dir; |
|
488 } |
|
489 } |
|
490 } |
|
491 return bestDir; |
|
492 } |
|
493 ai.survive.prototype.evalFn = function(brd, depth) { |
|
494 if (brd.freeCnt() >= this.cfg.freeCells) |
|
495 return 1; |
|
496 if (depth >= this.cfg.maxDepth) |
|
497 return 0; |
|
498 var wMin = +Infinity; |
|
499 var randBoard = new this.brdEngine(); |
|
500 var nextBrd = new this.brdEngine(); |
|
501 exit: |
|
502 for (var i = 0; i < 3; i++) { |
|
503 for (var j = 0; j < 3; j++) { |
|
504 if (brd.get(i, j) !== 0) |
|
505 continue; |
|
506 brd.copy(randBoard); |
|
507 randBoard.set(i, j, 1); |
|
508 var wMax = -1; |
|
509 for (var diri = 0; diri < ai.dirs.length; diri++) { |
|
510 if (randBoard[ai.dirs[diri]](nextBrd)) { |
|
511 var w = this.evalFn(nextBrd, depth+1); |
|
512 if (w === 1) { |
|
513 wMax = 1; |
|
514 break; |
|
515 } |
|
516 wMax = Math.max(wMax, w); |
|
517 } |
|
518 } |
|
519 if (wMax === -1) { |
|
520 wMin = -1; |
|
521 break exit; |
|
522 } |
|
523 wMin = Math.min(wMin, wMax); |
|
524 } |
|
525 } |
|
526 if (wMin === +Infinity) |
|
527 return -1; |
|
528 return wMin; |
|
529 } |
|
530 /* Mark that next board will be unrelated to previous, so any stored precompution can be cleared. */ |
|
531 ai.survive.prototype.cleanup = function() { |
|
532 } |
|
533 |