diff --git a/CHANGELOG.md b/CHANGELOG.md index d362c851..b99237ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- more tests of skills and bges (#66, #71) + +### Fixed + +- `all="0"` in xmls is treated as no all attribute (#70) +- structures now can have barrier (#71) +- test of db scaling did not write to db (#71) +- memory leak in test runs (#71) +- segfault on missing operation (#71) + ## [6.5.0] - 2023-07-11 -- Updated Subdue skill now cancels an attack, if subdue reduces the attack to 0 +- Updated Subdue skill now cancels an attack, if subdue reduces the attack to 0 - Update corrosive skill - Added options `update-corrosive-protect-armor` and `no-update-corrosive-protect-armor` to toggle new behaviour (default: updated) diff --git a/cards.cpp b/cards.cpp index a5badbfa..421bcc45 100644 --- a/cards.cpp +++ b/cards.cpp @@ -40,8 +40,22 @@ std::list get_abbreviations(const std::string& name) //------------------------------------------------------------------------------ Cards::~Cards() +{ + //for (Card* c: all_cards) { delete(c); } + clear(); +} + +void Cards::clear() { for (Card* c: all_cards) { delete(c); } + all_cards.clear(); + cards_by_id.clear(); + player_cards.clear(); + cards_by_name.clear(); + player_commanders.clear(); + player_assaults.clear(); + player_structures.clear(); + visible_cardset.clear(); } const Card* Cards::by_id(unsigned id) const diff --git a/cards.h b/cards.h index 53526816..73311232 100644 --- a/cards.h +++ b/cards.h @@ -23,7 +23,9 @@ class Cards std::map player_cards_abbr; std::unordered_set visible_cardset; std::unordered_set ambiguous_names; + const Card* by_id(unsigned id) const; + void clear(); void organize(); void fix_dominion_recipes(); void add_card(Card* card, const std::string & name); diff --git a/deck.cpp b/deck.cpp index 5df0ab80..e6572e2a 100644 --- a/deck.cpp +++ b/deck.cpp @@ -214,6 +214,11 @@ DeckEncoder encode_deck = encode_deck_ext_b64; namespace range = boost::range; +Decks::~Decks() +{ + decks.clear(); +} + void Deck::set(const std::vector& ids, const std::map &marks) { commander = nullptr; diff --git a/deck.h b/deck.h index 12823afb..c6abfc4b 100644 --- a/deck.h +++ b/deck.h @@ -161,11 +161,15 @@ typedef std::map DeckList; class Decks { public: - void add_deck(Deck* deck, const std::string& deck_name); - Deck* find_deck_by_name(const std::string& deck_name); + ~Decks(); + std::list decks; std::map, Deck*> by_type_id; std::map by_name; + + void add_deck(Deck* deck, const std::string& deck_name); + Deck* find_deck_by_name(const std::string& deck_name); + }; #endif diff --git a/sim.cpp b/sim.cpp index 96725391..9e8bff9c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1435,6 +1435,8 @@ void turn_end_phase(Field* fd) { continue; } + // reset the structure's (barrier) protect + status.m_protected = 0; status.m_evaded = 0; // so far only useful in Inactive turn } } @@ -1608,6 +1610,7 @@ struct PerformAttack { unsigned pre_modifier_dmg = att_status->attack_power(); // Bug fix? 2023-04-03 a card with zero attack power can not attack and won't trigger subdue + // Confirmed subdue behaviour by MK 2023-07-12 if(pre_modifier_dmg == 0) { return 0; } @@ -2289,12 +2292,25 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus { dst->ext_hp(s.x); } + + template<> +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ + //only structures can be sieged + _DEBUG_ASSERT(dst->m_card->m_type != CardType::assault); + _DEBUG_ASSERT(dst->m_card->m_type != CardType::commander); + unsigned siege_dmg = remove_absorption(fd,dst,s.x); + // structure should not have protect normally..., but let's allow it for barrier support + siege_dmg = safe_minus(siege_dmg, src->m_overloaded ? 0 : dst->m_protected); + remove_hp(fd, dst, siege_dmg); +} + template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { if (dst->m_card->m_type == CardType::structure) { - remove_hp(fd, dst, remove_absorption(fd,dst,s.x)); + perform_skill(fd, src, dst, s); } else { @@ -2355,12 +2371,6 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* d } } - template<> -inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) -{ - remove_hp(fd, dst, remove_absorption(fd,dst,s.x)); -} - template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { diff --git a/sim.h b/sim.h index b12eb96c..8a24179f 100644 --- a/sim.h +++ b/sim.h @@ -183,7 +183,7 @@ struct CardStatus const Card* m_card; unsigned m_index; unsigned m_action_index; - Field* m_field; + Field* m_field; // only needed for fixes/updated skills in attack power calculation unsigned m_player; unsigned m_delay; unsigned m_hp; diff --git a/sim_test.cpp b/sim_test.cpp index 5e386781..3403c3ec 100644 --- a/sim_test.cpp +++ b/sim_test.cpp @@ -26,7 +26,7 @@ int iter = 1000; unsigned seed = 0; // limit for float diffing // disable -double eps = 1; +double eps = 1.; // enable // double eps = 0.0000001; @@ -59,10 +59,10 @@ std::ostream &operator<<(std::ostream &os, const TestInfo &ti) inline Result run_sim(int argc, const char **argv, bool pipe_output = true) { + init(); Result res; std::string rdeck = ""; FinalResults fr; - debug_str.clear(); // auto start_time = std::chrono::system_clock::now(); @@ -84,6 +84,7 @@ inline Result run_sim(int argc, const char **argv, bool pipe_output = true) param[argc] = const_cast("-t"); param[argc + 1] = const_cast("1"); auto rett = run(argc + 2, param); + delete[] param; fr = rett.second; // result to string auto drc = rett; @@ -107,6 +108,7 @@ inline Result run_sim(int argc, const char **argv, bool pipe_output = true) param[argc] = const_cast("-t"); param[argc + 1] = const_cast("1"); auto rett = run(argc + 2, param); + delete[] param; fr = rett.second; // result to string auto drc = rett; @@ -142,10 +144,10 @@ inline void check_win_sim(TestInfo ti) // Max. Iter == 100, else check_win fails with integer vs double equal in check_win //////////// string s = std::to_string(iter); - char *ii = new char[s.length()]; + char *ii = new char[s.length()+1]; strcpy(ii, s.c_str()); s = std::to_string(seed); - char *iii = new char[s.length()]; + char *iii = new char[s.length()+1]; strcpy(iii, s.c_str()); const char *argv[] = {"tuo", ti.your_deck.c_str(), ti.enemy_deck.c_str(), "-e", ti.bge.c_str(), "sim", ii, "seed", iii, "prefix", "tests/sim/"}; // much output on error?! // better 100 iterations for test, 10 for checking errors // const char* argv[] = {"tuo",ti.your_deck.c_str(),ti.enemy_deck.c_str(),"-e",ti.bge.c_str(),"sim", ii,"seed", iii}; //much output on error?! // better 100 iterations for test, 10 for checking errors @@ -161,12 +163,12 @@ inline double time_db_sim(std::string gnt1, std::string gnt2) // Max. Iter == 100, else check_win fails with integer vs double equal in check_win //////////// string s = std::to_string(iter); - char *ii = new char[s.length()]; + char *ii = new char[s.length()+1]; strcpy(ii, s.c_str()); s = std::to_string(seed); - char *iii = new char[s.length()]; + char *iii = new char[s.length()+1]; strcpy(iii, s.c_str()); - const char *argv[] = {"tuo", gnt1.c_str(), gnt2.c_str(), "sim", ii, "seed", iii, "prefix", "tests/db/", "no-db-load"}; // much output on error?! // better 100 iterations for test, 10 for checking errors + const char *argv[] = {"tuo", gnt1.c_str(), gnt2.c_str(), "sim", ii, "seed", iii, "prefix", "tests/db/", "db", "no-db-load"}; // much output on error?! // better 100 iterations for test, 10 for checking errors // const char* argv[] = {"tuo",ti.your_deck.c_str(),ti.enemy_deck.c_str(),"-e",ti.bge.c_str(),"sim", ii,"seed", iii}; //much output on error?! // better 100 iterations for test, 10 for checking errors Result result(run_sim(sizeof(argv) / sizeof(*argv), argv)); delete[] ii; @@ -181,10 +183,10 @@ inline double time_db(std::string gnt1, std::string gnt2) // Max. Iter == 100, else check_win fails with integer vs double equal in check_win //////////// string s = std::to_string(iter); - char *ii = new char[s.length()]; + char *ii = new char[s.length()+1]; strcpy(ii, s.c_str()); s = std::to_string(seed); - char *iii = new char[s.length()]; + char *iii = new char[s.length()+1]; strcpy(iii, s.c_str()); const char *argv[] = {"tuo", gnt1.c_str(), gnt2.c_str(), "sim", ii, "seed", iii, "prefix", "tests/db/", "db"}; // much output on error?! // better 100 iterations for test, 10 for checking errors // const char* argv[] = {"tuo",ti.your_deck.c_str(),ti.enemy_deck.c_str(),"-e",ti.bge.c_str(),"sim", ii,"seed", iii}; //much output on error?! // better 100 iterations for test, 10 for checking errors @@ -201,10 +203,10 @@ inline double time_ml_sim(std::string gnt1, std::string gnt2) // Max. Iter == 100, else check_win fails with integer vs double equal in check_win //////////// string s = std::to_string(iter); - char *ii = new char[s.length()]; + char *ii = new char[s.length()+1]; strcpy(ii, s.c_str()); s = std::to_string(seed); - char *iii = new char[s.length()]; + char *iii = new char[s.length()+1]; strcpy(iii, s.c_str()); const char *argv[] = {"tuo", gnt1.c_str(), gnt2.c_str(), "sim", ii, "seed", iii, "prefix", "tests/ml/", "no-db-load"}; // much output on error?! // better 100 iterations for test, 10 for checking errors // const char* argv[] = {"tuo",ti.your_deck.c_str(),ti.enemy_deck.c_str(),"-e",ti.bge.c_str(),"sim", ii,"seed", iii}; //much output on error?! // better 100 iterations for test, 10 for checking errors @@ -221,10 +223,10 @@ inline double time_ml(std::string gnt1, std::string gnt2) // Max. Iter == 100, else check_win fails with integer vs double equal in check_win //////////// string s = std::to_string(iter); - char *ii = new char[s.length()]; + char *ii = new char[s.length()+1]; strcpy(ii, s.c_str()); s = std::to_string(seed); - char *iii = new char[s.length()]; + char *iii = new char[s.length()+1]; strcpy(iii, s.c_str()); const char *argv[] = {"tuo", gnt1.c_str(), gnt2.c_str(), "sim", ii, "seed", iii, "prefix", "tests/ml/", "no-db", "ml"}; // much output on error?! // better 100 iterations for test, 10 for checking errors // const char* argv[] = {"tuo",ti.your_deck.c_str(),ti.enemy_deck.c_str(),"-e",ti.bge.c_str(),"sim", ii,"seed", iii}; //much output on error?! // better 100 iterations for test, 10 for checking errors @@ -239,7 +241,7 @@ inline double time_ml(std::string gnt1, std::string gnt2) inline void genetic(std::string gnt1, std::string gnt2) { string s = std::to_string(iter); - char *ii = new char[s.length()]; + char *ii = new char[s.length()+1]; strcpy(ii, s.c_str()); const char *argv[] = {"tuo", gnt1.c_str(), gnt2.c_str(), "brawl", "genetic", ii}; Result result(run_sim(sizeof(argv) / sizeof(*argv), argv, false)); @@ -252,10 +254,10 @@ inline void genetic(std::string gnt1, std::string gnt2) inline void check_algo(std::string gnt1, std::string gnt2, std::string algo) { string s = std::to_string(iter); - char *ii = new char[s.length()]; + char *ii = new char[s.length()+1]; strcpy(ii, s.c_str()); s = std::to_string(seed); - char *iii = new char[s.length()]; + char *iii = new char[s.length()+1]; strcpy(iii, s.c_str()); const char *argv1[] = {"tuo", gnt1.c_str(), gnt2.c_str(), "sim", ii, "seed", iii, "prefix", "tests/algo/", "no-db"}; Result result_sim(run_sim(sizeof(argv1) / sizeof(*argv1), argv1)); @@ -277,16 +279,16 @@ inline void check_anneal(std::string gnt1, std::string gnt2) double anneal_temp = 100; double anneal_temp_down = 0.01; string s = std::to_string(iter); - char *ii = new char[s.length()]; + char *ii = new char[s.length()+1]; strcpy(ii, s.c_str()); s = std::to_string(seed); - char *iii = new char[s.length()]; + char *iii = new char[s.length()+1]; strcpy(iii, s.c_str()); s = std::to_string(anneal_temp); - char *iv = new char[s.length()]; + char *iv = new char[s.length()+1]; strcpy(iv, s.c_str()); s = std::to_string(anneal_temp_down); - char *v = new char[s.length()]; + char *v = new char[s.length()+1]; strcpy(v, s.c_str()); const char *argv1[] = {"tuo", gnt1.c_str(), gnt2.c_str(), "sim", ii, "seed", iii, "prefix", "tests/algo/", "no-db"}; Result result_sim(run_sim(sizeof(argv1) / sizeof(*argv1), argv1)); @@ -308,13 +310,13 @@ inline void check_climbex(std::string gnt1, std::string gnt2) std::string algo = "climbex"; int init = 10; string s = std::to_string(iter); - char *ii = new char[s.length()]; + char *ii = new char[s.length()+1]; strcpy(ii, s.c_str()); s = std::to_string(seed); - char *iii = new char[s.length()]; + char *iii = new char[s.length()+1]; strcpy(iii, s.c_str()); s = std::to_string(init); - char *iv = new char[s.length()]; + char *iv = new char[s.length()+1]; strcpy(iv, s.c_str()); const char *argv1[] = {"tuo", gnt1.c_str(), gnt2.c_str(), "sim", ii, "seed", iii, "prefix", "tests/algo/", "no-db"}; Result result_sim(run_sim(sizeof(argv1) / sizeof(*argv1), argv1)); @@ -336,13 +338,13 @@ inline void check_climb_forts(std::string gnt1, std::string gnt2, std::string yf std::string algo = "climb_forts"; int init = 10; string s = std::to_string(iter); - char *ii = new char[s.length()]; + char *ii = new char[s.length()+1]; strcpy(ii, s.c_str()); s = std::to_string(seed); - char *iii = new char[s.length()]; + char *iii = new char[s.length()+1]; strcpy(iii, s.c_str()); s = std::to_string(init); - char *iv = new char[s.length()]; + char *iv = new char[s.length()+1]; strcpy(iv, s.c_str()); const char *argv1[] = {"tuo", gnt1.c_str(), gnt2.c_str(), "sim", ii, "seed", iii, "yf", yf.c_str(), "yfpool", "2", "ef", ef.c_str(), "efpool", "2", "prefix", "tests/algo/", "no-db"}; Result result_sim(run_sim(sizeof(argv1) / sizeof(*argv1), argv1)); @@ -404,7 +406,7 @@ BOOST_AUTO_TEST_CASE(test_init) { seed = atoi(boost::unit_test::framework::master_test_suite().argv[2]); } - BOOST_TEST_MESSAGE("ITER: " << iter); + //BOOST_TEST_MESSAGE("ITER: " << iter); BOOST_TEST_MESSAGE("SEED: " << seed); BOOST_CHECK(1 == 1); //.. } @@ -499,22 +501,9 @@ BOOST_DATA_TEST_CASE(test_whole_decks, bdata::make(read_test_file("tests/test_wh BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(test_crashes) -BOOST_AUTO_TEST_CASE(test_crashes) +BOOST_DATA_TEST_CASE(test_crashes, bdata::make(read_test_file("tests/test_crash.csv")), ti) { eps = 1.; // only check for crashes now - std::vector> aati; - // aati.emplace_back(read_test_file("tests/test_whole_decks.csv")); - // aati.emplace_back(read_test_file("tests/test_bges.csv")); - // aati.emplace_back(read_test_file("tests/test_multi_units.csv")); - aati.emplace_back(read_test_file("tests/test_crash.csv")); - std::string decks = ""; - for (auto t = aati.begin(); t != aati.end(); ++t) - for (auto tt = t->begin(); tt != t->end(); ++tt) - decks += tt->your_deck + ";" + tt->enemy_deck + ";"; - TestInfo ti; - ti.your_deck = decks; - ti.enemy_deck = decks; - ti.bge = ""; check_win_sim(ti); } BOOST_AUTO_TEST_SUITE_END() @@ -523,13 +512,17 @@ BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(test_db) BOOST_AUTO_TEST_CASE(test_db_init) { - iter = 10000; + iter = 100000; + debug_print = 0; + debug_cached = 0; + debug_line = false; } BOOST_AUTO_TEST_SUITE(test_db_scaling) BOOST_AUTO_TEST_CASE(test_db_scaling) { - auto t1 = time_db_sim("Sir Alaric the Swift-1, Broodmother's Nexus-6, Mystic Gatekeeper-6, Ruthless Pursuer-6, Maculakornos-6, Primal Yeren-6, Megalift Foundry-6, Kinaxa Soulspark-6, Mangler of Existence-6, Mangler of Existence-6, Eranore's Obstructor-6, Eranore's Obstructor-6, ", "Sir Alaric the Swift-1, Broodmother's Nexus-6, Mystic Gatekeeper-6, Ruthless Pursuer-6, Maculakornos-6, Primal Yeren-6, Megalift Foundry-6, Kinaxa Soulspark-6, Mangler of Existence-6, Mangler of Existence-6, Eranore's Obstructor-6, Eranore's Obstructor-6,"); - auto t2 = time_db("Sir Alaric the Swift-1, Broodmother's Nexus-6, Mystic Gatekeeper-6, Ruthless Pursuer-6, Maculakornos-6, Primal Yeren-6, Megalift Foundry-6, Kinaxa Soulspark-6, Mangler of Existence-6, Mangler of Existence-6, Eranore's Obstructor-6, Eranore's Obstructor-6, ", "Sir Alaric the Swift-1, Broodmother's Nexus-6, Mystic Gatekeeper-6, Ruthless Pursuer-6, Maculakornos-6, Primal Yeren-6, Megalift Foundry-6, Kinaxa Soulspark-6, Mangler of Existence-6, Mangler of Existence-6, Eranore's Obstructor-6, Eranore's Obstructor-6,"); + BOOST_TEST_MESSAGE("DB time improvement test"); + auto t1 = time_db_sim("Sir Alaric the Swift-1, Broodmother's Nexus-1, Mystic Gatekeeper-6, Ruthless Pursuer-6, Maculakornos-6, Primal Yeren-6, Megalift Foundry-6, Kinaxa Soulspark-6, Mangler of Existence-6, Mangler of Existence-6, Eranore's Obstructor-6, Eranore's Obstructor-6, ", "Sir Alaric the Swift-1, Broodmother's Nexus-6, Mystic Gatekeeper-6, Ruthless Pursuer-6, Maculakornos-6, Primal Yeren-6, Megalift Foundry-6, Kinaxa Soulspark-6, Mangler of Existence-6, Mangler of Existence-6, Eranore's Obstructor-6, Eranore's Obstructor-6,"); + auto t2 = time_db("Sir Alaric the Swift-1, Broodmother's Nexus-1, Mystic Gatekeeper-6, Ruthless Pursuer-6, Maculakornos-6, Primal Yeren-6, Megalift Foundry-6, Kinaxa Soulspark-6, Mangler of Existence-6, Mangler of Existence-6, Eranore's Obstructor-6, Eranore's Obstructor-6, ", "Sir Alaric the Swift-1, Broodmother's Nexus-6, Mystic Gatekeeper-6, Ruthless Pursuer-6, Maculakornos-6, Primal Yeren-6, Megalift Foundry-6, Kinaxa Soulspark-6, Mangler of Existence-6, Mangler of Existence-6, Eranore's Obstructor-6, Eranore's Obstructor-6,"); BOOST_CHECK_MESSAGE(t1 > t2, "DB time improvement failed: " + std::to_string(t1) + " > " + std::to_string(t2)); } BOOST_AUTO_TEST_SUITE_END() @@ -538,7 +531,10 @@ BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(test_ml) BOOST_AUTO_TEST_CASE(test_ml_init) { - iter = 10000; + iter = 100000; + debug_print = 0; + debug_cached = 0; + debug_line = false; } BOOST_AUTO_TEST_SUITE(test_ml_scaling) BOOST_AUTO_TEST_CASE(test_ml_scaling) diff --git a/tests/sim/data/customdecks.txt b/tests/sim/data/customdecks.txt index 32abb6fa..3c1dbf9f 100644 --- a/tests/sim/data/customdecks.txt +++ b/tests/sim/data/customdecks.txt @@ -25,4 +25,20 @@ Bug62_1: Octane Optimized, Broodmother's Nexus, Orbo the Holy City, Delphi of th Bug62_2: Octane Optimized, Broodmother's Nexus, Orbo the Holy City, Sinew Shredder,Sulfuris Mine, Neocyte Spikes, Argent Crusader,Thurnaxtur,Semyaza's Disciple,Kleave's Vanskrad Bug62_3: Ascaris the Heartless,Dracorex's Nexus,Umgraf Reveler,Sinew Shredder,Sulfuris Mine, Neocyte Spikes, Argent Crusader,Thurnaxtur,Semyaza's Disciple,Kleave's Vanskrad Bug62_4: Gaia the Purifier,Alpha Retainer,Duststorm Pirates,Cassius' ATV,Exarch's Wisp,Deranged Fanatic,Deranged Fanatic,Sulfuris Mine, Neocyte Spikes, Argent Crusader, -Bug62_5: Insurgent Malika,Alpha Retainer,Exarch's Wisp, Delphi of the Pantheon,Sulfuris Mine,Zashikinoz,Demi Constrictor,Kleave's Vanskrad \ No newline at end of file +Bug62_5: Insurgent Malika,Alpha Retainer,Exarch's Wisp, Delphi of the Pantheon,Sulfuris Mine,Zashikinoz,Demi Constrictor,Kleave's Vanskrad + +Bug66: /^Bug66_\d+$/ +Bug66_01: Vyander Hazix, Alpha Retainer, Haunted Incarnation, Flak Fusillade, Pillars of Cyprion, Ku Kaili Moku, Buckle Clanger, Nautical Carrier +Bug66_02: Octane Optimized, Broodmother's Nexus, Orbo the Holy City, Barracus Redeemed, Orbo the Unstopable, Orbo, Gilian Shardkeeper +Bug66_03: Broodmother Queen, Delphinian Pilgrim, Lithid Vizier, Noxious Tank, Insanitius, Inferno Demon, Worldformer, Ruin Crawler, Sulfuris Cascade +Bug66_04: Ulgor Nerve-splicer, Boneshaper Nemesis#2, Alecto Furious-6#2,Vyander Hazix, Plutonium Ravager, Plague of Euclid,Hazard Krux +Bug66_05: Sepulchre of Talos, Conclave Manufactory, Cassius the Centurion, Relic Spelunker#5, Acolyte Immoral#2 +Bug66_06: Petrisis Gorefist, Calix the Clever#2, Remote Rig, Mausoleum Tenet, Bio Equalizer, Arcadia Redeemed, Scar Mesher +Bug66_07: Insurgent Malika,Alpha Retainer, Necropocalypse, Hylas Adroit, Avarice, Towerbreaker, Galvanic Commando, Magistrate, Irrian Grafter, Trench Hurler, Leviathan + + +Bug67: /^Bug67_\d+$/ +Bug67_1: Insurgent Malika,Alpha Retainer,Sulfuris Mine, Neocyte Spikes, Argent Crusader, Thurnaxtur, Semyaza's Disciple, Kleave's Vanskrad, Atomic Silo#2 + +Bug68: /^Bug68_\d+$/ +Bug68_1: Petrisis Gorefist, Calix the Clever#2, Remote Rig, Mausoleum Tenet \ No newline at end of file diff --git a/tests/sim/data/sim_test.cpp b/tests/sim/data/sim_test.cpp deleted file mode 100644 index 66d74fee..00000000 --- a/tests/sim/data/sim_test.cpp +++ /dev/null @@ -1,277 +0,0 @@ -// exec: ./tuo-test , verbose: ./tuo-test --log_level=all -// set-iterations ./tuo-test 100 , default = 10 // more than 100 cause errors -#ifdef TEST -#ifdef NQUEST // only without quests -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE sim - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "tyrant.h" -#include "tyrant_optimize.h" -#include "sim.h" -#include "read.h" - -using namespace std; -namespace bdata = boost::unit_test::data; -typedef std::tuple,std::string,double> Result; // score, output, time -int iter = 10; -unsigned seed = 0; -//limit for float diffing -//disable -double eps = 1; -//enable -//double eps = 0.0000001; - -//pipe output: https://stackoverflow.com/questions/5405016/can-i-check-my-programs-output-with-boost-test -struct ios_redirect { - ios_redirect( std::streambuf * new_buffer ,std::ostream& ios) - : old( ios.rdbuf( new_buffer )), iios(ios) - { } - - ~ios_redirect( ) { - iios.rdbuf( old ); - } - -private: - std::streambuf * old; - std::ostream& iios; -}; - -struct TestInfo{ - std::string your_deck,enemy_deck,bge; -}; -std::ostream& operator<<(std::ostream& os, const TestInfo& ti) - { - return os << "Your Deck: " << ti.your_deck << "; Enemy Deck: " << ti.enemy_deck << "; BGE: " << ti.bge; - } - -inline Result run_sim(int argc,const char** argv, bool pipe_output=true) -{ - Result res; - FinalResults fr; - debug_str.clear(); - // - - auto start_time = std::chrono::system_clock::now(); - //pipe output - std::stringstream output; - std::stringstream eoutput; - { - ios_redirect guard2(eoutput.rdbuf(),std::cerr); //block warnings - if(pipe_output){ - ios_redirect guard1(output.rdbuf() ,std::cout); - - ////////////////////// - // only single thread for string stream build - ////////////////////// - const char** param = new const char*[argc+2]; - for(int i = 0; i < argc;i++) - param[i] = const_cast(argv[i]); - param[argc] = const_cast("-t"); - param[argc+1] = const_cast("1"); - fr = run(argc+2,param).second; - } - else{ - //no guard here - ////////////////////// - // only single thread, else crashes - ////////////////////// - const char** param = new const char*[argc+2]; - for(int i = 0; i < argc;i++) - param[i] = const_cast(argv[i]); - param[argc] = const_cast("-t"); - param[argc+1] = const_cast("1"); - fr = run(argc+2,param).second; - } - } - - auto end_time = std::chrono::system_clock::now(); - std::chrono::duration delta_t = (end_time - start_time); - res= std::make_tuple(fr,"\n" + debug_str + output.str(),delta_t.count()); - return res; -} - -inline void check_win(Result result) { - BOOST_CHECK_MESSAGE( - 1-eps<=std::get<0>(result).wins && - eps>=std::get<0>(result).losses && - eps>=std::get<0>(result).draws - ,std::get<1>(result)); - //BOOST_CHECK(100==result.points); -} -//inline void check_win_sim(const char* your_deck, const char* enemy_deck, const char* bge="") { -inline void check_win_sim(TestInfo ti) { - ///////////// - // Max. Iter == 100, else check_win fails with integer vs double equal in check_win - //////////// - string s = std::to_string(iter); - char * ii = new char[s.length()]; - strcpy(ii,s.c_str()); - s = std::to_string(seed); - char * iii = new char[s.length()]; - strcpy(iii,s.c_str()); - const char* argv[] = {"tuo",ti.your_deck.c_str(),ti.enemy_deck.c_str(),"-e",ti.bge.c_str(),"sim", ii,"seed", iii, "prefix" , "./"}; //much output on error?! // better 100 iterations for test, 10 for checking errors - //const char* argv[] = {"tuo",ti.your_deck.c_str(),ti.enemy_deck.c_str(),"-e",ti.bge.c_str(),"sim", ii,"seed", iii}; //much output on error?! // better 100 iterations for test, 10 for checking errors - Result result(run_sim(sizeof(argv)/sizeof(*argv),argv)); - delete ii; - delete iii; - //result.second += "\nTest: " + ti.your_deck + "; " + ti.enemy_deck + "; " + ti.bge; - check_win(result); -} - -inline void genetic(std::string gnt1,std::string gnt2){ - string s = std::to_string(iter); - char * ii = new char[s.length()]; - strcpy(ii,s.c_str()); - const char* argv[] = {"tuo",gnt1.c_str(),gnt2.c_str(),"brawl","genetic",ii}; - Result result(run_sim(sizeof(argv)/sizeof(*argv),argv,false)); - std::ofstream mf; - mf.open("out.csv", std::ios_base::app); - mf << gnt1 << ";" << gnt2 << ";" << std::get<0>(result).points << ";" << std::get<2>(result) << std::endl; - mf.close(); -} - -inline void check_algo(std::string gnt1,std::string gnt2,std::string algo) { - int iter = 100; - string s = std::to_string(iter); - char * ii = new char[s.length()]; - strcpy(ii,s.c_str()); - s = std::to_string(seed); - char * iii = new char[s.length()]; - strcpy(iii,s.c_str()); - const char* argv1[] = {"tuo",gnt1.c_str(),gnt2.c_str(),"sim",ii, "seed", iii, "prefix" , "tests/algo/"}; - Result result_sim(run_sim(sizeof(argv1)/sizeof(*argv1),argv1,false)); - const char* argv2[] = {"tuo",gnt1.c_str(),gnt2.c_str(),algo.c_str(),ii, "seed", iii, "prefix" , "tests/algo/"}; - Result result_opt(run_sim(sizeof(argv2)/sizeof(*argv2),argv2,false)); - delete ii; - delete iii; - BOOST_CHECK_MESSAGE(std::get<0>(result_sim).wins < std::get<0>(result_opt).wins, - std::get<1>(result_sim) + "\n" + std::get<1>(result_opt) - ); -} - - -std::vector read_test_file(const std::string filename) { - std::vector ret; - std::ifstream test_file(filename); - if(test_file.good()) - { - try{ - while(test_file && !test_file.eof()) - { - TestInfo ti; - std::string line,yd,ed,bg; - std::getline(test_file,line); - if(is_line_empty_or_commented(line)){ continue;} - std::istringstream iss(line); - std::getline(iss,ti.your_deck,';'); - std::getline(iss,ti.enemy_deck,';'); - std::getline(iss,ti.bge,';'); - ret.push_back(ti); - } - } - catch (std::exception& e) - { - } - } - return ret; -} - -BOOST_AUTO_TEST_SUITE(test) -BOOST_AUTO_TEST_CASE(test_init) -{ - init(); - debug_print++; - debug_cached++; - debug_line =true; - seed=std::chrono::system_clock::now().time_since_epoch().count() * 2654435761; - if(boost::unit_test::framework::master_test_suite().argc>=2) - { - iter = atoi(boost::unit_test::framework::master_test_suite().argv[1]); - } - if(boost::unit_test::framework::master_test_suite().argc>=3) - { - seed=atoi(boost::unit_test::framework::master_test_suite().argv[2]); - } - BOOST_TEST_MESSAGE("ITER: " << iter); - BOOST_TEST_MESSAGE("SEED: " << seed); - BOOST_CHECK(1==1);//.. -} - - -//BOOST_AUTO_TEST_SUITE(test_algo) -//BOOST_AUTO_TEST_SUITE(test_algo_climb) // bench_climb -//BOOST_AUTO_TEST_CASE(test_algo_climb) -//{ -// check_algo("Mission#136","Mission#136","climb"); -// -//} -//BOOST_AUTO_TEST_SUITE_END() -//BOOST_AUTO_TEST_SUITE_END() - - -BOOST_AUTO_TEST_SUITE(test_sim ) -///////////////////////////////////// -// Test Cases !should! be very close fights for maximum sensitivity of errors -///////////////////////////////////// -BOOST_AUTO_TEST_SUITE(test_single_units) -BOOST_DATA_TEST_CASE(test_single_units,bdata::make(read_test_file("tests/test_sinlge_units.csv")),ti) -{ - check_win_sim(ti); -} -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(test_multi_units) -BOOST_DATA_TEST_CASE(test_multi_units,bdata::make(read_test_file("tests/test_multi_units.csv")),ti) -{ - check_win_sim(ti); -} -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(test_bges) -BOOST_DATA_TEST_CASE(test_bges,bdata::make(read_test_file("tests/test_bges.csv")),ti) -{ - check_win_sim(ti); -} -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(test_whole_decks) -BOOST_DATA_TEST_CASE(test_whole_decks,bdata::make(read_test_file("tests/test_whole_decks.csv")),ti) -{ - check_win_sim(ti); -} -BOOST_AUTO_TEST_SUITE_END() - - -BOOST_AUTO_TEST_SUITE(test_crashes) -BOOST_AUTO_TEST_CASE(test_crashes) -{ - eps=1.; //only check for crashes now - std::vector> aati; - //aati.emplace_back(read_test_file("tests/test_whole_decks.csv")); - //aati.emplace_back(read_test_file("tests/test_bges.csv")); - //aati.emplace_back(read_test_file("tests/test_multi_units.csv")); - aati.emplace_back(read_test_file("tests/test_crash.csv")); - std::string decks=""; - for(auto t=aati.begin(); t!=aati.end(); ++t) - for(auto tt=t->begin(); tt!=t->end(); ++tt) - decks += tt->your_deck + ";" + tt->enemy_deck + ";"; - TestInfo ti; - ti.your_deck=decks; - ti.enemy_deck=decks; - ti.bge=""; - check_win_sim(ti); -} -BOOST_AUTO_TEST_SUITE_END() -BOOST_AUTO_TEST_SUITE_END() -BOOST_AUTO_TEST_SUITE_END() -#endif -#endif diff --git a/tests/test_crash.csv b/tests/test_crash.csv index debb1908..4b6b53d2 100644 --- a/tests/test_crash.csv +++ b/tests/test_crash.csv @@ -1,3 +1,37 @@ //random test decks -Bug00;Bug00;"" -Bug62;Bug62;"" \ No newline at end of file +Bug00;Bug00; +Bug62;Bug62; +Bug66;Bug66; +Bug66;Bug66;Enduring-Rage +Bug66;Bug66;Fortification +Bug66;Bug66;Heroism +Bug66;Bug66;Zealots-Preservation +Bug66;Bug66;Metamorphosis +Bug66;Bug66;Megamorphosis +Bug66;Bug66;Turning-Tides +Bug66;Bug66;Halted-Orders +Bug66;Bug66;Critical-Reach +Bug66;Bug66;Temporal-Backlash +Bug66;Bug66;Blood-Vengeance +Bug66;Bug66;Virulence +Bug66;Bug66;Revenge +Bug66;Bug66;Divert +Bug66;Bug66;Devotion +Bug66;Bug66;Bloodlust +Bug66;Bug66;Brigade +Bug66;Bug66;Unity +Bug66;Bug66;Furiosity +Bug66;Bug66;Devour +Bug66;Bug66;Crackdown +Bug66;Bug66;Counterflux +Bug66;Bug66;SuperHeroism +Bug66;Bug66;Cold-Sleep +Bug66;Bug66;Iron-Will +Bug66;Bug66;Oath-Of-Loyalty +Bug66;Bug66;Devour 1 +Bug66;Bug66;Revenge 1 +Bug66;Bug66;Bloodlust 1 +Bug66;Bug66;Devour 2 +Bug66;Bug66;Revenge 2 +Bug66;Bug66;Bloodlust 2 +Bug67;Bug68; \ No newline at end of file diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 52f27f09..50afd8d7 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -268,6 +268,7 @@ void load_db(std::string prefix) { if (use_db_load) { + _DEBUG_MSG(1, "start loading db\n"); // open file to read from std::ifstream file; file.open(prefix + "data/database.yml"); @@ -333,6 +334,7 @@ void load_db(std::string prefix) } // close file file.close(); + _DEBUG_MSG(1, "done loading db\n"); } } @@ -341,6 +343,7 @@ void write_db(std::string prefix) { if (use_db_write) { + _DEBUG_MSG(1, "start writing to db\n"); // open file to write to std::ofstream file; file.open(prefix + "data/database.yml"); @@ -365,11 +368,14 @@ void write_db(std::string prefix) } // close file file.close(); + _DEBUG_MSG(1, "done writing to db\n"); } } void init() { + debug_str.clear(); + database.clear(); thread_num_iterations = 0; // written by threads thread_results = nullptr; // written by threads thread_best_results = nullptr; @@ -455,9 +461,7 @@ void init() prefered_skills.clear(); prefered_factor = 3; - for (Card *c : all_cards.all_cards) - delete c; // prevent memory leak - all_cards.visible_cardset.clear(); + all_cards.clear(); // fix defaults for (int i = 0; i < Fix::num_fixes; ++i) @@ -542,8 +546,11 @@ extern "C" JNIEXPORT void } char buffer[bufsize]; }; - std::cout.rdbuf(new androidbuf); - std::cerr.rdbuf(new androidbuf); + // TODO should these news be deleted at some point? + auto ao = new androidbuf(); + auto ae = new androidbuf(); + std::cout.rdbuf(ao); + std::cerr.rdbuf(ae); __android_log_write(ANDROID_LOG_DEBUG, "TUO_TUO", "START"); int stringCount = env->GetArrayLength(stringArray); char **param = new char *[stringCount]; @@ -566,6 +573,11 @@ extern "C" JNIEXPORT void } // std::string text = "return"; // return env->NewStringUTF(text.c_str()); + delete[] param; + delete[] cparam; + delete[] strs; + delete ao; + delete ae; } extern "C" JNIEXPORT jstring @@ -3449,9 +3461,11 @@ DeckResults run(int argc, const char **argv) #ifdef _OPENMP opt_num_threads = omp_get_max_threads(); #endif - // TODO delete ? since prefix/suffix might change we reload all cards. - // if(all_cards.all_cards.size()>0) delete(&all_cards); // complains invalid pointer - // all_cards.organize(); + // delete, since prefix/suffix might change we reload all cards. + // redundant to calling init() before run() + all_cards.clear(); + owned_alpha_dominion = nullptr; + all_cards = Cards(); Decks decks; std::unordered_map bge_aliases; @@ -4071,6 +4085,8 @@ DeckResults run(int argc, const char **argv) auto your_deck = your_decks[0]; + if (!opt_todo.empty()) + { auto op = opt_todo.back(); // for (auto op: opt_todo) { @@ -4197,6 +4213,10 @@ DeckResults run(int argc, const char **argv) } } write_db(prefix); + } + else { + std::cout << "No operation specified" << std::endl; + } return fr; } @@ -4396,7 +4416,6 @@ int main(int argc, const char **argv) usage(argc, argv); return 255; } - // init(); start(argc, argv); return 0; } diff --git a/tyrant_optimize.h b/tyrant_optimize.h index a9642fde..f4d8c5fe 100644 --- a/tyrant_optimize.h +++ b/tyrant_optimize.h @@ -110,7 +110,6 @@ namespace tuo { // TUO5 db of results // map - //EXTERN std::map> database; EXTERN std::map>>> database; EXTERN hpmml::Model win_model, loss_model, stall_model, points_model; diff --git a/xml.cpp b/xml.cpp index 7da4ffc5..d6800d75 100644 --- a/xml.cpp +++ b/xml.cpp @@ -176,7 +176,7 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) if (attack_node) { card->m_attack = atoi(attack_node->value()); if(abs(1-atk_scale)>eps) card->m_attack = ceil(card->m_attack/(atk_scale)); -} + } if (health_node) { card->m_health = atoi(health_node->value()); if(abs(1-hp_scale)>eps) card->m_health = ceil(card->m_health/(hp_scale)); @@ -299,11 +299,12 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) } if (card_node->first_node("skill")) - { // inherit no skill if there is skill node + { + // inherit no skill if there is skill node card->m_skills.clear(); card->m_skills_on_play.clear(); - //APN - card->m_skills_on_attacked.clear(); + //APN + card->m_skills_on_attacked.clear(); card->m_skills_on_death.clear(); memset(card->m_skill_value, 0, sizeof card->m_skill_value); memset(card->m_skill_trigger, 0, sizeof card->m_skill_trigger);