Repository: APN-Pucky/tyrant_optimize (language: python)

Issue:
barrier does not protect structure from siege and mortar - discord bug report
> Also another bug related to Barrier on structures; Barrier doesn't protect structures from Siege and Mortar
Get sim.cpp to ~100% coverage
Cards/Skills:

- [ ] stasis commander
- [ ] berserk + enraged
- [ ] enhance
- [ ] mend
- [ ] Fly
- [ ] rush
- [ ] siege

BGEs:
- [ ] Virulence
- [ ] Revenge
- [ ] Cold Sleep
- [ ] Oath of Loyalty
- [ ] Devotion
- [ ] Bloodlust
- [ ] Brigade
- [ ] Unity
- [ ] Zealot's Preservation
- [ ] Furiosity
- [ ] Devour
- [ ] Heroism
- [ ] EnduringRage
- [ ] Counterflux
- [ ] Valor
- [ ] SuperHeroism
- [ ] Divert
- [ ] TurningTides

Mode:
- [ ] Flex
- [ ] Raid
- [ ] Brawl
- [ ] Brawl_def
- [ ] War
- [ ] War_def
- [ ] campagin


Discussion / hints:

Fix patch:
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<std::string> 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<std::string, std::string> player_cards_abbr;
     std::unordered_set<unsigned> visible_cardset;
     std::unordered_set<std::string> 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<unsigned>& ids, const std::map<signed, char> &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<std::string, long double> 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<Deck> decks;
     std::map<std::pair<DeckType::DeckType, unsigned>, Deck*> by_type_id;
     std::map<std::string, Deck*> 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<Skill::fortify>(Field* fd, CardStatus* src, CardStatus
 {
     dst->ext_hp(s.x);
 }
+
+    template<>
+inline void perform_skill<Skill::siege>(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<Skill::mortar>(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<Skill::siege>(fd, src, dst, s);
     }
     else
     {
@@ -2355,12 +2371,6 @@ inline void perform_skill<Skill::rush>(Field* fd, CardStatus* src, CardStatus* d
     }
 }
 
-    template<>
-inline void perform_skill<Skill::siege>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
-{
-    remove_hp(fd, dst, remove_absorption(fd,dst,s.x));
-}
-
     template<>
 inline void perform_skill<Skill::strike>(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/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<std::string, std::string> 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<hash of proc,ydeck edeck decks but also more,result>
-    //EXTERN std::map<std::string, Results<uint64_t>> database;
 	EXTERN std::map<std::string,std::map<std::string,std::map<std::string,Results<uint64_t>>>> database;
     EXTERN hpmml::Model win_model, loss_model, stall_model, points_model;
 
diff --git a/xml.cpp b/xml.cpp
index bc1d897d..97fe478f 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);


Test patch:
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<long double> 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<char *>("-t");
             param[argc + 1] = const_cast<char *>("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<char *>("-t");
             param[argc + 1] = const_cast<char *>("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<std::vector<TestInfo>> 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 <chrono>
-#include <boost/test/included/unit_test.hpp>
-#include <boost/test/data/test_case.hpp>
-#include <boost/test/data/monomorphic.hpp>
-#include <iostream>
-#include <fstream>
-#include <iostream>
-#include <exception>
-#include <sstream>
-#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<FinalResults<long double>,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<long double> 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<char*>(argv[i]);
-          param[argc] = const_cast<char*>("-t");
-          param[argc+1] = const_cast<char*>("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<char*>(argv[i]);
-          param[argc] = const_cast<char*>("-t");
-          param[argc+1] = const_cast<char*>("1");
-        fr = run(argc+2,param).second;
-    }
-  }
-
-    auto end_time = std::chrono::system_clock::now();
-    std::chrono::duration<double> 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<TestInfo> read_test_file(const std::string filename) {
-    std::vector<TestInfo> 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<std::vector<TestInfo>> 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