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 |