139 lines
4.9 KiB
C++
139 lines
4.9 KiB
C++
#include "game/sim/movement.h"
|
|
|
|
#include "check.h"
|
|
|
|
using namespace sots::sim;
|
|
|
|
static void test_vectors() {
|
|
CHECK_NEAR(Distance({0, 0, 0}, {3, 4, 0}), 5.0, 1e-12);
|
|
Vec3 p = AdvanceToward({0, 0, 0}, {10, 0, 0}, 4);
|
|
CHECK_NEAR(p.x, 4.0, 1e-12);
|
|
CHECK_NEAR(p.y, 0.0, 0.0);
|
|
p = AdvanceToward({0, 0, 0}, {10, 0, 0}, 20); // snaps to the destination
|
|
CHECK_NEAR(p.x, 10.0, 0.0);
|
|
p = AdvanceToward({1, 2, 3}, {1, 2, 3}, 5); // already there
|
|
CHECK_NEAR(p.z, 3.0, 0.0);
|
|
p = AdvanceToward({0, 0, 0}, {3, 4, 0}, 2.5); // half way along a 3-4-5
|
|
CHECK_NEAR(p.x, 1.5, 1e-12);
|
|
CHECK_NEAR(p.y, 2.0, 1e-12);
|
|
}
|
|
|
|
static void test_steps() {
|
|
CHECK_NEAR(StraightStep(10, 0.5), 5.0, 0.0);
|
|
CHECK_NEAR(StraightStep(10, kFullStep), 10.0, 0.0);
|
|
CHECK_NEAR(StraightStep(0, kHalfStep), 0.0, 0.0);
|
|
|
|
TuningTable t;
|
|
t.STUTTER_SYSTEM_INFLUENCE_RADIUS = 100;
|
|
t.STUTTER_MIN_SPEED = 0.2;
|
|
t.STUTTER_MAX_SPEED = 1.0;
|
|
CHECK_NEAR(NodeLineSpeed(10, 50, t), 6.0, 1e-12); // 10 x (0.8 x 0.5 + 0.2)
|
|
CHECK_NEAR(NodeLineSpeed(10, 0, t), 2.0, 1e-12); // at a system: min profile
|
|
CHECK_NEAR(NodeLineSpeed(10, 100, t), 10.0, 1e-12); // at the radius: max profile
|
|
CHECK_NEAR(NodeLineSpeed(10, 200, t), 10.0, 1e-12); // beyond: clamped
|
|
TuningTable zero;
|
|
CHECK_NEAR(NodeLineSpeed(10, 50, zero), 0.0, 0.0); // no tuning -> no speed
|
|
}
|
|
|
|
static void test_resolve() {
|
|
MoveStepResult r = ResolveMoveStep(5, 10, 20);
|
|
CHECK_NEAR(r.moved, 5.0, 0.0);
|
|
CHECK_NEAR(r.fraction, 1.0, 0.0);
|
|
CHECK(!r.arrived);
|
|
CHECK(!r.outOfFuel);
|
|
|
|
r = ResolveMoveStep(5, 3, 20); // range 2.95 limits the step
|
|
CHECK_NEAR(r.moved, 2.95, 1e-12);
|
|
CHECK_NEAR(r.fraction, 0.59, 1e-12);
|
|
|
|
r = ResolveMoveStep(5, 0, 20); // no fuel at all
|
|
CHECK_NEAR(r.moved, 0.0, 0.0);
|
|
CHECK(r.outOfFuel);
|
|
CHECK(!r.arrived);
|
|
|
|
r = ResolveMoveStep(5, 0, 0.01); // even a tiny hop needs range
|
|
CHECK(r.outOfFuel);
|
|
|
|
r = ResolveMoveStep(50, 100, 20); // arrives with step to spare
|
|
CHECK_NEAR(r.moved, 20.0, 0.0);
|
|
CHECK(r.arrived);
|
|
CHECK_NEAR(r.fraction, 0.4, 1e-12);
|
|
|
|
r = ResolveMoveStep(5, 0.02, 20); // range below the margin: stuck
|
|
CHECK_NEAR(r.moved, 0.0, 0.0);
|
|
CHECK(!r.outOfFuel);
|
|
|
|
r = ResolveMoveStep(0, 10, 20); // zero step
|
|
CHECK_NEAR(r.moved, 0.0, 0.0);
|
|
CHECK_NEAR(r.fraction, 1.0, 0.0);
|
|
|
|
r = ResolveMoveStep(5, 10, 0); // already at the destination
|
|
CHECK(r.arrived);
|
|
|
|
CHECK_NEAR(ConsumeShipRange(10, 3, false), 7.0, 0.0);
|
|
CHECK_NEAR(ConsumeShipRange(2, 3, false), 0.0, 0.0);
|
|
CHECK_NEAR(ConsumeShipRange(10, 3, true), 10.0, 0.0);
|
|
|
|
CHECK_NEAR(RemainingPassTime(0.4, 1.0), 0.6, 1e-12);
|
|
CHECK_NEAR(RemainingPassTime(0.4, 0.5), 0.3, 1e-12);
|
|
CHECK_NEAR(RemainingPassTime(0.99995, 1.0), 0.0, 0.0);
|
|
CHECK_NEAR(RemainingPassTime(1.0, 1.0), 0.0, 0.0);
|
|
}
|
|
|
|
static void test_multi_waypoint_turn() {
|
|
// A fleet with speed 10 and plenty of range covers a 4-unit leg, then continues
|
|
// with the remaining 0.6 of the turn onto the next leg.
|
|
double dt = kFullStep;
|
|
MoveStepResult a = ResolveMoveStep(StraightStep(10, dt), 100, 4);
|
|
CHECK(a.arrived);
|
|
dt = RemainingPassTime(a.fraction, dt);
|
|
CHECK_NEAR(dt, 0.6, 1e-12);
|
|
MoveStepResult b = ResolveMoveStep(StraightStep(10, dt), 96, 20);
|
|
CHECK_NEAR(b.moved, 6.0, 1e-12);
|
|
CHECK(!b.arrived);
|
|
CHECK_NEAR(RemainingPassTime(b.fraction, dt), 0.0, 0.0);
|
|
}
|
|
|
|
static void test_jump() {
|
|
{
|
|
simtest::ScriptedRng rng({0.7f});
|
|
JumpResult j = RollProbabilisticJump(1.0, 0.5, rng);
|
|
CHECK(!j.arrived);
|
|
CHECK_NEAR(j.stopFraction, 0.7, 1e-7);
|
|
CHECK_EQ(rng.floatDraws(), std::size_t{1});
|
|
}
|
|
{
|
|
simtest::ScriptedRng rng({0.3f});
|
|
JumpResult j = RollProbabilisticJump(1.0, 0.5, rng);
|
|
CHECK(j.arrived);
|
|
CHECK_NEAR(j.stopFraction, 1.0, 0.0);
|
|
}
|
|
{ // efficiency scales the roll: 0.9 x 0.5 = 0.45 <= 0.5 arrives
|
|
simtest::ScriptedRng rng({0.9f});
|
|
JumpResult j = RollProbabilisticJump(0.5, 0.5, rng);
|
|
CHECK(j.arrived);
|
|
}
|
|
{ // roll equal to the threshold is not "greater": arrives
|
|
simtest::ScriptedRng rng({0.5f});
|
|
JumpResult j = RollProbabilisticJump(1.0, 0.5, rng);
|
|
CHECK(j.arrived);
|
|
}
|
|
{ // determinism: the same script gives the same outcome
|
|
auto run = [] {
|
|
simtest::ScriptedRng rng({0.6f, 0.2f, 0.95f});
|
|
std::vector<double> out;
|
|
for (int i = 0; i < 3; ++i) out.push_back(RollProbabilisticJump(1.0, 0.5, rng).stopFraction);
|
|
return out;
|
|
};
|
|
CHECK(run() == run());
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
test_vectors();
|
|
test_steps();
|
|
test_resolve();
|
|
test_multi_waypoint_turn();
|
|
test_jump();
|
|
return simtest::finish("test_movement");
|
|
}
|