diff --git a/src/Daemon.ts b/src/Daemon.ts index 1a30521..4da53a8 100644 --- a/src/Daemon.ts +++ b/src/Daemon.ts @@ -47,7 +47,7 @@ class Daemon { return proposals.indexOf(maxWeight); } - adjust(journal: Journal, humanValue: number) { + adjust(journal: Journal, humanValue: number): void { const steps = this._getStepSlice(journal); const adjustmentWeight = this._getAdjustmentWeight(journal.length); this._adjustWeight([...steps, humanValue], adjustmentWeight); diff --git a/src/Predictor.ts b/src/Predictor.ts index b8bbcca..8a3bf9a 100644 --- a/src/Predictor.ts +++ b/src/Predictor.ts @@ -2,7 +2,19 @@ import Daemon from './Daemon'; import Journal from './Journal'; import Supervisor from './Supervisor'; -const DEFAULT_CONFIG = { +interface DaemonConfig { + human: number; + robot: number; + epsilon: number; +} + +interface PredictorConfig { + base: number; + supervisor_epsilon: number; + daemons: DaemonConfig[]; +} + +const DEFAULT_CONFIG: PredictorConfig = { base: 2, supervisor_epsilon: 0.01, daemons: [ @@ -35,10 +47,7 @@ export default class Predictor { */ supervisor: Supervisor; - /** - * @param {Object} config - */ - constructor(config = DEFAULT_CONFIG) { + constructor(config: PredictorConfig = DEFAULT_CONFIG) { this.base = config.base; this.score = 0; this.journal = new Journal(); @@ -47,25 +56,17 @@ export default class Predictor { } pass(humanValue: number): number { - const value = humanValue; - if (value < 0 || value >= this.base) { + if (humanValue < 0 || humanValue >= 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); + this.score += prediction === humanValue ? -1 : 1; + this.supervisor.adjust(this.journal, humanValue); + this.journal.makeMove(humanValue, prediction); return prediction; } - /** - * @param {Object} daemonConfigs - * - * @returns {Daemon[]} - * - * @private - */ - private _createDaemons(daemonConfigs) { + private _createDaemons(daemonConfigs: DaemonConfig[]): Daemon[] { return daemonConfigs.map(config => { return new Daemon( this.base, diff --git a/src/Supervisor.ts b/src/Supervisor.ts index 468c075..95e67e7 100644 --- a/src/Supervisor.ts +++ b/src/Supervisor.ts @@ -3,8 +3,20 @@ import Daemon from './Daemon'; const DEFAULT_EPSILON = 0.01; +interface DaemonRate { + daemon: Daemon; + rate: number; +} + +interface Prediction { + daemonRate: DaemonRate; + rate: number; + power: number; + value: number; +} + class Supervisor { - daemons: { daemon: Daemon; rate: number }[] = []; + daemonRates: DaemonRate[] = []; readonly epsilon: number; @@ -13,7 +25,7 @@ class Supervisor { throw Error('Empty daemon list'); } - this.daemons = daemons.map(daemon => ({ + this.daemonRates = daemons.map(daemon => ({ daemon: daemon, rate: 0, })); @@ -28,43 +40,29 @@ class Supervisor { return ordered[0].value; } - adjust(journal: Journal, humanValue) { + adjust(journal: Journal, humanValue: number) { const predictions = this._createPredictions(journal); for (const prediction of predictions) { if (prediction.value === humanValue) { - prediction.daemon.rate += this._getAdjustmentWeight( + prediction.daemonRate.rate += this._getAdjustmentWeight( journal.length ); } - prediction.daemon.daemon.adjust(journal, humanValue); + prediction.daemonRate.daemon.adjust(journal, humanValue); } } - /** - * @param {Journal} journal - * - * @returns {Array} - * - * @private - */ - private _createPredictions(journal: Journal) { - return this.daemons.map(daemon => ({ - daemon: daemon, - power: daemon.daemon.power, - rate: daemon.rate, - value: daemon.daemon.predict(journal), + private _createPredictions(journal: Journal): Prediction[] { + return this.daemonRates.map(daemonRate => ({ + daemonRate: daemonRate, + power: daemonRate.daemon.power, + rate: daemonRate.rate, + value: daemonRate.daemon.predict(journal), })); } - /** - * @param {Array} predictions - * - * @returns {Array} - * - * @private - */ - private _sortPredictions(predictions) { - return predictions.sort((result1, result2) => { + private _sortPredictions(predictions: Prediction[]) { + return predictions.sort((result1: Prediction, result2: Prediction) => { const rateDiff = result2.rate - result1.rate; if (Math.abs(rateDiff) > 0.000001) { return rateDiff; diff --git a/tests/PredictorTest.ts b/tests/PredictorTest.ts new file mode 100644 index 0000000..53d934c --- /dev/null +++ b/tests/PredictorTest.ts @@ -0,0 +1,17 @@ +import { it, describe } from 'mocha'; +import { expect } from 'chai'; + +import Predictor from '../src/Predictor'; + +describe('Predictor', function() { + it('Get prediction for one daemon state', function() { + const predictor = new Predictor({ + base: 2, + supervisor_epsilon: 0.01, + daemons: [{ robot: 1, human: 1, epsilon: 0.01 }], + }); + const predicted = predictor.pass(1); + expect(predicted).to.equals(0); + expect(predictor.stepCount()).to.equals(1); + }); +}); diff --git a/tests/SupervisorTest.ts b/tests/SupervisorTest.ts new file mode 100644 index 0000000..fdf1d59 --- /dev/null +++ b/tests/SupervisorTest.ts @@ -0,0 +1,30 @@ +import { it, describe } from 'mocha'; +import { expect } from 'chai'; + +import Supervisor from '../src/Supervisor'; +import Daemon from '../src/Daemon'; +import Journal from '../src/Journal'; + +describe('Supervisor', function() { + it('Get prediction for one daemon state', function() { + const supervisor = new Supervisor([new Daemon(2, 1, 1, 0.01)], 0.01); + const journal = new Journal(); + + const human1 = 1; + const predicted1 = supervisor.predict(journal); + expect(0).to.equals(predicted1, 'First prediction for empty journal'); + + journal.makeMove(human1, predicted1); + supervisor.adjust(journal, human1); + + const human2 = 1; + const predicted2 = supervisor.predict(journal); + expect(1).to.equals( + predicted2, + `Second prediction for (${human1}, ${predicted1})` + ); + + journal.makeMove(human2, predicted1); + supervisor.adjust(journal, human2); + }); +});