Код гадалки Шеннона перенесен в проект
This commit is contained in:
		
							
								
								
									
										145
									
								
								source/_assets/predictor/Daemon.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								source/_assets/predictor/Daemon.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| const DEFAULT_EPSILON = 0.01; | ||||
|  | ||||
| /** | ||||
|  * @param {Number[]} steps | ||||
|  * | ||||
|  * @returns {String} | ||||
|  */ | ||||
| function create_key(steps) { | ||||
|     return steps.join(':'); | ||||
| } | ||||
|  | ||||
| class Daemon { | ||||
|     /** | ||||
|      * @type {Number} | ||||
|      */ | ||||
|     base; | ||||
|  | ||||
|     /** | ||||
|      * @type {Number} | ||||
|      */ | ||||
|     humanCount; | ||||
|  | ||||
|     /** | ||||
|      * @type {Number} | ||||
|      */ | ||||
|     robotCount; | ||||
|  | ||||
|     /** | ||||
|      * @type {Number} | ||||
|      */ | ||||
|     epsilon; | ||||
|  | ||||
|     /** | ||||
|      * @type {Object} | ||||
|      */ | ||||
|     weights = {}; | ||||
|  | ||||
|     /** | ||||
|      * @param {Number} base | ||||
|      * @param {Number} humanCount | ||||
|      * @param {Number} robotCount | ||||
|      * @param {Number} epsilon | ||||
|      */ | ||||
|     constructor(base, humanCount, robotCount, epsilon = DEFAULT_EPSILON) { | ||||
|         this.base = base; | ||||
|         this.humanCount = humanCount; | ||||
|         this.robotCount = robotCount; | ||||
|         this.epsilon = epsilon; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @returns {Number} | ||||
|      */ | ||||
|     get power() { | ||||
|         return this.humanCount + this.robotCount; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Journal} journal | ||||
|      * | ||||
|      * @returns {Number} | ||||
|      */ | ||||
|     predict(journal) { | ||||
|         const steps = this._getStepSlice(journal); | ||||
|  | ||||
|         const proposals = []; | ||||
|         for (let i = 0; i < this.base; ++i) { | ||||
|             proposals[i] = this._getWeight([...steps, i]); | ||||
|         } | ||||
|  | ||||
|         const maxWeight = Math.max(...proposals); | ||||
|  | ||||
|         return proposals.indexOf(maxWeight); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Journal} journal | ||||
|      * @param {Number} humanValue | ||||
|      */ | ||||
|     adjust(journal, humanValue) { | ||||
|         const steps = this._getStepSlice(journal); | ||||
|         const adjustmentWeight = this._getAdjustmentWeight(journal.length); | ||||
|         this._adjustWeight([...steps, humanValue], adjustmentWeight); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Journal} journal | ||||
|      * | ||||
|      * @returns {Number[]} | ||||
|      */ | ||||
|     _getStepSlice(journal) { | ||||
|         return journal.getLastMovements(this.humanCount, this.robotCount); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Number} stepNumber | ||||
|      * | ||||
|      * @returns {Number} | ||||
|      * | ||||
|      * @private | ||||
|      */ | ||||
|     _getAdjustmentWeight(stepNumber) { | ||||
|         return Math.pow(1 + this.epsilon, stepNumber); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Number[]} steps | ||||
|      * | ||||
|      * @returns {Number} | ||||
|      * | ||||
|      * @private | ||||
|      */ | ||||
|     _getWeight(steps) { | ||||
|         const key = create_key(steps); | ||||
|         const weight = this.weights[key]; | ||||
|         return weight === undefined ? 0 : weight; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Number[]} steps | ||||
|      * @param {Number} value | ||||
|      * | ||||
|      * @returns {Number} | ||||
|      * | ||||
|      * @private | ||||
|      */ | ||||
|     _setWeight(steps, value) { | ||||
|         const key = create_key(steps); | ||||
|         this.weights[key] = value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Number[]} steps | ||||
|      * @param {Number} weight | ||||
|      * | ||||
|      * @private | ||||
|      */ | ||||
|     _adjustWeight(steps, weight) { | ||||
|         const currentWeight = this._getWeight(steps); | ||||
|         const newWeight = currentWeight + weight; | ||||
|         this._setWeight(steps, newWeight); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default Daemon; | ||||
							
								
								
									
										47
									
								
								source/_assets/predictor/Journal.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								source/_assets/predictor/Journal.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| import Move from './Move'; | ||||
|  | ||||
| class Journal { | ||||
|     /** | ||||
|      * @type {Move[]} | ||||
|      */ | ||||
|     moves = []; | ||||
|  | ||||
|     /** | ||||
|      * @param {Move[]} moves | ||||
|      */ | ||||
|     constructor(moves = []) { | ||||
|         this.moves = moves; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Number} human | ||||
|      * @param {Number} robot | ||||
|      */ | ||||
|     makeMove(human, robot) { | ||||
|         this.moves.push(new Move(human, robot)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Number} humanCount | ||||
|      * @param {Number} robotCount | ||||
|      * | ||||
|      * @returns {Number[]} | ||||
|      */ | ||||
|     getLastMovements(humanCount, robotCount) { | ||||
|         const humanMoves = this.moves.map(m => m.human); | ||||
|         const robotMoves = this.moves.map(m => m.robot); | ||||
|         return [].concat( | ||||
|             robotMoves.slice(-robotCount), | ||||
|             humanMoves.slice(-humanCount) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @returns {Number} | ||||
|      */ | ||||
|     get length() { | ||||
|         return this.moves.length; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default Journal; | ||||
							
								
								
									
										15
									
								
								source/_assets/predictor/Move.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								source/_assets/predictor/Move.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| /** | ||||
|  * Represents one game move. | ||||
|  */ | ||||
| class Move { | ||||
|     /** | ||||
|      * @param {Number} human | ||||
|      * @param {Number} robot | ||||
|      */ | ||||
|     constructor(human, robot) { | ||||
|         this.human = human; | ||||
|         this.robot = robot; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default Move; | ||||
							
								
								
									
										90
									
								
								source/_assets/predictor/Predictor.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								source/_assets/predictor/Predictor.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| import Daemon from './Daemon'; | ||||
| import Journal from './Journal'; | ||||
| import Supervisor from './Supervisor'; | ||||
|  | ||||
| const DEFAULT_CONFIG = { | ||||
|     base: 2, | ||||
|     supervisor_epsilon: 0.01, | ||||
|     daemons: [ | ||||
|         { human: 2, robot: 2, epsilon: 0.01 }, | ||||
|         { human: 3, robot: 3, epsilon: 0.01 }, | ||||
|         { human: 4, robot: 4, epsilon: 0.01 }, | ||||
|         { human: 5, robot: 5, epsilon: 0.01 }, | ||||
|         { human: 6, robot: 6, epsilon: 0.01 }, | ||||
|     ], | ||||
| }; | ||||
|  | ||||
| export default class Predictor { | ||||
|     /** | ||||
|      * @type {Number} | ||||
|      */ | ||||
|     base; | ||||
|  | ||||
|     /** | ||||
|      * @type {Number} | ||||
|      */ | ||||
|     score; | ||||
|  | ||||
|     /** | ||||
|      * @type {Journal} | ||||
|      */ | ||||
|     journal; | ||||
|  | ||||
|     /** | ||||
|      * @type {Supervisor} | ||||
|      */ | ||||
|     supervisor; | ||||
|  | ||||
|     /** | ||||
|      * @param {Object} config | ||||
|      */ | ||||
|     constructor(config = DEFAULT_CONFIG) { | ||||
|         this.base = config.base; | ||||
|         this.score = 0; | ||||
|         this.journal = new Journal(); | ||||
|         const daemons = this._createDaemons(config.daemons); | ||||
|         this.supervisor = new Supervisor(daemons, config.supervisor_epsilon); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Number|String} humanValue | ||||
|      * | ||||
|      * @returns {Number} | ||||
|      */ | ||||
|     pass(humanValue) { | ||||
|         const value = parseInt(humanValue, 10); | ||||
|         if (value < 0 || value >= this.base) { | ||||
|             throw new Error(`Passed value must be in [0, ${this.base})`); | ||||
|         } | ||||
|         const prediction = this.supervisor.predict(this.journal); | ||||
|         this.score += prediction === value ? -1 : 1; | ||||
|         this.supervisor.adjust(this.journal, value); | ||||
|         this.journal.makeMove(value, prediction); | ||||
|         return prediction; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Object} daemonConfigs | ||||
|      * | ||||
|      * @returns {Daemon[]} | ||||
|      * | ||||
|      * @private | ||||
|      */ | ||||
|     _createDaemons(daemonConfigs) { | ||||
|         return daemonConfigs.map(config => { | ||||
|             return new Daemon( | ||||
|                 this.base, | ||||
|                 config.human, | ||||
|                 config.robot, | ||||
|                 config.epsilon || 0.01 | ||||
|             ); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @returns {Number} | ||||
|      */ | ||||
|     stepCount() { | ||||
|         return this.journal.length; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										102
									
								
								source/_assets/predictor/Supervisor.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								source/_assets/predictor/Supervisor.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| const DEFAULT_EPSILON = 0.01; | ||||
|  | ||||
| class Supervisor { | ||||
|     /** | ||||
|      * @type {{daemon: Daemon, rate: Number}[]} | ||||
|      */ | ||||
|     daemons = []; | ||||
|  | ||||
|     /** | ||||
|      * @type {Number} | ||||
|      */ | ||||
|     epsilon; | ||||
|  | ||||
|     /** | ||||
|      * @param {Daemon[]} daemons | ||||
|      * @param {Number} epsilon | ||||
|      */ | ||||
|     constructor(daemons, epsilon = DEFAULT_EPSILON) { | ||||
|         if (!daemons || daemons.length === 0) { | ||||
|             throw Error('Empty daemon list'); | ||||
|         } | ||||
|         this.daemons = daemons.map(daemon => ({ | ||||
|             daemon: daemon, | ||||
|             rate: 0, | ||||
|         })); | ||||
|         this.epsilon = epsilon; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Journal} journal | ||||
|      * | ||||
|      * @returns {Number} | ||||
|      */ | ||||
|     predict(journal) { | ||||
|         const predictions = this._createPredictions(journal); | ||||
|         const ordered = this._sortPredictions(predictions); | ||||
|  | ||||
|         return ordered[0].value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Journal} journal | ||||
|      * @param {Number} humanValue | ||||
|      */ | ||||
|     adjust(journal, humanValue) { | ||||
|         const predictions = this._createPredictions(journal); | ||||
|         for (const prediction of predictions) { | ||||
|             if (prediction.value === humanValue) { | ||||
|                 prediction.daemon.rate += this._getAdjustmentWeight( | ||||
|                     journal.length | ||||
|                 ); | ||||
|             } | ||||
|             prediction.daemon.daemon.adjust(journal, humanValue); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Journal} journal | ||||
|      * | ||||
|      * @returns {Array} | ||||
|      * | ||||
|      * @private | ||||
|      */ | ||||
|     _createPredictions(journal) { | ||||
|         return this.daemons.map(daemon => ({ | ||||
|             daemon: daemon, | ||||
|             power: daemon.daemon.power, | ||||
|             rate: daemon.rate, | ||||
|             value: daemon.daemon.predict(journal), | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Array} predictions | ||||
|      * | ||||
|      * @returns {Array} | ||||
|      * | ||||
|      * @private | ||||
|      */ | ||||
|     _sortPredictions(predictions) { | ||||
|         return predictions.sort((result1, result2) => { | ||||
|             const rateDiff = result2.rate - result1.rate; | ||||
|             if (Math.abs(rateDiff) > 0.000001) { | ||||
|                 return rateDiff; | ||||
|             } | ||||
|             return result1.power - result2.power; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param {Number} stepNumber | ||||
|      * | ||||
|      * @returns {Number} | ||||
|      * | ||||
|      * @private | ||||
|      */ | ||||
|     _getAdjustmentWeight(stepNumber) { | ||||
|         return Math.pow(1 + this.epsilon, stepNumber); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default Supervisor; | ||||
| @@ -39,7 +39,7 @@ | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import Predictor from 'predictor'; | ||||
| import Predictor from './Predictor'; | ||||
| import Metrika from '../common/metrika'; | ||||
|  | ||||
| const PREDICTOR_GAME_END_GOAL = 'PREDICTOR_GAME_END'; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user