Add history for predictor moves
This commit is contained in:
parent
9e06a1c630
commit
49bb3da4bb
@ -1,34 +1,42 @@
|
|||||||
import Journal from './Journal';
|
import Journal from './Journal';
|
||||||
|
|
||||||
const DEFAULT_EPSILON = 0.01;
|
|
||||||
|
|
||||||
function create_key(steps: number[]): string {
|
function create_key(steps: number[]): string {
|
||||||
return steps.join(':');
|
return steps.join(':');
|
||||||
}
|
}
|
||||||
|
|
||||||
class Daemon {
|
class Daemon {
|
||||||
base: number;
|
static DEFAULT_EPSILON = 0.01;
|
||||||
|
|
||||||
humanCount: number;
|
private readonly thisId: string;
|
||||||
|
|
||||||
robotCount: number;
|
private readonly base: number;
|
||||||
|
|
||||||
epsilon: number;
|
private readonly humanCount: number;
|
||||||
|
|
||||||
weights: { [key: string]: number } = {};
|
private readonly robotCount: number;
|
||||||
|
|
||||||
|
private readonly epsilon: number;
|
||||||
|
|
||||||
|
private weights: { [key: string]: number } = {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
id: string,
|
||||||
base: number,
|
base: number,
|
||||||
humanCount: number,
|
humanCount: number,
|
||||||
robotCount: number,
|
robotCount: number,
|
||||||
epsilon: number = DEFAULT_EPSILON
|
epsilon: number = Daemon.DEFAULT_EPSILON
|
||||||
) {
|
) {
|
||||||
|
this.thisId = id;
|
||||||
this.base = base;
|
this.base = base;
|
||||||
this.humanCount = humanCount;
|
this.humanCount = humanCount;
|
||||||
this.robotCount = robotCount;
|
this.robotCount = robotCount;
|
||||||
this.epsilon = epsilon;
|
this.epsilon = epsilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get id(): string {
|
||||||
|
return this.thisId;
|
||||||
|
}
|
||||||
|
|
||||||
get power(): number {
|
get power(): number {
|
||||||
return this.humanCount + this.robotCount;
|
return this.humanCount + this.robotCount;
|
||||||
}
|
}
|
||||||
@ -53,6 +61,10 @@ class Daemon {
|
|||||||
this._adjustWeight([...steps, humanValue], adjustmentWeight);
|
this._adjustWeight([...steps, humanValue], adjustmentWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getWeights() {
|
||||||
|
return { ...this.weights };
|
||||||
|
}
|
||||||
|
|
||||||
private _getStepSlice(journal: Journal): number[] {
|
private _getStepSlice(journal: Journal): number[] {
|
||||||
return journal.getLastMovements(this.humanCount, this.robotCount);
|
return journal.getLastMovements(this.humanCount, this.robotCount);
|
||||||
}
|
}
|
||||||
|
16
src/Move.ts
16
src/Move.ts
@ -2,12 +2,20 @@
|
|||||||
* Represents one game move.
|
* Represents one game move.
|
||||||
*/
|
*/
|
||||||
class Move {
|
class Move {
|
||||||
public human: number;
|
private readonly itsHuman: number;
|
||||||
public robot: number;
|
private readonly itsRobot: number;
|
||||||
|
|
||||||
constructor(human: number, robot: number) {
|
constructor(human: number, robot: number) {
|
||||||
this.human = human;
|
this.itsHuman = human;
|
||||||
this.robot = robot;
|
this.itsRobot = robot;
|
||||||
|
}
|
||||||
|
|
||||||
|
get human() {
|
||||||
|
return this.itsHuman;
|
||||||
|
}
|
||||||
|
|
||||||
|
get robot() {
|
||||||
|
return this.itsRobot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,10 @@ import Journal from './Journal';
|
|||||||
import Supervisor from './Supervisor';
|
import Supervisor from './Supervisor';
|
||||||
|
|
||||||
interface DaemonConfig {
|
interface DaemonConfig {
|
||||||
|
id?: string;
|
||||||
human: number;
|
human: number;
|
||||||
robot: number;
|
robot: number;
|
||||||
epsilon: number;
|
epsilon?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PredictorConfig {
|
interface PredictorConfig {
|
||||||
@ -14,15 +15,22 @@ interface PredictorConfig {
|
|||||||
daemons: DaemonConfig[];
|
daemons: DaemonConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface HistoryRecord {
|
||||||
|
score: number;
|
||||||
|
move: [number, number];
|
||||||
|
rates: { [id: string]: number };
|
||||||
|
weights: { [id: string]: any };
|
||||||
|
}
|
||||||
|
|
||||||
const DEFAULT_CONFIG: PredictorConfig = {
|
const DEFAULT_CONFIG: PredictorConfig = {
|
||||||
base: 2,
|
base: 2,
|
||||||
supervisor_epsilon: 0.01,
|
supervisor_epsilon: 0.01,
|
||||||
daemons: [
|
daemons: [
|
||||||
{ human: 2, robot: 2, epsilon: 0.01 },
|
{ human: 2, robot: 2 },
|
||||||
{ human: 3, robot: 3, epsilon: 0.01 },
|
{ human: 3, robot: 3 },
|
||||||
{ human: 4, robot: 4, epsilon: 0.01 },
|
{ human: 4, robot: 4 },
|
||||||
{ human: 5, robot: 5, epsilon: 0.01 },
|
{ human: 5, robot: 5 },
|
||||||
{ human: 6, robot: 6, epsilon: 0.01 },
|
{ human: 6, robot: 6 },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -30,7 +38,7 @@ export default class Predictor {
|
|||||||
/**
|
/**
|
||||||
* @type {Number}
|
* @type {Number}
|
||||||
*/
|
*/
|
||||||
base: number;
|
readonly base: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Number}
|
* @type {Number}
|
||||||
@ -47,12 +55,15 @@ export default class Predictor {
|
|||||||
*/
|
*/
|
||||||
supervisor: Supervisor;
|
supervisor: Supervisor;
|
||||||
|
|
||||||
|
history: HistoryRecord[];
|
||||||
|
|
||||||
constructor(config: PredictorConfig = DEFAULT_CONFIG) {
|
constructor(config: PredictorConfig = DEFAULT_CONFIG) {
|
||||||
this.base = config.base;
|
this.base = config.base;
|
||||||
this.score = 0;
|
this.score = 0;
|
||||||
this.journal = new Journal();
|
this.journal = new Journal();
|
||||||
const daemons = this._createDaemons(config.daemons);
|
const daemons = this._createDaemons(config.daemons);
|
||||||
this.supervisor = new Supervisor(daemons, config.supervisor_epsilon);
|
this.supervisor = new Supervisor(daemons, config.supervisor_epsilon);
|
||||||
|
this.history = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
pass(humanValue: number): number {
|
pass(humanValue: number): number {
|
||||||
@ -63,16 +74,29 @@ export default class Predictor {
|
|||||||
this.score += prediction === humanValue ? -1 : 1;
|
this.score += prediction === humanValue ? -1 : 1;
|
||||||
this.supervisor.adjust(this.journal, humanValue);
|
this.supervisor.adjust(this.journal, humanValue);
|
||||||
this.journal.makeMove(humanValue, prediction);
|
this.journal.makeMove(humanValue, prediction);
|
||||||
|
this.history.push({
|
||||||
|
score: this.score,
|
||||||
|
move: [humanValue, prediction],
|
||||||
|
rates: this.supervisor.rates(),
|
||||||
|
weights: this.supervisor.weights(),
|
||||||
|
});
|
||||||
return prediction;
|
return prediction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showHistory(deep: number): HistoryRecord[] {
|
||||||
|
return this.history.slice(-deep);
|
||||||
|
}
|
||||||
|
|
||||||
private _createDaemons(daemonConfigs: DaemonConfig[]): Daemon[] {
|
private _createDaemons(daemonConfigs: DaemonConfig[]): Daemon[] {
|
||||||
return daemonConfigs.map(config => {
|
return daemonConfigs.map(config => {
|
||||||
|
const epsilon = config.epsilon || Daemon.DEFAULT_EPSILON;
|
||||||
return new Daemon(
|
return new Daemon(
|
||||||
|
config.id ||
|
||||||
|
`daemon-${config.human}-${config.robot}-${epsilon}`,
|
||||||
this.base,
|
this.base,
|
||||||
config.human,
|
config.human,
|
||||||
config.robot,
|
config.robot,
|
||||||
config.epsilon || 0.01
|
epsilon
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import Journal from './Journal';
|
import Journal from './Journal';
|
||||||
import Daemon from './Daemon';
|
import Daemon from './Daemon';
|
||||||
|
|
||||||
const DEFAULT_EPSILON = 0.01;
|
|
||||||
|
|
||||||
interface DaemonRate {
|
interface DaemonRate {
|
||||||
daemon: Daemon;
|
daemon: Daemon;
|
||||||
rate: number;
|
rate: number;
|
||||||
@ -16,11 +14,16 @@ interface Prediction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Supervisor {
|
class Supervisor {
|
||||||
|
static DEFAULT_EPSILON = 0.01;
|
||||||
|
|
||||||
daemonRates: DaemonRate[] = [];
|
daemonRates: DaemonRate[] = [];
|
||||||
|
|
||||||
readonly epsilon: number;
|
readonly epsilon: number;
|
||||||
|
|
||||||
constructor(daemons: Daemon[], epsilon: number = DEFAULT_EPSILON) {
|
constructor(
|
||||||
|
daemons: Daemon[],
|
||||||
|
epsilon: number = Supervisor.DEFAULT_EPSILON
|
||||||
|
) {
|
||||||
if (!daemons || daemons.length === 0) {
|
if (!daemons || daemons.length === 0) {
|
||||||
throw Error('Empty daemon list');
|
throw Error('Empty daemon list');
|
||||||
}
|
}
|
||||||
@ -52,6 +55,22 @@ class Supervisor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rates() {
|
||||||
|
const result = {};
|
||||||
|
this.daemonRates.forEach(r => {
|
||||||
|
result[r.daemon.id] = r.rate;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
weights() {
|
||||||
|
const result = {};
|
||||||
|
this.daemonRates.forEach(r => {
|
||||||
|
result[r.daemon.id] = r.daemon.getWeights();
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private _createPredictions(journal: Journal): Prediction[] {
|
private _createPredictions(journal: Journal): Prediction[] {
|
||||||
return this.daemonRates.map(daemonRate => ({
|
return this.daemonRates.map(daemonRate => ({
|
||||||
daemonRate: daemonRate,
|
daemonRate: daemonRate,
|
||||||
|
@ -3,23 +3,32 @@ import { expect } from 'chai';
|
|||||||
|
|
||||||
import Daemon from '../src/Daemon';
|
import Daemon from '../src/Daemon';
|
||||||
import Journal from '../src/Journal';
|
import Journal from '../src/Journal';
|
||||||
|
import Move from '../src/Move';
|
||||||
|
|
||||||
describe('Daemon', function() {
|
describe('Daemon', function() {
|
||||||
it('Get prediction for beginning', function() {
|
it('Get prediction for beginning', function() {
|
||||||
const journal = new Journal();
|
const daemon = new Daemon('d1', 2, 1, 1);
|
||||||
const daemon = new Daemon(2, 1, 1);
|
expect('d1').to.equals(daemon.id);
|
||||||
const predicted = daemon.predict(journal);
|
|
||||||
expect(predicted).to.equals(0);
|
const predicted = daemon.predict(new Journal());
|
||||||
|
expect(0).to.equals(predicted);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Can get power', function() {
|
it('Can get power', function() {
|
||||||
const d = new Daemon(2, 5, 8);
|
const d = new Daemon('d1', 2, 5, 8);
|
||||||
expect(d.power).to.eqls(13);
|
expect(13).to.eqls(d.power);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Can adjust', function() {
|
||||||
|
const journal = new Journal([new Move(0, 0), new Move(0, 0)]);
|
||||||
|
const daemon = new Daemon('d1', 2, 1, 1, 0.1);
|
||||||
|
daemon.adjust(journal, 1);
|
||||||
|
expect({ '0:0:1': 1.1 ** 2 }).to.eqls(daemon.getWeights());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Daemon 1-1', function() {
|
it('Daemon 1-1', function() {
|
||||||
const journal = new Journal();
|
const journal = new Journal();
|
||||||
const daemon = new Daemon(2, 1, 1, 0.1);
|
const daemon = new Daemon('d1', 2, 1, 1, 0.1);
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,10 @@ import Journal from '../src/Journal';
|
|||||||
|
|
||||||
describe('Supervisor', function() {
|
describe('Supervisor', function() {
|
||||||
it('Get prediction for one daemon state', function() {
|
it('Get prediction for one daemon state', function() {
|
||||||
const supervisor = new Supervisor([new Daemon(2, 1, 1, 0.01)], 0.01);
|
const supervisor = new Supervisor(
|
||||||
|
[new Daemon('d1', 2, 1, 1, 0.1)],
|
||||||
|
0.1
|
||||||
|
);
|
||||||
const journal = new Journal();
|
const journal = new Journal();
|
||||||
|
|
||||||
const human1 = 1;
|
const human1 = 1;
|
||||||
@ -26,5 +29,7 @@ describe('Supervisor', function() {
|
|||||||
|
|
||||||
journal.makeMove(human2, predicted1);
|
journal.makeMove(human2, predicted1);
|
||||||
supervisor.adjust(journal, human2);
|
supervisor.adjust(journal, human2);
|
||||||
|
|
||||||
|
expect({ d1: 1.1 ** 2 }).to.eqls(supervisor.rates());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user