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

135 lines
3.6 KiB
TypeScript
Raw Normal View History

2017-09-24 22:23:22 +00:00
module TK.SpaceTac {
export type AIPlanProducer = Iterable<AIPlan>;
export type AIPlanScoring = (plan: AIPlan) => number;
/**
2019-05-23 17:54:05 +00:00
* Standard system for Artificial Intelligence interaction
*
* An AI should work indefinitely on a battle state, to provide the best TurnPlan possible
*/
2019-05-23 17:54:05 +00:00
export class ContinuousAI {
2017-02-16 22:59:41 +00:00
// Timer for scheduled calls
timer = Timer.global
// Time at which work as started
2019-05-23 17:54:05 +00:00
started = 0
2015-02-17 00:00:00 +00:00
// Best plan so far
2019-05-23 17:54:05 +00:00
best_plan = new AIPlan()
// Number of plans produced
2019-05-23 17:54:05 +00:00
produced = 0
// Is the work interrupted
2019-05-23 17:54:05 +00:00
interrupted = false
2015-02-17 00:00:00 +00:00
2019-05-23 17:54:05 +00:00
constructor(readonly settings: AISettings, public debug = false) {
}
2017-02-24 00:34:31 +00:00
/**
* 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();
2015-02-17 00:00:00 +00:00
this.started = (new Date()).getTime();
2017-02-24 00:34:31 +00:00
// 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 {
2019-05-23 17:54:05 +00:00
console.warn("AI produced an invalid plan", this, plan);
}
if (this.interrupted) {
break;
2015-02-17 00:00:00 +00:00
}
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;
2015-02-17 00:00:00 +00:00
}
2017-02-16 22:59:41 +00:00
/**
* Get the producer of plans for this AI
*
* Plans produced may be scored or not.
* The iterable may (in fact, should) be infinite.
2017-02-16 22:59:41 +00:00
*/
getPlanProducer(): AIPlanProducer {
2019-05-23 17:54:05 +00:00
return this.settings.producer;
}
/**
* Get the plan scoring method for this AI
*
* A standard scoring system is provided by default
*/
getPlanScoring(): AIPlanScoring {
2019-05-23 17:54:05 +00:00
return this.settings.scoring;
}
/**
* Add a scored plan to the memory (by default, it keeps only the best one)
*/
pushScoredPlan(plan: AIPlan): void {
2019-05-23 17:54:05 +00:00
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;
2017-02-16 22:59:41 +00:00
}
/**
* Get the time spent thinking on this turn
2017-02-16 22:59:41 +00:00
*/
getDuration() {
return (new Date()).getTime() - this.started;
}
}
}