Write more tests
This commit is contained in:
parent
eb66b6904d
commit
9e06a1c630
@ -47,7 +47,7 @@ class Daemon {
|
|||||||
return proposals.indexOf(maxWeight);
|
return proposals.indexOf(maxWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
adjust(journal: Journal, humanValue: number) {
|
adjust(journal: Journal, humanValue: number): void {
|
||||||
const steps = this._getStepSlice(journal);
|
const steps = this._getStepSlice(journal);
|
||||||
const adjustmentWeight = this._getAdjustmentWeight(journal.length);
|
const adjustmentWeight = this._getAdjustmentWeight(journal.length);
|
||||||
this._adjustWeight([...steps, humanValue], adjustmentWeight);
|
this._adjustWeight([...steps, humanValue], adjustmentWeight);
|
||||||
|
@ -2,7 +2,19 @@ import Daemon from './Daemon';
|
|||||||
import Journal from './Journal';
|
import Journal from './Journal';
|
||||||
import Supervisor from './Supervisor';
|
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,
|
base: 2,
|
||||||
supervisor_epsilon: 0.01,
|
supervisor_epsilon: 0.01,
|
||||||
daemons: [
|
daemons: [
|
||||||
@ -35,10 +47,7 @@ export default class Predictor {
|
|||||||
*/
|
*/
|
||||||
supervisor: Supervisor;
|
supervisor: Supervisor;
|
||||||
|
|
||||||
/**
|
constructor(config: PredictorConfig = DEFAULT_CONFIG) {
|
||||||
* @param {Object} config
|
|
||||||
*/
|
|
||||||
constructor(config = DEFAULT_CONFIG) {
|
|
||||||
this.base = config.base;
|
this.base = config.base;
|
||||||
this.score = 0;
|
this.score = 0;
|
||||||
this.journal = new Journal();
|
this.journal = new Journal();
|
||||||
@ -47,25 +56,17 @@ export default class Predictor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pass(humanValue: number): number {
|
pass(humanValue: number): number {
|
||||||
const value = humanValue;
|
if (humanValue < 0 || humanValue >= this.base) {
|
||||||
if (value < 0 || value >= this.base) {
|
|
||||||
throw new Error(`Passed value must be in [0, ${this.base})`);
|
throw new Error(`Passed value must be in [0, ${this.base})`);
|
||||||
}
|
}
|
||||||
const prediction = this.supervisor.predict(this.journal);
|
const prediction = this.supervisor.predict(this.journal);
|
||||||
this.score += prediction === value ? -1 : 1;
|
this.score += prediction === humanValue ? -1 : 1;
|
||||||
this.supervisor.adjust(this.journal, value);
|
this.supervisor.adjust(this.journal, humanValue);
|
||||||
this.journal.makeMove(value, prediction);
|
this.journal.makeMove(humanValue, prediction);
|
||||||
return prediction;
|
return prediction;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private _createDaemons(daemonConfigs: DaemonConfig[]): Daemon[] {
|
||||||
* @param {Object} daemonConfigs
|
|
||||||
*
|
|
||||||
* @returns {Daemon[]}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private _createDaemons(daemonConfigs) {
|
|
||||||
return daemonConfigs.map(config => {
|
return daemonConfigs.map(config => {
|
||||||
return new Daemon(
|
return new Daemon(
|
||||||
this.base,
|
this.base,
|
||||||
|
@ -3,8 +3,20 @@ import Daemon from './Daemon';
|
|||||||
|
|
||||||
const DEFAULT_EPSILON = 0.01;
|
const DEFAULT_EPSILON = 0.01;
|
||||||
|
|
||||||
|
interface DaemonRate {
|
||||||
|
daemon: Daemon;
|
||||||
|
rate: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Prediction {
|
||||||
|
daemonRate: DaemonRate;
|
||||||
|
rate: number;
|
||||||
|
power: number;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
class Supervisor {
|
class Supervisor {
|
||||||
daemons: { daemon: Daemon; rate: number }[] = [];
|
daemonRates: DaemonRate[] = [];
|
||||||
|
|
||||||
readonly epsilon: number;
|
readonly epsilon: number;
|
||||||
|
|
||||||
@ -13,7 +25,7 @@ class Supervisor {
|
|||||||
throw Error('Empty daemon list');
|
throw Error('Empty daemon list');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.daemons = daemons.map(daemon => ({
|
this.daemonRates = daemons.map(daemon => ({
|
||||||
daemon: daemon,
|
daemon: daemon,
|
||||||
rate: 0,
|
rate: 0,
|
||||||
}));
|
}));
|
||||||
@ -28,43 +40,29 @@ class Supervisor {
|
|||||||
return ordered[0].value;
|
return ordered[0].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
adjust(journal: Journal, humanValue) {
|
adjust(journal: Journal, humanValue: number) {
|
||||||
const predictions = this._createPredictions(journal);
|
const predictions = this._createPredictions(journal);
|
||||||
for (const prediction of predictions) {
|
for (const prediction of predictions) {
|
||||||
if (prediction.value === humanValue) {
|
if (prediction.value === humanValue) {
|
||||||
prediction.daemon.rate += this._getAdjustmentWeight(
|
prediction.daemonRate.rate += this._getAdjustmentWeight(
|
||||||
journal.length
|
journal.length
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
prediction.daemon.daemon.adjust(journal, humanValue);
|
prediction.daemonRate.daemon.adjust(journal, humanValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private _createPredictions(journal: Journal): Prediction[] {
|
||||||
* @param {Journal} journal
|
return this.daemonRates.map(daemonRate => ({
|
||||||
*
|
daemonRate: daemonRate,
|
||||||
* @returns {Array}
|
power: daemonRate.daemon.power,
|
||||||
*
|
rate: daemonRate.rate,
|
||||||
* @private
|
value: daemonRate.daemon.predict(journal),
|
||||||
*/
|
|
||||||
private _createPredictions(journal: Journal) {
|
|
||||||
return this.daemons.map(daemon => ({
|
|
||||||
daemon: daemon,
|
|
||||||
power: daemon.daemon.power,
|
|
||||||
rate: daemon.rate,
|
|
||||||
value: daemon.daemon.predict(journal),
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private _sortPredictions(predictions: Prediction[]) {
|
||||||
* @param {Array} predictions
|
return predictions.sort((result1: Prediction, result2: Prediction) => {
|
||||||
*
|
|
||||||
* @returns {Array}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private _sortPredictions(predictions) {
|
|
||||||
return predictions.sort((result1, result2) => {
|
|
||||||
const rateDiff = result2.rate - result1.rate;
|
const rateDiff = result2.rate - result1.rate;
|
||||||
if (Math.abs(rateDiff) > 0.000001) {
|
if (Math.abs(rateDiff) > 0.000001) {
|
||||||
return rateDiff;
|
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