UNPKG

ai-for-shogi-like-games

Version:

javascript library of A.I. algorithms for Shogi-like games

153 lines (135 loc) 8.42 kB
'use strict'; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _boardLib = require('./board-lib.js'); var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var _assert = require('assert'); var _assert2 = _interopRequireDefault(_assert); var _numUtils = require('./num-utils.js'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var EvaluationModel = function () { function EvaluationModel(pieceValues, royalDistanceFromFarEndBonus, multipliersForDistFromPromotionZone, offBoardMultiplier, bonusForEachPossibleMove, malusForEachPossibleMoveOfTheOpponent, bonusForEnemyLionUnderCheck, malusForFriendlyLionUnderCheck) { _classCallCheck(this, EvaluationModel); (0, _assert2.default)(!_lodash2.default.some(Array.from(pieceValues.keys()), function (k) { return k.isKing; }), 'Evaluation model contains a King piece'); (0, _numUtils.assertArrayOfNotAscendingPositiveValues)(royalDistanceFromFarEndBonus); (0, _numUtils.assertArrayOfNotAscendingPositiveValues)(multipliersForDistFromPromotionZone); (0, _assert2.default)(bonusForEachPossibleMove >= 0); (0, _assert2.default)(malusForEachPossibleMoveOfTheOpponent <= 0); (0, _assert2.default)(bonusForEnemyLionUnderCheck >= 0); (0, _assert2.default)(malusForFriendlyLionUnderCheck <= 0); this.pieceValues = pieceValues; this.royalDistanceFromFarEndBonus = royalDistanceFromFarEndBonus; this.multipliersForDistFromPromotionZone = multipliersForDistFromPromotionZone; this.offBoardMultiplier = offBoardMultiplier; this.bonusForEachPossibleMove = bonusForEachPossibleMove; this.malusForEachPossibleMoveOfTheOpponent = malusForEachPossibleMoveOfTheOpponent; this.bonusForEnemyLionUnderCheck = bonusForEnemyLionUnderCheck; this.malusForFriendlyLionUnderCheck = malusForFriendlyLionUnderCheck; } _createClass(EvaluationModel, [{ key: 'bonusFromDistanceToPromotionZone', value: function bonusFromDistanceToPromotionZone(gb, pn) { var _piece = gb._get(pn); (0, _assert2.default)(_piece); if (_piece != null) { // Flow forces me to do that var piece = _piece.piece; if (!piece.isPromotable()) { return 0; } else { var promotedPiece = piece.promote(); (0, _assert2.default)(this.pieceValues.has(promotedPiece)); var valueOfPromotedPiece = this.pieceValues.get(promotedPiece); (0, _assert2.default)(valueOfPromotedPiece !== undefined); if (valueOfPromotedPiece != null) { var d = gb.distanceFromPromotionZone(pn); if (d >= this.multipliersForDistFromPromotionZone.length) { return 0; } else return this.multipliersForDistFromPromotionZone[d] * valueOfPromotedPiece; } else throw new Error('bug'); } } else throw new Error('bug'); } }, { key: '_royalBonusFromDistanceToFarEnd', value: function _royalBonusFromDistanceToFarEnd(gb, pn) { var d = gb.royalDistanceFromFarEndForGameWin(pn); if (d === Number.POSITIVE_INFINITY) throw new Error('bad choreography - should have already checked if this board is set for instant win on far end'); if (d >= this.royalDistanceFromFarEndBonus.length) return 0;else return this.royalDistanceFromFarEndBonus[d]; } }, { key: 'evaluateBoard', value: function evaluateBoard(gb) { // evaluation is done from the perspective of side A so higher value is better for sideA, lower value is better for sideB var b = gb.boardImmediateWinSide(); (0, _assert2.default)(b !== undefined); if (b != null) { // escape for immediate win situations return b ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; } else { // normal evaluation var pieceValuesFriendlySide = this._evalPieceValues(gb, true); var pieceValuesEnemySide = this._evalPieceValues(gb, false); var freedomOfMoveValuation = this._evalFreedomOfMovement(gb); var evalRoyalCheckage = this._evalRoyalCheckage(gb); return pieceValuesFriendlySide - pieceValuesEnemySide + freedomOfMoveValuation + evalRoyalCheckage; } } }, { key: '_evalPieceValues', value: function _evalPieceValues(gb, sideA) { var _this = this; var rv = 0; var piecesOnBoard = gb.onBoardPieces(sideA); piecesOnBoard.forEach(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), p = _ref2[0], pn = _ref2[1]; if (p.isKing) { if (gb.winIfKingReachesFarEnd) rv += _this._royalBonusFromDistanceToFarEnd(gb, pn); } else { (0, _assert2.default)(_this.pieceValues.has(p)); rv += _this.pieceValues.get(p); var promotionBonus = _this.bonusFromDistanceToPromotionZone(gb, pn); rv += promotionBonus; } }); var piecesOffBoard = gb.offBoardPieces(sideA); piecesOffBoard.forEach(function (p) { (0, _assert2.default)(_this.pieceValues.has(p)); var pieceValue = _this.pieceValues.get(p); (0, _assert2.default)(pieceValue !== undefined); if (pieceValue != null) { rv += pieceValue * _this.offBoardMultiplier; } else throw new Error('bug'); }); return rv; } }, { key: '_evalFreedomOfMovement', value: function _evalFreedomOfMovement(gb) { var moveBonusFriendlySide = this._numOfMoves(gb, true) * this.bonusForEachPossibleMove; var moveMalusEnemySide = this._numOfMoves(gb, false) * this.malusForEachPossibleMoveOfTheOpponent; return moveBonusFriendlySide + moveMalusEnemySide; } }, { key: '_evalRoyalCheckage', value: function _evalRoyalCheckage(gb) { var checkBonusEnemyLionUnderCheck = gb.isKingUnderAttack(false) ? this.bonusForEnemyLionUnderCheck : 0; var checkMalusFriendlyLionUnderCheck = gb.isKingUnderAttack(true) ? this.malusForFriendlyLionUnderCheck : 0; return checkBonusEnemyLionUnderCheck + checkMalusFriendlyLionUnderCheck; } }, { key: '_numOfMoves', value: function _numOfMoves(gb, sideA) { return gb.nextStates(sideA, false).size; } }]); return EvaluationModel; }(); exports.EvaluationModel = EvaluationModel; //# sourceMappingURL=eval-model.js.map