173 lines
6.1 KiB
C++
173 lines
6.1 KiB
C++
// B4 adapter tests: a StarFleet snapshot -> one movement step, and the fleet list -> the
|
|
// turn's pass schedule and per-player gate traffic.
|
|
//
|
|
// Hand-written fixtures only.
|
|
#include "shim/hooks/movement_inputs.h"
|
|
|
|
#include "check.h"
|
|
|
|
using namespace shim::hooks;
|
|
using sots::sim::WaypointKind;
|
|
|
|
namespace {
|
|
|
|
FleetStepSnapshot Fleet(double distance, int waypointType, int ships, float range) {
|
|
FleetStepSnapshot s;
|
|
s.fleetId = 42;
|
|
s.ownerIndex = 1;
|
|
s.waypointType = waypointType;
|
|
s.waypointCount = 1;
|
|
s.dt = 1.0f;
|
|
s.speed = 10.0f;
|
|
s.pos[0] = 0;
|
|
s.dest[0] = distance;
|
|
s.shipCount = ships;
|
|
for (int i = 0; i < ships; ++i) s.ships[i].range = range;
|
|
return s;
|
|
}
|
|
|
|
void test_straight_step() {
|
|
simtest::ScriptedRng rng;
|
|
FleetStepSnapshot s = Fleet(100, 0, 2, 50.f);
|
|
FleetStepResult r = StepFleet(s, 10.0, rng);
|
|
CHECK_NEAR(r.distance, 100.0, 1e-9);
|
|
CHECK_NEAR(r.moved, 10.0, 1e-6);
|
|
CHECK_NEAR(r.pos[0], 10.0, 1e-5);
|
|
CHECK(!r.arrived);
|
|
CHECK(!r.stranded);
|
|
CHECK_EQ(r.rngDraws, 0);
|
|
CHECK_NEAR(r.shipRanges[0], 40.0, 1e-5);
|
|
CHECK_NEAR(r.shipRanges[1], 40.0, 1e-5);
|
|
CHECK_NEAR(r.fraction, 1.0, 0.0); // a straight leg reports a full pass
|
|
|
|
// an exempt ship still clamps the fleet but pays nothing
|
|
s.ships[1].range = 3.f;
|
|
s.ships[1].exempt = 1;
|
|
r = StepFleet(s, 10.0, rng);
|
|
CHECK_NEAR(r.moved, 3.05, 1e-5); // the tanker's 3 + the 0.05 grace margin
|
|
CHECK_NEAR(r.shipRanges[1], 3.0, 0.0);
|
|
CHECK_NEAR(r.shipRanges[0], 50.0 - 3.05, 1e-4);
|
|
|
|
// out of fuel entirely
|
|
s.ships[0].range = 0.f;
|
|
s.ships[1].range = 0.f;
|
|
s.ships[1].exempt = 0;
|
|
r = StepFleet(s, 10.0, rng);
|
|
CHECK(r.stranded);
|
|
CHECK_NEAR(r.moved, 0.0, 0.0);
|
|
CHECK_NEAR(r.pos[0], 0.0, 0.0);
|
|
}
|
|
|
|
void test_arrival_snap() {
|
|
simtest::ScriptedRng rng;
|
|
FleetStepSnapshot s = Fleet(4, 0, 1, 100.f);
|
|
s.dest[1] = 3; // a 3-4-5 leg, distance 5
|
|
const FleetStepResult r = StepFleet(s, 10.0, rng);
|
|
CHECK(r.arrived);
|
|
CHECK_NEAR(r.pos[0], 4.0, 0.0); // copied verbatim, not extrapolated
|
|
CHECK_NEAR(r.pos[1], 3.0, 0.0);
|
|
CHECK_NEAR(r.moved, 5.0, 1e-9);
|
|
}
|
|
|
|
void test_node_waypoint_fraction() {
|
|
simtest::ScriptedRng rng;
|
|
FleetStepSnapshot s = Fleet(4, static_cast<int>(WaypointKind::NodeRoute), 1, 100.f);
|
|
const FleetStepResult r = StepFleet(s, 10.0, rng);
|
|
CHECK(r.arrived);
|
|
CHECK_NEAR(r.fraction, 0.4, 1e-6); // only a node waypoint reports a partial pass
|
|
}
|
|
|
|
void test_gate_teleport() {
|
|
simtest::ScriptedRng rng;
|
|
FleetStepSnapshot s = Fleet(1000, static_cast<int>(WaypointKind::GateTeleport), 1, 1.f);
|
|
const FleetStepResult r = StepFleet(s, 10.0, rng);
|
|
CHECK(r.arrived);
|
|
CHECK_NEAR(r.pos[0], 1000.0, 0.0); // no distance clamp at all
|
|
CHECK_NEAR(r.shipRanges[0], 1.0, 0.0); // and no fuel is charged
|
|
CHECK_EQ(r.rngDraws, 0);
|
|
}
|
|
|
|
void test_probabilistic_jump() {
|
|
FleetStepSnapshot s = Fleet(1000, static_cast<int>(WaypointKind::ProbabilisticJump), 1, 1.f);
|
|
s.castEfficiency = 2.f;
|
|
s.castThreshold = 1.f;
|
|
{ // 0.3 x 2 = 0.6 <= 1: it arrives, on one draw, with no fuel charged
|
|
simtest::ScriptedRng rng({0.3f});
|
|
const FleetStepResult r = StepFleet(s, 10.0, rng);
|
|
CHECK(r.arrived);
|
|
CHECK_NEAR(r.pos[0], 1000.0, 0.0);
|
|
CHECK_EQ(r.rngDraws, 1);
|
|
CHECK_NEAR(r.shipRanges[0], 1.0, 0.0);
|
|
}
|
|
{ // 0.9 x 2 = 1.8 > 1: a misjump, two draws, scattered by 1.8
|
|
simtest::ScriptedRng rng({0.9f}, {7u});
|
|
const FleetStepResult r = StepFleet(s, 10.0, rng);
|
|
CHECK(!r.arrived);
|
|
CHECK_EQ(r.rngDraws, 2);
|
|
CHECK_NEAR(r.moved, 1.8, 1e-6);
|
|
}
|
|
}
|
|
|
|
void test_held_fleet() {
|
|
simtest::ScriptedRng rng;
|
|
FleetStepSnapshot s = Fleet(100, 0, 1, 50.f);
|
|
s.heldFlag = 1;
|
|
const FleetStepResult r = StepFleet(s, 10.0, rng);
|
|
CHECK_NEAR(r.moved, 0.0, 0.0);
|
|
CHECK(!r.arrived);
|
|
CHECK_EQ(r.rngDraws, 0);
|
|
|
|
FleetStepSnapshot empty = Fleet(100, -1, 1, 50.f);
|
|
const FleetStepResult e = StepFleet(empty, 10.0, rng);
|
|
CHECK_NEAR(e.moved, 0.0, 0.0); // no waypoints: nothing happens
|
|
}
|
|
|
|
void test_gate_traffic_and_schedule() {
|
|
std::vector<FleetSummary> f(4);
|
|
f[0] = {1, 0, 2, 0, static_cast<int>(WaypointKind::GateTeleport), 10, 0};
|
|
f[1] = {2, 0, 0, 0, static_cast<int>(WaypointKind::ProbabilisticJump), 5, 0};
|
|
f[2] = {3, 1, 0, 0, static_cast<int>(WaypointKind::NodeLine), 100, 0};
|
|
f[3] = {4, 1, 3, 2, static_cast<int>(WaypointKind::GateTeleport), -4, 0};
|
|
|
|
const std::vector<int> g = GateTraffic(f, 2);
|
|
CHECK_EQ(g.size(), std::size_t{2});
|
|
CHECK_EQ(g[0], 15); // both gate-transit kinds count
|
|
CHECK_EQ(g[1], -4); // the node-line fleet contributes nothing; the word is signed
|
|
|
|
// fleet 1 chases fleet 2 (relation 0), fleet 4 follows fleet 3 (relation 2)
|
|
const std::vector<sots::sim::MovementPass> p = PassSchedule(f);
|
|
CHECK_EQ(p.size(), std::size_t{6});
|
|
CHECK_EQ(p[0].fleetId, 2); CHECK_EQ(p[0].pass, 1);
|
|
CHECK_EQ(p[1].fleetId, 1); CHECK_EQ(p[1].pass, 2);
|
|
CHECK_EQ(p[2].fleetId, 2); CHECK_EQ(p[2].pass, 3);
|
|
CHECK_EQ(p[3].fleetId, 1); CHECK_EQ(p[3].pass, 4); // the chase failed: a second half
|
|
// fleet 3 is only a *follower's* target, which is not the prey set, so it takes a full
|
|
// turn in pass 4 like any unengaged fleet
|
|
CHECK_EQ(p[4].fleetId, 3); CHECK_EQ(p[4].pass, 4); CHECK_NEAR(p[4].dt, 1.0, 0.0);
|
|
CHECK_EQ(p[5].fleetId, 4); CHECK_EQ(p[5].pass, 5);
|
|
}
|
|
|
|
void test_ship_ranges_helper() {
|
|
FleetStepSnapshot s = Fleet(10, 0, 3, 1.f);
|
|
s.ships[1].range = 2.f;
|
|
s.ships[2].range = 3.f;
|
|
const std::vector<double> r = ShipRanges(s);
|
|
CHECK_EQ(r.size(), std::size_t{3});
|
|
CHECK_NEAR(r[2], 3.0, 0.0);
|
|
s.shipCount = kMaxTracedShips + 10; // a garbage count is truncated, not overrun
|
|
CHECK_EQ(ShipRanges(s).size(), std::size_t{kMaxTracedShips});
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
test_straight_step();
|
|
test_arrival_snap();
|
|
test_node_waypoint_fraction();
|
|
test_gate_teleport();
|
|
test_probabilistic_jump();
|
|
test_held_fleet();
|
|
test_gate_traffic_and_schedule();
|
|
test_ship_ranges_helper();
|
|
return simtest::finish("shim_movement_unit");
|
|
}
|