1
0
Fork 0

BullyAI now uses the MoveFireSimulator

This commit is contained in:
Michaël Lemaire 2017-03-07 23:16:47 +01:00
parent 144eb56537
commit ed5d338522
5 changed files with 39 additions and 105 deletions

View file

@ -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 }
]);
});

View file

@ -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;

View file

@ -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 () {

View file

@ -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);

View file

@ -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);
}