1
0
Fork 0
spacetac/src/core/ai/ContinuousAI.ts

135 lines
3.6 KiB
TypeScript

module TK.SpaceTac {
export type AIPlanProducer = Iterable<AIPlan>;
export type AIPlanScoring = (plan: AIPlan) => number;
/**
* Standard system for Artificial Intelligence interaction
*
* An AI should work indefinitely on a battle state, to provide the best TurnPlan possible
*/
export class ContinuousAI {
// Timer for scheduled calls
timer = Timer.global
// Time at which work as started
started = 0
// Best plan so far
best_plan = new AIPlan()
// Number of plans produced
produced = 0
// Is the work interrupted
interrupted = false
constructor(readonly settings: AISettings, public debug = false) {
}
/**
* Start working on the current battle state.
*
* This will only be interrupted by a call to getPlan.
*/
async play(): Promise<void> {
// Init
this.interrupted = false;
this.produced = 0;
this.best_plan = new AIPlan();
this.started = (new Date()).getTime();
// Work loop
const producer = this.getPlanProducer();
const scoring = this.getPlanScoring();
for (let plan of producer) {
if (plan.isValid()) {
if (!plan.isScored()) {
plan.setScore(scoring(plan));
}
this.produced++;
this.pushScoredPlan(plan);
} else {
console.warn("AI produced an invalid plan", this, plan);
}
if (this.interrupted) {
break;
}
await this.timer.sleep(10);
}
this.interrupted = true;
}
/**
* Interrupt the thinking
*/
interrupt(): void {
this.interrupted = true;
}
/**
* Ask for a final plan to play
*/
async getPlan(): Promise<AIPlan> {
// Leave at least 5 seconds of thinking
while (!this.interrupted && this.getDuration() < 5000) {
await this.timer.sleep(1000);
}
// TODO Wait for a sufficient score, at most 5 seconds
this.interrupt();
const result = this.peekBestPlan();
if (this.debug) {
console.log(`AI plan after ${this.produced} produced:`, result);
}
return result;
}
/**
* Get the producer of plans for this AI
*
* Plans produced may be scored or not.
* The iterable may (in fact, should) be infinite.
*/
getPlanProducer(): AIPlanProducer {
return this.settings.producer;
}
/**
* Get the plan scoring method for this AI
*
* A standard scoring system is provided by default
*/
getPlanScoring(): AIPlanScoring {
return this.settings.scoring;
}
/**
* Add a scored plan to the memory (by default, it keeps only the best one)
*/
pushScoredPlan(plan: AIPlan): void {
if (plan.score >= this.best_plan.score) {
this.best_plan = plan;
}
}
/**
* Peek at the current best plan the AI came with
*/
peekBestPlan(): AIPlan {
return this.best_plan;
}
/**
* Get the time spent thinking on this turn
*/
getDuration() {
return (new Date()).getTime() - this.started;
}
}
}