BullyAI now uses the MoveFireSimulator
This commit is contained in:
parent
144eb56537
commit
ed5d338522
|
@ -36,7 +36,7 @@ module TS.SpaceTac.Specs {
|
|||
expect(result.total_fire_ap).toBe(3, 'total_fire_ap');
|
||||
|
||||
expect(result.parts).toEqual([
|
||||
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 3 }
|
||||
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 3, possible: true }
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -50,7 +50,7 @@ module TS.SpaceTac.Specs {
|
|||
expect(result.total_fire_ap).toBe(3, 'total_fire_ap');
|
||||
|
||||
expect(result.parts).toEqual([
|
||||
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 3 }
|
||||
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 3, possible: false }
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -67,8 +67,8 @@ module TS.SpaceTac.Specs {
|
|||
expect(result.total_fire_ap).toBe(3, 'total_fire_ap');
|
||||
|
||||
expect(result.parts).toEqual([
|
||||
{ action: jasmine.objectContaining({ code: "move" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 1 },
|
||||
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 15, ship.arena_y, null), ap: 3 }
|
||||
{ action: jasmine.objectContaining({ code: "move" }), target: new Target(ship.arena_x + 5, ship.arena_y, null), ap: 1, possible: true },
|
||||
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 15, ship.arena_y, null), ap: 3, possible: true }
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -85,8 +85,8 @@ module TS.SpaceTac.Specs {
|
|||
expect(result.total_fire_ap).toBe(2, 'total_fire_ap');
|
||||
|
||||
expect(result.parts).toEqual([
|
||||
{ action: jasmine.objectContaining({ code: "move" }), target: new Target(ship.arena_x + 10, ship.arena_y, null), ap: 2 },
|
||||
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 18, ship.arena_y, null), ap: 2 }
|
||||
{ action: jasmine.objectContaining({ code: "move" }), target: new Target(ship.arena_x + 10, ship.arena_y, null), ap: 2, possible: true },
|
||||
{ action: jasmine.objectContaining({ code: "fire-null" }), target: new Target(ship.arena_x + 18, ship.arena_y, null), ap: 2, possible: false }
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ module TS.SpaceTac {
|
|||
action: BaseAction
|
||||
target: Target
|
||||
ap: number
|
||||
possible: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,7 +59,7 @@ module TS.SpaceTac {
|
|||
/**
|
||||
* Simulate a given action on a given valid target.
|
||||
*/
|
||||
simulateAction(action: BaseAction, target: Target): MoveFireResult {
|
||||
simulateAction(action: BaseAction, target: Target, move_margin = 0): MoveFireResult {
|
||||
let result = new MoveFireResult();
|
||||
|
||||
let dx = target.x - this.ship.arena_x;
|
||||
|
@ -69,7 +70,7 @@ module TS.SpaceTac {
|
|||
let action_radius = action.getRangeRadius(this.ship);
|
||||
|
||||
if (action instanceof MoveAction || distance > action_radius) {
|
||||
let move_distance = action instanceof MoveAction ? distance : distance - action_radius;
|
||||
let move_distance = action instanceof MoveAction ? distance : (distance - action_radius + move_margin);
|
||||
if (move_distance > 0.000001) {
|
||||
result.need_move = true;
|
||||
|
||||
|
@ -80,7 +81,8 @@ module TS.SpaceTac {
|
|||
result.can_move = ap > 0;
|
||||
result.can_end_move = result.total_move_ap <= ap;
|
||||
result.move_location = move_target;
|
||||
result.parts.push({ action: engine.action, target: move_target, ap: result.total_move_ap });
|
||||
// TODO Split in "this turn" part and "next turn" part if needed
|
||||
result.parts.push({ action: engine.action, target: move_target, ap: result.total_move_ap, possible: result.can_move });
|
||||
|
||||
ap -= result.total_move_ap;
|
||||
distance -= move_distance;
|
||||
|
@ -95,7 +97,7 @@ module TS.SpaceTac {
|
|||
result.total_fire_ap = action.getActionPointsUsage(this.ship, target);
|
||||
result.can_fire = result.total_fire_ap <= ap;
|
||||
result.fire_location = target;
|
||||
result.parts.push({ action: action, target: target, ap: result.total_fire_ap });
|
||||
result.parts.push({ action: action, target: target, ap: result.total_fire_ap, possible: result.can_fire });
|
||||
}
|
||||
} else {
|
||||
result.success = false;
|
||||
|
|
|
@ -41,19 +41,13 @@ module TS.SpaceTac.Specs {
|
|||
|
||||
it("checks a firing possibility", function () {
|
||||
var ship = new Ship();
|
||||
var engine = new Equipment(SlotType.Engine);
|
||||
engine.ap_usage = 3;
|
||||
engine.distance = 1;
|
||||
ship.addSlot(SlotType.Engine).attach(engine);
|
||||
let engine = TestTools.addEngine(ship, 1 / 3);
|
||||
TestTools.setShipAP(ship, 10);
|
||||
var enemy = new Ship();
|
||||
var ai = new BullyAI(ship, Timer.synchronous);
|
||||
ai.ship = ship;
|
||||
ai.move_margin = 0;
|
||||
var weapon = new Equipment(SlotType.Weapon);
|
||||
weapon.ap_usage = 2;
|
||||
weapon.distance = 3;
|
||||
ship.addSlot(SlotType.Weapon).attach(weapon);
|
||||
let weapon = TestTools.addWeapon(ship, 0, 2, 3);
|
||||
|
||||
// enemy in range, the ship can fire without moving
|
||||
ship.values.power.set(8);
|
||||
|
@ -62,9 +56,9 @@ module TS.SpaceTac.Specs {
|
|||
enemy.arena_x = 3;
|
||||
enemy.arena_y = 0;
|
||||
var result = ai.checkBullyManeuver(enemy, weapon);
|
||||
expect(result.move).toBeNull();
|
||||
expect(result.fire.target).toEqual(Target.newFromShip(enemy));
|
||||
expect(result.fire.equipment).toBe(weapon);
|
||||
expect(result.simulation.need_move).toBe(false);
|
||||
expect(result.simulation.fire_location).toEqual(Target.newFromShip(enemy));
|
||||
expect(result.equipment).toBe(weapon);
|
||||
|
||||
// enemy out of range, but moving can bring it in range
|
||||
ship.values.power.set(8);
|
||||
|
@ -73,9 +67,9 @@ module TS.SpaceTac.Specs {
|
|||
enemy.arena_x = 6;
|
||||
enemy.arena_y = 0;
|
||||
result = ai.checkBullyManeuver(enemy, weapon);
|
||||
expect(result.move.target).toEqual(Target.newFromLocation(3, 0));
|
||||
expect(result.fire.target).toEqual(Target.newFromShip(enemy));
|
||||
expect(result.fire.equipment).toBe(weapon);
|
||||
expect(result.simulation.move_location).toEqual(Target.newFromLocation(3, 0));
|
||||
expect(result.simulation.fire_location).toEqual(Target.newFromShip(enemy));
|
||||
expect(result.equipment).toBe(weapon);
|
||||
|
||||
// enemy out of range, but moving can bring it in range, except for the safety margin
|
||||
ai.move_margin = 0.1;
|
||||
|
@ -145,19 +139,9 @@ module TS.SpaceTac.Specs {
|
|||
var result = ai.listAllManeuvers();
|
||||
expect(result.length).toBe(0);
|
||||
|
||||
var weapon1 = new Equipment(SlotType.Weapon);
|
||||
weapon1.distance = 50;
|
||||
weapon1.ap_usage = 1;
|
||||
weapon1.target_effects.push(new DamageEffect(10));
|
||||
ai.ship.addSlot(SlotType.Weapon).attach(weapon1);
|
||||
var weapon2 = new Equipment(SlotType.Weapon);
|
||||
weapon2.distance = 10;
|
||||
weapon2.ap_usage = 1;
|
||||
weapon2.target_effects.push(new DamageEffect(5));
|
||||
ai.ship.addSlot(SlotType.Weapon).attach(weapon2);
|
||||
|
||||
ai.ship.values.power.setMaximal(10);
|
||||
ai.ship.values.power.set(8);
|
||||
TestTools.setShipAP(ai.ship, 8);
|
||||
let weapon1 = TestTools.addWeapon(ai.ship, 10, 1, 50);
|
||||
let weapon2 = TestTools.addWeapon(ai.ship, 5, 1, 10);
|
||||
|
||||
result = ai.listAllManeuvers();
|
||||
expect(result.length).toBe(3);
|
||||
|
@ -188,10 +172,10 @@ module TS.SpaceTac.Specs {
|
|||
// Move towards an enemy (up to minimal distance)
|
||||
ai.ship.setArenaPosition(30, 0);
|
||||
maneuver = ai.getFallbackManeuver();
|
||||
expect(maneuver.move.target).toEqual(Target.newFromLocation(25, 0));
|
||||
expect(maneuver.simulation.move_location).toEqual(Target.newFromLocation(25, 0));
|
||||
ai.ship.setArenaPosition(25, 0);
|
||||
maneuver = ai.getFallbackManeuver();
|
||||
expect(maneuver.move.target).toEqual(Target.newFromLocation(22.5, 0));
|
||||
expect(maneuver.simulation.move_location).toEqual(Target.newFromLocation(22.5, 0));
|
||||
});
|
||||
|
||||
it("applies the chosen move", function () {
|
||||
|
|
|
@ -1,22 +1,11 @@
|
|||
/// <reference path="AbstractAI.ts"/>
|
||||
/// <reference path="Maneuver.ts"/>
|
||||
module TS.SpaceTac {
|
||||
// Combination of a move action and a fire action
|
||||
export class BullyManeuver {
|
||||
// Move action to position the ship before firing
|
||||
move: Maneuver;
|
||||
|
||||
// Fire action
|
||||
fire: Maneuver;
|
||||
|
||||
constructor(move: Maneuver = null, fire: Maneuver = null) {
|
||||
this.move = move;
|
||||
this.fire = fire;
|
||||
}
|
||||
|
||||
export class BullyManeuver extends Maneuver {
|
||||
// Get a sorting score, by distance to another point
|
||||
// Nearest means higher score
|
||||
getScoreByDistance(point: Target): number {
|
||||
return -point.getDistanceTo(this.fire.target);
|
||||
return -point.getDistanceTo(this.simulation.fire_location);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,46 +82,13 @@ module TS.SpaceTac {
|
|||
|
||||
// Check if a weapon can be used against an enemy
|
||||
// Returns the BullyManeuver, or null if impossible to fire
|
||||
checkBullyManeuver(enemy: Ship, weapon: Equipment): BullyManeuver {
|
||||
// Check if enemy in range
|
||||
checkBullyManeuver(enemy: Ship, weapon: Equipment): BullyManeuver | null {
|
||||
var target = weapon.blast ? Target.newFromLocation(enemy.arena_x, enemy.arena_y) : Target.newFromShip(enemy);
|
||||
var distance = target.getDistanceTo(Target.newFromShip(this.ship));
|
||||
var move: Target;
|
||||
var engine: Equipment;
|
||||
var remaining_ap = this.ship.values.power.get();
|
||||
if (distance <= weapon.distance) {
|
||||
// No need to move
|
||||
move = null;
|
||||
let maneuver = new BullyManeuver(this.ship, weapon, target, this.move_margin);
|
||||
if (maneuver.simulation.can_fire) {
|
||||
return maneuver;
|
||||
} else {
|
||||
// Move to be in range, using first engine
|
||||
engine = this.getEngine();
|
||||
if (!engine) {
|
||||
// No engine available to move
|
||||
return null;
|
||||
} else {
|
||||
var move_distance = distance - weapon.distance + this.move_margin;
|
||||
var move_ap = engine.ap_usage * move_distance / engine.distance;
|
||||
if (move_ap > remaining_ap) {
|
||||
// Not enough AP to move in range
|
||||
return null;
|
||||
} else {
|
||||
move = target.constraintInRange(this.ship.arena_x, this.ship.arena_y, move_distance);
|
||||
remaining_ap -= move_ap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check fire
|
||||
if (weapon.ap_usage > remaining_ap) {
|
||||
// Not enough AP to fire
|
||||
return null;
|
||||
} else {
|
||||
var result = new BullyManeuver();
|
||||
if (move) {
|
||||
result.move = new Maneuver(this.ship, engine, move);
|
||||
}
|
||||
result.fire = new Maneuver(this.ship, weapon, target);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,7 +110,7 @@ module TS.SpaceTac {
|
|||
target = target.constraintInRange(this.ship.arena_x, this.ship.arena_y,
|
||||
(distance - safety_distance) * APPROACH_FACTOR);
|
||||
target = engine.action.checkLocationTarget(this.ship, target);
|
||||
return new BullyManeuver(new Maneuver(this.ship, engine, target));
|
||||
return new BullyManeuver(this.ship, engine, target);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -178,17 +134,9 @@ module TS.SpaceTac {
|
|||
// Effectively apply the chosen maneuver
|
||||
applyManeuver(maneuver: BullyManeuver): void {
|
||||
if (maneuver) {
|
||||
if (maneuver.move) {
|
||||
this.addWorkItem(() => {
|
||||
maneuver.move.apply();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
if (maneuver.fire) {
|
||||
this.addWorkItem(() => {
|
||||
maneuver.fire.apply();
|
||||
}, 1500);
|
||||
}
|
||||
this.addWorkItem(() => {
|
||||
maneuver.apply();
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
this.addWorkItem(null, 1500);
|
||||
|
|
|
@ -17,13 +17,13 @@ module TS.SpaceTac {
|
|||
// Result of move-fire simulation
|
||||
simulation: MoveFireResult;
|
||||
|
||||
constructor(ship: Ship, equipment: Equipment, target: Target) {
|
||||
constructor(ship: Ship, equipment: Equipment, target: Target, move_margin = 0) {
|
||||
this.ship = ship;
|
||||
this.equipment = equipment;
|
||||
this.target = target;
|
||||
|
||||
let simulator = new MoveFireSimulator(this.ship);
|
||||
this.simulation = simulator.simulateAction(this.equipment.action, this.target);
|
||||
this.simulation = simulator.simulateAction(this.equipment.action, this.target, move_margin);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,7 +31,7 @@ module TS.SpaceTac {
|
|||
*/
|
||||
apply(): void {
|
||||
if (this.simulation.success) {
|
||||
this.simulation.parts.forEach(part => {
|
||||
this.simulation.parts.filter(part => part.possible).forEach(part => {
|
||||
if (!part.action.apply(this.ship, part.target)) {
|
||||
console.error("AI cannot apply maneuver", this, part);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue