2017-09-24 22:23:22 +00:00
|
|
|
module TK.SpaceTac {
|
2017-08-23 17:59:22 +00:00
|
|
|
/**
|
|
|
|
* Base class for all Artificial Intelligence interaction
|
|
|
|
*/
|
2017-02-07 18:54:53 +00:00
|
|
|
export class AbstractAI {
|
2017-02-21 21:16:18 +00:00
|
|
|
// Name of the AI
|
2017-08-23 17:59:22 +00:00
|
|
|
name: string
|
2015-02-16 00:00:00 +00:00
|
|
|
|
2015-02-17 00:00:00 +00:00
|
|
|
// Current ship being played
|
2017-08-23 17:59:22 +00:00
|
|
|
ship: Ship
|
2015-02-17 00:00:00 +00:00
|
|
|
|
2015-04-15 00:00:00 +00:00
|
|
|
// Random generator, if needed
|
2017-08-23 17:59:22 +00:00
|
|
|
random = RandomGenerator.global
|
2015-04-15 00:00:00 +00:00
|
|
|
|
2017-02-16 22:59:41 +00:00
|
|
|
// Timer for scheduled calls
|
2017-08-23 17:59:22 +00:00
|
|
|
timer: Timer
|
2017-02-16 22:59:41 +00:00
|
|
|
|
2017-08-23 17:59:22 +00:00
|
|
|
// Debug mode
|
|
|
|
debug = false
|
|
|
|
|
|
|
|
// Time at which work as started
|
|
|
|
private started: number
|
2015-02-17 00:00:00 +00:00
|
|
|
|
2017-03-09 17:11:00 +00:00
|
|
|
constructor(ship: Ship, timer = Timer.global, name?: string) {
|
2017-02-21 21:16:18 +00:00
|
|
|
this.ship = ship;
|
|
|
|
this.timer = timer;
|
2017-08-23 17:59:22 +00:00
|
|
|
this.name = name || classname(this);
|
2015-02-17 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
2017-02-24 00:34:31 +00:00
|
|
|
toString = () => this.name;
|
|
|
|
|
2017-08-23 17:59:22 +00:00
|
|
|
/**
|
|
|
|
* Start playing current ship's turn.
|
|
|
|
*/
|
|
|
|
async play(debug = false): Promise<void> {
|
2015-02-17 00:00:00 +00:00
|
|
|
this.started = (new Date()).getTime();
|
2017-08-23 17:59:22 +00:00
|
|
|
this.debug = debug;
|
2017-02-24 00:34:31 +00:00
|
|
|
|
|
|
|
if (!this.ship.playing) {
|
|
|
|
console.error(`${this.name} tries to play a ship out of turn`);
|
2017-08-23 17:59:22 +00:00
|
|
|
return;
|
2017-02-16 22:59:41 +00:00
|
|
|
}
|
2015-02-17 00:00:00 +00:00
|
|
|
|
2017-08-23 17:59:22 +00:00
|
|
|
// Work loop
|
|
|
|
this.initWork();
|
|
|
|
let last = new Date().getTime();
|
|
|
|
let ship = this.ship;
|
|
|
|
while (this.doWorkUnit()) {
|
|
|
|
if (!this.ship.playing || this.ship != ship) {
|
|
|
|
console.error(`${this.name} switched to another ship in unit work`);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (this.getDuration() >= 10000) {
|
|
|
|
console.warn(`${this.name} takes too long to play, forcing turn end`);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
let t = new Date().getTime();
|
|
|
|
if (t - last > 50) {
|
|
|
|
await this.timer.sleep(10);
|
|
|
|
last = t + 10;
|
2015-02-17 00:00:00 +00:00
|
|
|
}
|
2015-02-19 00:00:00 +00:00
|
|
|
}
|
2015-02-17 00:00:00 +00:00
|
|
|
|
2017-08-23 17:59:22 +00:00
|
|
|
// End the ship turn
|
2017-11-14 00:07:06 +00:00
|
|
|
this.applyAction(EndTurnAction.SINGLETON, Target.newFromShip(ship));
|
2015-02-17 00:00:00 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 22:59:41 +00:00
|
|
|
/**
|
2017-08-23 17:59:22 +00:00
|
|
|
* Make the AI play an action
|
|
|
|
*
|
|
|
|
* This should be the only real interaction point with battle state
|
2017-02-16 22:59:41 +00:00
|
|
|
*/
|
2017-09-19 15:09:06 +00:00
|
|
|
private applyAction(action: BaseAction, target: Target): boolean {
|
2017-11-14 00:07:06 +00:00
|
|
|
let battle = this.ship.getBattle();
|
|
|
|
if (battle) {
|
|
|
|
return battle.applyOneAction(action, target);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2017-02-16 22:59:41 +00:00
|
|
|
}
|
|
|
|
|
2017-08-23 17:59:22 +00:00
|
|
|
/**
|
|
|
|
* Make the AI play a full maneuver (sequence of actions)
|
|
|
|
*/
|
|
|
|
applyManeuver(maneuver: Maneuver): boolean {
|
|
|
|
if (maneuver.simulation.success) {
|
|
|
|
let parts = maneuver.simulation.parts;
|
|
|
|
for (let i = 0; i < parts.length; i++) {
|
|
|
|
let part = parts[i];
|
|
|
|
if (part.action instanceof EndTurnAction || !part.possible || !this.applyAction(part.action, part.target)) {
|
|
|
|
return false;
|
2017-03-09 17:11:00 +00:00
|
|
|
}
|
2017-02-16 22:59:41 +00:00
|
|
|
}
|
2017-08-23 17:59:22 +00:00
|
|
|
return true;
|
2015-02-17 00:00:00 +00:00
|
|
|
} else {
|
2017-08-23 17:59:22 +00:00
|
|
|
return false;
|
2015-02-17 00:00:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-16 22:59:41 +00:00
|
|
|
/**
|
2017-08-23 17:59:22 +00:00
|
|
|
* Prepare the groundwork for future doWorkUnit calls
|
2017-02-16 22:59:41 +00:00
|
|
|
*/
|
2017-08-23 17:59:22 +00:00
|
|
|
protected initWork(): void {
|
|
|
|
}
|
2017-02-22 01:14:14 +00:00
|
|
|
|
2017-08-23 17:59:22 +00:00
|
|
|
/**
|
|
|
|
* Do a single unit of synchronous work
|
|
|
|
*
|
|
|
|
* Returns true if something was done, false if the AI should end the ship turn and stop.
|
|
|
|
*/
|
|
|
|
protected doWorkUnit(): boolean {
|
|
|
|
return false;
|
2017-02-16 22:59:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-08-23 17:59:22 +00:00
|
|
|
* Get the time spent thinking on this turn
|
2017-02-16 22:59:41 +00:00
|
|
|
*/
|
2017-08-23 17:59:22 +00:00
|
|
|
protected getDuration() {
|
|
|
|
return (new Date()).getTime() - this.started;
|
2015-02-16 00:00:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|