From 80fa478292275af805d6f54b27ba23f3bcd4f22a Mon Sep 17 00:00:00 2001 From: APN-Pucky Date: Fri, 6 Mar 2020 19:36:54 +0100 Subject: [PATCH 1/8] Init Evaluation DeckStrategy --- deck.cpp | 1439 ++++++++++++++++++++----------------- deck.h | 2 + make/Makefile-debug.linux | 2 +- sim.cpp | 170 ++++- sim.h | 10 +- tyrant_optimize.cpp | 36 +- tyrant_optimize.h | 2 + 7 files changed, 975 insertions(+), 686 deletions(-) diff --git a/deck.cpp b/deck.cpp index 80e8a6fe..30252d1f 100644 --- a/deck.cpp +++ b/deck.cpp @@ -13,200 +13,200 @@ #include "read.h" #include "sim.h" -template + template void partial_shuffle(RandomAccessIterator first, RandomAccessIterator middle, - RandomAccessIterator last, - UniformRandomNumberGenerator&& g) + RandomAccessIterator last, + UniformRandomNumberGenerator&& g) { - typedef typename std::iterator_traits::difference_type diff_t; - typedef typename std::make_unsigned::type udiff_t; - typedef typename std::uniform_int_distribution distr_t; - typedef typename distr_t::param_type param_t; + typedef typename std::iterator_traits::difference_type diff_t; + typedef typename std::make_unsigned::type udiff_t; + typedef typename std::uniform_int_distribution distr_t; + typedef typename distr_t::param_type param_t; - distr_t D; - diff_t m = middle - first; - diff_t n = last - first; - for (diff_t i = 0; i < m; ++i) - { - std::swap(first[i], first[D(g, param_t(i, n-1))]); - } + distr_t D; + diff_t m = middle - first; + diff_t n = last - first; + for (diff_t i = 0; i < m; ++i) + { + std::swap(first[i], first[D(g, param_t(i, n-1))]); + } } //------------------------------------------------------------------------------ const char* base64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; +"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +"abcdefghijklmnopqrstuvwxyz" +"0123456789+/"; const char* wmt_b64_magic_chars = "-.~!*_@#$%^&()[]|\\"; // Converts cards in `hash' to a deck. // Stores resulting card IDs in `ids'. void hash_to_ids_wmt_b64(const char* hash, std::vector& ids) { - unsigned int last_id = 0; - const char* pc = hash; - - while (*pc) - { - unsigned id_plus = 0; - const char* pmagic = strchr(wmt_b64_magic_chars, *pc); - if (pmagic) - { - ++ pc; - id_plus = 4000 * (pmagic - wmt_b64_magic_chars + 1); - } - if (!*pc || !*(pc + 1)) - { - throw std::runtime_error("Invalid hash length"); - } - const char* p0 = strchr(base64_chars, *pc); - const char* p1 = strchr(base64_chars, *(pc + 1)); - if (!p0 || !p1) - { - throw std::runtime_error("Invalid hash character"); - } - pc += 2; - size_t index0 = p0 - base64_chars; - size_t index1 = p1 - base64_chars; - unsigned int id = (index0 << 6) + index1; - - if (id < 4001) - { - id += id_plus; - ids.push_back(id); - last_id = id; - } - else for (unsigned int j = 0; j < id - 4001; ++j) - { - ids.push_back(last_id); - } - } + unsigned int last_id = 0; + const char* pc = hash; + + while (*pc) + { + unsigned id_plus = 0; + const char* pmagic = strchr(wmt_b64_magic_chars, *pc); + if (pmagic) + { + ++ pc; + id_plus = 4000 * (pmagic - wmt_b64_magic_chars + 1); + } + if (!*pc || !*(pc + 1)) + { + throw std::runtime_error("Invalid hash length"); + } + const char* p0 = strchr(base64_chars, *pc); + const char* p1 = strchr(base64_chars, *(pc + 1)); + if (!p0 || !p1) + { + throw std::runtime_error("Invalid hash character"); + } + pc += 2; + size_t index0 = p0 - base64_chars; + size_t index1 = p1 - base64_chars; + unsigned int id = (index0 << 6) + index1; + + if (id < 4001) + { + id += id_plus; + ids.push_back(id); + last_id = id; + } + else for (unsigned int j = 0; j < id - 4001; ++j) + { + ids.push_back(last_id); + } + } } void encode_id_wmt_b64(std::stringstream &ios, unsigned card_id) { - if (card_id > 4000) - { - ios << wmt_b64_magic_chars[(card_id - 1) / 4000 - 1]; - card_id = (card_id - 1) % 4000 + 1; - } - ios << base64_chars[card_id / 64]; - ios << base64_chars[card_id % 64]; + if (card_id > 4000) + { + ios << wmt_b64_magic_chars[(card_id - 1) / 4000 - 1]; + card_id = (card_id - 1) % 4000 + 1; + } + ios << base64_chars[card_id / 64]; + ios << base64_chars[card_id % 64]; } void encode_deck_wmt_b64(std::stringstream &ios, std::vector cards) { - unsigned last_id = 0; - unsigned num_repeat = 0; - for (const Card* card: cards) - { - auto card_id = card->m_id; - if (card_id == last_id) - { - ++ num_repeat; - } - else - { - if (num_repeat > 1) - { - ios << base64_chars[(num_repeat + 4000) / 64]; - ios << base64_chars[(num_repeat + 4000) % 64]; - } - last_id = card_id; - num_repeat = 1; - encode_id_wmt_b64(ios, card_id); - } - } - if (num_repeat > 1) - { - ios << base64_chars[(num_repeat + 4000) / 64]; - ios << base64_chars[(num_repeat + 4000) % 64]; - } + unsigned last_id = 0; + unsigned num_repeat = 0; + for (const Card* card: cards) + { + auto card_id = card->m_id; + if (card_id == last_id) + { + ++ num_repeat; + } + else + { + if (num_repeat > 1) + { + ios << base64_chars[(num_repeat + 4000) / 64]; + ios << base64_chars[(num_repeat + 4000) % 64]; + } + last_id = card_id; + num_repeat = 1; + encode_id_wmt_b64(ios, card_id); + } + } + if (num_repeat > 1) + { + ios << base64_chars[(num_repeat + 4000) / 64]; + ios << base64_chars[(num_repeat + 4000) % 64]; + } } void hash_to_ids_ext_b64(const char* hash, std::vector& ids) { - const char* pc = hash; - while (*pc) - { - unsigned id = 0; - unsigned factor = 1; - const char* p = strchr(base64_chars, *pc); - if (!p) - { throw std::runtime_error("Invalid hash character"); } - size_t d = p - base64_chars; - while (d < 32) - { - id += factor * d; - factor *= 32; - ++ pc; - p = strchr(base64_chars, *pc); - if (!p) - { throw std::runtime_error("Invalid hash character"); } - d = p - base64_chars; - } - id += factor * (d - 32); - ++ pc; - ids.push_back(id); - } + const char* pc = hash; + while (*pc) + { + unsigned id = 0; + unsigned factor = 1; + const char* p = strchr(base64_chars, *pc); + if (!p) + { throw std::runtime_error("Invalid hash character"); } + size_t d = p - base64_chars; + while (d < 32) + { + id += factor * d; + factor *= 32; + ++ pc; + p = strchr(base64_chars, *pc); + if (!p) + { throw std::runtime_error("Invalid hash character"); } + d = p - base64_chars; + } + id += factor * (d - 32); + ++ pc; + ids.push_back(id); + } } void encode_id_ext_b64(std::stringstream &ios, unsigned card_id) { - while (card_id >= 32) - { - ios << base64_chars[card_id % 32]; - card_id /= 32; - } - ios << base64_chars[card_id + 32]; + while (card_id >= 32) + { + ios << base64_chars[card_id % 32]; + card_id /= 32; + } + ios << base64_chars[card_id + 32]; } void encode_deck_ext_b64(std::stringstream &ios, std::vector cards) { - for (const Card* card: cards) - { - encode_id_ext_b64(ios, card->m_id); - } + for (const Card* card: cards) + { + encode_id_ext_b64(ios, card->m_id); + } } void hash_to_ids_ddd_b64(const char* hash, std::vector& ids) { - const char* pc = hash; - while (*pc) - { - if (!*pc || !*(pc + 1) || !*(pc + 2)) - { - throw std::runtime_error("Invalid hash length"); - } - const char* p0 = strchr(base64_chars, *pc); - const char* p1 = strchr(base64_chars, *(pc + 1)); - const char* p2 = strchr(base64_chars, *(pc + 2)); - if (!p0 || !p1 || !p2) - { - throw std::runtime_error("Invalid hash character"); - } - pc += 3; - size_t index0 = p0 - base64_chars; - size_t index1 = p1 - base64_chars; - size_t index2 = p2 - base64_chars; - unsigned int id = (index0 << 12) + (index1 << 6) + index2; - ids.push_back(id); - } + const char* pc = hash; + while (*pc) + { + if (!*pc || !*(pc + 1) || !*(pc + 2)) + { + throw std::runtime_error("Invalid hash length"); + } + const char* p0 = strchr(base64_chars, *pc); + const char* p1 = strchr(base64_chars, *(pc + 1)); + const char* p2 = strchr(base64_chars, *(pc + 2)); + if (!p0 || !p1 || !p2) + { + throw std::runtime_error("Invalid hash character"); + } + pc += 3; + size_t index0 = p0 - base64_chars; + size_t index1 = p1 - base64_chars; + size_t index2 = p2 - base64_chars; + unsigned int id = (index0 << 12) + (index1 << 6) + index2; + ids.push_back(id); + } } void encode_id_ddd_b64(std::stringstream &ios, unsigned card_id) { - ios << base64_chars[card_id / 4096]; - ios << base64_chars[card_id % 4096 / 64]; - ios << base64_chars[card_id % 64]; + ios << base64_chars[card_id / 4096]; + ios << base64_chars[card_id % 4096 / 64]; + ios << base64_chars[card_id % 64]; } void encode_deck_ddd_b64(std::stringstream &ios, std::vector cards) { - for (const Card* card: cards) - { - encode_id_ddd_b64(ios, card->m_id); - } + for (const Card* card: cards) + { + encode_id_ddd_b64(ios, card->m_id); + } } DeckDecoder hash_to_ids = hash_to_ids_ext_b64; @@ -216,616 +216,713 @@ namespace range = boost::range; void Deck::set(const std::vector& ids, const std::map &marks) { - commander = nullptr; - strategy = DeckStrategy::random; - - int non_deck_cards_seen = 0; - for (auto id: ids) - { - const Card* card{all_cards.by_id(id)}; - if (card->m_type == CardType::commander) - { - if (commander == nullptr) - { - commander = card; - if (marks.find(-1) != marks.end()) - card_marks[-1] = marks.at(-1); - } - else - { - non_deck_cards_seen++; - std::cerr << "WARNING: Ignoring additional commander " << card->m_name << " (" << commander->m_name << " already in deck)\n"; - } - } - else if (card->m_category == CardCategory::dominion_alpha) - { - add_dominion(card, false); - non_deck_cards_seen++; - } - else if (card->m_category == CardCategory::fortress_defense || card->m_category == CardCategory::fortress_siege || card->m_category == CardCategory::fortress_conquest) - { - fortress_cards.emplace_back(card); - non_deck_cards_seen++; - } - else - { - cards.emplace_back(card); - int mark_dst = cards.size() - 1; - int mark_src = mark_dst + non_deck_cards_seen; - - if (marks.find(mark_src) != marks.end()) - card_marks[mark_dst] = marks.at(mark_src); - } - } - if (commander == nullptr) - { - throw std::runtime_error("While constructing a deck: no commander found"); - } - commander_max_level = commander->m_top_level_card->m_level; - deck_size = cards.size(); + commander = nullptr; + strategy = DeckStrategy::random; + + int non_deck_cards_seen = 0; + for (auto id: ids) + { + const Card* card{all_cards.by_id(id)}; + if (card->m_type == CardType::commander) + { + if (commander == nullptr) + { + commander = card; + if (marks.find(-1) != marks.end()) + card_marks[-1] = marks.at(-1); + } + else + { + non_deck_cards_seen++; + std::cerr << "WARNING: Ignoring additional commander " << card->m_name << " (" << commander->m_name << " already in deck)\n"; + } + } + else if (card->m_category == CardCategory::dominion_alpha) + { + add_dominion(card, false); + non_deck_cards_seen++; + } + else if (card->m_category == CardCategory::fortress_defense || card->m_category == CardCategory::fortress_siege || card->m_category == CardCategory::fortress_conquest) + { + fortress_cards.emplace_back(card); + non_deck_cards_seen++; + } + else + { + cards.emplace_back(card); + int mark_dst = cards.size() - 1; + int mark_src = mark_dst + non_deck_cards_seen; + + if (marks.find(mark_src) != marks.end()) + card_marks[mark_dst] = marks.at(mark_src); + } + } + if (commander == nullptr) + { + throw std::runtime_error("While constructing a deck: no commander found"); + } + commander_max_level = commander->m_top_level_card->m_level; + deck_size = cards.size(); } void Deck::set(const std::string& deck_string_) { - deck_string = deck_string_; + deck_string = deck_string_; } void Deck::resolve() { - if (commander != nullptr) - { - return; - } - auto && id_marks = string_to_ids(all_cards, deck_string, short_description()); - set(id_marks.first, id_marks.second); - deck_string.clear(); + if (commander != nullptr) + { + return; + } + auto && id_marks = string_to_ids(all_cards, deck_string, short_description()); + set(id_marks.first, id_marks.second); + deck_string.clear(); } void Deck::shrink(const unsigned deck_len) { - if (cards.size() > deck_len) - { - cards.resize(deck_len); - } + if (cards.size() > deck_len) + { + cards.resize(deck_len); + } } void Deck::set_vip_cards(const std::string& deck_string) { - auto && id_marks = string_to_ids(all_cards, deck_string, "vip"); - for (const auto & cid : id_marks.first) - { - vip_cards.insert(cid); - } + auto && id_marks = string_to_ids(all_cards, deck_string, "vip"); + for (const auto & cid : id_marks.first) + { + vip_cards.insert(cid); + } } void Deck::set_given_hand(const std::string& deck_string) { - auto && id_marks = string_to_ids(all_cards, deck_string, "hand"); - given_hand = id_marks.first; + auto && id_marks = string_to_ids(all_cards, deck_string, "hand"); + given_hand = id_marks.first; } void Deck::add_forts(const std::string& deck_string) { - auto && id_marks = string_to_ids(all_cards, deck_string, "fortress_cards"); - for (auto id: id_marks.first) - { - fortress_cards.push_back(all_cards.by_id(id)); - } + auto && id_marks = string_to_ids(all_cards, deck_string, "fortress_cards"); + for (auto id: id_marks.first) + { + fortress_cards.push_back(all_cards.by_id(id)); + } } void Deck::add_pool_forts(const std::string& deck_string, unsigned amount) { - auto && id_marks = string_to_ids(all_cards, deck_string, "fortress_cards"); - unsigned replicates{1}; - std::vector cards; - if(id_marks.first.size() < amount) { - std::cerr << "WARNING: fortress pool bigger than fortress cards"; - } - for (auto id: id_marks.first) - { - cards.push_back(all_cards.by_id(id)); - } - variable_forts.push_back(std::make_tuple(amount,replicates,cards)); + auto && id_marks = string_to_ids(all_cards, deck_string, "fortress_cards"); + unsigned replicates{1}; + std::vector cards; + if(id_marks.first.size() < amount) { + std::cerr << "WARNING: fortress pool bigger than fortress cards"; + } + for (auto id: id_marks.first) + { + cards.push_back(all_cards.by_id(id)); + } + variable_forts.push_back(std::make_tuple(amount,replicates,cards)); } void Deck::add_dominions(const std::string& deck_string, bool override_dom) { - auto && id_marks = string_to_ids(all_cards, deck_string, "dominion_cards"); - for (auto id: id_marks.first) - { - add_dominion(all_cards.by_id(id), override_dom); - } + auto && id_marks = string_to_ids(all_cards, deck_string, "dominion_cards"); + for (auto id: id_marks.first) + { + add_dominion(all_cards.by_id(id), override_dom); + } } void Deck::add_dominion(const Card* dom_card, bool override_dom) { - if (dom_card->m_category == CardCategory::dominion_alpha) - { - if (alpha_dominion && !override_dom) - { - std::cerr << "WARNING: "; - if (!name.empty()) { std::cerr << "deck " << name << ": "; } - std::cerr << "Ignoring additional alpha dominion " << dom_card->m_name - << " (" << alpha_dominion->m_name << " already in deck)\n"; - } - else - { - if (alpha_dominion) - { - std::cerr << "WARNING: "; - if (!name.empty()) { std::cerr << "deck " << name << ": "; } - std::cerr << "Overriding alpha dominion " << alpha_dominion->m_name - << " by " << dom_card->m_name << std::endl; - } - alpha_dominion = dom_card; - } - } - else - { - std::cerr << "WARNING: "; - if (!name.empty()) { std::cerr << "deck " << name << ": "; } - std::cerr << "Ignoring non-dominion card " << dom_card->m_name << std::endl; - } + if (dom_card->m_category == CardCategory::dominion_alpha) + { + if (alpha_dominion && !override_dom) + { + std::cerr << "WARNING: "; + if (!name.empty()) { std::cerr << "deck " << name << ": "; } + std::cerr << "Ignoring additional alpha dominion " << dom_card->m_name + << " (" << alpha_dominion->m_name << " already in deck)\n"; + } + else + { + if (alpha_dominion) + { + std::cerr << "WARNING: "; + if (!name.empty()) { std::cerr << "deck " << name << ": "; } + std::cerr << "Overriding alpha dominion " << alpha_dominion->m_name + << " by " << dom_card->m_name << std::endl; + } + alpha_dominion = dom_card; + } + } + else + { + std::cerr << "WARNING: "; + if (!name.empty()) { std::cerr << "deck " << name << ": "; } + std::cerr << "Ignoring non-dominion card " << dom_card->m_name << std::endl; + } } std::string Deck::hash() const { - std::stringstream ios; - std::vector deck_all_cards; - deck_all_cards.emplace_back(commander); - if (alpha_dominion) { deck_all_cards.emplace_back(alpha_dominion); } - deck_all_cards.insert(deck_all_cards.end(), cards.begin(), cards.end()); - if (strategy == DeckStrategy::random || strategy == DeckStrategy::flexible) - { - std::sort(deck_all_cards.end() - cards.size(), deck_all_cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); - } - encode_deck(ios, deck_all_cards); - return ios.str(); + std::stringstream ios; + std::vector deck_all_cards; + deck_all_cards.emplace_back(commander); + if (alpha_dominion) { deck_all_cards.emplace_back(alpha_dominion); } + deck_all_cards.insert(deck_all_cards.end(), cards.begin(), cards.end()); + if (strategy == DeckStrategy::random || strategy == DeckStrategy::flexible || strategy == DeckStrategy::evaluate || strategy == DeckStrategy::evaluate_twice) + { + std::sort(deck_all_cards.end() - cards.size(), deck_all_cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); + } + encode_deck(ios, deck_all_cards); + return ios.str(); } std::string Deck::short_description() const { - std::stringstream ios; - ios << decktype_names[decktype]; - if (id > 0) { ios << " #" << id; } - if (!name.empty()) { ios << " \"" << name << "\""; } - if (deck_string.empty()) - { - if (variable_cards.empty()) { ios << ": " << hash(); } - } - else - { - ios << ": " << deck_string; - } - return ios.str(); + std::stringstream ios; + ios << decktype_names[decktype]; + if (id > 0) { ios << " #" << id; } + if (!name.empty()) { ios << " \"" << name << "\""; } + if (deck_string.empty()) + { + if (variable_cards.empty()) { ios << ": " << hash(); } + } + else + { + ios << ": " << deck_string; + } + return ios.str(); } std::string Deck::medium_description() const { - std::stringstream ios; - ios << short_description() << std::endl; - if (commander) - { - ios << commander->m_name; - } - else - { - ios << "No commander"; - } - - // dominions - if (alpha_dominion) - { ios << ", " << alpha_dominion->m_name; } - - // fortresses (fixed) - for (const Card * card: fortress_cards) - { ios << ", " << card->m_name; } - - // normal cards (fixed) - for (const Card * card: cards) - { ios << ", " << card->m_name; } - - // fortress (variable) - unsigned num_pool_cards = 0; - for (auto& pool: variable_forts) - { - num_pool_cards += std::get<0>(pool) * std::get<1>(pool); - } - if (num_pool_cards > 0) - { - ios << ", and " << num_pool_cards << " fortresses from pool"; - } - - // normal cards (variable) - num_pool_cards = 0; - for (auto& pool: variable_cards) - { - num_pool_cards += std::get<0>(pool) * std::get<1>(pool); - } - if (num_pool_cards > 0) - { - ios << ", and " << num_pool_cards << " cards from pool"; - } - - // upgrade points/opports info - if (upgrade_points > 0) - { - ios << " +" << upgrade_points << "/" << upgrade_opportunities; - } - return ios.str(); + std::stringstream ios; + ios << short_description() << std::endl; + if (commander) + { + ios << commander->m_name; + } + else + { + ios << "No commander"; + } + + // dominions + if (alpha_dominion) + { ios << ", " << alpha_dominion->m_name; } + + // fortresses (fixed) + for (const Card * card: fortress_cards) + { ios << ", " << card->m_name; } + + // normal cards (fixed) + for (const Card * card: cards) + { ios << ", " << card->m_name; } + + // fortress (variable) + unsigned num_pool_cards = 0; + for (auto& pool: variable_forts) + { + num_pool_cards += std::get<0>(pool) * std::get<1>(pool); + } + if (num_pool_cards > 0) + { + ios << ", and " << num_pool_cards << " fortresses from pool"; + } + + // normal cards (variable) + num_pool_cards = 0; + for (auto& pool: variable_cards) + { + num_pool_cards += std::get<0>(pool) * std::get<1>(pool); + } + if (num_pool_cards > 0) + { + ios << ", and " << num_pool_cards << " cards from pool"; + } + + // upgrade points/opports info + if (upgrade_points > 0) + { + ios << " +" << upgrade_points << "/" << upgrade_opportunities; + } + return ios.str(); } extern std::string card_description(const Cards& all_cards, const Card* c); std::string Deck::long_description() const { - std::stringstream ios; - ios << medium_description() << "\n"; - if (commander) - { - show_upgrades(ios, commander, commander_max_level, ""); - } - else - { - ios << "No commander\n"; - } - - // fixed fortresses - for (const Card * card: fortress_cards) - { - show_upgrades(ios, card, card->m_top_level_card->m_level, ""); - } - - // fixed cards - for (const Card* card: cards) - { - show_upgrades(ios, card, card->m_top_level_card->m_level, " "); - } - - // variable fortresses - for (auto& pool: variable_forts) - { - if (std::get<1>(pool) > 1) - { - ios << std::get<1>(pool) << " copies of each of "; - } - ios << std::get<0>(pool) << " in:\n"; - for (auto& card: std::get<2>(pool)) - { - show_upgrades(ios, card, card->m_top_level_card->m_level, " "); - } - } - - // variable cards - for (auto& pool: variable_cards) - { - if (std::get<1>(pool) > 1) - { - ios << std::get<1>(pool) << " copies of each of "; - } - ios << std::get<0>(pool) << " in:\n"; - for (auto& card: std::get<2>(pool)) - { - show_upgrades(ios, card, card->m_top_level_card->m_level, " "); - } - } - - // return formed string - return ios.str(); + std::stringstream ios; + ios << medium_description() << "\n"; + if (commander) + { + show_upgrades(ios, commander, commander_max_level, ""); + } + else + { + ios << "No commander\n"; + } + + // fixed fortresses + for (const Card * card: fortress_cards) + { + show_upgrades(ios, card, card->m_top_level_card->m_level, ""); + } + + // fixed cards + for (const Card* card: cards) + { + show_upgrades(ios, card, card->m_top_level_card->m_level, " "); + } + + // variable fortresses + for (auto& pool: variable_forts) + { + if (std::get<1>(pool) > 1) + { + ios << std::get<1>(pool) << " copies of each of "; + } + ios << std::get<0>(pool) << " in:\n"; + for (auto& card: std::get<2>(pool)) + { + show_upgrades(ios, card, card->m_top_level_card->m_level, " "); + } + } + + // variable cards + for (auto& pool: variable_cards) + { + if (std::get<1>(pool) > 1) + { + ios << std::get<1>(pool) << " copies of each of "; + } + ios << std::get<0>(pool) << " in:\n"; + for (auto& card: std::get<2>(pool)) + { + show_upgrades(ios, card, card->m_top_level_card->m_level, " "); + } + } + + // return formed string + return ios.str(); } void Deck::show_upgrades(std::stringstream &ios, const Card* card, unsigned card_max_level, const char * leading_chars) const { - ios << leading_chars << card_description(all_cards, card) << "\n"; - if (upgrade_points == 0 || card->m_level == card_max_level) - { - return; - } - if (debug_print < 2 && decktype != DeckType::raid) - { - while (card->m_level != card_max_level) - { card = card->upgraded(); } - ios << leading_chars << "-> " << card_description(all_cards, card) << "\n"; - return; - } - // nCm * p^m / q^(n-m) - double p = 1.0 * upgrade_points / upgrade_opportunities; - double q = 1.0 - p; - unsigned n = card_max_level - card->m_level; - unsigned m = 0; - double prob = 100.0 * pow(q, n); - ios << leading_chars << std::fixed << std::setprecision(2) << std::setw(5) << prob << "% no up\n"; - while (card->m_level != card_max_level) - { - card = card->upgraded(); - ++m; - prob = prob * (n + 1 - m) / m * p / q; - ios << leading_chars << std::setw(5) << prob << "% -> " << card_description(all_cards, card) << "\n"; - } + ios << leading_chars << card_description(all_cards, card) << "\n"; + if (upgrade_points == 0 || card->m_level == card_max_level) + { + return; + } + if (debug_print < 2 && decktype != DeckType::raid) + { + while (card->m_level != card_max_level) + { card = card->upgraded(); } + ios << leading_chars << "-> " << card_description(all_cards, card) << "\n"; + return; + } + // nCm * p^m / q^(n-m) + double p = 1.0 * upgrade_points / upgrade_opportunities; + double q = 1.0 - p; + unsigned n = card_max_level - card->m_level; + unsigned m = 0; + double prob = 100.0 * pow(q, n); + ios << leading_chars << std::fixed << std::setprecision(2) << std::setw(5) << prob << "% no up\n"; + while (card->m_level != card_max_level) + { + card = card->upgraded(); + ++m; + prob = prob * (n + 1 - m) / m * p / q; + ios << leading_chars << std::setw(5) << prob << "% -> " << card_description(all_cards, card) << "\n"; + } } Deck* Deck::clone() const { - return(new Deck(*this)); + return(new Deck(*this)); } const Card* Deck::next(Field* f) { - if (shuffled_cards.empty()) - { - return(nullptr); - } - else if (strategy == DeckStrategy::random || strategy == DeckStrategy::exact_ordered) - { - const Card* card = shuffled_cards.front(); - shuffled_cards.pop_front(); - return(card); - } - else if (strategy == DeckStrategy::ordered) - { - auto cardIter = std::min_element( - shuffled_cards.begin(), - shuffled_cards.begin() + std::min(3u, shuffled_cards.size()), - [this](const Card* card1, const Card* card2) -> bool { - auto card1_order = order.find(card1->m_id); - if (card1_order->second.empty()) - return false; - auto card2_order = order.find(card2->m_id); - if (card2_order->second.empty()) - return true; - return (*card1_order->second.begin() < *card2_order->second.begin()); - } - ); - auto card = *cardIter; - shuffled_cards.erase(cardIter); - auto card_order = order.find(card->m_id); - if (!card_order->second.empty()) - { - card_order->second.erase(card_order->second.begin()); - } - return(card); - } - else if (strategy == DeckStrategy::flexible) - { - _DEBUG_MSG(1,">>>>FLEX SIMS>>>>\n"); - std::vector res(std::min(3u,shuffled_cards.size())); - unsigned iter = f->flexible_iter; - - bool all_same{true}; - for(unsigned j =1; j < res.size();j++) - { - if(shuffled_cards.begin()[0]->m_id!=shuffled_cards.begin()[j]->m_id) - { - all_same=false; - break; - } - } - if(all_same || f->flexible_turn*2turn) //no need for flex here, three same or only one card or flexible_turn reached - { - const Card* card = shuffled_cards.front(); - shuffled_cards.pop_front(); - return(card); - } - for(unsigned j =0; j < res.size();j++) - { - bool repeat{false}; - for(unsigned k=0;km_id==shuffled_cards.begin()[k]->m_id) - { - res[j]=res[k]; //copy prev result - repeat=true; - break; - } - } - if(repeat)continue; //skip resim - for(unsigned i =0; i < iter;i++) - { - //copy hand - Hand hand1(*f->players[0]); - //hand1.deck=hand1.deck->clone(); - Hand hand2(*f->players[1]); - //hand2.deck = hand2.deck->clone(); - Deck deck1(*hand1.deck); - Deck deck2(*hand2.deck); - hand1.deck = &deck1; - hand2.deck = &deck2; - hand1.deck->strategy = DeckStrategy::random; - hand2.deck->strategy = DeckStrategy::random; - - //copy Field - Field fd(*f); - fd.players = {{&hand1,&hand2}}; - fd.tap = fd.players[fd.tapi]; - fd.tip = fd.players[fd.tipi]; - fd.selection_array.clear(); - fd.skill_queue.clear(); - fd.killed_units.clear(); - fd.damaged_units_to_times.clear(); - - std::swap(fd.tap->deck->shuffled_cards.begin()[0],fd.tap->deck->shuffled_cards.begin()[j]); - std::shuffle(++fd.tap->deck->shuffled_cards.begin(),fd.tap->deck->shuffled_cards.end(),f->re); - std::shuffle(fd.tip->deck->shuffled_cards.begin(),fd.tip->deck->shuffled_cards.end(),f->re); - - Results result(play(&fd,true)); - res[j]+=result.points; - } - } - - _DEBUG_MSG(1,"<<<m_name.c_str(),static_cast(res[0]/iter),res.size()>1?shuffled_cards[1]->m_name.c_str():"", static_cast(res.size()>1?res[1]/iter:0),res.size()>2?shuffled_cards[2]->m_name.c_str():"", static_cast(res.size()>2?res[2]/iter:0)); - unsigned best_j = std::distance(res.begin(), (f->tapi==0)?std::max_element(res.begin(), res.end()):std::min_element(res.begin(), res.end())); //max for own flex. enemy flex should optimize him, so min result is best for him - std::swap(shuffled_cards.begin()[0],shuffled_cards.begin()[best_j]); - const Card* card = shuffled_cards.front(); - shuffled_cards.pop_front(); - return(card); - } - throw std::runtime_error("Unknown strategy for deck."); + if (shuffled_cards.empty()) + { + return(nullptr); + } + else if (strategy == DeckStrategy::random || strategy == DeckStrategy::exact_ordered) + { + const Card* card = shuffled_cards.front(); + shuffled_cards.pop_front(); + return(card); + } + else if (strategy == DeckStrategy::ordered) + { + auto cardIter = std::min_element( + shuffled_cards.begin(), + shuffled_cards.begin() + std::min(3u, shuffled_cards.size()), + [this](const Card* card1, const Card* card2) -> bool { + auto card1_order = order.find(card1->m_id); + if (card1_order->second.empty()) + return false; + auto card2_order = order.find(card2->m_id); + if (card2_order->second.empty()) + return true; + return (*card1_order->second.begin() < *card2_order->second.begin()); + } + ); + auto card = *cardIter; + shuffled_cards.erase(cardIter); + auto card_order = order.find(card->m_id); + if (!card_order->second.empty()) + { + card_order->second.erase(card_order->second.begin()); + } + return(card); + } + else if (strategy == DeckStrategy::flexible) + { + _DEBUG_MSG(1,">>>>FLEX SIMS>>>>\n"); + std::vector res(std::min(3u,shuffled_cards.size())); + unsigned iter = f->flexible_iter; + + bool all_same{true}; + for(unsigned j =1; j < res.size();j++) + { + if(shuffled_cards.begin()[0]->m_id!=shuffled_cards.begin()[j]->m_id) + { + all_same=false; + break; + } + } + if(all_same || f->flexible_turn*2turn) //no need for flex here, three same or only one card or flexible_turn reached + { + const Card* card = shuffled_cards.front(); + shuffled_cards.pop_front(); + return(card); + } + for(unsigned j =0; j < res.size();j++) + { + bool repeat{false}; + for(unsigned k=0;km_id==shuffled_cards.begin()[k]->m_id) + { + res[j]=res[k]; //copy prev result + repeat=true; + break; + } + } + if(repeat)continue; //skip resim + for(unsigned i =0; i < iter;i++) + { + //copy hand + Hand hand1(*f->players[0]); + //hand1.deck=hand1.deck->clone(); + Hand hand2(*f->players[1]); + //hand2.deck = hand2.deck->clone(); + Deck deck1(*hand1.deck); + Deck deck2(*hand2.deck); + hand1.deck = &deck1; + hand2.deck = &deck2; + hand1.deck->strategy = DeckStrategy::random; + hand2.deck->strategy = DeckStrategy::random; + + //copy Field + Field fd(*f); + fd.players = {{&hand1,&hand2}}; + fd.tap = fd.players[fd.tapi]; + fd.tip = fd.players[fd.tipi]; + fd.selection_array.clear(); + fd.skill_queue.clear(); + fd.killed_units.clear(); + fd.damaged_units_to_times.clear(); + + std::swap(fd.tap->deck->shuffled_cards.begin()[0],fd.tap->deck->shuffled_cards.begin()[j]); + // randomize all following cards + std::shuffle(++fd.tap->deck->shuffled_cards.begin(),fd.tap->deck->shuffled_cards.end(),f->re); + //// randomize 2 remaining + 1 random card // worse results + //unsigned resplusone = std::min(4u,shuffled_cards.size()); + //std::shuffle(++fd.tap->deck->shuffled_cards.begin(),fd.tap->deck->shuffled_cards.begin()+resplusone,f->re); + std::shuffle(fd.tip->deck->shuffled_cards.begin(),fd.tip->deck->shuffled_cards.end(),f->re); + + Results result(play(&fd,true,true)); + res[j]+=result.points; + } + } + + _DEBUG_MSG(1,"<<<m_name.c_str(),static_cast(res[0]/iter),res.size()>1?shuffled_cards[1]->m_name.c_str():"", static_cast(res.size()>1?res[1]/iter:0),res.size()>2?shuffled_cards[2]->m_name.c_str():"", static_cast(res.size()>2?res[2]/iter:0)); + unsigned best_j = std::distance(res.begin(), (f->tapi==0)?std::max_element(res.begin(), res.end()):std::min_element(res.begin(), res.end())); //max for own flex. enemy flex should optimize him, so min result is best for him + std::swap(shuffled_cards.begin()[0],shuffled_cards.begin()[best_j]); + const Card* card = shuffled_cards.front(); + shuffled_cards.pop_front(); + return(card); + } + else if (strategy == DeckStrategy::evaluate || strategy == DeckStrategy::evaluate_twice) + { + _DEBUG_MSG(1,">>>>EVAL%i SIMS>>>>\n",strategy); + std::vector res(std::min(3u,shuffled_cards.size())); + unsigned iter = f->eval_iter; + + bool all_same{true}; + for(unsigned j =1; j < res.size();j++) + { + if(shuffled_cards.begin()[0]->m_id!=shuffled_cards.begin()[j]->m_id) + { + all_same=false; + break; + } + } + if(all_same || f->eval_turn*2turn) //no need for flex here, three same or only one card or eval_turn reached + { + const Card* card = shuffled_cards.front(); + shuffled_cards.pop_front(); + return(card); + } + _DEBUG_MSG(1, ">>EVAL%i List: (%s , %s , %s )\n",strategy,shuffled_cards[0]->m_name.c_str(),res.size()>1?shuffled_cards[1]->m_name.c_str():"", res.size()>2?shuffled_cards[2]->m_name.c_str():""); + for(unsigned j =0; j < res.size();j++) + { + bool repeat{false}; + for(unsigned k=0;km_id==shuffled_cards.begin()[k]->m_id) + { + res[j]=res[k]; //copy prev result + repeat=true; + break; + } + } + if(repeat)continue; //skip resim + for(unsigned i =0; i < iter;i++) + { + //copy hand + Hand hand1(*f->players[0]); + //hand1.deck=hand1.deck->clone(); + Hand hand2(*f->players[1]); + //hand2.deck = hand2.deck->clone(); + Deck deck1(*hand1.deck); + Deck deck2(*hand2.deck); + hand1.deck = &deck1; + hand2.deck = &deck2; + unsigned turn_steps = 1; + hand1.deck->strategy = DeckStrategy::random; + hand2.deck->strategy = DeckStrategy::random; + + + //copy Field + Field fd(*f); + fd.players = {{&hand1,&hand2}}; + fd.tap = fd.players[fd.tapi]; + fd.tip = fd.players[fd.tipi]; + fd.selection_array.clear(); + fd.skill_queue.clear(); + fd.killed_units.clear(); + fd.damaged_units_to_times.clear(); + fd.eval_iter =1; + + std::swap(fd.tap->deck->shuffled_cards.begin()[0],fd.tap->deck->shuffled_cards.begin()[j]); + // randomize all following cards + //std::shuffle(++fd.tap->deck->shuffled_cards.begin(),fd.tap->deck->shuffled_cards.end(),f->re); + // randomize 2 remaining + 1 random card // worse results + unsigned resplusone = std::min(4u,shuffled_cards.size()); + std::shuffle(++fd.tap->deck->shuffled_cards.begin(),fd.tap->deck->shuffled_cards.begin()+resplusone,f->re); + std::shuffle(fd.tip->deck->shuffled_cards.begin(),fd.tip->deck->shuffled_cards.end(),f->re); + + Results result(play(&fd,true,true,turn_steps)); + if (result.wins == 0 && result.losses ==0 && strategy == DeckStrategy::evaluate_twice) { + _DEBUG_MSG(1,">>>>>>EVAL%i SIMS>>>>>>%lu\n",strategy,result.points); + if(f->players[0]->deck->strategy==DeckStrategy::evaluate_twice)hand1.deck->strategy = DeckStrategy::evaluate; + else hand1.deck->strategy = DeckStrategy::random; + if(f->players[1]->deck->strategy==DeckStrategy::evaluate_twice)hand2.deck->strategy = DeckStrategy::evaluate; + else hand2.deck->strategy = DeckStrategy::random; + result=(play(&fd,true,false,turn_steps)); + _DEBUG_MSG(1,"<<<<<m_name.c_str(),static_cast(res[0]/iter),res.size()>1?shuffled_cards[1]->m_name.c_str():"", static_cast(res.size()>1?res[1]/iter:0),res.size()>2?shuffled_cards[2]->m_name.c_str():"", static_cast(res.size()>2?res[2]/iter:0)); + unsigned best_j = std::distance(res.begin(), (f->tapi==0)?std::max_element(res.begin(), res.end()):std::min_element(res.begin(), res.end())); //max for own flex. enemy flex should optimize him, so min result is best for him + std::swap(shuffled_cards.begin()[0],shuffled_cards.begin()[best_j]); + const Card* card = shuffled_cards.front(); + shuffled_cards.pop_front(); + return(card); + + } + throw std::runtime_error("Unknown strategy for deck."); } const Card* Deck::upgrade_card(const Card* card, unsigned card_max_level, std::mt19937& re, unsigned &remaining_upgrade_points, unsigned &remaining_upgrade_opportunities) { - unsigned oppos = card_max_level - card->m_level; - if (remaining_upgrade_points > 0) - { - for (; oppos > 0; -- oppos) - { - std::mt19937::result_type rnd = re(); - if (rnd % remaining_upgrade_opportunities < remaining_upgrade_points) - { - card = card->upgraded(); - -- remaining_upgrade_points; - } - -- remaining_upgrade_opportunities; - } - } - return card; + unsigned oppos = card_max_level - card->m_level; + if (remaining_upgrade_points > 0) + { + for (; oppos > 0; -- oppos) + { + std::mt19937::result_type rnd = re(); + if (rnd % remaining_upgrade_opportunities < remaining_upgrade_points) + { + card = card->upgraded(); + -- remaining_upgrade_points; + } + -- remaining_upgrade_opportunities; + } + } + return card; } void Deck::shuffle(std::mt19937& re) { - shuffled_commander = commander; - shuffled_forts.clear(); - boost::insert(shuffled_forts, shuffled_forts.end(), fortress_cards); - shuffled_cards.clear(); - boost::insert(shuffled_cards, shuffled_cards.end(), cards); - if (!variable_forts.empty()) - { - if (decktype == DeckType::raid && strategy != DeckStrategy::random) - { - throw std::runtime_error("Support only random strategy for raid/quest deck."); - } - for (auto& card_pool: variable_forts) - { - auto & amount = std::get<0>(card_pool); - auto & replicates = std::get<1>(card_pool); - auto & card_list = std::get<2>(card_pool); - assert(amount <= card_list.size()); - partial_shuffle(card_list.begin(), card_list.begin() + amount, card_list.end(), re); - for (unsigned rep = 0; rep < replicates; ++ rep) - { - shuffled_forts.insert(shuffled_forts.end(), card_list.begin(), card_list.begin() + amount); - } - } - } - if (!variable_cards.empty()) - { - if (decktype == DeckType::raid && strategy != DeckStrategy::random) - { - throw std::runtime_error("Support only random strategy for raid/quest deck."); - } - for (auto& card_pool: variable_cards) - { - auto & amount = std::get<0>(card_pool); - auto & replicates = std::get<1>(card_pool); - auto & card_list = std::get<2>(card_pool); - assert(amount <= card_list.size()); - partial_shuffle(card_list.begin(), card_list.begin() + amount, card_list.end(), re); - for (unsigned rep = 0; rep < replicates; ++ rep) - { - shuffled_cards.insert(shuffled_cards.end(), card_list.begin(), card_list.begin() + amount); - } - } - } - if (upgrade_points > 0) - { - unsigned remaining_upgrade_points = upgrade_points; - std::vector*, unsigned>> up_cards; - std::deque commander_storage; - commander_storage.emplace_back(shuffled_commander); - up_cards.emplace_back(&commander_storage, 0); - for (unsigned index(0); index < shuffled_forts.size(); ++ index) - { up_cards.emplace_back(&shuffled_forts, index); } - for (unsigned index(0); index < shuffled_cards.size(); ++ index) - { up_cards.emplace_back(&shuffled_cards, index); } - - // distribute upgrade points randomly (no gaussian/poisson distribution) - while (remaining_upgrade_points && up_cards.size()) - { - unsigned idx = re() % up_cards.size(); - std::pair*, unsigned> x_pair = up_cards.at(idx); - std::deque* storage_ptr = x_pair.first; - unsigned storage_idx = x_pair.second; - const Card* card = storage_ptr->at(storage_idx); - if (card->is_top_level_card()) - { - up_cards.erase(up_cards.begin() + idx); - continue; - } - (*storage_ptr)[storage_idx] = card->upgraded(); - -- remaining_upgrade_points; - } - shuffled_commander = commander_storage[0]; - } - if (strategy == DeckStrategy::ordered) - { - unsigned i = 0; - order.clear(); - for (auto card: cards) - { - order[card->m_id].push_back(i); - ++i; - } - } - if (strategy != DeckStrategy::exact_ordered) - { - std::deque* pools[] = { &shuffled_forts, &shuffled_cards }; - for (std::deque* pool : pools) - { - auto shufflable_iter = pool->begin(); - for (auto hand_card_id: given_hand) - { - auto it = std::find_if (shufflable_iter, pool->end(), - [hand_card_id](const Card* card) -> bool { return card->m_id == hand_card_id; }); - if (it != pool->end()) - { - std::swap(*shufflable_iter, *it); - ++ shufflable_iter; - } - } - std::shuffle(shufflable_iter, pool->end(), re); - } + shuffled_commander = commander; + shuffled_forts.clear(); + boost::insert(shuffled_forts, shuffled_forts.end(), fortress_cards); + shuffled_cards.clear(); + boost::insert(shuffled_cards, shuffled_cards.end(), cards); + if (!variable_forts.empty()) + { + if (decktype == DeckType::raid && strategy != DeckStrategy::random) + { + throw std::runtime_error("Support only random strategy for raid/quest deck."); + } + for (auto& card_pool: variable_forts) + { + auto & amount = std::get<0>(card_pool); + auto & replicates = std::get<1>(card_pool); + auto & card_list = std::get<2>(card_pool); + assert(amount <= card_list.size()); + partial_shuffle(card_list.begin(), card_list.begin() + amount, card_list.end(), re); + for (unsigned rep = 0; rep < replicates; ++ rep) + { + shuffled_forts.insert(shuffled_forts.end(), card_list.begin(), card_list.begin() + amount); + } + } + } + if (!variable_cards.empty()) + { + if (decktype == DeckType::raid && strategy != DeckStrategy::random) + { + throw std::runtime_error("Support only random strategy for raid/quest deck."); + } + for (auto& card_pool: variable_cards) + { + auto & amount = std::get<0>(card_pool); + auto & replicates = std::get<1>(card_pool); + auto & card_list = std::get<2>(card_pool); + assert(amount <= card_list.size()); + partial_shuffle(card_list.begin(), card_list.begin() + amount, card_list.end(), re); + for (unsigned rep = 0; rep < replicates; ++ rep) + { + shuffled_cards.insert(shuffled_cards.end(), card_list.begin(), card_list.begin() + amount); + } + } + } + if (upgrade_points > 0) + { + unsigned remaining_upgrade_points = upgrade_points; + std::vector*, unsigned>> up_cards; + std::deque commander_storage; + commander_storage.emplace_back(shuffled_commander); + up_cards.emplace_back(&commander_storage, 0); + for (unsigned index(0); index < shuffled_forts.size(); ++ index) + { up_cards.emplace_back(&shuffled_forts, index); } + for (unsigned index(0); index < shuffled_cards.size(); ++ index) + { up_cards.emplace_back(&shuffled_cards, index); } + + // distribute upgrade points randomly (no gaussian/poisson distribution) + while (remaining_upgrade_points && up_cards.size()) + { + unsigned idx = re() % up_cards.size(); + std::pair*, unsigned> x_pair = up_cards.at(idx); + std::deque* storage_ptr = x_pair.first; + unsigned storage_idx = x_pair.second; + const Card* card = storage_ptr->at(storage_idx); + if (card->is_top_level_card()) + { + up_cards.erase(up_cards.begin() + idx); + continue; + } + (*storage_ptr)[storage_idx] = card->upgraded(); + -- remaining_upgrade_points; + } + shuffled_commander = commander_storage[0]; + } + if (strategy == DeckStrategy::ordered) + { + unsigned i = 0; + order.clear(); + for (auto card: cards) + { + order[card->m_id].push_back(i); + ++i; + } + } + if (strategy != DeckStrategy::exact_ordered) + { + std::deque* pools[] = { &shuffled_forts, &shuffled_cards }; + for (std::deque* pool : pools) + { + auto shufflable_iter = pool->begin(); + for (auto hand_card_id: given_hand) + { + auto it = std::find_if (shufflable_iter, pool->end(), + [hand_card_id](const Card* card) -> bool { return card->m_id == hand_card_id; }); + if (it != pool->end()) + { + std::swap(*shufflable_iter, *it); + ++ shufflable_iter; + } + } + std::shuffle(shufflable_iter, pool->end(), re); + } #if 0 - if (!given_hand.empty()) - { - for (auto card: cards) std::cout << ", " << card->m_name; - std::cout << std::endl; - std::cout << strategy; - for (auto card: shuffled_cards) std::cout << ", " << card->m_name; - std::cout << std::endl; - } + if (!given_hand.empty()) + { + for (auto card: cards) std::cout << ", " << card->m_name; + std::cout << std::endl; + std::cout << strategy; + for (auto card: shuffled_cards) std::cout << ", " << card->m_name; + std::cout << std::endl; + } #endif - } + } #ifndef NDEBUG - if (upgrade_points > 0) - { - _DEBUG_MSG(2, " ** upgraded cards:\n"); - _DEBUG_MSG(2, " >> Commander: %s\n", shuffled_commander->m_name.c_str()); - for (auto * card: shuffled_forts) - { - _DEBUG_MSG(2, " >> Fortress: %s\n", card->m_name.c_str()); - } - for (auto * card: shuffled_cards) - { - _DEBUG_MSG(2, " >> Card from pool: %s\n", card->m_name.c_str()); - } - } + if (upgrade_points > 0) + { + _DEBUG_MSG(2, " ** upgraded cards:\n"); + _DEBUG_MSG(2, " >> Commander: %s\n", shuffled_commander->m_name.c_str()); + for (auto * card: shuffled_forts) + { + _DEBUG_MSG(2, " >> Fortress: %s\n", card->m_name.c_str()); + } + for (auto * card: shuffled_cards) + { + _DEBUG_MSG(2, " >> Card from pool: %s\n", card->m_name.c_str()); + } + } #endif } void Deck::place_at_bottom(const Card* card) { - shuffled_cards.push_back(card); + shuffled_cards.push_back(card); } void Decks::add_deck(Deck* deck, const std::string& deck_name) { - by_name[deck_name] = deck; - by_name[simplify_name(deck_name)] = deck; + by_name[deck_name] = deck; + by_name[simplify_name(deck_name)] = deck; } Deck* Decks::find_deck_by_name(const std::string& deck_name) { - auto it = by_name.find(simplify_name(deck_name)); - return it == by_name.end() ? nullptr : it->second; + auto it = by_name.find(simplify_name(deck_name)); + return it == by_name.end() ? nullptr : it->second; } diff --git a/deck.h b/deck.h index 1555d552..e079f4ee 100644 --- a/deck.h +++ b/deck.h @@ -26,6 +26,8 @@ enum DeckStrategy ordered, exact_ordered, flexible, + evaluate, + evaluate_twice, num_deckstrategies }; } diff --git a/make/Makefile-debug.linux b/make/Makefile-debug.linux index 36ba6eb8..1bfa3446 100644 --- a/make/Makefile-debug.linux +++ b/make/Makefile-debug.linux @@ -10,7 +10,7 @@ ${warning "VERSION is not set (USING NO VERSION instead), use make VERSION=vX.XX endif endif -CPPFLAGS := -Wall -Werror -std=gnu++11 -Ofast -g -DTYRANT_OPTIMIZER_VERSION='"$(VERSION)--debug"' +CPPFLAGS := -Wall -Werror -std=gnu++11 -g -DTYRANT_OPTIMIZER_VERSION='"$(VERSION)--debug"' LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_regex -lboost_timer -lpthread all: $(MAIN) diff --git a/sim.cpp b/sim.cpp index 63421b45..ac51dd89 100644 --- a/sim.cpp +++ b/sim.cpp @@ -251,6 +251,12 @@ inline signed CardStatus::calc_attack_power() const + m_temp_attack_buff; } //------------------------------------------------------------------------------ +const Card* card_by_id_safe(const Cards& cards, const unsigned card_id) +{ + const auto cardIter = cards.cards_by_id.find(card_id); + if (cardIter == cards.cards_by_id.end()) assert(false);//"UnknownCard.id[" + to_string(card_id) + "]"); } + return cardIter->second; +} std::string card_name_by_id_safe(const Cards& cards, const unsigned card_id) { const auto cardIter = cards.cards_by_id.find(card_id); @@ -1820,6 +1826,7 @@ struct PerformAttack template void attack_damage() { + remove_hp(fd, def_status, att_dmg); prepend_on_death(fd); resolve_skill(fd); @@ -2276,6 +2283,7 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { + _DEBUG_MSG(1,"absorb\n"); if (dst->m_card->m_type == CardType::structure) { remove_hp(fd, dst, remove_absorption(fd,dst,s.x)); @@ -3136,9 +3144,145 @@ inline unsigned evaluate_war_score(Field* fd, unsigned player) { return 208 - ((unsigned)(fd->turn)/2)*4; } - - -Results evaluate_sim_result(Field* fd) +int evaluate_card(Field* fd,const Card* cs); +int evaluate_skill(Field* fd,const Card* c , SkillSpec* ss) +{ + // TODO optimize this + int tvalue = ss->x; + + if(ss->card_id != 0)tvalue += 1*evaluate_card(fd,card_by_id_safe(fd->cards,ss->card_id)); + tvalue += 10*(ss->id==Skill::flurry); + tvalue += 10*(ss->id==Skill::jam); + tvalue += 5*(ss->id==Skill::overload); + tvalue += 1*(ss->id==Skill::flying); + tvalue += 1*(ss->id==Skill::evolve); + tvalue += 1*(ss->id==Skill::wall); + //tvalue += 5*(ss->id==Skill::tribute); + + tvalue *= 1.+1*(ss->id==Skill::evade); + + + tvalue *= 1.+0.5*(ss->id==Skill::drain); + tvalue *= 1.+0.5*(ss->id==Skill::flurry); + tvalue *= 1.+0.5*(ss->id==Skill::protect); + tvalue *= 1.+0.5*(ss->id==Skill::fortify); + tvalue *= 1.+0.5*(ss->id==Skill::mend); + + tvalue *= 1.+0.4*(ss->id==Skill::mortar); + tvalue *= 1.+0.4*(ss->id==Skill::sunder); + tvalue *= 1.+0.4*(ss->id==Skill::jam); + tvalue *= 1.+0.4*(ss->id==Skill::overload); + tvalue *= 1.+0.4*(ss->id==Skill::rupture); + tvalue *= 1.+0.4*(ss->id==Skill::bravery); + tvalue *= 1.+0.4*(ss->id==Skill::entrap); + tvalue *= 1.+0.4*(ss->id==Skill::heal); + + + tvalue *= 1.+0.3*(ss->id==Skill::rally); + tvalue *= 1.+0.3*(ss->id==Skill::enfeeble); + tvalue *= 1.+0.3*(ss->id==Skill::revenge); + tvalue *= 1.+0.3*(ss->id==Skill::scavenge); + tvalue *= 1.+0.3*(ss->id==Skill::strike); + tvalue *= 1.+0.3*(ss->id==Skill::enrage); + + tvalue *= 1.+0.2*(ss->id==Skill::avenge); + + tvalue *= 1.+0.1*(ss->id==Skill::hunt); + tvalue *= 1.+0.1*(ss->id==Skill::venom); + tvalue *= 1.+0.1*(ss->id==Skill::mark); + tvalue *= 1.+0.1*(ss->id==Skill::coalition); + tvalue *= 1.+0.1*(ss->id==Skill::legion); + tvalue *= 1.+0.1*(ss->id==Skill::barrier); + tvalue *= 1.+0.1*(ss->id==Skill::armor); + tvalue *= 1.+0.1*(ss->id==Skill::disease); + tvalue *= 1.+0.1*(ss->id==Skill::pierce); + //tvalue *= 1.+0.1*(ss->id==Skill::swipe); + //tvalue *= 1.+0.1*(ss->id==Skill::berserk); + + + + tvalue *= 1.-0.1*(ss->id==Skill::weaken); + + + + tvalue *= 1.-0.5 *(ss->id==Skill::payback); //sucks + tvalue *= 1.-0.5 *(ss->id==Skill::leech); //sucks + tvalue *= 1.-0.5 *(ss->id==Skill::corrosive); //sucks + tvalue *= 1.-0.5 *(ss->id==Skill::sabotage); //sucks + tvalue *= 1.-0.5 *(ss->id==Skill::inhibit); //sucks + + + tvalue *= 1.+1*ss->all; + tvalue *= 1.-1./5.*ss->all*(ss->y!=0); + tvalue *= 1.+0.75*std::min(3,ss->n); + tvalue *= 1.-1./2.* ((c->m_skill_trigger[ss->id] == Skill::Trigger::death) + (c->m_skill_trigger[ss->id] == Skill::Trigger::play)); + tvalue *= 1./(1.+ss->c); + if(tvalue == 0) std::cout << ss->id << std::endl; + if(tvalue > 1000) std::cout << ss->id << std::endl; + return tvalue; +} +int evaluate_card(Field* fd,const Card* cs) +{ + int value = 0; + value += cs->m_health; + value += cs->m_attack; + for( auto ss : cs->m_skills) { + value += evaluate_skill(fd,cs,&ss); + } + int denom_scale = 1+cs->m_delay*0; + if(value > 10000) std::cout << cs->m_name << value << std::endl; + return value /denom_scale; +} +int evaluate_cardstatus(Field* fd,CardStatus* cs) +{ + int value = 0; + value += cs->m_hp; + value += cs->attack_power(); + value += cs->protected_value(); + for( auto ss : cs->m_card->m_skills) { + value += evaluate_skill(fd,cs->m_card,&ss); + } + value -= (cs->m_enfeebled); + int denom_scale = 1+cs->m_delay*0; + if(value > 10000) std::cout << cs->m_card->m_name << value <tap +// dead commander -> the player gets zero value +int evaluate_field(Field* fd) +{ + int value = 0; + + int scale = is_alive(&fd->tap->commander); + auto& assaults(fd->tap->assaults); + auto& structures(fd->tap->structures); + + + for(unsigned index(0); index < assaults.size();++index) + { + value += scale * evaluate_cardstatus(fd,&assaults[index]); + } + for(unsigned index(0); index < structures.size();++index) + { + value += scale * evaluate_cardstatus(fd,&structures[index]); + } + + scale = is_alive(&fd->tip->commander); + auto& eassaults(fd->tip->assaults); + auto& estructures(fd->tip->structures); + for(unsigned index(0); index < eassaults.size();++index) + { + value -= (scale * evaluate_cardstatus(fd,&eassaults[index])); + } + for(unsigned index(0); index < estructures.size();++index) + { + value -= (scale * evaluate_cardstatus(fd,&estructures[index])); + } + return value; +} + + +Results evaluate_sim_result(Field* fd, bool single_turn_both) { typedef unsigned points_score_type; const auto & p = fd->players; @@ -3146,6 +3290,13 @@ Results evaluate_sim_result(Field* fd) #ifndef NQUEST unsigned quest_score = 0; #endif + + if(single_turn_both) + { + bool sign = evaluate_field(fd)<0; + unsigned val = evaluate_field(fd) *(1-2*sign); + return {!is_alive(&fd->players[1]->commander),sign,!is_alive(&fd->players[0]->commander),val}; + } switch (fd->optimization_mode) { case OptimizationMode::raid: @@ -3273,7 +3424,8 @@ Results evaluate_sim_result(Field* fd) } //------------------------------------------------------------------------------ -Results play(Field* fd,bool skip_init) +//turns_both sets the number of turns to sim before exiting before winner exists. +Results play(Field* fd,bool skip_init, bool skip_preplay,unsigned turns_both) { if(!skip_init){ //>>> start skip init fd->players[0]->commander.m_player = 0; @@ -3313,10 +3465,10 @@ Results play(Field* fd,bool skip_init) ai = opponent(ai); } }//>>> end skip init - - while(__builtin_expect(fd->turn <= turn_limit && !fd->end, true)) + unsigned both_turn_limit = fd->turn+2*turns_both; + while(__builtin_expect(fd->turn <= turn_limit && !fd->end && (turns_both==0 || fd->turn < both_turn_limit), true)) { - if(!skip_init){ //>>> start skip init + if(!skip_preplay){ //>>> start skip init fd->current_phase = Field::playcard_phase; // Initialize stuff, remove dead cards @@ -3332,7 +3484,7 @@ Results play(Field* fd,bool skip_init) //bool bge_megamorphosis = fd->bg_effects[fd->tapi][PassiveBGE::megamorphosis]; }//>>> end skip init - else { skip_init = false;} + else { skip_preplay = false;} // Play a card const Card* played_card(fd->tap->deck->next(fd)); if (played_card) @@ -3540,7 +3692,7 @@ Results play(Field* fd,bool skip_init) ++fd->turn; } - return evaluate_sim_result(fd); + return evaluate_sim_result(fd,turns_both!= 0); } //------------------------------------------------------------------------------ diff --git a/sim.h b/sim.h index 10db1da8..f606bf53 100644 --- a/sim.h +++ b/sim.h @@ -65,7 +65,7 @@ bool operator <(const FinalResults& x, const FinalResults play(Field* fd, bool skip_init=false); +Results play(Field* fd, bool skip_init=false, bool skip_preplay=false , unsigned turns_both=0); // Pool-based indexed storage. //---------------------- Pool-based indexed storage ---------------------------- template @@ -301,6 +301,9 @@ class Field unsigned turn; unsigned flexible_iter = 20; unsigned flexible_turn = 20; + unsigned eval_iter = 10; + unsigned eval_turn = 20; + gamemode_t gamemode; OptimizationMode optimization_mode; #ifndef NQUEST @@ -346,7 +349,7 @@ class Field std::array& enemy_bg_effects_, std::vector& your_bg_skills_, std::vector& enemy_bg_skills_,bool (&fixes_)[Fix::num_fixes], - unsigned flexible_iter_=20,unsigned flexible_turn_=10) : + unsigned flexible_iter_=20,unsigned flexible_turn_=20,unsigned eval_iter_=10,unsigned eval_turn_=20) : end{false}, re(re_), cards(cards_), @@ -354,6 +357,9 @@ class Field turn(1), flexible_iter(flexible_iter_), flexible_turn(flexible_turn_), + eval_iter(eval_iter_), + eval_turn(eval_turn_), + gamemode(gamemode_), optimization_mode(optimization_mode_), #ifndef NQUEST diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 99d39d48..7219107c 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -173,7 +173,9 @@ void init() iterations_multiplier=10; sim_seed=0; flexible_iter=20; - flexible_turn=10; + flexible_turn=20; + eval_iter=10; + eval_turn=20; requirement.num_cards.clear(); #ifndef NQUEST //quest = new Quest(); //TODO Quest bugged in Android now here @@ -667,7 +669,7 @@ FinalResults compute_score(const EvaluatedResults& results, std::ve #ifndef NQUEST quest, #endif - your_bg_effects, enemy_bg_effects, your_bg_skills, enemy_bg_skills,fixes, flexible_iter,flexible_turn); + your_bg_effects, enemy_bg_effects, your_bg_skills, enemy_bg_skills,fixes, flexible_iter,flexible_turn, eval_iter,eval_turn); Results result(play(&fd)); if (__builtin_expect(mode_open_the_deck, false)) { @@ -1299,7 +1301,7 @@ if (deck->alpha_dominion) { std::cout << ", " << deck->alpha_dominion->m_name; } // print deck cards -if (deck->strategy == DeckStrategy::random || deck->strategy == DeckStrategy::flexible) +if (deck->strategy == DeckStrategy::random || deck->strategy == DeckStrategy::flexible || deck->strategy == DeckStrategy::evaluate|| deck->strategy == DeckStrategy::evaluate_twice) { std::sort(deck->cards.begin(), deck->cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); } @@ -1982,10 +1984,38 @@ FinalResults run(int argc, char** argv) flexible_turn = atoi(argv[argIndex+1]); argIndex += 1; } + else if (strcmp(argv[argIndex], "evaluate") == 0 || strcmp(argv[argIndex], "eval") == 0) + { + opt_your_strategy = DeckStrategy::evaluate; + } + else if (strcmp(argv[argIndex], "evaluate2") == 0 || strcmp(argv[argIndex], "eval2") == 0) + { + opt_your_strategy = DeckStrategy::evaluate_twice; + } + else if (strcmp(argv[argIndex], "eval-iter") == 0) + { + if(check_input_amount(argc,argv,argIndex,1))exit(1); + eval_iter = atoi(argv[argIndex+1]); + argIndex += 1; + } + else if (strcmp(argv[argIndex], "eval-turn") == 0) + { + if(check_input_amount(argc,argv,argIndex,1))exit(1); + eval_turn = atoi(argv[argIndex+1]); + argIndex += 1; + } else if (strcmp(argv[argIndex], "exact-ordered") == 0) { opt_your_strategy = DeckStrategy::exact_ordered; } + else if (strcmp(argv[argIndex], "enemy:evaluate") == 0) + { + opt_enemy_strategy = DeckStrategy::evaluate; + } + else if (strcmp(argv[argIndex], "enemy:evaluater2") == 0) + { + opt_enemy_strategy = DeckStrategy::evaluate_twice; + } else if (strcmp(argv[argIndex], "enemy:flexible") == 0) { opt_enemy_strategy = DeckStrategy::flexible; diff --git a/tyrant_optimize.h b/tyrant_optimize.h index 680111ff..9a990fe1 100644 --- a/tyrant_optimize.h +++ b/tyrant_optimize.h @@ -57,6 +57,8 @@ namespace tuo { EXTERN unsigned sim_seed; EXTERN unsigned flexible_iter; EXTERN unsigned flexible_turn; + EXTERN unsigned eval_iter; + EXTERN unsigned eval_turn; EXTERN Requirement requirement; #ifndef NQUEST EXTERN Quest quest; From 38617cde53207f062aca0305509455bb2d7a1306 Mon Sep 17 00:00:00 2001 From: APN-Pucky Date: Fri, 6 Mar 2020 23:19:46 +0100 Subject: [PATCH 2/8] First tuning of parameters of evaluate --- deck.cpp | 5 ++- sim.cpp | 74 +++++++++++++++++++++++---------------------- tyrant_optimize.cpp | 4 +-- 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/deck.cpp b/deck.cpp index 30252d1f..72967e1b 100644 --- a/deck.cpp +++ b/deck.cpp @@ -710,7 +710,6 @@ const Card* Deck::next(Field* f) Deck deck2(*hand2.deck); hand1.deck = &deck1; hand2.deck = &deck2; - unsigned turn_steps = 1; hand1.deck->strategy = DeckStrategy::random; hand2.deck->strategy = DeckStrategy::random; @@ -734,14 +733,14 @@ const Card* Deck::next(Field* f) std::shuffle(++fd.tap->deck->shuffled_cards.begin(),fd.tap->deck->shuffled_cards.begin()+resplusone,f->re); std::shuffle(fd.tip->deck->shuffled_cards.begin(),fd.tip->deck->shuffled_cards.end(),f->re); - Results result(play(&fd,true,true,turn_steps)); + Results result(play(&fd,true,true,1)); if (result.wins == 0 && result.losses ==0 && strategy == DeckStrategy::evaluate_twice) { _DEBUG_MSG(1,">>>>>>EVAL%i SIMS>>>>>>%lu\n",strategy,result.points); if(f->players[0]->deck->strategy==DeckStrategy::evaluate_twice)hand1.deck->strategy = DeckStrategy::evaluate; else hand1.deck->strategy = DeckStrategy::random; if(f->players[1]->deck->strategy==DeckStrategy::evaluate_twice)hand2.deck->strategy = DeckStrategy::evaluate; else hand2.deck->strategy = DeckStrategy::random; - result=(play(&fd,true,false,turn_steps)); + result=(play(&fd,true,false,1)); _DEBUG_MSG(1,"<<<<<id==Skill::flurry); tvalue += 10*(ss->id==Skill::jam); tvalue += 5*(ss->id==Skill::overload); - tvalue += 1*(ss->id==Skill::flying); - tvalue += 1*(ss->id==Skill::evolve); - tvalue += 1*(ss->id==Skill::wall); - //tvalue += 5*(ss->id==Skill::tribute); + tvalue += 2*(ss->id==Skill::flying); + tvalue += 2*(ss->id==Skill::evolve); + tvalue += 2*(ss->id==Skill::wall); + tvalue += 5*(ss->id==Skill::tribute); - tvalue *= 1.+1*(ss->id==Skill::evade); + tvalue *= 1.+1.5*(ss->id==Skill::flurry); + tvalue *= 1.+1.5*(ss->id==Skill::drain); + tvalue *= 1.+1.5*(ss->id==Skill::mortar); + tvalue *= 1.+1.5*(ss->id==Skill::scavenge); + tvalue *= 1.+1.5*(ss->id==Skill::disease); + tvalue *= 1.+1.3*(ss->id==Skill::rally); + tvalue *= 1.+1.3*(ss->id==Skill::strike); - tvalue *= 1.+0.5*(ss->id==Skill::drain); - tvalue *= 1.+0.5*(ss->id==Skill::flurry); - tvalue *= 1.+0.5*(ss->id==Skill::protect); - tvalue *= 1.+0.5*(ss->id==Skill::fortify); + tvalue *= 1.+1.2*(ss->id==Skill::avenge); + tvalue *= 1.+1.1*(ss->id==Skill::sunder); + tvalue *= 1.+1.1*(ss->id==Skill::venom); + + tvalue *= 1.+1.0*(ss->id==Skill::evade); + tvalue *= 1.+1.0*(ss->id==Skill::enfeeble); + + tvalue *= 1.+0.2*(ss->id==Skill::protect); + + tvalue *= 1.+0.2*(ss->id==Skill::fortify); tvalue *= 1.+0.5*(ss->id==Skill::mend); - tvalue *= 1.+0.4*(ss->id==Skill::mortar); - tvalue *= 1.+0.4*(ss->id==Skill::sunder); tvalue *= 1.+0.4*(ss->id==Skill::jam); tvalue *= 1.+0.4*(ss->id==Skill::overload); - tvalue *= 1.+0.4*(ss->id==Skill::rupture); + //tvalue *= 1.+0.4*(ss->id==Skill::rupture); tvalue *= 1.+0.4*(ss->id==Skill::bravery); tvalue *= 1.+0.4*(ss->id==Skill::entrap); tvalue *= 1.+0.4*(ss->id==Skill::heal); - tvalue *= 1.+0.3*(ss->id==Skill::rally); - tvalue *= 1.+0.3*(ss->id==Skill::enfeeble); tvalue *= 1.+0.3*(ss->id==Skill::revenge); - tvalue *= 1.+0.3*(ss->id==Skill::scavenge); - tvalue *= 1.+0.3*(ss->id==Skill::strike); tvalue *= 1.+0.3*(ss->id==Skill::enrage); - tvalue *= 1.+0.2*(ss->id==Skill::avenge); - tvalue *= 1.+0.1*(ss->id==Skill::hunt); - tvalue *= 1.+0.1*(ss->id==Skill::venom); + //tvalue *= 1.+2.1*(ss->id==Skill::hunt); tvalue *= 1.+0.1*(ss->id==Skill::mark); tvalue *= 1.+0.1*(ss->id==Skill::coalition); tvalue *= 1.+0.1*(ss->id==Skill::legion); - tvalue *= 1.+0.1*(ss->id==Skill::barrier); - tvalue *= 1.+0.1*(ss->id==Skill::armor); - tvalue *= 1.+0.1*(ss->id==Skill::disease); + //tvalue *= 1.+1.1*(ss->id==Skill::barrier); tvalue *= 1.+0.1*(ss->id==Skill::pierce); + tvalue *= 1.+0.1*(ss->id==Skill::armor); //tvalue *= 1.+0.1*(ss->id==Skill::swipe); //tvalue *= 1.+0.1*(ss->id==Skill::berserk); - - - tvalue *= 1.-0.1*(ss->id==Skill::weaken); - tvalue *= 1.-0.5 *(ss->id==Skill::payback); //sucks - tvalue *= 1.-0.5 *(ss->id==Skill::leech); //sucks - tvalue *= 1.-0.5 *(ss->id==Skill::corrosive); //sucks tvalue *= 1.-0.5 *(ss->id==Skill::sabotage); //sucks tvalue *= 1.-0.5 *(ss->id==Skill::inhibit); //sucks + tvalue *= 1.-0.5 *(ss->id==Skill::corrosive); //sucks + tvalue *= 1.-0.5 *(ss->id==Skill::payback); //sucks + tvalue *= 1.-0.5 *(ss->id==Skill::leech); //sucks tvalue *= 1.+1*ss->all; tvalue *= 1.-1./5.*ss->all*(ss->y!=0); - tvalue *= 1.+0.75*std::min(3,ss->n); - tvalue *= 1.-1./2.* ((c->m_skill_trigger[ss->id] == Skill::Trigger::death) + (c->m_skill_trigger[ss->id] == Skill::Trigger::play)); - tvalue *= 1./(1.+ss->c); - if(tvalue == 0) std::cout << ss->id << std::endl; - if(tvalue > 1000) std::cout << ss->id << std::endl; - return tvalue; + tvalue *= 1.+1*std::min(3,ss->n); + tvalue *= 1.-1./3.* ((c->m_skill_trigger[ss->id] == Skill::Trigger::death) + (c->m_skill_trigger[ss->id] == Skill::Trigger::play)); + tvalue *= 1./(2.+ss->c); + if(tvalue == 0) std::cout << ss->id << " "< 10000) std::cout << ss->id <<" "<< tvalue << std::endl; + return 0.9*tvalue; // 0.85 } int evaluate_card(Field* fd,const Card* cs) { int value = 0; value += cs->m_health; - value += cs->m_attack; + value += 2*cs->m_attack; for( auto ss : cs->m_skills) { value += evaluate_skill(fd,cs,&ss); } @@ -3237,7 +3237,7 @@ int evaluate_cardstatus(Field* fd,CardStatus* cs) { int value = 0; value += cs->m_hp; - value += cs->attack_power(); + value += 2*cs->attack_power(); value += cs->protected_value(); for( auto ss : cs->m_card->m_skills) { value += evaluate_skill(fd,cs->m_card,&ss); @@ -3258,6 +3258,7 @@ int evaluate_field(Field* fd) auto& structures(fd->tap->structures); + value += 0.5*scale * evaluate_cardstatus(fd,&fd->tap->commander); for(unsigned index(0); index < assaults.size();++index) { value += scale * evaluate_cardstatus(fd,&assaults[index]); @@ -3270,6 +3271,7 @@ int evaluate_field(Field* fd) scale = is_alive(&fd->tip->commander); auto& eassaults(fd->tip->assaults); auto& estructures(fd->tip->structures); + value -= 0.5*scale * evaluate_cardstatus(fd,&fd->tip->commander); for(unsigned index(0); index < eassaults.size();++index) { value -= (scale * evaluate_cardstatus(fd,&eassaults[index])); diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 7219107c..3fc83416 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -174,8 +174,8 @@ void init() sim_seed=0; flexible_iter=20; flexible_turn=20; - eval_iter=10; - eval_turn=20; + eval_iter=8; + eval_turn=8; requirement.num_cards.clear(); #ifndef NQUEST //quest = new Quest(); //TODO Quest bugged in Android now here From cf0ec2a6d2581acd78935599e152a9ba3aa21265 Mon Sep 17 00:00:00 2001 From: APN-Pucky Date: Fri, 6 Mar 2020 23:57:49 +0100 Subject: [PATCH 3/8] RM debug int statement Travis complains about those if there is no static_cast. --- deck.cpp | 4 ++-- tyrant_optimize.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deck.cpp b/deck.cpp index 72967e1b..71577d61 100644 --- a/deck.cpp +++ b/deck.cpp @@ -735,13 +735,13 @@ const Card* Deck::next(Field* f) Results result(play(&fd,true,true,1)); if (result.wins == 0 && result.losses ==0 && strategy == DeckStrategy::evaluate_twice) { - _DEBUG_MSG(1,">>>>>>EVAL%i SIMS>>>>>>%lu\n",strategy,result.points); + _DEBUG_MSG(1,">>>>>>EVAL%i SIMS>>>>>>\n",strategy); if(f->players[0]->deck->strategy==DeckStrategy::evaluate_twice)hand1.deck->strategy = DeckStrategy::evaluate; else hand1.deck->strategy = DeckStrategy::random; if(f->players[1]->deck->strategy==DeckStrategy::evaluate_twice)hand2.deck->strategy = DeckStrategy::evaluate; else hand2.deck->strategy = DeckStrategy::random; result=(play(&fd,true,false,1)); - _DEBUG_MSG(1,"<<<<< run(int argc, char** argv) { opt_enemy_strategy = DeckStrategy::evaluate; } - else if (strcmp(argv[argIndex], "enemy:evaluater2") == 0) + else if (strcmp(argv[argIndex], "enemy:evaluate2") == 0) { opt_enemy_strategy = DeckStrategy::evaluate_twice; } From f7b7b9ecce753e89dba63ad6afa605959ffb875e Mon Sep 17 00:00:00 2001 From: APN-Pucky Date: Mon, 27 Apr 2020 18:37:47 +0200 Subject: [PATCH 4/8] Add Scaling/Modifier for SuperHeroism --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 64285ac8..97496094 100644 --- a/sim.cpp +++ b/sim.cpp @@ -2725,7 +2725,7 @@ bool check_and_perform_bravery(Field* fd, CardStatus* src) //BGE: superheroism if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::superheroism], false)) { - unsigned bge_value = bravery_value; + unsigned bge_value = bravery_value * fd->bg_effects[fd->tapi][PassiveBGE::superheroism]; const SkillSpec ss_heal{Skill::heal, bge_value, allfactions, 0, 0, Skill::no_skill, Skill::no_skill, true, 0,}; _DEBUG_MSG(1, "%s activates SuperHeroism: %s\n", status_description(src).c_str(), skill_description(fd->cards, ss_heal).c_str()); From 9dc138fa6a955f6976ef0b837e31461af378053b Mon Sep 17 00:00:00 2001 From: Evgeniy Kozlov Date: Thu, 7 May 2020 23:04:38 +0300 Subject: [PATCH 5/8] Add Fix: Legion under Mega(morphosis) * In the game, the Legion skill does not work properly under the Megamorphosis BGE, i. e. the BGE does not have any effect against Legion skill (Legion works like no BGE) --- sim.cpp | 2 +- tyrant.h | 1 + tyrant_optimize.cpp | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 97496094..b1d620d8 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1684,7 +1684,7 @@ struct PerformAttack if (__builtin_expect(legion_base, false)) { unsigned itr_idx, till_idx; - bool bge_megamorphosis = fd->bg_effects[fd->tapi][PassiveBGE::megamorphosis]; + bool bge_megamorphosis = fd->bg_effects[fd->tapi][PassiveBGE::megamorphosis] && !fd->fixes[Fix::legion_under_mega]; //scan all assaults for Global Legion itr_idx = 0; till_idx = att_assaults.size() - 1; diff --git a/tyrant.h b/tyrant.h index f2178fdb..0f213489 100644 --- a/tyrant.h +++ b/tyrant.h @@ -20,6 +20,7 @@ enum Fix enhance_early, revenge_on_death, death_from_bge, + legion_under_mega, num_fixes }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 97394b9e..a55d3fa6 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -217,6 +217,7 @@ void init() fixes[Fix::enhance_early] = true; fixes[Fix::revenge_on_death] = true; fixes[Fix::death_from_bge] = true; + fixes[Fix::legion_under_mega] = true; } #if defined(ANDROID) || defined(__ANDROID__) @@ -2130,6 +2131,10 @@ FinalResults run(int argc, char** argv) { fixes[Fix::death_from_bge] = true; } + else if (strcmp(argv[argIndex], "fix-legion-under-megamorphosis") == 0) + { + fixes[Fix::legion_under_mega] = true; + } else if (strcmp(argv[argIndex], "seed") == 0) { if(check_input_amount(argc,argv,argIndex,1))exit(1); From b406a228e8d3b1f46876423a9425fb00ea3121c9 Mon Sep 17 00:00:00 2001 From: Isilrond <42411053+Isilrond@users.noreply.github.com> Date: Sat, 13 Jun 2020 22:36:35 +0200 Subject: [PATCH 6/8] Update bges.txt Guildwar BGE updates --- data/bges.txt | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/data/bges.txt b/data/bges.txt index 78ce3a64..bea4997c 100644 --- a/data/bges.txt +++ b/data/bges.txt @@ -1,34 +1,34 @@ // Guild War BGEs Halcyon's Command: Enhance All Armor 8; Heal All Imperial 10; Enrage Imperial 4 Typhon's Blood: Enhance All Refresh 8; Enrage All Bloodthirsty 5; Heal 16 -Sandblast: Entrap All 10; Weaken All 10 -Plasma Burst: Heal 15; Protect 15; Sunder 15 -Landmine: Entrap 38 -Inspired: Rally all 7 -Blightblast: Enfeeble all 6 +Sandblast: Entrap all 50; Weaken All 50 +Plasma Burst: Heal all 60; Protect all 60; Sunder all 60 +Landmine: Entrap all 60 +Inspired: Rally all 60 +Blightblast: Enfeeble all 60 Bombing Run: Strike all 3 -Triage: Enhance Fortify 9; Heal all 18 -Charged Up: Overload 3 -Combined Arms: Enhance All Coalition 3; Rally+Enfeeble+Strike all 3 +Triage: Enhance Fortify 50; Heal all 50 +Charged Up: Overload all +Combined Arms: Enhance All Coalition 30; Rally all 30; Enfeeble all 30; Strike all 30 Blitzkrieg: Rush Paladin's Presence: Protect all 3 Viral Warfare: Enhance all Poison 3 -Progenitor Tech: Heal+Protect+Rally+Enfeeble+Strike+Weaken all 4 +Progenitor Tech: Heal all 40; Protect all 40; Rally all 40; Enfeeble all 40; Strike all 40; Weaken all 40 Diminisher: Sunder all 2 -Divine Blessing: Heal all 10; Rally all 7 +Divine Blessing: Heal all 50; Rally all 50 Opalescence: Heal+Protect all 2 Power Source: Overload 1 Ferocity: Enrage 6 -Tartarian Gift: Rally all 10; Enrage all 10 -Artillery: Enhance Mortar 4; Mortar all 18 +Tartarian Gift: Rally all 50; Enrage all 50 +Artillery: Enhance Mortar 40; Mortar all 50 //Orbital Cannon: Strike 16 Orbital Cannon: Enfeeble 14 Bloodthirst: Enhance All Leech 6 -Emergency Aid: Heal 48 +Emergency Aid: Heal All 60 Extended Reach: Enhance All Swipe 5 -Mirror Madness: Mimic 16 +Mirror Madness: Mimic 160 Eternal Backlash: Enhance All Counter 6; Entrap 6 -Winter Tempest: Enhance all subdue 10; Protect All 5; Weaken All 7 +Winter Tempest: Enhance all subdue 40; Protect All 40; Weaken All 40 Sulfuris Essence: Evolve All Poison Venom; Enhance All Venom 6 Way of the Forsaken: Enhance All Corrosive 8; Heal All Raider 7; Enfeeble 7 Critical Blast: Enhance All Swipe 4; Enfeeble All 4 From 91c629cba0660186474c45749f317b7e297e3aa8 Mon Sep 17 00:00:00 2001 From: Isilrond <42411053+Isilrond@users.noreply.github.com> Date: Sat, 25 Jul 2020 00:26:15 +0200 Subject: [PATCH 7/8] War BGE Update --- data/bges.txt | 57 ++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/data/bges.txt b/data/bges.txt index bea4997c..a82d3063 100644 --- a/data/bges.txt +++ b/data/bges.txt @@ -1,39 +1,44 @@ // Guild War BGEs +Artillery: Enhance Mortar 100; Mortar all 70 +Divine Blessing: Heal all 120; Rally all 120; Strike all 120 +Winter Tempest: Enhance all subdue 999; Protect All 100; Weaken All 100 +Charged Up: Overload all +Progenitor Tech: Heal all 40; Protect all 40; Rally all 40; Enfeeble all 40; Strike all 40; Weaken all 40 +Plasma Burst: Heal all 60; Protect all 60; Sunder 60 +Tartarian Gift: Rally all 120; Enrage all 120 +Combined Arms: Enhance All Coalition 70; Rally all 75; Enfeeble all 75; Strike all 75 +Inspired: Rally all 200 +Blightblast: Enfeeble all 300 +Emergency Aid: Heal All 70; Protect all 70; Enhance all Absorb 999 +Landmine: Entrap all 200; Weaken all 200 +Triage: Enhance all Fortify 250; Heal 170; Protect 170 +Mirror Madness: Mimic 700; Entrap all 100 +Surgical Strike: Mortar 150; Strike 200; Weaken 200 +Apocalypse: Mortar all 75; Enfeeble all 75; Strike all 75 +Minor Setback: Weaken 170; Strike 170; Mimic 170 +Entrapment: Enhance all Counter 400; Entrap 50; Heal 50 + +// Weird BGEs we will never ever see again +Sulfuris Essence: Evolve All Poison Venom; Enhance All Venom 6 +Way of the Forsaken: Enhance All Corrosive 8; Heal All Raider 7; Enfeeble 7 +Critical Blast: Enhance All Swipe 4; Enfeeble All 4 +Will of the Nephilim: Enhance All Avenge 5; Protect All Bloodthirsty 4; Heal All Righteous 6 +Armada's Conquest: Enhance All Evade 4; Rally All Xeno 6; Mortar 10 +Eternal Backlash: Enhance All Counter 6; Entrap 6 +//Orbital Cannon: Strike 16 +Orbital Cannon: Enfeeble 14 +Bloodthirst: Enhance All Leech 6 +Diminisher: Sunder all 2 Halcyon's Command: Enhance All Armor 8; Heal All Imperial 10; Enrage Imperial 4 Typhon's Blood: Enhance All Refresh 8; Enrage All Bloodthirsty 5; Heal 16 -Sandblast: Entrap all 50; Weaken All 50 -Plasma Burst: Heal all 60; Protect all 60; Sunder all 60 -Landmine: Entrap all 60 -Inspired: Rally all 60 -Blightblast: Enfeeble all 60 +Extended Reach: Enhance All Swipe 5 Bombing Run: Strike all 3 -Triage: Enhance Fortify 50; Heal all 50 -Charged Up: Overload all -Combined Arms: Enhance All Coalition 30; Rally all 30; Enfeeble all 30; Strike all 30 Blitzkrieg: Rush Paladin's Presence: Protect all 3 Viral Warfare: Enhance all Poison 3 -Progenitor Tech: Heal all 40; Protect all 40; Rally all 40; Enfeeble all 40; Strike all 40; Weaken all 40 -Diminisher: Sunder all 2 -Divine Blessing: Heal all 50; Rally all 50 Opalescence: Heal+Protect all 2 Power Source: Overload 1 Ferocity: Enrage 6 -Tartarian Gift: Rally all 50; Enrage all 50 -Artillery: Enhance Mortar 40; Mortar all 50 -//Orbital Cannon: Strike 16 -Orbital Cannon: Enfeeble 14 -Bloodthirst: Enhance All Leech 6 -Emergency Aid: Heal All 60 -Extended Reach: Enhance All Swipe 5 -Mirror Madness: Mimic 160 -Eternal Backlash: Enhance All Counter 6; Entrap 6 -Winter Tempest: Enhance all subdue 40; Protect All 40; Weaken All 40 -Sulfuris Essence: Evolve All Poison Venom; Enhance All Venom 6 -Way of the Forsaken: Enhance All Corrosive 8; Heal All Raider 7; Enfeeble 7 -Critical Blast: Enhance All Swipe 4; Enfeeble All 4 -Will of the Nephilim: Enhance All Avenge 5; Protect All Bloodthirsty 4; Heal All Righteous 6 -Armada's Conquest: Enhance All Evade 4; Rally All Xeno 6; Mortar 10 // Some other BGEs Soothing Chant: Heal all 2 From 6c897ff5ea03652d92f38a3648d7884764f4ad0d Mon Sep 17 00:00:00 2001 From: Isilrond <42411053+Isilrond@users.noreply.github.com> Date: Sat, 25 Jul 2020 21:33:31 +0200 Subject: [PATCH 8/8] Update bges.txt Removed all "Enhance" skills on war BGEs --- data/bges.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/data/bges.txt b/data/bges.txt index a82d3063..f36bc4df 100644 --- a/data/bges.txt +++ b/data/bges.txt @@ -1,22 +1,27 @@ // Guild War BGEs -Artillery: Enhance Mortar 100; Mortar all 70 +//Artillery: Enhance Mortar 100; Mortar all 70 +Artillery: Mortar all 70 Divine Blessing: Heal all 120; Rally all 120; Strike all 120 -Winter Tempest: Enhance all subdue 999; Protect All 100; Weaken All 100 +//Winter Tempest: Enhance all subdue 999; Protect All 100; Weaken All 100 +Winter Tempest: Protect All 100; Weaken All 100 Charged Up: Overload all Progenitor Tech: Heal all 40; Protect all 40; Rally all 40; Enfeeble all 40; Strike all 40; Weaken all 40 Plasma Burst: Heal all 60; Protect all 60; Sunder 60 Tartarian Gift: Rally all 120; Enrage all 120 -Combined Arms: Enhance All Coalition 70; Rally all 75; Enfeeble all 75; Strike all 75 +//Combined Arms: Enhance All Coalition 70; Rally all 75; Enfeeble all 75; Strike all 75 +Combined Arms: Rally all 75; Enfeeble all 75; Strike all 75 Inspired: Rally all 200 Blightblast: Enfeeble all 300 Emergency Aid: Heal All 70; Protect all 70; Enhance all Absorb 999 Landmine: Entrap all 200; Weaken all 200 -Triage: Enhance all Fortify 250; Heal 170; Protect 170 +//Triage: Enhance all Fortify 250; Heal 170; Protect 170 +Triage: Heal 170; Protect 170 Mirror Madness: Mimic 700; Entrap all 100 Surgical Strike: Mortar 150; Strike 200; Weaken 200 Apocalypse: Mortar all 75; Enfeeble all 75; Strike all 75 Minor Setback: Weaken 170; Strike 170; Mimic 170 -Entrapment: Enhance all Counter 400; Entrap 50; Heal 50 +//Entrapment: Enhance all Counter 400; Entrap 50; Heal 50 +Entrapment: Entrap 50; Heal 50 // Weird BGEs we will never ever see again Sulfuris Essence: Evolve All Poison Venom; Enhance All Venom 6