WIP
This commit is contained in:
parent
6d414a8f5e
commit
d4452e9753
1
TODO.md
1
TODO.md
|
@ -53,7 +53,6 @@ Battle
|
|||
* [WIP] Add an hexagonal grid
|
||||
* Use the grid for "border" exclusion areas
|
||||
* Fix repel effect to snap to grid and use grid units (beware of two ships being moved to the same location!)
|
||||
* Fix move-fire simulator's scanCircle
|
||||
* Apply to action's default targets
|
||||
* Add engine trail effect, and sound
|
||||
* Find incentives to move from starting position (permanent drones or anomalies?)
|
||||
|
|
|
@ -33,9 +33,11 @@ module TK.SpaceTac.Specs {
|
|||
checkLocation(check, grid.left({ x: 6, y: 2 }), 3, 2);
|
||||
checkLocation(check, grid.right({ x: 3, y: 1 }), 6, 1);
|
||||
});
|
||||
});
|
||||
|
||||
testing("SquareArenaGrid", test => {
|
||||
test.case("iterates around a location", check => {
|
||||
let grid = new PixelArenaGrid(undefined, 5);
|
||||
let grid = new SquareArenaGrid(undefined, 5);
|
||||
let result = imaterialize(grid.iterate({ x: 5, y: 5 }, { xmin: 0, xmax: 15, ymin: 0, ymax: 10 }));
|
||||
check.equals(result.length, 12);
|
||||
checkLocation(check, result[0], 5, 5);
|
||||
|
|
|
@ -44,7 +44,7 @@ module TK.SpaceTac {
|
|||
* This returns a distance in grid units
|
||||
*/
|
||||
measure(loc1: IArenaLocation, loc2: IArenaLocation): number {
|
||||
return arenaDistance(this.snap(loc1), this.snap(loc2));
|
||||
return arenaDistance(this.snap(loc1), this.snap(loc2)) / this.getUnit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,22 +85,23 @@ module TK.SpaceTac {
|
|||
/**
|
||||
* Produce all valid coordinates on the grid inside a rectangular area, starting from a given point
|
||||
*/
|
||||
iterate(from: IArenaLocation, bounds = nn(this.bounds)): Iterable<IArenaLocation> {
|
||||
iterate(from: IArenaLocation, bounds?: ArenaBounds): Iterable<IArenaLocation> {
|
||||
from = this.snap(from);
|
||||
let fbounds = arenaBoundsIntersect(this.bounds || ARENA_INFINITE, bounds || ARENA_INFINITE);
|
||||
|
||||
let row = (rfrom: IArenaLocation): Iterable<IArenaLocation> => {
|
||||
return ichain(
|
||||
irecur(rfrom, loc => loc.x <= bounds.xmax ? this.right(loc) : null),
|
||||
irecur(this.left(rfrom), loc => loc.x >= bounds.xmin ? this.left(loc) : null),
|
||||
irecur(rfrom, loc => loc.x <= fbounds.xmax ? this.right(loc) : null),
|
||||
irecur(this.left(rfrom), loc => loc.x >= fbounds.xmin ? this.left(loc) : null),
|
||||
);
|
||||
};
|
||||
|
||||
let result = ichain(
|
||||
ichainit(imap(irecur(from, loc => loc.y <= bounds.ymax ? this.down(loc) : null), row)),
|
||||
ichainit(imap(irecur(this.up(from), loc => loc.y >= bounds.ymin ? this.up(loc) : null), row)),
|
||||
ichainit(imap(irecur(from, loc => loc.y <= fbounds.ymax ? this.down(loc) : null), row)),
|
||||
ichainit(imap(irecur(this.up(from), loc => loc.y >= fbounds.ymin ? this.up(loc) : null), row)),
|
||||
);
|
||||
|
||||
return ifilter(result, loc => this.check(loc, this.bounds));
|
||||
return ifilter(result, loc => this.check(loc, fbounds));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,33 +122,12 @@ module TK.SpaceTac {
|
|||
snap(loc: IArenaLocation): IArenaLocation {
|
||||
return new ArenaLocation(Math.round(loc.x), Math.round(loc.y));
|
||||
}
|
||||
|
||||
measure(loc1: IArenaLocation, loc2: IArenaLocation): number {
|
||||
// FIXME
|
||||
let d = super.measure(loc1, loc2) / this.unit;
|
||||
let r = Math.round(d);
|
||||
if (r >= d) {
|
||||
return Math.ceil(d);
|
||||
} else if (d - r < 1e-8) {
|
||||
return r;
|
||||
} else {
|
||||
return Math.floor(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Square grid
|
||||
*/
|
||||
export class SquareGrid extends PixelArenaGrid {
|
||||
constructor(bounds?: ArenaBounds, unit = 1) {
|
||||
super(bounds, unit);
|
||||
}
|
||||
|
||||
getUnit(): number {
|
||||
return this.unit;
|
||||
}
|
||||
|
||||
export class SquareArenaGrid extends PixelArenaGrid {
|
||||
snap(loc: IArenaLocation): IArenaLocation {
|
||||
return new ArenaLocation(Math.round(loc.x / this.unit) * this.unit, Math.round(loc.y / this.unit) * this.unit);
|
||||
}
|
||||
|
|
|
@ -61,6 +61,20 @@ module TK.SpaceTac {
|
|||
ymax: number,
|
||||
}
|
||||
|
||||
export const ARENA_INFINITE = { xmin: -Infinity, xmax: Infinity, ymin: -Infinity, ymax: Infinity };
|
||||
|
||||
/**
|
||||
* Intersection of two arena bounds
|
||||
*/
|
||||
export function arenaBoundsIntersect(bounds1: ArenaBounds, bounds2: ArenaBounds): ArenaBounds {
|
||||
return {
|
||||
xmin: Math.max(bounds1.xmin, bounds2.xmin),
|
||||
xmax: Math.min(bounds1.xmax, bounds2.xmax),
|
||||
ymin: Math.max(bounds1.ymin, bounds2.ymin),
|
||||
ymax: Math.min(bounds1.ymax, bounds2.ymax),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the normalized angle (in radians) between two locations
|
||||
*/
|
||||
|
|
|
@ -92,35 +92,20 @@ module TK.SpaceTac.Specs {
|
|||
});
|
||||
|
||||
test.case("scans a circle for move targets", check => {
|
||||
let simulator = new MoveFireSimulator(new Ship(), new PixelArenaGrid());
|
||||
let simulator = new MoveFireSimulator(new Ship(), new SquareArenaGrid(undefined, 1));
|
||||
|
||||
let result = simulator.scanCircle(50, 30, 10, 1, 1);
|
||||
let result = simulator.scanCircle(10, 5, 0);
|
||||
check.equals(imaterialize(result), [
|
||||
new Target(50, 30)
|
||||
new Target(10, 5)
|
||||
]);
|
||||
|
||||
result = simulator.scanCircle(50, 30, 10, 2, 1);
|
||||
result = simulator.scanCircle(10, 5, 1);
|
||||
check.equals(imaterialize(result), [
|
||||
new Target(50, 30),
|
||||
new Target(60, 30)
|
||||
]);
|
||||
|
||||
result = simulator.scanCircle(50, 30, 10, 2, 2);
|
||||
check.equals(imaterialize(result), [
|
||||
new Target(50, 30),
|
||||
new Target(60, 30),
|
||||
new Target(40, 30)
|
||||
]);
|
||||
|
||||
result = simulator.scanCircle(50, 30, 10, 3, 4);
|
||||
check.equals(imaterialize(result), [
|
||||
new Target(50, 30),
|
||||
new Target(55, 30),
|
||||
new Target(45, 30),
|
||||
new Target(60, 30),
|
||||
new Target(50, 40),
|
||||
new Target(40, 30),
|
||||
new Target(50, 20)
|
||||
new Target(10, 5),
|
||||
new Target(11, 5),
|
||||
new Target(9, 5),
|
||||
new Target(10, 6),
|
||||
new Target(10, 4),
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -128,6 +113,7 @@ module TK.SpaceTac.Specs {
|
|||
let [ship, simulator, action] = simpleWeaponCase(100, 5, 1, 50);
|
||||
ship.setArenaPosition(300, 200);
|
||||
let battle = new Battle();
|
||||
battle.grid = new PixelArenaGrid();
|
||||
battle.fleets[0].addShip(ship);
|
||||
let ship1 = battle.fleets[0].addShip();
|
||||
let moveaction = nn(simulator.findEngine());
|
||||
|
@ -145,7 +131,7 @@ module TK.SpaceTac.Specs {
|
|||
new Target(420, 210),
|
||||
new Target(480, 260),
|
||||
]));
|
||||
check.equals(simulator.getApproach(moveaction, Target.newFromLocation(500, 200), 100), new Target(410, 230));
|
||||
check.equals(simulator.getApproach(moveaction, Target.newFromLocation(500, 200), 100), new Target(480, 260));
|
||||
});
|
||||
|
||||
test.case("moves to get in range, even if not enough AP to fire", check => {
|
||||
|
|
|
@ -75,15 +75,15 @@ module TK.SpaceTac {
|
|||
/**
|
||||
* Get an iterator for scanning a circle
|
||||
*/
|
||||
scanCircle(x: number, y: number, radius: number, nr = 6, na = 30): Iterable<Target> {
|
||||
// TODO If there is a grid, produce only grid coordinates
|
||||
let rcount = nr ? 1 / (nr - 1) : 0;
|
||||
return ichainit(imap(istep(0, irepeat(rcount, nr - 1)), r => {
|
||||
let angles = Math.max(1, Math.ceil(na * r));
|
||||
return imap(istep(0, irepeat(2 * Math.PI / angles, angles - 1)), a => {
|
||||
return new Target(x + r * radius * Math.cos(a), y + r * radius * Math.sin(a)).snap(this.grid);
|
||||
});
|
||||
}));
|
||||
scanCircle(x: number, y: number, radius: number): Iterable<Target> {
|
||||
let cells = this.grid.iterate({ x, y }, {
|
||||
xmin: x - radius,
|
||||
xmax: x + radius,
|
||||
ymin: y - radius,
|
||||
ymax: y + radius,
|
||||
});
|
||||
cells = ifilter(cells, cell => this.grid.inRange({ x, y }, cell, radius / this.grid.getUnit()));
|
||||
return imap(cells, cell => Target.newFromLocation(cell.x, cell.y));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -127,7 +127,7 @@ module TK.SpaceTac.Specs {
|
|||
|
||||
test.case("can only be used on the grid, and in range", check => {
|
||||
let battle = new Battle();
|
||||
battle.grid = new SquareGrid({ xmin: 0, xmax: 10, ymin: 0, ymax: 5 }, 1);
|
||||
battle.grid = new SquareArenaGrid({ xmin: 0, xmax: 10, ymin: 0, ymax: 5 }, 1);
|
||||
let ship = battle.fleets[0].addShip();
|
||||
let action = ship.actions.addCustom(new TriggerAction("Weapon", { range: 8, blast: 1 }));
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ module TK.SpaceTac.Specs {
|
|||
|
||||
test.case("produces random moves inside a grid", check => {
|
||||
let battle = new Battle();
|
||||
battle.grid = new SquareGrid({ xmin: 0, ymin: 0, xmax: 20, ymax: 10 }, 10);
|
||||
battle.grid = new SquareArenaGrid({ xmin: 0, ymin: 0, xmax: 20, ymax: 10 }, 10);
|
||||
let ship = battle.fleets[0].addShip();
|
||||
|
||||
TestTools.setShipModel(ship, 100, 0, 10);
|
||||
|
|
Loading…
Reference in New Issue