WIP
This commit is contained in:
parent
9b051c3ef8
commit
1a27c99225
3
TODO.md
3
TODO.md
|
@ -52,7 +52,6 @@ Battle
|
|||
* Show a cooldown indicator on move action icon, if the simulation would cause the engine to overheat
|
||||
* [WIP] Add an hexagonal grid
|
||||
* Use the grid for "border" exclusion areas
|
||||
* Check if move target is already occupied (replaces hard exclusion area)
|
||||
* Fix repel effect to snap to grid and use grid units (beware of two ships being moved to the same location!)
|
||||
* Display the grid in targetting mode, with colors (replaces range hint)
|
||||
* Fix move-fire simulator's scanCircle
|
||||
|
@ -93,7 +92,7 @@ Artificial Intelligence
|
|||
|
||||
* If web worker is not responsive, or produces only errors, it should be disabled for the session
|
||||
* Prevent infinite loops of toggle/untoggle
|
||||
* Produce interesting "angle" areas
|
||||
* Produce all actions combined with all accepted targets (currently, a self-targetting trigger action is not possible for example)
|
||||
* Evaluate vigilance actions
|
||||
* Evaluate the "interest" of an active effect (e.g healing is better when harmed...)
|
||||
* Evaluators result should be more specific (final state evaluation, diff evaluation, confidence...)
|
||||
|
|
|
@ -93,10 +93,10 @@ module TK.SpaceTac {
|
|||
getDefaultProducers() {
|
||||
let producers = [
|
||||
TacticalAIHelpers.produceEndTurn,
|
||||
TacticalAIHelpers.produceDirectShots,
|
||||
TacticalAIHelpers.produceBlastShots,
|
||||
TacticalAIHelpers.produceTriggerActionsOnShip,
|
||||
TacticalAIHelpers.produceTriggerActionsInSpace,
|
||||
TacticalAIHelpers.produceToggleActions,
|
||||
TacticalAIHelpers.produceRandomMoves,
|
||||
TacticalAIHelpers.produceMoveActions,
|
||||
]
|
||||
return producers.map(producer => producer(this.ship, this.ship.getBattle() || new Battle()));
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@ module TK.SpaceTac.Specs {
|
|||
TestTools.setShipModel(ship0a, 100, 0, 10);
|
||||
TestTools.setShipPlaying(battle, ship0a);
|
||||
|
||||
let result = imaterialize(TacticalAIHelpers.produceDirectShots(ship0a, battle));
|
||||
let result = imaterialize(TacticalAIHelpers.produceTriggerActionsOnShip(ship0a, battle));
|
||||
check.equals(result.length, 0);
|
||||
|
||||
let weapon1 = TestTools.addWeapon(ship0a, 10);
|
||||
let weapon2 = TestTools.addWeapon(ship0a, 15);
|
||||
result = imaterialize(TacticalAIHelpers.produceDirectShots(ship0a, battle));
|
||||
result = imaterialize(TacticalAIHelpers.produceTriggerActionsOnShip(ship0a, battle));
|
||||
check.equals(result.length, 4);
|
||||
check.contains(result, new Maneuver(ship0a, weapon1, Target.newFromShip(ship1a)));
|
||||
check.contains(result, new Maneuver(ship0a, weapon1, Target.newFromShip(ship1b)));
|
||||
|
@ -32,12 +32,12 @@ module TK.SpaceTac.Specs {
|
|||
TestTools.setShipModel(ship, 100, 0, 10);
|
||||
TestTools.setShipPlaying(battle, ship);
|
||||
|
||||
let result = imaterialize(TacticalAIHelpers.produceRandomMoves(ship, battle, 2, 1));
|
||||
let result = imaterialize(TacticalAIHelpers.produceMoveActions(ship, battle));
|
||||
check.equals(result.length, 0);
|
||||
|
||||
let engine = TestTools.addEngine(ship, 1000);
|
||||
|
||||
result = imaterialize(TacticalAIHelpers.produceRandomMoves(ship, battle, 2, 1, new SkewedRandomGenerator([0.5], true)));
|
||||
result = imaterialize(TacticalAIHelpers.produceMoveActions(ship, battle));
|
||||
check.equals(result, [
|
||||
new Maneuver(ship, engine, Target.newFromLocation(25, 25)),
|
||||
new Maneuver(ship, engine, Target.newFromLocation(75, 25)),
|
||||
|
@ -46,41 +46,6 @@ module TK.SpaceTac.Specs {
|
|||
]);
|
||||
});
|
||||
|
||||
test.case("produces interesting blast shots", check => {
|
||||
let battle = new Battle();
|
||||
let ship = battle.fleets[0].addShip();
|
||||
let weapon = TestTools.addWeapon(ship, 50, 1, 1000, 105);
|
||||
TestTools.setShipModel(ship, 100, 0, 10, 1, [weapon]);
|
||||
TestTools.setShipPlaying(battle, ship);
|
||||
|
||||
let result = imaterialize(TacticalAIHelpers.produceInterestingBlastShots(ship, battle));
|
||||
check.equals(result.length, 0);
|
||||
|
||||
let enemy1 = battle.fleets[1].addShip();
|
||||
enemy1.setArenaPosition(500, 0);
|
||||
|
||||
result = imaterialize(TacticalAIHelpers.produceInterestingBlastShots(ship, battle));
|
||||
check.equals(result.length, 0);
|
||||
|
||||
let enemy2 = battle.fleets[1].addShip();
|
||||
enemy2.setArenaPosition(700, 0);
|
||||
|
||||
result = imaterialize(TacticalAIHelpers.produceInterestingBlastShots(ship, battle));
|
||||
check.equals(result, [
|
||||
new Maneuver(ship, weapon, Target.newFromLocation(600, 0)),
|
||||
new Maneuver(ship, weapon, Target.newFromLocation(600, 0)),
|
||||
]);
|
||||
|
||||
let enemy3 = battle.fleets[1].addShip();
|
||||
enemy3.setArenaPosition(700, 300);
|
||||
|
||||
result = imaterialize(TacticalAIHelpers.produceInterestingBlastShots(ship, battle));
|
||||
check.equals(result, [
|
||||
new Maneuver(ship, weapon, Target.newFromLocation(600, 0)),
|
||||
new Maneuver(ship, weapon, Target.newFromLocation(600, 0)),
|
||||
]);
|
||||
});
|
||||
|
||||
test.case("produces toggle/untoggle actions", check => {
|
||||
let battle = new Battle();
|
||||
let ship = battle.fleets[0].addShip();
|
||||
|
|
|
@ -2,8 +2,8 @@ module TK.SpaceTac {
|
|||
/**
|
||||
* Get a list of all playable actions (like the actionbar for player) for a ship
|
||||
*/
|
||||
function getPlayableActions(ship: Ship): Iterator<BaseAction> {
|
||||
let actions = ship.actions.listAll();
|
||||
function getPlayableActions<T extends BaseAction>(ship: Ship, type_class: { new(...args: any[]): T }): Iterator<T> {
|
||||
let actions = cfilter(ship.actions.listAll(), type_class);
|
||||
return ifilter(iarray(actions), action => !action.checkCannotBeApplied(ship));
|
||||
}
|
||||
|
||||
|
@ -45,14 +45,11 @@ module TK.SpaceTac {
|
|||
*/
|
||||
export class TacticalAIHelpers {
|
||||
/**
|
||||
* Iterator of a list of "random" arena coordinates, based on a grid
|
||||
* Iterator of a list of arena coordinates
|
||||
*/
|
||||
static scanArena(battle: Battle, cells = 10, random = RandomGenerator.global): Iterator<Target> {
|
||||
return imap(irange(cells * cells), cellpos => {
|
||||
let y = Math.floor(cellpos / cells);
|
||||
let x = cellpos - y * cells;
|
||||
return Target.newFromLocation((x + random.random()) * battle.width / cells, (y + random.random()) * battle.height / cells);
|
||||
});
|
||||
static scanArena(battle: Battle): Iterator<Target> {
|
||||
let start = { x: battle.width / 2, y: battle.height / 2 };
|
||||
return imap(battle.grid.iterate(start), loc => Target.newFromLocation(loc.x, loc.y));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,62 +59,39 @@ module TK.SpaceTac {
|
|||
return isingle(new Maneuver(ship, EndTurnAction.SINGLETON, Target.newFromShip(ship)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce all "direct hit" weapon shots.
|
||||
*/
|
||||
static produceDirectShots(ship: Ship, battle: Battle): TacticalProducer {
|
||||
let enemies = battle.ienemies(ship, true);
|
||||
let weapons = ifilter(getPlayableActions(ship), action => action instanceof TriggerAction);
|
||||
return imap(icombine(enemies, weapons), ([enemy, weapon]) => new Maneuver(ship, weapon, Target.newFromShip(enemy)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce random moves inside arena cell
|
||||
*/
|
||||
static produceRandomMoves(ship: Ship, battle: Battle, cells = 10, iterations = 1, random = RandomGenerator.global): TacticalProducer {
|
||||
let engines = ifilter(getPlayableActions(ship), action => action instanceof MoveAction);
|
||||
return ichainit(imap(irange(iterations), iteration => {
|
||||
let moves = icombine(engines, TacticalAIHelpers.scanArena(battle, cells, random));
|
||||
static produceMoveActions(ship: Ship, battle: Battle): TacticalProducer {
|
||||
let engines = getPlayableActions(ship, MoveAction);
|
||||
let moves = icombine(engines, TacticalAIHelpers.scanArena(battle));
|
||||
return imap(moves, ([engine, target]) => new Maneuver(ship, engine, target));
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce blast weapon shots, with multiple targets.
|
||||
* Produce trigger actions on ships
|
||||
*/
|
||||
static produceInterestingBlastShots(ship: Ship, battle: Battle): TacticalProducer {
|
||||
// TODO Work with groups of 3, 4 ...
|
||||
let weapons = <Iterator<TriggerAction>>ifilter(getPlayableActions(ship), action => action instanceof TriggerAction && action.blast > 0);
|
||||
let enemies = battle.ienemies(ship, true);
|
||||
// TODO This produces duplicates (x, y) and (y, x)
|
||||
let couples = ifilter(icombine(enemies, enemies), ([e1, e2]) => e1 != e2);
|
||||
let candidates = ifilter(icombine(weapons, couples), ([weapon, [e1, e2]]) => Target.newFromShip(e1).getDistanceTo(Target.newFromShip(e2)) < weapon.blast * 2);
|
||||
let result = imap(candidates, ([weapon, [e1, e2]]) => new Maneuver(ship, weapon, Target.newFromLocation((e1.arena_x + e2.arena_x) / 2, (e1.arena_y + e2.arena_y) / 2)));
|
||||
return result;
|
||||
static produceTriggerActionsOnShip(ship: Ship, battle: Battle): TacticalProducer {
|
||||
let ships = battle.iships(true);
|
||||
let weapons = ifilter(getPlayableActions(ship, TriggerAction), action => action.getTargettingMode(ship) == ActionTargettingMode.SHIP);
|
||||
return imap(icombine(ships, weapons), ([iship, weapon]) => new Maneuver(ship, weapon, Target.newFromShip(iship)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce random blast weapon shots, on a grid.
|
||||
* Produce trigger actions in space
|
||||
*/
|
||||
static produceRandomBlastShots(ship: Ship, battle: Battle): TacticalProducer {
|
||||
let weapons = ifilter(getPlayableActions(ship), action => action instanceof TriggerAction && action.blast > 0);
|
||||
let candidates = ifilter(icombine(weapons, TacticalAIHelpers.scanArena(battle)), ([weapon, location]) => (<TriggerAction>weapon).getEffects(ship, location).length > 0);
|
||||
static produceTriggerActionsInSpace(ship: Ship, battle: Battle): TacticalProducer {
|
||||
let weapons = ifilter(getPlayableActions(ship, TriggerAction), action => action.getTargettingMode(ship) == ActionTargettingMode.SPACE);
|
||||
let candidates = ifilter(icombine(weapons, TacticalAIHelpers.scanArena(battle)), ([weapon, location]) => weapon.getEffects(ship, location).length > 0);
|
||||
let result = imap(candidates, ([weapon, location]) => new Maneuver(ship, weapon, location));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce interesting then random blast shots
|
||||
*/
|
||||
static produceBlastShots(ship: Ship, battle: Battle): TacticalProducer {
|
||||
return ichain(TacticalAIHelpers.produceInterestingBlastShots(ship, battle), TacticalAIHelpers.produceRandomBlastShots(ship, battle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce toggle actions at random locations.
|
||||
*/
|
||||
static produceToggleActions(ship: Ship, battle: Battle): TacticalProducer {
|
||||
let toggles = ifilter(getPlayableActions(ship), action => action instanceof ToggleAction);
|
||||
let toggles = getPlayableActions(ship, ToggleAction);
|
||||
|
||||
let self_toggles = ifilter(toggles, toggle => contains([ActionTargettingMode.SELF_CONFIRM, ActionTargettingMode.SELF], toggle.getTargettingMode(ship)));
|
||||
let self_maneuvers = imap(self_toggles, toggle => new Maneuver(ship, toggle, Target.newFromShip(ship)));
|
||||
|
|
Loading…
Reference in a new issue