199 lines
4.6 KiB
C++
199 lines
4.6 KiB
C++
#include "../s2ga.hpp"
|
||
#include <catch2/benchmark/catch_benchmark.hpp>
|
||
#include <catch2/catch_all.hpp>
|
||
#include <catch2/catch_approx.hpp>
|
||
#include <catch2/catch_test_macros.hpp>
|
||
#include <catch2/matchers/catch_matchers_range_equals.hpp>
|
||
#include <cmath>
|
||
#include <random>
|
||
#include <vector>
|
||
|
||
using namespace Catch;
|
||
using namespace s2ga;
|
||
|
||
TEST_CASE("(dummy)", "[test]")
|
||
{
|
||
enum State
|
||
{
|
||
HAS_AMMO,
|
||
HAS_WEAPON,
|
||
ENEMY_ALIVE,
|
||
SATISFIED
|
||
};
|
||
|
||
action<State> shoot_action;
|
||
shoot_action.name = "SHOOT";
|
||
shoot_action.positive_preconds = bm<State>(HAS_AMMO, HAS_WEAPON, ENEMY_ALIVE);
|
||
shoot_action.negative_preconds = bm<State>(SATISFIED);
|
||
shoot_action.positive_effects = bm<State>(SATISFIED);
|
||
shoot_action.negative_effects = bm<State>(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<int> 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<float> 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<double>(num_bins);
|
||
const double critical_value = 16.92; // χ²(0.05, 9)
|
||
|
||
std::vector<int> 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<double> 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);
|
||
}
|
||
};
|
||
}
|