s2ga/tests/test.cpp
2025-03-12 18:49:28 +03:00

199 lines
4.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
}
};
}