vibe coding an orbital mechanics simulation to try out claude code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

181 lines
6.4 KiB

#include <catch2/catch_test_macros.hpp>
#include "../src/physics.h"
#include "../src/simulation.h"
#include "../src/config_loader.h"
#include <cmath>
#include <vector>
struct ParentHistory {
std::vector<int> planet_a_parents;
std::vector<int> planet_b_parents;
};
TEST_CASE("Invalid parent: Earth should not become child of spacecraft",
"[init][parent][bug]") {
const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, 0, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/earth_mars_simple.toml"));
const int EARTH_IDX = 1;
const int SPACECRAFT_IDX = 3;
const int SUN_IDX = 0;
REQUIRE(sim->bodies[EARTH_IDX].parent_index == SUN_IDX);
for (int step = 0; step < 10; step++) {
update_simulation(sim);
int earth_parent = sim->bodies[EARTH_IDX].parent_index;
INFO("Step " << step << ": Earth's parent = " << earth_parent
<< " (" << (earth_parent == SPACECRAFT_IDX ? "SPACECRAFT" :
earth_parent == SUN_IDX ? "Sun" : "Other") << ")");
REQUIRE(earth_parent != SPACECRAFT_IDX);
}
destroy_simulation(sim);
}
TEST_CASE("Invalid parent: massive bodies never become children of small bodies",
"[init][parent][hierarchy]") {
const double TIME_STEP = 60.0;
const double MASS_THRESHOLD_RATIO = 1000.0;
SimulationState* sim = create_simulation(10, 0, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/earth_mars_simple.toml"));
for (int i = 0; i < sim->body_count; i++) {
if (sim->bodies[i].parent_index >= 0) {
int parent_idx = sim->bodies[i].parent_index;
double parent_mass = sim->bodies[parent_idx].mass;
double child_mass = sim->bodies[i].mass;
double mass_ratio = parent_mass / child_mass;
INFO("Body " << i << " (" << sim->bodies[i].name
<< ") parent " << parent_idx << " (" << sim->bodies[parent_idx].name
<< "): mass ratio = " << mass_ratio);
REQUIRE(mass_ratio >= 1.0);
if (strcmp(sim->bodies[parent_idx].name, "Spacecraft") != 0 &&
strcmp(sim->bodies[i].name, "Spacecraft") != 0) {
REQUIRE(mass_ratio >= MASS_THRESHOLD_RATIO);
}
}
}
for (int step = 0; step < 100; step++) {
update_simulation(sim);
for (int i = 0; i < sim->body_count; i++) {
if (sim->bodies[i].parent_index >= 0) {
int parent_idx = sim->bodies[i].parent_index;
double parent_mass = sim->bodies[parent_idx].mass;
double child_mass = sim->bodies[i].mass;
if (child_mass > 1e20) {
REQUIRE(parent_mass > child_mass);
}
}
}
}
destroy_simulation(sim);
}
TEST_CASE("Invalid parent: detect placeholder config values",
"[init][config][validation]") {
const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, 0, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/earth_mars_simple.toml"));
const int EARTH_IDX = 1;
const int SPACECRAFT_IDX = 3;
Vec3 craft_pos = sim->bodies[SPACECRAFT_IDX].position;
Vec3 earth_pos = sim->bodies[EARTH_IDX].position;
double distance = vec3_distance(craft_pos, earth_pos);
double min_distance = sim->bodies[EARTH_IDX].radius + sim->bodies[SPACECRAFT_IDX].radius;
INFO("Distance Earth <-> Spacecraft: " << distance << " m");
INFO("Earth position: (" << earth_pos.x << ", " << earth_pos.y << ", " << earth_pos.z << ")");
INFO("Spacecraft position: (" << craft_pos.x << ", " << craft_pos.y << ", " << craft_pos.z << ")");
INFO("Earth radius: " << sim->bodies[EARTH_IDX].radius << " m");
INFO("Spacecraft radius: " << sim->bodies[SPACECRAFT_IDX].radius << " m");
INFO("Minimum required: " << min_distance << " m (parent radius + body radius)");
REQUIRE(distance >= min_distance);
destroy_simulation(sim);
}
TEST_CASE("Mutual SOI: similar mass planets within SOI boundary",
"[init][soi][mutual][edge_case]") {
const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, 0, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/mutual_soi_close.toml"));
const int PLANET_A_IDX = 1;
const int PLANET_B_IDX = 2;
const int SUN_IDX = 0;
double planet_a_soi = sim->bodies[PLANET_A_IDX].soi_radius;
double planet_b_soi = sim->bodies[PLANET_B_IDX].soi_radius;
double separation = vec3_distance(sim->bodies[PLANET_A_IDX].position,
sim->bodies[PLANET_B_IDX].position);
INFO("PlanetA SOI: " << planet_a_soi / 1e9 << " million km");
INFO("PlanetB SOI: " << planet_b_soi / 1e9 << " million km");
INFO("Separation: " << separation / 1e9 << " million km");
REQUIRE(separation < planet_a_soi);
REQUIRE(separation < planet_b_soi);
ParentHistory history;
for (int step = 0; step < 10000; step++) {
update_simulation(sim);
history.planet_a_parents.push_back(sim->bodies[PLANET_A_IDX].parent_index);
history.planet_b_parents.push_back(sim->bodies[PLANET_B_IDX].parent_index);
if (step > 0) {
int prev_a = history.planet_a_parents[step-1];
int curr_a = history.planet_a_parents[step];
if (prev_a != curr_a) {
INFO("Step " << step << ": PlanetA parent " << prev_a
<< " -> " << curr_a);
}
int prev_b = history.planet_b_parents[step-1];
int curr_b = history.planet_b_parents[step];
if (prev_b != curr_b) {
INFO("Step " << step << ": PlanetB parent " << prev_b
<< " -> " << curr_b);
}
}
}
int final_parent_a = sim->bodies[PLANET_A_IDX].parent_index;
int final_parent_b = sim->bodies[PLANET_B_IDX].parent_index;
INFO("Final parent PlanetA: " << final_parent_a);
INFO("Final parent PlanetB: " << final_parent_b);
REQUIRE(final_parent_a == SUN_IDX);
REQUIRE(final_parent_b == SUN_IDX);
for (size_t i = 0; i < history.planet_a_parents.size(); i++) {
REQUIRE(history.planet_a_parents[i] != PLANET_B_IDX);
}
for (size_t i = 0; i < history.planet_b_parents.size(); i++) {
REQUIRE(history.planet_b_parents[i] != PLANET_A_IDX);
}
destroy_simulation(sim);
}