From 851c59bac15bd8119db6ff0be7452ef6f538ac38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Fri, 2 Mar 2018 00:14:09 +0100 Subject: [PATCH] Fixed several AI related problems --- TODO.md | 1 - src/core/ai/AIWorker.ts | 2 +- src/core/ai/Maneuver.ts | 11 ++++++++++- src/core/ai/TacticalAI.ts | 14 +++++++------- src/core/ai/TacticalAIHelpers.spec.ts | 21 ++++++++++++++++----- src/core/ai/TacticalAIHelpers.ts | 20 ++++++++++---------- src/core/models/ModelTrapper.ts | 2 +- src/ui/BaseView.ts | 3 +++ src/ui/battle/BattleView.ts | 2 +- 9 files changed, 49 insertions(+), 27 deletions(-) diff --git a/TODO.md b/TODO.md index b4b873c..84a4148 100644 --- a/TODO.md +++ b/TODO.md @@ -69,7 +69,6 @@ Ships models and actions Artificial Intelligence ----------------------- -* Fix tendency to use moves for nothing * Produce interesting "angle" areas * Evaluate active effects * Account for luck diff --git a/src/core/ai/AIWorker.ts b/src/core/ai/AIWorker.ts index d7d78ae..0c2ea24 100644 --- a/src/core/ai/AIWorker.ts +++ b/src/core/ai/AIWorker.ts @@ -27,7 +27,7 @@ module TK.SpaceTac { * Process AI in a webworker if possible, else do the work in the render thread */ async processAuto(feedback: AIFeedback): Promise { - if ((window).Worker) { + if (!this.debug && (window).Worker) { await this.processInWorker(feedback); } else { await this.processHere(feedback); diff --git a/src/core/ai/Maneuver.ts b/src/core/ai/Maneuver.ts index 7254456..c9634e8 100644 --- a/src/core/ai/Maneuver.ts +++ b/src/core/ai/Maneuver.ts @@ -41,6 +41,13 @@ module TK.SpaceTac { 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 */ @@ -87,7 +94,9 @@ module TK.SpaceTac { for (let i = 0; i < parts.length; i++) { let part = parts[i]; 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 { return false; } diff --git a/src/core/ai/TacticalAI.ts b/src/core/ai/TacticalAI.ts index e42eca0..0d8efa6 100644 --- a/src/core/ai/TacticalAI.ts +++ b/src/core/ai/TacticalAI.ts @@ -40,7 +40,7 @@ module TK.SpaceTac { [maneuver, producer] = producer(); } - if (maneuver) { + if (maneuver && maneuver.isPossible()) { if (producer) { this.producers.push(producer); } @@ -113,12 +113,12 @@ module TK.SpaceTac { let evaluators: EvaluatorHelper[] = [ scaled(TacticalAIHelpers.evaluateTurnCost, 1), - scaled(TacticalAIHelpers.evaluateOverheat, 10), - scaled(TacticalAIHelpers.evaluateEnemyHealth, 500), - scaled(TacticalAIHelpers.evaluateAllyHealth, 800), - scaled(TacticalAIHelpers.evaluateClustering, 3), - scaled(TacticalAIHelpers.evaluatePosition, 1), - scaled(TacticalAIHelpers.evaluateIdling, 1), + scaled(TacticalAIHelpers.evaluateOverheat, 3), + scaled(TacticalAIHelpers.evaluateEnemyHealth, 5), + scaled(TacticalAIHelpers.evaluateAllyHealth, 20), + scaled(TacticalAIHelpers.evaluateClustering, 4), + scaled(TacticalAIHelpers.evaluatePosition, 0.5), + scaled(TacticalAIHelpers.evaluateIdling, 2), ] let battle = nn(this.ship.getBattle()); diff --git a/src/core/ai/TacticalAIHelpers.spec.ts b/src/core/ai/TacticalAIHelpers.spec.ts index 5781eb2..bf9435a 100644 --- a/src/core/ai/TacticalAIHelpers.spec.ts +++ b/src/core/ai/TacticalAIHelpers.spec.ts @@ -120,20 +120,31 @@ module TK.SpaceTac.Specs { TestTools.setShipModel(ship, 100, 0, 10); let engine = TestTools.addEngine(ship, 50); 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)); - 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)); - check.equals(TacticalAIHelpers.evaluateIdling(ship, battle, maneuver), 0); + maneuver = new Maneuver(ship, toggle, Target.newFromShip(ship)); + 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)); - 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); + 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)); - 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 => { diff --git a/src/core/ai/TacticalAIHelpers.ts b/src/core/ai/TacticalAIHelpers.ts index 7a36be2..5799e90 100644 --- a/src/core/ai/TacticalAIHelpers.ts +++ b/src/core/ai/TacticalAIHelpers.ts @@ -59,7 +59,7 @@ module TK.SpaceTac { * Produce a turn end. */ 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 */ static evaluateIdling(ship: Ship, battle: Battle, maneuver: Maneuver): number { + let power_capacity = ship.getAttribute("power_capacity") || 1; + if (maneuver.action instanceof EndTurnAction) { - return -ship.getValue("power") / ship.getAttribute("power_capacity"); - } else if (maneuver.action instanceof TriggerAction || maneuver.action instanceof ToggleAction) { - // TODO Evaluate if drone is useful - // TODO Check there are "interesting" effects - if (maneuver.effects.length == 0) { - return -1; - } else { - return 0.5; - } + return -ship.getValue("power") / power_capacity; + } else if (maneuver.action instanceof TriggerAction) { + return 0.5; + } else if (maneuver.action instanceof ToggleAction) { + return ship.actions.isToggled(maneuver.action) ? -0.2 : 0.5; + } else if (maneuver.action instanceof MoveAction) { + return -(ship.getValue("power") - maneuver.getPowerUsage()) / power_capacity; } else { return 0; } diff --git a/src/core/models/ModelTrapper.ts b/src/core/models/ModelTrapper.ts index 8b7d8c2..66a814f 100644 --- a/src/core/models/ModelTrapper.ts +++ b/src/core/models/ModelTrapper.ts @@ -33,7 +33,7 @@ module TK.SpaceTac { let missile = new TriggerAction("Defense Missiles", { effects: [new DamageEffect(25, 30)], power: 3, - range: 200, blast: 200, + range: 200, blast: 180, }, "submunitionmissile"); return [ diff --git a/src/ui/BaseView.ts b/src/ui/BaseView.ts index ca91e7a..fa61705 100644 --- a/src/ui/BaseView.ts +++ b/src/ui/BaseView.ts @@ -29,6 +29,9 @@ module TK.SpaceTac.UI { dialogs_layer!: Phaser.Group dialogs_opened: UIDialog[] = [] + // Verbose debug output + readonly debug = false + // Get the size of display getWidth(): number { return this.game.width || 1280; diff --git a/src/ui/battle/BattleView.ts b/src/ui/battle/BattleView.ts index 5116d88..a8652ee 100644 --- a/src/ui/battle/BattleView.ts +++ b/src/ui/battle/BattleView.ts @@ -174,7 +174,7 @@ module TK.SpaceTac.UI { return; } - if (this.actual_battle.playAI()) { + if (this.actual_battle.playAI(this.debug)) { if (this.interacting) { this.action_bar.setShip(new Ship()); }