Use of iterators as producers for TacticalAI
This commit is contained in:
parent
80a82664e1
commit
de8651440a
|
@ -1 +1 @@
|
|||
Subproject commit 9a82049a4a898cfb3eb939156cb07f94e66cce2e
|
||||
Subproject commit d84417976adb78147656616fccd7cefc5eca21bb
|
|
@ -61,6 +61,13 @@ module TS.SpaceTac {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an iterator over all ships engaged in the battle
|
||||
*/
|
||||
iships(): Iterator<Ship> {
|
||||
return ichainit(imap(iarray(this.fleets), fleet => iarray(fleet.ships)));
|
||||
}
|
||||
|
||||
// Check if a player is able to play
|
||||
// This can be used by the UI to determine if player interaction is allowed
|
||||
canPlay(player: Player): boolean {
|
||||
|
|
|
@ -46,11 +46,13 @@ module TS.SpaceTac {
|
|||
}
|
||||
|
||||
// Add a ship in this fleet
|
||||
addShip(ship: Ship): void {
|
||||
if (this.ships.indexOf(ship) < 0) {
|
||||
this.ships.push(ship);
|
||||
addShip(ship: Ship): Ship {
|
||||
if (ship.fleet && ship.fleet != this) {
|
||||
remove(ship.fleet.ships, ship);
|
||||
}
|
||||
add(this.ships, ship);
|
||||
ship.fleet = this;
|
||||
return ship;
|
||||
}
|
||||
|
||||
// Set the current battle
|
||||
|
|
|
@ -27,7 +27,7 @@ module TS.SpaceTac {
|
|||
}
|
||||
|
||||
// Attach an equipment in this slot
|
||||
attach(equipment: Equipment): void {
|
||||
attach(equipment: Equipment): Equipment | null {
|
||||
if (this.type === equipment.slot && equipment.canBeEquipped(this.ship)) {
|
||||
this.attached = equipment;
|
||||
equipment.attached_to = this;
|
||||
|
@ -35,6 +35,10 @@ module TS.SpaceTac {
|
|||
if (this.ship) {
|
||||
this.ship.updateAttributes();
|
||||
}
|
||||
|
||||
return equipment;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,10 @@ module TS.SpaceTac {
|
|||
* 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();
|
||||
|
@ -101,6 +105,8 @@ module TS.SpaceTac {
|
|||
if (battle) {
|
||||
battle.advanceToNextShip();
|
||||
}
|
||||
} else {
|
||||
console.error(`${this.name} tries to end turn of another ship`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,10 @@ module TS.SpaceTac {
|
|||
|
||||
// Apply the maneuver in current battle
|
||||
apply(): void {
|
||||
this.equipment.action.apply(this.ship.getBattle(), this.ship, this.target);
|
||||
let result = this.equipment.action.apply(this.ship.getBattle(), this.ship, this.target);
|
||||
if (!result) {
|
||||
console.warn("AI could not apply maneuver", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ module TS.SpaceTac.Specs {
|
|||
}
|
||||
|
||||
// producer of FixedManeuver from a list of scores
|
||||
let producer = (...scores: number[]) => iterator(scores.map(score => new FixedManeuver(score)));
|
||||
let producer = (...scores: number[]) => iarray(scores.map(score => new FixedManeuver(score)));
|
||||
let applied = [];
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -30,5 +30,25 @@ module TS.SpaceTac.Specs {
|
|||
ai.play();
|
||||
expect(applied).toEqual([7]);
|
||||
});
|
||||
|
||||
it("produces direct weapon hits", function () {
|
||||
let battle = new Battle();
|
||||
let ship0a = battle.fleets[0].addShip(new Ship(null, "0A"));
|
||||
let ship0b = battle.fleets[0].addShip(new Ship(null, "0B"));
|
||||
let ship1a = battle.fleets[1].addShip(new Ship(null, "1A"));
|
||||
let ship1b = battle.fleets[1].addShip(new Ship(null, "1B"));
|
||||
|
||||
let result = imaterialize(produceDirectWeapon(ship0a, battle));
|
||||
expect(result.length).toBe(0);
|
||||
|
||||
let weapon1 = ship0a.addSlot(SlotType.Weapon).attach(new Equipment(SlotType.Weapon));
|
||||
let weapon2 = ship0a.addSlot(SlotType.Weapon).attach(new Equipment(SlotType.Weapon));
|
||||
result = imaterialize(produceDirectWeapon(ship0a, battle));
|
||||
expect(result.length).toBe(4);
|
||||
expect(result).toContain(new Maneuver(ship0a, weapon1, Target.newFromShip(ship1a)));
|
||||
expect(result).toContain(new Maneuver(ship0a, weapon1, Target.newFromShip(ship1b)));
|
||||
expect(result).toContain(new Maneuver(ship0a, weapon2, Target.newFromShip(ship1a)));
|
||||
expect(result).toContain(new Maneuver(ship0a, weapon2, Target.newFromShip(ship1b)));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,15 +9,25 @@ module TS.SpaceTac {
|
|||
* AI that applies a set of tactical rules
|
||||
*
|
||||
* It uses a set of producers (to propose new maneuvers), and evaluators (to choose the best maneuver).
|
||||
*
|
||||
* As much work as possible is done using iterators, without materializing every possibilities.
|
||||
*/
|
||||
export class TacticalAI extends AbstractAI {
|
||||
producers: TacticalProducer[] = []
|
||||
evaluators: TacticalEvaluator[] = []
|
||||
|
||||
best: Maneuver | null = null;
|
||||
best_score = -Infinity;
|
||||
best: Maneuver | null = null
|
||||
best_score = -Infinity
|
||||
|
||||
protected initWork(): void {
|
||||
if (this.producers.length == 0) {
|
||||
this.setupDefaultProducers();
|
||||
}
|
||||
|
||||
if (this.evaluators.length == 0) {
|
||||
this.setupDefaultEvaluators();
|
||||
}
|
||||
|
||||
this.addWorkItem(() => this.unitWork());
|
||||
}
|
||||
|
||||
|
@ -56,8 +66,31 @@ module TS.SpaceTac {
|
|||
this.addWorkItem(() => this.unitWork());
|
||||
} else if (this.best) {
|
||||
// TODO Also apply after a certain time of not finding better
|
||||
// TODO If not in range for action, make an approach move
|
||||
this.best.apply();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the default set of maneuver producers
|
||||
*/
|
||||
private setupDefaultProducers() {
|
||||
this.producers.push(produceDirectWeapon(this.ship, this.ship.getBattle()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the default set of maneuver evaluators
|
||||
*/
|
||||
private setupDefaultEvaluators() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce all "direct hit" weapon shots.
|
||||
*/
|
||||
export function produceDirectWeapon(ship: Ship, battle: Battle): TacticalProducer {
|
||||
let enemies = ifilter(battle.iships(), iship => iship.getPlayer() != ship.getPlayer());
|
||||
let weapons = iarray(ship.listEquipment(SlotType.Weapon));
|
||||
return imap(icombine(enemies, weapons), ([enemy, weapon]) => new Maneuver(ship, weapon, Target.newFromShip(enemy)));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue