2017-09-24 22:23:22 +00:00
|
|
|
module TK.SpaceTac {
|
2017-02-24 00:34:31 +00:00
|
|
|
/**
|
|
|
|
* Duel between two AIs, over multiple battles
|
|
|
|
*/
|
|
|
|
export class AIDuel {
|
|
|
|
static current: AIDuel | null = null
|
|
|
|
|
|
|
|
ai1: AbstractAI
|
|
|
|
ai2: AbstractAI
|
|
|
|
win1 = 0
|
|
|
|
win2 = 0
|
|
|
|
draw = 0
|
|
|
|
scheduled = null
|
|
|
|
stopped = false
|
|
|
|
onupdate: Function | null = null
|
|
|
|
|
|
|
|
constructor(ai1: AbstractAI, ai2: AbstractAI) {
|
|
|
|
this.ai1 = ai1;
|
|
|
|
this.ai2 = ai2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start the duel
|
|
|
|
*/
|
|
|
|
start(onupdate: Function | null = null) {
|
|
|
|
if (!this.scheduled) {
|
|
|
|
this.stopped = false;
|
|
|
|
this.scheduled = Timer.global.schedule(100, () => this.next());
|
|
|
|
this.onupdate = onupdate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stop the duel
|
|
|
|
*/
|
|
|
|
stop() {
|
|
|
|
this.stopped = true;
|
|
|
|
if (this.scheduled) {
|
|
|
|
Timer.global.cancel(this.scheduled);
|
|
|
|
this.scheduled = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the result of a single battle
|
|
|
|
*/
|
2017-03-07 19:27:46 +00:00
|
|
|
update(winner: number) {
|
|
|
|
if (winner >= 0) {
|
|
|
|
if (winner == 0) {
|
2017-02-24 00:34:31 +00:00
|
|
|
this.win1 += 1;
|
2017-03-07 19:27:46 +00:00
|
|
|
console.log(` => Player 1 wins (${this.ai1})`);
|
2017-02-24 00:34:31 +00:00
|
|
|
} else {
|
|
|
|
this.win2 += 1;
|
2017-03-07 19:27:46 +00:00
|
|
|
console.log(` => Player 2 wins (${this.ai2})`);
|
2017-02-24 00:34:31 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.draw += 1;
|
|
|
|
console.log(" => draw");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.onupdate) {
|
|
|
|
this.onupdate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Perform the next battle
|
|
|
|
*/
|
2017-08-23 17:59:22 +00:00
|
|
|
async next() {
|
2017-02-24 00:34:31 +00:00
|
|
|
console.log(`${this.ai1.name} vs ${this.ai2.name} ...`);
|
|
|
|
|
2017-08-23 17:59:22 +00:00
|
|
|
// Prepare battle
|
2017-02-24 00:34:31 +00:00
|
|
|
let battle = Battle.newQuickRandom();
|
2017-08-23 17:59:22 +00:00
|
|
|
battle.fleets.forEach((fleet, findex) => {
|
|
|
|
fleet.ships.forEach((ship, sindex) => {
|
|
|
|
ship.name = `F${findex + 1}S${sindex + 1} (${ship.model.name})`;
|
|
|
|
});
|
|
|
|
});
|
2017-02-24 00:34:31 +00:00
|
|
|
|
2017-08-23 17:59:22 +00:00
|
|
|
// Run battle
|
2017-10-25 22:45:53 +00:00
|
|
|
while (!battle.ended && battle.cycle < 100) {
|
2017-08-23 17:59:22 +00:00
|
|
|
if (this.stopped) {
|
|
|
|
return;
|
|
|
|
}
|
2017-03-08 23:18:40 +00:00
|
|
|
|
2017-08-23 17:59:22 +00:00
|
|
|
let playing = battle.playing_ship;
|
2017-03-09 17:11:00 +00:00
|
|
|
if (playing) {
|
|
|
|
let ai = (playing.fleet == battle.fleets[0]) ? this.ai1 : this.ai2;
|
|
|
|
ai.ship = playing;
|
2017-08-23 17:59:22 +00:00
|
|
|
await ai.play();
|
2017-02-24 00:34:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-23 17:59:22 +00:00
|
|
|
// Update results, and go on to next battle
|
|
|
|
if (!battle.outcome.draw && battle.outcome.winner) {
|
2017-03-07 19:27:46 +00:00
|
|
|
this.update(battle.fleets.indexOf(battle.outcome.winner));
|
2017-02-24 00:34:31 +00:00
|
|
|
} else {
|
2017-03-07 19:27:46 +00:00
|
|
|
this.update(-1);
|
2017-02-24 00:34:31 +00:00
|
|
|
}
|
2017-08-23 17:59:22 +00:00
|
|
|
this.scheduled = Timer.global.schedule(100, () => this.next());
|
2017-02-24 00:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Setup the duel HTML page
|
|
|
|
*/
|
|
|
|
static setup(element: HTMLElement) {
|
2017-03-09 17:11:00 +00:00
|
|
|
let fakeship = new Ship();
|
2017-05-17 22:07:16 +00:00
|
|
|
let ais = [new TacticalAI(fakeship), new AbstractAI(fakeship)];
|
2017-02-24 00:34:31 +00:00
|
|
|
ais.forEach((ai, idx) => {
|
|
|
|
let selects = element.getElementsByTagName("select");
|
|
|
|
for (let i = 0; i < selects.length; i++) {
|
|
|
|
let option = document.createElement("option");
|
|
|
|
option.setAttribute("value", idx.toString());
|
|
|
|
option.textContent = ai.name;
|
|
|
|
selects[i].appendChild(option);
|
|
|
|
}
|
2017-08-23 17:59:22 +00:00
|
|
|
ai.name += `${idx + 1}`;
|
|
|
|
ai.timer = new Timer();
|
|
|
|
ai.timer.schedule = (delay, callback) => Timer.global.schedule(1, callback);
|
2017-02-24 00:34:31 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
let button = element.getElementsByTagName("button").item(0);
|
|
|
|
button.onclick = () => {
|
|
|
|
if (AIDuel.current) {
|
|
|
|
AIDuel.current.stop();
|
|
|
|
AIDuel.current = null;
|
|
|
|
button.textContent = "Start !";
|
|
|
|
} else {
|
2017-03-08 23:18:40 +00:00
|
|
|
console.clear();
|
2017-02-24 00:34:31 +00:00
|
|
|
let ai1 = parseInt(element.getElementsByTagName("select").item(0).value);
|
|
|
|
let ai2 = parseInt(element.getElementsByTagName("select").item(1).value);
|
2017-03-09 17:11:00 +00:00
|
|
|
let duel = new AIDuel(ais[ai1], ais[ai2]);
|
|
|
|
AIDuel.current = duel;
|
|
|
|
duel.start(() => {
|
|
|
|
element.getElementsByClassName("win1").item(0).textContent = duel.win1.toString();
|
|
|
|
element.getElementsByClassName("win2").item(0).textContent = duel.win2.toString();
|
|
|
|
element.getElementsByClassName("draw").item(0).textContent = duel.draw.toString();
|
2017-02-24 00:34:31 +00:00
|
|
|
});
|
|
|
|
button.textContent = "Stop !";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|