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

129 lines
4 KiB
TypeScript

module TS.SpaceTac {
// Base class for all Artificial Intelligence interaction
export class AbstractAI {
// Name of the AI
name: string;
// Current ship being played
ship: Ship;
// Time at which work as started
started: number;
// Random generator, if needed
random: RandomGenerator;
// Timer for scheduled calls
timer: Timer;
// Queue of work items to process
// Work items will be called successively, leaving time for other processing between them.
// So work items should always be as short as possible.
// When the queue is empty, the ship will end its turn.
private workqueue: Function[];
constructor(ship: Ship, timer = Timer.global, name: string = null) {
this.name = name || classname(this);
this.ship = ship;
this.workqueue = [];
this.random = new RandomGenerator();
this.timer = timer;
}
toString = () => this.name;
// Play a ship turn
// This will start asynchronous work. The AI will then call action methods, then advanceToNextShip to
// indicate it has finished.
play(): void {
this.workqueue = [];
this.started = (new Date()).getTime();
if (!this.ship.playing) {
console.error(`${this.name} tries to play a ship out of turn`);
} else {
this.initWork();
if (this.workqueue.length > 0) {
this.processNextWorkItem();
} else {
this.endTurn();
}
}
}
// Add a work item to the work queue
addWorkItem(item: Function, delay = 100): void {
if (this.timer.isSynchronous()) {
if (item) {
item();
}
} else {
var wrapped = () => {
if (item) {
item();
}
this.processNextWorkItem();
};
this.workqueue.push(() => this.timer.schedule(delay, wrapped));
}
}
// Initially fill the work queue.
// Subclasses MUST reimplement this and call addWorkItem to add work to do.
protected initWork(): void {
// Abstract method
}
/**
* Get the time spent thinking by the AI.
*/
private getDuration() {
return (new Date()).getTime() - this.started;
}
// Process the next work item
private processNextWorkItem(): void {
if (this.workqueue.length > 0) {
if (this.getDuration() >= 10000) {
console.warn("AI take too long to play, forcing turn end");
this.effectiveEndTurn();
} else {
// Take the first item
var item = this.workqueue.shift();
item();
}
} else {
this.endTurn();
}
}
/**
* Effectively end the current ship's turn
*/
private effectiveEndTurn() {
if (this.workqueue.length > 0) {
console.error(`${this.name} ends turn, but there is pending work`);
}
if (this.ship.playing) {
let battle = this.ship.getBattle();
this.ship.endTurn();
this.ship = null;
if (battle) {
battle.advanceToNextShip();
}
} else {
console.error(`${this.name} tries to end turn of another ship`);
}
}
/**
* Called when we want the AI decides to end the ship turn
*/
private endTurn(): void {
// Delay, as to make the AI not too fast to play
this.timer.schedule(2000 - this.getDuration(), () => this.effectiveEndTurn());
}
}
}