Write more tests

This commit is contained in:
Anton Vakhrushev 2020-03-22 13:30:37 +03:00
parent eb66b6904d
commit 9e06a1c630
5 changed files with 92 additions and 46 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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;

17
tests/PredictorTest.ts Normal file
View File

@ -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);
});
});

30
tests/SupervisorTest.ts Normal file
View File

@ -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);
});
});