#include "../s2ga.hpp" #include #include #include #include #include #include #include #include using namespace Catch; using namespace s2ga; TEST_CASE("(dummy)", "[test]") { enum State { HAS_AMMO, HAS_WEAPON, ENEMY_ALIVE, SATISFIED }; action shoot_action; shoot_action.name = "SHOOT"; shoot_action.positive_preconds = bm(HAS_AMMO, HAS_WEAPON, ENEMY_ALIVE); shoot_action.negative_preconds = bm(SATISFIED); shoot_action.positive_effects = bm(SATISFIED); shoot_action.negative_effects = bm(ENEMY_ALIVE); shoot_action.cost = 0.5f; shoot_action.print(); /* Will print this: [SHOOT, 0.5] Effects: -ENEMY_ALIVE +SATISFIED Preconditions: +HAS_AMMO +HAS_WEAPON +ENEMY_ALIVE -SATISFIED */ } TEST_CASE("lehmer64 rng", "[test]") { s2ga::lehmer64 rng(0); REQUIRE(rng() != 0); const int N = 100; { std::vector ivalues; rng.seed(4); for(int i = 0; i < N; ++i) { ivalues.emplace_back(rng.random(0, 256)); } rng.seed(4); for(int i = 0; i < N; ++i) { REQUIRE(ivalues[i] == rng.random(0, 256)); } } { std::vector fvalues; rng.seed(5); for(int i = 0; i < N; ++i) { fvalues.emplace_back(rng.random(-256.0f, 256.0f)); } rng.seed(5); for(int i = 0; i < N; ++i) { REQUIRE(fvalues[i] == rng.random(-256.0f, 256.0f)); } } } TEST_CASE("uniform distribution tests", "[test]") { s2ga::lehmer64 rng(42); // Fixed seed for reproducibility SECTION("integer uniformity - chi-squared test") { const int min = 0; const int max = 9; const int num_samples = 1'000'000; const int num_bins = max - min + 1; const double expected = num_samples / static_cast(num_bins); const double critical_value = 16.92; // χ²(0.05, 9) std::vector counts(num_bins, 0); for(int i = 0; i < num_samples; ++i) { int val = rng.random(min, max); ++counts[val - min]; } double chi_sq = 0.0; for(int count: counts) { double diff = count - expected; chi_sq += (diff * diff) / expected; } CHECK(chi_sq < critical_value); } SECTION("floating point uniformity - Kolmogorov-Smirnov test") { const double min = 0.0; const double max = 1.0; const int num_samples = 100'000; const double ks_critical = 1.36 / std::sqrt(num_samples); // α=0.05 std::vector samples(num_samples); std::generate(samples.begin(), samples.end(), [&] { return rng.random(min, max); }); std::sort(samples.begin(), samples.end()); double d_plus = 0.0; double d_minus = 0.0; for(size_t i = 0; i < samples.size(); ++i) { double fn = (i + 1.0) / num_samples; double f = samples[i]; d_plus = std::max(d_plus, fn - f); d_minus = std::max(d_minus, f - (i / (num_samples - 1.0))); } double d_stat = std::max(d_plus, d_minus); CHECK(d_stat < ks_critical); } SECTION("edge case coverage") { const int iterations = 10'000; // Test minimum and maximum inclusion bool hit_min = false; bool hit_max = false; for(int i = 0; i < iterations; ++i) { int val = rng.random(1, 10); if(val == 1) hit_min = true; if(val == 10) hit_max = true; } CHECK(hit_min); CHECK(hit_max); // Floating point bounds check for(int i = 0; i < iterations; ++i) { double val = rng.random(0.0, 1.0); CHECK(val >= 0.0); CHECK(val < 1.0); } } } TEST_CASE("random benchmarking", "[benchmark]") { s2ga::lehmer64 rng(0); const int N = 100'000'000; std::mt19937 std_rng(4); std::uniform_int_distribution dist(-512, 512); BENCHMARK("mt19937 100'000'000") { for(int i = 0; i < N; ++i) { (void)dist(std_rng); } }; BENCHMARK("lehmer64 100'000'000") { for(int i = 0; i < N; ++i) { (void)dist(rng); } }; }