#include #include #include #include #include #include #include #include import ra3; namespace { int failures = 0; auto check(bool condition, std::string_view what) -> void { if (!condition) { std::printf("FAIL: %.*s\n", static_cast(what.size()), what.data()); ++failures; } } auto append_be32(std::vector &out, std::uint32_t value) -> void { out.push_back(static_cast(value >> 24U)); out.push_back(static_cast(value >> 16U)); out.push_back(static_cast(value >> 8U)); out.push_back(static_cast(value)); } auto append_le32(std::vector &out, std::uint32_t value) -> void { out.push_back(static_cast(value)); out.push_back(static_cast(value >> 8U)); out.push_back(static_cast(value >> 16U)); out.push_back(static_cast(value >> 24U)); } /** Build a one-entry BIG4 archive and return its path. */ auto make_test_archive(const std::filesystem::path &path) -> void { const std::string name = "data\\hello.txt"; const std::string payload = "hello big4"; std::vector index; append_be32(index, 0); // offset, patched below append_be32(index, static_cast(payload.size())); index.insert(index.end(), name.begin(), name.end()); index.push_back(0); const std::uint32_t payload_offset = 16U + static_cast(index.size()); std::vector index_patched; append_be32(index_patched, payload_offset); index_patched.insert(index_patched.end(), index.begin() + 4, index.end()); std::vector file; file.insert(file.end(), {'B', 'I', 'G', '4'}); append_le32(file, static_cast(16U + index_patched.size() + payload.size())); append_be32(file, 1U); append_le32(file, 0U); file.insert(file.end(), index_patched.begin(), index_patched.end()); file.insert(file.end(), payload.begin(), payload.end()); std::ofstream out(path, std::ios::binary); out.write(reinterpret_cast(file.data()), static_cast(file.size())); } } auto main() -> int { using namespace ra3; check(core::make_name_key("Tank") == core::make_name_key("tank"), "name keys are case-insensitive"); core::message_stream stream; stream.append(core::message_id::begin_block); stream.append(core::message_id::new_game); check(stream.size() == 2U, "message stream counts entries"); check(stream.pop()->id == core::message_id::begin_block, "message stream is FIFO"); logic::game_logic simulation; simulation.prepare_new_game(7U); check(simulation.starting(), "prepare raises the starting flag"); simulation.start_new_game(); check(!simulation.messages().empty(), "start appends new_game"); simulation.update(); check(simulation.frame() == 1U, "update advances the frame counter"); check(simulation.messages().empty(), "update drains the message stream"); auto &side = simulation.players().add(std::make_unique(0U, "Test")); side.set_money(250); side.add_money(-100); check(side.money() == 150, "money clamps and accumulates"); check(game::to_string(game::faction::soviet) == "Soviet", "faction names round-trip"); // RefPack: a literal-only stream decodes to its payload. const std::vector refpack{0x10, 0xFB, 0x00, 0x00, 0x03, 0xFF, 'A', 'B', 'C'}; check(fs::is_refpack(refpack), "literal RefPack stream is recognised"); const auto decoded = fs::refpack_decompress(refpack); check(decoded.size() == 3U && decoded[0] == 'A' && decoded[2] == 'C', "RefPack literals decode"); check(fs::maybe_decompress(std::vector{'x', 'y'}).size() == 2U, "non-RefPack passes through"); // BIG4: build and read a synthetic archive. const auto archive_path = std::filesystem::temp_directory_path() / "openra3_test.big"; make_test_archive(archive_path); const auto archive = fs::big_archive::open(archive_path); check(archive.size() == 1U, "BIG4 index has one entry"); check(archive.contains("data\\hello.txt"), "BIG4 entry is indexed"); const auto payload = archive.read("data\\hello.txt"); check(std::string(payload.begin(), payload.end()) == "hello big4", "BIG4 payload reads back"); std::error_code ignored; std::filesystem::remove(archive_path, ignored); // Skirmish: deterministic, resolves within the frame cap. skirmish::match_config config; config.seed = 42U; const auto starts = skirmish::builtin_start_positions(); auto first = skirmish::skirmish_match::create(config, starts); const auto first_result = first.run(); auto second = skirmish::skirmish_match::create(config, starts); const auto second_result = second.run(); check(first_result.decided, "skirmish reaches a decision"); check(first_result.winner == 0 || first_result.winner == 1, "skirmish has a winner"); check(first_result.frames == second_result.frames && first_result.winner == second_result.winner, "skirmish is deterministic"); if (failures == 0) { std::puts("ra3_tests: OK"); } return failures == 0 ? 0 : 1; }