#include "game/sim/player_turn.h" #include "check.h" using namespace sots::sim; static TuningTable tuning() { TuningTable t; t.BANKRUPTCY_PROTECTION_LIMIT_FACTOR = 3.3; return t; } static void test_snapshot() { // S00 is a plain copy, and the point of the test is that it is a copy of the value // BEFORE the turn, so the identity has to hold for the negative and the saturated cases // too rather than only for the corpus' positive treasuries. CHECK_EQ(SnapshotPreviousTurn(50000), 50000); CHECK_EQ(SnapshotPreviousTurn(0), 0); CHECK_EQ(SnapshotPreviousTurn(-1234), -1234); CHECK_EQ(SnapshotPreviousTurn(2000000000), 2000000000); } // The corpus' two real empires, with the max incomes recovered by inverting the `BnkEl` each // save carries. Player 0 is not AI; player 1 is, and its income therefore carries the x1.1. static void test_column_identified_from_the_stored_limit() { const TuningTable t = tuning(); // turn1-state, player 0 ("re"): stored BnkEl -1,590,613, max income 238,592. DifficultyColumnEvidence e = IdentifyDifficultyColumn(-1590613, 238592, 262451, t); CHECK(e.column == DifficultyColumn::NonAI); CHECK(!e.IsAI()); CHECK(e.Reproduced()); // turn1-state, player 1 ("Fane Lao"): stored -1,811,273, max income 271,691 -- which is // what the AI column produces; the non-AI column would produce 271691 / 1.1. e = IdentifyDifficultyColumn(-1811273, 246992, 271691, t); CHECK(e.column == DifficultyColumn::AI); CHECK(e.IsAI()); CHECK(e.Reproduced()); } static void test_zero_income_is_ambiguous_not_identified() { const TuningTable t = tuning(); // Five of the corpus' eight players own nothing. Both columns reproduce the stored zero, // and the honest answer is "the column is unknown and cannot matter" -- not "non-AI". const DifficultyColumnEvidence e = IdentifyDifficultyColumn(0, 0, 0, t); CHECK(e.column == DifficultyColumn::Ambiguous); CHECK(!e.IsAI()); CHECK(e.Reproduced()); CHECK_EQ(e.nonAiLimit, 0); CHECK_EQ(e.aiLimit, 0); } static void test_neither_column_fits_is_unidentified() { const TuningTable t = tuning(); // A stored limit no column reproduces means the max-income chain is wrong for this // player, and the caller must abstain rather than pick the nearer of two wrong answers. const DifficultyColumnEvidence e = IdentifyDifficultyColumn(-1000000, 238592, 262451, t); CHECK(e.column == DifficultyColumn::Unidentified); CHECK(!e.IsAI()); CHECK(!e.Reproduced()); } // The identification only works because the two columns are far apart. A tenth of the income // is thousands of units of BnkEl at any empire size that matters, so a truncation can never // make the wrong column fit -- but at a max income of a handful of units they collapse, and // the answer there is Ambiguous, which is correct and is worth pinning. static void test_the_two_columns_collapse_only_at_a_trivial_income() { const TuningTable t = tuning(); DifficultyColumnEvidence e = IdentifyDifficultyColumn(-6, 1, 1, t); CHECK(e.column == DifficultyColumn::Ambiguous); // one unit apart, and already separable e = IdentifyDifficultyColumn(-133, 20, 22, t); CHECK(e.column == DifficultyColumn::NonAI); e = IdentifyDifficultyColumn(-146, 20, 22, t); CHECK(e.column == DifficultyColumn::AI); } int main() { test_snapshot(); test_column_identified_from_the_stored_limit(); test_zero_income_is_ambiguous_not_identified(); test_neither_column_fits_is_unidentified(); test_the_two_columns_collapse_only_at_a_trivial_income(); return simtest::finish("player_turn"); }