Write more tests
This commit is contained in:
parent
eb66b6904d
commit
9e06a1c630
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
17
tests/PredictorTest.ts
Normal 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
30
tests/SupervisorTest.ts
Normal 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);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user