Fixed several AI related problems
This commit is contained in:
parent
ea8771fc75
commit
851c59bac1
1
TODO.md
1
TODO.md
|
@ -69,7 +69,6 @@ Ships models and actions
|
||||||
Artificial Intelligence
|
Artificial Intelligence
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
* Fix tendency to use moves for nothing
|
|
||||||
* Produce interesting "angle" areas
|
* Produce interesting "angle" areas
|
||||||
* Evaluate active effects
|
* Evaluate active effects
|
||||||
* Account for luck
|
* Account for luck
|
||||||
|
|
|
@ -27,7 +27,7 @@ module TK.SpaceTac {
|
||||||
* Process AI in a webworker if possible, else do the work in the render thread
|
* Process AI in a webworker if possible, else do the work in the render thread
|
||||||
*/
|
*/
|
||||||
async processAuto(feedback: AIFeedback): Promise<void> {
|
async processAuto(feedback: AIFeedback): Promise<void> {
|
||||||
if ((<any>window).Worker) {
|
if (!this.debug && (<any>window).Worker) {
|
||||||
await this.processInWorker(feedback);
|
await this.processInWorker(feedback);
|
||||||
} else {
|
} else {
|
||||||
await this.processHere(feedback);
|
await this.processHere(feedback);
|
||||||
|
|
|
@ -41,6 +41,13 @@ module TK.SpaceTac {
|
||||||
return `Use ${this.action.code} on ${this.target.jasmineToString()}`;
|
return `Use ${this.action.code} on ${this.target.jasmineToString()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the maneuver has at least one part doable
|
||||||
|
*/
|
||||||
|
isPossible(): boolean {
|
||||||
|
return any(this.simulation.parts, part => part.possible);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the maneuver cannot be fully done this turn
|
* Returns true if the maneuver cannot be fully done this turn
|
||||||
*/
|
*/
|
||||||
|
@ -87,7 +94,9 @@ module TK.SpaceTac {
|
||||||
for (let i = 0; i < parts.length; i++) {
|
for (let i = 0; i < parts.length; i++) {
|
||||||
let part = parts[i];
|
let part = parts[i];
|
||||||
if (part.action instanceof EndTurnAction || part.possible) {
|
if (part.action instanceof EndTurnAction || part.possible) {
|
||||||
return battle.applyOneAction(part.action.id, part.target);
|
if (!battle.applyOneAction(part.action.id, part.target)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ module TK.SpaceTac {
|
||||||
[maneuver, producer] = producer();
|
[maneuver, producer] = producer();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maneuver) {
|
if (maneuver && maneuver.isPossible()) {
|
||||||
if (producer) {
|
if (producer) {
|
||||||
this.producers.push(producer);
|
this.producers.push(producer);
|
||||||
}
|
}
|
||||||
|
@ -113,12 +113,12 @@ module TK.SpaceTac {
|
||||||
|
|
||||||
let evaluators: EvaluatorHelper[] = [
|
let evaluators: EvaluatorHelper[] = [
|
||||||
scaled(TacticalAIHelpers.evaluateTurnCost, 1),
|
scaled(TacticalAIHelpers.evaluateTurnCost, 1),
|
||||||
scaled(TacticalAIHelpers.evaluateOverheat, 10),
|
scaled(TacticalAIHelpers.evaluateOverheat, 3),
|
||||||
scaled(TacticalAIHelpers.evaluateEnemyHealth, 500),
|
scaled(TacticalAIHelpers.evaluateEnemyHealth, 5),
|
||||||
scaled(TacticalAIHelpers.evaluateAllyHealth, 800),
|
scaled(TacticalAIHelpers.evaluateAllyHealth, 20),
|
||||||
scaled(TacticalAIHelpers.evaluateClustering, 3),
|
scaled(TacticalAIHelpers.evaluateClustering, 4),
|
||||||
scaled(TacticalAIHelpers.evaluatePosition, 1),
|
scaled(TacticalAIHelpers.evaluatePosition, 0.5),
|
||||||
scaled(TacticalAIHelpers.evaluateIdling, 1),
|
scaled(TacticalAIHelpers.evaluateIdling, 2),
|
||||||
]
|
]
|
||||||
|
|
||||||
let battle = nn(this.ship.getBattle());
|
let battle = nn(this.ship.getBattle());
|
||||||
|
|
|
@ -120,20 +120,31 @@ module TK.SpaceTac.Specs {
|
||||||
TestTools.setShipModel(ship, 100, 0, 10);
|
TestTools.setShipModel(ship, 100, 0, 10);
|
||||||
let engine = TestTools.addEngine(ship, 50);
|
let engine = TestTools.addEngine(ship, 50);
|
||||||
let weapon = TestTools.addWeapon(ship, 10, 2, 100, 10);
|
let weapon = TestTools.addWeapon(ship, 10, 2, 100, 10);
|
||||||
|
let toggle = ship.actions.addCustom(new ToggleAction("test"));
|
||||||
|
|
||||||
let maneuver = new Maneuver(ship, weapon, Target.newFromLocation(0, 0));
|
let maneuver = new Maneuver(ship, weapon, Target.newFromLocation(0, 0));
|
||||||
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), 0.5);
|
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), 0.5, "fire");
|
||||||
|
|
||||||
maneuver = new Maneuver(ship, engine, Target.newFromLocation(0, 0));
|
maneuver = new Maneuver(ship, toggle, Target.newFromShip(ship));
|
||||||
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), 0);
|
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), 0.5, "toggle on");
|
||||||
|
|
||||||
|
ship.actions.toggle(toggle, true);
|
||||||
|
maneuver = new Maneuver(ship, toggle, Target.newFromShip(ship));
|
||||||
|
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -0.2, "toggle off");
|
||||||
|
|
||||||
|
maneuver = new Maneuver(ship, engine, Target.newFromLocation(0, 48));
|
||||||
|
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -0.9, "move only, at full power");
|
||||||
|
|
||||||
maneuver = new Maneuver(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship));
|
maneuver = new Maneuver(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship));
|
||||||
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -1);
|
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -1, "end turn, at full power");
|
||||||
|
|
||||||
ship.setValue("power", 2);
|
ship.setValue("power", 2);
|
||||||
|
|
||||||
|
maneuver = new Maneuver(ship, engine, Target.newFromLocation(0, 48));
|
||||||
|
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -0.1, "move only, at reduced power");
|
||||||
|
|
||||||
maneuver = new Maneuver(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship));
|
maneuver = new Maneuver(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship));
|
||||||
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -0.2);
|
check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), -0.2, "end turn, at reduced power");
|
||||||
});
|
});
|
||||||
|
|
||||||
test.case("evaluates damage to enemies", check => {
|
test.case("evaluates damage to enemies", check => {
|
||||||
|
|
|
@ -59,7 +59,7 @@ module TK.SpaceTac {
|
||||||
* Produce a turn end.
|
* Produce a turn end.
|
||||||
*/
|
*/
|
||||||
static produceEndTurn(ship: Ship, battle: Battle): TacticalProducer {
|
static produceEndTurn(ship: Ship, battle: Battle): TacticalProducer {
|
||||||
return isingle(new Maneuver(ship, new EndTurnAction(), Target.newFromShip(ship)));
|
return isingle(new Maneuver(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,16 +142,16 @@ module TK.SpaceTac {
|
||||||
* Evaluate doing nothing, between -1 and 1
|
* Evaluate doing nothing, between -1 and 1
|
||||||
*/
|
*/
|
||||||
static evaluateIdling(ship: Ship, battle: Battle, maneuver: Maneuver): number {
|
static evaluateIdling(ship: Ship, battle: Battle, maneuver: Maneuver): number {
|
||||||
|
let power_capacity = ship.getAttribute("power_capacity") || 1;
|
||||||
|
|
||||||
if (maneuver.action instanceof EndTurnAction) {
|
if (maneuver.action instanceof EndTurnAction) {
|
||||||
return -ship.getValue("power") / ship.getAttribute("power_capacity");
|
return -ship.getValue("power") / power_capacity;
|
||||||
} else if (maneuver.action instanceof TriggerAction || maneuver.action instanceof ToggleAction) {
|
} else if (maneuver.action instanceof TriggerAction) {
|
||||||
// TODO Evaluate if drone is useful
|
return 0.5;
|
||||||
// TODO Check there are "interesting" effects
|
} else if (maneuver.action instanceof ToggleAction) {
|
||||||
if (maneuver.effects.length == 0) {
|
return ship.actions.isToggled(maneuver.action) ? -0.2 : 0.5;
|
||||||
return -1;
|
} else if (maneuver.action instanceof MoveAction) {
|
||||||
} else {
|
return -(ship.getValue("power") - maneuver.getPowerUsage()) / power_capacity;
|
||||||
return 0.5;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ module TK.SpaceTac {
|
||||||
let missile = new TriggerAction("Defense Missiles", {
|
let missile = new TriggerAction("Defense Missiles", {
|
||||||
effects: [new DamageEffect(25, 30)],
|
effects: [new DamageEffect(25, 30)],
|
||||||
power: 3,
|
power: 3,
|
||||||
range: 200, blast: 200,
|
range: 200, blast: 180,
|
||||||
}, "submunitionmissile");
|
}, "submunitionmissile");
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -29,6 +29,9 @@ module TK.SpaceTac.UI {
|
||||||
dialogs_layer!: Phaser.Group
|
dialogs_layer!: Phaser.Group
|
||||||
dialogs_opened: UIDialog[] = []
|
dialogs_opened: UIDialog[] = []
|
||||||
|
|
||||||
|
// Verbose debug output
|
||||||
|
readonly debug = false
|
||||||
|
|
||||||
// Get the size of display
|
// Get the size of display
|
||||||
getWidth(): number {
|
getWidth(): number {
|
||||||
return this.game.width || 1280;
|
return this.game.width || 1280;
|
||||||
|
|
|
@ -174,7 +174,7 @@ module TK.SpaceTac.UI {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.actual_battle.playAI()) {
|
if (this.actual_battle.playAI(this.debug)) {
|
||||||
if (this.interacting) {
|
if (this.interacting) {
|
||||||
this.action_bar.setShip(new Ship());
|
this.action_bar.setShip(new Ship());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue