From 10edb7c9159caa8b4463aad775a9af0d16652905 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 15:16:25 -0500 Subject: [PATCH 001/406] Remove unneeded header files --- tyrant_optimize.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index f25bc189..5e859cdf 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -20,25 +20,16 @@ #include #include #include -#include -#include -#include -#include #include #include #include -#include #include #include -#include #include #include // because of 1.51 bug. missing include in range/any_range.hpp ? #include #include -#include -#include #include -#include #include #include #include From 27f2c6eda022654a2a380adbb9b099cfed0038ef Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 09:57:16 -0500 Subject: [PATCH 002/406] Add Makefile --- Makefile | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..56fafdc6 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +MAIN := tyrant_optimize +SRCS := $(wildcard *.cpp) +OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) + +CPPFLAGS := -Wall -std=gnu++11 -O3 +LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem + +all: $(MAIN) + +obj/%.o: %.cpp + $(CXX) $(CPPFLAGS) -o $@ -c $< + +$(MAIN): $(OBJS) + $(CXX) -o $@ $(LDFLAGS) $(OBJS) + +clean: + rm -f $(MAIN) obj/*.o From 0970e39cf72db2939ea4c907c80b169d82975e07 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 10:34:10 -0500 Subject: [PATCH 003/406] Move enums and typedefs into tyrant.{cpp,h} --- tyrant.cpp | 16 ++++++++++ tyrant.h | 77 +++++++++++++++++++++++++++++++++++++++++++++ tyrant_optimize.cpp | 77 +-------------------------------------------- 3 files changed, 94 insertions(+), 76 deletions(-) create mode 100644 tyrant.cpp create mode 100644 tyrant.h diff --git a/tyrant.cpp b/tyrant.cpp new file mode 100644 index 00000000..4055f1ca --- /dev/null +++ b/tyrant.cpp @@ -0,0 +1,16 @@ +#include "tyrant.h" + +#include + +const std::string faction_names[num_factions] = +{ "", "bloodthirsty", "imperial", "raider", "righteous", "xeno" }; + +std::string skill_names[num_skills] = +{"augment", "augment_all", "chaos", "chaos_all", "cleanse", "cleanse_all", "enfeeble", "enfeeble_all", + "freeze", "freeze_all", "heal", "heal_all", "infuse", "jam", "jam_all", + "mimic", "protect", "protect_all", "rally", "rally_all", "rush", "shock", + "siege", "siege_all", "strike", "strike_all", "summon", "supply", + "trigger_regen", + "weaken", "weaken_all"}; + +std::string cardtype_names[CardType::num_cardtypes]{"action", "assault", "commander", "structure"}; diff --git a/tyrant.h b/tyrant.h new file mode 100644 index 00000000..32360744 --- /dev/null +++ b/tyrant.h @@ -0,0 +1,77 @@ +#ifndef TYRANT_H_INCLUDED +#define TYRANT_H_INCLUDED + +#include +#include + +enum Faction +{ + allfactions, + bloodthirsty, + imperial, + raider, + righteous, + xeno, + num_factions +}; +extern const std::string faction_names[num_factions]; + +enum ActiveSkill +{augment, augment_all, chaos, chaos_all, cleanse, cleanse_all, enfeeble, enfeeble_all, + freeze, freeze_all, heal, heal_all, infuse, jam, jam_all, + mimic, protect, protect_all, rally, rally_all, rush, shock, + siege, siege_all, strike, strike_all, summon, supply, + trigger_regen, // not actually a skill; handles regeneration after strike/siege + weaken, weaken_all, num_skills}; +extern std::string skill_names[num_skills]; + +namespace CardType { +enum CardType { + action, + assault, + commander, + structure, + num_cardtypes +}; +} + +extern std::string cardtype_names[CardType::num_cardtypes]; + +enum gamemode_t +{ + fight, + surge, + tournament +}; + +struct true_ {}; + +struct false_ {}; + +template +struct skillTriggersRegen { typedef false_ T; }; + +template<> +struct skillTriggersRegen { typedef true_ T; }; + +template<> +struct skillTriggersRegen { typedef true_ T; }; + +template<> +struct skillTriggersRegen { typedef true_ T; }; + +template<> +struct skillTriggersRegen { typedef true_ T; }; + +enum SkillSourceType +{ + source_hostile, + source_allied, + source_global_hostile, + source_global_allied, + source_chaos +}; + +typedef std::tuple SkillSpec; + +#endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 5e859cdf..24002162 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -41,6 +41,7 @@ #include #include #include "rapidxml.hpp" +#include "tyrant.h" //#include "timer.hpp" using namespace rapidxml; @@ -172,82 +173,6 @@ class Storage boost::pool<> m_pool; }; //--------------------- $10 data model: card properties, etc ------------------- -enum Faction -{ - allfactions, - bloodthirsty, - imperial, - raider, - righteous, - xeno, - num_factions -}; -const std::string faction_names[num_factions] = -{ "", "bloodthirsty", "imperial", "raider", "righteous", "xeno" }; - -enum ActiveSkill -{augment, augment_all, chaos, chaos_all, cleanse, cleanse_all, enfeeble, enfeeble_all, - freeze, freeze_all, heal, heal_all, infuse, jam, jam_all, - mimic, protect, protect_all, rally, rally_all, rush, shock, - siege, siege_all, strike, strike_all, summon, supply, - trigger_regen, // not actually a skill; handles regeneration after strike/siege - weaken, weaken_all, num_skills}; -std::string skill_names[num_skills] = -{"augment", "augment_all", "chaos", "chaos_all", "cleanse", "cleanse_all", "enfeeble", "enfeeble_all", - "freeze", "freeze_all", "heal", "heal_all", "infuse", "jam", "jam_all", - "mimic", "protect", "protect_all", "rally", "rally_all", "rush", "shock", - "siege", "siege_all", "strike", "strike_all", "summon", "supply", - "trigger_regen", - "weaken", "weaken_all"}; - -namespace CardType { -enum CardType { - action, - assault, - commander, - structure, - num_cardtypes -}; -} -std::string cardtype_names[CardType::num_cardtypes]{"action", "assault", "commander", "structure"}; - -enum gamemode_t -{ - fight, - surge, - tournament -}; - -struct true_ {}; - -struct false_ {}; - -template -struct skillTriggersRegen { typedef false_ T; }; - -template<> -struct skillTriggersRegen { typedef true_ T; }; - -template<> -struct skillTriggersRegen { typedef true_ T; }; - -template<> -struct skillTriggersRegen { typedef true_ T; }; - -template<> -struct skillTriggersRegen { typedef true_ T; }; - -enum SkillSourceType -{ - source_hostile, - source_allied, - source_global_hostile, - source_global_allied, - source_chaos -}; - -typedef std::tuple SkillSpec; - class Card { public: From 011d01e708abf4956cb9602923a767d6f72dbe69 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 10:52:43 -0500 Subject: [PATCH 004/406] Move Card into card.h --- card.h | 111 ++++++++++++++++++++++++++++++++++++++++++++ tyrant_optimize.cpp | 104 +---------------------------------------- 2 files changed, 112 insertions(+), 103 deletions(-) create mode 100644 card.h diff --git a/card.h b/card.h new file mode 100644 index 00000000..d9cf9597 --- /dev/null +++ b/card.h @@ -0,0 +1,111 @@ +#ifndef CARD_H_INCLUDED +#define CARD_H_INCLUDED + +#include +#include +#include "tyrant.h" + +class Card +{ +public: + Card() : + m_antiair(0), + m_armored(0), + m_attack(0), + m_berserk(0), + m_berserk_oa(0), + m_blitz(false), + m_burst(0), + m_counter(0), + m_crush(0), + m_delay(0), + m_disease(false), + m_disease_oa(false), + m_evade(false), + m_faction(imperial), + m_fear(false), + m_flurry(0), + m_flying(false), + m_health(0), + m_id(0), + m_immobilize(false), + m_intercept(false), + m_leech(0), + m_name(""), + m_payback(false), + m_pierce(0), + m_poison(0), + m_poison_oa(0), + m_rarity(1), + m_recharge(false), + m_refresh(false), + m_regenerate(0), + m_set(0), + m_siphon(0), + m_split(false), + m_swipe(false), + m_tribute(false), + m_unique(false), + m_valor(0), + m_wall(false), + m_type(CardType::assault), + m_skills() + { + } + + void add_skill(ActiveSkill v1, unsigned v2, Faction v3) + { m_skills.push_back(std::make_tuple(v1, v2, v3)); } + void add_played_skill(ActiveSkill v1, unsigned v2, Faction v3) + { m_skills_played.push_back(std::make_tuple(v1, v2, v3)); } + void add_died_skill(ActiveSkill v1, unsigned v2, Faction v3) + { m_skills_died.push_back(std::make_tuple(v1, v2, v3)); } + void add_attacked_skill(ActiveSkill v1, unsigned v2, Faction v3) + { m_skills_attacked.push_back(std::make_tuple(v1, v2, v3)); } + + unsigned m_antiair; + unsigned m_armored; + unsigned m_attack; + unsigned m_berserk; + unsigned m_berserk_oa; + bool m_blitz; + unsigned m_burst; + unsigned m_counter; + unsigned m_crush; + unsigned m_delay; + bool m_disease; + bool m_disease_oa; + bool m_evade; + Faction m_faction; + bool m_fear; + unsigned m_flurry; + bool m_flying; + unsigned m_health; + unsigned m_id; + bool m_immobilize; + bool m_intercept; + unsigned m_leech; + std::string m_name; + bool m_payback; + unsigned m_pierce; + unsigned m_poison; + unsigned m_poison_oa; + unsigned m_rarity; + bool m_recharge; + bool m_refresh; + unsigned m_regenerate; + unsigned m_set; + unsigned m_siphon; + bool m_split; + bool m_swipe; + bool m_tribute; + bool m_unique; + unsigned m_valor; + bool m_wall; + std::vector m_skills; + std::vector m_skills_played; + std::vector m_skills_died; + std::vector m_skills_attacked; + CardType::CardType m_type; +}; + +#endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 24002162..69fd6a7b 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -41,6 +41,7 @@ #include #include #include "rapidxml.hpp" +#include "card.h" #include "tyrant.h" //#include "timer.hpp" @@ -173,109 +174,6 @@ class Storage boost::pool<> m_pool; }; //--------------------- $10 data model: card properties, etc ------------------- -class Card -{ -public: - Card() : - m_antiair(0), - m_armored(0), - m_attack(0), - m_berserk(0), - m_berserk_oa(0), - m_blitz(false), - m_burst(0), - m_counter(0), - m_crush(0), - m_delay(0), - m_disease(false), - m_disease_oa(false), - m_evade(false), - m_faction(imperial), - m_fear(false), - m_flurry(0), - m_flying(false), - m_health(0), - m_id(0), - m_immobilize(false), - m_intercept(false), - m_leech(0), - m_name(""), - m_payback(false), - m_pierce(0), - m_poison(0), - m_poison_oa(0), - m_rarity(1), - m_recharge(false), - m_refresh(false), - m_regenerate(0), - m_set(0), - m_siphon(0), - m_split(false), - m_swipe(false), - m_tribute(false), - m_unique(false), - m_valor(0), - m_wall(false), - m_type(CardType::assault), - m_skills() - { - } - - void add_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills.push_back(std::make_tuple(v1, v2, v3)); } - void add_played_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills_played.push_back(std::make_tuple(v1, v2, v3)); } - void add_died_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills_died.push_back(std::make_tuple(v1, v2, v3)); } - void add_attacked_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills_attacked.push_back(std::make_tuple(v1, v2, v3)); } - - unsigned m_antiair; - unsigned m_armored; - unsigned m_attack; - unsigned m_berserk; - unsigned m_berserk_oa; - bool m_blitz; - unsigned m_burst; - unsigned m_counter; - unsigned m_crush; - unsigned m_delay; - bool m_disease; - bool m_disease_oa; - bool m_evade; - Faction m_faction; - bool m_fear; - unsigned m_flurry; - bool m_flying; - unsigned m_health; - unsigned m_id; - bool m_immobilize; - bool m_intercept; - unsigned m_leech; - std::string m_name; - bool m_payback; - unsigned m_pierce; - unsigned m_poison; - unsigned m_poison_oa; - unsigned m_rarity; - bool m_recharge; - bool m_refresh; - unsigned m_regenerate; - unsigned m_set; - unsigned m_siphon; - bool m_split; - bool m_swipe; - bool m_tribute; - bool m_unique; - unsigned m_valor; - bool m_wall; - std::vector m_skills; - std::vector m_skills_played; - std::vector m_skills_died; - std::vector m_skills_attacked; - CardType::CardType m_type; -}; - struct Cards { ~Cards() From c705f16aae24ce7252b6d861eeba7b3dddb210b7 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 11:15:39 -0500 Subject: [PATCH 005/406] Split Cards into cards.{cpp,h} --- cards.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++ cards.h | 30 +++++++++++++++++ tyrant_optimize.cpp | 80 +-------------------------------------------- 3 files changed, 106 insertions(+), 79 deletions(-) create mode 100644 cards.cpp create mode 100644 cards.h diff --git a/cards.cpp b/cards.cpp new file mode 100644 index 00000000..850c758c --- /dev/null +++ b/cards.cpp @@ -0,0 +1,75 @@ +#include "cards.h" + +#include +#include +#include + +#include "tyrant.h" + +template +std::string to_string(T val) +{ + std::stringstream s; + s << val; + return s.str(); +} + +const Card* Cards::by_id(unsigned id) const +{ + std::map::const_iterator cardIter{cards_by_id.find(id)}; + if(cardIter == cards_by_id.end()) + { + throw std::runtime_error("While trying to find the card with id " + to_string(id) + ": no such key in the cards_by_id map."); + } + else + { + return(cardIter->second); + } +} +//------------------------------------------------------------------------------ +void Cards::organize() +{ + cards_by_id.clear(); + player_cards.clear(); + player_cards_by_name.clear(); + player_commanders.clear(); + player_assaults.clear(); + player_structures.clear(); + player_actions.clear(); + for(Card* card: cards) + { + cards_by_id[card->m_id] = card; + // Card available to players + if(card->m_set != -1) + { + player_cards.push_back(card); + switch(card->m_type) + { + case CardType::commander: { + player_commanders.push_back(card); + break; + } + case CardType::assault: { + player_assaults.push_back(card); + break; + } + case CardType::structure: { + player_structures.push_back(card); + break; + } + case CardType::action: { + player_actions.push_back(card); + break; + } + } + if(player_cards_by_name.find(card->m_name) != player_cards_by_name.end()) + { + throw std::runtime_error("While trying to insert the card [" + card->m_name + ", id " + to_string(card->m_id) + "] in the player_cards_by_name map: the key already exists [id " + to_string(player_cards_by_name[card->m_name]->m_id) + "]."); + } + else + { + player_cards_by_name[card->m_name] = card; + } + } + } +} diff --git a/cards.h b/cards.h new file mode 100644 index 00000000..8b5749e4 --- /dev/null +++ b/cards.h @@ -0,0 +1,30 @@ +#ifndef CARDS_H_INCLUDED +#define CARDS_H_INCLUDED + +#include +#include +#include + +#include "card.h" + +struct Cards +{ + ~Cards() + { + for(Card* c: cards) { delete(c); } + } + + std::vector cards; + std::map cards_by_id; + std::vector player_cards; + std::map player_cards_by_name; + std::vector player_commanders; + std::vector player_assaults; + std::vector player_structures; + std::vector player_actions; + std::map replace; + const Card * by_id(unsigned id) const; + void organize(); +}; + +#endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 69fd6a7b..bf7672ed 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -42,6 +42,7 @@ #include #include "rapidxml.hpp" #include "card.h" +#include "cards.h" #include "tyrant.h" //#include "timer.hpp" @@ -174,25 +175,6 @@ class Storage boost::pool<> m_pool; }; //--------------------- $10 data model: card properties, etc ------------------- -struct Cards -{ - ~Cards() - { - for(Card* c: cards) { delete(c); } - } - - std::vector cards; - std::map cards_by_id; - std::vector player_cards; - std::map player_cards_by_name; - std::vector player_commanders; - std::vector player_assaults; - std::vector player_structures; - std::vector player_actions; - std::map replace; - const Card * by_id(unsigned id) const; - void organize(); -}; Cards globalCards; //------------------------------------------------------------------------------ struct CardStatus @@ -396,66 +378,6 @@ void handle_skill(xml_node<>* node, Card* card) } } //------------------------------------------------------------------------------ -const Card* Cards::by_id(unsigned id) const -{ - std::map::const_iterator cardIter{cards_by_id.find(id)}; - if(cardIter == cards_by_id.end()) - { - throw std::runtime_error("While trying to find the card with id " + to_string(id) + ": no such key in the cards_by_id map."); - } - else - { - return(cardIter->second); - } -} -//------------------------------------------------------------------------------ -void Cards::organize() -{ - cards_by_id.clear(); - player_cards.clear(); - player_cards_by_name.clear(); - player_commanders.clear(); - player_assaults.clear(); - player_structures.clear(); - player_actions.clear(); - for(Card* card: cards) - { - cards_by_id[card->m_id] = card; - // Card available to players - if(card->m_set != -1) - { - player_cards.push_back(card); - switch(card->m_type) - { - case CardType::commander: { - player_commanders.push_back(card); - break; - } - case CardType::assault: { - player_assaults.push_back(card); - break; - } - case CardType::structure: { - player_structures.push_back(card); - break; - } - case CardType::action: { - player_actions.push_back(card); - break; - } - } - if(player_cards_by_name.find(card->m_name) != player_cards_by_name.end()) - { - throw std::runtime_error("While trying to insert the card [" + card->m_name + ", id " + to_string(card->m_id) + "] in the player_cards_by_name map: the key already exists [id " + to_string(player_cards_by_name[card->m_name]->m_id) + "]."); - } - else - { - player_cards_by_name[card->m_name] = card; - } - } - } -} -//------------------------------------------------------------------------------ void parse_file(const char* filename, std::vector& buffer, xml_document<>& doc) { std::ifstream cards_stream(filename, std::ios::binary); From 5406fc3f444f1044efdc50f40abed2df7bf63228 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 11:50:38 -0500 Subject: [PATCH 006/406] Move deck-related code into deck.h There is much inline code that we could consider moving into a deck.cpp, but this can come later. --- deck.h | 309 ++++++++++++++++++++++++++++++++++++++++++++ tyrant_optimize.cpp | 297 +----------------------------------------- 2 files changed, 310 insertions(+), 296 deletions(-) create mode 100644 deck.h diff --git a/deck.h b/deck.h new file mode 100644 index 00000000..ef4a04a4 --- /dev/null +++ b/deck.h @@ -0,0 +1,309 @@ +#ifndef DECK_H_INCLUDED +#define DECK_H_INCLUDED + +#include // because of 1.51 bug. missing include in range/any_range.hpp ? +#include +#include +#include +#include +#include + +#include "cards.h" + +class Card; + +template +void partial_shuffle(RandomAccessIterator first, RandomAccessIterator middle, + 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; + + 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))]); + } +} + +namespace boost +{ +namespace range +{ +template +void shuffle(Range& range, UniformRandomNumberGenerator&& rand) +{ + std::shuffle(boost::begin(range), boost::end(range), rand); +} +} // namespace range +using range::shuffle; +} // namespace boost + +namespace range = boost::range; +//---------------------- $30 Deck: a commander + a sequence of cards ----------- +// Can be shuffled. +// Implementations: random player and raid decks, ordered player decks. +//------------------------------------------------------------------------------ +struct DeckIface +{ + const Card* commander; + std::vector cards; + + DeckIface() : + commander{nullptr} + {} + + DeckIface(const Card* commander_, + boost::any_range cards_) : + commander(commander_), + cards(std::begin(cards_), std::end(cards_)) + {} + ; + virtual ~DeckIface() {}; + virtual DeckIface* clone() const = 0; + virtual const Card* get_commander() = 0; + virtual const Card* next() = 0; + virtual void shuffle(std::mt19937& re) = 0; + // Special case for recharge (behemoth raid's ability). + virtual void place_at_bottom(const Card*) = 0; +}; +//------------------------------------------------------------------------------ +struct DeckRandom : DeckIface +{ + std::vector > > raid_cards; + std::deque shuffled_cards; + + DeckRandom( + const Card* commander_, + const std::vector& cards_, + std::vector > > raid_cards_ = + std::vector > >()) : + DeckIface(commander_, cards_), + raid_cards(raid_cards_) + { + } + + DeckRandom(const DeckIface& other) : + DeckIface(other) + { + } + + DeckRandom(const Cards& all_cards, const std::vector& names) + { + for(auto name: names) + { + auto card_it(all_cards.player_cards_by_name.find(name)); + if(card_it == all_cards.player_cards_by_name.end()) + { + throw std::runtime_error("While constructing a deck: the card " + name + " was not found."); + } + else + { + const Card* card{card_it->second}; + if(card->m_type == CardType::commander) + { + if(commander == nullptr) + { + commander = card; + } + else + { + throw std::runtime_error("While constructing a deck: two commanders detected (" + name + " and " + commander->m_name + ")"); + } + } + else + { + cards.emplace_back(card); + } + } + } + if(commander == nullptr) + { + throw std::runtime_error("While constructing a deck: no commander found"); + } + } + + DeckRandom(const Cards& all_cards, const std::vector& ids) + { + for(auto id: ids) + { + const Card* card{all_cards.by_id(id)}; + if(card->m_type == CardType::commander) + { + if(commander == nullptr) + { + commander = card; + } + else + { + throw std::runtime_error("While constructing a deck: two commanders detected (" + card->m_name + " and " + commander->m_name + ")"); + } + } + else + { + cards.emplace_back(card); + } + } + if(commander == nullptr) + { + throw std::runtime_error("While constructing a deck: no commander found"); + } + } + + ~DeckRandom() {} + + virtual DeckIface* clone() const + { + return(new DeckRandom(*this)); + } + + const Card* get_commander() + { + return(commander); + } + + const Card* next() + { + if(!shuffled_cards.empty()) + { + const Card* card = shuffled_cards.front(); + shuffled_cards.pop_front(); + return(card); + } + else + { + return(nullptr); + } + } + + void shuffle(std::mt19937& re) + { + shuffled_cards.clear(); + boost::insert(shuffled_cards, shuffled_cards.end(), cards); + for(auto& card_pool: raid_cards) + { + assert(card_pool.first <= card_pool.second.size()); + partial_shuffle(card_pool.second.begin(), card_pool.second.begin() + card_pool.first, card_pool.second.end(), re); + shuffled_cards.insert(shuffled_cards.end(), card_pool.second.begin(), card_pool.second.begin() + card_pool.first); + } + boost::shuffle(shuffled_cards, re); + } + + void place_at_bottom(const Card* card) + { + shuffled_cards.push_back(card); + } +}; +//------------------------------------------------------------------------------ +// No support for ordered raid decks +struct DeckOrdered : DeckIface +{ + std::deque shuffled_cards; + // card id -> card order + std::map > order; + + DeckOrdered(const Card* commander_, boost::any_range cards_) : + DeckIface(commander_, cards_), + shuffled_cards(cards.begin(), cards.end()) + { + } + + DeckOrdered(const DeckIface& other) : + DeckIface(other) + { + } + + ~DeckOrdered() {} + + virtual DeckOrdered* clone() const + { + return(new DeckOrdered(*this)); + } + + const Card* get_commander() { return(commander); } + + const Card* next() + { + if(shuffled_cards.empty()) + { + return(nullptr); + } + else + { + 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()) + { + auto card2_order = order.find(card2->m_id); + if(!card1_order->second.empty()) + { + return(*card1_order->second.begin() < *card2_order->second.begin()); + } + else + { + return(true); + } + } + else + { + return(false); + } + }); + 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); + } + } + + void shuffle(std::mt19937& re) + { + unsigned i = 0; + order.clear(); + for(auto card: cards) + { + order[card->m_id].push_back(i); + ++i; + } + shuffled_cards.clear(); + range::insert(shuffled_cards, shuffled_cards.end(), cards); + std::shuffle(shuffled_cards.begin(), shuffled_cards.end(), re); + } + + void place_at_bottom(const Card* card) + { + shuffled_cards.push_back(card); + } +}; + +// + also the custom decks +struct Decks +{ + std::map custom_decks; + std::list mission_decks; + std::map mission_decks_by_id; + std::map mission_decks_by_name; + std::list raid_decks; + std::map raid_decks_by_id; + std::map raid_decks_by_name; + + ~Decks() + { + for(auto& obj: custom_decks) + { + delete(obj.second); + } + } +}; + +#endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index bf7672ed..42de77fc 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -26,9 +26,6 @@ #include #include #include -#include // because of 1.51 bug. missing include in range/any_range.hpp ? -#include -#include #include #include #include @@ -43,12 +40,12 @@ #include "rapidxml.hpp" #include "card.h" #include "cards.h" +#include "deck.h" #include "tyrant.h" //#include "timer.hpp" using namespace rapidxml; using namespace std::placeholders; -namespace range = boost::range; //---------------------- $00 general stuff ------------------------------------- template std::string to_string(T val) @@ -57,38 +54,6 @@ std::string to_string(T val) s << val; return s.str(); } - -template -void partial_shuffle(RandomAccessIterator first, RandomAccessIterator middle, - 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; - - 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))]); - } -} - -namespace boost -{ -namespace range -{ -template -void shuffle(Range& range, UniformRandomNumberGenerator&& rand) -{ - std::shuffle(boost::begin(range), boost::end(range), rand); -} -} // namespace range -using range::shuffle; -} // namespace boost //---------------------- Debugging stuff --------------------------------------- bool debug_print(false); bool debug_line(false); @@ -578,161 +543,7 @@ void read_cards(Cards& cards) // } // std::cout << "nb mission cards: " << cards.mission_cards.size() << "\n"; } -//---------------------- $30 Deck: a commander + a sequence of cards ----------- -// Can be shuffled. -// Implementations: random player and raid decks, ordered player decks. //------------------------------------------------------------------------------ -struct DeckIface -{ - const Card* commander; - std::vector cards; - - DeckIface() : - commander{nullptr} - {} - - DeckIface(const Card* commander_, - boost::any_range cards_) : - commander(commander_), - cards(std::begin(cards_), std::end(cards_)) - {} - ; - virtual ~DeckIface() {}; - virtual DeckIface* clone() const = 0; - virtual const Card* get_commander() = 0; - virtual const Card* next() = 0; - virtual void shuffle(std::mt19937& re) = 0; - // Special case for recharge (behemoth raid's ability). - virtual void place_at_bottom(const Card*) = 0; -}; -//------------------------------------------------------------------------------ -struct DeckRandom : DeckIface -{ - std::vector > > raid_cards; - std::deque shuffled_cards; - - DeckRandom( - const Card* commander_, - const std::vector& cards_, - std::vector > > raid_cards_ = - std::vector > >()) : - DeckIface(commander_, cards_), - raid_cards(raid_cards_) - { - } - - DeckRandom(const DeckIface& other) : - DeckIface(other) - { - } - - DeckRandom(const Cards& all_cards, const std::vector& names) - { - for(auto name: names) - { - auto card_it(all_cards.player_cards_by_name.find(name)); - if(card_it == all_cards.player_cards_by_name.end()) - { - throw std::runtime_error("While constructing a deck: the card " + name + " was not found."); - } - else - { - const Card* card{card_it->second}; - if(card->m_type == CardType::commander) - { - if(commander == nullptr) - { - commander = card; - } - else - { - throw std::runtime_error("While constructing a deck: two commanders detected (" + name + " and " + commander->m_name + ")"); - } - } - else - { - cards.emplace_back(card); - } - } - } - if(commander == nullptr) - { - throw std::runtime_error("While constructing a deck: no commander found"); - } - } - - DeckRandom(const Cards& all_cards, const std::vector& ids) - { - for(auto id: ids) - { - const Card* card{all_cards.by_id(id)}; - if(card->m_type == CardType::commander) - { - if(commander == nullptr) - { - commander = card; - } - else - { - throw std::runtime_error("While constructing a deck: two commanders detected (" + card->m_name + " and " + commander->m_name + ")"); - } - } - else - { - cards.emplace_back(card); - } - } - if(commander == nullptr) - { - throw std::runtime_error("While constructing a deck: no commander found"); - } - } - - ~DeckRandom() {} - - virtual DeckIface* clone() const - { - return(new DeckRandom(*this)); - } - - const Card* get_commander() - { - return(commander); - } - - const Card* next() - { - if(!shuffled_cards.empty()) - { - const Card* card = shuffled_cards.front(); - shuffled_cards.pop_front(); - return(card); - } - else - { - return(nullptr); - } - } - - void shuffle(std::mt19937& re) - { - shuffled_cards.clear(); - boost::insert(shuffled_cards, shuffled_cards.end(), cards); - for(auto& card_pool: raid_cards) - { - assert(card_pool.first <= card_pool.second.size()); - partial_shuffle(card_pool.second.begin(), card_pool.second.begin() + card_pool.first, card_pool.second.end(), re); - shuffled_cards.insert(shuffled_cards.end(), card_pool.second.begin(), card_pool.second.begin() + card_pool.first); - } - boost::shuffle(shuffled_cards, re); - } - - void place_at_bottom(const Card* card) - { - shuffled_cards.push_back(card); - } -}; - void print_deck(DeckIface& deck) { std::cout << "Deck:" << std::endl; @@ -750,92 +561,6 @@ void print_deck(DeckIface& deck) } } //------------------------------------------------------------------------------ -// No support for ordered raid decks -struct DeckOrdered : DeckIface -{ - std::deque shuffled_cards; - // card id -> card order - std::map > order; - - DeckOrdered(const Card* commander_, boost::any_range cards_) : - DeckIface(commander_, cards_), - shuffled_cards(cards.begin(), cards.end()) - { - } - - DeckOrdered(const DeckIface& other) : - DeckIface(other) - { - } - - ~DeckOrdered() {} - - virtual DeckOrdered* clone() const - { - return(new DeckOrdered(*this)); - } - - const Card* get_commander() { return(commander); } - - const Card* next() - { - if(shuffled_cards.empty()) - { - return(nullptr); - } - else - { - 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()) - { - auto card2_order = order.find(card2->m_id); - if(!card1_order->second.empty()) - { - return(*card1_order->second.begin() < *card2_order->second.begin()); - } - else - { - return(true); - } - } - else - { - return(false); - } - }); - 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); - } - } - - void shuffle(std::mt19937& re) - { - unsigned i = 0; - order.clear(); - for(auto card: cards) - { - order[card->m_id].push_back(i); - ++i; - } - shuffled_cards.clear(); - range::insert(shuffled_cards, shuffled_cards.end(), cards); - std::shuffle(shuffled_cards.begin(), shuffled_cards.end(), re); - } - - void place_at_bottom(const Card* card) - { - shuffled_cards.push_back(card); - } -}; -//------------------------------------------------------------------------------ // Represents a particular draw from a deck. // Persistent object: call reset to get a new draw. class Hand @@ -2382,26 +2107,6 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) } //---------------------- $70 More xml parsing: missions and raids -------------- -// + also the custom decks -struct Decks -{ - std::map custom_decks; - std::list mission_decks; - std::map mission_decks_by_id; - std::map mission_decks_by_name; - std::list raid_decks; - std::map raid_decks_by_id; - std::map raid_decks_by_name; - - ~Decks() - { - for(auto& obj: custom_decks) - { - delete(obj.second); - } - } -}; - template Iterator advance_until(Iterator it, Iterator it_end, Functor f) { while(it != it_end) From f7db48937206f31e4d6f5f0429677962e6b0e561 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 12:41:17 -0500 Subject: [PATCH 007/406] Move xml-related functions into xml.{cpp,h} --- tyrant_optimize.cpp | 410 +------------------------------------------ xml.cpp | 419 ++++++++++++++++++++++++++++++++++++++++++++ xml.h | 13 ++ 3 files changed, 433 insertions(+), 409 deletions(-) create mode 100644 xml.cpp create mode 100644 xml.h diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 42de77fc..e3813fa2 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -42,9 +42,9 @@ #include "cards.h" #include "deck.h" #include "tyrant.h" +#include "xml.h" //#include "timer.hpp" -using namespace rapidxml; using namespace std::placeholders; //---------------------- $00 general stuff ------------------------------------- template @@ -244,305 +244,6 @@ std::string status_description(CardStatus* status) desc += "[" + status->m_card->m_name + "]"; return(desc); } -//---------------------- $20 cards.xml parsing --------------------------------- -// Sets: 1 enclave; 2 nexus; 3 blight; 4 purity; 5 homeworld; -// 6 phobos; 7 phobos aftermath; 8 awakening -// 1000 standard; 5000 rewards; 5001 promotional; 9000 exclusive -// mission only and test cards have no set -using namespace rapidxml; - -std::map sets_counts; - -Faction map_to_faction(unsigned i) -{ - return(i == 1 ? imperial : - i == 3 ? bloodthirsty : - i == 4 ? xeno : - i == 8 ? righteous : - i == 9 ? raider : - allfactions); -} - -Faction skill_faction(xml_node<>* skill) -{ - unsigned unmapped_faction(0); - xml_attribute<>* y(skill->first_attribute("y")); - if(y) - { - unmapped_faction = atoi(y->value()); - } - return(unmapped_faction == 0 ? allfactions : map_to_faction(unmapped_faction)); -} - -unsigned skill_value(xml_node<>* skill) -{ - unsigned value(0); - xml_attribute<>* x(skill->first_attribute("x")); - if(x) - { - value = atoi(x->value()); - } - return(value); -} - -template -struct GlobalSkill -{ - enum { type = 99 }; -}; - -template<> struct GlobalSkill { enum {type = augment_all}; }; -template<> struct GlobalSkill { enum {type = chaos_all}; }; -template<> struct GlobalSkill { enum {type = cleanse_all}; }; -template<> struct GlobalSkill { enum {type = enfeeble_all}; }; -template<> struct GlobalSkill { enum {type = freeze_all}; }; -template<> struct GlobalSkill { enum {type = heal_all}; }; -template<> struct GlobalSkill { enum {type = jam_all}; }; -template<> struct GlobalSkill { enum {type = protect_all}; }; -template<> struct GlobalSkill { enum {type = rally_all}; }; -template<> struct GlobalSkill { enum {type = siege_all}; }; -template<> struct GlobalSkill { enum {type = strike_all}; }; -template<> struct GlobalSkill { enum {type = weaken_all}; }; - -template -bool handle_global_skill(xml_node<>* node, Card* card) -{ - bool played(node->first_attribute("played")); - bool died(node->first_attribute("died")); - bool attacked(node->first_attribute("attacked")); - if(node->first_attribute("all")) - { - if(played) {card->add_played_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } - else if(died) {card->add_died_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } - else if(attacked) {card->add_attacked_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } - else {card->add_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } - return(true); - } - return(false); -} - -template<> -bool handle_global_skill<99>(xml_node<>* node, Card* card) -{ - return(false); -} - -template -void handle_skill(xml_node<>* node, Card* card) -{ - bool played(node->first_attribute("played")); - bool died(node->first_attribute("died")); - bool attacked(node->first_attribute("attacked")); - if(handle_global_skill::type>(node, card)) {} - else - { - if(played) {card->add_played_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } - else if(died) {card->add_died_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } - else if(attacked) {card->add_attacked_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } - else {card->add_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } - } -} -//------------------------------------------------------------------------------ -void parse_file(const char* filename, std::vector& buffer, xml_document<>& doc) -{ - std::ifstream cards_stream(filename, std::ios::binary); - // Get the size of the file - cards_stream.seekg(0,std::ios::end); - std::streampos length = cards_stream.tellg(); - cards_stream.seekg(0,std::ios::beg); - buffer.resize(length + std::streampos(1)); - cards_stream.read(&buffer[0],length); - // zero-terminate - buffer[length] = '\0'; - try - { - doc.parse<0>(&buffer[0]); - } - catch(rapidxml::parse_error& e) - { - std::cout << "Parse error exception.\n"; - std::cout << e.what(); - throw(e); - } -} -//------------------------------------------------------------------------------ -void read_cards(Cards& cards) -{ - std::vector buffer; - xml_document<> doc; - parse_file("cards.xml", buffer, doc); - xml_node<>* root = doc.first_node(); - bool mission_only(false); - unsigned nb_cards(0); - for(xml_node<>* card = root->first_node(); - card; - card = card->next_sibling()) - { - if(strcmp(card->name(), "unit") == 0) - { - xml_node<>* id_node(card->first_node("id")); - int id(id_node ? atoi(id_node->value()) : -1); - // Replacement art card - xml_node<>* replace_node(card->first_node("replace")); - if(replace_node) - { - cards.replace[id] = atoi(replace_node->value()); - continue; - } - xml_node<>* name_node(card->first_node("name")); - xml_node<>* attack_node(card->first_node("attack")); - xml_node<>* health_node(card->first_node("health")); - xml_node<>* cost_node(card->first_node("cost")); - xml_node<>* unique_node(card->first_node("unique")); - xml_node<>* rarity_node(card->first_node("rarity")); - xml_node<>* type_node(card->first_node("type")); - xml_node<>* set_node(card->first_node("set")); - int set(set_node ? atoi(set_node->value()) : -1); - mission_only = set == -1; - if((mission_only || set >= 0) && name_node && rarity_node) - { - if(!mission_only) - { - nb_cards++; - sets_counts[set]++; - } - Card* c(new Card()); - c->m_id = id; - c->m_name = name_node->value(); - if(id < 1000) - { c->m_type = CardType::assault; } - else if(id < 2000) - { c->m_type = CardType::commander; } - else if(id < 3000) - { c->m_type = CardType::structure; } - else - { c->m_type = CardType::action; } - if(attack_node) { c->m_attack = atoi(attack_node->value()); } - if(health_node) { c->m_health = atoi(health_node->value()); } - if(cost_node) { c->m_delay = atoi(cost_node->value()); } - if(unique_node) { c->m_unique = true; } - c->m_rarity = atoi(rarity_node->value()); - unsigned type(type_node ? atoi(type_node->value()) : 0); - c->m_faction = map_to_faction(type); - c->m_set = set; - for(xml_node<>* skill = card->first_node("skill"); skill; - skill = skill->next_sibling("skill")) - { - if(strcmp(skill->first_attribute("id")->value(), "antiair") == 0) - { c->m_antiair = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "armored") == 0) - { c->m_armored = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "augment") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "berserk") == 0) - { - bool attacked(skill->first_attribute("attacked")); - if(attacked) { c->m_berserk_oa = atoi(skill->first_attribute("x")->value()); } - else {c->m_berserk = atoi(skill->first_attribute("x")->value()); } - } - if(strcmp(skill->first_attribute("id")->value(), "blitz") == 0) - { c->m_blitz = true; } - if(strcmp(skill->first_attribute("id")->value(), "burst") == 0) - { c->m_burst = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "counter") == 0) - { c->m_counter = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "crush") == 0) - { c->m_crush = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "disease") == 0) - { - bool attacked(skill->first_attribute("attacked")); - if(attacked) { c->m_disease_oa = true; } - else {c->m_disease = true; } - } - if(strcmp(skill->first_attribute("id")->value(), "evade") == 0) - { c->m_evade = true; } - if(strcmp(skill->first_attribute("id")->value(), "fear") == 0) - { c->m_fear = true; } - if(strcmp(skill->first_attribute("id")->value(), "flurry") == 0) - { c->m_flurry = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "flying") == 0) - { c->m_flying = true; } - if(strcmp(skill->first_attribute("id")->value(), "immobilize") == 0) - { c->m_immobilize = true; } - if(strcmp(skill->first_attribute("id")->value(), "intercept") == 0) - { c->m_intercept = true; } - if(strcmp(skill->first_attribute("id")->value(), "leech") == 0) - { c->m_leech = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "payback") == 0) - { c->m_payback = true; } - if(strcmp(skill->first_attribute("id")->value(), "pierce") == 0) - { c->m_pierce = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "poison") == 0) - { - bool attacked(skill->first_attribute("attacked")); - if(attacked) { c->m_poison_oa = atoi(skill->first_attribute("x")->value()); } - else {c->m_poison = atoi(skill->first_attribute("x")->value()); } - } - if(strcmp(skill->first_attribute("id")->value(), "recharge") == 0) - { c->m_recharge = true; } - if(strcmp(skill->first_attribute("id")->value(), "refresh") == 0) - { c->m_refresh = true; } - if(strcmp(skill->first_attribute("id")->value(), "regenerate") == 0) - { c->m_regenerate = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "siphon") == 0) - { c->m_siphon = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "split") == 0) - { c->m_split = true; } - if(strcmp(skill->first_attribute("id")->value(), "swipe") == 0) - { c->m_swipe = true; } - if(strcmp(skill->first_attribute("id")->value(), "tribute") == 0) - { c->m_tribute = true; } - if(strcmp(skill->first_attribute("id")->value(), "valor") == 0) - { c->m_valor = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "wall") == 0) - { c->m_wall = true; } - if(strcmp(skill->first_attribute("id")->value(), "chaos") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "cleanse") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "enfeeble") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "freeze") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "heal") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "infuse") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "jam") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "mimic") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "protect") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "rally") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "rush") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "shock") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "siege") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "strike") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "summon") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "supply") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "weaken") == 0) - { handle_skill(skill, c); } - } - cards.cards.push_back(c); - } - } - } - cards.organize(); - // std::cout << "nb cards: " << nb_cards << "\n"; - // for(auto counts: sets_counts) - // { - // std::cout << "set " << counts.first << " (" << sets[counts.first] << ")" << ": " << counts.second << "\n"; - // } - // std::cout << "nb mission cards: " << cards.mission_cards.size() << "\n"; -} //------------------------------------------------------------------------------ void print_deck(DeckIface& deck) { @@ -2262,115 +1963,6 @@ unsigned read_custom_decks(Cards& cards, std::string filename, std::map buffer; - xml_document<> doc; - parse_file(filename.c_str(), buffer, doc); - xml_node<>* root = doc.first_node(); - for(xml_node<>* mission_node = root->first_node(); - mission_node; - mission_node = mission_node->next_sibling()) - { - if(strcmp(mission_node->name(), "mission") == 0) - { - std::vector card_ids; - xml_node<>* id_node(mission_node->first_node("id")); - int id(id_node ? atoi(id_node->value()) : -1); - xml_node<>* name_node(mission_node->first_node("name")); - std::string deck_name{name_node->value()}; - xml_node<>* commander_node(mission_node->first_node("commander")); - card_ids.push_back(atoi(commander_node->value())); - xml_node<>* deck_node(mission_node->first_node("deck")); - for(xml_node<>* card_node = deck_node->first_node(); - card_node; - card_node = card_node->next_sibling()) - { - unsigned card_id{atoi(card_node->value())}; - // Handle the replacement art cards - if(cards.replace.find(card_id) != cards.replace.end()) - { - card_id = cards.replace[card_id]; - } - card_ids.push_back(card_id); - } - decks.mission_decks.push_back(DeckRandom{cards, card_ids}); - DeckRandom* deck = &decks.mission_decks.back(); - decks.mission_decks_by_id[id] = deck; - decks.mission_decks_by_name[deck_name] = deck; - } - } -} -//------------------------------------------------------------------------------ -void read_raids(Decks& decks, Cards& cards, std::string filename) -{ - std::vector buffer; - xml_document<> doc; - parse_file(filename.c_str(), buffer, doc); - xml_node<>* root = doc.first_node(); - for(xml_node<>* raid_node = root->first_node(); - raid_node; - raid_node = raid_node->next_sibling()) - { - if(strcmp(raid_node->name(), "raid") == 0) - { - std::vector always_cards; - std::vector > > some_cards; - xml_node<>* id_node(raid_node->first_node("id")); - int id(id_node ? atoi(id_node->value()) : -1); - xml_node<>* name_node(raid_node->first_node("name")); - std::string deck_name{name_node->value()}; - xml_node<>* commander_node(raid_node->first_node("commander")); - const Card* commander_card{cards.by_id(atoi(commander_node->value()))}; - xml_node<>* deck_node(raid_node->first_node("deck")); - xml_node<>* always_node{deck_node->first_node("always_include")}; - if(always_node) - { - for(xml_node<>* card_node = always_node->first_node(); - card_node; - card_node = card_node->next_sibling()) - { - unsigned card_id{atoi(card_node->value())}; - // Handle the replacement art cards - if(cards.replace.find(card_id) != cards.replace.end()) - { - card_id = cards.replace[card_id]; - } - always_cards.push_back(cards.by_id(card_id)); - } - } - for(xml_node<>* pool_node = always_node->next_sibling(); - pool_node; - pool_node = pool_node->next_sibling()) - { - if(strcmp(pool_node->name(), "card_pool") == 0) - { - unsigned num_cards_from_pool{atoi(pool_node->first_attribute("amount")->value())}; - std::vector cards_from_pool; - - for(xml_node<>* card_node = pool_node->first_node(); - card_node; - card_node = card_node->next_sibling()) - { - unsigned card_id{atoi(card_node->value())}; - // Handle the replacement art cards - if(cards.replace.find(card_id) != cards.replace.end()) - { - card_id = cards.replace[card_id]; - } - cards_from_pool.push_back(cards.by_id(card_id)); - } - some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); - } - } - decks.raid_decks.push_back(DeckRandom{commander_card, always_cards, some_cards}); - DeckRandom* deck = &decks.raid_decks.back(); - decks.raid_decks_by_id[id] = deck; - decks.raid_decks_by_name[deck_name] = deck; - } - } -} -//------------------------------------------------------------------------------ void load_decks(Decks& decks, Cards& cards) { try diff --git a/xml.cpp b/xml.cpp new file mode 100644 index 00000000..af7ca673 --- /dev/null +++ b/xml.cpp @@ -0,0 +1,419 @@ +#include "xml.h" + +#include +#include +#include +#include +#include "rapidxml.hpp" +#include "card.h" +#include "cards.h" +#include "deck.h" +#include "tyrant.h" +//---------------------- $20 cards.xml parsing --------------------------------- +// Sets: 1 enclave; 2 nexus; 3 blight; 4 purity; 5 homeworld; +// 6 phobos; 7 phobos aftermath; 8 awakening +// 1000 standard; 5000 rewards; 5001 promotional; 9000 exclusive +// mission only and test cards have no set +using namespace rapidxml; + +std::map sets_counts; + +Faction map_to_faction(unsigned i) +{ + return(i == 1 ? imperial : + i == 3 ? bloodthirsty : + i == 4 ? xeno : + i == 8 ? righteous : + i == 9 ? raider : + allfactions); +} + +Faction skill_faction(xml_node<>* skill) +{ + unsigned unmapped_faction(0); + xml_attribute<>* y(skill->first_attribute("y")); + if(y) + { + unmapped_faction = atoi(y->value()); + } + return(unmapped_faction == 0 ? allfactions : map_to_faction(unmapped_faction)); +} + +unsigned skill_value(xml_node<>* skill) +{ + unsigned value(0); + xml_attribute<>* x(skill->first_attribute("x")); + if(x) + { + value = atoi(x->value()); + } + return(value); +} + +template +struct GlobalSkill +{ + enum { type = 99 }; +}; + +template<> struct GlobalSkill { enum {type = augment_all}; }; +template<> struct GlobalSkill { enum {type = chaos_all}; }; +template<> struct GlobalSkill { enum {type = cleanse_all}; }; +template<> struct GlobalSkill { enum {type = enfeeble_all}; }; +template<> struct GlobalSkill { enum {type = freeze_all}; }; +template<> struct GlobalSkill { enum {type = heal_all}; }; +template<> struct GlobalSkill { enum {type = jam_all}; }; +template<> struct GlobalSkill { enum {type = protect_all}; }; +template<> struct GlobalSkill { enum {type = rally_all}; }; +template<> struct GlobalSkill { enum {type = siege_all}; }; +template<> struct GlobalSkill { enum {type = strike_all}; }; +template<> struct GlobalSkill { enum {type = weaken_all}; }; + +template +bool handle_global_skill(xml_node<>* node, Card* card) +{ + bool played(node->first_attribute("played")); + bool died(node->first_attribute("died")); + bool attacked(node->first_attribute("attacked")); + if(node->first_attribute("all")) + { + if(played) {card->add_played_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } + else if(died) {card->add_died_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } + else if(attacked) {card->add_attacked_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } + else {card->add_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } + return(true); + } + return(false); +} + +template<> +bool handle_global_skill<99>(xml_node<>* node, Card* card) +{ + return(false); +} + +template +void handle_skill(xml_node<>* node, Card* card) +{ + bool played(node->first_attribute("played")); + bool died(node->first_attribute("died")); + bool attacked(node->first_attribute("attacked")); + if(handle_global_skill::type>(node, card)) {} + else + { + if(played) {card->add_played_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } + else if(died) {card->add_died_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } + else if(attacked) {card->add_attacked_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } + else {card->add_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } + } +} +//------------------------------------------------------------------------------ +void parse_file(const char* filename, std::vector& buffer, xml_document<>& doc) +{ + std::ifstream cards_stream(filename, std::ios::binary); + // Get the size of the file + cards_stream.seekg(0,std::ios::end); + std::streampos length = cards_stream.tellg(); + cards_stream.seekg(0,std::ios::beg); + buffer.resize(length + std::streampos(1)); + cards_stream.read(&buffer[0],length); + // zero-terminate + buffer[length] = '\0'; + try + { + doc.parse<0>(&buffer[0]); + } + catch(rapidxml::parse_error& e) + { + std::cout << "Parse error exception.\n"; + std::cout << e.what(); + throw(e); + } +} +//------------------------------------------------------------------------------ +void read_cards(Cards& cards) +{ + std::vector buffer; + xml_document<> doc; + parse_file("cards.xml", buffer, doc); + xml_node<>* root = doc.first_node(); + bool mission_only(false); + unsigned nb_cards(0); + for(xml_node<>* card = root->first_node(); + card; + card = card->next_sibling()) + { + if(strcmp(card->name(), "unit") == 0) + { + xml_node<>* id_node(card->first_node("id")); + int id(id_node ? atoi(id_node->value()) : -1); + // Replacement art card + xml_node<>* replace_node(card->first_node("replace")); + if(replace_node) + { + cards.replace[id] = atoi(replace_node->value()); + continue; + } + xml_node<>* name_node(card->first_node("name")); + xml_node<>* attack_node(card->first_node("attack")); + xml_node<>* health_node(card->first_node("health")); + xml_node<>* cost_node(card->first_node("cost")); + xml_node<>* unique_node(card->first_node("unique")); + xml_node<>* rarity_node(card->first_node("rarity")); + xml_node<>* type_node(card->first_node("type")); + xml_node<>* set_node(card->first_node("set")); + int set(set_node ? atoi(set_node->value()) : -1); + mission_only = set == -1; + if((mission_only || set >= 0) && name_node && rarity_node) + { + if(!mission_only) + { + nb_cards++; + sets_counts[set]++; + } + Card* c(new Card()); + c->m_id = id; + c->m_name = name_node->value(); + if(id < 1000) + { c->m_type = CardType::assault; } + else if(id < 2000) + { c->m_type = CardType::commander; } + else if(id < 3000) + { c->m_type = CardType::structure; } + else + { c->m_type = CardType::action; } + if(attack_node) { c->m_attack = atoi(attack_node->value()); } + if(health_node) { c->m_health = atoi(health_node->value()); } + if(cost_node) { c->m_delay = atoi(cost_node->value()); } + if(unique_node) { c->m_unique = true; } + c->m_rarity = atoi(rarity_node->value()); + unsigned type(type_node ? atoi(type_node->value()) : 0); + c->m_faction = map_to_faction(type); + c->m_set = set; + for(xml_node<>* skill = card->first_node("skill"); skill; + skill = skill->next_sibling("skill")) + { + if(strcmp(skill->first_attribute("id")->value(), "antiair") == 0) + { c->m_antiair = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "armored") == 0) + { c->m_armored = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "augment") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "berserk") == 0) + { + bool attacked(skill->first_attribute("attacked")); + if(attacked) { c->m_berserk_oa = atoi(skill->first_attribute("x")->value()); } + else {c->m_berserk = atoi(skill->first_attribute("x")->value()); } + } + if(strcmp(skill->first_attribute("id")->value(), "blitz") == 0) + { c->m_blitz = true; } + if(strcmp(skill->first_attribute("id")->value(), "burst") == 0) + { c->m_burst = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "counter") == 0) + { c->m_counter = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "crush") == 0) + { c->m_crush = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "disease") == 0) + { + bool attacked(skill->first_attribute("attacked")); + if(attacked) { c->m_disease_oa = true; } + else {c->m_disease = true; } + } + if(strcmp(skill->first_attribute("id")->value(), "evade") == 0) + { c->m_evade = true; } + if(strcmp(skill->first_attribute("id")->value(), "fear") == 0) + { c->m_fear = true; } + if(strcmp(skill->first_attribute("id")->value(), "flurry") == 0) + { c->m_flurry = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "flying") == 0) + { c->m_flying = true; } + if(strcmp(skill->first_attribute("id")->value(), "immobilize") == 0) + { c->m_immobilize = true; } + if(strcmp(skill->first_attribute("id")->value(), "intercept") == 0) + { c->m_intercept = true; } + if(strcmp(skill->first_attribute("id")->value(), "leech") == 0) + { c->m_leech = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "payback") == 0) + { c->m_payback = true; } + if(strcmp(skill->first_attribute("id")->value(), "pierce") == 0) + { c->m_pierce = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "poison") == 0) + { + bool attacked(skill->first_attribute("attacked")); + if(attacked) { c->m_poison_oa = atoi(skill->first_attribute("x")->value()); } + else {c->m_poison = atoi(skill->first_attribute("x")->value()); } + } + if(strcmp(skill->first_attribute("id")->value(), "recharge") == 0) + { c->m_recharge = true; } + if(strcmp(skill->first_attribute("id")->value(), "refresh") == 0) + { c->m_refresh = true; } + if(strcmp(skill->first_attribute("id")->value(), "regenerate") == 0) + { c->m_regenerate = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "siphon") == 0) + { c->m_siphon = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "split") == 0) + { c->m_split = true; } + if(strcmp(skill->first_attribute("id")->value(), "swipe") == 0) + { c->m_swipe = true; } + if(strcmp(skill->first_attribute("id")->value(), "tribute") == 0) + { c->m_tribute = true; } + if(strcmp(skill->first_attribute("id")->value(), "valor") == 0) + { c->m_valor = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "wall") == 0) + { c->m_wall = true; } + if(strcmp(skill->first_attribute("id")->value(), "chaos") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "cleanse") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "enfeeble") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "freeze") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "heal") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "infuse") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "jam") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "mimic") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "protect") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "rally") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "rush") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "shock") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "siege") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "strike") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "summon") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "supply") == 0) + { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "weaken") == 0) + { handle_skill(skill, c); } + } + cards.cards.push_back(c); + } + } + } + cards.organize(); + // std::cout << "nb cards: " << nb_cards << "\n"; + // for(auto counts: sets_counts) + // { + // std::cout << "set " << counts.first << " (" << sets[counts.first] << ")" << ": " << counts.second << "\n"; + // } + // std::cout << "nb mission cards: " << cards.mission_cards.size() << "\n"; +} +//------------------------------------------------------------------------------ +void read_missions(Decks& decks, Cards& cards, std::string filename) +{ + std::vector buffer; + xml_document<> doc; + parse_file(filename.c_str(), buffer, doc); + xml_node<>* root = doc.first_node(); + for(xml_node<>* mission_node = root->first_node(); + mission_node; + mission_node = mission_node->next_sibling()) + { + if(strcmp(mission_node->name(), "mission") == 0) + { + std::vector card_ids; + xml_node<>* id_node(mission_node->first_node("id")); + int id(id_node ? atoi(id_node->value()) : -1); + xml_node<>* name_node(mission_node->first_node("name")); + std::string deck_name{name_node->value()}; + xml_node<>* commander_node(mission_node->first_node("commander")); + card_ids.push_back(atoi(commander_node->value())); + xml_node<>* deck_node(mission_node->first_node("deck")); + for(xml_node<>* card_node = deck_node->first_node(); + card_node; + card_node = card_node->next_sibling()) + { + unsigned card_id{atoi(card_node->value())}; + // Handle the replacement art cards + if(cards.replace.find(card_id) != cards.replace.end()) + { + card_id = cards.replace[card_id]; + } + card_ids.push_back(card_id); + } + decks.mission_decks.push_back(DeckRandom{cards, card_ids}); + DeckRandom* deck = &decks.mission_decks.back(); + decks.mission_decks_by_id[id] = deck; + decks.mission_decks_by_name[deck_name] = deck; + } + } +} +//------------------------------------------------------------------------------ +void read_raids(Decks& decks, Cards& cards, std::string filename) +{ + std::vector buffer; + xml_document<> doc; + parse_file(filename.c_str(), buffer, doc); + xml_node<>* root = doc.first_node(); + for(xml_node<>* raid_node = root->first_node(); + raid_node; + raid_node = raid_node->next_sibling()) + { + if(strcmp(raid_node->name(), "raid") == 0) + { + std::vector always_cards; + std::vector > > some_cards; + xml_node<>* id_node(raid_node->first_node("id")); + int id(id_node ? atoi(id_node->value()) : -1); + xml_node<>* name_node(raid_node->first_node("name")); + std::string deck_name{name_node->value()}; + xml_node<>* commander_node(raid_node->first_node("commander")); + const Card* commander_card{cards.by_id(atoi(commander_node->value()))}; + xml_node<>* deck_node(raid_node->first_node("deck")); + xml_node<>* always_node{deck_node->first_node("always_include")}; + if(always_node) + { + for(xml_node<>* card_node = always_node->first_node(); + card_node; + card_node = card_node->next_sibling()) + { + unsigned card_id{atoi(card_node->value())}; + // Handle the replacement art cards + if(cards.replace.find(card_id) != cards.replace.end()) + { + card_id = cards.replace[card_id]; + } + always_cards.push_back(cards.by_id(card_id)); + } + } + for(xml_node<>* pool_node = always_node->next_sibling(); + pool_node; + pool_node = pool_node->next_sibling()) + { + if(strcmp(pool_node->name(), "card_pool") == 0) + { + unsigned num_cards_from_pool{atoi(pool_node->first_attribute("amount")->value())}; + std::vector cards_from_pool; + + for(xml_node<>* card_node = pool_node->first_node(); + card_node; + card_node = card_node->next_sibling()) + { + unsigned card_id{atoi(card_node->value())}; + // Handle the replacement art cards + if(cards.replace.find(card_id) != cards.replace.end()) + { + card_id = cards.replace[card_id]; + } + cards_from_pool.push_back(cards.by_id(card_id)); + } + some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); + } + } + decks.raid_decks.push_back(DeckRandom{commander_card, always_cards, some_cards}); + DeckRandom* deck = &decks.raid_decks.back(); + decks.raid_decks_by_id[id] = deck; + decks.raid_decks_by_name[deck_name] = deck; + } + } +} diff --git a/xml.h b/xml.h new file mode 100644 index 00000000..39df06da --- /dev/null +++ b/xml.h @@ -0,0 +1,13 @@ +#ifndef XML_H_INCLUDED +#define XML_H_INCLUDED + +#include + +class Cards; +class Decks; + +void read_cards(Cards& cards); +void read_missions(Decks& decks, Cards& cards, std::string filename); +void read_raids(Decks& decks, Cards& cards, std::string filename); + +#endif From d6538985a0ee4affa99adf88e54f711ec03779e4 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 14:10:44 -0500 Subject: [PATCH 008/406] Move implementation from deck.h into deck.cpp --- deck.cpp | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ deck.h | 211 ++++-------------------------------------------------- 2 files changed, 226 insertions(+), 199 deletions(-) create mode 100644 deck.cpp diff --git a/deck.cpp b/deck.cpp new file mode 100644 index 00000000..0478b15f --- /dev/null +++ b/deck.cpp @@ -0,0 +1,214 @@ +#include "deck.h" + +#include // because of 1.51 bug. missing include in range/any_range.hpp ? +#include +#include + +#include "card.h" + +template +void partial_shuffle(RandomAccessIterator first, RandomAccessIterator middle, + 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; + + 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))]); + } +} + +namespace boost +{ +namespace range +{ +template +void shuffle(Range& range, UniformRandomNumberGenerator&& rand) +{ + std::shuffle(boost::begin(range), boost::end(range), rand); +} +} // namespace range +using range::shuffle; +} // namespace boost + +namespace range = boost::range; + +DeckRandom::DeckRandom(const Cards& all_cards, const std::vector& names) +{ + for(auto name: names) + { + auto card_it(all_cards.player_cards_by_name.find(name)); + if(card_it == all_cards.player_cards_by_name.end()) + { + throw std::runtime_error("While constructing a deck: the card " + name + " was not found."); + } + else + { + const Card* card{card_it->second}; + if(card->m_type == CardType::commander) + { + if(commander == nullptr) + { + commander = card; + } + else + { + throw std::runtime_error("While constructing a deck: two commanders detected (" + name + " and " + commander->m_name + ")"); + } + } + else + { + cards.emplace_back(card); + } + } + } + if(commander == nullptr) + { + throw std::runtime_error("While constructing a deck: no commander found"); + } +} + +DeckRandom::DeckRandom(const Cards& all_cards, const std::vector& ids) +{ + for(auto id: ids) + { + const Card* card{all_cards.by_id(id)}; + if(card->m_type == CardType::commander) + { + if(commander == nullptr) + { + commander = card; + } + else + { + throw std::runtime_error("While constructing a deck: two commanders detected (" + card->m_name + " and " + commander->m_name + ")"); + } + } + else + { + cards.emplace_back(card); + } + } + if(commander == nullptr) + { + throw std::runtime_error("While constructing a deck: no commander found"); + } +} + +DeckIface* DeckRandom::clone() const +{ + return(new DeckRandom(*this)); +} + +const Card* DeckRandom::get_commander() +{ + return(commander); +} + +const Card* DeckRandom::next() +{ + if(!shuffled_cards.empty()) + { + const Card* card = shuffled_cards.front(); + shuffled_cards.pop_front(); + return(card); + } + else + { + return(nullptr); + } +} + +void DeckRandom::shuffle(std::mt19937& re) +{ + shuffled_cards.clear(); + boost::insert(shuffled_cards, shuffled_cards.end(), cards); + for(auto& card_pool: raid_cards) + { + assert(card_pool.first <= card_pool.second.size()); + partial_shuffle(card_pool.second.begin(), card_pool.second.begin() + card_pool.first, card_pool.second.end(), re); + shuffled_cards.insert(shuffled_cards.end(), card_pool.second.begin(), card_pool.second.begin() + card_pool.first); + } + boost::shuffle(shuffled_cards, re); +} + +void DeckRandom::place_at_bottom(const Card* card) +{ + shuffled_cards.push_back(card); +} + +DeckOrdered* DeckOrdered::clone() const +{ + return(new DeckOrdered(*this)); +} + + +const Card* DeckOrdered::get_commander() +{ + return(commander); +} + +const Card* DeckOrdered::next() +{ + if(shuffled_cards.empty()) + { + return(nullptr); + } + else + { + 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()) + { + auto card2_order = order.find(card2->m_id); + if(!card1_order->second.empty()) + { + return(*card1_order->second.begin() < *card2_order->second.begin()); + } + else + { + return(true); + } + } + else + { + return(false); + } + }); + 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); + } +} + +void DeckOrdered::shuffle(std::mt19937& re) +{ + unsigned i = 0; + order.clear(); + for(auto card: cards) + { + order[card->m_id].push_back(i); + ++i; + } + shuffled_cards.clear(); + range::insert(shuffled_cards, shuffled_cards.end(), cards); + std::shuffle(shuffled_cards.begin(), shuffled_cards.end(), re); +} + +void DeckOrdered::place_at_bottom(const Card* card) +{ + shuffled_cards.push_back(card); +} diff --git a/deck.h b/deck.h index ef4a04a4..46a56525 100644 --- a/deck.h +++ b/deck.h @@ -12,39 +12,6 @@ class Card; -template -void partial_shuffle(RandomAccessIterator first, RandomAccessIterator middle, - 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; - - 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))]); - } -} - -namespace boost -{ -namespace range -{ -template -void shuffle(Range& range, UniformRandomNumberGenerator&& rand) -{ - std::shuffle(boost::begin(range), boost::end(range), rand); -} -} // namespace range -using range::shuffle; -} // namespace boost - -namespace range = boost::range; //---------------------- $30 Deck: a commander + a sequence of cards ----------- // Can be shuffled. // Implementations: random player and raid decks, ordered player decks. @@ -93,111 +60,16 @@ struct DeckRandom : DeckIface { } - DeckRandom(const Cards& all_cards, const std::vector& names) - { - for(auto name: names) - { - auto card_it(all_cards.player_cards_by_name.find(name)); - if(card_it == all_cards.player_cards_by_name.end()) - { - throw std::runtime_error("While constructing a deck: the card " + name + " was not found."); - } - else - { - const Card* card{card_it->second}; - if(card->m_type == CardType::commander) - { - if(commander == nullptr) - { - commander = card; - } - else - { - throw std::runtime_error("While constructing a deck: two commanders detected (" + name + " and " + commander->m_name + ")"); - } - } - else - { - cards.emplace_back(card); - } - } - } - if(commander == nullptr) - { - throw std::runtime_error("While constructing a deck: no commander found"); - } - } - - DeckRandom(const Cards& all_cards, const std::vector& ids) - { - for(auto id: ids) - { - const Card* card{all_cards.by_id(id)}; - if(card->m_type == CardType::commander) - { - if(commander == nullptr) - { - commander = card; - } - else - { - throw std::runtime_error("While constructing a deck: two commanders detected (" + card->m_name + " and " + commander->m_name + ")"); - } - } - else - { - cards.emplace_back(card); - } - } - if(commander == nullptr) - { - throw std::runtime_error("While constructing a deck: no commander found"); - } - } + DeckRandom(const Cards& all_cards, const std::vector& names); + DeckRandom(const Cards& all_cards, const std::vector& ids); ~DeckRandom() {} - virtual DeckIface* clone() const - { - return(new DeckRandom(*this)); - } - - const Card* get_commander() - { - return(commander); - } - - const Card* next() - { - if(!shuffled_cards.empty()) - { - const Card* card = shuffled_cards.front(); - shuffled_cards.pop_front(); - return(card); - } - else - { - return(nullptr); - } - } - - void shuffle(std::mt19937& re) - { - shuffled_cards.clear(); - boost::insert(shuffled_cards, shuffled_cards.end(), cards); - for(auto& card_pool: raid_cards) - { - assert(card_pool.first <= card_pool.second.size()); - partial_shuffle(card_pool.second.begin(), card_pool.second.begin() + card_pool.first, card_pool.second.end(), re); - shuffled_cards.insert(shuffled_cards.end(), card_pool.second.begin(), card_pool.second.begin() + card_pool.first); - } - boost::shuffle(shuffled_cards, re); - } - - void place_at_bottom(const Card* card) - { - shuffled_cards.push_back(card); - } + virtual DeckIface* clone() const; + const Card* get_commander(); + const Card* next(); + void shuffle(std::mt19937& re); + void place_at_bottom(const Card* card); }; //------------------------------------------------------------------------------ // No support for ordered raid decks @@ -220,70 +92,11 @@ struct DeckOrdered : DeckIface ~DeckOrdered() {} - virtual DeckOrdered* clone() const - { - return(new DeckOrdered(*this)); - } - - const Card* get_commander() { return(commander); } - - const Card* next() - { - if(shuffled_cards.empty()) - { - return(nullptr); - } - else - { - 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()) - { - auto card2_order = order.find(card2->m_id); - if(!card1_order->second.empty()) - { - return(*card1_order->second.begin() < *card2_order->second.begin()); - } - else - { - return(true); - } - } - else - { - return(false); - } - }); - 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); - } - } - - void shuffle(std::mt19937& re) - { - unsigned i = 0; - order.clear(); - for(auto card: cards) - { - order[card->m_id].push_back(i); - ++i; - } - shuffled_cards.clear(); - range::insert(shuffled_cards, shuffled_cards.end(), cards); - std::shuffle(shuffled_cards.begin(), shuffled_cards.end(), re); - } - - void place_at_bottom(const Card* card) - { - shuffled_cards.push_back(card); - } + virtual DeckOrdered* clone() const; + const Card* get_commander(); + const Card* next(); + void shuffle(std::mt19937& re); + void place_at_bottom(const Card* card); }; // + also the custom decks From aebb8df14cea787c83fe3c98a8cd88e2543237c8 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 14:12:47 -0500 Subject: [PATCH 009/406] Move Cards destructor into cards.cpp This allows using a forward declaration of Card in cards.h, hooray. --- cards.cpp | 6 ++++++ cards.h | 7 ++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cards.cpp b/cards.cpp index 850c758c..c49f128f 100644 --- a/cards.cpp +++ b/cards.cpp @@ -4,6 +4,7 @@ #include #include +#include "card.h" #include "tyrant.h" template @@ -14,6 +15,11 @@ std::string to_string(T val) return s.str(); } +Cards::~Cards() +{ + for(Card* c: cards) { delete(c); } +} + const Card* Cards::by_id(unsigned id) const { std::map::const_iterator cardIter{cards_by_id.find(id)}; diff --git a/cards.h b/cards.h index 8b5749e4..4772cdcf 100644 --- a/cards.h +++ b/cards.h @@ -5,14 +5,11 @@ #include #include -#include "card.h" +class Card; struct Cards { - ~Cards() - { - for(Card* c: cards) { delete(c); } - } + ~Cards(); std::vector cards; std::map cards_by_id; From 9c7a0bcfad5dbb815a2394da719b231cee0071ae Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 14:40:56 -0500 Subject: [PATCH 010/406] Split load_decks into load_decks{,_xml} This allows all rapidxml stuff to be isolated in xml.cpp. --- tyrant_optimize.cpp | 18 +----------------- xml.cpp | 20 ++++++++++++++++++++ xml.h | 1 + 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index e3813fa2..bded1386 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -37,7 +37,6 @@ #include #include #include -#include "rapidxml.hpp" #include "card.h" #include "cards.h" #include "deck.h" @@ -1965,22 +1964,6 @@ unsigned read_custom_decks(Cards& cards, std::string filename, std::map; diff --git a/xml.cpp b/xml.cpp index af7ca673..b774bd02 100644 --- a/xml.cpp +++ b/xml.cpp @@ -108,6 +108,26 @@ void handle_skill(xml_node<>* node, Card* card) } } //------------------------------------------------------------------------------ +void load_decks_xml(Decks& decks, Cards& cards) +{ + try + { + read_missions(decks, cards, "missions.xml"); + } + catch(const rapidxml::parse_error& e) + { + std::cout << "\nException while loading decks from file missions.xml\n"; + } + try + { + read_raids(decks, cards, "raids.xml"); + } + catch(const rapidxml::parse_error& e) + { + std::cout << "\nException while loading decks from file raids.xml\n"; + } +} +//------------------------------------------------------------------------------ void parse_file(const char* filename, std::vector& buffer, xml_document<>& doc) { std::ifstream cards_stream(filename, std::ios::binary); diff --git a/xml.h b/xml.h index 39df06da..3a30208a 100644 --- a/xml.h +++ b/xml.h @@ -6,6 +6,7 @@ class Cards; class Decks; +void load_decks_xml(Decks& decks, Cards& cards); void read_cards(Cards& cards); void read_missions(Decks& decks, Cards& cards, std::string filename); void read_raids(Decks& decks, Cards& cards, std::string filename); From 9578a0a07e23667293cd46abaeca008ac7f87fe4 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 15:05:36 -0500 Subject: [PATCH 011/406] Move file IO to read.{cpp,h} --- read.cpp | 223 ++++++++++++++++++++++++++++++++++++++++++++ read.h | 17 ++++ tyrant_optimize.cpp | 218 +------------------------------------------ 3 files changed, 242 insertions(+), 216 deletions(-) create mode 100644 read.cpp create mode 100644 read.h diff --git a/read.cpp b/read.cpp new file mode 100644 index 00000000..3f105b73 --- /dev/null +++ b/read.cpp @@ -0,0 +1,223 @@ +#include "read.h" + +#include +#include +#include +#include +#include + +#include "card.h" +#include "cards.h" +#include "deck.h" + +void load_decks(Decks& decks, Cards& cards) +{ + if(boost::filesystem::exists("Custom.txt")) + { + try + { + read_custom_decks(cards, std::string{"Custom.txt"}, decks.custom_decks); + } + catch(const std::runtime_error& e) + { + std::cout << "Exception while loading custom decks: " << e.what() << "\n"; + } + } +} + +std::vector > parse_deck_list(std::string list_string) +{ + std::vector > res; + boost::tokenizer > list_tokens{list_string, boost::char_delimiters_separator{false, ";", ""}}; + for(auto list_token = list_tokens.begin(); list_token != list_tokens.end(); ++list_token) + { + boost::tokenizer > deck_tokens{*list_token, boost::char_delimiters_separator{false, ":", ""}}; + auto deck_token = deck_tokens.begin(); + res.push_back(std::make_pair(*deck_token, 1.0d)); + ++deck_token; + if(deck_token != deck_tokens.end()) + { + res.back().second = boost::lexical_cast(*deck_token); + } + } + return(res); +} + +template Iterator advance_until(Iterator it, Iterator it_end, Functor f) +{ + while(it != it_end) + { + if(f(*it)) + { + break; + } + ++it; + } + return(it); +} + +// take care that "it" is 1 past current. +template Iterator recede_until(Iterator it, Iterator it_beg, Functor f) +{ + if(it == it_beg) { return(it_beg); } + --it; + do + { + if(f(*it)) + { + return(++it); + } + --it; + } while(it != it_beg); + return(it_beg); +} + +template Iterator read_token(Iterator it, Iterator it_end, Functor f, boost::optional& token) +{ + Iterator token_start = advance_until(it, it_end, [](const char& c){return(c != ' ');}); + Iterator token_end_after_spaces = advance_until(token_start, it_end, f); + if(token_start != token_end_after_spaces) + { + Iterator token_end = recede_until(token_end_after_spaces, token_start, [](const char& c){return(c != ' ');}); + token = boost::lexical_cast(std::string{token_start, token_end}); + return(token_end_after_spaces); + } + return(token_end_after_spaces); +} + +// Error codes: +// 2 -> file not readable +// 3 -> error while parsing file +unsigned read_custom_decks(Cards& cards, std::string filename, std::map& custom_decks) +{ + std::ifstream decks_file(filename.c_str()); + if(!decks_file.is_open()) + { + std::cerr << "File " << filename << " could not be opened\n"; + return(2); + } + unsigned num_line(0); + decks_file.exceptions(std::ifstream::badbit); + try + { + while(decks_file && !decks_file.eof()) + { + std::vector card_ids; + std::string deck_string; + getline(decks_file, deck_string); + ++num_line; + if(deck_string.size() > 0) + { + if(strncmp(deck_string.c_str(), "//", 2) == 0) + { + continue; + } + boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; + auto token_iter = deck_tokens.begin(); + boost::optional deck_name; + if(token_iter != deck_tokens.end()) + { + read_token(token_iter->begin(), token_iter->end(), [](char c){return(false);}, deck_name); + if(!deck_name || (*deck_name).size() == 0) + { + std::cerr << "Error in file " << filename << " at line " << num_line << ", could not read the deck name.\n"; + continue; + } + } + else + { + std::cerr << "Error in file " << filename << " at line " << num_line << ", could not read the deck name.\n"; + continue; + } + ++token_iter; + for(; token_iter != deck_tokens.end(); ++token_iter) + { + std::string card_spec(*token_iter); + try + { + auto card_spec_iter = card_spec.begin(); + boost::optional card_name; + card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c=='[' || c=='#' || c=='\r');}, card_name); + if(!card_name) + { + std::cerr << "Error in file " << filename << " at line " << num_line << " while parsing card " << card_spec << " in deck " << deck_name << "\n"; + break; + } + else + { + boost::optional card_id; + if(*card_spec_iter == '[') + { + ++card_spec_iter; + card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c==']');}, card_id); + card_spec_iter = advance_until(card_spec_iter, card_spec.end(), [](char c){return(c!=' ');}); + } + boost::optional card_num; + if(*card_spec_iter == '#') + { + ++card_spec_iter; + card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c < '0' || c > '9');}, card_num); + } + unsigned resolved_id{card_id ? *card_id : 0}; + if(resolved_id == 0) + { + auto card_it = cards.player_cards_by_name.find(*card_name); + if(card_it != cards.player_cards_by_name.end()) + { + resolved_id = card_it->second->m_id; + } + else + { + std::cerr << "Error in file " << filename << " at line " << num_line << " while parsing card " << card_spec << " in deck " << *deck_name << ": card not found\n"; + break; + } + } + for(unsigned i(0); i < (card_num ? *card_num : 1); ++i) + { + card_ids.push_back(resolved_id); + } + } + } + catch(boost::bad_lexical_cast e) + { + std::cerr << "Error in file " << filename << " at line " << num_line << " while parsing card " << card_spec << " in deck " << deck_name << "\n"; + } + } + if(deck_name) + { + custom_decks.insert({*deck_name, new DeckRandom{cards, card_ids}}); + } + } + } + } + catch (std::ifstream::failure e) + { + std::cerr << "Exception while parsing the file " << filename << " (badbit is set).\n"; + e.what(); + return(3); + } +} + +void read_owned_cards(Cards& cards, std::map& owned_cards) +{ + std::ifstream owned_file{"ownedcards.txt"}; + std::string owned_str{(std::istreambuf_iterator(owned_file)), std::istreambuf_iterator()}; + boost::tokenizer > tok{owned_str, boost::char_delimiters_separator{false, "()\n", ""}}; + for(boost::tokenizer >::iterator beg=tok.begin(); beg!=tok.end();++beg) + { + std::string name{*beg}; + ++beg; + assert(beg != tok.end()); + unsigned num{atoi((*beg).c_str())}; + auto card_itr = cards.player_cards_by_name.find(name); + if(card_itr == cards.player_cards_by_name.end()) + { + std::cerr << "Error in file ownedcards.txt, the card \"" << name << "\" does not seem to be a valid card.\n"; + } + else + { + owned_cards[card_itr->second->m_id] = num; + } + } +} + diff --git a/read.h b/read.h new file mode 100644 index 00000000..528fb777 --- /dev/null +++ b/read.h @@ -0,0 +1,17 @@ +#ifndef READ_H_INCLUDED +#define READ_H_INCLUDED + +#include +#include +#include + +class Cards; +class Decks; +class DeckIface; + +void load_decks(Decks& decks, Cards& cards); +std::vector > parse_deck_list(std::string list_string); +unsigned read_custom_decks(Cards& cards, std::string filename, std::map& custom_decks); +void read_owned_cards(Cards& cards, std::map& owned_cards); + +#endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index bded1386..0cebe720 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -22,24 +22,21 @@ #include #include #include -#include #include #include #include #include #include -#include -#include #include #include #include #include #include #include -#include #include "card.h" #include "cards.h" #include "deck.h" +#include "read.h" #include "tyrant.h" #include "xml.h" //#include "timer.hpp" @@ -1805,177 +1802,6 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) } } } - -//---------------------- $70 More xml parsing: missions and raids -------------- -template Iterator advance_until(Iterator it, Iterator it_end, Functor f) -{ - while(it != it_end) - { - if(f(*it)) - { - break; - } - ++it; - } - return(it); -} - -// take care that "it" is 1 past current. -template Iterator recede_until(Iterator it, Iterator it_beg, Functor f) -{ - if(it == it_beg) { return(it_beg); } - --it; - do - { - if(f(*it)) - { - return(++it); - } - --it; - } while(it != it_beg); - return(it_beg); -} - -template Iterator read_token(Iterator it, Iterator it_end, Functor f, boost::optional& token) -{ - Iterator token_start = advance_until(it, it_end, [](const char& c){return(c != ' ');}); - Iterator token_end_after_spaces = advance_until(token_start, it_end, f); - if(token_start != token_end_after_spaces) - { - Iterator token_end = recede_until(token_end_after_spaces, token_start, [](const char& c){return(c != ' ');}); - token = boost::lexical_cast(std::string{token_start, token_end}); - return(token_end_after_spaces); - } - return(token_end_after_spaces); -} - -// Error codes: -// 2 -> file not readable -// 3 -> error while parsing file -unsigned read_custom_decks(Cards& cards, std::string filename, std::map& custom_decks) -{ - std::ifstream decks_file(filename.c_str()); - if(!decks_file.is_open()) - { - std::cerr << "File " << filename << " could not be opened\n"; - return(2); - } - unsigned num_line(0); - decks_file.exceptions(std::ifstream::badbit); - try - { - while(decks_file && !decks_file.eof()) - { - std::vector card_ids; - std::string deck_string; - getline(decks_file, deck_string); - ++num_line; - if(deck_string.size() > 0) - { - if(strncmp(deck_string.c_str(), "//", 2) == 0) - { - continue; - } - boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; - auto token_iter = deck_tokens.begin(); - boost::optional deck_name; - if(token_iter != deck_tokens.end()) - { - read_token(token_iter->begin(), token_iter->end(), [](char c){return(false);}, deck_name); - if(!deck_name || (*deck_name).size() == 0) - { - std::cerr << "Error in file " << filename << " at line " << num_line << ", could not read the deck name.\n"; - continue; - } - } - else - { - std::cerr << "Error in file " << filename << " at line " << num_line << ", could not read the deck name.\n"; - continue; - } - ++token_iter; - for(; token_iter != deck_tokens.end(); ++token_iter) - { - std::string card_spec(*token_iter); - try - { - auto card_spec_iter = card_spec.begin(); - boost::optional card_name; - card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c=='[' || c=='#' || c=='\r');}, card_name); - if(!card_name) - { - std::cerr << "Error in file " << filename << " at line " << num_line << " while parsing card " << card_spec << " in deck " << deck_name << "\n"; - break; - } - else - { - boost::optional card_id; - if(*card_spec_iter == '[') - { - ++card_spec_iter; - card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c==']');}, card_id); - card_spec_iter = advance_until(card_spec_iter, card_spec.end(), [](char c){return(c!=' ');}); - } - boost::optional card_num; - if(*card_spec_iter == '#') - { - ++card_spec_iter; - card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c < '0' || c > '9');}, card_num); - } - unsigned resolved_id{card_id ? *card_id : 0}; - if(resolved_id == 0) - { - auto card_it = cards.player_cards_by_name.find(*card_name); - if(card_it != cards.player_cards_by_name.end()) - { - resolved_id = card_it->second->m_id; - } - else - { - std::cerr << "Error in file " << filename << " at line " << num_line << " while parsing card " << card_spec << " in deck " << *deck_name << ": card not found\n"; - break; - } - } - for(unsigned i(0); i < (card_num ? *card_num : 1); ++i) - { - card_ids.push_back(resolved_id); - } - } - } - catch(boost::bad_lexical_cast e) - { - std::cerr << "Error in file " << filename << " at line " << num_line << " while parsing card " << card_spec << " in deck " << deck_name << "\n"; - } - } - if(deck_name) - { - custom_decks.insert({*deck_name, new DeckRandom{cards, card_ids}}); - } - } - } - } - catch (std::ifstream::failure e) - { - std::cerr << "Exception while parsing the file " << filename << " (badbit is set).\n"; - e.what(); - return(3); - } -} -//------------------------------------------------------------------------------ -void load_decks(Decks& decks, Cards& cards) -{ - if(boost::filesystem::exists("Custom.txt")) - { - try - { - read_custom_decks(cards, std::string{"Custom.txt"}, decks.custom_decks); - } - catch(const std::runtime_error& e) - { - std::cout << "Exception while loading custom decks: " << e.what() << "\n"; - } - } -} //------------------------------------------------------------------------------ DeckIface* find_deck(const Decks& decks, std::string name) { @@ -2002,28 +1828,6 @@ DeckIface* find_deck(const Decks& decks, std::string name) //------------------------------------------------------------------------------ std::map owned_cards; bool use_owned_cards{false}; -void read_owned_cards(Cards& cards) -{ - std::ifstream owned_file{"ownedcards.txt"}; - std::string owned_str{(std::istreambuf_iterator(owned_file)), std::istreambuf_iterator()}; - boost::tokenizer > tok{owned_str, boost::char_delimiters_separator{false, "()\n", ""}}; - for(boost::tokenizer >::iterator beg=tok.begin(); beg!=tok.end();++beg) - { - std::string name{*beg}; - ++beg; - assert(beg != tok.end()); - unsigned num{atoi((*beg).c_str())}; - auto card_itr = cards.player_cards_by_name.find(name); - if(card_itr == cards.player_cards_by_name.end()) - { - std::cerr << "Error in file ownedcards.txt, the card \"" << name << "\" does not seem to be a valid card.\n"; - } - else - { - owned_cards[card_itr->second->m_id] = num; - } - } -} // No raid rewards from 500 and 1k honor for ancient raids // No very hard to get rewards (level >= 150, faction >= 13) @@ -2822,24 +2626,6 @@ void print_available_decks(const Decks& decks) } } -std::vector > parse_deck_list(std::string list_string) -{ - std::vector > res; - boost::tokenizer > list_tokens{list_string, boost::char_delimiters_separator{false, ";", ""}}; - for(auto list_token = list_tokens.begin(); list_token != list_tokens.end(); ++list_token) - { - boost::tokenizer > deck_tokens{*list_token, boost::char_delimiters_separator{false, ":", ""}}; - auto deck_token = deck_tokens.begin(); - res.push_back(std::make_pair(*deck_token, 1.0d)); - ++deck_token; - if(deck_token != deck_tokens.end()) - { - res.back().second = boost::lexical_cast(*deck_token); - } - } - return(res); -} - void usage(int argc, char** argv) { std::cout << "usage: " << argv[0] << " [optional flags] [brute ] [climb ]\n"; @@ -2871,7 +2657,7 @@ int main(int argc, char** argv) bool ordered = false; Cards cards; read_cards(cards); - read_owned_cards(cards); + read_owned_cards(cards, owned_cards); Decks decks; load_decks_xml(decks, cards); load_decks(decks, cards); From b3feae2810a179cb06f16ae2ff8f0b266341e8e9 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 19 Nov 2012 21:24:04 -0500 Subject: [PATCH 012/406] Move simulation-related things to sim.cpp --- sim.cpp | 1614 ++++++++++++++++++++++++++++++++++++++ sim.h | 204 +++++ tyrant_optimize.cpp | 1798 +------------------------------------------ 3 files changed, 1828 insertions(+), 1788 deletions(-) create mode 100644 sim.cpp create mode 100644 sim.h diff --git a/sim.cpp b/sim.cpp new file mode 100644 index 00000000..8df5cfba --- /dev/null +++ b/sim.cpp @@ -0,0 +1,1614 @@ +#include "sim.h" + +#include +#include +#include +#include +#include + +#include "card.h" +#include "deck.h" + +//---------------------- $00 general stuff ------------------------------------- +template +std::string to_string(T val) +{ + std::stringstream s; + s << val; + return s.str(); +} +//---------------------- Debugging stuff --------------------------------------- +bool debug_print(false); +bool debug_line(false); +#ifndef NDEBUG +#define _DEBUG_MSG(format, args...) \ + { \ + if(debug_print) \ + { \ + char* format2 = new char[strlen(format) + (8 + 1)*sizeof(char)]; \ + if(debug_line) { strcpy(format2, "%i - "); } \ + else { strcpy(format2, ""); } \ + strcat(format2, format); \ + if(debug_line) { printf(format2, __LINE__ , ##args); } \ + else { printf(format2, ##args); } \ + delete[] format2; \ + std::cout << std::flush; \ + } \ + } +#else +#define _DEBUG_MSG(format, args...) +#endif +//------------------------------------------------------------------------------ +CardStatus::CardStatus(const Card* card) : + m_card(card), + m_index(0), + m_player(0), + m_augmented(0), + m_berserk(0), + blitz(false), + m_chaos(false), + m_delay(card->m_delay), + m_diseased(false), + m_enfeebled(0), + m_faction(card->m_faction), + m_frozen(false), + m_hp(card->m_health), + m_immobilized(false), + m_infused(false), + m_jammed(false), + m_poisoned(0), + m_protected(0), + m_rallied(0), + m_weakened(0) +{ +} +//------------------------------------------------------------------------------ +inline void CardStatus::set(const Card* card) +{ + this->set(*card); +} +//------------------------------------------------------------------------------ +inline void CardStatus::set(const Card& card) +{ + m_card = &card; + m_index = 0; + m_player = 0; + m_augmented = 0; + m_berserk = 0; + blitz = false; + m_chaos = false; + m_delay = card.m_delay; + m_diseased = false; + m_enfeebled = 0; + m_faction = card.m_faction; + m_frozen = false; + m_hp = card.m_health; + m_immobilized = false; + infused_skills.clear(); + m_infused = false; + m_jammed = false; + m_poisoned = 0; + m_protected = 0; + m_rallied = 0; + m_weakened = 0; +} +//------------------------------------------------------------------------------ +std::string skill_description(const SkillSpec& s) +{ + return(skill_names[std::get<0>(s)] + + (std::get<2>(s) == allfactions ? "" : std::string(" ") + faction_names[std::get<2>(s)]) + + (std::get<1>(s) == 0 ? "" : std::string(" ") + to_string(std::get<1>(s)))); +} +//------------------------------------------------------------------------------ +std::string status_description(CardStatus* status) +{ + assert(status); + std::string desc; + switch(status->m_card->m_type) + { + case CardType::commander: desc = "Commander "; break; + case CardType::action: desc = "Action "; break; + case CardType::assault: desc = "A " + to_string(status->m_index) + " "; break; + case CardType::structure: desc = "S " + to_string(status->m_index) + " "; break; + } + desc += "[" + status->m_card->m_name + "]"; + return(desc); +} +//------------------------------------------------------------------------------ +void Hand::reset(std::mt19937& re) +{ + assaults.reset(); + structures.reset(); + commander = CardStatus(deck->get_commander()); + deck->shuffle(re); +} +//---------------------- $40 Game rules implementation ------------------------- +// Everything about how a battle plays out, except the following: +// the implementation of the attack by an assault card is in the next section; +// the implementation of the active skills is in the section after that. +unsigned turn_limit{50}; +//------------------------------------------------------------------------------ +inline unsigned opponent(unsigned player) +{ + return((player + 1) % 2); +} +//------------------------------------------------------------------------------ +void prepend_on_death(Field* fd) +{ + for(auto status: boost::adaptors::reverse(fd->killed_with_on_death)) + { + for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_died)) + { + _DEBUG_MSG("On death skill pushed in front %s %u %s\n", skill_names[std::get<0>(skill)].c_str(), std::get<1>(skill), faction_names[std::get<2>(skill)].c_str()); + fd->skill_queue.emplace_front(status, skill); + } + } + fd->killed_with_on_death.clear(); +} +//------------------------------------------------------------------------------ +void(*skill_table[num_skills])(Field*, CardStatus* src_status, const SkillSpec&); +void resolve_skill(Field* fd) +{ + while(!fd->skill_queue.empty()) + { + auto skill_instance(fd->skill_queue.front()); + auto& status(std::get<0>(skill_instance)); + auto& skill(std::get<1>(skill_instance)); + fd->skill_queue.pop_front(); + skill_table[std::get<0>(skill)](fd, status, skill); + } +} +//------------------------------------------------------------------------------ +void attack_phase(Field* fd); +SkillSpec augmented_skill(CardStatus* status, const SkillSpec& s) +{ + SkillSpec augmented_s = s; + if(std::get<0>(s) != augment && std::get<0>(s) != augment_all && std::get<0>(s) != summon) + { + std::get<1>(augmented_s) += status->m_augmented; + } + return(augmented_s); +} +void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills) +{ + assert(status); + assert(fd->skill_queue.size() == 0); + for(auto& skill: skills) + { + _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(skill).c_str()); + fd->skill_queue.emplace_back(status, status->m_augmented == 0 ? skill : augmented_skill(status, skill)); + resolve_skill(fd); + } +} +struct PlayCard +{ + const Card* card; + Field* fd; + CardStatus* status; + Storage* storage; + + PlayCard(const Card* card_, Field* fd_) : + card{card_}, + fd{fd_}, + status{nullptr}, + storage{nullptr} + {} + + template + bool op() + { + setStorage(); + placeCard(); + onPlaySkills(); + blitz(); + } + + // action + template + void setStorage() + { + } + + // assault + structure + template + void placeCard() + { + status = &storage->add_back(); + status->set(card); + status->m_index = storage->size() - 1; + status->m_player = fd->tapi; + if(fd->turn == 1 && fd->gamemode == tournament && status->m_delay > 0) + { + ++status->m_delay; + } + placeDebugMsg(); + } + + // assault + structure + template + void placeDebugMsg() + { + _DEBUG_MSG("Placed [%s] as %s %d\n", card->m_name.c_str(), cardtype_names[type].c_str(), storage->size() - 1); + } + + // all except assault: noop + template + void blitz() + { + } + + // assault + structure + template + void onPlaySkills() + { + for(auto& skill: card->m_skills_played) + { + fd->skill_queue.emplace_back(status, skill); + resolve_skill(fd); + } + } +}; +// assault +template <> +void PlayCard::setStorage() +{ + storage = &fd->tap->assaults; +} +// structure +template <> +void PlayCard::setStorage() +{ + storage = &fd->tap->structures; +} +// action +template <> +void PlayCard::placeCard() +{ +} +// assault +template <> +void PlayCard::blitz() +{ + if(card->m_blitz && fd->tip->assaults.size() > status->m_index && fd->tip->assaults[status->m_index].m_hp > 0 && fd->tip->assaults[status->m_index].m_delay == 0) + { + status->blitz = true; + } +} +// action +template <> +void PlayCard::onPlaySkills() +{ + for(auto& skill: card->m_skills) + { + fd->skill_queue.emplace_back(nullptr, skill); + resolve_skill(fd); + // Special case: enemy commander killed by a shock action card + if(fd->tip->commander.m_hp == 0) + { + _DEBUG_MSG("turn's defender dead.\n"); + fd->end = true; + break; + } + } + // Special case: recharge ability + if(card->m_recharge && fd->flip()) + { + fd->tap->deck->place_at_bottom(card); + } +} +//------------------------------------------------------------------------------ +void turn_start_phase(Field* fd); +void prepend_on_death(Field* fd); +// return value : 0 -> attacker wins, 1 -> defender wins +unsigned play(Field* fd) +{ + fd->players[0]->commander.m_player = 0; + fd->players[1]->commander.m_player = 1; + fd->tapi = fd->gamemode == surge ? 1 : 0; + fd->tipi = opponent(fd->tapi); + fd->tap = fd->players[fd->tapi]; + fd->tip = fd->players[fd->tipi]; + fd->end = false; + // Shuffle deck + while(fd->turn < turn_limit && !fd->end) + { + fd->current_phase = Field::playcard_phase; + // Initialize stuff, remove dead cards + _DEBUG_MSG("##### TURN %u #####\n", fd->turn); + turn_start_phase(fd); + // Special case: refresh on commander + if(fd->tip->commander.m_card->m_refresh && fd->tip->commander.m_hp > 0) + { + fd->tip->commander.m_hp = fd->tip->commander.m_card->m_health; + } + // Play a card + const Card* played_card(fd->tap->deck->next()); + if(played_card) + { + switch(played_card->m_type) + { + case CardType::action: + // end: handles commander death by shock + PlayCard(played_card, fd).op(); + break; + case CardType::assault: + PlayCard(played_card, fd).op(); + break; + case CardType::structure: + PlayCard(played_card, fd).op(); + break; + } + } + // Evaluate commander + fd->current_phase = Field::commander_phase; + evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); + // Evaluate structures + fd->current_phase = Field::structures_phase; + for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->structures.size(); ++fd->current_ci) + { + CardStatus& current_status(fd->tap->structures[fd->current_ci]); + if(current_status.m_delay == 0) + { + evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); + } + } + // Evaluate assaults + fd->current_phase = Field::assaults_phase; + for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->assaults.size(); ++fd->current_ci) + { + // ca: current assault + CardStatus& current_status(fd->tap->assaults[fd->current_ci]); + if((current_status.m_delay > 0 && !current_status.blitz) || current_status.m_hp == 0 || current_status.m_jammed || current_status.m_frozen) + { + //_DEBUG_MSG("! Assault %u (%s) hp: %u, jammed %u\n", card_index, current_status.m_card->m_name.c_str(), current_status.m_hp, current_status.m_jammed); + } + else + { + // Special case: check for split (tartarus swarm raid) + if(current_status.m_card->m_split && fd->tap->assaults.size() + fd->tap->structures.size() < 100) + { + CardStatus& status_split(fd->tap->assaults.add_back()); + status_split.set(current_status.m_card); + _DEBUG_MSG("Split assault %d (%s)\n", fd->tap->assaults.size() - 1, current_status.m_card->m_name.c_str()); + } + // Evaluate skills + // Special case: Gore Typhon's infuse + evaluate_skills(fd, ¤t_status, current_status.m_infused ? current_status.infused_skills : current_status.m_card->m_skills); + // Attack + if(!current_status.m_immobilized && current_status.m_hp > 0) + { + attack_phase(fd); + } + } + } + std::swap(fd->tapi, fd->tipi); + std::swap(fd->tap, fd->tip); + ++fd->turn; + } + // defender wins + if(fd->players[0]->commander.m_hp == 0) { _DEBUG_MSG("Defender wins.\n"); return(1); } + // attacker wins + if(fd->players[1]->commander.m_hp == 0) { _DEBUG_MSG("Attacker wins.\n"); return(0); } + if(fd->turn >= turn_limit) { return(1); } +} +//------------------------------------------------------------------------------ +// All the stuff that happens at the beginning of a turn, before a card is played +inline unsigned safe_minus(unsigned x, unsigned y) +{ + return(x - std::min(x, y)); +} +// returns true iff the card died. +bool remove_hp(Field* fd, CardStatus& status, unsigned dmg) +{ + assert(status.m_hp > 0); + status.m_hp = safe_minus(status.m_hp, dmg); + const bool just_died(status.m_hp == 0); + if(just_died) + { + _DEBUG_MSG("Card %u (%s) dead\n", status.m_index, status.m_card->m_name.c_str()); + if(status.m_card->m_skills_died.size() > 0) + { + fd->killed_with_on_death.push_back(&status); + } + if(status.m_card->m_regenerate) + { + fd->killed_with_regen.push_back(&status); + } + } + return(just_died); +} +inline bool is_it_dead(CardStatus& c) +{ + if(c.m_hp == 0) // yes it is + { + _DEBUG_MSG("Dead: %s\n", status_description(&c).c_str()); + return(true); + } + else { return(false); } // nope still kickin' +} +inline void remove_dead(Storage& storage) +{ + storage.remove(is_it_dead); +} +void check_regeneration(Field* fd) +{ + for(unsigned i(0); i < fd->killed_with_regen.size(); ++i) + { + CardStatus& status = *fd->killed_with_regen[i]; + if(status.m_hp == 0 && status.m_card->m_regenerate > 0 && !status.m_diseased) + { + status.m_hp = fd->flip() ? status.m_card->m_regenerate : 0; + } + if(status.m_hp > 0) + { + _DEBUG_MSG("Card %s regenerated, hp 0 -> %u\n", status.m_card->m_name.c_str(), status.m_hp); + } + + } + fd->killed_with_regen.clear(); +} +void turn_start_phase(Field* fd) +{ + remove_dead(fd->tap->assaults); + remove_dead(fd->tap->structures); + remove_dead(fd->tip->assaults); + remove_dead(fd->tip->structures); + // Active player's assault cards: + // update index + // remove enfeeble, protect; apply poison damage, reduce delay + { + auto& assaults(fd->tap->assaults); + for(unsigned index(0), end(assaults.size()); + index < end; + ++index) + { + CardStatus& status(assaults[index]); + status.m_index = index; + status.m_enfeebled = 0; + status.m_protected = 0; + remove_hp(fd, status, status.m_poisoned); + if(status.m_delay > 0 && !status.m_frozen) { --status.m_delay; } + } + } + // Active player's structure cards: + // update index + // reduce delay + { + auto& structures(fd->tap->structures); + for(unsigned index(0), end(structures.size()); + index < end; + ++index) + { + CardStatus& status(structures[index]); + status.m_index = index; + if(status.m_delay > 0) { --status.m_delay; } + } + } + // Defending player's assault cards: + // update index + // remove augment, chaos, freeze, immobilize, jam, rally, weaken, apply refresh + { + auto& assaults(fd->tip->assaults); + for(unsigned index(0), end(assaults.size()); + index < end; + ++index) + { + CardStatus& status(assaults[index]); + status.m_index = index; + status.m_augmented = 0; + status.blitz = false; + status.m_chaos = false; + status.m_frozen = false; + status.m_immobilized = false; + status.m_jammed = false; + status.m_rallied = 0; + status.m_weakened = 0; + if(status.m_card->m_refresh && !status.m_diseased) + { +#ifndef NDEBUG + if(status.m_hp < status.m_card->m_health) + { + _DEBUG_MSG("%u %s refreshed. hp %u -> %u.\n", index, status_description(&status).c_str(), status.m_hp, status.m_card->m_health); + } +#endif + status.m_hp = status.m_card->m_health; + } + } + } + // Defending player's structure cards: + // update index + // apply refresh + { + auto& structures(fd->tip->structures); + for(unsigned index(0), end(structures.size()); + index < end; + ++index) + { + CardStatus& status(structures[index]); + status.m_index = index; + if(status.m_card->m_refresh && status.m_hp < status.m_card->m_health) + { +#ifndef NDEBUG + _DEBUG_MSG("%s refreshed. hp %u -> %u.\n", index, status_description(&status).c_str(), status.m_hp, status.m_card->m_health); +#endif + status.m_hp = status.m_card->m_health; + } + } + } + // Perform on death skills (from cards killed by poison damage) + prepend_on_death(fd); + resolve_skill(fd); + // Regen from poison + check_regeneration(fd); +} +//---------------------- $50 attack by assault card implementation ------------- +inline void add_hp(CardStatus* target, unsigned v) +{ + target->m_hp = std::min(target->m_hp + v, target->m_card->m_health); +} +inline void apply_poison(CardStatus* target, unsigned v) +{ + target->m_poisoned = std::max(target->m_poisoned, v); +} +inline int attack_power(CardStatus* att) +{ + return(safe_minus(att->m_card->m_attack + att->m_berserk + att->m_rallied, att->m_weakened)); +} +// Counter damage dealt to the attacker (att) by defender (def) +// pre-condition: only valid if m_card->m_counter > 0 +inline unsigned counter_damage(CardStatus* att, CardStatus* def) +{ + assert(att->m_card->m_type == CardType::assault); + assert(def->m_card->m_type != CardType::action); + return(safe_minus(def->m_card->m_counter + att->m_enfeebled, att->m_protected)); +} +inline CardStatus* select_first_enemy_wall(Field* fd) +{ + for(unsigned i(0); i < fd->tip->structures.size(); ++i) + { + CardStatus& card(fd->tip->structures[i]); + if(card.m_card->m_wall && card.m_hp > 0) { return(&card); } + } + return(nullptr); +} + +inline unsigned valor_damage(Field* fd, CardStatus& status) +{ + if(status.m_card->m_valor > 0) + { + unsigned count_ta(0); + unsigned count_td(0); + for(unsigned i(0); i < fd->tap->assaults.size(); ++i) + { + if(fd->tap->assaults[i].m_hp > 0) { ++count_ta; } + } + for(unsigned i(0); i < fd->tip->assaults.size(); ++i) + { + if(fd->tip->assaults[i].m_hp > 0) { ++count_td; } + } + if(count_ta < count_td) { return(status.m_card->m_valor); } + } + return(0); +} + +inline unsigned attack_damage_against_non_assault(Field* fd, CardStatus& att_status) +{ + const Card& att_card(*att_status.m_card); + assert(att_card.m_type == CardType::assault); + // pre modifier damage + unsigned damage(attack_power(&att_status)); + // + if(damage > 0) + { + damage += valor_damage(fd, att_status); + } + return(damage); +} + +inline unsigned attack_damage_against_assault(Field* fd, CardStatus& att_status, CardStatus& def_status) +{ + const Card& att_card(*att_status.m_card); + const Card& def_card(*def_status.m_card); + assert(att_card.m_type == CardType::assault); + assert(def_card.m_type == CardType::assault); + // pre modifier damage + unsigned damage(attack_power(&att_status)); + // + if(damage > 0) + { + damage = safe_minus( + damage // pre-modifier damage + + valor_damage(fd, att_status) // valor + + def_status.m_enfeebled // enfeeble + + (def_card.m_flying ? att_card.m_antiair : 0) // anti-air + + (att_card.m_burst > 0 ? (def_status.m_hp == def_card.m_health ? att_card.m_burst : 0) : 0) // burst + // armor + protect + pierce + , safe_minus(def_card.m_armored + def_status.m_protected, att_card.m_pierce)); + } + return(damage); +} + +inline bool alive_assault(Storage& assaults, unsigned index) +{ + return(index >= 0 && assaults.size() > index && assaults[index].m_hp > 0); +} + +void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg) +{ + assert(status.m_hp > 0); + assert(status.m_card->m_type == CardType::commander); + status.m_hp = safe_minus(status.m_hp, dmg); + if(status.m_hp == 0) { fd->end = true; } +} +//------------------------------------------------------------------------------ +// implementation of one attack by an assault card, against either an enemy +// assault card, the first enemy wall, or the enemy commander. +struct PerformAttack +{ + Field* fd; + CardStatus* att_status; + CardStatus* def_status; + unsigned att_dmg; + bool killed_by_attack; + + PerformAttack(Field* fd_, CardStatus* att_status_, CardStatus* def_status_) : + fd(fd_), att_status(att_status_), def_status(def_status_), att_dmg(0), killed_by_attack(false) + {} + + template + void op() + { + if(attack_power(att_status) > 0) + { + const bool fly_check(!def_status->m_card->m_flying || att_status->m_card->m_flying || att_status->m_card->m_antiair > 0 || fd->flip()); + if(fly_check) // unnecessary check for structures, commander -> fix later ? + { + // Evaluation order: + // assaults only: fly check + // assaults only: immobilize + // deal damage + // assaults only: (siphon, poison, disease) + // oa: poison, disease, assaults only: berserk, skills + // counter, berserk + // assaults only: (crush, leech if still alive) + // check regeneration + att_dmg = calculate_attack_damage(); + if(att_dmg > 0) + { + immobilize(); + attack_damage(); + siphon_poison_disease(); + } + oa(); + if(att_dmg > 0) + { + if(att_status->m_hp > 0) + { + counter_berserk(); + } + crush_leech(); + } + prepend_on_death(fd); + resolve_skill(fd); + check_regeneration(fd); + } + } + } + + template + unsigned calculate_attack_damage() + { + return(attack_damage_against_non_assault(fd, *att_status)); + } + + template + void immobilize() {} + + template + void attack_damage() + { + remove_hp(fd, *def_status, att_dmg); + killed_by_attack = def_status->m_hp == 0; + _DEBUG_MSG("%s attack damage %u\n", status_description(att_status).c_str(), att_dmg); + } + + template + void siphon_poison_disease() {} + + template + void oa() + { + if(def_status->m_card->m_poison_oa > 0) + { + apply_poison(att_status, def_status->m_card->m_poison_oa); + } + if(def_status->m_card->m_disease_oa) + { + att_status->m_diseased = true; + } + oa_berserk(); + for(auto& oa_skill: def_status->m_card->m_skills_attacked) + { + fd->skill_queue.emplace_back(def_status, oa_skill); + resolve_skill(fd); + } + } + + template + void oa_berserk() {} + + template + void counter_berserk() + { + if(def_status->m_card->m_counter > 0) + { + unsigned counter_dmg(counter_damage(att_status, def_status)); + remove_hp(fd, *att_status, counter_dmg); + _DEBUG_MSG("%s counter %u by %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); + } + att_status->m_berserk += att_status->m_card->m_berserk; + } + + template + void crush_leech() {} +}; + +template<> +unsigned PerformAttack::calculate_attack_damage() +{ + return(attack_damage_against_assault(fd, *att_status, *def_status)); +} + +template<> +void PerformAttack::immobilize() +{ + if(att_status->m_card->m_immobilize) + { + def_status->m_immobilized |= fd->flip(); + } +} + +template<> +void PerformAttack::attack_damage() +{ + remove_commander_hp(fd, *def_status, att_dmg); + _DEBUG_MSG("%s attack damage %u to commander; commander hp %u\n", status_description(att_status).c_str(), att_dmg, fd->tip->commander.m_hp); +} + +template<> +void PerformAttack::siphon_poison_disease() +{ + if(att_status->m_card->m_siphon > 0) + { + add_hp(&fd->tap->commander, std::min(att_dmg, att_status->m_card->m_siphon)); + _DEBUG_MSG(" \033[1;32m%s siphon %u; hp %u\033[0m\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_siphon), fd->tap->commander.m_hp); + } + if(att_status->m_card->m_poison > 0) + { + apply_poison(def_status, att_status->m_card->m_poison); + } + if(att_status->m_card->m_disease) + { + def_status->m_diseased = true; + } +} + +template<> +void PerformAttack::oa_berserk() { def_status->m_berserk += def_status->m_card->m_berserk_oa; } + +template<> +void PerformAttack::crush_leech() +{ + if(att_status->m_card->m_crush > 0 && killed_by_attack) + { + CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall + if (def_status != nullptr) + { + remove_hp(fd, *def_status, att_status->m_card->m_crush); + _DEBUG_MSG("%s crush %u; wall %s hp %u\n", status_description(att_status).c_str(), att_status->m_card->m_crush, status_description(def_status).c_str(), def_status->m_hp); + } + else + { + remove_commander_hp(fd, fd->tip->commander, att_status->m_card->m_crush); + _DEBUG_MSG("%s crush %u; commander hp %u\n", status_description(att_status).c_str(), att_status->m_card->m_crush, fd->tip->commander.m_hp); + } + } + if(att_status->m_card->m_leech > 0 && att_status->m_hp > 0 && !att_status->m_diseased) + { + add_hp(att_status, std::min(att_dmg, att_status->m_card->m_leech)); + _DEBUG_MSG("%s leech %u; hp: %u.\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech), att_status->m_hp); + } +} + +// General attack phase by the currently evaluated assault, taking into accounts exotic stuff such as flurry,swipe,etc. +void attack_phase(Field* fd) +{ + CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card + Storage& def_assaults(fd->tip->assaults); + unsigned num_attacks(att_status->m_card->m_flurry > 0 && fd->flip() ? att_status->m_card->m_flurry + 1 : 1); + for(unsigned attack_index(0); attack_index < num_attacks && att_status->m_hp > 0 && fd->tip->commander.m_hp > 0; ++attack_index) + { + // 3 possibilities: + // - 1. attack against the assault in front + // - 2. swipe attack the assault in front and adjacent assaults if any + // - 3. attack against the commander or walls (if there is no assault or if the attacker has the fear attribute) + // Check if attack mode is 1. or 2. (there is a living assault card in front, and no fear) + if(alive_assault(def_assaults, fd->current_ci) && !att_status->m_card->m_fear) + { + // attack mode 1. + if(!att_status->m_card->m_swipe) + { + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); + } + // attack mode 2. + else + { + // attack the card on the left + if(alive_assault(def_assaults, fd->current_ci - 1)) + { + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(); + } + // stille alive? attack the card in front + if(fd->tip->commander.m_hp > 0 && att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci)) + { + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); + } + // still alive? attack the card on the right + if(fd->tip->commander.m_hp > 0 && att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci + 1)) + { + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(); + } + } + } + // attack mode 3. + else + { + CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall + if(def_status != nullptr) + { + PerformAttack{fd, att_status, def_status}.op(); + } + else + { + PerformAttack{fd, att_status, &fd->tip->commander}.op(); + } + } + } +} + +//---------------------- $65 active skills implementation ---------------------- +unsigned strike_damage(CardStatus* target, unsigned v) +{ + return(safe_minus(v + target->m_enfeebled, target->m_protected)); +} + +template< + bool C + , typename T1 + , typename T2 + > +struct if_ +{ + typedef T1 type; +}; + +template< + typename T1 + , typename T2 + > +struct if_ +{ + typedef T2 type; +}; + +template +inline bool skill_predicate(CardStatus* c) +{ assert(false); return(false); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ + if(c->m_hp > 0 && (c->m_delay == 0 || c->blitz) && !c->m_jammed && !c->m_frozen) + { + for(auto& s: c->m_card->m_skills) + { + // Any quantifiable skill except augment + if(std::get<1>(s) > 0 && std::get<0>(s) != augment && std::get<0>(s) != augment_all && std::get<0>(s) != summon) { return(true); } + } + } + return(false); +} + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_delay <= 1 && c->m_hp > 0 && !c->m_chaos); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ + return(c->m_hp > 0 && ( + c->m_chaos || + c->m_diseased || + c->m_enfeebled > 0 || + (c->m_frozen && c->m_delay == 0) || + c->m_jammed || + c->m_poisoned + )); +} + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_hp > 0); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_hp > 0 && !c->m_frozen); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_faction != bloodthirsty); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_delay <= 1 && c->m_hp > 0 && !c->m_jammed); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_hp > 0); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_hp > 0); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return((c->m_delay == 0 || c->blitz) && c->m_hp > 0 && !c->m_jammed && !c->m_frozen && !c->m_immobilized); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_delay > 0); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_hp > 0); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_hp > 0); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } + +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_delay <= 1 && c->m_hp > 0 && attack_power(c) > 0 && !c->m_jammed && !c->m_frozen && !c->m_immobilized); } + +template +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ assert(false); } + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_augmented += v; +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_chaos = true; +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_chaos = false; + c->m_diseased = false; + c->m_enfeebled = 0; + c->m_frozen = false; + c->m_immobilized = false; + c->m_jammed = false; + c->m_poisoned = 0; +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_enfeebled += v; +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_frozen = true; +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + add_hp(c, v); +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_faction = bloodthirsty; + c->m_infused = true; + c->infused_skills.clear(); + for(auto& skill: c->m_card->m_skills) + { + c->infused_skills.emplace_back(std::get<0>(skill), std::get<1>(skill), std::get<2>(skill) == allfactions ? allfactions : bloodthirsty); + } +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_jammed = fd->flip(); +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_protected += v; +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_rallied += v; +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_delay = safe_minus(c->m_delay, v); +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + _DEBUG_MSG(" \033[1;31mshock %u. hp %u -> %u.\033[0m", v, c->m_hp, safe_minus(c->m_hp, v)); + c->m_hp = safe_minus(c->m_hp, v); +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + _DEBUG_MSG(" hp %u -> %u.", c->m_hp, safe_minus(c->m_hp, v)); + remove_hp(fd, *c, v); +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + remove_hp(fd, *c, strike_damage(c, v)); +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + add_hp(c, v); +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_weakened += v; +} + +template +inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) +{ + unsigned array_head{0}; + if(std::get<2>(s) == allfactions) + { + for(auto card: cards) + { + if(skill_predicate(card)) + { + fd->selection_array[array_head] = card; + ++array_head; + } + } + } + else + { + for(auto card: cards) + { + if(card->m_faction == std::get<2>(s) && + skill_predicate(card)) + { + fd->selection_array[array_head] = card; + ++array_head; + } + } + } + return(array_head); +} + +template +inline unsigned select_rally_like(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) +{ + unsigned array_head{0}; + unsigned card_index(fd->current_phase == Field::assaults_phase ? fd->current_ci : 0); + if(std::get<2>(s) == allfactions) + { + for(; card_index < cards.size(); ++card_index) + { + if(skill_predicate(cards[card_index])) + { + fd->selection_array[array_head] = cards[card_index]; + ++array_head; + } + } + } + else + { + for(; card_index < cards.size(); ++card_index) + { + if(cards[card_index]->m_faction == std::get<2>(s) && + skill_predicate(cards[card_index])) + { + fd->selection_array[array_head] = cards[card_index]; + ++array_head; + } + } + } + return(array_head); +} + +template<> +inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) +{ + return(select_rally_like(fd, src_status, cards, s)); +} + +template<> +inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) +{ + return(select_rally_like(fd, src_status, cards, s)); +} + +template<> +inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) +{ + // mimiced supply by a structure, etc ? + if(!(src_status && src_status->m_card->m_type == CardType::assault)) { return(0); } + unsigned array_head{0}; + const unsigned min_index(src_status->m_index - (src_status->m_index == 0 ? 0 : 1)); + const unsigned max_index(src_status->m_index + (src_status->m_index == cards.size() - 1 ? 0 : 1)); + for(unsigned card_index(min_index); card_index <= max_index; ++card_index) + { + if(skill_predicate(cards[card_index])) + { + fd->selection_array[array_head] = cards[card_index]; + ++array_head; + } + } + return(array_head); +} + +inline unsigned select_infuse(Field* fd, const SkillSpec& s) +{ + unsigned array_head{0}; + // Select candidates among attacker's assaults + for(auto card_status: fd->tap->assaults.m_indirect) + { + if(skill_predicate(card_status)) + { + fd->selection_array[array_head] = card_status; + ++array_head; + } + } + // Select candidates among defender's assaults + for(auto card_status: fd->tip->assaults.m_indirect) + { + if(skill_predicate(card_status)) + { + fd->selection_array[array_head] = card_status; + ++array_head; + } + } + return(array_head); +} + +inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src_status) +{ + return(fd->players[src_status ? (src_status->m_chaos ? src_status->m_player : opponent(src_status->m_player)) : fd->tipi]->assaults.m_indirect); +} + +inline std::vector& skill_targets_allied_assault(Field* fd, CardStatus* src_status) +{ + return(fd->players[src_status ? src_status->m_player : fd->tapi]->assaults.m_indirect); +} + +inline std::vector& skill_targets_hostile_structure(Field* fd, CardStatus* src_status) +{ + return(fd->players[src_status ? (src_status->m_chaos ? src_status->m_player : opponent(src_status->m_player)) : fd->tipi]->structures.m_indirect); +} + +inline std::vector& skill_targets_allied_structure(Field* fd, CardStatus* src_status) +{ + return(fd->players[src_status ? src_status->m_player : fd->tapi]->structures.m_indirect); +} + +template +std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ + std::cout << "skill_targets: Error: no specialization for " << skill_names[skill] << "\n"; + assert(false); +} + +template<> inline std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_hostile_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_hostile_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_hostile_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_hostile_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_hostile_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_hostile_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_hostile_assault(fd, src_status)); } + +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_hostile_structure(fd, src_status)); } + +template +void maybeTriggerRegen(Field* fd) +{ +} + +template<> +void maybeTriggerRegen(Field* fd) +{ + fd->skill_queue.emplace_front(nullptr, std::make_tuple(trigger_regen, 0, allfactions)); +} + +template +CardStatus* get_target_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + std::vector& cards(skill_targets(fd, src_status)); + unsigned array_head{select_fast(fd, src_status, cards, s)}; + if(array_head > 0) + { + unsigned rand_index(fd->rand(0, array_head - 1)); + CardStatus* c(fd->selection_array[rand_index]); + // intercept + if(src_status && !src_status->m_chaos) + { + CardStatus* intercept_card(nullptr); + if(rand_index > 0) + { + CardStatus* left_status(fd->selection_array[rand_index-1]); + if(left_status->m_card->m_intercept && left_status->m_index == c->m_index-1) + { + intercept_card = left_status; + } + } + if(rand_index+1 < array_head && !intercept_card) + { + CardStatus* right_status(fd->selection_array[rand_index+1]); + if(right_status->m_card->m_intercept && right_status->m_index == c->m_index+1) + { + intercept_card = right_status; + } + } + if(intercept_card) { c = intercept_card; } + } + return(c); + } + return(nullptr); +} + +template +void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + // null status = action card + CardStatus* c(get_target_hostile_fast(fd, src_status, s)); + if(c) + { + // evade + if(!c->m_card->m_evade || fd->flip()) + { + _DEBUG_MSG("%s (%u) from %s on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str(), status_description(c).c_str()); + // skill + perform_skill(fd, c, std::get<1>(s)); + // payback + if(c->m_card->m_payback && + src_status && + src_status->m_card->m_type == CardType::assault && + !src_status->m_chaos && + src_status->m_hp > 0 && + fd->flip()) + { + // payback evade + if(skill_predicate(src_status) && + (!src_status->m_card->m_evade || fd->flip())) + { + _DEBUG_MSG("Payback (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); + // payback skill + perform_skill(fd, src_status, std::get<1>(s)); + } + } + } + } + maybeTriggerRegen::T>(fd); + prepend_on_death(fd); +} + +template +void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + std::vector& cards(skill_targets(fd, src_status)); + unsigned array_head{select_fast(fd, src_status, cards, s)}; + if(array_head > 0) + { + CardStatus* c(fd->selection_array[fd->rand(0, array_head - 1)]); + _DEBUG_MSG(" \033[1;34m%s: %s on %s\033[0m", status_description(src_status).c_str(), skill_description(s).c_str(), status_description(c).c_str()); + perform_skill(fd, c, std::get<1>(s)); + _DEBUG_MSG("\n"); + if(c->m_card->m_tribute && + src_status && + src_status->m_card->m_type == CardType::assault && + src_status != c && + src_status->m_hp > 0 && + fd->flip()) + { + if(skill_predicate(src_status)) + { + _DEBUG_MSG("Tribute (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); + perform_skill(fd, src_status, std::get<1>(s)); + } + } + } +} + +template +void perform_global_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + std::vector& cards(skill_targets(fd, src_status)); + unsigned array_head{select_fast(fd, src_status, cards, s)}; + unsigned payback_count(0); + for(unsigned s_index(0); s_index < array_head; ++s_index) + { + CardStatus* c(fd->selection_array[s_index]); + if(!c->m_card->m_evade || fd->flip()) + { + _DEBUG_MSG("%s (%u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), c->m_card->m_name.c_str()); + perform_skill(fd, c, std::get<1>(s)); + // payback + if(c->m_card->m_payback && + src_status && + src_status->m_card->m_type == CardType::assault && + !src_status->m_chaos && + src_status->m_hp > 0 && + fd->flip()) + { + ++payback_count; + } + } + } + for(unsigned i(0); i < payback_count && skill_predicate(src_status); ++i) + { + if((!src_status->m_card->m_evade || fd->flip())) + { + _DEBUG_MSG("Payback (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); + perform_skill(fd, src_status, std::get<1>(s)); + } + } + maybeTriggerRegen::T>(fd); + prepend_on_death(fd); +} + +template +void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + std::vector& cards(skill_targets(fd, src_status)); + unsigned array_head{select_fast(fd, src_status, cards, s)}; + for(unsigned s_index(0); s_index < array_head; ++s_index) + { + CardStatus* c(fd->selection_array[s_index]); + _DEBUG_MSG("%s (%u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), c->m_card->m_name.c_str()); + perform_skill(fd, c, std::get<1>(s)); + if(c->m_card->m_tribute && + src_status && + src_status->m_card->m_type == CardType::assault && + src_status != c && + src_status->m_hp > 0 && + fd->flip()) + { + if(skill_predicate(src_status)) + { + _DEBUG_MSG("Tribute (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); + perform_skill(fd, src_status, std::get<1>(s)); + } + } + } +} + +void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + unsigned array_head{0}; + // Select candidates among attacker's assaults + for(auto card_status: fd->players[src_status->m_player]->assaults.m_indirect) + { + if(skill_predicate(card_status)) + { + fd->selection_array[array_head] = card_status; + ++array_head; + } + } + // Select candidates among defender's assaults + for(auto card_status: fd->players[opponent(src_status->m_player)]->assaults.m_indirect) + { + if(skill_predicate(card_status)) + { + fd->selection_array[array_head] = card_status; + ++array_head; + } + } + if(array_head > 0) + { + CardStatus* c(fd->selection_array[fd->rand(0, array_head - 1)]); + // check evade for enemy assaults only + if(c->m_player == src_status->m_player || !c->m_card->m_evade || fd->flip()) + { + _DEBUG_MSG("%s on (%s).", skill_names[infuse].c_str(), c->m_card->m_name.c_str()); + perform_skill(fd, c, std::get<1>(s)); + _DEBUG_MSG("\n"); + } + } +} + +// a summoned card's on play skills seem to be evaluated before any other skills on the skill queue. +inline void prepend_skills(Field* fd, CardStatus* status) +{ + for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_played)) + { + fd->skill_queue.emplace_front(status, skill); + } +} +void summon_card(Field* fd, unsigned player, const Card* summoned) +{ + assert(summoned->m_type == CardType::assault || summoned->m_type == CardType::structure); + Hand* hand{fd->players[player]}; + if(hand->assaults.size() + hand->structures.size() < 100) + { + Storage* storage{summoned->m_type == CardType::assault ? &hand->assaults : &hand->structures}; + CardStatus& card_status(storage->add_back()); + card_status.set(summoned); + card_status.m_index = storage->size() - 1; + card_status.m_player = player; + _DEBUG_MSG("Summoned [%s] as %s %d\n", summoned->m_name.c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index); + prepend_skills(fd, &card_status); + if(card_status.m_card->m_blitz && + fd->players[opponent(player)]->assaults.size() > card_status.m_index && + fd->players[opponent(player)]->assaults[card_status.m_index].m_hp > 0 && + fd->players[opponent(player)]->assaults[card_status.m_index].m_delay == 0) + { + card_status.blitz = true; + } + } +} +void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + summon_card(fd, src_status ? src_status->m_player : fd->tapi, fd->cards.by_id(std::get<1>(s))); +} + +void perform_trigger_regen(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + check_regeneration(fd); +} + +void perform_shock(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + _DEBUG_MSG("Performing shock on (%s).", fd->tip->commander.m_card->m_name.c_str()); + perform_skill(fd, &fd->tip->commander, std::get<1>(s)); + _DEBUG_MSG("\n"); +} + +void perform_supply(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + perform_global_allied_fast(fd, src_status, s); +} + +// Special rules for mimic : +// cannot mimic mimic, +// structures cannot mimic supply, +// and is not affected by payback. +void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + // mimic cannot be triggered by anything. So it should be the only skill in the unresolved skill table. + // so we can probably clear it safely. This is necessary, because mimic calls resolve_skill as well (infinite loop). + fd->skill_queue.clear(); + CardStatus* c(get_target_hostile_fast(fd, src_status, s)); + if(c) + { + _DEBUG_MSG("%s on (%s)\n", skill_names[std::get<0>(s)].c_str(), c->m_card->m_name.c_str()); + for(auto skill: c->m_card->m_skills) + { + if(src_status && src_status->m_card->m_type == CardType::assault && src_status->m_hp == 0) + { break; } + if(std::get<0>(skill) != mimic && + (std::get<0>(skill) != supply || (src_status && src_status->m_card->m_type == CardType::assault))) + { + SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions); + fd->skill_queue.emplace_back(src_status, src_status && src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); + resolve_skill(fd); + check_regeneration(fd); + } + } + } +} +//------------------------------------------------------------------------------ +void fill_skill_table() +{ + skill_table[augment] = perform_targetted_allied_fast; + skill_table[augment_all] = perform_global_allied_fast; + skill_table[chaos] = perform_targetted_hostile_fast; + skill_table[chaos_all] = perform_global_hostile_fast; + skill_table[cleanse] = perform_targetted_allied_fast; + skill_table[cleanse_all] = perform_global_allied_fast; + skill_table[enfeeble] = perform_targetted_hostile_fast; + skill_table[enfeeble_all] = perform_global_hostile_fast; + skill_table[freeze] = perform_targetted_hostile_fast; + skill_table[freeze_all] = perform_global_hostile_fast; + skill_table[heal] = perform_targetted_allied_fast; + skill_table[heal_all] = perform_global_allied_fast; + skill_table[infuse] = perform_infuse; + skill_table[jam] = perform_targetted_hostile_fast; + skill_table[jam_all] = perform_global_hostile_fast; + skill_table[mimic] = perform_mimic; + skill_table[protect] = perform_targetted_allied_fast; + skill_table[protect_all] = perform_global_allied_fast; + skill_table[rally] = perform_targetted_allied_fast; + skill_table[rally_all] = perform_global_allied_fast; + skill_table[rush] = perform_targetted_allied_fast; + skill_table[shock] = perform_shock; + skill_table[siege] = perform_targetted_hostile_fast; + skill_table[siege_all] = perform_global_hostile_fast; + skill_table[supply] = perform_supply; + skill_table[strike] = perform_targetted_hostile_fast; + skill_table[strike_all] = perform_global_hostile_fast; + skill_table[summon] = perform_summon; + skill_table[trigger_regen] = perform_trigger_regen; + skill_table[weaken] = perform_targetted_hostile_fast; + skill_table[weaken_all] = perform_global_hostile_fast; +} diff --git a/sim.h b/sim.h new file mode 100644 index 00000000..a11dbbf0 --- /dev/null +++ b/sim.h @@ -0,0 +1,204 @@ +#ifndef SIM_H_INCLUDED +#define SIM_H_INCLUDED + +#include +#include +#include +#include +#include + +#include "tyrant.h" + +class Card; +class Cards; +class DeckIface; +class Field; + +extern bool debug_print; +extern bool debug_line; +extern unsigned turn_limit; + +void fill_skill_table(); +unsigned play(Field* fd); +// Pool-based indexed storage. +//---------------------- Pool-based indexed storage ---------------------------- +template +class Storage +{ +public: + typedef typename std::vector::size_type size_type; + typedef T value_type; + Storage(size_type size) : + m_pool(sizeof(T)) + { + m_indirect.reserve(size); + } + + inline T& operator[](size_type i) + { + return(*m_indirect[i]); + } + + inline T& add_back() + { + m_indirect.emplace_back((T*) m_pool.malloc()); + return(*m_indirect.back()); + } + + template + void remove(Pred p) + { + size_type head(0); + for(size_type current(0); current < m_indirect.size(); ++current) + { + if(p((*this)[current])) + { + m_pool.free(m_indirect[current]); + } + else + { + if(current != head) + { + m_indirect[head] = m_indirect[current]; + } + ++head; + } + } + m_indirect.erase(m_indirect.begin() + head, m_indirect.end()); + } + + void reset() + { + for(auto index: m_indirect) + { + m_pool.free(index); + } + m_indirect.clear(); + } + + inline size_type size() const + { + return(m_indirect.size()); + } + + std::vector m_indirect; + boost::pool<> m_pool; +}; +//------------------------------------------------------------------------------ +struct CardStatus +{ + const Card* m_card; + unsigned m_index; + unsigned m_player; + unsigned m_augmented; + unsigned m_berserk; + bool blitz; + bool m_chaos; + unsigned m_delay; + bool m_diseased; + unsigned m_enfeebled; + Faction m_faction; + bool m_frozen; + unsigned m_hp; + bool m_immobilized; + bool m_infused; + std::vector infused_skills; + bool m_jammed; + unsigned m_poisoned; + unsigned m_protected; + unsigned m_rallied; + unsigned m_weakened; + + CardStatus() {} + CardStatus(const Card* card); + + void set(const Card* card); + void set(const Card& card); +}; +//------------------------------------------------------------------------------ +// Represents a particular draw from a deck. +// Persistent object: call reset to get a new draw. +class Hand +{ +public: + + Hand(DeckIface* deck_) : + deck(deck_), + assaults(15), + structures(15) + { + } + + void reset(std::mt19937& re); + + DeckIface* deck; + CardStatus commander; + Storage assaults; + Storage structures; +}; +//------------------------------------------------------------------------------ +// struct Field is the data model of a battle: +// an attacker and a defender deck, list of assaults and structures, etc. +class Field +{ +public: + bool end; + std::mt19937& re; + const Cards& cards; + // players[0]: the attacker, players[1]: the defender + std::array players; + unsigned tapi; // current turn's active player index + unsigned tipi; // and inactive + Hand* tap; + Hand* tip; + std::array selection_array; + unsigned turn; + gamemode_t gamemode; + // With the introduction of on death skills, a single skill can trigger arbitrary many skills. + // They are stored in this, and cleared after all have been performed. + std::deque > skill_queue; + std::vector killed_with_on_death; + std::vector killed_with_regen; + enum phase + { + playcard_phase, + commander_phase, + structures_phase, + assaults_phase + }; + // the current phase of the turn: starts with playcard_phase, then commander_phase, structures_phase, and assaults_phase + phase current_phase; + // the index of the card being evaluated in the current phase. + // Meaningless in playcard_phase, + // otherwise is the index of the current card in players->structures or players->assaults + unsigned current_ci; + + Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t _gamemode) : + end{false}, + re(re_), + cards(cards_), + players{{&hand1, &hand2}}, + turn(1), + gamemode(_gamemode) + { + } + + inline unsigned rand(unsigned x, unsigned y) + { + return(std::uniform_int_distribution(x, y)(re)); + } + + inline unsigned flip() + { + return(this->rand(0,1)); + } + + template + inline T& random_in_vector(std::vector& v) + { + assert(v.size() > 0); + return(v[this->rand(0, v.size() - 1)]); + } +}; + +#endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 0cebe720..279f58e2 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -18,16 +18,11 @@ #include #include #include -#include -#include #include -#include #include #include #include #include -#include -#include #include #include #include @@ -37,1769 +32,27 @@ #include "cards.h" #include "deck.h" #include "read.h" +#include "sim.h" #include "tyrant.h" #include "xml.h" //#include "timer.hpp" using namespace std::placeholders; -//---------------------- $00 general stuff ------------------------------------- -template -std::string to_string(T val) -{ - std::stringstream s; - s << val; - return s.str(); -} -//---------------------- Debugging stuff --------------------------------------- -bool debug_print(false); -bool debug_line(false); -#ifndef NDEBUG -#define _DEBUG_MSG(format, args...) \ - { \ - if(debug_print) \ - { \ - char* format2 = new char[strlen(format) + (8 + 1)*sizeof(char)]; \ - if(debug_line) { strcpy(format2, "%i - "); } \ - else { strcpy(format2, ""); } \ - strcat(format2, format); \ - if(debug_line) { printf(format2, __LINE__ , ##args); } \ - else { printf(format2, ##args); } \ - delete[] format2; \ - std::cout << std::flush; \ - } \ - } -#else -#define _DEBUG_MSG(format, args...) -#endif -// Pool-based indexed storage. -//---------------------- Pool-based indexed storage ---------------------------- -template -class Storage -{ -public: - typedef typename std::vector::size_type size_type; - typedef T value_type; - Storage(size_type size) : - m_pool(sizeof(T)) - { - m_indirect.reserve(size); - } - - inline T& operator[](size_type i) - { - return(*m_indirect[i]); - } - - inline T& add_back() - { - m_indirect.emplace_back((T*) m_pool.malloc()); - return(*m_indirect.back()); - } - - template - void remove(Pred p) - { - size_type head(0); - for(size_type current(0); current < m_indirect.size(); ++current) - { - if(p((*this)[current])) - { - m_pool.free(m_indirect[current]); - } - else - { - if(current != head) - { - m_indirect[head] = m_indirect[current]; - } - ++head; - } - } - m_indirect.erase(m_indirect.begin() + head, m_indirect.end()); - } - - void reset() - { - for(auto index: m_indirect) - { - m_pool.free(index); - } - m_indirect.clear(); - } - - inline size_type size() const - { - return(m_indirect.size()); - } - - std::vector m_indirect; - boost::pool<> m_pool; -}; -//--------------------- $10 data model: card properties, etc ------------------- -Cards globalCards; -//------------------------------------------------------------------------------ -struct CardStatus -{ - const Card* m_card; - unsigned m_index; - unsigned m_player; - unsigned m_augmented; - unsigned m_berserk; - bool blitz; - bool m_chaos; - unsigned m_delay; - bool m_diseased; - unsigned m_enfeebled; - Faction m_faction; - bool m_frozen; - unsigned m_hp; - bool m_immobilized; - bool m_infused; - std::vector infused_skills; - bool m_jammed; - unsigned m_poisoned; - unsigned m_protected; - unsigned m_rallied; - unsigned m_weakened; - - CardStatus() {} - - CardStatus(const Card* card) : - m_card(card), - m_index(0), - m_player(0), - m_augmented(0), - m_berserk(0), - blitz(false), - m_chaos(false), - m_delay(card->m_delay), - m_diseased(false), - m_enfeebled(0), - m_faction(card->m_faction), - m_frozen(false), - m_hp(card->m_health), - m_immobilized(false), - m_infused(false), - m_jammed(false), - m_poisoned(0), - m_protected(0), - m_rallied(0), - m_weakened(0) - { - } - - inline void set(const Card* card) - { - this->set(*card); - } - - inline void set(const Card& card) - { - m_card = &card; - m_index = 0; - m_player = 0; - m_augmented = 0; - m_berserk = 0; - blitz = false; - m_chaos = false; - m_delay = card.m_delay; - m_diseased = false; - m_enfeebled = 0; - m_faction = card.m_faction; - m_frozen = false; - m_hp = card.m_health; - m_immobilized = false; - infused_skills.clear(); - m_infused = false; - m_jammed = false; - m_poisoned = 0; - m_protected = 0; - m_rallied = 0; - m_weakened = 0; - } -}; -//------------------------------------------------------------------------------ -std::string skill_description(const SkillSpec& s) -{ - return(skill_names[std::get<0>(s)] + - (std::get<2>(s) == allfactions ? "" : std::string(" ") + faction_names[std::get<2>(s)]) + - (std::get<1>(s) == 0 ? "" : std::string(" ") + to_string(std::get<1>(s)))); -} -//------------------------------------------------------------------------------ -std::string status_description(CardStatus* status) -{ - assert(status); - std::string desc; - switch(status->m_card->m_type) - { - case CardType::commander: desc = "Commander "; break; - case CardType::action: desc = "Action "; break; - case CardType::assault: desc = "A " + to_string(status->m_index) + " "; break; - case CardType::structure: desc = "S " + to_string(status->m_index) + " "; break; - } - desc += "[" + status->m_card->m_name + "]"; - return(desc); -} -//------------------------------------------------------------------------------ -void print_deck(DeckIface& deck) -{ - std::cout << "Deck:" << std::endl; - if(deck.commander) - { - std::cout << deck.commander->m_name << "\n"; - } - else - { - std::cout << "No commander\n"; - } - for(const Card* card: deck.cards) - { - std::cout << " " << card->m_name << "\n" << std::flush; - } -} -//------------------------------------------------------------------------------ -// Represents a particular draw from a deck. -// Persistent object: call reset to get a new draw. -class Hand -{ -public: - - Hand(DeckIface* deck_) : - deck(deck_), - assaults(15), - structures(15) - { - } - - void reset(std::mt19937& re) - { - assaults.reset(); - structures.reset(); - commander = CardStatus(deck->get_commander()); - deck->shuffle(re); - } - - DeckIface* deck; - CardStatus commander; - Storage assaults; - Storage structures; -}; -//---------------------- $40 Game rules implementation ------------------------- -// Everything about how a battle plays out, except the following: -// the implementation of the attack by an assault card is in the next section; -// the implementation of the active skills is in the section after that. -// struct Field is the data model of a battle: -// an attacker and a defender deck, list of assaults and structures, etc. -unsigned turn_limit{50}; -class Field -{ -public: - bool end; - std::mt19937& re; - const Cards& cards; - // players[0]: the attacker, players[1]: the defender - std::array players; - unsigned tapi; // current turn's active player index - unsigned tipi; // and inactive - Hand* tap; - Hand* tip; - std::array selection_array; - unsigned turn; - gamemode_t gamemode; - // With the introduction of on death skills, a single skill can trigger arbitrary many skills. - // They are stored in this, and cleared after all have been performed. - std::deque > skill_queue; - std::vector killed_with_on_death; - std::vector killed_with_regen; - enum phase - { - playcard_phase, - commander_phase, - structures_phase, - assaults_phase - }; - // the current phase of the turn: starts with playcard_phase, then commander_phase, structures_phase, and assaults_phase - phase current_phase; - // the index of the card being evaluated in the current phase. - // Meaningless in playcard_phase, - // otherwise is the index of the current card in players->structures or players->assaults - unsigned current_ci; - - Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t _gamemode) : - end{false}, - re(re_), - cards(cards_), - players{{&hand1, &hand2}}, - turn(1), - gamemode(_gamemode) - { - } - - inline unsigned rand(unsigned x, unsigned y) - { - return(std::uniform_int_distribution(x, y)(re)); - } - - inline unsigned flip() - { - return(this->rand(0,1)); - } - - template - inline T& random_in_vector(std::vector& v) - { - assert(v.size() > 0); - return(v[this->rand(0, v.size() - 1)]); - } -}; -//------------------------------------------------------------------------------ -inline unsigned opponent(unsigned player) -{ - return((player + 1) % 2); -} -//------------------------------------------------------------------------------ -void prepend_on_death(Field* fd) -{ - for(auto status: boost::adaptors::reverse(fd->killed_with_on_death)) - { - for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_died)) - { - _DEBUG_MSG("On death skill pushed in front %s %u %s\n", skill_names[std::get<0>(skill)].c_str(), std::get<1>(skill), faction_names[std::get<2>(skill)].c_str()); - fd->skill_queue.emplace_front(status, skill); - } - } - fd->killed_with_on_death.clear(); -} -//------------------------------------------------------------------------------ -void(*skill_table[num_skills])(Field*, CardStatus* src_status, const SkillSpec&); -void resolve_skill(Field* fd) -{ - while(!fd->skill_queue.empty()) - { - auto skill_instance(fd->skill_queue.front()); - auto& status(std::get<0>(skill_instance)); - auto& skill(std::get<1>(skill_instance)); - fd->skill_queue.pop_front(); - skill_table[std::get<0>(skill)](fd, status, skill); - } -} -//------------------------------------------------------------------------------ -void attack_phase(Field* fd); -SkillSpec augmented_skill(CardStatus* status, const SkillSpec& s) -{ - SkillSpec augmented_s = s; - if(std::get<0>(s) != augment && std::get<0>(s) != augment_all && std::get<0>(s) != summon) - { - std::get<1>(augmented_s) += status->m_augmented; - } - return(augmented_s); -} -void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills) -{ - assert(status); - assert(fd->skill_queue.size() == 0); - for(auto& skill: skills) - { - _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(skill).c_str()); - fd->skill_queue.emplace_back(status, status->m_augmented == 0 ? skill : augmented_skill(status, skill)); - resolve_skill(fd); - } -} -struct PlayCard -{ - const Card* card; - Field* fd; - CardStatus* status; - Storage* storage; - - PlayCard(const Card* card_, Field* fd_) : - card{card_}, - fd{fd_}, - status{nullptr}, - storage{nullptr} - {} - - template - bool op() - { - setStorage(); - placeCard(); - onPlaySkills(); - blitz(); - } - - // action - template - void setStorage() - { - } - - // assault + structure - template - void placeCard() - { - status = &storage->add_back(); - status->set(card); - status->m_index = storage->size() - 1; - status->m_player = fd->tapi; - if(fd->turn == 1 && fd->gamemode == tournament && status->m_delay > 0) - { - ++status->m_delay; - } - placeDebugMsg(); - } - - // assault + structure - template - void placeDebugMsg() - { - _DEBUG_MSG("Placed [%s] as %s %d\n", card->m_name.c_str(), cardtype_names[type].c_str(), storage->size() - 1); - } - - // all except assault: noop - template - void blitz() - { - } - - // assault + structure - template - void onPlaySkills() - { - for(auto& skill: card->m_skills_played) - { - fd->skill_queue.emplace_back(status, skill); - resolve_skill(fd); - } - } -}; -// assault -template <> -void PlayCard::setStorage() -{ - storage = &fd->tap->assaults; -} -// structure -template <> -void PlayCard::setStorage() -{ - storage = &fd->tap->structures; -} -// action -template <> -void PlayCard::placeCard() -{ -} -// assault -template <> -void PlayCard::blitz() -{ - if(card->m_blitz && fd->tip->assaults.size() > status->m_index && fd->tip->assaults[status->m_index].m_hp > 0 && fd->tip->assaults[status->m_index].m_delay == 0) - { - status->blitz = true; - } -} -// action -template <> -void PlayCard::onPlaySkills() -{ - for(auto& skill: card->m_skills) - { - fd->skill_queue.emplace_back(nullptr, skill); - resolve_skill(fd); - // Special case: enemy commander killed by a shock action card - if(fd->tip->commander.m_hp == 0) - { - _DEBUG_MSG("turn's defender dead.\n"); - fd->end = true; - break; - } - } - // Special case: recharge ability - if(card->m_recharge && fd->flip()) - { - fd->tap->deck->place_at_bottom(card); - } -} -//------------------------------------------------------------------------------ -void turn_start_phase(Field* fd); -void prepend_on_death(Field* fd); -// return value : 0 -> attacker wins, 1 -> defender wins -unsigned play(Field* fd) -{ - fd->players[0]->commander.m_player = 0; - fd->players[1]->commander.m_player = 1; - fd->tapi = fd->gamemode == surge ? 1 : 0; - fd->tipi = opponent(fd->tapi); - fd->tap = fd->players[fd->tapi]; - fd->tip = fd->players[fd->tipi]; - fd->end = false; - // Shuffle deck - while(fd->turn < turn_limit && !fd->end) - { - fd->current_phase = Field::playcard_phase; - // Initialize stuff, remove dead cards - _DEBUG_MSG("##### TURN %u #####\n", fd->turn); - turn_start_phase(fd); - // Special case: refresh on commander - if(fd->tip->commander.m_card->m_refresh && fd->tip->commander.m_hp > 0) - { - fd->tip->commander.m_hp = fd->tip->commander.m_card->m_health; - } - // Play a card - const Card* played_card(fd->tap->deck->next()); - if(played_card) - { - switch(played_card->m_type) - { - case CardType::action: - // end: handles commander death by shock - PlayCard(played_card, fd).op(); - break; - case CardType::assault: - PlayCard(played_card, fd).op(); - break; - case CardType::structure: - PlayCard(played_card, fd).op(); - break; - } - } - // Evaluate commander - fd->current_phase = Field::commander_phase; - evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); - // Evaluate structures - fd->current_phase = Field::structures_phase; - for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->structures.size(); ++fd->current_ci) - { - CardStatus& current_status(fd->tap->structures[fd->current_ci]); - if(current_status.m_delay == 0) - { - evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); - } - } - // Evaluate assaults - fd->current_phase = Field::assaults_phase; - for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->assaults.size(); ++fd->current_ci) - { - // ca: current assault - CardStatus& current_status(fd->tap->assaults[fd->current_ci]); - if((current_status.m_delay > 0 && !current_status.blitz) || current_status.m_hp == 0 || current_status.m_jammed || current_status.m_frozen) - { - //_DEBUG_MSG("! Assault %u (%s) hp: %u, jammed %u\n", card_index, current_status.m_card->m_name.c_str(), current_status.m_hp, current_status.m_jammed); - } - else - { - // Special case: check for split (tartarus swarm raid) - if(current_status.m_card->m_split && fd->tap->assaults.size() + fd->tap->structures.size() < 100) - { - CardStatus& status_split(fd->tap->assaults.add_back()); - status_split.set(current_status.m_card); - _DEBUG_MSG("Split assault %d (%s)\n", fd->tap->assaults.size() - 1, current_status.m_card->m_name.c_str()); - } - // Evaluate skills - // Special case: Gore Typhon's infuse - evaluate_skills(fd, ¤t_status, current_status.m_infused ? current_status.infused_skills : current_status.m_card->m_skills); - // Attack - if(!current_status.m_immobilized && current_status.m_hp > 0) - { - attack_phase(fd); - } - } - } - std::swap(fd->tapi, fd->tipi); - std::swap(fd->tap, fd->tip); - ++fd->turn; - } - // defender wins - if(fd->players[0]->commander.m_hp == 0) { _DEBUG_MSG("Defender wins.\n"); return(1); } - // attacker wins - if(fd->players[1]->commander.m_hp == 0) { _DEBUG_MSG("Attacker wins.\n"); return(0); } - if(fd->turn >= turn_limit) { return(1); } -} -//------------------------------------------------------------------------------ -// All the stuff that happens at the beginning of a turn, before a card is played -inline unsigned safe_minus(unsigned x, unsigned y) -{ - return(x - std::min(x, y)); -} -// returns true iff the card died. -bool remove_hp(Field* fd, CardStatus& status, unsigned dmg) -{ - assert(status.m_hp > 0); - status.m_hp = safe_minus(status.m_hp, dmg); - const bool just_died(status.m_hp == 0); - if(just_died) - { - _DEBUG_MSG("Card %u (%s) dead\n", status.m_index, status.m_card->m_name.c_str()); - if(status.m_card->m_skills_died.size() > 0) - { - fd->killed_with_on_death.push_back(&status); - } - if(status.m_card->m_regenerate) - { - fd->killed_with_regen.push_back(&status); - } - } - return(just_died); -} -inline bool is_it_dead(CardStatus& c) -{ - if(c.m_hp == 0) // yes it is - { - _DEBUG_MSG("Dead: %s\n", status_description(&c).c_str()); - return(true); - } - else { return(false); } // nope still kickin' -} -inline void remove_dead(Storage& storage) -{ - storage.remove(is_it_dead); -} -void check_regeneration(Field* fd) -{ - for(unsigned i(0); i < fd->killed_with_regen.size(); ++i) - { - CardStatus& status = *fd->killed_with_regen[i]; - if(status.m_hp == 0 && status.m_card->m_regenerate > 0 && !status.m_diseased) - { - status.m_hp = fd->flip() ? status.m_card->m_regenerate : 0; - } - if(status.m_hp > 0) - { - _DEBUG_MSG("Card %s regenerated, hp 0 -> %u\n", status.m_card->m_name.c_str(), status.m_hp); - } - - } - fd->killed_with_regen.clear(); -} -void turn_start_phase(Field* fd) -{ - remove_dead(fd->tap->assaults); - remove_dead(fd->tap->structures); - remove_dead(fd->tip->assaults); - remove_dead(fd->tip->structures); - // Active player's assault cards: - // update index - // remove enfeeble, protect; apply poison damage, reduce delay - { - auto& assaults(fd->tap->assaults); - for(unsigned index(0), end(assaults.size()); - index < end; - ++index) - { - CardStatus& status(assaults[index]); - status.m_index = index; - status.m_enfeebled = 0; - status.m_protected = 0; - remove_hp(fd, status, status.m_poisoned); - if(status.m_delay > 0 && !status.m_frozen) { --status.m_delay; } - } - } - // Active player's structure cards: - // update index - // reduce delay - { - auto& structures(fd->tap->structures); - for(unsigned index(0), end(structures.size()); - index < end; - ++index) - { - CardStatus& status(structures[index]); - status.m_index = index; - if(status.m_delay > 0) { --status.m_delay; } - } - } - // Defending player's assault cards: - // update index - // remove augment, chaos, freeze, immobilize, jam, rally, weaken, apply refresh - { - auto& assaults(fd->tip->assaults); - for(unsigned index(0), end(assaults.size()); - index < end; - ++index) - { - CardStatus& status(assaults[index]); - status.m_index = index; - status.m_augmented = 0; - status.blitz = false; - status.m_chaos = false; - status.m_frozen = false; - status.m_immobilized = false; - status.m_jammed = false; - status.m_rallied = 0; - status.m_weakened = 0; - if(status.m_card->m_refresh && !status.m_diseased) - { -#ifndef NDEBUG - if(status.m_hp < status.m_card->m_health) - { - _DEBUG_MSG("%u %s refreshed. hp %u -> %u.\n", index, status_description(&status).c_str(), status.m_hp, status.m_card->m_health); - } -#endif - status.m_hp = status.m_card->m_health; - } - } - } - // Defending player's structure cards: - // update index - // apply refresh - { - auto& structures(fd->tip->structures); - for(unsigned index(0), end(structures.size()); - index < end; - ++index) - { - CardStatus& status(structures[index]); - status.m_index = index; - if(status.m_card->m_refresh && status.m_hp < status.m_card->m_health) - { -#ifndef NDEBUG - _DEBUG_MSG("%s refreshed. hp %u -> %u.\n", index, status_description(&status).c_str(), status.m_hp, status.m_card->m_health); -#endif - status.m_hp = status.m_card->m_health; - } - } - } - // Perform on death skills (from cards killed by poison damage) - prepend_on_death(fd); - resolve_skill(fd); - // Regen from poison - check_regeneration(fd); -} -//---------------------- $50 attack by assault card implementation ------------- -inline void add_hp(CardStatus* target, unsigned v) -{ - target->m_hp = std::min(target->m_hp + v, target->m_card->m_health); -} -inline void apply_poison(CardStatus* target, unsigned v) -{ - target->m_poisoned = std::max(target->m_poisoned, v); -} -inline int attack_power(CardStatus* att) -{ - return(safe_minus(att->m_card->m_attack + att->m_berserk + att->m_rallied, att->m_weakened)); -} -// Counter damage dealt to the attacker (att) by defender (def) -// pre-condition: only valid if m_card->m_counter > 0 -inline unsigned counter_damage(CardStatus* att, CardStatus* def) -{ - assert(att->m_card->m_type == CardType::assault); - assert(def->m_card->m_type != CardType::action); - return(safe_minus(def->m_card->m_counter + att->m_enfeebled, att->m_protected)); -} -inline CardStatus* select_first_enemy_wall(Field* fd) -{ - for(unsigned i(0); i < fd->tip->structures.size(); ++i) - { - CardStatus& card(fd->tip->structures[i]); - if(card.m_card->m_wall && card.m_hp > 0) { return(&card); } - } - return(nullptr); -} - -inline unsigned valor_damage(Field* fd, CardStatus& status) -{ - if(status.m_card->m_valor > 0) - { - unsigned count_ta(0); - unsigned count_td(0); - for(unsigned i(0); i < fd->tap->assaults.size(); ++i) - { - if(fd->tap->assaults[i].m_hp > 0) { ++count_ta; } - } - for(unsigned i(0); i < fd->tip->assaults.size(); ++i) - { - if(fd->tip->assaults[i].m_hp > 0) { ++count_td; } - } - if(count_ta < count_td) { return(status.m_card->m_valor); } - } - return(0); -} - -inline unsigned attack_damage_against_non_assault(Field* fd, CardStatus& att_status) -{ - const Card& att_card(*att_status.m_card); - assert(att_card.m_type == CardType::assault); - // pre modifier damage - unsigned damage(attack_power(&att_status)); - // - if(damage > 0) - { - damage += valor_damage(fd, att_status); - } - return(damage); -} - -inline unsigned attack_damage_against_assault(Field* fd, CardStatus& att_status, CardStatus& def_status) -{ - const Card& att_card(*att_status.m_card); - const Card& def_card(*def_status.m_card); - assert(att_card.m_type == CardType::assault); - assert(def_card.m_type == CardType::assault); - // pre modifier damage - unsigned damage(attack_power(&att_status)); - // - if(damage > 0) - { - damage = safe_minus( - damage // pre-modifier damage - + valor_damage(fd, att_status) // valor - + def_status.m_enfeebled // enfeeble - + (def_card.m_flying ? att_card.m_antiair : 0) // anti-air - + (att_card.m_burst > 0 ? (def_status.m_hp == def_card.m_health ? att_card.m_burst : 0) : 0) // burst - // armor + protect + pierce - , safe_minus(def_card.m_armored + def_status.m_protected, att_card.m_pierce)); - } - return(damage); -} - -inline bool alive_assault(Storage& assaults, unsigned index) -{ - return(index >= 0 && assaults.size() > index && assaults[index].m_hp > 0); -} - -void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg) -{ - assert(status.m_hp > 0); - assert(status.m_card->m_type == CardType::commander); - status.m_hp = safe_minus(status.m_hp, dmg); - if(status.m_hp == 0) { fd->end = true; } -} -//------------------------------------------------------------------------------ -// implementation of one attack by an assault card, against either an enemy -// assault card, the first enemy wall, or the enemy commander. -struct PerformAttack -{ - Field* fd; - CardStatus* att_status; - CardStatus* def_status; - unsigned att_dmg; - bool killed_by_attack; - - PerformAttack(Field* fd_, CardStatus* att_status_, CardStatus* def_status_) : - fd(fd_), att_status(att_status_), def_status(def_status_), att_dmg(0), killed_by_attack(false) - {} - - template - void op() - { - if(attack_power(att_status) > 0) - { - const bool fly_check(!def_status->m_card->m_flying || att_status->m_card->m_flying || att_status->m_card->m_antiair > 0 || fd->flip()); - if(fly_check) // unnecessary check for structures, commander -> fix later ? - { - // Evaluation order: - // assaults only: fly check - // assaults only: immobilize - // deal damage - // assaults only: (siphon, poison, disease) - // oa: poison, disease, assaults only: berserk, skills - // counter, berserk - // assaults only: (crush, leech if still alive) - // check regeneration - att_dmg = calculate_attack_damage(); - if(att_dmg > 0) - { - immobilize(); - attack_damage(); - siphon_poison_disease(); - } - oa(); - if(att_dmg > 0) - { - if(att_status->m_hp > 0) - { - counter_berserk(); - } - crush_leech(); - } - prepend_on_death(fd); - resolve_skill(fd); - check_regeneration(fd); - } - } - } - - template - unsigned calculate_attack_damage() - { - return(attack_damage_against_non_assault(fd, *att_status)); - } - - template - void immobilize() {} - - template - void attack_damage() - { - remove_hp(fd, *def_status, att_dmg); - killed_by_attack = def_status->m_hp == 0; - _DEBUG_MSG("%s attack damage %u\n", status_description(att_status).c_str(), att_dmg); - } - - template - void siphon_poison_disease() {} - - template - void oa() - { - if(def_status->m_card->m_poison_oa > 0) - { - apply_poison(att_status, def_status->m_card->m_poison_oa); - } - if(def_status->m_card->m_disease_oa) - { - att_status->m_diseased = true; - } - oa_berserk(); - for(auto& oa_skill: def_status->m_card->m_skills_attacked) - { - fd->skill_queue.emplace_back(def_status, oa_skill); - resolve_skill(fd); - } - } - - template - void oa_berserk() {} - - template - void counter_berserk() - { - if(def_status->m_card->m_counter > 0) - { - unsigned counter_dmg(counter_damage(att_status, def_status)); - remove_hp(fd, *att_status, counter_dmg); - _DEBUG_MSG("%s counter %u by %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); - } - att_status->m_berserk += att_status->m_card->m_berserk; - } - - template - void crush_leech() {} -}; - -template<> -unsigned PerformAttack::calculate_attack_damage() -{ - return(attack_damage_against_assault(fd, *att_status, *def_status)); -} - -template<> -void PerformAttack::immobilize() -{ - if(att_status->m_card->m_immobilize) - { - def_status->m_immobilized |= fd->flip(); - } -} - -template<> -void PerformAttack::attack_damage() -{ - remove_commander_hp(fd, *def_status, att_dmg); - _DEBUG_MSG("%s attack damage %u to commander; commander hp %u\n", status_description(att_status).c_str(), att_dmg, fd->tip->commander.m_hp); -} - -template<> -void PerformAttack::siphon_poison_disease() -{ - if(att_status->m_card->m_siphon > 0) - { - add_hp(&fd->tap->commander, std::min(att_dmg, att_status->m_card->m_siphon)); - _DEBUG_MSG(" \033[1;32m%s siphon %u; hp %u\033[0m\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_siphon), fd->tap->commander.m_hp); - } - if(att_status->m_card->m_poison > 0) - { - apply_poison(def_status, att_status->m_card->m_poison); - } - if(att_status->m_card->m_disease) - { - def_status->m_diseased = true; - } -} - -template<> -void PerformAttack::oa_berserk() { def_status->m_berserk += def_status->m_card->m_berserk_oa; } - -template<> -void PerformAttack::crush_leech() -{ - if(att_status->m_card->m_crush > 0 && killed_by_attack) - { - CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall - if (def_status != nullptr) - { - remove_hp(fd, *def_status, att_status->m_card->m_crush); - _DEBUG_MSG("%s crush %u; wall %s hp %u\n", status_description(att_status).c_str(), att_status->m_card->m_crush, status_description(def_status).c_str(), def_status->m_hp); - } - else - { - remove_commander_hp(fd, fd->tip->commander, att_status->m_card->m_crush); - _DEBUG_MSG("%s crush %u; commander hp %u\n", status_description(att_status).c_str(), att_status->m_card->m_crush, fd->tip->commander.m_hp); - } - } - if(att_status->m_card->m_leech > 0 && att_status->m_hp > 0 && !att_status->m_diseased) - { - add_hp(att_status, std::min(att_dmg, att_status->m_card->m_leech)); - _DEBUG_MSG("%s leech %u; hp: %u.\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech), att_status->m_hp); - } -} - -// General attack phase by the currently evaluated assault, taking into accounts exotic stuff such as flurry,swipe,etc. -void attack_phase(Field* fd) -{ - CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card - Storage& def_assaults(fd->tip->assaults); - unsigned num_attacks(att_status->m_card->m_flurry > 0 && fd->flip() ? att_status->m_card->m_flurry + 1 : 1); - for(unsigned attack_index(0); attack_index < num_attacks && att_status->m_hp > 0 && fd->tip->commander.m_hp > 0; ++attack_index) - { - // 3 possibilities: - // - 1. attack against the assault in front - // - 2. swipe attack the assault in front and adjacent assaults if any - // - 3. attack against the commander or walls (if there is no assault or if the attacker has the fear attribute) - // Check if attack mode is 1. or 2. (there is a living assault card in front, and no fear) - if(alive_assault(def_assaults, fd->current_ci) && !att_status->m_card->m_fear) - { - // attack mode 1. - if(!att_status->m_card->m_swipe) - { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); - } - // attack mode 2. - else - { - // attack the card on the left - if(alive_assault(def_assaults, fd->current_ci - 1)) - { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(); - } - // stille alive? attack the card in front - if(fd->tip->commander.m_hp > 0 && att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci)) - { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); - } - // still alive? attack the card on the right - if(fd->tip->commander.m_hp > 0 && att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci + 1)) - { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(); - } - } - } - // attack mode 3. - else - { - CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall - if(def_status != nullptr) - { - PerformAttack{fd, att_status, def_status}.op(); - } - else - { - PerformAttack{fd, att_status, &fd->tip->commander}.op(); - } - } - } -} - -//---------------------- $65 active skills implementation ---------------------- -unsigned strike_damage(CardStatus* target, unsigned v) -{ - return(safe_minus(v + target->m_enfeebled, target->m_protected)); -} - -template< - bool C - , typename T1 - , typename T2 - > -struct if_ -{ - typedef T1 type; -}; - -template< - typename T1 - , typename T2 - > -struct if_ -{ - typedef T2 type; -}; - -template -inline bool skill_predicate(CardStatus* c) -{ assert(false); return(false); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ - if(c->m_hp > 0 && (c->m_delay == 0 || c->blitz) && !c->m_jammed && !c->m_frozen) - { - for(auto& s: c->m_card->m_skills) - { - // Any quantifiable skill except augment - if(std::get<1>(s) > 0 && std::get<0>(s) != augment && std::get<0>(s) != augment_all && std::get<0>(s) != summon) { return(true); } - } - } - return(false); -} - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_delay <= 1 && c->m_hp > 0 && !c->m_chaos); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ - return(c->m_hp > 0 && ( - c->m_chaos || - c->m_diseased || - c->m_enfeebled > 0 || - (c->m_frozen && c->m_delay == 0) || - c->m_jammed || - c->m_poisoned - )); -} - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_hp > 0); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_hp > 0 && !c->m_frozen); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_faction != bloodthirsty); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_delay <= 1 && c->m_hp > 0 && !c->m_jammed); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_hp > 0); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_hp > 0); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return((c->m_delay == 0 || c->blitz) && c->m_hp > 0 && !c->m_jammed && !c->m_frozen && !c->m_immobilized); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_delay > 0); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_hp > 0); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_hp > 0); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } - -template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_delay <= 1 && c->m_hp > 0 && attack_power(c) > 0 && !c->m_jammed && !c->m_frozen && !c->m_immobilized); } - -template -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ assert(false); } - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_augmented += v; -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_chaos = true; -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_chaos = false; - c->m_diseased = false; - c->m_enfeebled = 0; - c->m_frozen = false; - c->m_immobilized = false; - c->m_jammed = false; - c->m_poisoned = 0; -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_enfeebled += v; -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_frozen = true; -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - add_hp(c, v); -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_faction = bloodthirsty; - c->m_infused = true; - c->infused_skills.clear(); - for(auto& skill: c->m_card->m_skills) - { - c->infused_skills.emplace_back(std::get<0>(skill), std::get<1>(skill), std::get<2>(skill) == allfactions ? allfactions : bloodthirsty); - } -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_jammed = fd->flip(); -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_protected += v; -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_rallied += v; -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_delay = safe_minus(c->m_delay, v); -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - _DEBUG_MSG(" \033[1;31mshock %u. hp %u -> %u.\033[0m", v, c->m_hp, safe_minus(c->m_hp, v)); - c->m_hp = safe_minus(c->m_hp, v); -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - _DEBUG_MSG(" hp %u -> %u.", c->m_hp, safe_minus(c->m_hp, v)); - remove_hp(fd, *c, v); -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - remove_hp(fd, *c, strike_damage(c, v)); -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - add_hp(c, v); -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_weakened += v; -} - -template -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) -{ - unsigned array_head{0}; - if(std::get<2>(s) == allfactions) - { - for(auto card: cards) - { - if(skill_predicate(card)) - { - fd->selection_array[array_head] = card; - ++array_head; - } - } - } - else - { - for(auto card: cards) - { - if(card->m_faction == std::get<2>(s) && - skill_predicate(card)) - { - fd->selection_array[array_head] = card; - ++array_head; - } - } - } - return(array_head); -} - -template -inline unsigned select_rally_like(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) +//------------------------------------------------------------------------------ +void print_deck(DeckIface& deck) { - unsigned array_head{0}; - unsigned card_index(fd->current_phase == Field::assaults_phase ? fd->current_ci : 0); - if(std::get<2>(s) == allfactions) + std::cout << "Deck:" << std::endl; + if(deck.commander) { - for(; card_index < cards.size(); ++card_index) - { - if(skill_predicate(cards[card_index])) - { - fd->selection_array[array_head] = cards[card_index]; - ++array_head; - } - } + std::cout << deck.commander->m_name << "\n"; } else { - for(; card_index < cards.size(); ++card_index) - { - if(cards[card_index]->m_faction == std::get<2>(s) && - skill_predicate(cards[card_index])) - { - fd->selection_array[array_head] = cards[card_index]; - ++array_head; - } - } - } - return(array_head); -} - -template<> -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) -{ - return(select_rally_like(fd, src_status, cards, s)); -} - -template<> -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) -{ - return(select_rally_like(fd, src_status, cards, s)); -} - -template<> -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) -{ - // mimiced supply by a structure, etc ? - if(!(src_status && src_status->m_card->m_type == CardType::assault)) { return(0); } - unsigned array_head{0}; - const unsigned min_index(src_status->m_index - (src_status->m_index == 0 ? 0 : 1)); - const unsigned max_index(src_status->m_index + (src_status->m_index == cards.size() - 1 ? 0 : 1)); - for(unsigned card_index(min_index); card_index <= max_index; ++card_index) - { - if(skill_predicate(cards[card_index])) - { - fd->selection_array[array_head] = cards[card_index]; - ++array_head; - } - } - return(array_head); -} - -inline unsigned select_infuse(Field* fd, const SkillSpec& s) -{ - unsigned array_head{0}; - // Select candidates among attacker's assaults - for(auto card_status: fd->tap->assaults.m_indirect) - { - if(skill_predicate(card_status)) - { - fd->selection_array[array_head] = card_status; - ++array_head; - } - } - // Select candidates among defender's assaults - for(auto card_status: fd->tip->assaults.m_indirect) - { - if(skill_predicate(card_status)) - { - fd->selection_array[array_head] = card_status; - ++array_head; - } - } - return(array_head); -} - -inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src_status) -{ - return(fd->players[src_status ? (src_status->m_chaos ? src_status->m_player : opponent(src_status->m_player)) : fd->tipi]->assaults.m_indirect); -} - -inline std::vector& skill_targets_allied_assault(Field* fd, CardStatus* src_status) -{ - return(fd->players[src_status ? src_status->m_player : fd->tapi]->assaults.m_indirect); -} - -inline std::vector& skill_targets_hostile_structure(Field* fd, CardStatus* src_status) -{ - return(fd->players[src_status ? (src_status->m_chaos ? src_status->m_player : opponent(src_status->m_player)) : fd->tipi]->structures.m_indirect); -} - -inline std::vector& skill_targets_allied_structure(Field* fd, CardStatus* src_status) -{ - return(fd->players[src_status ? src_status->m_player : fd->tapi]->structures.m_indirect); -} - -template -std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ - std::cout << "skill_targets: Error: no specialization for " << skill_names[skill] << "\n"; - assert(false); -} - -template<> inline std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_structure(fd, src_status)); } - -template -void maybeTriggerRegen(Field* fd) -{ -} - -template<> -void maybeTriggerRegen(Field* fd) -{ - fd->skill_queue.emplace_front(nullptr, std::make_tuple(trigger_regen, 0, allfactions)); -} - -template -CardStatus* get_target_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - std::vector& cards(skill_targets(fd, src_status)); - unsigned array_head{select_fast(fd, src_status, cards, s)}; - if(array_head > 0) - { - unsigned rand_index(fd->rand(0, array_head - 1)); - CardStatus* c(fd->selection_array[rand_index]); - // intercept - if(src_status && !src_status->m_chaos) - { - CardStatus* intercept_card(nullptr); - if(rand_index > 0) - { - CardStatus* left_status(fd->selection_array[rand_index-1]); - if(left_status->m_card->m_intercept && left_status->m_index == c->m_index-1) - { - intercept_card = left_status; - } - } - if(rand_index+1 < array_head && !intercept_card) - { - CardStatus* right_status(fd->selection_array[rand_index+1]); - if(right_status->m_card->m_intercept && right_status->m_index == c->m_index+1) - { - intercept_card = right_status; - } - } - if(intercept_card) { c = intercept_card; } - } - return(c); - } - return(nullptr); -} - -template -void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - // null status = action card - CardStatus* c(get_target_hostile_fast(fd, src_status, s)); - if(c) - { - // evade - if(!c->m_card->m_evade || fd->flip()) - { - _DEBUG_MSG("%s (%u) from %s on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str(), status_description(c).c_str()); - // skill - perform_skill(fd, c, std::get<1>(s)); - // payback - if(c->m_card->m_payback && - src_status && - src_status->m_card->m_type == CardType::assault && - !src_status->m_chaos && - src_status->m_hp > 0 && - fd->flip()) - { - // payback evade - if(skill_predicate(src_status) && - (!src_status->m_card->m_evade || fd->flip())) - { - _DEBUG_MSG("Payback (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); - // payback skill - perform_skill(fd, src_status, std::get<1>(s)); - } - } - } - } - maybeTriggerRegen::T>(fd); - prepend_on_death(fd); -} - -template -void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - std::vector& cards(skill_targets(fd, src_status)); - unsigned array_head{select_fast(fd, src_status, cards, s)}; - if(array_head > 0) - { - CardStatus* c(fd->selection_array[fd->rand(0, array_head - 1)]); - _DEBUG_MSG(" \033[1;34m%s: %s on %s\033[0m", status_description(src_status).c_str(), skill_description(s).c_str(), status_description(c).c_str()); - perform_skill(fd, c, std::get<1>(s)); - _DEBUG_MSG("\n"); - if(c->m_card->m_tribute && - src_status && - src_status->m_card->m_type == CardType::assault && - src_status != c && - src_status->m_hp > 0 && - fd->flip()) - { - if(skill_predicate(src_status)) - { - _DEBUG_MSG("Tribute (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); - perform_skill(fd, src_status, std::get<1>(s)); - } - } - } -} - -template -void perform_global_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - std::vector& cards(skill_targets(fd, src_status)); - unsigned array_head{select_fast(fd, src_status, cards, s)}; - unsigned payback_count(0); - for(unsigned s_index(0); s_index < array_head; ++s_index) - { - CardStatus* c(fd->selection_array[s_index]); - if(!c->m_card->m_evade || fd->flip()) - { - _DEBUG_MSG("%s (%u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), c->m_card->m_name.c_str()); - perform_skill(fd, c, std::get<1>(s)); - // payback - if(c->m_card->m_payback && - src_status && - src_status->m_card->m_type == CardType::assault && - !src_status->m_chaos && - src_status->m_hp > 0 && - fd->flip()) - { - ++payback_count; - } - } - } - for(unsigned i(0); i < payback_count && skill_predicate(src_status); ++i) - { - if((!src_status->m_card->m_evade || fd->flip())) - { - _DEBUG_MSG("Payback (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); - perform_skill(fd, src_status, std::get<1>(s)); - } - } - maybeTriggerRegen::T>(fd); - prepend_on_death(fd); -} - -template -void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - std::vector& cards(skill_targets(fd, src_status)); - unsigned array_head{select_fast(fd, src_status, cards, s)}; - for(unsigned s_index(0); s_index < array_head; ++s_index) - { - CardStatus* c(fd->selection_array[s_index]); - _DEBUG_MSG("%s (%u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), c->m_card->m_name.c_str()); - perform_skill(fd, c, std::get<1>(s)); - if(c->m_card->m_tribute && - src_status && - src_status->m_card->m_type == CardType::assault && - src_status != c && - src_status->m_hp > 0 && - fd->flip()) - { - if(skill_predicate(src_status)) - { - _DEBUG_MSG("Tribute (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); - perform_skill(fd, src_status, std::get<1>(s)); - } - } - } -} - -void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - unsigned array_head{0}; - // Select candidates among attacker's assaults - for(auto card_status: fd->players[src_status->m_player]->assaults.m_indirect) - { - if(skill_predicate(card_status)) - { - fd->selection_array[array_head] = card_status; - ++array_head; - } - } - // Select candidates among defender's assaults - for(auto card_status: fd->players[opponent(src_status->m_player)]->assaults.m_indirect) - { - if(skill_predicate(card_status)) - { - fd->selection_array[array_head] = card_status; - ++array_head; - } - } - if(array_head > 0) - { - CardStatus* c(fd->selection_array[fd->rand(0, array_head - 1)]); - // check evade for enemy assaults only - if(c->m_player == src_status->m_player || !c->m_card->m_evade || fd->flip()) - { - _DEBUG_MSG("%s on (%s).", skill_names[infuse].c_str(), c->m_card->m_name.c_str()); - perform_skill(fd, c, std::get<1>(s)); - _DEBUG_MSG("\n"); - } - } -} - -// a summoned card's on play skills seem to be evaluated before any other skills on the skill queue. -inline void prepend_skills(Field* fd, CardStatus* status) -{ - for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_played)) - { - fd->skill_queue.emplace_front(status, skill); - } -} -void summon_card(Field* fd, unsigned player, const Card* summoned) -{ - assert(summoned->m_type == CardType::assault || summoned->m_type == CardType::structure); - Hand* hand{fd->players[player]}; - if(hand->assaults.size() + hand->structures.size() < 100) - { - Storage* storage{summoned->m_type == CardType::assault ? &hand->assaults : &hand->structures}; - CardStatus& card_status(storage->add_back()); - card_status.set(summoned); - card_status.m_index = storage->size() - 1; - card_status.m_player = player; - _DEBUG_MSG("Summoned [%s] as %s %d\n", summoned->m_name.c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index); - prepend_skills(fd, &card_status); - if(card_status.m_card->m_blitz && - fd->players[opponent(player)]->assaults.size() > card_status.m_index && - fd->players[opponent(player)]->assaults[card_status.m_index].m_hp > 0 && - fd->players[opponent(player)]->assaults[card_status.m_index].m_delay == 0) - { - card_status.blitz = true; - } + std::cout << "No commander\n"; } -} -void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - summon_card(fd, src_status ? src_status->m_player : fd->tapi, fd->cards.by_id(std::get<1>(s))); -} - -void perform_trigger_regen(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - check_regeneration(fd); -} - -void perform_shock(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - _DEBUG_MSG("Performing shock on (%s).", fd->tip->commander.m_card->m_name.c_str()); - perform_skill(fd, &fd->tip->commander, std::get<1>(s)); - _DEBUG_MSG("\n"); -} - -void perform_supply(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - perform_global_allied_fast(fd, src_status, s); -} - -// Special rules for mimic : -// cannot mimic mimic, -// structures cannot mimic supply, -// and is not affected by payback. -void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - // mimic cannot be triggered by anything. So it should be the only skill in the unresolved skill table. - // so we can probably clear it safely. This is necessary, because mimic calls resolve_skill as well (infinite loop). - fd->skill_queue.clear(); - CardStatus* c(get_target_hostile_fast(fd, src_status, s)); - if(c) + for(const Card* card: deck.cards) { - _DEBUG_MSG("%s on (%s)\n", skill_names[std::get<0>(s)].c_str(), c->m_card->m_name.c_str()); - for(auto skill: c->m_card->m_skills) - { - if(src_status && src_status->m_card->m_type == CardType::assault && src_status->m_hp == 0) - { break; } - if(std::get<0>(skill) != mimic && - (std::get<0>(skill) != supply || (src_status && src_status->m_card->m_type == CardType::assault))) - { - SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions); - fd->skill_queue.emplace_back(src_status, src_status && src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); - resolve_skill(fd); - check_regeneration(fd); - } - } + std::cout << " " << card->m_name << "\n" << std::flush; } } //------------------------------------------------------------------------------ @@ -2661,38 +914,7 @@ int main(int argc, char** argv) Decks decks; load_decks_xml(decks, cards); load_decks(decks, cards); - - skill_table[augment] = perform_targetted_allied_fast; - skill_table[augment_all] = perform_global_allied_fast; - skill_table[chaos] = perform_targetted_hostile_fast; - skill_table[chaos_all] = perform_global_hostile_fast; - skill_table[cleanse] = perform_targetted_allied_fast; - skill_table[cleanse_all] = perform_global_allied_fast; - skill_table[enfeeble] = perform_targetted_hostile_fast; - skill_table[enfeeble_all] = perform_global_hostile_fast; - skill_table[freeze] = perform_targetted_hostile_fast; - skill_table[freeze_all] = perform_global_hostile_fast; - skill_table[heal] = perform_targetted_allied_fast; - skill_table[heal_all] = perform_global_allied_fast; - skill_table[infuse] = perform_infuse; - skill_table[jam] = perform_targetted_hostile_fast; - skill_table[jam_all] = perform_global_hostile_fast; - skill_table[mimic] = perform_mimic; - skill_table[protect] = perform_targetted_allied_fast; - skill_table[protect_all] = perform_global_allied_fast; - skill_table[rally] = perform_targetted_allied_fast; - skill_table[rally_all] = perform_global_allied_fast; - skill_table[rush] = perform_targetted_allied_fast; - skill_table[shock] = perform_shock; - skill_table[siege] = perform_targetted_hostile_fast; - skill_table[siege_all] = perform_global_hostile_fast; - skill_table[supply] = perform_supply; - skill_table[strike] = perform_targetted_hostile_fast; - skill_table[strike_all] = perform_global_hostile_fast; - skill_table[summon] = perform_summon; - skill_table[trigger_regen] = perform_trigger_regen; - skill_table[weaken] = perform_targetted_hostile_fast; - skill_table[weaken_all] = perform_global_hostile_fast; + fill_skill_table(); if(argc <= 2) { From e05cdf59a70bb35d7b4e5b6bdcde4bdc7221c10f Mon Sep 17 00:00:00 2001 From: leftylink Date: Thu, 22 Nov 2012 11:03:50 -0500 Subject: [PATCH 013/406] deck: Use vector rather than boost::any_range Only vectors were being used anyway, and this reduces compilation time on netbook from 150s to 130s, because fewer headers are being included in deck.h. I think it's worth it. --- deck.cpp | 3 +-- deck.h | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/deck.cpp b/deck.cpp index 0478b15f..d98bee96 100644 --- a/deck.cpp +++ b/deck.cpp @@ -1,8 +1,7 @@ #include "deck.h" -#include // because of 1.51 bug. missing include in range/any_range.hpp ? #include -#include +#include #include "card.h" diff --git a/deck.h b/deck.h index 46a56525..cd9e14f1 100644 --- a/deck.h +++ b/deck.h @@ -1,11 +1,9 @@ #ifndef DECK_H_INCLUDED #define DECK_H_INCLUDED -#include // because of 1.51 bug. missing include in range/any_range.hpp ? -#include -#include #include #include +#include #include #include "cards.h" @@ -26,7 +24,7 @@ struct DeckIface {} DeckIface(const Card* commander_, - boost::any_range cards_) : + std::vector cards_) : commander(commander_), cards(std::begin(cards_), std::end(cards_)) {} @@ -79,7 +77,7 @@ struct DeckOrdered : DeckIface // card id -> card order std::map > order; - DeckOrdered(const Card* commander_, boost::any_range cards_) : + DeckOrdered(const Card* commander_, std::vector cards_) : DeckIface(commander_, cards_), shuffled_cards(cards.begin(), cards.end()) { From da08c84a3a181fd68b9dcc882847ae703da6a31e Mon Sep 17 00:00:00 2001 From: leftylink Date: Thu, 22 Nov 2012 11:27:56 -0500 Subject: [PATCH 014/406] Forward-declare Cards in deck.h And now we see that it was completely unnecessary to split up cards.h and card.h, because anything that needs Cards also needs Card, and nothing needs only Card. Even better, currently any file that includes any of {card,cards,deck}.h includes all three of them. What was the point of splitting these three files up? I have no clue. At least using forward-declarations makes me feel better about myself. --- deck.cpp | 1 + deck.h | 4 ++-- sim.cpp | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/deck.cpp b/deck.cpp index d98bee96..e1b7fe62 100644 --- a/deck.cpp +++ b/deck.cpp @@ -4,6 +4,7 @@ #include #include "card.h" +#include "cards.h" template void partial_shuffle(RandomAccessIterator first, RandomAccessIterator middle, diff --git a/deck.h b/deck.h index cd9e14f1..be2b44ec 100644 --- a/deck.h +++ b/deck.h @@ -3,12 +3,12 @@ #include #include +#include #include #include -#include "cards.h" - class Card; +class Cards; //---------------------- $30 Deck: a commander + a sequence of cards ----------- // Can be shuffled. diff --git a/sim.cpp b/sim.cpp index 8df5cfba..f80b68bb 100644 --- a/sim.cpp +++ b/sim.cpp @@ -7,6 +7,7 @@ #include #include "card.h" +#include "cards.h" #include "deck.h" //---------------------- $00 general stuff ------------------------------------- From f56bcfa6ca407dd8e2c57a2d7ff4c683fe0004f9 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 3 Dec 2012 22:02:09 -0500 Subject: [PATCH 015/406] Switch order of LDFLAGS and OBJS This fixes linking failure on pretty much every platform. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 56fafdc6..7a8419a2 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ obj/%.o: %.cpp $(CXX) $(CPPFLAGS) -o $@ -c $< $(MAIN): $(OBJS) - $(CXX) -o $@ $(LDFLAGS) $(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) clean: rm -f $(MAIN) obj/*.o From 8561b164abc03fec96ddc4d37a45fd9a2ee15da6 Mon Sep 17 00:00:00 2001 From: leftylink Date: Tue, 4 Dec 2012 11:26:11 -0500 Subject: [PATCH 016/406] Check evade against mimic --- sim.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sim.cpp b/sim.cpp index f80b68bb..3d0d8ebc 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1562,6 +1562,13 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) CardStatus* c(get_target_hostile_fast(fd, src_status, s)); if(c) { + // evade check for mimic + // individual skills are subject to evade checks too, + // but resolve_skill will handle those. + if(c->m_card->m_evade && fd->flip()) + { + return; + } _DEBUG_MSG("%s on (%s)\n", skill_names[std::get<0>(s)].c_str(), c->m_card->m_name.c_str()); for(auto skill: c->m_card->m_skills) { From 4f99573e2c52a2e8f4f30be0a13769bb406eb03a Mon Sep 17 00:00:00 2001 From: leftylink Date: Tue, 4 Dec 2012 11:29:15 -0500 Subject: [PATCH 017/406] Fix some skill predicates Immobilize now checks for delay <= 1, unfrozen, and unjammed Chaos now checks for unfrozen and unjammed Freeze now checks for unjammed Jam now checks for unfrozen --- sim.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sim.cpp b/sim.cpp index 3d0d8ebc..24a2cb70 100644 --- a/sim.cpp +++ b/sim.cpp @@ -763,7 +763,7 @@ unsigned PerformAttack::calculate_attack_damage() template<> void PerformAttack::immobilize() { - if(att_status->m_card->m_immobilize) + if(att_status->m_card->m_immobilize && def_status->m_delay <= 1 && !def_status->m_jammed && !def_status->m_frozen) { def_status->m_immobilized |= fd->flip(); } @@ -922,7 +922,7 @@ inline bool skill_predicate(CardStatus* c) template<> inline bool skill_predicate(CardStatus* c) -{ return(c->m_delay <= 1 && c->m_hp > 0 && !c->m_chaos); } +{ return(c->m_delay <= 1 && c->m_hp > 0 && !c->m_chaos && !c->m_jammed && !c->m_frozen); } template<> inline bool skill_predicate(CardStatus* c) @@ -943,7 +943,7 @@ inline bool skill_predicate(CardStatus* c) template<> inline bool skill_predicate(CardStatus* c) -{ return(c->m_hp > 0 && !c->m_frozen); } +{ return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen); } template<> inline bool skill_predicate(CardStatus* c) @@ -955,7 +955,7 @@ inline bool skill_predicate(CardStatus* c) template<> inline bool skill_predicate(CardStatus* c) -{ return(c->m_delay <= 1 && c->m_hp > 0 && !c->m_jammed); } +{ return(c->m_delay <= 1 && c->m_hp > 0 && !c->m_jammed && !c->m_frozen); } template<> inline bool skill_predicate(CardStatus* c) From 729e940f0b8b907fd84dedbbe80e27b52a37062d Mon Sep 17 00:00:00 2001 From: leftylink Date: Tue, 4 Dec 2012 13:28:14 -0500 Subject: [PATCH 018/406] Check for jammed/frozen before each attack in attack_phase This accounts for a few problems: If a Chaosed assault uses Freeze or Jam on itself, it should not be able to attack, but it was previously able to. If an assault attacks, triggering a Jam on Attacked, it should not be able to attack, but it was previously able to. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 24a2cb70..bf1cafb0 100644 --- a/sim.cpp +++ b/sim.cpp @@ -827,7 +827,7 @@ void attack_phase(Field* fd) CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); unsigned num_attacks(att_status->m_card->m_flurry > 0 && fd->flip() ? att_status->m_card->m_flurry + 1 : 1); - for(unsigned attack_index(0); attack_index < num_attacks && att_status->m_hp > 0 && fd->tip->commander.m_hp > 0; ++attack_index) + for(unsigned attack_index(0); attack_index < num_attacks && !att_status->m_jammed && !att_status->m_frozen && att_status->m_hp > 0 && fd->tip->commander.m_hp > 0; ++attack_index) { // 3 possibilities: // - 1. attack against the assault in front From 293b81e5132f9f47eb079cd2c60cc000bf5e7d62 Mon Sep 17 00:00:00 2001 From: leftylink Date: Tue, 4 Dec 2012 13:31:11 -0500 Subject: [PATCH 019/406] Stop Evade from triggering on Chaosed skills --- sim.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index bf1cafb0..ebaa3706 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1346,7 +1346,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski if(c) { // evade - if(!c->m_card->m_evade || fd->flip()) + if(!c->m_card->m_evade || (src_status && src_status->m_chaos) || fd->flip()) { _DEBUG_MSG("%s (%u) from %s on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str(), status_description(c).c_str()); // skill @@ -1410,7 +1410,7 @@ void perform_global_hostile_fast(Field* fd, CardStatus* src_status, const SkillS for(unsigned s_index(0); s_index < array_head; ++s_index) { CardStatus* c(fd->selection_array[s_index]); - if(!c->m_card->m_evade || fd->flip()) + if(!c->m_card->m_evade || (src_status && src_status->m_chaos) || fd->flip()) { _DEBUG_MSG("%s (%u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), c->m_card->m_name.c_str()); perform_skill(fd, c, std::get<1>(s)); @@ -1565,7 +1565,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // evade check for mimic // individual skills are subject to evade checks too, // but resolve_skill will handle those. - if(c->m_card->m_evade && fd->flip()) + if(c->m_card->m_evade && (!src_status || !src_status->m_chaos) && fd->flip()) { return; } From 2f898032b31a077d805c19dfd47cdb2213bbb0bc Mon Sep 17 00:00:00 2001 From: leftylink Date: Tue, 4 Dec 2012 21:16:05 -0500 Subject: [PATCH 020/406] Implement Backfire skill --- sim.cpp | 19 +++++++++++++++++++ tyrant.cpp | 2 +- tyrant.h | 2 +- xml.cpp | 2 ++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index ebaa3706..fc8392b3 100644 --- a/sim.cpp +++ b/sim.cpp @@ -999,6 +999,16 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) c->m_augmented += v; } +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_hp = safe_minus(c->m_hp, v); + if(c->m_hp == 0) + { + fd->end = true; + } +} + template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { @@ -1464,6 +1474,14 @@ void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSp } } +void perform_backfire(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + Hand* backfired_side = fd->players[src_status->m_player]; + _DEBUG_MSG("Performing backfire on (%s).", backfired_side->commander.m_card->m_name.c_str()); + perform_skill(fd, &backfired_side->commander, std::get<1>(s)); + _DEBUG_MSG("\n"); +} + void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) { unsigned array_head{0}; @@ -1590,6 +1608,7 @@ void fill_skill_table() { skill_table[augment] = perform_targetted_allied_fast; skill_table[augment_all] = perform_global_allied_fast; + skill_table[backfire] = perform_backfire; skill_table[chaos] = perform_targetted_hostile_fast; skill_table[chaos_all] = perform_global_hostile_fast; skill_table[cleanse] = perform_targetted_allied_fast; diff --git a/tyrant.cpp b/tyrant.cpp index 4055f1ca..09337338 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -6,7 +6,7 @@ const std::string faction_names[num_factions] = { "", "bloodthirsty", "imperial", "raider", "righteous", "xeno" }; std::string skill_names[num_skills] = -{"augment", "augment_all", "chaos", "chaos_all", "cleanse", "cleanse_all", "enfeeble", "enfeeble_all", +{"augment", "augment_all", "backfire", "chaos", "chaos_all", "cleanse", "cleanse_all", "enfeeble", "enfeeble_all", "freeze", "freeze_all", "heal", "heal_all", "infuse", "jam", "jam_all", "mimic", "protect", "protect_all", "rally", "rally_all", "rush", "shock", "siege", "siege_all", "strike", "strike_all", "summon", "supply", diff --git a/tyrant.h b/tyrant.h index 32360744..2eb74fe7 100644 --- a/tyrant.h +++ b/tyrant.h @@ -17,7 +17,7 @@ enum Faction extern const std::string faction_names[num_factions]; enum ActiveSkill -{augment, augment_all, chaos, chaos_all, cleanse, cleanse_all, enfeeble, enfeeble_all, +{augment, augment_all, backfire, chaos, chaos_all, cleanse, cleanse_all, enfeeble, enfeeble_all, freeze, freeze_all, heal, heal_all, infuse, jam, jam_all, mimic, protect, protect_all, rally, rally_all, rush, shock, siege, siege_all, strike, strike_all, summon, supply, diff --git a/xml.cpp b/xml.cpp index b774bd02..5823f52c 100644 --- a/xml.cpp +++ b/xml.cpp @@ -281,6 +281,8 @@ void read_cards(Cards& cards) { c->m_valor = atoi(skill->first_attribute("x")->value()); } if(strcmp(skill->first_attribute("id")->value(), "wall") == 0) { c->m_wall = true; } + if(strcmp(skill->first_attribute("id")->value(), "backfire") == 0) + { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "chaos") == 0) { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "cleanse") == 0) From 840591801233be405300a45d4cb5ee85bed34d22 Mon Sep 17 00:00:00 2001 From: leftylink Date: Wed, 5 Dec 2012 01:16:04 -0500 Subject: [PATCH 021/406] Implement Fusion skill --- card.h | 2 ++ sim.cpp | 15 ++++++++++++++- sim.h | 2 ++ xml.cpp | 2 ++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/card.h b/card.h index d9cf9597..6771e033 100644 --- a/card.h +++ b/card.h @@ -26,6 +26,7 @@ class Card m_fear(false), m_flurry(0), m_flying(false), + m_fusion(false), m_health(0), m_id(0), m_immobilize(false), @@ -79,6 +80,7 @@ class Card bool m_fear; unsigned m_flurry; bool m_flying; + bool m_fusion; unsigned m_health; unsigned m_id; bool m_immobilize; diff --git a/sim.cpp b/sim.cpp index fc8392b3..b76a84a4 100644 --- a/sim.cpp +++ b/sim.cpp @@ -170,14 +170,23 @@ SkillSpec augmented_skill(CardStatus* status, const SkillSpec& s) } return(augmented_s); } +SkillSpec fusioned_skill(const SkillSpec& s) +{ + SkillSpec fusioned_s = s; + std::get<1>(fusioned_s) *= 2; + return(fusioned_s); +} void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills) { assert(status); assert(fd->skill_queue.size() == 0); for(auto& skill: skills) { + // Assumptions for fusion: Only active player can use it, and it is incompatible with augment. + // This is fine for now since it only exists on the three Blightbloom structures (can't augment structures) _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(skill).c_str()); - fd->skill_queue.emplace_back(status, status->m_augmented == 0 ? skill : augmented_skill(status, skill)); + bool fusion_active = status->m_card->m_fusion && status->m_player == fd->tapi && fd->fusion_count >= 3; + fd->skill_queue.emplace_back(status, fusion_active ? fusioned_skill(skill) : (status->m_augmented == 0 ? skill : augmented_skill(status, skill))); resolve_skill(fd); } } @@ -309,6 +318,7 @@ unsigned play(Field* fd) fd->tipi = opponent(fd->tapi); fd->tap = fd->players[fd->tapi]; fd->tip = fd->players[fd->tipi]; + fd->fusion_count = 0; fd->end = false; // Shuffle deck while(fd->turn < turn_limit && !fd->end) @@ -454,6 +464,7 @@ void turn_start_phase(Field* fd) remove_dead(fd->tap->structures); remove_dead(fd->tip->assaults); remove_dead(fd->tip->structures); + fd->fusion_count = 0; // Active player's assault cards: // update index // remove enfeeble, protect; apply poison damage, reduce delay @@ -469,6 +480,7 @@ void turn_start_phase(Field* fd) status.m_protected = 0; remove_hp(fd, status, status.m_poisoned); if(status.m_delay > 0 && !status.m_frozen) { --status.m_delay; } + if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } } } // Active player's structure cards: @@ -483,6 +495,7 @@ void turn_start_phase(Field* fd) CardStatus& status(structures[index]); status.m_index = index; if(status.m_delay > 0) { --status.m_delay; } + if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } } } // Defending player's assault cards: diff --git a/sim.h b/sim.h index a11dbbf0..0c9b0451 100644 --- a/sim.h +++ b/sim.h @@ -173,6 +173,8 @@ class Field // otherwise is the index of the current card in players->structures or players->assaults unsigned current_ci; + unsigned fusion_count; + Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t _gamemode) : end{false}, re(re_), diff --git a/xml.cpp b/xml.cpp index 5823f52c..0e027726 100644 --- a/xml.cpp +++ b/xml.cpp @@ -247,6 +247,8 @@ void read_cards(Cards& cards) { c->m_flurry = atoi(skill->first_attribute("x")->value()); } if(strcmp(skill->first_attribute("id")->value(), "flying") == 0) { c->m_flying = true; } + if(strcmp(skill->first_attribute("id")->value(), "fusion") == 0) + { c->m_fusion = true; } if(strcmp(skill->first_attribute("id")->value(), "immobilize") == 0) { c->m_immobilize = true; } if(strcmp(skill->first_attribute("id")->value(), "intercept") == 0) From ed0616d26f235c7dcbcfb904b0c238fea369e71c Mon Sep 17 00:00:00 2001 From: leftylink Date: Wed, 5 Dec 2012 01:56:12 -0500 Subject: [PATCH 022/406] Implement Repair skill --- sim.cpp | 15 +++++++++++++++ tyrant.cpp | 2 +- tyrant.h | 2 +- xml.cpp | 3 +++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index b76a84a4..d50c9c21 100644 --- a/sim.cpp +++ b/sim.cpp @@ -982,6 +982,10 @@ template<> inline bool skill_predicate(CardStatus* c) { return((c->m_delay == 0 || c->blitz) && c->m_hp > 0 && !c->m_jammed && !c->m_frozen && !c->m_immobilized); } +template<> +inline bool skill_predicate(CardStatus* c) +{ return(c->m_hp > 0 && c->m_hp < c->m_card->m_health); } + template<> inline bool skill_predicate(CardStatus* c) { return(c->m_delay > 0); } @@ -1088,6 +1092,12 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) c->m_rallied += v; } +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + add_hp(c, v); +} + template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { @@ -1299,6 +1309,9 @@ template<> std::vector& skill_targets(Field* fd, CardStatu template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_structure(fd, src_status)); } + template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } @@ -1640,6 +1653,8 @@ void fill_skill_table() skill_table[protect_all] = perform_global_allied_fast; skill_table[rally] = perform_targetted_allied_fast; skill_table[rally_all] = perform_global_allied_fast; + skill_table[repair] = perform_targetted_allied_fast; + skill_table[repair_all] = perform_global_allied_fast; skill_table[rush] = perform_targetted_allied_fast; skill_table[shock] = perform_shock; skill_table[siege] = perform_targetted_hostile_fast; diff --git a/tyrant.cpp b/tyrant.cpp index 09337338..8fb0dfe7 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -8,7 +8,7 @@ const std::string faction_names[num_factions] = std::string skill_names[num_skills] = {"augment", "augment_all", "backfire", "chaos", "chaos_all", "cleanse", "cleanse_all", "enfeeble", "enfeeble_all", "freeze", "freeze_all", "heal", "heal_all", "infuse", "jam", "jam_all", - "mimic", "protect", "protect_all", "rally", "rally_all", "rush", "shock", + "mimic", "protect", "protect_all", "rally", "rally_all", "repair", "repair_all", "rush", "shock", "siege", "siege_all", "strike", "strike_all", "summon", "supply", "trigger_regen", "weaken", "weaken_all"}; diff --git a/tyrant.h b/tyrant.h index 2eb74fe7..c8413647 100644 --- a/tyrant.h +++ b/tyrant.h @@ -19,7 +19,7 @@ extern const std::string faction_names[num_factions]; enum ActiveSkill {augment, augment_all, backfire, chaos, chaos_all, cleanse, cleanse_all, enfeeble, enfeeble_all, freeze, freeze_all, heal, heal_all, infuse, jam, jam_all, - mimic, protect, protect_all, rally, rally_all, rush, shock, + mimic, protect, protect_all, rally, rally_all, repair, repair_all, rush, shock, siege, siege_all, strike, strike_all, summon, supply, trigger_regen, // not actually a skill; handles regeneration after strike/siege weaken, weaken_all, num_skills}; diff --git a/xml.cpp b/xml.cpp index 0e027726..4beaf427 100644 --- a/xml.cpp +++ b/xml.cpp @@ -65,6 +65,7 @@ template<> struct GlobalSkill { enum {type = heal_all}; }; template<> struct GlobalSkill { enum {type = jam_all}; }; template<> struct GlobalSkill { enum {type = protect_all}; }; template<> struct GlobalSkill { enum {type = rally_all}; }; +template<> struct GlobalSkill { enum {type = repair_all}; }; template<> struct GlobalSkill { enum {type = siege_all}; }; template<> struct GlobalSkill { enum {type = strike_all}; }; template<> struct GlobalSkill { enum {type = weaken_all}; }; @@ -305,6 +306,8 @@ void read_cards(Cards& cards) { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "rally") == 0) { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "repair") == 0) + { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "rush") == 0) { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "shock") == 0) From 4c7d75a984e8c0092fe20fc6c9dc139414bcebc9 Mon Sep 17 00:00:00 2001 From: leftylink Date: Wed, 5 Dec 2012 20:09:38 -0500 Subject: [PATCH 023/406] Print error message if a required file does not exist --- xml.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/xml.cpp b/xml.cpp index 4beaf427..03446b9b 100644 --- a/xml.cpp +++ b/xml.cpp @@ -132,6 +132,14 @@ void load_decks_xml(Decks& decks, Cards& cards) void parse_file(const char* filename, std::vector& buffer, xml_document<>& doc) { std::ifstream cards_stream(filename, std::ios::binary); + if(!cards_stream.good()) + { + std::cout << "Warning: The file '" << filename << "' does not exist. Proceeding without reading from this file.\n"; + buffer.resize(1); + buffer[0] = 0; + doc.parse<0>(&buffer[0]); + return; + } // Get the size of the file cards_stream.seekg(0,std::ios::end); std::streampos length = cards_stream.tellg(); @@ -158,6 +166,12 @@ void read_cards(Cards& cards) xml_document<> doc; parse_file("cards.xml", buffer, doc); xml_node<>* root = doc.first_node(); + + if(!root) + { + return; + } + bool mission_only(false); unsigned nb_cards(0); for(xml_node<>* card = root->first_node(); @@ -342,6 +356,12 @@ void read_missions(Decks& decks, Cards& cards, std::string filename) xml_document<> doc; parse_file(filename.c_str(), buffer, doc); xml_node<>* root = doc.first_node(); + + if(!root) + { + return; + } + for(xml_node<>* mission_node = root->first_node(); mission_node; mission_node = mission_node->next_sibling()) @@ -382,6 +402,12 @@ void read_raids(Decks& decks, Cards& cards, std::string filename) xml_document<> doc; parse_file(filename.c_str(), buffer, doc); xml_node<>* root = doc.first_node(); + + if(!root) + { + return; + } + for(xml_node<>* raid_node = root->first_node(); raid_node; raid_node = raid_node->next_sibling()) From ac7dd636a35f6dd2868ca15f6e4b38ae0dc34c7b Mon Sep 17 00:00:00 2001 From: leftylink Date: Wed, 5 Dec 2012 20:20:41 -0500 Subject: [PATCH 024/406] Only read ownedcards.txt if -o flag passed --- tyrant_optimize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 279f58e2..eb3d7ce7 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -910,7 +910,6 @@ int main(int argc, char** argv) bool ordered = false; Cards cards; read_cards(cards); - read_owned_cards(cards, owned_cards); Decks decks; load_decks_xml(decks, cards); load_decks(decks, cards); @@ -949,6 +948,7 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "-o") == 0) { + read_owned_cards(cards, owned_cards); use_owned_cards = true; } else if(strcmp(argv[argIndex], "-r") == 0) From 0260e288a2e407964d421be104c24e5c741b9f03 Mon Sep 17 00:00:00 2001 From: leftylink Date: Wed, 5 Dec 2012 20:25:54 -0500 Subject: [PATCH 025/406] Print warning if ownedcards.txt doesn't exist --- read.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/read.cpp b/read.cpp index 3f105b73..080d083d 100644 --- a/read.cpp +++ b/read.cpp @@ -201,6 +201,13 @@ unsigned read_custom_decks(Cards& cards, std::string filename, std::map& owned_cards) { std::ifstream owned_file{"ownedcards.txt"}; + + if(!owned_file.good()) + { + std::cerr << "Warning: The file 'ownedcards.txt' does not exist. This will result in you not owning any cards.\n"; + return; + } + std::string owned_str{(std::istreambuf_iterator(owned_file)), std::istreambuf_iterator()}; boost::tokenizer > tok{owned_str, boost::char_delimiters_separator{false, "()\n", ""}}; for(boost::tokenizer >::iterator beg=tok.begin(); beg!=tok.end();++beg) From 8c55555452dd7db457ba15d678383f3a621a20ef Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 10 Dec 2012 18:41:01 -0500 Subject: [PATCH 026/406] Set m_player and m_index when splitting a unit If this doesn't happen (particularly, m_index doesn't get set), we can get segfaults when evaluating supply in the Clone Project and Clone Experiment battleground effects. --- sim.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sim.cpp b/sim.cpp index d50c9c21..a0260320 100644 --- a/sim.cpp +++ b/sim.cpp @@ -380,6 +380,8 @@ unsigned play(Field* fd) { CardStatus& status_split(fd->tap->assaults.add_back()); status_split.set(current_status.m_card); + status_split.m_index = fd->tap->assaults.size() - 1; + status_split.m_player = fd->tapi; _DEBUG_MSG("Split assault %d (%s)\n", fd->tap->assaults.size() - 1, current_status.m_card->m_name.c_str()); } // Evaluate skills From 061b94a687ae34b6d04fac7978b55e2dd4b397ec Mon Sep 17 00:00:00 2001 From: leftylink Date: Wed, 12 Dec 2012 14:32:17 -0500 Subject: [PATCH 027/406] Evaluate on-play skills when splitting a unit --- sim.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sim.cpp b/sim.cpp index a0260320..1a5363aa 100644 --- a/sim.cpp +++ b/sim.cpp @@ -383,6 +383,11 @@ unsigned play(Field* fd) status_split.m_index = fd->tap->assaults.size() - 1; status_split.m_player = fd->tapi; _DEBUG_MSG("Split assault %d (%s)\n", fd->tap->assaults.size() - 1, current_status.m_card->m_name.c_str()); + for(auto& skill: status_split.m_card->m_skills_played) + { + fd->skill_queue.emplace_back(&status_split, skill); + resolve_skill(fd); + } } // Evaluate skills // Special case: Gore Typhon's infuse From 670e51109b046b9112b423a821dc980cf2f39874 Mon Sep 17 00:00:00 2001 From: leftylink Date: Tue, 18 Dec 2012 02:33:43 -0500 Subject: [PATCH 028/406] xml: Deal with stray ` character in raids.xml Do this by explicitly ignoring the card with ID 0 in Arctis Vanguard. It's a dumb hack, but it will do. I won't ignore other ID 0 cards because they could be indicative of other problems. --- xml.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xml.cpp b/xml.cpp index 03446b9b..a895f143 100644 --- a/xml.cpp +++ b/xml.cpp @@ -453,6 +453,12 @@ void read_raids(Decks& decks, Cards& cards, std::string filename) card_node = card_node->next_sibling()) { unsigned card_id{atoi(card_node->value())}; + // Special case Arctis Vanguard id 0 because of stray ` character. + // Don't continue on other raids because I want to be notified of other errors. + if(card_id == 0 && id == 1) + { + continue; + } // Handle the replacement art cards if(cards.replace.find(card_id) != cards.replace.end()) { From d105d071dc333a141e0b51b793f66cf751eba8ce Mon Sep 17 00:00:00 2001 From: Herethios Date: Tue, 18 Dec 2012 02:35:06 -0500 Subject: [PATCH 029/406] Remove narrowing conversion warnings from xml.cpp --- xml.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xml.cpp b/xml.cpp index a895f143..94e00b30 100644 --- a/xml.cpp +++ b/xml.cpp @@ -380,7 +380,7 @@ void read_missions(Decks& decks, Cards& cards, std::string filename) card_node; card_node = card_node->next_sibling()) { - unsigned card_id{atoi(card_node->value())}; + unsigned card_id{static_cast(atoi(card_node->value()))}; // Handle the replacement art cards if(cards.replace.find(card_id) != cards.replace.end()) { @@ -430,7 +430,7 @@ void read_raids(Decks& decks, Cards& cards, std::string filename) card_node; card_node = card_node->next_sibling()) { - unsigned card_id{atoi(card_node->value())}; + unsigned card_id{static_cast(atoi(card_node->value()))}; // Handle the replacement art cards if(cards.replace.find(card_id) != cards.replace.end()) { @@ -445,14 +445,14 @@ void read_raids(Decks& decks, Cards& cards, std::string filename) { if(strcmp(pool_node->name(), "card_pool") == 0) { - unsigned num_cards_from_pool{atoi(pool_node->first_attribute("amount")->value())}; + unsigned num_cards_from_pool{static_cast(atoi(pool_node->first_attribute("amount")->value()))}; std::vector cards_from_pool; for(xml_node<>* card_node = pool_node->first_node(); card_node; card_node = card_node->next_sibling()) { - unsigned card_id{atoi(card_node->value())}; + unsigned card_id{static_cast(atoi(card_node->value()))}; // Special case Arctis Vanguard id 0 because of stray ` character. // Don't continue on other raids because I want to be notified of other errors. if(card_id == 0 && id == 1) From 533d7212c8093dd6d2aef40d638e1cd6de37af4c Mon Sep 17 00:00:00 2001 From: Herethios Date: Tue, 18 Dec 2012 03:02:56 -0500 Subject: [PATCH 030/406] Remove warnings trom tyrant_optimize.cpp This includes some signed comparisons, some reordered initializations, and some unused variables. --- tyrant_optimize.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index eb3d7ce7..5d739bf9 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -333,12 +333,12 @@ class Process Process(unsigned _num_threads, const Cards& cards_, const Decks& decks_, DeckIface* att_deck_, std::vector _def_decks, std::vector _factors, gamemode_t _gamemode) : num_threads(_num_threads), + main_barrier(num_threads+1), cards(cards_), decks(decks_), att_deck(att_deck_), def_decks(_def_decks), factors(_factors), - main_barrier(num_threads+1), gamemode(_gamemode) { destroy_threads = false; @@ -674,7 +674,10 @@ class Combination bool next() { - for(index = choose - 1; index >= 0; --index) + // The end condition's a bit odd here, but index is unsigned. + // The last iteration is when index = 0. + // After that, index = max int, which is clearly >= choose. + for(index = choose - 1; index < choose; --index) { if(indices[index] < indicesLimits[index]) { @@ -695,8 +698,8 @@ class Combination unsigned firstIndexLimit; std::vector indices; std::vector indicesLimits; - int index; - int nextIndex; + unsigned index; + unsigned nextIndex; }; //------------------------------------------------------------------------------ static unsigned total_num_combinations_test(0); @@ -940,7 +943,7 @@ int main(int argc, char** argv) } } std::vector > todo; - for(unsigned argIndex(3); argIndex < argc; ++argIndex) + for(int argIndex(3); argIndex < argc; ++argIndex) { if(strcmp(argv[argIndex], "-c") == 0) { @@ -987,9 +990,6 @@ int main(int argc, char** argv) } } - unsigned attacker_wins(0); - unsigned defender_wins(0); - DeckIface* att_deck{nullptr}; auto custom_deck_it = decks.custom_decks.find(att_deck_name); if(custom_deck_it != decks.custom_decks.end()) From 4e05f926ba5f617e34bd00c19c7aecb9ef71aaf0 Mon Sep 17 00:00:00 2001 From: Herethios Date: Tue, 18 Dec 2012 03:09:20 -0500 Subject: [PATCH 031/406] Remove warnings from sim.cpp Includes some missing cases in switch and missing returns. --- sim.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sim.cpp b/sim.cpp index 1a5363aa..4756d148 100644 --- a/sim.cpp +++ b/sim.cpp @@ -111,6 +111,7 @@ std::string status_description(CardStatus* status) case CardType::action: desc = "Action "; break; case CardType::assault: desc = "A " + to_string(status->m_index) + " "; break; case CardType::structure: desc = "S " + to_string(status->m_index) + " "; break; + case CardType::num_cardtypes: assert(false); break; } desc += "[" + status->m_card->m_name + "]"; return(desc); @@ -211,6 +212,7 @@ struct PlayCard placeCard(); onPlaySkills(); blitz(); + return(true); } // action @@ -348,6 +350,10 @@ unsigned play(Field* fd) case CardType::structure: PlayCard(played_card, fd).op(); break; + case CardType::commander: + case CardType::num_cardtypes: + assert(false); + break; } } // Evaluate commander @@ -408,6 +414,10 @@ unsigned play(Field* fd) // attacker wins if(fd->players[1]->commander.m_hp == 0) { _DEBUG_MSG("Attacker wins.\n"); return(0); } if(fd->turn >= turn_limit) { return(1); } + + // Huh? How did we get here? + assert(false); + return 0; } //------------------------------------------------------------------------------ // All the stuff that happens at the beginning of a turn, before a card is played From 72d18840b4b10670587f226fb0e60f3c8e9893c5 Mon Sep 17 00:00:00 2001 From: Herethios Date: Tue, 18 Dec 2012 03:13:23 -0500 Subject: [PATCH 032/406] Remove warnings from read.cpp Add a missing return and explicitly cast a narrowing conversion. --- read.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/read.cpp b/read.cpp index 080d083d..10cd0cd9 100644 --- a/read.cpp +++ b/read.cpp @@ -196,6 +196,7 @@ unsigned read_custom_decks(Cards& cards, std::string filename, std::map& owned_cards) @@ -215,7 +216,7 @@ void read_owned_cards(Cards& cards, std::map& owned_cards) std::string name{*beg}; ++beg; assert(beg != tok.end()); - unsigned num{atoi((*beg).c_str())}; + unsigned num{static_cast(atoi((*beg).c_str()))}; auto card_itr = cards.player_cards_by_name.find(name); if(card_itr == cards.player_cards_by_name.end()) { From b1daf0bd1c51c6ce294764594e8d68499e0f1c5c Mon Sep 17 00:00:00 2001 From: Herethios Date: Tue, 18 Dec 2012 03:17:04 -0500 Subject: [PATCH 033/406] Remove reordered initialization warning in card.h --- card.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/card.h b/card.h index 6771e033..750f09ee 100644 --- a/card.h +++ b/card.h @@ -49,8 +49,8 @@ class Card m_unique(false), m_valor(0), m_wall(false), - m_type(CardType::assault), - m_skills() + m_skills(), + m_type(CardType::assault) { } From 26be78a70a1a01ce80e05e436a76a2e6e77b4471 Mon Sep 17 00:00:00 2001 From: Herethios Date: Tue, 18 Dec 2012 03:20:33 -0500 Subject: [PATCH 034/406] Fix missing case warning in cards.cpp --- cards.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cards.cpp b/cards.cpp index c49f128f..c346125f 100644 --- a/cards.cpp +++ b/cards.cpp @@ -67,6 +67,10 @@ void Cards::organize() player_actions.push_back(card); break; } + case CardType::num_cardtypes: { + throw card->m_type; + break; + } } if(player_cards_by_name.find(card->m_name) != player_cards_by_name.end()) { From 591ba51eff86d115363e4e46b04a806240398237 Mon Sep 17 00:00:00 2001 From: leftylink Date: Tue, 18 Dec 2012 03:22:33 -0500 Subject: [PATCH 035/406] Remove signed comparison warning in cards.cpp Because -1 is a special value for no card set, we'll just make this an int. --- card.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/card.h b/card.h index 750f09ee..86a11d6d 100644 --- a/card.h +++ b/card.h @@ -95,7 +95,7 @@ class Card bool m_recharge; bool m_refresh; unsigned m_regenerate; - unsigned m_set; + int m_set; unsigned m_siphon; bool m_split; bool m_swipe; From a95c759d934c526360cc3e792dd76ad926d390ec Mon Sep 17 00:00:00 2001 From: leftylink Date: Tue, 18 Dec 2012 03:24:31 -0500 Subject: [PATCH 036/406] Compile with -Werror --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7a8419a2..d9a57c48 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ MAIN := tyrant_optimize SRCS := $(wildcard *.cpp) OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) -CPPFLAGS := -Wall -std=gnu++11 -O3 +CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem all: $(MAIN) From f05a94bfdfdf7b113f8eac2280e041198aaf41bb Mon Sep 17 00:00:00 2001 From: leftylink Date: Wed, 19 Dec 2012 10:38:49 -0500 Subject: [PATCH 037/406] xml: Correctly handle "on Play on Death" --- xml.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/xml.cpp b/xml.cpp index 94e00b30..d8d57dc2 100644 --- a/xml.cpp +++ b/xml.cpp @@ -78,7 +78,11 @@ bool handle_global_skill(xml_node<>* node, Card* card) bool attacked(node->first_attribute("attacked")); if(node->first_attribute("all")) { - if(played) {card->add_played_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } + if(played) + { + card->add_played_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); + if(died) {card->add_died_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } + } else if(died) {card->add_died_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } else if(attacked) {card->add_attacked_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } else {card->add_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } @@ -102,7 +106,11 @@ void handle_skill(xml_node<>* node, Card* card) if(handle_global_skill::type>(node, card)) {} else { - if(played) {card->add_played_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } + if(played) + { + card->add_played_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); + if(died) {card->add_died_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } + } else if(died) {card->add_died_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } else if(attacked) {card->add_attacked_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } else {card->add_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } From 84015d38046c83dcd1d7bdca6e46064973bfe5aa Mon Sep 17 00:00:00 2001 From: leftylink Date: Sun, 25 Nov 2012 23:16:42 -0500 Subject: [PATCH 038/406] Add -e flag and effect names/ids The effect gets passed to the Process and SimulationData, which will pass it to the Field. The Field doesn't do anything with this yet, but it will soon. --- sim.h | 6 ++++-- tyrant.cpp | 22 ++++++++++++++++++++++ tyrant.h | 25 +++++++++++++++++++++++++ tyrant_optimize.cpp | 38 +++++++++++++++++++++++++++++++------- 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/sim.h b/sim.h index 0c9b0451..4531ed21 100644 --- a/sim.h +++ b/sim.h @@ -154,6 +154,7 @@ class Field std::array selection_array; unsigned turn; gamemode_t gamemode; + const enum Effect effect; // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque > skill_queue; @@ -175,13 +176,14 @@ class Field unsigned fusion_count; - Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t _gamemode) : + Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t _gamemode, enum Effect _effect) : end{false}, re(re_), cards(cards_), players{{&hand1, &hand2}}, turn(1), - gamemode(_gamemode) + gamemode(_gamemode), + effect(_effect) { } diff --git a/tyrant.cpp b/tyrant.cpp index 8fb0dfe7..31e6e2af 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -14,3 +14,25 @@ std::string skill_names[num_skills] = "weaken", "weaken_all"}; std::string cardtype_names[CardType::num_cardtypes]{"action", "assault", "commander", "structure"}; + +std::string effect_names[Effect::num_effects] = { + "None", + "Time Surge", + "Copycat", + "Quicksilver", + "Decay", + "High Skies", + "Impenetrable", + "Invigorate", + "Clone Project", + "Friendly Fire", + "Genesis", + "Artillery Fire", + "Photon Shield", + "Decrepit", + "Forcefield", + "Chilling Touch", + "Clone Experiment", + "Toxic", + "Haunt", +}; diff --git a/tyrant.h b/tyrant.h index c8413647..81b8fec0 100644 --- a/tyrant.h +++ b/tyrant.h @@ -37,6 +37,31 @@ enum CardType { extern std::string cardtype_names[CardType::num_cardtypes]; +enum Effect { + none, + time_surge, + copycat, + quicksilver, + decay, + high_skies, + impenetrable, + invigorate, + clone_project, + friendly_fire, + genesis, + artillery_fire, + photon_shield, + decrepit, + forcefield, + chilling_touch, + clone_experiment, + toxic, + haunt, + num_effects +}; + +extern std::string effect_names[Effect::num_effects]; + enum gamemode_t { fight, diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 5d739bf9..eaae5ec5 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -262,8 +262,9 @@ struct SimulationData std::vector def_hands; std::vector factors; gamemode_t gamemode; + enum Effect effect; - SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_def_decks_, std::vector factors_, gamemode_t gamemode_) : + SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_def_decks_, std::vector factors_, gamemode_t gamemode_, enum Effect effect_) : re(seed), cards(cards_), decks(decks_), @@ -271,7 +272,8 @@ struct SimulationData att_hand(nullptr), def_decks(num_def_decks_), factors(factors_), - gamemode(gamemode_) + gamemode(gamemode_), + effect(effect_) { for(auto def_deck: def_decks) { @@ -302,7 +304,7 @@ struct SimulationData { att_hand.reset(re); def_hand->reset(re); - Field fd(re, cards, att_hand, *def_hand, gamemode); + Field fd(re, cards, att_hand, *def_hand, gamemode, effect); unsigned result(play(&fd)); res.emplace_back(result); } @@ -330,8 +332,9 @@ class Process const std::vector def_decks; std::vector factors; gamemode_t gamemode; + enum Effect effect; - Process(unsigned _num_threads, const Cards& cards_, const Decks& decks_, DeckIface* att_deck_, std::vector _def_decks, std::vector _factors, gamemode_t _gamemode) : + Process(unsigned _num_threads, const Cards& cards_, const Decks& decks_, DeckIface* att_deck_, std::vector _def_decks, std::vector _factors, gamemode_t _gamemode, enum Effect _effect) : num_threads(_num_threads), main_barrier(num_threads+1), cards(cards_), @@ -339,13 +342,14 @@ class Process att_deck(att_deck_), def_decks(_def_decks), factors(_factors), - gamemode(_gamemode) + gamemode(_gamemode), + effect(_effect) { destroy_threads = false; unsigned seed(time(0)); for(unsigned i(0); i < num_threads; ++i) { - threads_data.push_back(new SimulationData(seed + i, cards, decks, def_decks.size(), factors, gamemode)); + threads_data.push_back(new SimulationData(seed + i, cards, decks, def_decks.size(), factors, gamemode, effect)); threads.push_back(new boost::thread(thread_evaluate, std::ref(main_barrier), std::ref(shared_mutex), std::ref(*threads_data.back()), std::ref(*this))); } } @@ -942,6 +946,14 @@ int main(int argc, char** argv) return(5); } } + + enum Effect effect = Effect::none; + std::map effect_map; + for(unsigned i(0); i < Effect::num_effects; ++i) + { + effect_map[effect_names[i]] = i; + } + std::vector > todo; for(int argIndex(3); argIndex < argc; ++argIndex) { @@ -949,6 +961,18 @@ int main(int argc, char** argv) { keep_commander = true; } + else if(strcmp(argv[argIndex], "-e") == 0) + { + std::string arg_effect(argv[argIndex + 1]); + auto x = effect_map.find(arg_effect); + if(x == effect_map.end()) + { + std::cout << "The effect '" << arg_effect << "' was not found.\n"; + return(6); + } + effect = static_cast(x->second); + argIndex += 1; + } else if(strcmp(argv[argIndex], "-o") == 0) { read_owned_cards(cards, owned_cards); @@ -1014,7 +1038,7 @@ int main(int argc, char** argv) att_deck_ordered = std::make_shared(*att_deck); } - Process p(num_threads, cards, decks, ordered ? att_deck_ordered.get() : att_deck, def_decks, def_decks_factors, gamemode); + Process p(num_threads, cards, decks, ordered ? att_deck_ordered.get() : att_deck, def_decks, def_decks_factors, gamemode, effect); { //ScopeClock timer; for(auto op: todo) From 57146a62d77d4797718c671027e102a6d41df383 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 00:32:54 -0500 Subject: [PATCH 039/406] Add modify_cards function This function will modify all cards passed to it based on the effect specified. --- sim.cpp | 13 +++++++++++++ sim.h | 1 + tyrant_optimize.cpp | 2 ++ 3 files changed, 16 insertions(+) diff --git a/sim.cpp b/sim.cpp index 4756d148..87318adc 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1684,3 +1684,16 @@ void fill_skill_table() skill_table[weaken] = perform_targetted_hostile_fast; skill_table[weaken_all] = perform_global_hostile_fast; } +//------------------------------------------------------------------------------ +void modify_cards(Cards& cards, enum Effect effect) +{ + switch (effect) + { + case Effect::none: + break; + default: + // TODO: throw something more useful + throw effect; + break; + } +} diff --git a/sim.h b/sim.h index 4531ed21..99c533ac 100644 --- a/sim.h +++ b/sim.h @@ -20,6 +20,7 @@ extern unsigned turn_limit; void fill_skill_table(); unsigned play(Field* fd); +void modify_cards(Cards& cards, enum Effect effect); // Pool-based indexed storage. //---------------------- Pool-based indexed storage ---------------------------- template diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index eaae5ec5..c293a4e6 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1038,6 +1038,8 @@ int main(int argc, char** argv) att_deck_ordered = std::make_shared(*att_deck); } + modify_cards(cards, effect); + Process p(num_threads, cards, decks, ordered ? att_deck_ordered.get() : att_deck, def_decks, def_decks_factors, gamemode, effect); { //ScopeClock timer; From 85fd11123ce1d5dabbba2cd6b949743a637200ac Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 01:02:33 -0500 Subject: [PATCH 040/406] Implement Time Surge, Decrepit, Forcefield, Chilling Touch --- sim.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/sim.cpp b/sim.cpp index 87318adc..318cdb48 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1685,12 +1685,70 @@ void fill_skill_table() skill_table[weaken_all] = perform_global_hostile_fast; } //------------------------------------------------------------------------------ +// Utility functions for modify_cards. + +// Adds the skill " " to all commanders. +inline void add_to_commanders(Cards& cards, ActiveSkill skill, unsigned magnitude) +{ + for(Card* card: cards.cards) + { + if(card->m_type != CardType::commander) + { + continue; + } + card->add_skill(skill, magnitude, allfactions); + } +} + +// Adds the skill " " to all commanders. +// If the commander has an instance of either or in its skill list, +// the new skill replaces it. +// Otherwise, the new skill is added on to the end. +inline void replace_on_commanders(Cards& cards, ActiveSkill old_skill, ActiveSkill new_skill, unsigned magnitude) +{ + for(Card* card: cards.cards) + { + if(card->m_type != CardType::commander) + { + continue; + } + + bool replaced(false); + for(auto& skill: card->m_skills) + { + if(std::get<0>(skill) == old_skill || + std::get<0>(skill) == new_skill) + { + skill = std::make_tuple(new_skill, magnitude, allfactions); + replaced = true; + } + } + + if(!replaced) + { + card->add_skill(new_skill, magnitude, allfactions); + } + } +} +//------------------------------------------------------------------------------ void modify_cards(Cards& cards, enum Effect effect) { switch (effect) { case Effect::none: break; + case Effect::time_surge: + add_to_commanders(cards, rush, 1); + break; + case Effect::decrepit: + replace_on_commanders(cards, enfeeble, enfeeble_all, 1); + break; + case Effect::forcefield: + replace_on_commanders(cards, protect, protect_all, 1); + break; + case Effect::chilling_touch: + add_to_commanders(cards, freeze, 0); + break; default: // TODO: throw something more useful throw effect; From d387669be4c87f0f35cf003ae665187f6e1d4f30 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 01:34:26 -0500 Subject: [PATCH 041/406] Implement Toxic Toxicize regularly played assaults in PlayCard::fieldEffects specialization for assaults. Toxicize summoned cards in summon_card. --- sim.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/sim.cpp b/sim.cpp index 318cdb48..1ba156a9 100644 --- a/sim.cpp +++ b/sim.cpp @@ -212,6 +212,7 @@ struct PlayCard placeCard(); onPlaySkills(); blitz(); + fieldEffects(); return(true); } @@ -249,6 +250,12 @@ struct PlayCard { } + // all except assault: noop + template + void fieldEffects() + { + } + // assault + structure template void onPlaySkills() @@ -286,6 +293,15 @@ void PlayCard::blitz() status->blitz = true; } } +// assault +template <> +void PlayCard::fieldEffects() +{ + if(fd->effect == Effect::toxic) + { + status->m_poisoned = 1; + } +} // action template <> void PlayCard::onPlaySkills() @@ -1587,6 +1603,11 @@ void summon_card(Field* fd, unsigned player, const Card* summoned) { card_status.blitz = true; } + + if(fd->effect == Effect::toxic) + { + card_status.m_poisoned = 1; + } } } void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) @@ -1749,6 +1770,10 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::chilling_touch: add_to_commanders(cards, freeze, 0); break; + case Effect::toxic: + // Do nothing; this is implemented in PlayCard::fieldEffects + // and summon_card + break; default: // TODO: throw something more useful throw effect; From 0a81af9537b20a3696e778b9524490c8bfde4c14 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 01:56:54 -0500 Subject: [PATCH 042/406] Use add_hp while refreshing and regenerating; add Field pointer Need some consistency here. This will ease the implementation of Invigorate. --- sim.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/sim.cpp b/sim.cpp index 1ba156a9..0ac7ed9b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -474,6 +474,10 @@ inline void remove_dead(Storage& storage) { storage.remove(is_it_dead); } +inline void add_hp(Field* field, CardStatus* target, unsigned v) +{ + target->m_hp = std::min(target->m_hp + v, target->m_card->m_health); +} void check_regeneration(Field* fd) { for(unsigned i(0); i < fd->killed_with_regen.size(); ++i) @@ -481,7 +485,10 @@ void check_regeneration(Field* fd) CardStatus& status = *fd->killed_with_regen[i]; if(status.m_hp == 0 && status.m_card->m_regenerate > 0 && !status.m_diseased) { - status.m_hp = fd->flip() ? status.m_card->m_regenerate : 0; + if (fd->flip()) + { + add_hp(fd, &status, status.m_card->m_regenerate); + } } if(status.m_hp > 0) { @@ -558,7 +565,7 @@ void turn_start_phase(Field* fd) _DEBUG_MSG("%u %s refreshed. hp %u -> %u.\n", index, status_description(&status).c_str(), status.m_hp, status.m_card->m_health); } #endif - status.m_hp = status.m_card->m_health; + add_hp(fd, &status, status.m_card->m_health); } } } @@ -578,7 +585,7 @@ void turn_start_phase(Field* fd) #ifndef NDEBUG _DEBUG_MSG("%s refreshed. hp %u -> %u.\n", index, status_description(&status).c_str(), status.m_hp, status.m_card->m_health); #endif - status.m_hp = status.m_card->m_health; + add_hp(fd, &status, status.m_card->m_health); } } } @@ -589,10 +596,6 @@ void turn_start_phase(Field* fd) check_regeneration(fd); } //---------------------- $50 attack by assault card implementation ------------- -inline void add_hp(CardStatus* target, unsigned v) -{ - target->m_hp = std::min(target->m_hp + v, target->m_card->m_health); -} inline void apply_poison(CardStatus* target, unsigned v) { target->m_poisoned = std::max(target->m_poisoned, v); @@ -827,7 +830,7 @@ void PerformAttack::siphon_poison_disease() { if(att_status->m_card->m_siphon > 0) { - add_hp(&fd->tap->commander, std::min(att_dmg, att_status->m_card->m_siphon)); + add_hp(fd, &fd->tap->commander, std::min(att_dmg, att_status->m_card->m_siphon)); _DEBUG_MSG(" \033[1;32m%s siphon %u; hp %u\033[0m\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_siphon), fd->tap->commander.m_hp); } if(att_status->m_card->m_poison > 0) @@ -862,7 +865,7 @@ void PerformAttack::crush_leech() } if(att_status->m_card->m_leech > 0 && att_status->m_hp > 0 && !att_status->m_diseased) { - add_hp(att_status, std::min(att_dmg, att_status->m_card->m_leech)); + add_hp(fd, att_status, std::min(att_dmg, att_status->m_card->m_leech)); _DEBUG_MSG("%s leech %u; hp: %u.\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech), att_status->m_hp); } } @@ -1092,7 +1095,7 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - add_hp(c, v); + add_hp(fd, c, v); } template<> @@ -1128,7 +1131,7 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - add_hp(c, v); + add_hp(fd, c, v); } template<> @@ -1160,7 +1163,7 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - add_hp(c, v); + add_hp(fd, c, v); } template<> From 1d15c189c56306db25bf991439724e9c12b77708 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 02:18:07 -0500 Subject: [PATCH 043/406] Implement Invigorate effect --- sim.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sim.cpp b/sim.cpp index 0ac7ed9b..8729a86c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -476,7 +476,13 @@ inline void remove_dead(Storage& storage) } inline void add_hp(Field* field, CardStatus* target, unsigned v) { + unsigned old_hp = target->m_hp; target->m_hp = std::min(target->m_hp + v, target->m_card->m_health); + if(field->effect == Effect::invigorate && target->m_card->m_type == CardType::assault) + { + unsigned healed = target->m_hp - old_hp; + target->m_berserk += healed; + } } void check_regeneration(Field* fd) { @@ -1764,6 +1770,9 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::time_surge: add_to_commanders(cards, rush, 1); break; + case Effect::invigorate: + // Do nothing; this is implemented in add_hp + break; case Effect::decrepit: replace_on_commanders(cards, enfeeble, enfeeble_all, 1); break; From 15cb453528c3158105d9ddfe754f360230d54f2f Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 02:51:34 -0500 Subject: [PATCH 044/406] Implement Copycat --- sim.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index 8729a86c..fda8c6b6 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1650,16 +1650,30 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // mimic cannot be triggered by anything. So it should be the only skill in the unresolved skill table. // so we can probably clear it safely. This is necessary, because mimic calls resolve_skill as well (infinite loop). fd->skill_queue.clear(); - CardStatus* c(get_target_hostile_fast(fd, src_status, s)); - if(c) + CardStatus* c(nullptr); + if(fd->effect == Effect::copycat) + { + std::vector& cards(skill_targets_allied_assault(fd, src_status)); + unsigned array_head{select_fast(fd, src_status, cards, s)}; + if(array_head > 0) + { + c = fd->selection_array[fd->rand(0, array_head - 1)]; + } + } + else { + c = get_target_hostile_fast(fd, src_status, s); // evade check for mimic // individual skills are subject to evade checks too, // but resolve_skill will handle those. - if(c->m_card->m_evade && (!src_status || !src_status->m_chaos) && fd->flip()) + if(c && c->m_card->m_evade && (!src_status || !src_status->m_chaos) && fd->flip()) { return; } + } + + if(c) + { _DEBUG_MSG("%s on (%s)\n", skill_names[std::get<0>(s)].c_str(), c->m_card->m_name.c_str()); for(auto skill: c->m_card->m_skills) { @@ -1770,6 +1784,9 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::time_surge: add_to_commanders(cards, rush, 1); break; + case Effect::copycat: + // Do nothing; this is implemented in perform_mimic + break; case Effect::invigorate: // Do nothing; this is implemented in add_hp break; From 26a8049075833ada314133455c9a07f5c4f912f5 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 03:47:14 -0500 Subject: [PATCH 045/406] Implement Clone Project and Experiment This is achieved by giving CardStatus a temporary_split flag. If this flag is set true, the unit will use Split on its turn. On the appropriate turns (9 and 10 if experiment, every turn if project), a skill called "temporary_split" is used by the commander to award this status to one active unit. To be determined: Whether the newly-split unit can activate Blitz. --- sim.cpp | 41 ++++++++++++++++++++++++++++++++++++++--- sim.h | 1 + tyrant.cpp | 1 + tyrant.h | 1 + 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index fda8c6b6..c11dcb2f 100644 --- a/sim.cpp +++ b/sim.cpp @@ -60,7 +60,8 @@ CardStatus::CardStatus(const Card* card) : m_poisoned(0), m_protected(0), m_rallied(0), - m_weakened(0) + m_weakened(0), + m_temporary_split(false) { } //------------------------------------------------------------------------------ @@ -350,6 +351,17 @@ unsigned play(Field* fd) { fd->tip->commander.m_hp = fd->tip->commander.m_card->m_health; } + + if(fd->effect == Effect::clone_project || + (fd->effect == Effect::clone_experiment && (fd->turn == 9 || fd->turn == 10))) + { + std::vector skills; + skills.emplace_back(temporary_split, 0, allfactions); + // The skill doesn't actually come from the commander, + // but we need to provide some source and it seemed most reasonable. + evaluate_skills(fd, &fd->tap->commander, skills); + } + // Play a card const Card* played_card(fd->tap->deck->next()); if(played_card) @@ -397,8 +409,8 @@ unsigned play(Field* fd) } else { - // Special case: check for split (tartarus swarm raid) - if(current_status.m_card->m_split && fd->tap->assaults.size() + fd->tap->structures.size() < 100) + // Special case: check for split (tartarus swarm raid, or clone battlefield effects) + if((current_status.m_temporary_split || current_status.m_card->m_split) && fd->tap->assaults.size() + fd->tap->structures.size() < 100) { CardStatus& status_split(fd->tap->assaults.add_back()); status_split.set(current_status.m_card); @@ -410,6 +422,7 @@ unsigned play(Field* fd) fd->skill_queue.emplace_back(&status_split, skill); resolve_skill(fd); } + // TODO: Determine whether we need to check for Blitz for the newly-Split unit } // Evaluate skills // Special case: Gore Typhon's infuse @@ -547,6 +560,7 @@ void turn_start_phase(Field* fd) // Defending player's assault cards: // update index // remove augment, chaos, freeze, immobilize, jam, rally, weaken, apply refresh + // remove temp split { auto& assaults(fd->tip->assaults); for(unsigned index(0), end(assaults.size()); @@ -563,6 +577,7 @@ void turn_start_phase(Field* fd) status.m_jammed = false; status.m_rallied = 0; status.m_weakened = 0; + status.m_temporary_split = false; if(status.m_card->m_refresh && !status.m_diseased) { #ifndef NDEBUG @@ -1044,6 +1059,12 @@ template<> inline bool skill_predicate(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } +template<> +inline bool skill_predicate(CardStatus* c) +// It is unnecessary to check for Blitz, since temporary_split status is +// awarded before a card is played. +{ return(c->m_delay == 0 && c->m_hp > 0); } + template<> inline bool skill_predicate(CardStatus* c) { return(c->m_delay <= 1 && c->m_hp > 0 && attack_power(c) > 0 && !c->m_jammed && !c->m_frozen && !c->m_immobilized); } @@ -1172,6 +1193,12 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) add_hp(fd, c, v); } +template<> +inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +{ + c->m_temporary_split = true; +} + template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { @@ -1363,6 +1390,9 @@ template<> std::vector& skill_targets(Field* fd, CardStatus template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_assault(fd, src_status)); } @@ -1724,6 +1754,7 @@ void fill_skill_table() skill_table[strike] = perform_targetted_hostile_fast; skill_table[strike_all] = perform_global_hostile_fast; skill_table[summon] = perform_summon; + skill_table[temporary_split] = perform_targetted_allied_fast; skill_table[trigger_regen] = perform_trigger_regen; skill_table[weaken] = perform_targetted_hostile_fast; skill_table[weaken_all] = perform_global_hostile_fast; @@ -1790,6 +1821,10 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::invigorate: // Do nothing; this is implemented in add_hp break; + case Effect::clone_project: + case Effect::clone_experiment: + // Do nothing; these are implemented in the temporary_split skill + break; case Effect::decrepit: replace_on_commanders(cards, enfeeble, enfeeble_all, 1); break; diff --git a/sim.h b/sim.h index 99c533ac..8293e906 100644 --- a/sim.h +++ b/sim.h @@ -109,6 +109,7 @@ struct CardStatus unsigned m_protected; unsigned m_rallied; unsigned m_weakened; + bool m_temporary_split; CardStatus() {} CardStatus(const Card* card); diff --git a/tyrant.cpp b/tyrant.cpp index 31e6e2af..aaa16830 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -10,6 +10,7 @@ std::string skill_names[num_skills] = "freeze", "freeze_all", "heal", "heal_all", "infuse", "jam", "jam_all", "mimic", "protect", "protect_all", "rally", "rally_all", "repair", "repair_all", "rush", "shock", "siege", "siege_all", "strike", "strike_all", "summon", "supply", + "temporary_split", "trigger_regen", "weaken", "weaken_all"}; diff --git a/tyrant.h b/tyrant.h index 81b8fec0..82a56183 100644 --- a/tyrant.h +++ b/tyrant.h @@ -21,6 +21,7 @@ enum ActiveSkill freeze, freeze_all, heal, heal_all, infuse, jam, jam_all, mimic, protect, protect_all, rally, rally_all, repair, repair_all, rush, shock, siege, siege_all, strike, strike_all, summon, supply, + temporary_split, // not actually a skill; handles Clone Project/Experiment trigger_regen, // not actually a skill; handles regeneration after strike/siege weaken, weaken_all, num_skills}; extern std::string skill_names[num_skills]; From c779807f7d5cc036a1e96bfdf2fa54e4e4564859 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 04:33:47 -0500 Subject: [PATCH 046/406] Implement Genesis --- sim.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sim.cpp b/sim.cpp index c11dcb2f..91079a5f 100644 --- a/sim.cpp +++ b/sim.cpp @@ -328,6 +328,7 @@ void PlayCard::onPlaySkills() //------------------------------------------------------------------------------ void turn_start_phase(Field* fd); void prepend_on_death(Field* fd); +void summon_card(Field* fd, unsigned player, const Card* summoned); // return value : 0 -> attacker wins, 1 -> defender wins unsigned play(Field* fd) { @@ -387,6 +388,14 @@ unsigned play(Field* fd) // Evaluate commander fd->current_phase = Field::commander_phase; evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); + + if (fd->effect == Effect::genesis) + { + unsigned index(fd->rand(0, fd->cards.player_assaults.size() - 1)); + Card* summonee(fd->cards.player_assaults[index]); + summon_card(fd, fd->tapi, summonee); + } + // Evaluate structures fd->current_phase = Field::structures_phase; for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->structures.size(); ++fd->current_ci) @@ -1825,6 +1834,9 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::clone_experiment: // Do nothing; these are implemented in the temporary_split skill break; + case Effect::genesis: + // Do nothing; this is implemented in play + break; case Effect::decrepit: replace_on_commanders(cards, enfeeble, enfeeble_all, 1); break; From b1eb58b3d62b100b2e6190230b7c4ce3459c24ca Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 04:56:00 -0500 Subject: [PATCH 047/406] Implement Quicksilver and Friendly Fire --- sim.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/sim.cpp b/sim.cpp index 91079a5f..e038e6e8 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1771,6 +1771,33 @@ void fill_skill_table() //------------------------------------------------------------------------------ // Utility functions for modify_cards. +// Adds the skill " " to all assaults, +// except those who have any instance of either or . +inline void maybe_add_to_assaults(Cards& cards, ActiveSkill new_skill, unsigned magnitude, ActiveSkill conflict) +{ + for(Card* card: cards.cards) + { + if(card->m_type != CardType::assault) + { + continue; + } + + bool conflict(false); + for(auto& skill: card->m_skills) + { + if(std::get<0>(skill) == new_skill || + std::get<0>(skill) == conflict) + { + conflict = true; + } + } + + if(!conflict) + { + card->add_skill(new_skill, magnitude, allfactions); + } + } +} // Adds the skill " " to all commanders. inline void add_to_commanders(Cards& cards, ActiveSkill skill, unsigned magnitude) { @@ -1827,6 +1854,15 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::copycat: // Do nothing; this is implemented in perform_mimic break; + case Effect::quicksilver: + for(Card* card: cards.cards) + { + if(card->m_type == CardType::assault) + { + card->m_evade = true; + } + } + break; case Effect::invigorate: // Do nothing; this is implemented in add_hp break; @@ -1834,6 +1870,10 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::clone_experiment: // Do nothing; these are implemented in the temporary_split skill break; + case Effect::friendly_fire: + replace_on_commanders(cards, chaos, chaos_all, 0); + maybe_add_to_assaults(cards, strike, 1, strike_all); + break; case Effect::genesis: // Do nothing; this is implemented in play break; From 0bb35ea343369db39628c2693db6d41bb7292412 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 05:00:09 -0500 Subject: [PATCH 048/406] Implement Decay --- sim.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sim.cpp b/sim.cpp index e038e6e8..3f57cd4a 100644 --- a/sim.cpp +++ b/sim.cpp @@ -302,6 +302,11 @@ void PlayCard::fieldEffects() { status->m_poisoned = 1; } + else if(fd->effect == Effect::decay) + { + status->m_poisoned = 1; + status->m_diseased = true; + } } // action template <> @@ -1107,6 +1112,10 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { + if(fd->effect == Effect::decay) + { + return; + } c->m_chaos = false; c->m_diseased = false; c->m_enfeebled = 0; @@ -1656,6 +1665,11 @@ void summon_card(Field* fd, unsigned player, const Card* summoned) { card_status.m_poisoned = 1; } + else if(fd->effect == Effect::decay) + { + card_status.m_poisoned = 1; + card_status.m_diseased = true; + } } } void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) @@ -1863,6 +1877,10 @@ void modify_cards(Cards& cards, enum Effect effect) } } break; + case Effect::decay: + // Do nothing; this is implemented in PlayCard::fieldEffects, + // summon_card, and perform_skill + break; case Effect::invigorate: // Do nothing; this is implemented in add_hp break; From ac7b0c87e4bdbc0105e49636534eb2852d4503d1 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 05:07:29 -0500 Subject: [PATCH 049/406] Implement High Skies --- sim.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 3f57cd4a..8a4bd3d1 100644 --- a/sim.cpp +++ b/sim.cpp @@ -745,7 +745,8 @@ struct PerformAttack { if(attack_power(att_status) > 0) { - const bool fly_check(!def_status->m_card->m_flying || att_status->m_card->m_flying || att_status->m_card->m_antiair > 0 || fd->flip()); + const bool fly_check(!def_status->m_card->m_flying || att_status->m_card->m_flying || att_status->m_card->m_antiair > 0 || + (fd->effect != Effect::high_skies && fd->flip())); if(fly_check) // unnecessary check for structures, commander -> fix later ? { // Evaluation order: @@ -1881,6 +1882,9 @@ void modify_cards(Cards& cards, enum Effect effect) // Do nothing; this is implemented in PlayCard::fieldEffects, // summon_card, and perform_skill break; + case Effect::high_skies: + // Do nothing; this is implemented in PerformAttack + break; case Effect::invigorate: // Do nothing; this is implemented in add_hp break; From 85f11996645be6cd75aa25f718a575141c7332bf Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 10:52:36 -0500 Subject: [PATCH 050/406] Split counter_berserk into two functions I need to do this for Impenetrable, since attacks will activate Counter, but not Berserk (presumably) --- sim.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index 8a4bd3d1..a33b1cac 100644 --- a/sim.cpp +++ b/sim.cpp @@ -770,7 +770,8 @@ struct PerformAttack { if(att_status->m_hp > 0) { - counter_berserk(); + counter(); + berserk(); } crush_leech(); } @@ -824,7 +825,7 @@ struct PerformAttack void oa_berserk() {} template - void counter_berserk() + void counter() { if(def_status->m_card->m_counter > 0) { @@ -832,6 +833,11 @@ struct PerformAttack remove_hp(fd, *att_status, counter_dmg); _DEBUG_MSG("%s counter %u by %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); } + } + + template + void berserk() + { att_status->m_berserk += att_status->m_card->m_berserk; } From 1a3ea2d2d544bb7e59a3ddbbf676d1d20556fc7a Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 10:57:05 -0500 Subject: [PATCH 051/406] Implement Impenetrable effect --- sim.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sim.cpp b/sim.cpp index a33b1cac..39b7f29b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -759,6 +759,14 @@ struct PerformAttack // assaults only: (crush, leech if still alive) // check regeneration att_dmg = calculate_attack_damage(); + + // If Impenetrable, force attack damage against walls to be 0, + // but still activate Counter! + if(fd->effect == Effect::impenetrable && def_status->m_card->m_wall) + { + att_dmg = 0; + } + if(att_dmg > 0) { immobilize(); @@ -775,6 +783,14 @@ struct PerformAttack } crush_leech(); } + + // If Impenetrable, force attack damage against walls to be 0, + // but still activate Counter! + if(fd->effect == Effect::impenetrable && def_status->m_card->m_wall && att_status->m_hp > 0) + { + counter(); + } + prepend_on_death(fd); resolve_skill(fd); check_regeneration(fd); @@ -1891,6 +1907,16 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::high_skies: // Do nothing; this is implemented in PerformAttack break; + case Effect::impenetrable: + // Also implemented in PerformAttack + for(Card* card: cards.cards) + { + if(card->m_type == CardType::structure) + { + card->m_refresh = false; + } + } + break; case Effect::invigorate: // Do nothing; this is implemented in add_hp break; From 35eabcd3bbe749d61536316e805e47e6579812bf Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 12:05:24 -0500 Subject: [PATCH 052/406] Add -e flag to usage message --- tyrant_optimize.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index c293a4e6..670bcdb6 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -898,6 +898,7 @@ void usage(int argc, char** argv) std::cout << "\n"; std::cout << "Flags:\n"; std::cout << " -c: don't try to optimize the commander.\n"; + std::cout << " -e : set the battleground effect.\n"; std::cout << " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n"; std::cout << " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n"; std::cout << " -s: use surge (default is fight).\n"; From db7eb338c130580d55fc1eb8985d402e8aa4817c Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 12:52:59 -0500 Subject: [PATCH 053/406] Use evaluate_skill to evaluate Genesis summon If we don't do this, then summoning a unit with on-play skill would cause those skills to remain on the skill queue unevaluated, causing assertion failures. --- sim.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index 39b7f29b..e6b52572 100644 --- a/sim.cpp +++ b/sim.cpp @@ -333,7 +333,6 @@ void PlayCard::onPlaySkills() //------------------------------------------------------------------------------ void turn_start_phase(Field* fd); void prepend_on_death(Field* fd); -void summon_card(Field* fd, unsigned player, const Card* summoned); // return value : 0 -> attacker wins, 1 -> defender wins unsigned play(Field* fd) { @@ -397,8 +396,9 @@ unsigned play(Field* fd) if (fd->effect == Effect::genesis) { unsigned index(fd->rand(0, fd->cards.player_assaults.size() - 1)); - Card* summonee(fd->cards.player_assaults[index]); - summon_card(fd, fd->tapi, summonee); + std::vector skills; + skills.emplace_back(summon, fd->cards.player_assaults[index]->m_id, allfactions); + evaluate_skills(fd, &fd->tap->commander, skills); } // Evaluate structures From 35454b973e99d98220013e16afe3578b065525b1 Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 11:52:55 -0500 Subject: [PATCH 054/406] Read in quest decks from quests.xml --- deck.h | 5 ++++ tyrant_optimize.cpp | 5 ++++ xml.cpp | 72 +++++++++++++++++++++++++++++++++++++++++++++ xml.h | 1 + 4 files changed, 83 insertions(+) diff --git a/deck.h b/deck.h index be2b44ec..b481e81c 100644 --- a/deck.h +++ b/deck.h @@ -107,6 +107,11 @@ struct Decks std::list raid_decks; std::map raid_decks_by_id; std::map raid_decks_by_name; + std::list quest_decks; + std::map quest_decks_by_id; + std::map quest_decks_by_name; + std::map quest_effects_by_id; + std::map quest_effects_by_name; ~Decks() { diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 670bcdb6..c59852d4 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -68,6 +68,11 @@ DeckIface* find_deck(const Decks& decks, std::string name) { return(it2->second); } + auto it4 = decks.quest_decks_by_name.find(name); + if(it4 != decks.quest_decks_by_name.end()) + { + return(it4->second); + } auto it3 = decks.custom_decks.find(name); if(it3 != decks.custom_decks.end()) { diff --git a/xml.cpp b/xml.cpp index d8d57dc2..75c1fcee 100644 --- a/xml.cpp +++ b/xml.cpp @@ -135,6 +135,14 @@ void load_decks_xml(Decks& decks, Cards& cards) { std::cout << "\nException while loading decks from file raids.xml\n"; } + try + { + read_quests(decks, cards, "quests.xml"); + } + catch(const rapidxml::parse_error& e) + { + std::cout << "\nException while loading decks from file quests.xml\n"; + } } //------------------------------------------------------------------------------ void parse_file(const char* filename, std::vector& buffer, xml_document<>& doc) @@ -484,3 +492,67 @@ void read_raids(Decks& decks, Cards& cards, std::string filename) } } } +//------------------------------------------------------------------------------ +void read_quests(Decks& decks, Cards& cards, std::string filename) +{ + std::vector buffer; + xml_document<> doc; + parse_file(filename.c_str(), buffer, doc); + xml_node<>* root = doc.first_node(); + + if(!root) + { + return; + } + + // Seems always_cards is empty for all quests. + std::vector always_cards; + + for(xml_node<>* quest_node = root->first_node(); + quest_node; + quest_node = quest_node->next_sibling()) + { + if(strcmp(quest_node->name(), "step") == 0) + { + std::vector > > some_cards; + xml_node<>* id_node(quest_node->first_node("id")); + int id(id_node ? atoi(id_node->value()) : -1); + std::string deck_name{"Step " + std::string{id_node->value()}}; + xml_node<>* commander_node(quest_node->first_node("commander")); + const Card* commander_card{cards.by_id(atoi(commander_node->value()))}; + xml_node<>* battleground_id_node(quest_node->first_node("battleground_id")); + int battleground_id(battleground_id_node ? atoi(battleground_id_node->value()) : -1); + xml_node<>* deck_node(quest_node->first_node("deck")); + for(xml_node<>* pool_node = deck_node->first_node("card_pool"); + pool_node; + pool_node = pool_node->next_sibling()) + { + if(strcmp(pool_node->name(), "card_pool") == 0) + { + unsigned num_cards_from_pool{static_cast(atoi(pool_node->first_attribute("amount")->value()))}; + std::vector cards_from_pool; + + for(xml_node<>* card_node = pool_node->first_node(); + card_node; + card_node = card_node->next_sibling()) + { + unsigned card_id{static_cast(atoi(card_node->value()))}; + // Handle the replacement art cards + if(cards.replace.find(card_id) != cards.replace.end()) + { + card_id = cards.replace[card_id]; + } + cards_from_pool.push_back(cards.by_id(card_id)); + } + some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); + } + } + decks.quest_decks.push_back(DeckRandom{commander_card, always_cards, some_cards}); + DeckRandom* deck = &decks.quest_decks.back(); + decks.quest_decks_by_id[id] = deck; + decks.quest_effects_by_id[id] = battleground_id; + decks.quest_decks_by_name[deck_name] = deck; + decks.quest_effects_by_name[deck_name] = battleground_id; + } + } +} diff --git a/xml.h b/xml.h index 3a30208a..7969aa63 100644 --- a/xml.h +++ b/xml.h @@ -10,5 +10,6 @@ void load_decks_xml(Decks& decks, Cards& cards); void read_cards(Cards& cards); void read_missions(Decks& decks, Cards& cards, std::string filename); void read_raids(Decks& decks, Cards& cards, std::string filename); +void read_quests(Decks& decks, Cards& cards, std::string filename); #endif From 92429c62a78dffc3c7eb98711e06d3db08ca0b4d Mon Sep 17 00:00:00 2001 From: leftylink Date: Mon, 26 Nov 2012 12:24:10 -0500 Subject: [PATCH 055/406] Add -q flag for quest mode --- tyrant_optimize.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index c59852d4..8f2107d5 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -905,6 +905,7 @@ void usage(int argc, char** argv) std::cout << " -c: don't try to optimize the commander.\n"; std::cout << " -e : set the battleground effect.\n"; std::cout << " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n"; + std::cout << " -q: quest mode. Removes faction restrictions from defending commanders and automatically sets quest effect.\n"; std::cout << " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n"; std::cout << " -s: use surge (default is fight).\n"; std::cout << " -t : set the number of threads, default is 4.\n"; @@ -984,6 +985,38 @@ int main(int argc, char** argv) read_owned_cards(cards, owned_cards); use_owned_cards = true; } + else if(strcmp(argv[argIndex], "-q") == 0) + { + // Strip faction restrictions from commanders of all defending decks + // The assumption is that the attacking deck won't use these commanders. + // If the attacking deck does, its commander will receive this modification as well. + for(auto def_deck: def_decks) + { + // Can't directly use def_deck->commander, as it is const. + unsigned commander_id(def_deck->commander->m_id); + for(auto& skill: cards.cards_by_id[commander_id]->m_skills) + { + skill = std::make_tuple(std::get<0>(skill), std::get<1>(skill), allfactions); + } + } + // Set quest effect: + for(auto deck_parsed: deck_list_parsed) + { + auto effect_id = decks.quest_effects_by_name.find(deck_parsed.first); + if(effect_id == decks.quest_effects_by_name.end()) + { + std::cout << "WARNING: The deck '" << deck_parsed.first << "' has no battleground effect! Are you sure it's a quest deck?\n"; + continue; + } + enum Effect this_effect = static_cast(effect_id->second); + if(effect != Effect::none && this_effect != effect) + { + std::cout << "ERROR: Inconsistent effects! Had " << effect << ", now have " << this_effect << "\n"; + return(7); + } + effect = this_effect; + } + } else if(strcmp(argv[argIndex], "-r") == 0) { ordered = true; From 9527a30de8d5a041cc47d3c329827c5c225dc392 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 21 Jan 2013 15:40:00 +0800 Subject: [PATCH 056/406] 1. Integrate branches: split, raid-skills, werror, bugfixes, emulate, onkill, hash, anp, effects and quests. 2. Fix bug: infuse skill crashes in Windows. 3. Fix bug: clone effect is unexpectedly activated in Windows. 4. Change less-than turnlimit to no-more-than turnlimit. (e.g., -turnlimit 9 for Speedy Lockeheart) 5. Add Occupation top commanders. --- Makefile.win | 17 ++++++ card.h | 5 ++ cards.cpp | 9 +++ read.cpp | 51 ++++++++++++++++ read.h | 1 + sim.cpp | 146 ++++++++++++++++++++++++++++++++++++-------- sim.h | 3 +- tyrant.cpp | 5 ++ tyrant.h | 2 + tyrant_optimize.cpp | 64 ++++++++++++------- xml.cpp | 6 ++ 11 files changed, 261 insertions(+), 48 deletions(-) create mode 100644 Makefile.win diff --git a/Makefile.win b/Makefile.win new file mode 100644 index 00000000..f993f22c --- /dev/null +++ b/Makefile.win @@ -0,0 +1,17 @@ +MAIN := tyrant_optimize +SRCS := $(wildcard *.cpp) +OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) + +CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 +LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_chrono + +all: $(MAIN) + +obj/%.o: %.cpp + $(CXX) $(CPPFLAGS) -o $@ -c $< + +$(MAIN): $(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +clean: + del /q $(MAIN).exe obj\*.o diff --git a/card.h b/card.h index 86a11d6d..dede4db8 100644 --- a/card.h +++ b/card.h @@ -21,6 +21,7 @@ class Card m_delay(0), m_disease(false), m_disease_oa(false), + m_emulate(false), m_evade(false), m_faction(imperial), m_fear(false), @@ -62,6 +63,8 @@ class Card { m_skills_died.push_back(std::make_tuple(v1, v2, v3)); } void add_attacked_skill(ActiveSkill v1, unsigned v2, Faction v3) { m_skills_attacked.push_back(std::make_tuple(v1, v2, v3)); } + void add_kill_skill(ActiveSkill v1, unsigned v2, Faction v3) + { m_skills_kill.push_back(std::make_tuple(v1, v2, v3)); } unsigned m_antiair; unsigned m_armored; @@ -75,6 +78,7 @@ class Card unsigned m_delay; bool m_disease; bool m_disease_oa; + bool m_emulate; bool m_evade; Faction m_faction; bool m_fear; @@ -107,6 +111,7 @@ class Card std::vector m_skills_played; std::vector m_skills_died; std::vector m_skills_attacked; + std::vector m_skills_kill; CardType::CardType m_type; }; diff --git a/cards.cpp b/cards.cpp index c346125f..0774c399 100644 --- a/cards.cpp +++ b/cards.cpp @@ -82,4 +82,13 @@ void Cards::organize() } } } + for(const auto& r: replace) + { + auto replacement = cards_by_id.find(r.second); + if(replacement == cards_by_id.end()) + { + throw std::runtime_error("Replacement " + to_string(r.second) + " for " + to_string(r.first) + " doesn't exist."); + } + cards_by_id[r.first] = replacement->second; + } } diff --git a/read.cpp b/read.cpp index 10cd0cd9..82b6a9d4 100644 --- a/read.cpp +++ b/read.cpp @@ -4,12 +4,63 @@ #include #include #include +#include +#include #include #include "card.h" #include "cards.h" #include "deck.h" +namespace { +const char* base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +// Converts `pairs' pairs of cards in `hash' to a deck. +// Stores resulting card IDs in `ids'. +void hash_to_ids(const char* hash, size_t pairs, + std::vector& ids) +{ + unsigned int last_id = 0; + + for (size_t i = 0; i < pairs; ++i) + { + const char* p0 = strchr(base64_chars, hash[2 * i]); + const char* p1 = strchr(base64_chars, hash[2 * i + 1]); + if (!p0 || !p1) + { + throw std::runtime_error(hash); + } + size_t index0 = p0 - base64_chars; + size_t index1 = p1 - base64_chars; + unsigned int id = (index0 << 6) + index1; + + if (id < 4000) + { + ids.push_back(id); + last_id = id; + } + else for (unsigned int j = 0; j < id - 4001; ++j) + { + ids.push_back(last_id); + } + } +} +} + +// Constructs and returns a deck from `hash'. +// The caller is responsible for freeing the deck. +DeckIface* hash_to_deck(const char* hash, const Cards& cards) +{ + std::vector ids; + size_t pairs = strlen(hash) / 2; + hash_to_ids(hash, pairs, ids); + + return new DeckRandom(cards, ids); +} + void load_decks(Decks& decks, Cards& cards) { if(boost::filesystem::exists("Custom.txt")) diff --git a/read.h b/read.h index 528fb777..5d22c43e 100644 --- a/read.h +++ b/read.h @@ -9,6 +9,7 @@ class Cards; class Decks; class DeckIface; +DeckIface* hash_to_deck(const char* hash, const Cards& cards); void load_decks(Decks& decks, Cards& cards); std::vector > parse_deck_list(std::string list_string); unsigned read_custom_decks(Cards& cards, std::string filename, std::map& custom_decks); diff --git a/sim.cpp b/sim.cpp index e6b52572..5aeb564c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -40,6 +40,7 @@ bool debug_line(false); #define _DEBUG_MSG(format, args...) #endif //------------------------------------------------------------------------------ + CardStatus::CardStatus(const Card* card) : m_card(card), m_index(0), @@ -64,6 +65,7 @@ CardStatus::CardStatus(const Card* card) : m_temporary_split(false) { } + //------------------------------------------------------------------------------ inline void CardStatus::set(const Card* card) { @@ -86,13 +88,13 @@ inline void CardStatus::set(const Card& card) m_frozen = false; m_hp = card.m_health; m_immobilized = false; - infused_skills.clear(); m_infused = false; m_jammed = false; m_poisoned = 0; m_protected = 0; m_rallied = 0; m_weakened = 0; + m_temporary_split = false; } //------------------------------------------------------------------------------ std::string skill_description(const SkillSpec& s) @@ -104,7 +106,10 @@ std::string skill_description(const SkillSpec& s) //------------------------------------------------------------------------------ std::string status_description(CardStatus* status) { - assert(status); + if (!status) // Unknown Action card? + { + return("Action"); + } std::string desc; switch(status->m_card->m_type) { @@ -165,11 +170,12 @@ void resolve_skill(Field* fd) void attack_phase(Field* fd); SkillSpec augmented_skill(CardStatus* status, const SkillSpec& s) { - SkillSpec augmented_s = s; - if(std::get<0>(s) != augment && std::get<0>(s) != augment_all && std::get<0>(s) != summon) + if (std::get<0>(s) == augment || std::get<0>(s) == augment_all || std::get<0>(s) == summon || std::get<1>(s) == 0) { - std::get<1>(augmented_s) += status->m_augmented; + return s; } + SkillSpec augmented_s = s; + std::get<1>(augmented_s) += status->m_augmented; return(augmented_s); } SkillSpec fusioned_skill(const SkillSpec& s) @@ -178,17 +184,28 @@ SkillSpec fusioned_skill(const SkillSpec& s) std::get<1>(fusioned_s) *= 2; return(fusioned_s); } +SkillSpec infused_skill(const SkillSpec& s) +{ + if (std::get<2>(s) == allfactions || std::get<2>(s) == bloodthirsty || helpful_skills.find(std::get<0>(s)) == helpful_skills.end()) + { + return s; + } + SkillSpec infused_s = s; + std::get<2>(infused_s) = bloodthirsty; + return(infused_s); +} void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills) { assert(status); assert(fd->skill_queue.size() == 0); for(auto& skill: skills) { - // Assumptions for fusion: Only active player can use it, and it is incompatible with augment. - // This is fine for now since it only exists on the three Blightbloom structures (can't augment structures) _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(skill).c_str()); bool fusion_active = status->m_card->m_fusion && status->m_player == fd->tapi && fd->fusion_count >= 3; - fd->skill_queue.emplace_back(status, fusion_active ? fusioned_skill(skill) : (status->m_augmented == 0 ? skill : augmented_skill(status, skill))); + auto& augmented_s = status->m_augmented > 0 ? augmented_skill(status, skill) : skill; + auto& fusioned_s = fusion_active ? fusioned_skill(augmented_s) : augmented_s; + auto& infused_s = status->m_infused ? infused_skill(fusioned_s) : fusioned_s; + fd->skill_queue.emplace_back(status, infused_s); resolve_skill(fd); } } @@ -211,6 +228,7 @@ struct PlayCard { setStorage(); placeCard(); + placeDebugMsg(); onPlaySkills(); blitz(); fieldEffects(); @@ -231,18 +249,25 @@ struct PlayCard status->set(card); status->m_index = storage->size() - 1; status->m_player = fd->tapi; + status->m_temporary_split = false; if(fd->turn == 1 && fd->gamemode == tournament && status->m_delay > 0) { ++status->m_delay; } - placeDebugMsg(); } - // assault + structure + // all template void placeDebugMsg() { - _DEBUG_MSG("Placed [%s] as %s %d\n", card->m_name.c_str(), cardtype_names[type].c_str(), storage->size() - 1); + if (storage) + { + _DEBUG_MSG("Placed [%s] as %s %d\n", card->m_name.c_str(), cardtype_names[type].c_str(), storage->size() - 1); + } + else + { + _DEBUG_MSG("Placed [%s] as %s\n", card->m_name.c_str(), cardtype_names[type].c_str()); + } } // all except assault: noop @@ -333,7 +358,7 @@ void PlayCard::onPlaySkills() //------------------------------------------------------------------------------ void turn_start_phase(Field* fd); void prepend_on_death(Field* fd); -// return value : 0 -> attacker wins, 1 -> defender wins +// return value : (raid points) -> attacker wins, 0 -> defender wins unsigned play(Field* fd) { fd->players[0]->commander.m_player = 0; @@ -344,12 +369,24 @@ unsigned play(Field* fd) fd->tip = fd->players[fd->tipi]; fd->fusion_count = 0; fd->end = false; + + // ANP: Last decision point is second-to-last card played. + fd->points_since_last_decision = 0; + unsigned p0_size = fd->players[0]->deck->cards.size(); + fd->last_decision_turn = p0_size * 2 - (surge ? 2 : 3); + // Shuffle deck - while(fd->turn < turn_limit && !fd->end) + while(fd->turn <= turn_limit && !fd->end) { fd->current_phase = Field::playcard_phase; // Initialize stuff, remove dead cards _DEBUG_MSG("##### TURN %u #####\n", fd->turn); + // ANP: If it's the player's turn and he's making a decision, + // reset his points to 0. + if(fd->tapi == 0 && fd->turn <= fd->last_decision_turn) + { + fd->points_since_last_decision = 0; + } turn_start_phase(fd); // Special case: refresh on commander if(fd->tip->commander.m_card->m_refresh && fd->tip->commander.m_hp > 0) @@ -439,8 +476,7 @@ unsigned play(Field* fd) // TODO: Determine whether we need to check for Blitz for the newly-Split unit } // Evaluate skills - // Special case: Gore Typhon's infuse - evaluate_skills(fd, ¤t_status, current_status.m_infused ? current_status.infused_skills : current_status.m_card->m_skills); + evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); // Attack if(!current_status.m_immobilized && current_status.m_hp > 0) { @@ -453,10 +489,30 @@ unsigned play(Field* fd) ++fd->turn; } // defender wins - if(fd->players[0]->commander.m_hp == 0) { _DEBUG_MSG("Defender wins.\n"); return(1); } + if(fd->players[0]->commander.m_hp == 0) + { + _DEBUG_MSG("Defender wins.\n"); + return(0); + } // attacker wins - if(fd->players[1]->commander.m_hp == 0) { _DEBUG_MSG("Attacker wins.\n"); return(0); } - if(fd->turn >= turn_limit) { return(1); } + if(fd->players[1]->commander.m_hp == 0) + { + // ANP: Speedy if last_decision + 10 > turn. + // fd->turn has advanced once past the actual turn the battle has ended. + // So we were speedy if last_decision + 10 > (fd->turn - 1), + // or, equivalently, if last_decision + 10 >= fd->turn. + bool speedy = fd->last_decision_turn + 10 >= fd->turn; + if(fd->points_since_last_decision > 10) + { + fd->points_since_last_decision = 10; + } + _DEBUG_MSG("Attacker wins.\n"); + return(10 + (speedy ? 5 : 0) + fd->points_since_last_decision); + } + if(fd->turn > turn_limit) + { + return(0); + } // Huh? How did we get here? assert(false); @@ -723,6 +779,12 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg) assert(status.m_hp > 0); assert(status.m_card->m_type == CardType::commander); status.m_hp = safe_minus(status.m_hp, dmg); + // ANP: If commander is enemy's, player gets points equal to damage. + // Points are awarded for overkill, so it is correct to simply add dmg. + if(status.m_player == 1) + { + fd->points_since_last_decision += dmg; + } if(status.m_hp == 0) { fd->end = true; } } //------------------------------------------------------------------------------ @@ -773,6 +835,7 @@ struct PerformAttack attack_damage(); siphon_poison_disease(); } + on_kill(); oa(); if(att_dmg > 0) { @@ -818,6 +881,9 @@ struct PerformAttack template void siphon_poison_disease() {} + template + void on_kill() {} + template void oa() { @@ -901,6 +967,19 @@ void PerformAttack::siphon_poison_disease() } } +template<> +void PerformAttack::on_kill() +{ + if(killed_by_attack) + { + for(auto& on_kill_skill: att_status->m_card->m_skills_kill) + { + fd->skill_queue.emplace_back(att_status, on_kill_skill); + resolve_skill(fd); + } + } +} + template<> void PerformAttack::oa_berserk() { def_status->m_berserk += def_status->m_card->m_berserk_oa; } @@ -1171,11 +1250,6 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { c->m_faction = bloodthirsty; c->m_infused = true; - c->infused_skills.clear(); - for(auto& skill: c->m_card->m_skills) - { - c->infused_skills.emplace_back(std::get<0>(skill), std::get<1>(skill), std::get<2>(skill) == allfactions ? allfactions : bloodthirsty); - } } template<> @@ -1547,6 +1621,18 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil perform_skill(fd, src_status, std::get<1>(s)); } } + + // check emulate + Hand* opp = fd->players[opponent(c->m_player)]; + if(opp->assaults.size() > c->m_index) + { + CardStatus& emulator = opp->assaults[c->m_index]; + if(emulator.m_card->m_emulate && skill_predicate(&emulator)) + { + _DEBUG_MSG("Emulate (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), emulator.m_card->m_name.c_str()); + perform_skill(fd, &emulator, std::get<1>(s)); + } + } } } @@ -1561,7 +1647,7 @@ void perform_global_hostile_fast(Field* fd, CardStatus* src_status, const SkillS CardStatus* c(fd->selection_array[s_index]); if(!c->m_card->m_evade || (src_status && src_status->m_chaos) || fd->flip()) { - _DEBUG_MSG("%s (%u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), c->m_card->m_name.c_str()); + _DEBUG_MSG("%s (%u) from %s on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str(), status_description(c).c_str()); perform_skill(fd, c, std::get<1>(s)); // payback if(c->m_card->m_payback && @@ -1610,6 +1696,18 @@ void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSp perform_skill(fd, src_status, std::get<1>(s)); } } + + // check emulate + Hand* opp = fd->players[opponent(c->m_player)]; + if(opp->assaults.size() > c->m_index) + { + CardStatus& emulator = opp->assaults[c->m_index]; + if(emulator.m_card->m_emulate && skill_predicate(&emulator)) + { + _DEBUG_MSG("Emulate (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), emulator.m_card->m_name.c_str()); + perform_skill(fd, &emulator, std::get<1>(s)); + } + } } } diff --git a/sim.h b/sim.h index 8293e906..a4aa5f18 100644 --- a/sim.h +++ b/sim.h @@ -103,7 +103,6 @@ struct CardStatus unsigned m_hp; bool m_immobilized; bool m_infused; - std::vector infused_skills; bool m_jammed; unsigned m_poisoned; unsigned m_protected; @@ -175,6 +174,8 @@ class Field // Meaningless in playcard_phase, // otherwise is the index of the current card in players->structures or players->assaults unsigned current_ci; + unsigned last_decision_turn; + unsigned points_since_last_decision; unsigned fusion_count; diff --git a/tyrant.cpp b/tyrant.cpp index aaa16830..d326cd21 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -14,6 +14,11 @@ std::string skill_names[num_skills] = "trigger_regen", "weaken", "weaken_all"}; +std::set helpful_skills{ + augment, augment_all, cleanse, cleanse_all, heal, heal_all, protect, protect_all, rally, rally_all, + /*repair, repair_all, rush, */supply, +}; + std::string cardtype_names[CardType::num_cardtypes]{"action", "assault", "commander", "structure"}; std::string effect_names[Effect::num_effects] = { diff --git a/tyrant.h b/tyrant.h index 82a56183..96b9b4a9 100644 --- a/tyrant.h +++ b/tyrant.h @@ -2,6 +2,7 @@ #define TYRANT_H_INCLUDED #include +#include #include enum Faction @@ -25,6 +26,7 @@ enum ActiveSkill trigger_regen, // not actually a skill; handles regeneration after strike/siege weaken, weaken_all, num_skills}; extern std::string skill_names[num_skills]; +extern std::set helpful_skills; namespace CardType { enum CardType { diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 8f2107d5..47c5e023 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -37,6 +37,8 @@ #include "xml.h" //#include "timer.hpp" +namespace { bool use_anp{false}; } + using namespace std::placeholders; //------------------------------------------------------------------------------ void print_deck(DeckIface& deck) @@ -151,6 +153,13 @@ std::set top_commanders{ 1049, // Lord Halcyon 1198, // Virulentus 1199, // Lord Silus + 1207, // Typhon Vex + // occupation + 1220, // Anzix + 1223, // Balthazar + 1226, // Gnorlock + 1221, // Nikolas + 1225, // Yuletta }; //------------------------------------------------------------------------------ bool suitable_non_commander(DeckIface& deck, unsigned slot, const Card* card) @@ -401,6 +410,7 @@ void thread_evaluate(boost::barrier& main_barrier, SimulationData& sim, const Process& p) { + bool use_anp_local{use_anp}; while(true) { main_barrier.wait(); @@ -424,7 +434,12 @@ void thread_evaluate(boost::barrier& main_barrier, std::vector thread_score_local(thread_score.size(), 0); //! for(unsigned index(0); index < result.size(); ++index) { - thread_score[index] += result[index] == 0 ? 1 : 0; //! + // If not using ANP and we won, just count 1 win, not points. + if(!use_anp_local && result[index] != 0) + { + result[index] = 1; + } + thread_score[index] += result[index]; //! thread_score_local[index] = thread_score[index]; // ! } ++thread_total; //! @@ -448,7 +463,8 @@ void thread_evaluate(boost::barrier& main_barrier, { score_accum = thread_score_local[0]; } - if(boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum, 0.01) < thread_prev_score) + // TODO: Don't know what we want to do with this for ANP + if(!use_anp && boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum, 0.01) < thread_prev_score) { shared_mutex.lock(); //<<<< //std::cout << thread_total_local << "\n"; @@ -463,6 +479,12 @@ void thread_evaluate(boost::barrier& main_barrier, //------------------------------------------------------------------------------ void print_score_info(const std::pair , unsigned>& results, std::vector& factors) { + if(use_anp) + { + std::cout << "ANP: " << compute_score(results, factors) << std::endl; + return; + } + std::cout << "win%: " << compute_score(results, factors) * 100.0 << " ("; for(auto val: results.first) { @@ -483,7 +505,8 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) std::vector best_cards = d1->cards; bool deck_has_been_improved = true; bool eval_commander = true; - while(deck_has_been_improved && best_score < 1.0) + double best_possible = use_anp ? 25 : 1; + while(deck_has_been_improved && best_score < best_possible) { deck_has_been_improved = false; for(unsigned slot_i(0); slot_i < d1->cards.size(); ++slot_i) @@ -564,7 +587,8 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr std::vector best_cards = d1->cards; bool deck_has_been_improved = true; bool eval_commander = true; - while(deck_has_been_improved && best_score < 1.0) + double best_possible = use_anp ? 25 : 1; + while(deck_has_been_improved && best_score < best_possible) { deck_has_been_improved = false; std::set remaining_cards; @@ -580,7 +604,7 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr { for(const Card* commander_candidate: proc.cards.player_commanders) { - if(best_score == 1.0) { break; } + if(best_score == best_possible) { break; } // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); if(commander_candidate == best_commander) { continue; } @@ -607,7 +631,7 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr } for(const Card* card_candidate: non_commander_cards) { - if(best_score == 1.0) { break; } + if(best_score == best_possible) { break; } // Various checks to check if the card is accepted assert(card_candidate->m_type != CardType::commander); for(unsigned slot_i(0); slot_i < d1->cards.size(); ++slot_i) @@ -902,6 +926,7 @@ void usage(int argc, char** argv) std::cout << " example: \'fear:0.2;slowroll:0.8\' means fear is the defense deck 20% of the time, while slowroll is the defense deck 80% of the time.\n"; std::cout << "\n"; std::cout << "Flags:\n"; + std::cout << " -a: optimize for ANP instead of win rate.\n"; std::cout << " -c: don't try to optimize the commander.\n"; std::cout << " -e : set the battleground effect.\n"; std::cout << " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n"; @@ -941,17 +966,12 @@ int main(int argc, char** argv) for(auto deck_parsed: deck_list_parsed) { DeckIface* def_deck = find_deck(decks, deck_parsed.first); - if(def_deck != nullptr) + if(def_deck == nullptr) { - def_decks.push_back(def_deck); - def_decks_factors.push_back(deck_parsed.second); - } - else - { - std::cout << "The deck " << deck_parsed.first << " was not found. Available decks:\n"; - print_available_decks(decks); - return(5); + def_deck = hash_to_deck(deck_parsed.first.c_str(), cards); } + def_decks.push_back(def_deck); + def_decks_factors.push_back(deck_parsed.second); } enum Effect effect = Effect::none; @@ -964,7 +984,11 @@ int main(int argc, char** argv) std::vector > todo; for(int argIndex(3); argIndex < argc; ++argIndex) { - if(strcmp(argv[argIndex], "-c") == 0) + if(strcmp(argv[argIndex], "-a") == 0) + { + use_anp = true; + } + else if(strcmp(argv[argIndex], "-c") == 0) { keep_commander = true; } @@ -1061,13 +1085,7 @@ int main(int argc, char** argv) } else { - std::cout << "The deck " << att_deck_name << " was not found. Available decks:\n"; - std::cout << "Custom decks:\n"; - for(auto it: decks.custom_decks) - { - std::cout << " " << it.first << "\n"; - } - return(5); + att_deck = hash_to_deck(att_deck_name.c_str(), cards); } print_deck(*att_deck); diff --git a/xml.cpp b/xml.cpp index 75c1fcee..e435f068 100644 --- a/xml.cpp +++ b/xml.cpp @@ -76,6 +76,7 @@ bool handle_global_skill(xml_node<>* node, Card* card) bool played(node->first_attribute("played")); bool died(node->first_attribute("died")); bool attacked(node->first_attribute("attacked")); + bool kill(node->first_attribute("kill")); if(node->first_attribute("all")) { if(played) @@ -85,6 +86,7 @@ bool handle_global_skill(xml_node<>* node, Card* card) } else if(died) {card->add_died_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } else if(attacked) {card->add_attacked_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } + else if(kill) {card->add_kill_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } else {card->add_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } return(true); } @@ -103,6 +105,7 @@ void handle_skill(xml_node<>* node, Card* card) bool played(node->first_attribute("played")); bool died(node->first_attribute("died")); bool attacked(node->first_attribute("attacked")); + bool kill(node->first_attribute("kill")); if(handle_global_skill::type>(node, card)) {} else { @@ -113,6 +116,7 @@ void handle_skill(xml_node<>* node, Card* card) } else if(died) {card->add_died_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } else if(attacked) {card->add_attacked_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } + else if(kill) {card->add_kill_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } else {card->add_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } } } @@ -270,6 +274,8 @@ void read_cards(Cards& cards) if(attacked) { c->m_disease_oa = true; } else {c->m_disease = true; } } + if(strcmp(skill->first_attribute("id")->value(), "emulate") == 0) + { c->m_emulate = true; } if(strcmp(skill->first_attribute("id")->value(), "evade") == 0) { c->m_evade = true; } if(strcmp(skill->first_attribute("id")->value(), "fear") == 0) From 2296277cb36ddcf28d0b0cf907adf35a908fb2be Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 25 Jan 2013 12:43:31 +0800 Subject: [PATCH 057/406] Disable top commanders. Speed up optimization. --- tyrant_optimize.cpp | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 47c5e023..9f7d56a4 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -218,7 +218,7 @@ bool suitable_commander(const Card* card) if(owned_iter->second <= 0) { return(false); } } } - if(top_commanders.find(card->m_id) == top_commanders.end()) { return(false); } +// if(top_commanders.find(card->m_id) == top_commanders.end()) { return(false); } // XXX return(true); } //------------------------------------------------------------------------------ @@ -330,7 +330,8 @@ class Process; void thread_evaluate(boost::barrier& main_barrier, boost::mutex& shared_mutex, SimulationData& sim, - const Process& p); + const Process& p, + unsigned thread_id); //------------------------------------------------------------------------------ class Process { @@ -364,7 +365,7 @@ class Process for(unsigned i(0); i < num_threads; ++i) { threads_data.push_back(new SimulationData(seed + i, cards, decks, def_decks.size(), factors, gamemode, effect)); - threads.push_back(new boost::thread(thread_evaluate, std::ref(main_barrier), std::ref(shared_mutex), std::ref(*threads_data.back()), std::ref(*this))); + threads.push_back(new boost::thread(thread_evaluate, std::ref(main_barrier), std::ref(shared_mutex), std::ref(*threads_data.back()), std::ref(*this), i)); } } @@ -408,7 +409,8 @@ class Process void thread_evaluate(boost::barrier& main_barrier, boost::mutex& shared_mutex, SimulationData& sim, - const Process& p) + const Process& p, + unsigned thread_id) { bool use_anp_local{use_anp}; while(true) @@ -445,7 +447,7 @@ void thread_evaluate(boost::barrier& main_barrier, ++thread_total; //! unsigned thread_total_local{thread_total}; //! shared_mutex.unlock(); //>>>> - if(thread_compare && thread_total_local >= 1 && thread_total_local % 100 == 0) + if(thread_compare && thread_id == 0 && thread_total_local > 1) { unsigned score_accum = 0; // Multiple defense decks case: scaling by factors and approximation of a "discrete" number of events. @@ -463,9 +465,19 @@ void thread_evaluate(boost::barrier& main_barrier, { score_accum = thread_score_local[0]; } - // TODO: Don't know what we want to do with this for ANP - if(!use_anp && boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum, 0.01) < thread_prev_score) + bool compare_stop(false); + if(use_anp_local) { + // TODO: Fix this solution for ANP + // Get a loose, rather than no, upper bound. + static const double best_possible = 25; + compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum / best_possible, 0.01) * best_possible < thread_prev_score); + } + else + { + compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum, 0.01) < thread_prev_score); + } + if(compare_stop) { shared_mutex.lock(); //<<<< //std::cout << thread_total_local << "\n"; thread_compare_stop = true; //! From 9d7c2dcb322fb3649a02999491dc299407e6c57a Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 28 Jan 2013 08:16:41 +0800 Subject: [PATCH 058/406] Use suffix * for upgraded cards. --- read.cpp | 5 +++++ xml.cpp | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/read.cpp b/read.cpp index 82b6a9d4..4ce1116d 100644 --- a/read.cpp +++ b/read.cpp @@ -265,6 +265,11 @@ void read_owned_cards(Cards& cards, std::map& owned_cards) for(boost::tokenizer >::iterator beg=tok.begin(); beg!=tok.end();++beg) { std::string name{*beg}; + auto pos = name.find(','); + if(pos != std::string::npos) + { + name.erase(pos, 1); + } ++beg; assert(beg != tok.end()); unsigned num{static_cast(atoi((*beg).c_str()))}; diff --git a/xml.cpp b/xml.cpp index e435f068..2d6e5824 100644 --- a/xml.cpp +++ b/xml.cpp @@ -209,6 +209,12 @@ void read_cards(Cards& cards) cards.replace[id] = atoi(replace_node->value()); continue; } + else if(id == 484) + { + // XXX: hardcode Necrogeddon replace + cards.replace[id] = 862; + continue; + } xml_node<>* name_node(card->first_node("name")); xml_node<>* attack_node(card->first_node("attack")); xml_node<>* health_node(card->first_node("health")); @@ -229,6 +235,15 @@ void read_cards(Cards& cards) Card* c(new Card()); c->m_id = id; c->m_name = name_node->value(); + auto pos = c->m_name.find(','); + if(pos != std::string::npos) + { + c->m_name.erase(pos, 1); + } + if(set == 5002) + { + c->m_name += '*'; + } if(id < 1000) { c->m_type = CardType::assault; } else if(id < 2000) From cb49655077315462959c1a865ff168ffd075d3ec Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 28 Jan 2013 23:45:13 +0800 Subject: [PATCH 059/406] Be able to add/remove cards during hill-climbing. --- tyrant_optimize.cpp | 288 +++++++++++++++++++++++++++----------------- 1 file changed, 177 insertions(+), 111 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 9f7d56a4..80a54243 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -57,6 +57,15 @@ void print_deck(DeckIface& deck) std::cout << " " << card->m_name << "\n" << std::flush; } } +void print_deck_inline(const Card *commander, std::vector cards) +{ + std::cout << commander->m_name; + for(const Card* card: cards) + { + std::cout << ", " << card->m_name; + } + std::cout << "\n"; +} //------------------------------------------------------------------------------ DeckIface* find_deck(const Decks& decks, std::string name) { @@ -512,79 +521,122 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) double current_score = compute_score(results, proc.factors); double best_score = current_score; // Non-commander cards - auto non_commander_cards = boost::join(boost::join(proc.cards.player_assaults, proc.cards.player_structures), proc.cards.player_actions); + auto non_commander_cards = boost::join(boost::join(boost::join(std::initializer_list{NULL,}, proc.cards.player_assaults), proc.cards.player_structures), proc.cards.player_actions); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; - while(deck_has_been_improved && best_score < best_possible) + for(unsigned slot_i(0), sentry_slot(0); (deck_has_been_improved || slot_i != sentry_slot) && best_score < best_possible; ++ slot_i) { - deck_has_been_improved = false; - for(unsigned slot_i(0); slot_i < d1->cards.size(); ++slot_i) + slot_i %= std::min(10u, static_cast(d1->cards.size() + 1)); + if(deck_has_been_improved) { - if(eval_commander && !keep_commander) - { - for(const Card* commander_candidate: proc.cards.player_commanders) - { - // Various checks to check if the card is accepted - assert(commander_candidate->m_type == CardType::commander); - if(commander_candidate == best_commander) { continue; } - if(!suitable_commander(commander_candidate)) { continue; } - // Place it in the deck - d1->commander = commander_candidate; - // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score); - current_score = compute_score(compare_results, proc.factors); - // Is it better ? - if(current_score > best_score) - { - // Then update best score/commander, print stuff - best_score = current_score; - best_commander = commander_candidate; - deck_has_been_improved = true; - std::cout << "Deck improved: commander -> " << commander_candidate->m_name << ": "; - print_score_info(compare_results, proc.factors); - } - } - // Now that all commanders are evaluated, take the best one - d1->commander = best_commander; - eval_commander = false; - } - for(const Card* card_candidate: non_commander_cards) + sentry_slot = slot_i; + deck_has_been_improved = false; + } + if(eval_commander && !keep_commander) + { + for(const Card* commander_candidate: proc.cards.player_commanders) { // Various checks to check if the card is accepted - assert(card_candidate->m_type != CardType::commander); - if(card_candidate == best_cards[slot_i]) { continue; } - if(!suitable_non_commander(*d1, slot_i, card_candidate)) { continue; } + assert(commander_candidate->m_type == CardType::commander); + if(commander_candidate == best_commander) { continue; } + if(!suitable_commander(commander_candidate)) { continue; } // Place it in the deck - d1->cards[slot_i] = card_candidate; + d1->commander = commander_candidate; // Evaluate new deck auto compare_results = proc.compare(num_iterations, best_score); current_score = compute_score(compare_results, proc.factors); // Is it better ? if(current_score > best_score) { - // Then update best score/slot, print stuff + // Then update best score/commander, print stuff best_score = current_score; - best_cards[slot_i] = card_candidate; - eval_commander = true; + best_commander = commander_candidate; deck_has_been_improved = true; - std::cout << "Deck improved: slot " << slot_i << " -> " << card_candidate->m_name << ": "; + std::cout << "Deck improved: commander -> " << commander_candidate->m_name << ": "; print_score_info(compare_results, proc.factors); } } - // Now that all cards are evaluated, take the best one + // Now that all commanders are evaluated, take the best one + d1->commander = best_commander; + eval_commander = false; + } + for(const Card* card_candidate: non_commander_cards) + { + if(card_candidate) + { + // Various checks to check if the card is accepted + assert(card_candidate->m_type != CardType::commander); + if(slot_i < best_cards.size() && card_candidate == best_cards[slot_i]) { continue; } + if(!suitable_non_commander(*d1, slot_i, card_candidate)) { continue; } + // Place it in the deck + if(slot_i == d1->cards.size()) + { + d1->cards.emplace_back(card_candidate); + } + else + { + d1->cards[slot_i] = card_candidate; + } + } + else + { + if(slot_i == best_cards.size()) { continue; } + // Remove it from the deck + d1->cards.erase(d1->cards.begin() + slot_i); + } + // Evaluate new deck + auto compare_results = proc.compare(num_iterations, best_score); + current_score = compute_score(compare_results, proc.factors); + // Is it better ? + if(current_score > best_score) + { + // Then update best score/slot, print stuff + best_score = current_score; + if(slot_i == best_cards.size()) + { + best_cards.emplace_back(card_candidate); + } + else if(!card_candidate) + { + best_cards.erase(best_cards.begin() + slot_i); + } + else + { + best_cards[slot_i] = card_candidate; + } + eval_commander = true; + deck_has_been_improved = true; + std::cout << "Deck improved: slot " << slot_i << " -> " << (card_candidate ? card_candidate->m_name : "-void-") << ": "; + print_score_info(compare_results, proc.factors); + } + if(d1->cards.size() < best_cards.size()) + { + d1->cards.emplace(d1->cards.begin() + slot_i, best_cards[slot_i]); + } + if(best_score == best_possible) { break; } + } + // Now that all cards are evaluated, take the best one + if(d1->cards.size() == best_cards.size()) + { d1->cards[slot_i] = best_cards[slot_i]; } + else //if(d1->cards.size() > best_cards.size()) + { + d1->cards.pop_back(); + } } - std::cout << "Best deck: " << best_score * 100.0 << "%\n"; - std::cout << best_commander->m_name; - for(const Card* card: best_cards) + if(use_anp) { - std::cout << ", " << card->m_name; + std::cout << "Best deck: " << best_score << "\n"; } - std::cout << "\n"; + else + { + std::cout << "Best deck: " << best_score * 100.0 << "%\n"; + } + print_deck_inline(best_commander, best_cards); } //------------------------------------------------------------------------------ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& proc) @@ -594,95 +646,109 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr double current_score = compute_score(results, proc.factors); double best_score = current_score; // Non-commander cards - auto non_commander_cards = boost::join(boost::join(proc.cards.player_assaults, proc.cards.player_structures), proc.cards.player_actions); + auto non_commander_cards = boost::join(boost::join(boost::join(std::initializer_list{NULL,}, proc.cards.player_assaults), proc.cards.player_structures), proc.cards.player_actions); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; - while(deck_has_been_improved && best_score < best_possible) + for(unsigned from_slot(0), sentry_slot(0); (deck_has_been_improved || from_slot != sentry_slot) && best_score < best_possible; ++ from_slot) { - deck_has_been_improved = false; - std::set remaining_cards; - for(unsigned i = 0; i < best_cards.size(); ++i) + from_slot %= std::min(10u, static_cast(d1->cards.size() + 1)); + if(deck_has_been_improved) { - remaining_cards.insert(i); + sentry_slot = from_slot; + deck_has_been_improved = false; } - while(!remaining_cards.empty()) + if(eval_commander && !keep_commander) { - unsigned current_slot(*remaining_cards.begin()); - remaining_cards.erase(remaining_cards.begin()); - if(eval_commander && !keep_commander) + for(const Card* commander_candidate: proc.cards.player_commanders) { - for(const Card* commander_candidate: proc.cards.player_commanders) + if(best_score == best_possible) { break; } + // Various checks to check if the card is accepted + assert(commander_candidate->m_type == CardType::commander); + if(commander_candidate == best_commander) { continue; } + if(!suitable_commander(commander_candidate)) { continue; } + // Place it in the deck + d1->commander = commander_candidate; + // Evaluate new deck + auto compare_results = proc.compare(num_iterations, best_score); + current_score = compute_score(compare_results, proc.factors); + // Is it better ? + if(current_score > best_score) { - if(best_score == best_possible) { break; } - // Various checks to check if the card is accepted - assert(commander_candidate->m_type == CardType::commander); - if(commander_candidate == best_commander) { continue; } - if(!suitable_commander(commander_candidate)) { continue; } - // Place it in the deck - d1->commander = commander_candidate; - // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score); - current_score = compute_score(compare_results, proc.factors); - // Is it better ? - if(current_score > best_score) - { - // Then update best score/commander, print stuff - best_score = current_score; - best_commander = commander_candidate; - deck_has_been_improved = true; - std::cout << "Deck improved: commander -> " << commander_candidate->m_name << ": "; - print_score_info(compare_results, proc.factors); - } + // Then update best score/commander, print stuff + best_score = current_score; + best_commander = commander_candidate; + deck_has_been_improved = true; + std::cout << "Deck improved: commander -> " << commander_candidate->m_name << ": "; + print_score_info(compare_results, proc.factors); } - // Now that all commanders are evaluated, take the best one - d1->commander = best_commander; - eval_commander = false; } - for(const Card* card_candidate: non_commander_cards) + // Now that all commanders are evaluated, take the best one + d1->commander = best_commander; + eval_commander = false; + } + for(const Card* card_candidate: non_commander_cards) + { + // Various checks to check if the card is accepted + assert(!card_candidate || card_candidate->m_type != CardType::commander); + for(unsigned to_slot(0); to_slot < (card_candidate ? d1->cards.size() : 1); ++to_slot) { - if(best_score == best_possible) { break; } - // Various checks to check if the card is accepted - assert(card_candidate->m_type != CardType::commander); - for(unsigned slot_i(0); slot_i < d1->cards.size(); ++slot_i) + if(card_candidate) { // Various checks to check if the card is accepted - if(card_candidate == best_cards[slot_i]) { continue; } - if(!suitable_non_commander(*d1, current_slot, card_candidate)) { continue; } + if(card_candidate == best_cards[to_slot]) { continue; } + if(!suitable_non_commander(*d1, from_slot, card_candidate)) { continue; } // Place it in the deck - d1->cards.erase(d1->cards.begin() + current_slot); - d1->cards.insert(d1->cards.begin() + slot_i, card_candidate); - // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score); - current_score = compute_score(compare_results, proc.factors); - // Is it better ? - if(current_score > best_score) + if(from_slot < d1->cards.size()) + { + d1->cards.erase(d1->cards.begin() + from_slot); + } + d1->cards.insert(d1->cards.begin() + to_slot, card_candidate); + } + else + { + if(from_slot == best_cards.size()) { continue; } + // Remove it from the deck + d1->cards.erase(d1->cards.begin() + from_slot); + } + // Evaluate new deck + auto compare_results = proc.compare(num_iterations, best_score); + current_score = compute_score(compare_results, proc.factors); + // Is it better ? + if(current_score > best_score) + { + // Then update best score/slot, print stuff + std::cout << "Deck improved: " << from_slot << " " << (from_slot < best_cards.size() ? best_cards[from_slot]->m_name : "-void-") << + " -> " << (card_candidate ? to_slot : d1->cards.size()) << " " << (card_candidate ? card_candidate->m_name : "-void-") << ": "; + best_score = current_score; + if(from_slot < best_cards.size()) + { + best_cards.erase(best_cards.begin() + from_slot); + } + if(card_candidate) { - // Then update best score/slot, print stuff - std::cout << "Deck improved: " << current_slot << " " << best_cards[current_slot]->m_name << " -> " << slot_i << " " << card_candidate->m_name << ": "; - best_score = current_score; - best_cards.erase(best_cards.begin() + current_slot); - best_cards.insert(best_cards.begin() + slot_i, card_candidate); - eval_commander = true; - deck_has_been_improved = true; - print_score_info(compare_results, proc.factors); + best_cards.insert(best_cards.begin() + to_slot, card_candidate); } - d1->cards = best_cards; + eval_commander = true; + deck_has_been_improved = true; + print_score_info(compare_results, proc.factors); } + d1->cards = best_cards; } - // Now that all cards are evaluated, take the best one - // d1->cards[slot_i] = best_cards[slot_i]; + if(best_score == best_possible) { break; } } } - std::cout << "Best deck: " << best_score * 100.0 << "%\n"; - std::cout << best_commander->m_name; - for(const Card* card: best_cards) + if(use_anp) { - std::cout << ", " << card->m_name; + std::cout << "Best deck: " << best_score << "\n"; } - std::cout << "\n"; + else + { + std::cout << "Best deck: " << best_score * 100.0 << "%\n"; + } + print_deck_inline(best_commander, best_cards); } //------------------------------------------------------------------------------ // Implements iteration over all combination of k elements from n elements. From 0724fad387b832bd83b8ef0291a1ceeadfc7840b Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 29 Jan 2013 11:52:59 +0800 Subject: [PATCH 060/406] Fix bug: run forever --- tyrant_optimize.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 80a54243..ce1d171d 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -527,9 +527,8 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; - for(unsigned slot_i(0), sentry_slot(0); (deck_has_been_improved || slot_i != sentry_slot) && best_score < best_possible; ++ slot_i) + for(unsigned slot_i(0), sentry_slot(0); (deck_has_been_improved || slot_i != sentry_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(10u, d1->cards.size() + 1)) { - slot_i %= std::min(10u, static_cast(d1->cards.size() + 1)); if(deck_has_been_improved) { sentry_slot = slot_i; @@ -652,9 +651,8 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; - for(unsigned from_slot(0), sentry_slot(0); (deck_has_been_improved || from_slot != sentry_slot) && best_score < best_possible; ++ from_slot) + for(unsigned from_slot(0), sentry_slot(0); (deck_has_been_improved || from_slot != sentry_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(10u, d1->cards.size() + 1)) { - from_slot %= std::min(10u, static_cast(d1->cards.size() + 1)); if(deck_has_been_improved) { sentry_slot = from_slot; From 5e7043166a0014d108103ad91f388d11b33779a4 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 29 Jan 2013 12:38:12 +0800 Subject: [PATCH 061/406] Print out the new deck after each evolution. --- tyrant_optimize.cpp | 61 ++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index ce1d171d..3224f35c 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -57,15 +57,6 @@ void print_deck(DeckIface& deck) std::cout << " " << card->m_name << "\n" << std::flush; } } -void print_deck_inline(const Card *commander, std::vector cards) -{ - std::cout << commander->m_name; - for(const Card* card: cards) - { - std::cout << ", " << card->m_name; - } - std::cout << "\n"; -} //------------------------------------------------------------------------------ DeckIface* find_deck(const Decks& decks, std::string name) { @@ -514,6 +505,24 @@ void print_score_info(const std::pair , unsigned>& results std::cout << "out of " << results.second << ")\n" << std::flush; } //------------------------------------------------------------------------------ +void print_deck_inline(const double score, const Card *commander, std::vector cards) +{ + if(use_anp) + { + std::cout << score << ": "; + } + else + { + std::cout << score * 100.0 << "%: "; + } + std::cout << commander->m_name; + for(const Card* card: cards) + { + std::cout << ", " << card->m_name; + } + std::cout << std::endl; +} +//------------------------------------------------------------------------------ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) { auto results = proc.evaluate(num_iterations); @@ -524,6 +533,7 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) auto non_commander_cards = boost::join(boost::join(boost::join(std::initializer_list{NULL,}, proc.cards.player_assaults), proc.cards.player_structures), proc.cards.player_actions); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; + print_deck_inline(best_score, best_commander, best_cards); bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; @@ -556,6 +566,7 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) deck_has_been_improved = true; std::cout << "Deck improved: commander -> " << commander_candidate->m_name << ": "; print_score_info(compare_results, proc.factors); + print_deck_inline(best_score, best_commander, best_cards); } } // Now that all commanders are evaluated, take the best one @@ -610,6 +621,7 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) deck_has_been_improved = true; std::cout << "Deck improved: slot " << slot_i << " -> " << (card_candidate ? card_candidate->m_name : "-void-") << ": "; print_score_info(compare_results, proc.factors); + print_deck_inline(best_score, best_commander, best_cards); } if(d1->cards.size() < best_cards.size()) { @@ -627,15 +639,8 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) d1->cards.pop_back(); } } - if(use_anp) - { - std::cout << "Best deck: " << best_score << "\n"; - } - else - { - std::cout << "Best deck: " << best_score * 100.0 << "%\n"; - } - print_deck_inline(best_commander, best_cards); + std::cout << "Best Deck: "; + print_deck_inline(best_score, best_commander, best_cards); } //------------------------------------------------------------------------------ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& proc) @@ -648,6 +653,7 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr auto non_commander_cards = boost::join(boost::join(boost::join(std::initializer_list{NULL,}, proc.cards.player_assaults), proc.cards.player_structures), proc.cards.player_actions); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; + print_deck_inline(best_score, best_commander, best_cards); bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; @@ -681,6 +687,7 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr deck_has_been_improved = true; std::cout << "Deck improved: commander -> " << commander_candidate->m_name << ": "; print_score_info(compare_results, proc.factors); + print_deck_inline(best_score, best_commander, best_cards); } } // Now that all commanders are evaluated, take the best one @@ -732,21 +739,15 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr eval_commander = true; deck_has_been_improved = true; print_score_info(compare_results, proc.factors); + print_deck_inline(best_score, best_commander, best_cards); } d1->cards = best_cards; } if(best_score == best_possible) { break; } } } - if(use_anp) - { - std::cout << "Best deck: " << best_score << "\n"; - } - else - { - std::cout << "Best deck: " << best_score * 100.0 << "%\n"; - } - print_deck_inline(best_commander, best_cards); + std::cout << "Best Deck: "; + print_deck_inline(best_score, best_commander, best_cards); } //------------------------------------------------------------------------------ // Implements iteration over all combination of k elements from n elements. @@ -854,8 +855,7 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig best_score = new_score; best_deck = deck; print_score_info(new_results, proc.factors); - print_deck(deck); - std::cout << std::flush; + print_deck_inline(best_score, commander, deck_cards); } //++num; // num_cards = num_cards_to_combine ... @@ -894,8 +894,7 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig best_score = new_score; best_deck = deck; print_score_info(new_results, proc.factors); - print_deck(deck); - std::cout << std::flush; + print_deck_inline(best_score, commander, deck_cards); } ++total_num_combinations_test; finished = cardAmounts.next(); From 693dc25ac27fd3a905b3831187709a8dd11ccef7 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 31 Jan 2013 19:05:00 +0800 Subject: [PATCH 062/406] Support new ownercards.txt. Add sim option. --- cards.cpp | 18 +----------- cards.h | 1 - read.cpp | 30 +++++++++++++++----- tyrant_optimize.cpp | 69 ++++++++++++++++++++++++++++++++++----------- xml.cpp | 33 ---------------------- 5 files changed, 76 insertions(+), 75 deletions(-) diff --git a/cards.cpp b/cards.cpp index 0774c399..b7f3a934 100644 --- a/cards.cpp +++ b/cards.cpp @@ -72,23 +72,7 @@ void Cards::organize() break; } } - if(player_cards_by_name.find(card->m_name) != player_cards_by_name.end()) - { - throw std::runtime_error("While trying to insert the card [" + card->m_name + ", id " + to_string(card->m_id) + "] in the player_cards_by_name map: the key already exists [id " + to_string(player_cards_by_name[card->m_name]->m_id) + "]."); - } - else - { - player_cards_by_name[card->m_name] = card; - } - } - } - for(const auto& r: replace) - { - auto replacement = cards_by_id.find(r.second); - if(replacement == cards_by_id.end()) - { - throw std::runtime_error("Replacement " + to_string(r.second) + " for " + to_string(r.first) + " doesn't exist."); + player_cards_by_name[card->m_name] = card; } - cards_by_id[r.first] = replacement->second; } } diff --git a/cards.h b/cards.h index 4772cdcf..60e7329b 100644 --- a/cards.h +++ b/cards.h @@ -19,7 +19,6 @@ struct Cards std::vector player_assaults; std::vector player_structures; std::vector player_actions; - std::map replace; const Card * by_id(unsigned id) const; void organize(); }; diff --git a/read.cpp b/read.cpp index 4ce1116d..c351e47b 100644 --- a/read.cpp +++ b/read.cpp @@ -265,22 +265,38 @@ void read_owned_cards(Cards& cards, std::map& owned_cards) for(boost::tokenizer >::iterator beg=tok.begin(); beg!=tok.end();++beg) { std::string name{*beg}; - auto pos = name.find(','); - if(pos != std::string::npos) + Card *card = NULL; + if(name[0] == '[') { - name.erase(pos, 1); + auto card_itr = cards.cards_by_id.find(atoi(name.substr(1).c_str())); + if(card_itr != cards.cards_by_id.end()) + { + card = card_itr->second; + } + } + else + { + auto pos = name.find(','); + if(pos != std::string::npos) + { + name.erase(pos, 1); + } + auto card_itr = cards.player_cards_by_name.find(name); + if(card_itr != cards.player_cards_by_name.end()) + { + card = card_itr->second; + } } ++beg; assert(beg != tok.end()); unsigned num{static_cast(atoi((*beg).c_str()))}; - auto card_itr = cards.player_cards_by_name.find(name); - if(card_itr == cards.player_cards_by_name.end()) + if(card) { - std::cerr << "Error in file ownedcards.txt, the card \"" << name << "\" does not seem to be a valid card.\n"; + owned_cards[card->m_id] = num; } else { - owned_cards[card_itr->second->m_id] = num; + std::cerr << "Error in file ownedcards.txt, the card \"" << name << "\" does not seem to be a valid card.\n"; } } } diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 3224f35c..458674a8 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -58,6 +58,34 @@ void print_deck(DeckIface& deck) } } //------------------------------------------------------------------------------ +std::string deck_hash(const Card* commander, const std::vector& cards) +{ + std::string base64= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::stringstream ios; + ios << base64[commander->m_id / 64]; + ios << base64[commander->m_id % 64]; + for(const Card* card: cards) + { + ios << base64[card->m_id / 64]; + ios << base64[card->m_id % 64]; + } + return ios.str(); +} +//------------------------------------------------------------------------------ +std::string card_id_name(const Card* card) +{ + std::stringstream ios; + if(card) + { + ios << "[" << card->m_id << "] " << card->m_name; + } + else + { + ios << "-void-"; + } + return ios.str(); +} +//------------------------------------------------------------------------------ DeckIface* find_deck(const Decks& decks, std::string name) { auto it1 = decks.mission_decks_by_name.find(name); @@ -537,11 +565,11 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; - for(unsigned slot_i(0), sentry_slot(0); (deck_has_been_improved || slot_i != sentry_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(10u, d1->cards.size() + 1)) + for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(10, d1->cards.size() + 1)) { if(deck_has_been_improved) { - sentry_slot = slot_i; + dead_slot = slot_i; deck_has_been_improved = false; } if(eval_commander && !keep_commander) @@ -564,7 +592,7 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) best_score = current_score; best_commander = commander_candidate; deck_has_been_improved = true; - std::cout << "Deck improved: commander -> " << commander_candidate->m_name << ": "; + std::cout << "Deck improved: " << deck_hash(best_commander, best_cards) << " commander -> " << card_id_name(commander_candidate) << ": "; print_score_info(compare_results, proc.factors); print_deck_inline(best_score, best_commander, best_cards); } @@ -619,10 +647,11 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) } eval_commander = true; deck_has_been_improved = true; - std::cout << "Deck improved: slot " << slot_i << " -> " << (card_candidate ? card_candidate->m_name : "-void-") << ": "; + std::cout << "Deck improved: " << deck_hash(best_commander, best_cards) << " slot " << slot_i << " -> " << card_id_name(card_candidate) << ": "; print_score_info(compare_results, proc.factors); print_deck_inline(best_score, best_commander, best_cards); } + d1->cards = best_cards; if(d1->cards.size() < best_cards.size()) { d1->cards.emplace(d1->cards.begin() + slot_i, best_cards[slot_i]); @@ -630,14 +659,7 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) if(best_score == best_possible) { break; } } // Now that all cards are evaluated, take the best one - if(d1->cards.size() == best_cards.size()) - { - d1->cards[slot_i] = best_cards[slot_i]; - } - else //if(d1->cards.size() > best_cards.size()) - { - d1->cards.pop_back(); - } + d1->cards = best_cards; } std::cout << "Best Deck: "; print_deck_inline(best_score, best_commander, best_cards); @@ -657,11 +679,11 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; - for(unsigned from_slot(0), sentry_slot(0); (deck_has_been_improved || from_slot != sentry_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(10u, d1->cards.size() + 1)) + for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(10, d1->cards.size() + 1)) { if(deck_has_been_improved) { - sentry_slot = from_slot; + dead_slot = from_slot; deck_has_been_improved = false; } if(eval_commander && !keep_commander) @@ -685,7 +707,7 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr best_score = current_score; best_commander = commander_candidate; deck_has_been_improved = true; - std::cout << "Deck improved: commander -> " << commander_candidate->m_name << ": "; + std::cout << "Deck improved: " << deck_hash(best_commander, best_cards) << " commander -> " << card_id_name(commander_candidate) << ": "; print_score_info(compare_results, proc.factors); print_deck_inline(best_score, best_commander, best_cards); } @@ -725,8 +747,6 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr if(current_score > best_score) { // Then update best score/slot, print stuff - std::cout << "Deck improved: " << from_slot << " " << (from_slot < best_cards.size() ? best_cards[from_slot]->m_name : "-void-") << - " -> " << (card_candidate ? to_slot : d1->cards.size()) << " " << (card_candidate ? card_candidate->m_name : "-void-") << ": "; best_score = current_score; if(from_slot < best_cards.size()) { @@ -738,6 +758,8 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr } eval_commander = true; deck_has_been_improved = true; + std::cout << "Deck improved: " << deck_hash(best_commander, best_cards) << " " << from_slot << " " << card_id_name(from_slot < best_cards.size() ? best_cards[from_slot] : NULL) << + " -> " << (card_candidate ? to_slot : d1->cards.size()) << " " << card_id_name(card_candidate) << ": "; print_score_info(compare_results, proc.factors); print_deck_inline(best_score, best_commander, best_cards); } @@ -948,6 +970,7 @@ void exhaustive_k(unsigned num_iterations, unsigned var_k, Process& proc) enum Operation { bruteforce, climb, + simulate, fightonce }; //------------------------------------------------------------------------------ @@ -1013,6 +1036,8 @@ void usage(int argc, char** argv) std::cout << "Operations:\n"; std::cout << "brute : find the best combination of different cards, using up to battles to evaluate a deck.\n"; std::cout << "climb : perform hill-climbing starting from the given attack deck, using up to battles to evaluate a deck.\n"; + std::cout << "sim : simulate battles to evaluate a deck.\n"; + std::cout << "debug: very verbose output. only one battle. testing purposes only.\n"; } int main(int argc, char** argv) @@ -1144,6 +1169,11 @@ int main(int argc, char** argv) todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex+1]), 0u, climb)); argIndex += 1; } + else if(strcmp(argv[argIndex], "sim") == 0) + { + todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex+1]), 0u, simulate)); + argIndex += 1; + } else if(strcmp(argv[argIndex], "debug") == 0) { debug_print = true; @@ -1194,6 +1224,11 @@ int main(int argc, char** argv) } break; } + case simulate: { + auto results = p.evaluate(std::get<0>(op)); + print_score_info(results,p.factors); + break; + } case fightonce: { p.evaluate(1); break; diff --git a/xml.cpp b/xml.cpp index 2d6e5824..b34b6f33 100644 --- a/xml.cpp +++ b/xml.cpp @@ -202,19 +202,6 @@ void read_cards(Cards& cards) { xml_node<>* id_node(card->first_node("id")); int id(id_node ? atoi(id_node->value()) : -1); - // Replacement art card - xml_node<>* replace_node(card->first_node("replace")); - if(replace_node) - { - cards.replace[id] = atoi(replace_node->value()); - continue; - } - else if(id == 484) - { - // XXX: hardcode Necrogeddon replace - cards.replace[id] = 862; - continue; - } xml_node<>* name_node(card->first_node("name")); xml_node<>* attack_node(card->first_node("attack")); xml_node<>* health_node(card->first_node("health")); @@ -418,11 +405,6 @@ void read_missions(Decks& decks, Cards& cards, std::string filename) card_node = card_node->next_sibling()) { unsigned card_id{static_cast(atoi(card_node->value()))}; - // Handle the replacement art cards - if(cards.replace.find(card_id) != cards.replace.end()) - { - card_id = cards.replace[card_id]; - } card_ids.push_back(card_id); } decks.mission_decks.push_back(DeckRandom{cards, card_ids}); @@ -468,11 +450,6 @@ void read_raids(Decks& decks, Cards& cards, std::string filename) card_node = card_node->next_sibling()) { unsigned card_id{static_cast(atoi(card_node->value()))}; - // Handle the replacement art cards - if(cards.replace.find(card_id) != cards.replace.end()) - { - card_id = cards.replace[card_id]; - } always_cards.push_back(cards.by_id(card_id)); } } @@ -496,11 +473,6 @@ void read_raids(Decks& decks, Cards& cards, std::string filename) { continue; } - // Handle the replacement art cards - if(cards.replace.find(card_id) != cards.replace.end()) - { - card_id = cards.replace[card_id]; - } cards_from_pool.push_back(cards.by_id(card_id)); } some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); @@ -558,11 +530,6 @@ void read_quests(Decks& decks, Cards& cards, std::string filename) card_node = card_node->next_sibling()) { unsigned card_id{static_cast(atoi(card_node->value()))}; - // Handle the replacement art cards - if(cards.replace.find(card_id) != cards.replace.end()) - { - card_id = cards.replace[card_id]; - } cards_from_pool.push_back(cards.by_id(card_id)); } some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); From 7b260df0a27414d42788b0e1b9482e0e8874b060 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 31 Jan 2013 21:32:59 +0800 Subject: [PATCH 063/406] Deprecate replace. Introduce base_id to check unique. --- card.h | 2 ++ cards.cpp | 27 ++++++++++++++++++++++++++- tyrant_optimize.cpp | 15 ++++----------- xml.cpp | 10 +--------- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/card.h b/card.h index dede4db8..7e7b2298 100644 --- a/card.h +++ b/card.h @@ -30,6 +30,7 @@ class Card m_fusion(false), m_health(0), m_id(0), + m_base_id(0), m_immobilize(false), m_intercept(false), m_leech(0), @@ -87,6 +88,7 @@ class Card bool m_fusion; unsigned m_health; unsigned m_id; + unsigned m_base_id; // Cards sharing the same name share a unique base id. (for "unique" check) bool m_immobilize; bool m_intercept; unsigned m_leech; diff --git a/cards.cpp b/cards.cpp index b7f3a934..d272481c 100644 --- a/cards.cpp +++ b/cards.cpp @@ -44,6 +44,15 @@ void Cards::organize() player_actions.clear(); for(Card* card: cards) { + auto pos = card->m_name.find(','); + if(pos != std::string::npos) + { + card->m_name.erase(pos, 1); + } + if(card->m_set == 5002) + { + card->m_name += '*'; + } cards_by_id[card->m_id] = card; // Card available to players if(card->m_set != -1) @@ -72,7 +81,23 @@ void Cards::organize() break; } } - player_cards_by_name[card->m_name] = card; + if(player_cards_by_name.find(card->m_name) == player_cards_by_name.end()) + { + player_cards_by_name[card->m_name] = card; + } + } + } + for(Card* card: cards) + { + std::string base_name = card->m_name; + if(card->m_set == 5002) + { + base_name.erase(base_name.size() - 1); + } + auto card_itr = player_cards_by_name.find(base_name); + if(card_itr != player_cards_by_name.end() && card_itr->second != card) + { + card->m_base_id = card_itr->second->m_id; } } } diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 458674a8..749b8143 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -223,7 +223,7 @@ bool suitable_non_commander(DeckIface& deck, unsigned slot, const Card* card) { for(unsigned i(0); i < deck.cards.size(); ++i) { - if(i != slot && deck.cards[i]->m_id == card->m_id) + if(i != slot && deck.cards[i]->m_base_id == card->m_base_id) { return(false); } @@ -747,19 +747,12 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr if(current_score > best_score) { // Then update best score/slot, print stuff + std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards) << " " << from_slot << " " << card_id_name(from_slot < best_cards.size() ? best_cards[from_slot] : NULL) << + " -> " << (card_candidate ? to_slot : d1->cards.size()) << " " << card_id_name(card_candidate) << ": "; best_score = current_score; - if(from_slot < best_cards.size()) - { - best_cards.erase(best_cards.begin() + from_slot); - } - if(card_candidate) - { - best_cards.insert(best_cards.begin() + to_slot, card_candidate); - } + best_cards = d1->cards; eval_commander = true; deck_has_been_improved = true; - std::cout << "Deck improved: " << deck_hash(best_commander, best_cards) << " " << from_slot << " " << card_id_name(from_slot < best_cards.size() ? best_cards[from_slot] : NULL) << - " -> " << (card_candidate ? to_slot : d1->cards.size()) << " " << card_id_name(card_candidate) << ": "; print_score_info(compare_results, proc.factors); print_deck_inline(best_score, best_commander, best_cards); } diff --git a/xml.cpp b/xml.cpp index b34b6f33..a9cf4ef2 100644 --- a/xml.cpp +++ b/xml.cpp @@ -221,16 +221,8 @@ void read_cards(Cards& cards) } Card* c(new Card()); c->m_id = id; + c->m_base_id = id; c->m_name = name_node->value(); - auto pos = c->m_name.find(','); - if(pos != std::string::npos) - { - c->m_name.erase(pos, 1); - } - if(set == 5002) - { - c->m_name += '*'; - } if(id < 1000) { c->m_type = CardType::assault; } else if(id < 2000) From ac252974595470c3ffd2d950d7bdb2db17edcf61 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 1 Feb 2013 09:56:27 +0800 Subject: [PATCH 064/406] Add a -fixedlen option. --- tyrant_optimize.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 749b8143..0e9e1834 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -116,6 +116,7 @@ DeckIface* find_deck(const Decks& decks, std::string name) //------------------------------------------------------------------------------ std::map owned_cards; bool use_owned_cards{false}; +bool fixed_len{false}; // No raid rewards from 500 and 1k honor for ancient raids // No very hard to get rewards (level >= 150, faction >= 13) @@ -565,7 +566,7 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; - for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(10, d1->cards.size() + 1)) + for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(10, d1->cards.size() + (fixed_len ? 0 : 1))) { if(deck_has_been_improved) { @@ -621,7 +622,7 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) } else { - if(slot_i == best_cards.size()) { continue; } + if(fixed_len || slot_i == best_cards.size() || best_cards.size() == 1) { continue; } // Remove it from the deck d1->cards.erase(d1->cards.begin() + slot_i); } @@ -631,6 +632,8 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) // Is it better ? if(current_score > best_score) { + std::cout << "Deck improved: " << deck_hash(best_commander, best_cards) << " " << slot_i << " " << card_id_name(slot_i < best_cards.size() ? best_cards[slot_i] : NULL) << + " -> " << card_id_name(card_candidate) << ": "; // Then update best score/slot, print stuff best_score = current_score; if(slot_i == best_cards.size()) @@ -647,7 +650,6 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) } eval_commander = true; deck_has_been_improved = true; - std::cout << "Deck improved: " << deck_hash(best_commander, best_cards) << " slot " << slot_i << " -> " << card_id_name(card_candidate) << ": "; print_score_info(compare_results, proc.factors); print_deck_inline(best_score, best_commander, best_cards); } @@ -679,7 +681,7 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; - for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(10, d1->cards.size() + 1)) + for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(10, d1->cards.size() + (fixed_len ? 0 : 1))) { if(deck_has_been_improved) { @@ -720,12 +722,12 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr { // Various checks to check if the card is accepted assert(!card_candidate || card_candidate->m_type != CardType::commander); - for(unsigned to_slot(0); to_slot < (card_candidate ? d1->cards.size() : 1); ++to_slot) + for(unsigned to_slot(card_candidate ? 0 : d1->cards.size() - 1); to_slot < d1->cards.size() + (from_slot < d1->cards.size() ? 0 : 1); ++to_slot) { if(card_candidate) { // Various checks to check if the card is accepted - if(card_candidate == best_cards[to_slot]) { continue; } + if(to_slot < best_cards.size() && card_candidate == best_cards[to_slot]) { continue; } if(!suitable_non_commander(*d1, from_slot, card_candidate)) { continue; } // Place it in the deck if(from_slot < d1->cards.size()) @@ -736,7 +738,7 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr } else { - if(from_slot == best_cards.size()) { continue; } + if(fixed_len || from_slot == best_cards.size() || best_cards.size() == 1) { continue; } // Remove it from the deck d1->cards.erase(d1->cards.begin() + from_slot); } @@ -1025,6 +1027,7 @@ void usage(int argc, char** argv) std::cout << " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n"; std::cout << " -s: use surge (default is fight).\n"; std::cout << " -t : set the number of threads, default is 4.\n"; + std::cout << " -fixedlen: prevent hill climbing from changing the number of cards.\n"; std::cout << " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n"; std::cout << "Operations:\n"; std::cout << "brute : find the best combination of different cards, using up to battles to evaluate a deck.\n"; @@ -1147,6 +1150,10 @@ int main(int argc, char** argv) num_threads = atoi(argv[argIndex+1]); argIndex += 1; } + else if(strcmp(argv[argIndex], "-fixedlen") == 0) + { + fixed_len = true; + } else if(strcmp(argv[argIndex], "-turnlimit") == 0) { turn_limit = atoi(argv[argIndex+1]); From 190e19645316e3a1987a76b22583cd332e833e92 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 5 Feb 2013 13:48:11 +0800 Subject: [PATCH 065/406] Add -o= option. --- read.cpp | 6 +++--- read.h | 2 +- tyrant_optimize.cpp | 10 ++++++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/read.cpp b/read.cpp index c351e47b..1eac4925 100644 --- a/read.cpp +++ b/read.cpp @@ -250,13 +250,13 @@ unsigned read_custom_decks(Cards& cards, std::string filename, std::map& owned_cards) +void read_owned_cards(Cards& cards, std::map& owned_cards, const char *filename) { - std::ifstream owned_file{"ownedcards.txt"}; + std::ifstream owned_file{filename}; if(!owned_file.good()) { - std::cerr << "Warning: The file 'ownedcards.txt' does not exist. This will result in you not owning any cards.\n"; + std::cerr << "Warning: The file '" << filename << "' does not exist. This will result in you not owning any cards.\n"; return; } diff --git a/read.h b/read.h index 5d22c43e..c2282fd4 100644 --- a/read.h +++ b/read.h @@ -13,6 +13,6 @@ DeckIface* hash_to_deck(const char* hash, const Cards& cards); void load_decks(Decks& decks, Cards& cards); std::vector > parse_deck_list(std::string list_string); unsigned read_custom_decks(Cards& cards, std::string filename, std::map& custom_decks); -void read_owned_cards(Cards& cards, std::map& owned_cards); +void read_owned_cards(Cards& cards, std::map& owned_cards, const char *filename); #endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 0e9e1834..1affe447 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -750,7 +750,7 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr { // Then update best score/slot, print stuff std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards) << " " << from_slot << " " << card_id_name(from_slot < best_cards.size() ? best_cards[from_slot] : NULL) << - " -> " << (card_candidate ? to_slot : d1->cards.size()) << " " << card_id_name(card_candidate) << ": "; + " -> " << to_slot << " " << card_id_name(card_candidate) << ": "; best_score = current_score; best_cards = d1->cards; eval_commander = true; @@ -1023,6 +1023,7 @@ void usage(int argc, char** argv) std::cout << " -c: don't try to optimize the commander.\n"; std::cout << " -e : set the battleground effect.\n"; std::cout << " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n"; + std::cout << " -o=: restrict hill climbing to the owned cards listed in .\n"; std::cout << " -q: quest mode. Removes faction restrictions from defending commanders and automatically sets quest effect.\n"; std::cout << " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n"; std::cout << " -s: use surge (default is fight).\n"; @@ -1102,7 +1103,12 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "-o") == 0) { - read_owned_cards(cards, owned_cards); + read_owned_cards(cards, owned_cards, "ownedcards.txt"); + use_owned_cards = true; + } + else if(strncmp(argv[argIndex], "-o=", 3) == 0) + { + read_owned_cards(cards, owned_cards, argv[argIndex] + 3); use_owned_cards = true; } else if(strcmp(argv[argIndex], "-q") == 0) From 753a9bb34fe5b8807914d0982c3e52b0daa85b76 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 6 Feb 2013 11:14:15 +0800 Subject: [PATCH 066/406] Add -wintie option. --- sim.cpp | 9 +++------ sim.h | 1 + tyrant_optimize.cpp | 15 ++++++++++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/sim.cpp b/sim.cpp index 5aeb564c..c064ccb7 100644 --- a/sim.cpp +++ b/sim.cpp @@ -135,6 +135,7 @@ void Hand::reset(std::mt19937& re) // the implementation of the attack by an assault card is in the next section; // the implementation of the active skills is in the section after that. unsigned turn_limit{50}; +bool win_tie{false}; //------------------------------------------------------------------------------ inline unsigned opponent(unsigned player) { @@ -489,13 +490,13 @@ unsigned play(Field* fd) ++fd->turn; } // defender wins - if(fd->players[0]->commander.m_hp == 0) + if(fd->players[0]->commander.m_hp == 0 || (!win_tie && fd->turn > turn_limit)) { _DEBUG_MSG("Defender wins.\n"); return(0); } // attacker wins - if(fd->players[1]->commander.m_hp == 0) + if(fd->players[1]->commander.m_hp == 0 || (win_tie && fd->turn > turn_limit)) { // ANP: Speedy if last_decision + 10 > turn. // fd->turn has advanced once past the actual turn the battle has ended. @@ -509,10 +510,6 @@ unsigned play(Field* fd) _DEBUG_MSG("Attacker wins.\n"); return(10 + (speedy ? 5 : 0) + fd->points_since_last_decision); } - if(fd->turn > turn_limit) - { - return(0); - } // Huh? How did we get here? assert(false); diff --git a/sim.h b/sim.h index a4aa5f18..f49c4d80 100644 --- a/sim.h +++ b/sim.h @@ -17,6 +17,7 @@ class Field; extern bool debug_print; extern bool debug_line; extern unsigned turn_limit; +extern bool win_tie; void fill_skill_table(); unsigned play(Field* fd); diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 1affe447..6eeaa95b 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1022,14 +1022,15 @@ void usage(int argc, char** argv) std::cout << " -a: optimize for ANP instead of win rate.\n"; std::cout << " -c: don't try to optimize the commander.\n"; std::cout << " -e : set the battleground effect.\n"; + std::cout << " -fixedlen: prevent hill climbing from changing the number of cards.\n"; std::cout << " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n"; std::cout << " -o=: restrict hill climbing to the owned cards listed in .\n"; std::cout << " -q: quest mode. Removes faction restrictions from defending commanders and automatically sets quest effect.\n"; std::cout << " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n"; std::cout << " -s: use surge (default is fight).\n"; std::cout << " -t : set the number of threads, default is 4.\n"; - std::cout << " -fixedlen: prevent hill climbing from changing the number of cards.\n"; std::cout << " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n"; + std::cout << " -wintie: attacker wins if turns run out (default is defeated).\n"; std::cout << "Operations:\n"; std::cout << "brute : find the best combination of different cards, using up to battles to evaluate a deck.\n"; std::cout << "climb : perform hill-climbing starting from the given attack deck, using up to battles to evaluate a deck.\n"; @@ -1101,6 +1102,10 @@ int main(int argc, char** argv) effect = static_cast(x->second); argIndex += 1; } + else if(strcmp(argv[argIndex], "-fixedlen") == 0) + { + fixed_len = true; + } else if(strcmp(argv[argIndex], "-o") == 0) { read_owned_cards(cards, owned_cards, "ownedcards.txt"); @@ -1156,15 +1161,15 @@ int main(int argc, char** argv) num_threads = atoi(argv[argIndex+1]); argIndex += 1; } - else if(strcmp(argv[argIndex], "-fixedlen") == 0) - { - fixed_len = true; - } else if(strcmp(argv[argIndex], "-turnlimit") == 0) { turn_limit = atoi(argv[argIndex+1]); argIndex += 1; } + else if(strcmp(argv[argIndex], "-wintie") == 0) + { + win_tie = true; + } else if(strcmp(argv[argIndex], "brute") == 0) { todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex+1]), (unsigned)atoi(argv[argIndex+2]), bruteforce)); From c2e003378bdcbcb17a7b20994f43b15ee70d2aae Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 7 Feb 2013 08:40:29 +0800 Subject: [PATCH 067/406] Fix bug: hash displayed in the 'Deck improved' line. --- tyrant_optimize.cpp | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 6eeaa95b..1b1a5189 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -593,7 +593,7 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) best_score = current_score; best_commander = commander_candidate; deck_has_been_improved = true; - std::cout << "Deck improved: " << deck_hash(best_commander, best_cards) << " commander -> " << card_id_name(commander_candidate) << ": "; + std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards) << " commander -> " << card_id_name(commander_candidate) << ": "; print_score_info(compare_results, proc.factors); print_deck_inline(best_score, best_commander, best_cards); } @@ -632,32 +632,17 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) // Is it better ? if(current_score > best_score) { - std::cout << "Deck improved: " << deck_hash(best_commander, best_cards) << " " << slot_i << " " << card_id_name(slot_i < best_cards.size() ? best_cards[slot_i] : NULL) << + std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards) << " " << slot_i << " " << card_id_name(slot_i < best_cards.size() ? best_cards[slot_i] : NULL) << " -> " << card_id_name(card_candidate) << ": "; // Then update best score/slot, print stuff best_score = current_score; - if(slot_i == best_cards.size()) - { - best_cards.emplace_back(card_candidate); - } - else if(!card_candidate) - { - best_cards.erase(best_cards.begin() + slot_i); - } - else - { - best_cards[slot_i] = card_candidate; - } + best_cards = d1->cards; eval_commander = true; deck_has_been_improved = true; print_score_info(compare_results, proc.factors); print_deck_inline(best_score, best_commander, best_cards); } d1->cards = best_cards; - if(d1->cards.size() < best_cards.size()) - { - d1->cards.emplace(d1->cards.begin() + slot_i, best_cards[slot_i]); - } if(best_score == best_possible) { break; } } // Now that all cards are evaluated, take the best one @@ -709,7 +694,7 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr best_score = current_score; best_commander = commander_candidate; deck_has_been_improved = true; - std::cout << "Deck improved: " << deck_hash(best_commander, best_cards) << " commander -> " << card_id_name(commander_candidate) << ": "; + std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards) << " commander -> " << card_id_name(commander_candidate) << ": "; print_score_info(compare_results, proc.factors); print_deck_inline(best_score, best_commander, best_cards); } @@ -1030,7 +1015,7 @@ void usage(int argc, char** argv) std::cout << " -s: use surge (default is fight).\n"; std::cout << " -t : set the number of threads, default is 4.\n"; std::cout << " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n"; - std::cout << " -wintie: attacker wins if turns run out (default is defeated).\n"; + std::cout << " -wintie: attacker wins if turns run out (default is defeated; can be used for def deck simulation).\n"; std::cout << "Operations:\n"; std::cout << "brute : find the best combination of different cards, using up to battles to evaluate a deck.\n"; std::cout << "climb : perform hill-climbing starting from the given attack deck, using up to battles to evaluate a deck.\n"; From 9387acc0193920b10f388d6ad1622550b45edc5d Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 11 Feb 2013 17:55:14 +0800 Subject: [PATCH 068/406] Add debug info. --- sim.cpp | 344 ++++++++++++++++++++++++++++++++++---------------------- sim.h | 5 +- xml.cpp | 4 +- 3 files changed, 216 insertions(+), 137 deletions(-) diff --git a/sim.cpp b/sim.cpp index c064ccb7..b708a25a 100644 --- a/sim.cpp +++ b/sim.cpp @@ -20,7 +20,7 @@ std::string to_string(T val) } //---------------------- Debugging stuff --------------------------------------- bool debug_print(false); -bool debug_line(false); +bool debug_line(true); #ifndef NDEBUG #define _DEBUG_MSG(format, args...) \ { \ @@ -47,8 +47,8 @@ CardStatus::CardStatus(const Card* card) : m_player(0), m_augmented(0), m_berserk(0), - blitz(false), - m_chaos(false), + m_blitzing(false), + m_chaosed(false), m_delay(card->m_delay), m_diseased(false), m_enfeebled(0), @@ -79,8 +79,8 @@ inline void CardStatus::set(const Card& card) m_player = 0; m_augmented = 0; m_berserk = 0; - blitz = false; - m_chaos = false; + m_blitzing = false; + m_chaosed = false; m_delay = card.m_delay; m_diseased = false; m_enfeebled = 0; @@ -97,32 +97,110 @@ inline void CardStatus::set(const Card& card) m_temporary_split = false; } //------------------------------------------------------------------------------ -std::string skill_description(const SkillSpec& s) +inline unsigned safe_minus(unsigned x, unsigned y) +{ + return(x - std::min(x, y)); +} +inline int attack_power(CardStatus* att) { - return(skill_names[std::get<0>(s)] + + return(safe_minus(att->m_card->m_attack + att->m_berserk + att->m_rallied, att->m_weakened)); +} +//------------------------------------------------------------------------------ +std::string skill_description(Field* fd, const SkillSpec& s) +{ + switch(std::get<0>(s)) + { + case summon: + return(skill_names[std::get<0>(s)] + " " + fd->cards.by_id(std::get<1>(s))->m_name.c_str()); + default: + return(skill_names[std::get<0>(s)] + (std::get<2>(s) == allfactions ? "" : std::string(" ") + faction_names[std::get<2>(s)]) + (std::get<1>(s) == 0 ? "" : std::string(" ") + to_string(std::get<1>(s)))); + } } //------------------------------------------------------------------------------ -std::string status_description(CardStatus* status) +std::string card_description(const Card* c) { - if (!status) // Unknown Action card? - { - return("Action"); + std::string desc; + desc = c->m_name; + switch(c->m_type) + { + case CardType::action: + break; + case CardType::assault: + desc += " " + to_string(c->m_attack) + "/" + to_string(c->m_health) + "/" + to_string(c->m_delay); + break; + case CardType::structure: + desc += " " + to_string(c->m_health) + "/" + to_string(c->m_delay); + break; + case CardType::commander: + desc += " hp:" + to_string(c->m_health); + break; + case CardType::num_cardtypes: + assert(false); + break; } + return(desc); +} +//------------------------------------------------------------------------------ +std::string CardStatus::description() +{ std::string desc; - switch(status->m_card->m_type) + switch(m_card->m_type) { case CardType::commander: desc = "Commander "; break; case CardType::action: desc = "Action "; break; - case CardType::assault: desc = "A " + to_string(status->m_index) + " "; break; - case CardType::structure: desc = "S " + to_string(status->m_index) + " "; break; + case CardType::assault: desc = "Assault " + to_string(m_index) + " "; break; + case CardType::structure: desc = "Structure " + to_string(m_index) + " "; break; case CardType::num_cardtypes: assert(false); break; } - desc += "[" + status->m_card->m_name + "]"; + desc += "[" + m_card->m_name; + switch(m_card->m_type) + { + case CardType::action: + break; + case CardType::assault: + desc += " att:" + to_string(m_card->m_attack); + { + std::string att_desc; + if(m_berserk > 0) { att_desc += "+" + to_string(m_berserk) + "(berserk)"; } + if(m_rallied > 0) { att_desc += "+" + to_string(m_rallied) + "(rallied)"; } + if(m_weakened > 0) { att_desc += "-" + to_string(m_weakened) + "(weakened)"; } + if(!att_desc.empty()) { desc += att_desc + "=" + to_string(attack_power(this)); } + } + case CardType::structure: + case CardType::commander: + desc += " hp:" + to_string(m_hp); + break; + case CardType::num_cardtypes: + assert(false); + break; + } + if(m_delay > 0) { + desc += " cd:" + to_string(m_delay); + if(m_blitzing) { desc += "(blitzing)"; } + } + if(m_chaosed) { desc += ", chaosed"; } + if(m_diseased) { desc += ", diseaseded"; } + if(m_frozen) { desc += ", frozen"; } + if(m_immobilized) { desc += ", immobilized"; } + if(m_infused) { desc += ", infused"; } + if(m_jammed) { desc += ", jammed"; } + if(m_temporary_split) { desc += ", cloning"; } + if(m_augmented > 0) { desc += ", augmented " + to_string(m_augmented); } + if(m_enfeebled > 0) { desc += ", enfeebled " + to_string(m_enfeebled); } + if(m_poisoned > 0) { desc += ", poisoned " + to_string(m_poisoned); } + if(m_protected > 0) { desc += ", protected " + to_string(m_protected); } + desc += "]"; return(desc); } //------------------------------------------------------------------------------ +std::string status_description(CardStatus* status) +{ + if (!status) { return("A vanished card"); } // Unknown Action card? + return status->description(); +} +//------------------------------------------------------------------------------ void Hand::reset(std::mt19937& re) { assaults.reset(); @@ -201,7 +279,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector assert(fd->skill_queue.size() == 0); for(auto& skill: skills) { - _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(skill).c_str()); + _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); bool fusion_active = status->m_card->m_fusion && status->m_player == fd->tapi && fd->fusion_count >= 3; auto& augmented_s = status->m_augmented > 0 ? augmented_skill(status, skill) : skill; auto& fusioned_s = fusion_active ? fusioned_skill(augmented_s) : augmented_s; @@ -263,11 +341,11 @@ struct PlayCard { if (storage) { - _DEBUG_MSG("Placed [%s] as %s %d\n", card->m_name.c_str(), cardtype_names[type].c_str(), storage->size() - 1); + _DEBUG_MSG("Placed [%s] as %s %d\n", card_description(card).c_str(), cardtype_names[type].c_str(), storage->size() - 1); } else { - _DEBUG_MSG("Placed [%s] as %s\n", card->m_name.c_str(), cardtype_names[type].c_str()); + _DEBUG_MSG("Placed [%s] as %s\n", card_description(card).c_str(), cardtype_names[type].c_str()); } } @@ -289,6 +367,7 @@ struct PlayCard { for(auto& skill: card->m_skills_played) { + _DEBUG_MSG("Evaluating %s skill %s on play\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(status, skill); resolve_skill(fd); } @@ -317,7 +396,7 @@ void PlayCard::blitz() { if(card->m_blitz && fd->tip->assaults.size() > status->m_index && fd->tip->assaults[status->m_index].m_hp > 0 && fd->tip->assaults[status->m_index].m_delay == 0) { - status->blitz = true; + status->m_blitzing = true; } } // assault @@ -340,6 +419,7 @@ void PlayCard::onPlaySkills() { for(auto& skill: card->m_skills) { + _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(nullptr, skill); resolve_skill(fd); // Special case: enemy commander killed by a shock action card @@ -455,7 +535,7 @@ unsigned play(Field* fd) { // ca: current assault CardStatus& current_status(fd->tap->assaults[fd->current_ci]); - if((current_status.m_delay > 0 && !current_status.blitz) || current_status.m_hp == 0 || current_status.m_jammed || current_status.m_frozen) + if((current_status.m_delay > 0 && !current_status.m_blitzing) || current_status.m_hp == 0 || current_status.m_jammed || current_status.m_frozen) { //_DEBUG_MSG("! Assault %u (%s) hp: %u, jammed %u\n", card_index, current_status.m_card->m_name.c_str(), current_status.m_hp, current_status.m_jammed); } @@ -468,9 +548,10 @@ unsigned play(Field* fd) status_split.set(current_status.m_card); status_split.m_index = fd->tap->assaults.size() - 1; status_split.m_player = fd->tapi; - _DEBUG_MSG("Split assault %d (%s)\n", fd->tap->assaults.size() - 1, current_status.m_card->m_name.c_str()); + _DEBUG_MSG("Split assault %s\n", status_description(¤t_status).c_str()); for(auto& skill: status_split.m_card->m_skills_played) { + _DEBUG_MSG("Evaluating %s skill %s on play\n", status_description(¤t_status).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(&status_split, skill); resolve_skill(fd); } @@ -517,19 +598,15 @@ unsigned play(Field* fd) } //------------------------------------------------------------------------------ // All the stuff that happens at the beginning of a turn, before a card is played -inline unsigned safe_minus(unsigned x, unsigned y) -{ - return(x - std::min(x, y)); -} // returns true iff the card died. -bool remove_hp(Field* fd, CardStatus& status, unsigned dmg) +void remove_hp(Field* fd, CardStatus& status, unsigned dmg) { assert(status.m_hp > 0); + _DEBUG_MSG("%s suffer damage %u.\n", status_description(&status).c_str(), dmg); status.m_hp = safe_minus(status.m_hp, dmg); - const bool just_died(status.m_hp == 0); - if(just_died) + if(status.m_hp == 0) { - _DEBUG_MSG("Card %u (%s) dead\n", status.m_index, status.m_card->m_name.c_str()); + _DEBUG_MSG("Card %s die.\n", status_description(&status).c_str()); if(status.m_card->m_skills_died.size() > 0) { fd->killed_with_on_death.push_back(&status); @@ -539,7 +616,6 @@ bool remove_hp(Field* fd, CardStatus& status, unsigned dmg) fd->killed_with_regen.push_back(&status); } } - return(just_died); } inline bool is_it_dead(CardStatus& c) { @@ -604,7 +680,11 @@ void turn_start_phase(Field* fd) status.m_index = index; status.m_enfeebled = 0; status.m_protected = 0; - remove_hp(fd, status, status.m_poisoned); + if(status.m_poisoned > 0) + { + _DEBUG_MSG("%s suffer damage from poison.\n", status_description(&status).c_str()); + remove_hp(fd, status, status.m_poisoned); + } if(status.m_delay > 0 && !status.m_frozen) { --status.m_delay; } if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } } @@ -637,8 +717,8 @@ void turn_start_phase(Field* fd) CardStatus& status(assaults[index]); status.m_index = index; status.m_augmented = 0; - status.blitz = false; - status.m_chaos = false; + status.m_blitzing = false; + status.m_chaosed = false; status.m_frozen = false; status.m_immobilized = false; status.m_jammed = false; @@ -650,7 +730,7 @@ void turn_start_phase(Field* fd) #ifndef NDEBUG if(status.m_hp < status.m_card->m_health) { - _DEBUG_MSG("%u %s refreshed. hp %u -> %u.\n", index, status_description(&status).c_str(), status.m_hp, status.m_card->m_health); + _DEBUG_MSG("%s refreshed. hp %u -> %u.\n", status_description(&status).c_str(), status.m_hp, status.m_card->m_health); } #endif add_hp(fd, &status, status.m_card->m_health); @@ -686,12 +766,9 @@ void turn_start_phase(Field* fd) //---------------------- $50 attack by assault card implementation ------------- inline void apply_poison(CardStatus* target, unsigned v) { + _DEBUG_MSG("%s is poisoned (%u).\n", status_description(target).c_str(), v); target->m_poisoned = std::max(target->m_poisoned, v); } -inline int attack_power(CardStatus* att) -{ - return(safe_minus(att->m_card->m_attack + att->m_berserk + att->m_rallied, att->m_weakened)); -} // Counter damage dealt to the attacker (att) by defender (def) // pre-condition: only valid if m_card->m_counter > 0 inline unsigned counter_damage(CardStatus* att, CardStatus* def) @@ -775,6 +852,7 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg) { assert(status.m_hp > 0); assert(status.m_card->m_type == CardType::commander); + _DEBUG_MSG("%s suffer damage %u.\n", status_description(&status).c_str(), dmg); status.m_hp = safe_minus(status.m_hp, dmg); // ANP: If commander is enemy's, player gets points equal to damage. // Points are awarded for overkill, so it is correct to simply add dmg. @@ -782,7 +860,11 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg) { fd->points_since_last_decision += dmg; } - if(status.m_hp == 0) { fd->end = true; } + if(status.m_hp == 0) + { + _DEBUG_MSG("Card %s die.\n", status_description(&status).c_str()); + fd->end = true; + } } //------------------------------------------------------------------------------ // implementation of one attack by an assault card, against either an enemy @@ -870,9 +952,9 @@ struct PerformAttack template void attack_damage() { + _DEBUG_MSG("%s attack damage %u to %s\n", status_description(att_status).c_str(), att_dmg, status_description(def_status).c_str()); remove_hp(fd, *def_status, att_dmg); killed_by_attack = def_status->m_hp == 0; - _DEBUG_MSG("%s attack damage %u\n", status_description(att_status).c_str(), att_dmg); } template @@ -895,6 +977,7 @@ struct PerformAttack oa_berserk(); for(auto& oa_skill: def_status->m_card->m_skills_attacked) { + _DEBUG_MSG("Evaluating %s skill %s on attacked\n", status_description(def_status).c_str(), skill_description(fd, oa_skill).c_str()); fd->skill_queue.emplace_back(def_status, oa_skill); resolve_skill(fd); } @@ -909,8 +992,8 @@ struct PerformAttack if(def_status->m_card->m_counter > 0) { unsigned counter_dmg(counter_damage(att_status, def_status)); + _DEBUG_MSG("%s suffer damage from counter %u by %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, *att_status, counter_dmg); - _DEBUG_MSG("%s counter %u by %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); } } @@ -933,17 +1016,18 @@ unsigned PerformAttack::calculate_attack_damage() template<> void PerformAttack::immobilize() { - if(att_status->m_card->m_immobilize && def_status->m_delay <= 1 && !def_status->m_jammed && !def_status->m_frozen) + if(att_status->m_card->m_immobilize && def_status->m_delay <= 1 && !def_status->m_jammed && !def_status->m_frozen && fd->flip()) { - def_status->m_immobilized |= fd->flip(); + _DEBUG_MSG("%s immobilize %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); + def_status->m_immobilized = true; } } template<> void PerformAttack::attack_damage() { + _DEBUG_MSG("%s attack damage %u to %s\n", status_description(att_status).c_str(), att_dmg, status_description(def_status).c_str()); remove_commander_hp(fd, *def_status, att_dmg); - _DEBUG_MSG("%s attack damage %u to commander; commander hp %u\n", status_description(att_status).c_str(), att_dmg, fd->tip->commander.m_hp); } template<> @@ -951,8 +1035,8 @@ void PerformAttack::siphon_poison_disease() { if(att_status->m_card->m_siphon > 0) { + _DEBUG_MSG("%s siphon %u for %s\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_siphon), status_description(&fd->tap->commander).c_str()); add_hp(fd, &fd->tap->commander, std::min(att_dmg, att_status->m_card->m_siphon)); - _DEBUG_MSG(" \033[1;32m%s siphon %u; hp %u\033[0m\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_siphon), fd->tap->commander.m_hp); } if(att_status->m_card->m_poison > 0) { @@ -971,6 +1055,7 @@ void PerformAttack::on_kill() { for(auto& on_kill_skill: att_status->m_card->m_skills_kill) { + _DEBUG_MSG("Evaluating %s skill %s on kill\n", status_description(att_status).c_str(), skill_description(fd, on_kill_skill).c_str()); fd->skill_queue.emplace_back(att_status, on_kill_skill); resolve_skill(fd); } @@ -988,19 +1073,19 @@ void PerformAttack::crush_leech() CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall if (def_status != nullptr) { + _DEBUG_MSG("%s crush %u on %s\n", status_description(att_status).c_str(), att_status->m_card->m_crush, status_description(def_status).c_str()); remove_hp(fd, *def_status, att_status->m_card->m_crush); - _DEBUG_MSG("%s crush %u; wall %s hp %u\n", status_description(att_status).c_str(), att_status->m_card->m_crush, status_description(def_status).c_str(), def_status->m_hp); } else { + _DEBUG_MSG("%s crush %u on %s\n", status_description(att_status).c_str(), att_status->m_card->m_crush, status_description(&fd->tip->commander).c_str()); remove_commander_hp(fd, fd->tip->commander, att_status->m_card->m_crush); - _DEBUG_MSG("%s crush %u; commander hp %u\n", status_description(att_status).c_str(), att_status->m_card->m_crush, fd->tip->commander.m_hp); } } if(att_status->m_card->m_leech > 0 && att_status->m_hp > 0 && !att_status->m_diseased) { + _DEBUG_MSG("%s leech %u.\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech)); add_hp(fd, att_status, std::min(att_dmg, att_status->m_card->m_leech)); - _DEBUG_MSG("%s leech %u; hp: %u.\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech), att_status->m_hp); } } @@ -1010,6 +1095,7 @@ void attack_phase(Field* fd) CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); unsigned num_attacks(att_status->m_card->m_flurry > 0 && fd->flip() ? att_status->m_card->m_flurry + 1 : 1); + if(num_attacks > 1) { _DEBUG_MSG("%s flurry.\n", status_description(att_status).c_str()); } for(unsigned attack_index(0); attack_index < num_attacks && !att_status->m_jammed && !att_status->m_frozen && att_status->m_hp > 0 && fd->tip->commander.m_hp > 0; ++attack_index) { // 3 possibilities: @@ -1086,13 +1172,16 @@ struct if_ }; template -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { assert(false); return(false); } +// For the active player, delay == 0 or blitzing; for the inactive player, delay <= 1. +inline bool can_act(Field* fd, CardStatus* c) { return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen && (fd->tapi == c->m_player ? c->m_delay == 0 || c->m_blitzing : c->m_delay <= 1)); } + template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { - if(c->m_hp > 0 && (c->m_delay == 0 || c->blitz) && !c->m_jammed && !c->m_frozen) + if(can_act(fd, c)) { for(auto& s: c->m_card->m_skills) { @@ -1104,14 +1193,14 @@ inline bool skill_predicate(CardStatus* c) } template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_delay <= 1 && c->m_hp > 0 && !c->m_chaos && !c->m_jammed && !c->m_frozen); } +inline bool skill_predicate(Field* fd, CardStatus* c) +{ return(!c->m_chaosed && can_act(fd, c)); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_hp > 0 && ( - c->m_chaos || + c->m_chaosed || c->m_diseased || c->m_enfeebled > 0 || (c->m_frozen && c->m_delay == 0) || @@ -1121,66 +1210,66 @@ inline bool skill_predicate(CardStatus* c) } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_faction != bloodthirsty); } template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_delay <= 1 && c->m_hp > 0 && !c->m_jammed && !c->m_frozen); } +inline bool skill_predicate(Field* fd, CardStatus* c) +{ return(can_act(fd, c)); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(CardStatus* c) -{ return((c->m_delay == 0 || c->blitz) && c->m_hp > 0 && !c->m_jammed && !c->m_frozen && !c->m_immobilized); } +inline bool skill_predicate(Field* fd, CardStatus* c) +{ return(!c->m_immobilized && can_act(fd, c)); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_delay > 0); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } template<> -inline bool skill_predicate(CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c) // It is unnecessary to check for Blitz, since temporary_split status is // awarded before a card is played. { return(c->m_delay == 0 && c->m_hp > 0); } template<> -inline bool skill_predicate(CardStatus* c) -{ return(c->m_delay <= 1 && c->m_hp > 0 && attack_power(c) > 0 && !c->m_jammed && !c->m_frozen && !c->m_immobilized); } +inline bool skill_predicate(Field* fd, CardStatus* c) +{ return(!c->m_immobilized && attack_power(c) > 0 && can_act(fd, c)); } template inline void perform_skill(Field* fd, CardStatus* c, unsigned v) @@ -1195,17 +1284,14 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - c->m_hp = safe_minus(c->m_hp, v); - if(c->m_hp == 0) - { - fd->end = true; - } + // TODO backfire damage counts in ANP? + remove_commander_hp(fd, *c, v); } template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - c->m_chaos = true; + c->m_chaosed = true; } template<> @@ -1215,7 +1301,7 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { return; } - c->m_chaos = false; + c->m_chaosed = false; c->m_diseased = false; c->m_enfeebled = 0; c->m_frozen = false; @@ -1282,14 +1368,14 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - _DEBUG_MSG(" \033[1;31mshock %u. hp %u -> %u.\033[0m", v, c->m_hp, safe_minus(c->m_hp, v)); +// _DEBUG_MSG("shock %u. hp %u -> %u.\n", v, c->m_hp, safe_minus(c->m_hp, v)); c->m_hp = safe_minus(c->m_hp, v); } template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - _DEBUG_MSG(" hp %u -> %u.", c->m_hp, safe_minus(c->m_hp, v)); +// _DEBUG_MSG(" hp %u -> %u.\n", c->m_hp, safe_minus(c->m_hp, v)); remove_hp(fd, *c, v); } @@ -1325,7 +1411,7 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector { for(auto card: cards) { - if(skill_predicate(card)) + if(skill_predicate(fd, card)) { fd->selection_array[array_head] = card; ++array_head; @@ -1337,7 +1423,7 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector for(auto card: cards) { if(card->m_faction == std::get<2>(s) && - skill_predicate(card)) + skill_predicate(fd, card)) { fd->selection_array[array_head] = card; ++array_head; @@ -1356,7 +1442,7 @@ inline unsigned select_rally_like(Field* fd, CardStatus* src_status, const std:: { for(; card_index < cards.size(); ++card_index) { - if(skill_predicate(cards[card_index])) + if(skill_predicate(fd, cards[card_index])) { fd->selection_array[array_head] = cards[card_index]; ++array_head; @@ -1368,7 +1454,7 @@ inline unsigned select_rally_like(Field* fd, CardStatus* src_status, const std:: for(; card_index < cards.size(); ++card_index) { if(cards[card_index]->m_faction == std::get<2>(s) && - skill_predicate(cards[card_index])) + skill_predicate(fd, cards[card_index])) { fd->selection_array[array_head] = cards[card_index]; ++array_head; @@ -1400,7 +1486,7 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std const unsigned max_index(src_status->m_index + (src_status->m_index == cards.size() - 1 ? 0 : 1)); for(unsigned card_index(min_index); card_index <= max_index; ++card_index) { - if(skill_predicate(cards[card_index])) + if(skill_predicate(fd, cards[card_index])) { fd->selection_array[array_head] = cards[card_index]; ++array_head; @@ -1415,7 +1501,7 @@ inline unsigned select_infuse(Field* fd, const SkillSpec& s) // Select candidates among attacker's assaults for(auto card_status: fd->tap->assaults.m_indirect) { - if(skill_predicate(card_status)) + if(skill_predicate(fd, card_status)) { fd->selection_array[array_head] = card_status; ++array_head; @@ -1424,7 +1510,7 @@ inline unsigned select_infuse(Field* fd, const SkillSpec& s) // Select candidates among defender's assaults for(auto card_status: fd->tip->assaults.m_indirect) { - if(skill_predicate(card_status)) + if(skill_predicate(fd, card_status)) { fd->selection_array[array_head] = card_status; ++array_head; @@ -1435,7 +1521,7 @@ inline unsigned select_infuse(Field* fd, const SkillSpec& s) inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src_status) { - return(fd->players[src_status ? (src_status->m_chaos ? src_status->m_player : opponent(src_status->m_player)) : fd->tipi]->assaults.m_indirect); + return(fd->players[src_status ? (src_status->m_chaosed ? src_status->m_player : opponent(src_status->m_player)) : fd->tipi]->assaults.m_indirect); } inline std::vector& skill_targets_allied_assault(Field* fd, CardStatus* src_status) @@ -1445,7 +1531,7 @@ inline std::vector& skill_targets_allied_assault(Field* fd, CardSta inline std::vector& skill_targets_hostile_structure(Field* fd, CardStatus* src_status) { - return(fd->players[src_status ? (src_status->m_chaos ? src_status->m_player : opponent(src_status->m_player)) : fd->tipi]->structures.m_indirect); + return(fd->players[src_status ? (src_status->m_chaosed ? src_status->m_player : opponent(src_status->m_player)) : fd->tipi]->structures.m_indirect); } inline std::vector& skill_targets_allied_structure(Field* fd, CardStatus* src_status) @@ -1532,7 +1618,7 @@ CardStatus* get_target_hostile_fast(Field* fd, CardStatus* src_status, const Ski unsigned rand_index(fd->rand(0, array_head - 1)); CardStatus* c(fd->selection_array[rand_index]); // intercept - if(src_status && !src_status->m_chaos) + if(src_status && !src_status->m_chaosed) { CardStatus* intercept_card(nullptr); if(rand_index > 0) @@ -1566,24 +1652,24 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski if(c) { // evade - if(!c->m_card->m_evade || (src_status && src_status->m_chaos) || fd->flip()) + if(!c->m_card->m_evade || (src_status && src_status->m_chaosed) || fd->flip()) { - _DEBUG_MSG("%s (%u) from %s on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str(), status_description(c).c_str()); + _DEBUG_MSG("%s %s (%u) on %s.\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); // skill perform_skill(fd, c, std::get<1>(s)); // payback if(c->m_card->m_payback && src_status && src_status->m_card->m_type == CardType::assault && - !src_status->m_chaos && + !src_status->m_chaosed && src_status->m_hp > 0 && fd->flip()) { // payback evade - if(skill_predicate(src_status) && + if(skill_predicate(fd, src_status) && (!src_status->m_card->m_evade || fd->flip())) { - _DEBUG_MSG("Payback (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); + _DEBUG_MSG("Payback (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); // payback skill perform_skill(fd, src_status, std::get<1>(s)); } @@ -1602,9 +1688,8 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil if(array_head > 0) { CardStatus* c(fd->selection_array[fd->rand(0, array_head - 1)]); - _DEBUG_MSG(" \033[1;34m%s: %s on %s\033[0m", status_description(src_status).c_str(), skill_description(s).c_str(), status_description(c).c_str()); + _DEBUG_MSG("%s %s (%u) on %s.\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); perform_skill(fd, c, std::get<1>(s)); - _DEBUG_MSG("\n"); if(c->m_card->m_tribute && src_status && src_status->m_card->m_type == CardType::assault && @@ -1612,9 +1697,9 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil src_status->m_hp > 0 && fd->flip()) { - if(skill_predicate(src_status)) + if(skill_predicate(fd, src_status)) { - _DEBUG_MSG("Tribute (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); + _DEBUG_MSG("Tribute (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); perform_skill(fd, src_status, std::get<1>(s)); } } @@ -1624,9 +1709,9 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil if(opp->assaults.size() > c->m_index) { CardStatus& emulator = opp->assaults[c->m_index]; - if(emulator.m_card->m_emulate && skill_predicate(&emulator)) + if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator)) { - _DEBUG_MSG("Emulate (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), emulator.m_card->m_name.c_str()); + _DEBUG_MSG("Emulate (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); perform_skill(fd, &emulator, std::get<1>(s)); } } @@ -1642,15 +1727,15 @@ void perform_global_hostile_fast(Field* fd, CardStatus* src_status, const SkillS for(unsigned s_index(0); s_index < array_head; ++s_index) { CardStatus* c(fd->selection_array[s_index]); - if(!c->m_card->m_evade || (src_status && src_status->m_chaos) || fd->flip()) + if(!c->m_card->m_evade || (src_status && src_status->m_chaosed) || fd->flip()) { - _DEBUG_MSG("%s (%u) from %s on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str(), status_description(c).c_str()); + _DEBUG_MSG("%s %s (%u) on %s.\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); perform_skill(fd, c, std::get<1>(s)); // payback if(c->m_card->m_payback && src_status && src_status->m_card->m_type == CardType::assault && - !src_status->m_chaos && + !src_status->m_chaosed && src_status->m_hp > 0 && fd->flip()) { @@ -1658,11 +1743,11 @@ void perform_global_hostile_fast(Field* fd, CardStatus* src_status, const SkillS } } } - for(unsigned i(0); i < payback_count && skill_predicate(src_status); ++i) + for(unsigned i(0); i < payback_count && skill_predicate(fd, src_status); ++i) { if((!src_status->m_card->m_evade || fd->flip())) { - _DEBUG_MSG("Payback (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); + _DEBUG_MSG("Payback (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); perform_skill(fd, src_status, std::get<1>(s)); } } @@ -1678,7 +1763,7 @@ void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSp for(unsigned s_index(0); s_index < array_head; ++s_index) { CardStatus* c(fd->selection_array[s_index]); - _DEBUG_MSG("%s (%u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), c->m_card->m_name.c_str()); + _DEBUG_MSG("%s %s (%u) on %s.\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); perform_skill(fd, c, std::get<1>(s)); if(c->m_card->m_tribute && src_status && @@ -1687,9 +1772,9 @@ void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSp src_status->m_hp > 0 && fd->flip()) { - if(skill_predicate(src_status)) + if(skill_predicate(fd, src_status)) { - _DEBUG_MSG("Tribute (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), src_status->m_card->m_name.c_str()); + _DEBUG_MSG("Tribute (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); perform_skill(fd, src_status, std::get<1>(s)); } } @@ -1699,9 +1784,9 @@ void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSp if(opp->assaults.size() > c->m_index) { CardStatus& emulator = opp->assaults[c->m_index]; - if(emulator.m_card->m_emulate && skill_predicate(&emulator)) + if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator)) { - _DEBUG_MSG("Emulate (%s %u) on (%s)\n", skill_names[skill_id].c_str(), std::get<1>(s), emulator.m_card->m_name.c_str()); + _DEBUG_MSG("Emulate (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); perform_skill(fd, &emulator, std::get<1>(s)); } } @@ -1710,10 +1795,9 @@ void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSp void perform_backfire(Field* fd, CardStatus* src_status, const SkillSpec& s) { - Hand* backfired_side = fd->players[src_status->m_player]; - _DEBUG_MSG("Performing backfire on (%s).", backfired_side->commander.m_card->m_name.c_str()); - perform_skill(fd, &backfired_side->commander, std::get<1>(s)); - _DEBUG_MSG("\n"); + CardStatus* c(&fd->players[src_status->m_player]->commander); + _DEBUG_MSG("%s %s (%u) on %s.\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), std::get<1>(s), status_description(c).c_str()); + perform_skill(fd, c, std::get<1>(s)); } void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) @@ -1722,7 +1806,7 @@ void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) // Select candidates among attacker's assaults for(auto card_status: fd->players[src_status->m_player]->assaults.m_indirect) { - if(skill_predicate(card_status)) + if(skill_predicate(fd, card_status)) { fd->selection_array[array_head] = card_status; ++array_head; @@ -1731,7 +1815,7 @@ void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) // Select candidates among defender's assaults for(auto card_status: fd->players[opponent(src_status->m_player)]->assaults.m_indirect) { - if(skill_predicate(card_status)) + if(skill_predicate(fd, card_status)) { fd->selection_array[array_head] = card_status; ++array_head; @@ -1743,9 +1827,8 @@ void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) // check evade for enemy assaults only if(c->m_player == src_status->m_player || !c->m_card->m_evade || fd->flip()) { - _DEBUG_MSG("%s on (%s).", skill_names[infuse].c_str(), c->m_card->m_name.c_str()); + _DEBUG_MSG("%s %s on %s.\n", status_description(src_status).c_str(), skill_names[infuse].c_str(), status_description(c).c_str()); perform_skill(fd, c, std::get<1>(s)); - _DEBUG_MSG("\n"); } } } @@ -1769,14 +1852,14 @@ void summon_card(Field* fd, unsigned player, const Card* summoned) card_status.set(summoned); card_status.m_index = storage->size() - 1; card_status.m_player = player; - _DEBUG_MSG("Summoned [%s] as %s %d\n", summoned->m_name.c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index); + _DEBUG_MSG("Summoned [%s] as %s %d\n", card_description(summoned).c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index); prepend_skills(fd, &card_status); if(card_status.m_card->m_blitz && fd->players[opponent(player)]->assaults.size() > card_status.m_index && fd->players[opponent(player)]->assaults[card_status.m_index].m_hp > 0 && fd->players[opponent(player)]->assaults[card_status.m_index].m_delay == 0) { - card_status.blitz = true; + card_status.m_blitzing = true; } if(fd->effect == Effect::toxic) @@ -1802,14 +1885,8 @@ void perform_trigger_regen(Field* fd, CardStatus* src_status, const SkillSpec& s void perform_shock(Field* fd, CardStatus* src_status, const SkillSpec& s) { - _DEBUG_MSG("Performing shock on (%s).", fd->tip->commander.m_card->m_name.c_str()); + _DEBUG_MSG("%s shock (%u) on %s.\n", status_description(src_status).c_str(), std::get<1>(s), status_description(&fd->tip->commander).c_str()); perform_skill(fd, &fd->tip->commander, std::get<1>(s)); - _DEBUG_MSG("\n"); -} - -void perform_supply(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - perform_global_allied_fast(fd, src_status, s); } // Special rules for mimic : @@ -1837,7 +1914,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // evade check for mimic // individual skills are subject to evade checks too, // but resolve_skill will handle those. - if(c && c->m_card->m_evade && (!src_status || !src_status->m_chaos) && fd->flip()) + if(c && c->m_card->m_evade && (!src_status || !src_status->m_chaosed) && fd->flip()) { return; } @@ -1845,7 +1922,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) if(c) { - _DEBUG_MSG("%s on (%s)\n", skill_names[std::get<0>(s)].c_str(), c->m_card->m_name.c_str()); + _DEBUG_MSG("%s on %s\n", skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); for(auto skill: c->m_card->m_skills) { if(src_status && src_status->m_card->m_type == CardType::assault && src_status->m_hp == 0) @@ -1854,6 +1931,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) (std::get<0>(skill) != supply || (src_status && src_status->m_card->m_type == CardType::assault))) { SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions); + _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(src_status, src_status && src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); resolve_skill(fd); check_regeneration(fd); @@ -1891,7 +1969,7 @@ void fill_skill_table() skill_table[shock] = perform_shock; skill_table[siege] = perform_targetted_hostile_fast; skill_table[siege_all] = perform_global_hostile_fast; - skill_table[supply] = perform_supply; + skill_table[supply] = perform_global_allied_fast; skill_table[strike] = perform_targetted_hostile_fast; skill_table[strike_all] = perform_global_hostile_fast; skill_table[summon] = perform_summon; diff --git a/sim.h b/sim.h index f49c4d80..4676c953 100644 --- a/sim.h +++ b/sim.h @@ -94,8 +94,8 @@ struct CardStatus unsigned m_player; unsigned m_augmented; unsigned m_berserk; - bool blitz; - bool m_chaos; + bool m_blitzing; + bool m_chaosed; unsigned m_delay; bool m_diseased; unsigned m_enfeebled; @@ -116,6 +116,7 @@ struct CardStatus void set(const Card* card); void set(const Card& card); + std::string description(); }; //------------------------------------------------------------------------------ // Represents a particular draw from a deck. diff --git a/xml.cpp b/xml.cpp index a9cf4ef2..d0d0bd56 100644 --- a/xml.cpp +++ b/xml.cpp @@ -246,8 +246,6 @@ void read_cards(Cards& cards) { c->m_antiair = atoi(skill->first_attribute("x")->value()); } if(strcmp(skill->first_attribute("id")->value(), "armored") == 0) { c->m_armored = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "augment") == 0) - { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "berserk") == 0) { bool attacked(skill->first_attribute("attacked")); @@ -314,6 +312,8 @@ void read_cards(Cards& cards) { c->m_valor = atoi(skill->first_attribute("x")->value()); } if(strcmp(skill->first_attribute("id")->value(), "wall") == 0) { c->m_wall = true; } + if(strcmp(skill->first_attribute("id")->value(), "augment") == 0) + { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "backfire") == 0) { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "chaos") == 0) From d1ff97a23aa4f1a900049799063ad2f024491db1 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 13 Feb 2013 02:08:32 +0800 Subject: [PATCH 069/406] Add more debug info. --- card.h | 16 +- read.cpp | 9 +- sim.cpp | 384 ++++++++++++++++++++++++-------------------- tyrant.cpp | 2 +- tyrant_optimize.cpp | 37 ++++- 5 files changed, 258 insertions(+), 190 deletions(-) diff --git a/card.h b/card.h index 7e7b2298..4a524cec 100644 --- a/card.h +++ b/card.h @@ -59,13 +59,13 @@ class Card void add_skill(ActiveSkill v1, unsigned v2, Faction v3) { m_skills.push_back(std::make_tuple(v1, v2, v3)); } void add_played_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills_played.push_back(std::make_tuple(v1, v2, v3)); } + { m_skills_on_play.push_back(std::make_tuple(v1, v2, v3)); } void add_died_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills_died.push_back(std::make_tuple(v1, v2, v3)); } + { m_skills_on_death.push_back(std::make_tuple(v1, v2, v3)); } void add_attacked_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills_attacked.push_back(std::make_tuple(v1, v2, v3)); } + { m_skills_on_attacked.push_back(std::make_tuple(v1, v2, v3)); } void add_kill_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills_kill.push_back(std::make_tuple(v1, v2, v3)); } + { m_skills_on_kill.push_back(std::make_tuple(v1, v2, v3)); } unsigned m_antiair; unsigned m_armored; @@ -110,10 +110,10 @@ class Card unsigned m_valor; bool m_wall; std::vector m_skills; - std::vector m_skills_played; - std::vector m_skills_died; - std::vector m_skills_attacked; - std::vector m_skills_kill; + std::vector m_skills_on_play; + std::vector m_skills_on_death; + std::vector m_skills_on_attacked; + std::vector m_skills_on_kill; CardType::CardType m_type; }; diff --git a/read.cpp b/read.cpp index 1eac4925..ca53787a 100644 --- a/read.cpp +++ b/read.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "card.h" #include "cards.h" @@ -20,7 +21,7 @@ const char* base64_chars = // Converts `pairs' pairs of cards in `hash' to a deck. // Stores resulting card IDs in `ids'. -void hash_to_ids(const char* hash, size_t pairs, +bool hash_to_ids(const char* hash, size_t pairs, std::vector& ids) { unsigned int last_id = 0; @@ -31,7 +32,7 @@ void hash_to_ids(const char* hash, size_t pairs, const char* p1 = strchr(base64_chars, hash[2 * i + 1]); if (!p0 || !p1) { - throw std::runtime_error(hash); + return(false); } size_t index0 = p0 - base64_chars; size_t index1 = p1 - base64_chars; @@ -47,6 +48,7 @@ void hash_to_ids(const char* hash, size_t pairs, ids.push_back(last_id); } } + return(true); } } @@ -55,8 +57,9 @@ void hash_to_ids(const char* hash, size_t pairs, DeckIface* hash_to_deck(const char* hash, const Cards& cards) { std::vector ids; + if(strlen(hash) % 2 > 0) { return(nullptr); } size_t pairs = strlen(hash) / 2; - hash_to_ids(hash, pairs, ids); + if(!hash_to_ids(hash, pairs, ids)) { return(nullptr); } return new DeckRandom(cards, ids); } diff --git a/sim.cpp b/sim.cpp index b708a25a..ad496c4c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -119,7 +119,7 @@ std::string skill_description(Field* fd, const SkillSpec& s) } } //------------------------------------------------------------------------------ -std::string card_description(const Card* c) +std::string card_description(Field* fd, const Card* c) { std::string desc; desc = c->m_name; @@ -140,6 +140,46 @@ std::string card_description(const Card* c) assert(false); break; } + if(c->m_unique) { desc += " unique"; } + if(c->m_rarity == 4) { desc += " legendary"; } + if(c->m_faction != allfactions) { desc += " " + faction_names[c->m_faction]; } + if(c->m_antiair > 0) { desc += ", antiair " + to_string(c->m_antiair); } + if(c->m_armored > 0) { desc += ", armored " + to_string(c->m_armored); } + if(c->m_berserk > 0) { desc += ", berserk " + to_string(c->m_berserk); } + if(c->m_blitz) { desc += ", blitz"; } + if(c->m_burst > 0) { desc += ", burst " + to_string(c->m_burst); } + if(c->m_counter > 0) { desc += ", counter " + to_string(c->m_counter); } + if(c->m_crush > 0) { desc += ", crush " + to_string(c->m_crush); } + if(c->m_disease) { desc += ", disease"; } + if(c->m_emulate) { desc += ", emulate"; } + if(c->m_evade) { desc += ", evade"; } + if(c->m_fear) { desc += ", fear"; } + if(c->m_flurry > 0) { desc += ", flurry " + to_string(c->m_flurry); } + if(c->m_flying) { desc += ", flying"; } + if(c->m_fusion) { desc += ", fusion"; } + if(c->m_immobilize) { desc += ", immobilize"; } + if(c->m_intercept) { desc += ", intercept"; } + if(c->m_leech > 0) { desc += ", leech " + to_string(c->m_leech); } + if(c->m_payback) { desc += ", payback"; } + if(c->m_pierce > 0) { desc += ", pierce " + to_string(c->m_pierce); } + if(c->m_poison > 0) { desc += ", poison " + to_string(c->m_poison); } + if(c->m_recharge) { desc += ", recharge"; } + if(c->m_refresh) { desc += ", refresh"; } + if(c->m_regenerate > 0) { desc += ", regenerate " + to_string(c->m_regenerate); } + if(c->m_siphon > 0) { desc += ", siphon " + to_string(c->m_siphon); } + if(c->m_split) { desc += ", split"; } + if(c->m_swipe) { desc += ", swipe"; } + if(c->m_tribute) { desc += ", tribute"; } + if(c->m_valor > 0) { desc += ", valor " + to_string(c->m_valor); } + if(c->m_wall) { desc += ", wall"; } + for(auto& skill: c->m_skills) { desc += ", " + skill_description(fd, skill); } + for(auto& skill: c->m_skills_on_play) { desc += ", " + skill_description(fd, skill) + " on play"; } + for(auto& skill: c->m_skills_on_kill) { desc += ", " + skill_description(fd, skill) + " on kill"; } + if(c->m_berserk_oa > 0) { desc += ", berserk " + to_string(c->m_berserk_oa) + " on attacked"; } + if(c->m_disease_oa) { desc += ", disease on attacked"; } + if(c->m_poison_oa > 0) { desc += ", poison " + to_string(c->m_poison_oa) + " on attacked"; } + for(auto& skill: c->m_skills_on_attacked) { desc += ", " + skill_description(fd, skill) + " on attacked"; } + for(auto& skill: c->m_skills_on_death) { desc += ", " + skill_description(fd, skill) + " on death"; } return(desc); } //------------------------------------------------------------------------------ @@ -224,9 +264,9 @@ void prepend_on_death(Field* fd) { for(auto status: boost::adaptors::reverse(fd->killed_with_on_death)) { - for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_died)) + for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_on_death)) { - _DEBUG_MSG("On death skill pushed in front %s %u %s\n", skill_names[std::get<0>(skill)].c_str(), std::get<1>(skill), faction_names[std::get<2>(skill)].c_str()); + _DEBUG_MSG("On death skill pushed in front: %s\n", skill_description(fd, skill).c_str()); fd->skill_queue.emplace_front(status, skill); } } @@ -341,11 +381,11 @@ struct PlayCard { if (storage) { - _DEBUG_MSG("Placed [%s] as %s %d\n", card_description(card).c_str(), cardtype_names[type].c_str(), storage->size() - 1); + _DEBUG_MSG("%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), storage->size() - 1, card_description(fd, card).c_str()); } else { - _DEBUG_MSG("Placed [%s] as %s\n", card_description(card).c_str(), cardtype_names[type].c_str()); + _DEBUG_MSG("%s plays %s [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), card_description(fd, card).c_str()); } } @@ -365,7 +405,7 @@ struct PlayCard template void onPlaySkills() { - for(auto& skill: card->m_skills_played) + for(auto& skill: card->m_skills_on_play) { _DEBUG_MSG("Evaluating %s skill %s on play\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(status, skill); @@ -425,7 +465,6 @@ void PlayCard::onPlaySkills() // Special case: enemy commander killed by a shock action card if(fd->tip->commander.m_hp == 0) { - _DEBUG_MSG("turn's defender dead.\n"); fd->end = true; break; } @@ -461,7 +500,8 @@ unsigned play(Field* fd) { fd->current_phase = Field::playcard_phase; // Initialize stuff, remove dead cards - _DEBUG_MSG("##### TURN %u #####\n", fd->turn); + _DEBUG_MSG("------------------------------------------------------------------------\n"); + _DEBUG_MSG("TURN %u begins for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); // ANP: If it's the player's turn and he's making a decision, // reset his points to 0. if(fd->tapi == 0 && fd->turn <= fd->last_decision_turn) @@ -548,13 +588,14 @@ unsigned play(Field* fd) status_split.set(current_status.m_card); status_split.m_index = fd->tap->assaults.size() - 1; status_split.m_player = fd->tapi; - _DEBUG_MSG("Split assault %s\n", status_description(¤t_status).c_str()); - for(auto& skill: status_split.m_card->m_skills_played) + _DEBUG_MSG("%s splits %s\n", status_description(¤t_status).c_str(), status_description(&status_split).c_str()); + for(auto& skill: status_split.m_card->m_skills_on_play) { _DEBUG_MSG("Evaluating %s skill %s on play\n", status_description(¤t_status).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(&status_split, skill); resolve_skill(fd); } + // TODO: Use summon to implement split? // TODO: Determine whether we need to check for Blitz for the newly-Split unit } // Evaluate skills @@ -566,6 +607,7 @@ unsigned play(Field* fd) } } } + _DEBUG_MSG("TURN %u ends for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); std::swap(fd->tapi, fd->tipi); std::swap(fd->tap, fd->tip); ++fd->turn; @@ -602,12 +644,12 @@ unsigned play(Field* fd) void remove_hp(Field* fd, CardStatus& status, unsigned dmg) { assert(status.m_hp > 0); - _DEBUG_MSG("%s suffer damage %u.\n", status_description(&status).c_str(), dmg); + _DEBUG_MSG("%s takes %u damage\n", status_description(&status).c_str(), dmg); status.m_hp = safe_minus(status.m_hp, dmg); if(status.m_hp == 0) { - _DEBUG_MSG("Card %s die.\n", status_description(&status).c_str()); - if(status.m_card->m_skills_died.size() > 0) + _DEBUG_MSG("%s dies\n", status_description(&status).c_str()); + if(status.m_card->m_skills_on_death.size() > 0) { fd->killed_with_on_death.push_back(&status); } @@ -649,14 +691,10 @@ void check_regeneration(Field* fd) { if (fd->flip()) { + _DEBUG_MSG("%s regenerates, hp 0 -> %u\n", status_description(&status).c_str(), status.m_card->m_health); add_hp(fd, &status, status.m_card->m_regenerate); } } - if(status.m_hp > 0) - { - _DEBUG_MSG("Card %s regenerated, hp 0 -> %u\n", status.m_card->m_name.c_str(), status.m_hp); - } - } fd->killed_with_regen.clear(); } @@ -682,10 +720,14 @@ void turn_start_phase(Field* fd) status.m_protected = 0; if(status.m_poisoned > 0) { - _DEBUG_MSG("%s suffer damage from poison.\n", status_description(&status).c_str()); + _DEBUG_MSG("%s takes poison damage\n", status_description(&status).c_str()); remove_hp(fd, status, status.m_poisoned); } - if(status.m_delay > 0 && !status.m_frozen) { --status.m_delay; } + if(status.m_delay > 0 && !status.m_frozen) + { + _DEBUG_MSG("%s reduces its timer\n", status_description(&status).c_str()); + --status.m_delay; + } if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } } } @@ -700,7 +742,11 @@ void turn_start_phase(Field* fd) { CardStatus& status(structures[index]); status.m_index = index; - if(status.m_delay > 0) { --status.m_delay; } + if(status.m_delay > 0) + { + _DEBUG_MSG("%s reduces its timer\n", status_description(&status).c_str()); + --status.m_delay; + } if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } } } @@ -725,14 +771,9 @@ void turn_start_phase(Field* fd) status.m_rallied = 0; status.m_weakened = 0; status.m_temporary_split = false; - if(status.m_card->m_refresh && !status.m_diseased) + if(status.m_card->m_refresh && status.m_hp < status.m_card->m_health && !status.m_diseased) { -#ifndef NDEBUG - if(status.m_hp < status.m_card->m_health) - { - _DEBUG_MSG("%s refreshed. hp %u -> %u.\n", status_description(&status).c_str(), status.m_hp, status.m_card->m_health); - } -#endif + _DEBUG_MSG("%s refreshes. hp -> %u.\n", status_description(&status).c_str(), status.m_card->m_health); add_hp(fd, &status, status.m_card->m_health); } } @@ -750,9 +791,7 @@ void turn_start_phase(Field* fd) status.m_index = index; if(status.m_card->m_refresh && status.m_hp < status.m_card->m_health) { -#ifndef NDEBUG - _DEBUG_MSG("%s refreshed. hp %u -> %u.\n", index, status_description(&status).c_str(), status.m_hp, status.m_card->m_health); -#endif + _DEBUG_MSG("%s refreshes. hp -> %u.\n", status_description(&status).c_str(), status.m_card->m_health); add_hp(fd, &status, status.m_card->m_health); } } @@ -766,8 +805,9 @@ void turn_start_phase(Field* fd) //---------------------- $50 attack by assault card implementation ------------- inline void apply_poison(CardStatus* target, unsigned v) { - _DEBUG_MSG("%s is poisoned (%u).\n", status_description(target).c_str(), v); - target->m_poisoned = std::max(target->m_poisoned, v); + if(v <= target->m_poisoned) { return; } + _DEBUG_MSG("%s is poisoned (%u)\n", status_description(target).c_str(), v); + target->m_poisoned = v; } // Counter damage dealt to the attacker (att) by defender (def) // pre-condition: only valid if m_card->m_counter > 0 @@ -806,53 +846,16 @@ inline unsigned valor_damage(Field* fd, CardStatus& status) return(0); } -inline unsigned attack_damage_against_non_assault(Field* fd, CardStatus& att_status) -{ - const Card& att_card(*att_status.m_card); - assert(att_card.m_type == CardType::assault); - // pre modifier damage - unsigned damage(attack_power(&att_status)); - // - if(damage > 0) - { - damage += valor_damage(fd, att_status); - } - return(damage); -} - -inline unsigned attack_damage_against_assault(Field* fd, CardStatus& att_status, CardStatus& def_status) -{ - const Card& att_card(*att_status.m_card); - const Card& def_card(*def_status.m_card); - assert(att_card.m_type == CardType::assault); - assert(def_card.m_type == CardType::assault); - // pre modifier damage - unsigned damage(attack_power(&att_status)); - // - if(damage > 0) - { - damage = safe_minus( - damage // pre-modifier damage - + valor_damage(fd, att_status) // valor - + def_status.m_enfeebled // enfeeble - + (def_card.m_flying ? att_card.m_antiair : 0) // anti-air - + (att_card.m_burst > 0 ? (def_status.m_hp == def_card.m_health ? att_card.m_burst : 0) : 0) // burst - // armor + protect + pierce - , safe_minus(def_card.m_armored + def_status.m_protected, att_card.m_pierce)); - } - return(damage); -} - inline bool alive_assault(Storage& assaults, unsigned index) { - return(index >= 0 && assaults.size() > index && assaults[index].m_hp > 0); + return(assaults.size() > index && assaults[index].m_hp > 0); } void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg) { assert(status.m_hp > 0); assert(status.m_card->m_type == CardType::commander); - _DEBUG_MSG("%s suffer damage %u.\n", status_description(&status).c_str(), dmg); + _DEBUG_MSG("%s takes %u damage\n", status_description(&status).c_str(), dmg); status.m_hp = safe_minus(status.m_hp, dmg); // ANP: If commander is enemy's, player gets points equal to damage. // Points are awarded for overkill, so it is correct to simply add dmg. @@ -862,7 +865,7 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg) } if(status.m_hp == 0) { - _DEBUG_MSG("Card %s die.\n", status_description(&status).c_str()); + _DEBUG_MSG("%s dies\n", status_description(&status).c_str()); fd->end = true; } } @@ -884,66 +887,98 @@ struct PerformAttack template void op() { - if(attack_power(att_status) > 0) - { - const bool fly_check(!def_status->m_card->m_flying || att_status->m_card->m_flying || att_status->m_card->m_antiair > 0 || - (fd->effect != Effect::high_skies && fd->flip())); - if(fly_check) // unnecessary check for structures, commander -> fix later ? - { - // Evaluation order: - // assaults only: fly check - // assaults only: immobilize - // deal damage - // assaults only: (siphon, poison, disease) - // oa: poison, disease, assaults only: berserk, skills - // counter, berserk - // assaults only: (crush, leech if still alive) - // check regeneration - att_dmg = calculate_attack_damage(); - - // If Impenetrable, force attack damage against walls to be 0, - // but still activate Counter! - if(fd->effect == Effect::impenetrable && def_status->m_card->m_wall) - { - att_dmg = 0; - } - - if(att_dmg > 0) - { - immobilize(); - attack_damage(); - siphon_poison_disease(); - } - on_kill(); - oa(); - if(att_dmg > 0) - { - if(att_status->m_hp > 0) - { - counter(); - berserk(); - } - crush_leech(); - } + att_dmg = calculate_attack_damage(); + if(att_dmg == 0) { return; } + // Evaluation order: + // assaults only: fly check + // assaults only: immobilize + // deal damage + // assaults only: (siphon, poison, disease, on_kill) + // on_attacked: poison, disease, assaults only: berserk, skills + // counter, berserk + // assaults only: (crush, leech if still alive) + // check regeneration + const bool dodge_by_fly(def_status->m_card->m_flying && !att_status->m_card->m_flying && !att_status->m_card->m_antiair > 0 && + (fd->effect == Effect::high_skies || fd->flip())); + if(dodge_by_fly) // unnecessary check for structures, commander -> fix later ? + { + _DEBUG_MSG("%s dodges with flying\n", status_description(def_status).c_str()); + return; + } - // If Impenetrable, force attack damage against walls to be 0, - // but still activate Counter! - if(fd->effect == Effect::impenetrable && def_status->m_card->m_wall && att_status->m_hp > 0) - { - counter(); - } + // If Impenetrable, force attack damage against walls to be 0, + // but still activate Counter! + if(fd->effect == Effect::impenetrable && def_status->m_card->m_wall) + { + att_dmg = 0; + } - prepend_on_death(fd); - resolve_skill(fd); - check_regeneration(fd); + if(att_dmg > 0) + { + immobilize(); + attack_damage(); + siphon_poison_disease(); + on_kill(); + } + on_attacked(); + if(att_dmg > 0) + { + if(att_status->m_hp > 0) + { + counter(); + berserk(); } + crush_leech(); + } + + // If Impenetrable, force attack damage against walls to be 0, + // but still activate Counter! + if(fd->effect == Effect::impenetrable && def_status->m_card->m_wall && att_status->m_hp > 0) + { + counter(); } + + prepend_on_death(fd); + resolve_skill(fd); + check_regeneration(fd); } template unsigned calculate_attack_damage() { - return(attack_damage_against_non_assault(fd, *att_status)); + const Card& att_card(*att_status->m_card); + const Card& def_card(*def_status->m_card); + assert(att_card.m_type == CardType::assault); + // pre modifier damage + unsigned damage(attack_power(att_status)); + if(damage == 0) { return(0); } + unsigned modified_damage = safe_minus( + damage // pre-modifier damage + + valor_damage(fd, *att_status) // valor + + def_status->m_enfeebled // enfeeble + + (def_card.m_flying ? att_card.m_antiair : 0) // anti-air + + (def_status->m_hp == def_card.m_health ? att_card.m_burst : 0) // burst + // armor + protect + pierce + , safe_minus(def_card.m_armored + def_status->m_protected, att_card.m_pierce)); + if(debug_print) + { + std::string desc; + if(valor_damage(fd, *att_status) > 0) { desc += "+" + to_string(valor_damage(fd, *att_status)) + "(valor)"; } + if(def_status->m_enfeebled > 0) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } + if(def_card.m_flying && att_card.m_antiair > 0) { desc += "+" + to_string(att_card.m_antiair) + "(antiair)"; } + if(def_status->m_hp == def_card.m_health && att_card.m_burst > 0) { desc += "+" + to_string(att_card.m_burst) + "(burst)"; } + if(!desc.empty()) + { + std::string reduced_desc; + if(def_card.m_armored > 0) { reduced_desc += to_string(def_card.m_armored) + "(armored)"; } + if(def_status->m_protected > 0) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->m_protected) + "(protected)"; } + if(!reduced_desc.empty() && att_card.m_pierce > 0) { reduced_desc += "-" + to_string(att_card.m_pierce) + "(pierce)"; } + if(!reduced_desc.empty()) { desc += "-(" + reduced_desc + ")"; } + desc += "=" + to_string(modified_damage); + } + _DEBUG_MSG("%s attacks %s for %u%s damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), damage, desc.c_str()); + } + return(modified_damage); } template @@ -952,7 +987,6 @@ struct PerformAttack template void attack_damage() { - _DEBUG_MSG("%s attack damage %u to %s\n", status_description(att_status).c_str(), att_dmg, status_description(def_status).c_str()); remove_hp(fd, *def_status, att_dmg); killed_by_attack = def_status->m_hp == 0; } @@ -964,7 +998,7 @@ struct PerformAttack void on_kill() {} template - void oa() + void on_attacked() { if(def_status->m_card->m_poison_oa > 0) { @@ -975,7 +1009,7 @@ struct PerformAttack att_status->m_diseased = true; } oa_berserk(); - for(auto& oa_skill: def_status->m_card->m_skills_attacked) + for(auto& oa_skill: def_status->m_card->m_skills_on_attacked) { _DEBUG_MSG("Evaluating %s skill %s on attacked\n", status_description(def_status).c_str(), skill_description(fd, oa_skill).c_str()); fd->skill_queue.emplace_back(def_status, oa_skill); @@ -992,7 +1026,7 @@ struct PerformAttack if(def_status->m_card->m_counter > 0) { unsigned counter_dmg(counter_damage(att_status, def_status)); - _DEBUG_MSG("%s suffer damage from counter %u by %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); + _DEBUG_MSG("%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, *att_status, counter_dmg); } } @@ -1007,18 +1041,12 @@ struct PerformAttack void crush_leech() {} }; -template<> -unsigned PerformAttack::calculate_attack_damage() -{ - return(attack_damage_against_assault(fd, *att_status, *def_status)); -} - template<> void PerformAttack::immobilize() { if(att_status->m_card->m_immobilize && def_status->m_delay <= 1 && !def_status->m_jammed && !def_status->m_frozen && fd->flip()) { - _DEBUG_MSG("%s immobilize %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); + _DEBUG_MSG("%s immobilizes %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_immobilized = true; } } @@ -1026,7 +1054,6 @@ void PerformAttack::immobilize() template<> void PerformAttack::attack_damage() { - _DEBUG_MSG("%s attack damage %u to %s\n", status_description(att_status).c_str(), att_dmg, status_description(def_status).c_str()); remove_commander_hp(fd, *def_status, att_dmg); } @@ -1035,7 +1062,7 @@ void PerformAttack::siphon_poison_disease() { if(att_status->m_card->m_siphon > 0) { - _DEBUG_MSG("%s siphon %u for %s\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_siphon), status_description(&fd->tap->commander).c_str()); + _DEBUG_MSG("%s siphons %u health for %s\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_siphon), status_description(&fd->tap->commander).c_str()); add_hp(fd, &fd->tap->commander, std::min(att_dmg, att_status->m_card->m_siphon)); } if(att_status->m_card->m_poison > 0) @@ -1053,7 +1080,7 @@ void PerformAttack::on_kill() { if(killed_by_attack) { - for(auto& on_kill_skill: att_status->m_card->m_skills_kill) + for(auto& on_kill_skill: att_status->m_card->m_skills_on_kill) { _DEBUG_MSG("Evaluating %s skill %s on kill\n", status_description(att_status).c_str(), skill_description(fd, on_kill_skill).c_str()); fd->skill_queue.emplace_back(att_status, on_kill_skill); @@ -1073,18 +1100,18 @@ void PerformAttack::crush_leech() CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall if (def_status != nullptr) { - _DEBUG_MSG("%s crush %u on %s\n", status_description(att_status).c_str(), att_status->m_card->m_crush, status_description(def_status).c_str()); + _DEBUG_MSG("%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), att_status->m_card->m_crush); remove_hp(fd, *def_status, att_status->m_card->m_crush); } else { - _DEBUG_MSG("%s crush %u on %s\n", status_description(att_status).c_str(), att_status->m_card->m_crush, status_description(&fd->tip->commander).c_str()); + _DEBUG_MSG("%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(&fd->tip->commander).c_str(), att_status->m_card->m_crush); remove_commander_hp(fd, fd->tip->commander, att_status->m_card->m_crush); } } if(att_status->m_card->m_leech > 0 && att_status->m_hp > 0 && !att_status->m_diseased) { - _DEBUG_MSG("%s leech %u.\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech)); + _DEBUG_MSG("%s leeches %u health\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech)); add_hp(fd, att_status, std::min(att_dmg, att_status->m_card->m_leech)); } } @@ -1095,7 +1122,7 @@ void attack_phase(Field* fd) CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); unsigned num_attacks(att_status->m_card->m_flurry > 0 && fd->flip() ? att_status->m_card->m_flurry + 1 : 1); - if(num_attacks > 1) { _DEBUG_MSG("%s flurry.\n", status_description(att_status).c_str()); } + if(num_attacks > 1) { _DEBUG_MSG("%s activates flurry\n", status_description(att_status).c_str()); } for(unsigned attack_index(0); attack_index < num_attacks && !att_status->m_jammed && !att_status->m_frozen && att_status->m_hp > 0 && fd->tip->commander.m_hp > 0; ++attack_index) { // 3 possibilities: @@ -1114,7 +1141,7 @@ void attack_phase(Field* fd) else { // attack the card on the left - if(alive_assault(def_assaults, fd->current_ci - 1)) + if(fd->current_ci > 0 && alive_assault(def_assaults, fd->current_ci - 1)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(); } @@ -1543,7 +1570,7 @@ template std::vector& skill_targets(Field* fd, CardStatus* src_status) { std::cout << "skill_targets: Error: no specialization for " << skill_names[skill] << "\n"; - assert(false); + throw; } template<> inline std::vector& skill_targets(Field* fd, CardStatus* src_status) @@ -1644,6 +1671,17 @@ CardStatus* get_target_hostile_fast(Field* fd, CardStatus* src_status, const Ski return(nullptr); } +bool negate_by_evade(Field* fd, CardStatus* c) +{ + if(c->m_card->m_evade && fd->flip()) + { + _DEBUG_MSG("%s evades\n", status_description(c).c_str()); + return(true); + } + else + { return(false); } +} + template void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { @@ -1651,11 +1689,9 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski CardStatus* c(get_target_hostile_fast(fd, src_status, s)); if(c) { - // evade - if(!c->m_card->m_evade || (src_status && src_status->m_chaosed) || fd->flip()) + _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); + if((src_status && src_status->m_chaosed) || !negate_by_evade(fd, c)) { - _DEBUG_MSG("%s %s (%u) on %s.\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); - // skill perform_skill(fd, c, std::get<1>(s)); // payback if(c->m_card->m_payback && @@ -1665,13 +1701,11 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski src_status->m_hp > 0 && fd->flip()) { - // payback evade - if(skill_predicate(fd, src_status) && - (!src_status->m_card->m_evade || fd->flip())) + if(skill_predicate(fd, src_status)) { - _DEBUG_MSG("Payback (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); + _DEBUG_MSG("Payback (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); // payback skill - perform_skill(fd, src_status, std::get<1>(s)); + if(!negate_by_evade(fd, c)) { perform_skill(fd, src_status, std::get<1>(s)); } } } } @@ -1688,7 +1722,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil if(array_head > 0) { CardStatus* c(fd->selection_array[fd->rand(0, array_head - 1)]); - _DEBUG_MSG("%s %s (%u) on %s.\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); + _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); perform_skill(fd, c, std::get<1>(s)); if(c->m_card->m_tribute && src_status && @@ -1699,7 +1733,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil { if(skill_predicate(fd, src_status)) { - _DEBUG_MSG("Tribute (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); + _DEBUG_MSG("Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); perform_skill(fd, src_status, std::get<1>(s)); } } @@ -1711,7 +1745,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil CardStatus& emulator = opp->assaults[c->m_index]; if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator)) { - _DEBUG_MSG("Emulate (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); + _DEBUG_MSG("Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); perform_skill(fd, &emulator, std::get<1>(s)); } } @@ -1727,9 +1761,9 @@ void perform_global_hostile_fast(Field* fd, CardStatus* src_status, const SkillS for(unsigned s_index(0); s_index < array_head; ++s_index) { CardStatus* c(fd->selection_array[s_index]); - if(!c->m_card->m_evade || (src_status && src_status->m_chaosed) || fd->flip()) + _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); + if((src_status && src_status->m_chaosed) || !negate_by_evade(fd, c)) { - _DEBUG_MSG("%s %s (%u) on %s.\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); perform_skill(fd, c, std::get<1>(s)); // payback if(c->m_card->m_payback && @@ -1745,11 +1779,8 @@ void perform_global_hostile_fast(Field* fd, CardStatus* src_status, const SkillS } for(unsigned i(0); i < payback_count && skill_predicate(fd, src_status); ++i) { - if((!src_status->m_card->m_evade || fd->flip())) - { - _DEBUG_MSG("Payback (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); - perform_skill(fd, src_status, std::get<1>(s)); - } + _DEBUG_MSG("Payback (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); + if(!negate_by_evade(fd, src_status)) { perform_skill(fd, src_status, std::get<1>(s)); } } maybeTriggerRegen::T>(fd); prepend_on_death(fd); @@ -1763,7 +1794,7 @@ void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSp for(unsigned s_index(0); s_index < array_head; ++s_index) { CardStatus* c(fd->selection_array[s_index]); - _DEBUG_MSG("%s %s (%u) on %s.\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); + _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); perform_skill(fd, c, std::get<1>(s)); if(c->m_card->m_tribute && src_status && @@ -1774,7 +1805,7 @@ void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSp { if(skill_predicate(fd, src_status)) { - _DEBUG_MSG("Tribute (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); + _DEBUG_MSG("Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); perform_skill(fd, src_status, std::get<1>(s)); } } @@ -1786,7 +1817,7 @@ void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSp CardStatus& emulator = opp->assaults[c->m_index]; if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator)) { - _DEBUG_MSG("Emulate (%s %u) on %s.\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); + _DEBUG_MSG("Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); perform_skill(fd, &emulator, std::get<1>(s)); } } @@ -1796,7 +1827,7 @@ void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSp void perform_backfire(Field* fd, CardStatus* src_status, const SkillSpec& s) { CardStatus* c(&fd->players[src_status->m_player]->commander); - _DEBUG_MSG("%s %s (%u) on %s.\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), std::get<1>(s), status_description(c).c_str()); + _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), std::get<1>(s), status_description(c).c_str()); perform_skill(fd, c, std::get<1>(s)); } @@ -1825,10 +1856,10 @@ void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) { CardStatus* c(fd->selection_array[fd->rand(0, array_head - 1)]); // check evade for enemy assaults only - if(c->m_player == src_status->m_player || !c->m_card->m_evade || fd->flip()) + if(c->m_player == src_status->m_player) { - _DEBUG_MSG("%s %s on %s.\n", status_description(src_status).c_str(), skill_names[infuse].c_str(), status_description(c).c_str()); - perform_skill(fd, c, std::get<1>(s)); + _DEBUG_MSG("%s %s on %s\n", status_description(src_status).c_str(), skill_names[infuse].c_str(), status_description(c).c_str()); + if(!negate_by_evade(fd, c)) { perform_skill(fd, c, std::get<1>(s)); } } } } @@ -1836,7 +1867,7 @@ void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) // a summoned card's on play skills seem to be evaluated before any other skills on the skill queue. inline void prepend_skills(Field* fd, CardStatus* status) { - for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_played)) + for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_on_play)) { fd->skill_queue.emplace_front(status, skill); } @@ -1852,7 +1883,7 @@ void summon_card(Field* fd, unsigned player, const Card* summoned) card_status.set(summoned); card_status.m_index = storage->size() - 1; card_status.m_player = player; - _DEBUG_MSG("Summoned [%s] as %s %d\n", card_description(summoned).c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index); + _DEBUG_MSG("Summon %s %u [%s]\n", cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd, summoned).c_str()); prepend_skills(fd, &card_status); if(card_status.m_card->m_blitz && fd->players[opponent(player)]->assaults.size() > card_status.m_index && @@ -1885,7 +1916,7 @@ void perform_trigger_regen(Field* fd, CardStatus* src_status, const SkillSpec& s void perform_shock(Field* fd, CardStatus* src_status, const SkillSpec& s) { - _DEBUG_MSG("%s shock (%u) on %s.\n", status_description(src_status).c_str(), std::get<1>(s), status_description(&fd->tip->commander).c_str()); + _DEBUG_MSG("%s shocks %s for %u damage\n", status_description(src_status).c_str(), status_description(&fd->tip->commander).c_str(), std::get<1>(s)); perform_skill(fd, &fd->tip->commander, std::get<1>(s)); } @@ -1906,6 +1937,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) if(array_head > 0) { c = fd->selection_array[fd->rand(0, array_head - 1)]; + _DEBUG_MSG("%s on %s\n", skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); } } else @@ -1914,15 +1946,15 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // evade check for mimic // individual skills are subject to evade checks too, // but resolve_skill will handle those. - if(c && c->m_card->m_evade && (!src_status || !src_status->m_chaosed) && fd->flip()) + if(c && (!src_status || !src_status->m_chaosed)) { - return; + _DEBUG_MSG("%s on %s\n", skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); + if(negate_by_evade(fd, c)) { return; } } } if(c) { - _DEBUG_MSG("%s on %s\n", skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); for(auto skill: c->m_card->m_skills) { if(src_status && src_status->m_card->m_type == CardType::assault && src_status->m_hp == 0) diff --git a/tyrant.cpp b/tyrant.cpp index d326cd21..3de81418 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -19,7 +19,7 @@ std::set helpful_skills{ /*repair, repair_all, rush, */supply, }; -std::string cardtype_names[CardType::num_cardtypes]{"action", "assault", "commander", "structure"}; +std::string cardtype_names[CardType::num_cardtypes]{"Action", "Assault", "Commander", "Structure"}; std::string effect_names[Effect::num_effects] = { "None", diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 1b1a5189..8f6bba15 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1020,7 +1020,9 @@ void usage(int argc, char** argv) std::cout << "brute : find the best combination of different cards, using up to battles to evaluate a deck.\n"; std::cout << "climb : perform hill-climbing starting from the given attack deck, using up to battles to evaluate a deck.\n"; std::cout << "sim : simulate battles to evaluate a deck.\n"; +#ifndef NDEBUG std::cout << "debug: very verbose output. only one battle. testing purposes only.\n"; +#endif } int main(int argc, char** argv) @@ -1051,7 +1053,19 @@ int main(int argc, char** argv) DeckIface* def_deck = find_deck(decks, deck_parsed.first); if(def_deck == nullptr) { - def_deck = hash_to_deck(deck_parsed.first.c_str(), cards); + try + { def_deck = hash_to_deck(deck_parsed.first.c_str(), cards); } + catch(const std::runtime_error& e) + { + std::cerr << "Error: Deck hash " << deck_parsed.first << ": " << e.what() << std::endl; + return(5); + } + if(def_deck == nullptr) + { + std::cerr << "Error: Invalid defense deck name/hash " << deck_parsed.first << ". Available decks:" << std::endl; + print_available_decks(decks); + return(5); + } } def_decks.push_back(def_deck); def_decks_factors.push_back(deck_parsed.second); @@ -1176,6 +1190,11 @@ int main(int argc, char** argv) num_threads = 1; todo.push_back(std::make_tuple(0u, 0u, fightonce)); } + else + { + std::cerr << "Error: Unknown option " << argv[argIndex] << std::endl; + return(1); + } } DeckIface* att_deck{nullptr}; @@ -1186,7 +1205,21 @@ int main(int argc, char** argv) } else { - att_deck = hash_to_deck(att_deck_name.c_str(), cards); + try + { att_deck = hash_to_deck(att_deck_name.c_str(), cards); } + catch(const std::runtime_error& e) + { + std::cerr << "Error: Deck hash " << att_deck_name << ": " << e.what() << std::endl; + return(5); + } + if(att_deck == nullptr) + { + std::cerr << "Error: Invalid attack deck name/hash " << att_deck_name << ". Available decks:" << std::endl; + std::cerr << "Custom decks:" << std::endl; + for(auto it: decks.custom_decks) + { std::cerr << " " << it.first << std::endl; } + return(5); + } } print_deck(*att_deck); From d204f11e64fe2bb658ff4df85be8d558cbb3f9be Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 13 Feb 2013 12:06:01 +0800 Subject: [PATCH 070/406] Add debuguntil operation. --- sim.cpp | 24 ++++++++---------------- tyrant_optimize.cpp | 21 +++++++++++++++++---- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/sim.cpp b/sim.cpp index ad496c4c..bc7b6258 100644 --- a/sim.cpp +++ b/sim.cpp @@ -26,13 +26,8 @@ bool debug_line(true); { \ if(debug_print) \ { \ - char* format2 = new char[strlen(format) + (8 + 1)*sizeof(char)]; \ - if(debug_line) { strcpy(format2, "%i - "); } \ - else { strcpy(format2, ""); } \ - strcat(format2, format); \ - if(debug_line) { printf(format2, __LINE__ , ##args); } \ - else { printf(format2, ##args); } \ - delete[] format2; \ + if(debug_line) { printf("%i - " format, __LINE__ , ##args); } \ + else { printf(format, ##args); } \ std::cout << std::flush; \ } \ } @@ -967,15 +962,12 @@ struct PerformAttack if(def_status->m_enfeebled > 0) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } if(def_card.m_flying && att_card.m_antiair > 0) { desc += "+" + to_string(att_card.m_antiair) + "(antiair)"; } if(def_status->m_hp == def_card.m_health && att_card.m_burst > 0) { desc += "+" + to_string(att_card.m_burst) + "(burst)"; } - if(!desc.empty()) - { - std::string reduced_desc; - if(def_card.m_armored > 0) { reduced_desc += to_string(def_card.m_armored) + "(armored)"; } - if(def_status->m_protected > 0) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->m_protected) + "(protected)"; } - if(!reduced_desc.empty() && att_card.m_pierce > 0) { reduced_desc += "-" + to_string(att_card.m_pierce) + "(pierce)"; } - if(!reduced_desc.empty()) { desc += "-(" + reduced_desc + ")"; } - desc += "=" + to_string(modified_damage); - } + std::string reduced_desc; + if(def_card.m_armored > 0) { reduced_desc += to_string(def_card.m_armored) + "(armored)"; } + if(def_status->m_protected > 0) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->m_protected) + "(protected)"; } + if(!reduced_desc.empty() && att_card.m_pierce > 0) { reduced_desc += "-" + to_string(att_card.m_pierce) + "(pierce)"; } + if(!reduced_desc.empty()) { desc += "-(" + reduced_desc + ")"; } + if(!desc.empty()) { desc += "=" + to_string(modified_damage); } _DEBUG_MSG("%s attacks %s for %u%s damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), damage, desc.c_str()); } return(modified_damage); diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 8f6bba15..908b741b 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -951,7 +951,7 @@ enum Operation { bruteforce, climb, simulate, - fightonce + fightuntil }; //------------------------------------------------------------------------------ // void print_raid_deck(DeckRandom& deck) @@ -1188,7 +1188,14 @@ int main(int argc, char** argv) { debug_print = true; num_threads = 1; - todo.push_back(std::make_tuple(0u, 0u, fightonce)); + todo.push_back(std::make_tuple(0u, 25u, fightuntil)); + } + else if(strcmp(argv[argIndex], "debuguntil") == 0) + { + debug_print = true; + num_threads = 1; + todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex+1]), (unsigned)atoi(argv[argIndex+2]), fightuntil)); + argIndex += 2; } else { @@ -1258,8 +1265,14 @@ int main(int argc, char** argv) print_score_info(results,p.factors); break; } - case fightonce: { - p.evaluate(1); + case fightuntil: { + while(1) + { + auto results = p.evaluate(1); + print_score_info(results, p.factors); + double score = compute_score(results, p.factors); + if(score >= std::get<0>(op) && score <= std::get<1>(op)) { break; } + } break; } } From 4bbaefbf7f8d886b347dc02b33e02184a793d600 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 13 Feb 2013 12:50:34 +0800 Subject: [PATCH 071/406] Shuffle card candidates for each round (slot) of testing to make better diversity. --- sim.cpp | 2 +- tyrant_optimize.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index bc7b6258..62b1a019 100644 --- a/sim.cpp +++ b/sim.cpp @@ -20,7 +20,7 @@ std::string to_string(T val) } //---------------------- Debugging stuff --------------------------------------- bool debug_print(false); -bool debug_line(true); +bool debug_line(false); #ifndef NDEBUG #define _DEBUG_MSG(format, args...) \ { \ diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 908b741b..468c0ffc 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -559,10 +559,14 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) double current_score = compute_score(results, proc.factors); double best_score = current_score; // Non-commander cards - auto non_commander_cards = boost::join(boost::join(boost::join(std::initializer_list{NULL,}, proc.cards.player_assaults), proc.cards.player_structures), proc.cards.player_actions); + auto non_commander_cards = proc.cards.player_assaults; + non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); + non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_actions.begin(), proc.cards.player_actions.end()); + if(!fixed_len) { non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); } const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; print_deck_inline(best_score, best_commander, best_cards); + std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; @@ -602,6 +606,7 @@ void hill_climbing(unsigned num_iterations, DeckIface* d1, Process& proc) d1->commander = best_commander; eval_commander = false; } + std::shuffle(non_commander_cards.begin(), non_commander_cards.end(), re); for(const Card* card_candidate: non_commander_cards) { if(card_candidate) @@ -659,10 +664,14 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr double current_score = compute_score(results, proc.factors); double best_score = current_score; // Non-commander cards - auto non_commander_cards = boost::join(boost::join(boost::join(std::initializer_list{NULL,}, proc.cards.player_assaults), proc.cards.player_structures), proc.cards.player_actions); + auto non_commander_cards = proc.cards.player_assaults; + non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); + non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_actions.begin(), proc.cards.player_actions.end()); + if(!fixed_len) { non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); } const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; print_deck_inline(best_score, best_commander, best_cards); + std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; bool eval_commander = true; double best_possible = use_anp ? 25 : 1; @@ -703,6 +712,7 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr d1->commander = best_commander; eval_commander = false; } + std::shuffle(non_commander_cards.begin(), non_commander_cards.end(), re); for(const Card* card_candidate: non_commander_cards) { // Various checks to check if the card is accepted From 4e93608f8dc808c4b85520a214d6ba1c7b77b3a1 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 13 Feb 2013 13:11:04 +0800 Subject: [PATCH 072/406] Add debuguntil option to help screen. --- tyrant_optimize.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 468c0ffc..473dbfad 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1031,7 +1031,8 @@ void usage(int argc, char** argv) std::cout << "climb : perform hill-climbing starting from the given attack deck, using up to battles to evaluate a deck.\n"; std::cout << "sim : simulate battles to evaluate a deck.\n"; #ifndef NDEBUG - std::cout << "debug: very verbose output. only one battle. testing purposes only.\n"; + std::cout << "debug: testing purpose only. very verbose output. only one battle.\n"; + std::cout << "debuguntil : testing purpose only. fight until the last fight results in range [, ]. recommend to redirect output.\n"; #endif } @@ -1204,6 +1205,8 @@ int main(int argc, char** argv) { debug_print = true; num_threads = 1; + // fight until the last battle results in range [min_score, max_score]. + // E.g., 0 0: until lose; 1 1: until win (non-ANP); 10 25: until win (ANP). todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex+1]), (unsigned)atoi(argv[argIndex+2]), fightuntil)); argIndex += 2; } From 6358c6f1b2696dd8c549754e7c7fdd7f3e044980 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 17 Feb 2013 03:29:55 +0800 Subject: [PATCH 073/406] Add -A option for achievements. --- achievement.h | 76 ++++ card.h | 22 +- deck.h | 2 + sim.cpp | 964 +++++++++++++++++++++++--------------------- sim.h | 28 +- tyrant.cpp | 42 +- tyrant.h | 45 ++- tyrant_optimize.cpp | 183 +++++---- xml.cpp | 231 ++++++++--- xml.h | 2 + 10 files changed, 939 insertions(+), 656 deletions(-) create mode 100644 achievement.h diff --git a/achievement.h b/achievement.h new file mode 100644 index 00000000..1c211c5d --- /dev/null +++ b/achievement.h @@ -0,0 +1,76 @@ +#ifndef ACHIEVEMENT_H_INCLUDED +#define ACHIEVEMENT_H_INCLUDED + +#include + +enum Comparator +{ + equal, + great_equal, + less_equal +}; + +class Counter +{ +public: + unsigned m_target; + Comparator m_comparator; +public: + Counter(unsigned target=1, Comparator comparator=great_equal): m_target(target), m_comparator(comparator) {} + void init(unsigned target, Comparator comparator) + { + m_target = target; + m_comparator = comparator; + } + bool check(unsigned value) const + { + switch(m_comparator) + { + case equal: return value == m_target; + case great_equal: return value >= m_target; + case less_equal: return value <= m_target; + default: throw; + } + } + // predict whether the monotonic increasing counter will be met: +1: true; 0: unknown; -1: false. + int predict_monoinc(unsigned value) const + { + switch(m_comparator) + { + case equal: + case less_equal: return value <= m_target ? 0 : -1; + case great_equal: return value < m_target ? 0 : +1; + default: throw; + } + } + std::string str() const + { + std::stringstream ios; + switch(m_comparator) + { + case equal: ios << " = "; break; + case great_equal: ios << " >= "; break; + case less_equal: ios << " <= "; break; + default: throw; + } + ios << m_target; + return ios.str(); + } +}; + +struct Achievement +{ + unsigned id; + std::string name; + Counter mission_condition; + std::vector req_counter; + // Following are indexes to the req_counter + std::map skill_used; + std::map unit_played; + std::map unit_type_played; + std::map unit_faction_played; + std::map unit_rarity_played; + std::map unit_type_killed; +}; + +#endif diff --git a/card.h b/card.h index 4a524cec..f7853a2b 100644 --- a/card.h +++ b/card.h @@ -40,7 +40,6 @@ class Card m_poison(0), m_poison_oa(0), m_rarity(1), - m_recharge(false), m_refresh(false), m_regenerate(0), m_set(0), @@ -56,16 +55,16 @@ class Card { } - void add_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills.push_back(std::make_tuple(v1, v2, v3)); } - void add_played_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills_on_play.push_back(std::make_tuple(v1, v2, v3)); } - void add_died_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills_on_death.push_back(std::make_tuple(v1, v2, v3)); } - void add_attacked_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills_on_attacked.push_back(std::make_tuple(v1, v2, v3)); } - void add_kill_skill(ActiveSkill v1, unsigned v2, Faction v3) - { m_skills_on_kill.push_back(std::make_tuple(v1, v2, v3)); } + void add_skill(Skill v1, unsigned v2, Faction v3, bool v4) + { m_skills.push_back(std::make_tuple(v1, v2, v3, v4)); } + void add_played_skill(Skill v1, unsigned v2, Faction v3, bool v4) + { m_skills_on_play.push_back(std::make_tuple(v1, v2, v3, v4)); } + void add_died_skill(Skill v1, unsigned v2, Faction v3, bool v4) + { m_skills_on_death.push_back(std::make_tuple(v1, v2, v3, v4)); } + void add_attacked_skill(Skill v1, unsigned v2, Faction v3, bool v4) + { m_skills_on_attacked.push_back(std::make_tuple(v1, v2, v3, v4)); } + void add_kill_skill(Skill v1, unsigned v2, Faction v3, bool v4) + { m_skills_on_kill.push_back(std::make_tuple(v1, v2, v3, v4)); } unsigned m_antiair; unsigned m_armored; @@ -98,7 +97,6 @@ class Card unsigned m_poison; unsigned m_poison_oa; unsigned m_rarity; - bool m_recharge; bool m_refresh; unsigned m_regenerate; int m_set; diff --git a/deck.h b/deck.h index b481e81c..2132f580 100644 --- a/deck.h +++ b/deck.h @@ -104,6 +104,8 @@ struct Decks std::list mission_decks; std::map mission_decks_by_id; std::map mission_decks_by_name; + std::map mission_name_by_id; + std::map mission_id_by_name; std::list raid_decks; std::map raid_decks_by_id; std::map raid_decks_by_name; diff --git a/sim.cpp b/sim.cpp index 62b1a019..42bee0db 100644 --- a/sim.cpp +++ b/sim.cpp @@ -9,6 +9,7 @@ #include "card.h" #include "cards.h" #include "deck.h" +#include "achievement.h" //---------------------- $00 general stuff ------------------------------------- template @@ -57,7 +58,9 @@ CardStatus::CardStatus(const Card* card) : m_protected(0), m_rallied(0), m_weakened(0), - m_temporary_split(false) + m_temporary_split(false), + m_summoned(false), + m_attacked(false) { } @@ -90,6 +93,8 @@ inline void CardStatus::set(const Card& card) m_rallied = 0; m_weakened = 0; m_temporary_split = false; + m_summoned = false; + m_attacked = false; } //------------------------------------------------------------------------------ inline unsigned safe_minus(unsigned x, unsigned y) @@ -109,6 +114,7 @@ std::string skill_description(Field* fd, const SkillSpec& s) return(skill_names[std::get<0>(s)] + " " + fd->cards.by_id(std::get<1>(s))->m_name.c_str()); default: return(skill_names[std::get<0>(s)] + + (std::get<3>(s) ? " all" : "") + (std::get<2>(s) == allfactions ? "" : std::string(" ") + faction_names[std::get<2>(s)]) + (std::get<1>(s) == 0 ? "" : std::string(" ") + to_string(std::get<1>(s)))); } @@ -158,7 +164,6 @@ std::string card_description(Field* fd, const Card* c) if(c->m_payback) { desc += ", payback"; } if(c->m_pierce > 0) { desc += ", pierce " + to_string(c->m_pierce); } if(c->m_poison > 0) { desc += ", poison " + to_string(c->m_poison); } - if(c->m_recharge) { desc += ", recharge"; } if(c->m_refresh) { desc += ", refresh"; } if(c->m_regenerate > 0) { desc += ", regenerate " + to_string(c->m_regenerate); } if(c->m_siphon > 0) { desc += ", siphon " + to_string(c->m_siphon); } @@ -230,12 +235,44 @@ std::string CardStatus::description() return(desc); } //------------------------------------------------------------------------------ -std::string status_description(CardStatus* status) +inline std::string status_description(CardStatus* status) { - if (!status) { return("A vanished card"); } // Unknown Action card? return status->description(); } //------------------------------------------------------------------------------ +inline void print_achievement_results(Field* fd) +{ + if(fd->achievement.req_counter.size() == 0) + { + return; + } + _DEBUG_MSG("Achievement:\n"); + for(auto i(fd->achievement.skill_used.begin()); i != fd->achievement.skill_used.end(); ++i) + { + _DEBUG_MSG(" Use skills: %s %u%s? %s\n", skill_names[i->first].c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + } + for(auto i(fd->achievement.unit_played.begin()); i != fd->achievement.unit_played.end(); ++i) + { + _DEBUG_MSG(" Play units: %s %u%s? %s\n", fd->cards.by_id(i->first)->m_name.c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + } + for(auto i(fd->achievement.unit_type_played.begin()); i != fd->achievement.unit_type_played.end(); ++i) + { + _DEBUG_MSG(" Play units of type: %s %u%s? %s\n", cardtype_names[i->first].c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + } + for(auto i(fd->achievement.unit_faction_played.begin()); i != fd->achievement.unit_faction_played.end(); ++i) + { + _DEBUG_MSG(" Play units of faction: %s %u%s? %s\n", faction_names[i->first].c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + } + for(auto i(fd->achievement.unit_rarity_played.begin()); i != fd->achievement.unit_rarity_played.end(); ++i) + { + _DEBUG_MSG(" Play units of rarity: %s %u%s? %s\n", rarity_names[i->first].c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + } + for(auto i(fd->achievement.unit_type_killed.begin()); i != fd->achievement.unit_type_killed.end(); ++i) + { + _DEBUG_MSG(" Kill units of type: %s %u%s? %s\n", cardtype_names[i->first].c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + } +} +//------------------------------------------------------------------------------ void Hand::reset(std::mt19937& re) { assaults.reset(); @@ -284,7 +321,7 @@ void resolve_skill(Field* fd) void attack_phase(Field* fd); SkillSpec augmented_skill(CardStatus* status, const SkillSpec& s) { - if (std::get<0>(s) == augment || std::get<0>(s) == augment_all || std::get<0>(s) == summon || std::get<1>(s) == 0) + if (std::get<0>(s) == augment || std::get<0>(s) == summon || std::get<1>(s) == 0) { return s; } @@ -321,6 +358,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector auto& infused_s = status->m_infused ? infused_skill(fusioned_s) : fusioned_s; fd->skill_queue.emplace_back(status, infused_s); resolve_skill(fd); + if(fd->end) { break; } } } struct PlayCard @@ -342,20 +380,17 @@ struct PlayCard { setStorage(); placeCard(); - placeDebugMsg(); onPlaySkills(); blitz(); fieldEffects(); return(true); } - // action template void setStorage() { } - // assault + structure template void placeCard() { @@ -368,20 +403,14 @@ struct PlayCard { ++status->m_delay; } - } - - // all - template - void placeDebugMsg() - { - if (storage) - { - _DEBUG_MSG("%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), storage->size() - 1, card_description(fd, card).c_str()); - } - else + if(status->m_player == 0) { - _DEBUG_MSG("%s plays %s [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), card_description(fd, card).c_str()); + fd->inc_counter(fd->achievement.unit_played, card->m_id); + fd->inc_counter(fd->achievement.unit_type_played, card->m_type); + fd->inc_counter(fd->achievement.unit_faction_played, card->m_faction); + fd->inc_counter(fd->achievement.unit_rarity_played, card->m_rarity); } + _DEBUG_MSG("%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), storage->size() - 1, card_description(fd, card).c_str()); } // all except assault: noop @@ -405,6 +434,7 @@ struct PlayCard _DEBUG_MSG("Evaluating %s skill %s on play\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(status, skill); resolve_skill(fd); + if(fd->end) { break; } } } }; @@ -420,10 +450,11 @@ void PlayCard::setStorage() { storage = &fd->tap->structures; } -// action +// action: played on structure slots template <> -void PlayCard::placeCard() +void PlayCard::setStorage() { + storage = &fd->tap->structures; } // assault template <> @@ -455,24 +486,15 @@ void PlayCard::onPlaySkills() for(auto& skill: card->m_skills) { _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); - fd->skill_queue.emplace_back(nullptr, skill); + fd->skill_queue.emplace_back(status, skill); resolve_skill(fd); - // Special case: enemy commander killed by a shock action card - if(fd->tip->commander.m_hp == 0) - { - fd->end = true; - break; - } - } - // Special case: recharge ability - if(card->m_recharge && fd->flip()) - { - fd->tap->deck->place_at_bottom(card); + if(fd->end) { break; } } } //------------------------------------------------------------------------------ void turn_start_phase(Field* fd); void prepend_on_death(Field* fd); +bool check_and_perform_refresh(Field* fd, CardStatus* src_status); // return value : (raid points) -> attacker wins, 0 -> defender wins unsigned play(Field* fd) { @@ -484,6 +506,8 @@ unsigned play(Field* fd) fd->tip = fd->players[fd->tipi]; fd->fusion_count = 0; fd->end = false; + fd->achievement_counter.clear(); + fd->achievement_counter.resize(fd->achievement.req_counter.size()); // ANP: Last decision point is second-to-last card played. fd->points_since_last_decision = 0; @@ -505,16 +529,16 @@ unsigned play(Field* fd) } turn_start_phase(fd); // Special case: refresh on commander - if(fd->tip->commander.m_card->m_refresh && fd->tip->commander.m_hp > 0) + if(fd->tip->commander.m_card->m_refresh) { - fd->tip->commander.m_hp = fd->tip->commander.m_card->m_health; + check_and_perform_refresh(fd, &fd->tip->commander); } if(fd->effect == Effect::clone_project || (fd->effect == Effect::clone_experiment && (fd->turn == 9 || fd->turn == 10))) { std::vector skills; - skills.emplace_back(temporary_split, 0, allfactions); + skills.emplace_back(temporary_split, 0, allfactions, false); // The skill doesn't actually come from the commander, // but we need to provide some source and it seemed most reasonable. evaluate_skills(fd, &fd->tap->commander, skills); @@ -550,7 +574,7 @@ unsigned play(Field* fd) { unsigned index(fd->rand(0, fd->cards.player_assaults.size() - 1)); std::vector skills; - skills.emplace_back(summon, fd->cards.player_assaults[index]->m_id, allfactions); + skills.emplace_back(summon, fd->cards.player_assaults[index]->m_id, allfactions, false); evaluate_skills(fd, &fd->tap->commander, skills); } @@ -559,7 +583,7 @@ unsigned play(Field* fd) for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->structures.size(); ++fd->current_ci) { CardStatus& current_status(fd->tap->structures[fd->current_ci]); - if(current_status.m_delay == 0) + if(current_status.m_delay == 0 && current_status.m_hp > 0) { evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); } @@ -573,33 +597,33 @@ unsigned play(Field* fd) if((current_status.m_delay > 0 && !current_status.m_blitzing) || current_status.m_hp == 0 || current_status.m_jammed || current_status.m_frozen) { //_DEBUG_MSG("! Assault %u (%s) hp: %u, jammed %u\n", card_index, current_status.m_card->m_name.c_str(), current_status.m_hp, current_status.m_jammed); + continue; } - else + // Special case: check for split (tartarus swarm raid, or clone battlefield effects) + // TODO skill_activate + if((current_status.m_temporary_split || current_status.m_card->m_split) && fd->tap->assaults.size() + fd->tap->structures.size() < 100) { - // Special case: check for split (tartarus swarm raid, or clone battlefield effects) - if((current_status.m_temporary_split || current_status.m_card->m_split) && fd->tap->assaults.size() + fd->tap->structures.size() < 100) - { - CardStatus& status_split(fd->tap->assaults.add_back()); - status_split.set(current_status.m_card); - status_split.m_index = fd->tap->assaults.size() - 1; - status_split.m_player = fd->tapi; - _DEBUG_MSG("%s splits %s\n", status_description(¤t_status).c_str(), status_description(&status_split).c_str()); - for(auto& skill: status_split.m_card->m_skills_on_play) - { - _DEBUG_MSG("Evaluating %s skill %s on play\n", status_description(¤t_status).c_str(), skill_description(fd, skill).c_str()); - fd->skill_queue.emplace_back(&status_split, skill); - resolve_skill(fd); - } - // TODO: Use summon to implement split? - // TODO: Determine whether we need to check for Blitz for the newly-Split unit - } - // Evaluate skills - evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); - // Attack - if(!current_status.m_immobilized && current_status.m_hp > 0) + CardStatus& status_split(fd->tap->assaults.add_back()); + status_split.set(current_status.m_card); + status_split.m_index = fd->tap->assaults.size() - 1; + status_split.m_player = fd->tapi; + _DEBUG_MSG("%s splits %s\n", status_description(¤t_status).c_str(), status_description(&status_split).c_str()); + for(auto& skill: status_split.m_card->m_skills_on_play) { - attack_phase(fd); + _DEBUG_MSG("Evaluating %s skill %s on play\n", status_description(¤t_status).c_str(), skill_description(fd, skill).c_str()); + fd->skill_queue.emplace_back(&status_split, skill); + resolve_skill(fd); + if(fd->end) { break; } } + // TODO: Use summon to implement split? + // TODO: Determine whether we need to check for Blitz for the newly-Split unit + } + // Evaluate skills + evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); + // Attack + if(!current_status.m_immobilized && current_status.m_hp > 0) + { + attack_phase(fd); } } _DEBUG_MSG("TURN %u ends for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); @@ -607,14 +631,29 @@ unsigned play(Field* fd) std::swap(fd->tap, fd->tip); ++fd->turn; } + bool made_achievement = true; + for(unsigned i(0); made_achievement && i < fd->achievement.req_counter.size(); ++i) + { + made_achievement = made_achievement && fd->achievement.req_counter[i].check(fd->achievement_counter[i]); + } + if(debug_print) + { + print_achievement_results(fd); + } // defender wins if(fd->players[0]->commander.m_hp == 0 || (!win_tie && fd->turn > turn_limit)) { _DEBUG_MSG("Defender wins.\n"); return(0); } + // achievement: assuming winner='1' + if (!made_achievement) + { + _DEBUG_MSG("Achievement fails.\n"); + return(0); + } // attacker wins - if(fd->players[1]->commander.m_hp == 0 || (win_tie && fd->turn > turn_limit)) + if((fd->players[1]->commander.m_hp == 0 || (win_tie && fd->turn > turn_limit))) { // ANP: Speedy if last_decision + 10 > turn. // fd->turn has advanced once past the actual turn the battle has ended. @@ -633,22 +672,157 @@ unsigned play(Field* fd) assert(false); return 0; } + +// For the active player, delay == 0 or blitzing; for the inactive player, delay <= 1. +inline bool can_act(Field* fd, CardStatus* c) { return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen && (fd->tapi == c->m_player ? c->m_delay == 0 || c->m_blitzing : c->m_delay <= 1)); } + +// Check if a skill actually activated. +template +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ return(true); } + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(ref->m_card->m_flying); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + unsigned opponent_player = opponent(c->m_player); + return(fd->players[opponent_player]->assaults.size() > c->m_index && + fd->players[opponent_player]->assaults[c->m_index].m_hp > 0 && + fd->players[opponent_player]->assaults[c->m_index].m_delay == 0); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(ref->m_card->m_type == CardType::assault && ref->m_hp == ref->m_card->m_health); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(!ref->m_diseased); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(c->m_player != ref->m_player && fd->flip()); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(fd->flip()); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(!ref->m_card->m_flying && !ref->m_card->m_antiair > 0 && (fd->effect == Effect::high_skies || fd->flip())); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(!ref->m_immobilized && can_act(fd, ref) && fd->flip()); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(fd->flip()); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(c->m_hp > 0 && !c->m_diseased); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + // Never payback allied units (chaosed). + return(ref->m_card->m_type == CardType::assault && c->m_player != ref->m_player && ref->m_hp > 0 && fd->flip()); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(fd->flip()); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(c->m_hp < c->m_card->m_health && !c->m_diseased); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(c->m_hp == 0 && !c->m_diseased && fd->flip()); +} + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ return(ref->m_card->m_type == CardType::assault && ref != c && ref->m_hp > 0 && fd->flip()); } + +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + unsigned count_ta(0); + unsigned count_td(0); + for(unsigned i(0); i < fd->tap->assaults.size(); ++i) + { + if(fd->tap->assaults[i].m_hp > 0) { ++count_ta; } + } + for(unsigned i(0); i < fd->tip->assaults.size(); ++i) + { + if(fd->tip->assaults[i].m_hp > 0) { ++count_td; } + } + return(count_ta < count_td); +} + +template +inline bool skill_activate(Field* fd, CardStatus* c, CardStatus* ref) +{ + if(skill_check(fd, c, ref)) + { + if(c->m_player == 0) + { + fd->inc_counter(fd->achievement.skill_used, skill_id); + } + return(true); + } + else + { return(false); } +} //------------------------------------------------------------------------------ // All the stuff that happens at the beginning of a turn, before a card is played // returns true iff the card died. void remove_hp(Field* fd, CardStatus& status, unsigned dmg) { assert(status.m_hp > 0); - _DEBUG_MSG("%s takes %u damage\n", status_description(&status).c_str(), dmg); +// _DEBUG_MSG("%s takes %u damage\n", status_description(&status).c_str(), dmg); status.m_hp = safe_minus(status.m_hp, dmg); if(status.m_hp == 0) { _DEBUG_MSG("%s dies\n", status_description(&status).c_str()); + if(status.m_player == 1 && !status.m_summoned) + { + fd->inc_counter(fd->achievement.unit_type_killed, status.m_card->m_type); + } if(status.m_card->m_skills_on_death.size() > 0) { fd->killed_with_on_death.push_back(&status); } - if(status.m_card->m_regenerate) + if(status.m_card->m_regenerate > 0) { fd->killed_with_regen.push_back(&status); } @@ -658,7 +832,10 @@ inline bool is_it_dead(CardStatus& c) { if(c.m_hp == 0) // yes it is { - _DEBUG_MSG("Dead: %s\n", status_description(&c).c_str()); + if(c.m_card->m_type != CardType::action) + { + _DEBUG_MSG("Dead: %s\n", status_description(&c).c_str()); + } return(true); } else { return(false); } // nope still kickin' @@ -682,13 +859,10 @@ void check_regeneration(Field* fd) for(unsigned i(0); i < fd->killed_with_regen.size(); ++i) { CardStatus& status = *fd->killed_with_regen[i]; - if(status.m_hp == 0 && status.m_card->m_regenerate > 0 && !status.m_diseased) + if(skill_activate(fd, &status, nullptr)) { - if (fd->flip()) - { - _DEBUG_MSG("%s regenerates, hp 0 -> %u\n", status_description(&status).c_str(), status.m_card->m_health); - add_hp(fd, &status, status.m_card->m_regenerate); - } + _DEBUG_MSG("%s regenerates, hp 0 -> %u\n", status_description(&status).c_str(), status.m_card->m_health); + add_hp(fd, &status, status.m_card->m_regenerate); } } fd->killed_with_regen.clear(); @@ -766,10 +940,10 @@ void turn_start_phase(Field* fd) status.m_rallied = 0; status.m_weakened = 0; status.m_temporary_split = false; - if(status.m_card->m_refresh && status.m_hp < status.m_card->m_health && !status.m_diseased) + status.m_attacked = false; + if(status.m_card->m_refresh) { - _DEBUG_MSG("%s refreshes. hp -> %u.\n", status_description(&status).c_str(), status.m_card->m_health); - add_hp(fd, &status, status.m_card->m_health); + check_and_perform_refresh(fd, &status); } } } @@ -784,10 +958,9 @@ void turn_start_phase(Field* fd) { CardStatus& status(structures[index]); status.m_index = index; - if(status.m_card->m_refresh && status.m_hp < status.m_card->m_health) + if(status.m_card->m_refresh) { - _DEBUG_MSG("%s refreshes. hp -> %u.\n", status_description(&status).c_str(), status.m_card->m_health); - add_hp(fd, &status, status.m_card->m_health); + check_and_perform_refresh(fd, &status); } } } @@ -798,12 +971,6 @@ void turn_start_phase(Field* fd) check_regeneration(fd); } //---------------------- $50 attack by assault card implementation ------------- -inline void apply_poison(CardStatus* target, unsigned v) -{ - if(v <= target->m_poisoned) { return; } - _DEBUG_MSG("%s is poisoned (%u)\n", status_description(target).c_str(), v); - target->m_poisoned = v; -} // Counter damage dealt to the attacker (att) by defender (def) // pre-condition: only valid if m_card->m_counter > 0 inline unsigned counter_damage(CardStatus* att, CardStatus* def) @@ -816,31 +983,12 @@ inline CardStatus* select_first_enemy_wall(Field* fd) { for(unsigned i(0); i < fd->tip->structures.size(); ++i) { - CardStatus& card(fd->tip->structures[i]); - if(card.m_card->m_wall && card.m_hp > 0) { return(&card); } + CardStatus& c(fd->tip->structures[i]); + if(c.m_card->m_wall && c.m_hp > 0 && skill_activate(fd, &c, nullptr)) { return(&c); } } return(nullptr); } -inline unsigned valor_damage(Field* fd, CardStatus& status) -{ - if(status.m_card->m_valor > 0) - { - unsigned count_ta(0); - unsigned count_td(0); - for(unsigned i(0); i < fd->tap->assaults.size(); ++i) - { - if(fd->tap->assaults[i].m_hp > 0) { ++count_ta; } - } - for(unsigned i(0); i < fd->tip->assaults.size(); ++i) - { - if(fd->tip->assaults[i].m_hp > 0) { ++count_td; } - } - if(count_ta < count_td) { return(status.m_card->m_valor); } - } - return(0); -} - inline bool alive_assault(Storage& assaults, unsigned index) { return(assaults.size() > index && assaults[index].m_hp > 0); @@ -883,7 +1031,6 @@ struct PerformAttack void op() { att_dmg = calculate_attack_damage(); - if(att_dmg == 0) { return; } // Evaluation order: // assaults only: fly check // assaults only: immobilize @@ -893,46 +1040,47 @@ struct PerformAttack // counter, berserk // assaults only: (crush, leech if still alive) // check regeneration - const bool dodge_by_fly(def_status->m_card->m_flying && !att_status->m_card->m_flying && !att_status->m_card->m_antiair > 0 && - (fd->effect == Effect::high_skies || fd->flip())); - if(dodge_by_fly) // unnecessary check for structures, commander -> fix later ? + if(att_dmg > 0 && def_status->m_card->m_flying && skill_activate(fd, def_status, att_status)) { _DEBUG_MSG("%s dodges with flying\n", status_description(def_status).c_str()); return; } - // If Impenetrable, force attack damage against walls to be 0, + // If Impenetrable, prevent attack damage against walls, // but still activate Counter! - if(fd->effect == Effect::impenetrable && def_status->m_card->m_wall) + if(att_dmg > 0 && fd->effect == Effect::impenetrable && def_status->m_card->m_wall) { + _DEBUG_MSG("%s is impenetrable\n", status_description(def_status).c_str()); att_dmg = 0; } - if(att_dmg > 0) { immobilize(); - attack_damage(); + if(skill_activate(fd, att_status, def_status)) + { + attack_damage(); + } siphon_poison_disease(); on_kill(); } on_attacked(); + if(att_status->m_hp > 0 && def_status->m_card->m_counter > 0 && skill_activate(fd, def_status, att_status)) + { + // perform_skill_counter + unsigned counter_dmg(counter_damage(att_status, def_status)); + _DEBUG_MSG("%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); + remove_hp(fd, *att_status, counter_dmg); + } if(att_dmg > 0) { - if(att_status->m_hp > 0) + if(att_status->m_hp > 0 && att_status->m_card->m_berserk > 0 && skill_activate(fd, att_status, nullptr)) { - counter(); - berserk(); + // perform_skill_berserk + att_status->m_berserk += att_status->m_card->m_berserk; } crush_leech(); } - // If Impenetrable, force attack damage against walls to be 0, - // but still activate Counter! - if(fd->effect == Effect::impenetrable && def_status->m_card->m_wall && att_status->m_hp > 0) - { - counter(); - } - prepend_on_death(fd); resolve_skill(fd); check_regeneration(fd); @@ -947,21 +1095,29 @@ struct PerformAttack // pre modifier damage unsigned damage(attack_power(att_status)); if(damage == 0) { return(0); } + unsigned valor_damage{att_card.m_valor && skill_activate(fd, att_status, nullptr) ? att_card.m_valor : 0}; + unsigned antiair_damage{att_card.m_antiair > 0 && skill_activate(fd, att_status, def_status) ? att_card.m_antiair : 0}; + unsigned burst_damage{att_card.m_burst > 0 && skill_activate(fd, att_status, def_status) ? att_card.m_burst : 0}; unsigned modified_damage = safe_minus( damage // pre-modifier damage - + valor_damage(fd, *att_status) // valor + + valor_damage + def_status->m_enfeebled // enfeeble - + (def_card.m_flying ? att_card.m_antiair : 0) // anti-air - + (def_status->m_hp == def_card.m_health ? att_card.m_burst : 0) // burst + + antiair_damage + + burst_damage // armor + protect + pierce , safe_minus(def_card.m_armored + def_status->m_protected, att_card.m_pierce)); + if(def_card.m_armored > 0) + { + skill_activate(fd, def_status, nullptr); + } + // TODO skill_activate if(debug_print) { std::string desc; - if(valor_damage(fd, *att_status) > 0) { desc += "+" + to_string(valor_damage(fd, *att_status)) + "(valor)"; } + if(valor_damage > 0) { desc += "+" + to_string(valor_damage) + "(valor)"; } if(def_status->m_enfeebled > 0) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } - if(def_card.m_flying && att_card.m_antiair > 0) { desc += "+" + to_string(att_card.m_antiair) + "(antiair)"; } - if(def_status->m_hp == def_card.m_health && att_card.m_burst > 0) { desc += "+" + to_string(att_card.m_burst) + "(burst)"; } + if(antiair_damage > 0) { desc += "+" + to_string(antiair_damage) + "(antiair)"; } + if(burst_damage) { desc += "+" + to_string(burst_damage) + "(burst)"; } std::string reduced_desc; if(def_card.m_armored > 0) { reduced_desc += to_string(def_card.m_armored) + "(armored)"; } if(def_status->m_protected > 0) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->m_protected) + "(protected)"; } @@ -992,43 +1148,31 @@ struct PerformAttack template void on_attacked() { - if(def_status->m_card->m_poison_oa > 0) + if(def_status->m_card->m_poison_oa > att_status->m_poisoned && skill_activate(fd, def_status, att_status)) { - apply_poison(att_status, def_status->m_card->m_poison_oa); + unsigned v = def_status->m_card->m_poison_oa; + _DEBUG_MSG("%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); + att_status->m_poisoned = v; } - if(def_status->m_card->m_disease_oa) + if(def_status->m_card->m_disease_oa && skill_activate(fd, def_status, att_status)) { + // perform_skill_disease + _DEBUG_MSG("%s (on attacked) diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); att_status->m_diseased = true; } - oa_berserk(); + if(def_status->m_hp > 0 && def_status->m_card->m_berserk_oa > 0 && skill_activate(fd, def_status, nullptr)) + { + def_status->m_berserk += def_status->m_card->m_berserk_oa; + } for(auto& oa_skill: def_status->m_card->m_skills_on_attacked) { _DEBUG_MSG("Evaluating %s skill %s on attacked\n", status_description(def_status).c_str(), skill_description(fd, oa_skill).c_str()); fd->skill_queue.emplace_back(def_status, oa_skill); resolve_skill(fd); + if(fd->end) { break; } } } - template - void oa_berserk() {} - - template - void counter() - { - if(def_status->m_card->m_counter > 0) - { - unsigned counter_dmg(counter_damage(att_status, def_status)); - _DEBUG_MSG("%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); - remove_hp(fd, *att_status, counter_dmg); - } - } - - template - void berserk() - { - att_status->m_berserk += att_status->m_card->m_berserk; - } - template void crush_leech() {} }; @@ -1036,7 +1180,7 @@ struct PerformAttack template<> void PerformAttack::immobilize() { - if(att_status->m_card->m_immobilize && def_status->m_delay <= 1 && !def_status->m_jammed && !def_status->m_frozen && fd->flip()) + if(att_status->m_card->m_immobilize && skill_activate(fd, att_status, def_status)) { _DEBUG_MSG("%s immobilizes %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_immobilized = true; @@ -1052,17 +1196,24 @@ void PerformAttack::attack_damage() template<> void PerformAttack::siphon_poison_disease() { - if(att_status->m_card->m_siphon > 0) + if(att_status->m_card->m_siphon > 0 && skill_activate(fd, att_status, def_status)) { - _DEBUG_MSG("%s siphons %u health for %s\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_siphon), status_description(&fd->tap->commander).c_str()); - add_hp(fd, &fd->tap->commander, std::min(att_dmg, att_status->m_card->m_siphon)); + // perform_skill_siphon + unsigned v = std::min(att_dmg, att_status->m_card->m_siphon); + _DEBUG_MSG("%s siphons %u health for %s\n", status_description(att_status).c_str(), v, status_description(&fd->tap->commander).c_str()); + add_hp(fd, &fd->tap->commander, v); } - if(att_status->m_card->m_poison > 0) + if(att_status->m_card->m_poison > def_status->m_poisoned && skill_activate(fd, att_status, def_status)) { - apply_poison(def_status, att_status->m_card->m_poison); + // perform_skill_poison + unsigned v = att_status->m_card->m_poison; + _DEBUG_MSG("%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); + def_status->m_poisoned = v; } - if(att_status->m_card->m_disease) + if(att_status->m_card->m_disease && skill_activate(fd, att_status, def_status)) { + // perform_skill_disease + _DEBUG_MSG("%s diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); def_status->m_diseased = true; } } @@ -1077,18 +1228,17 @@ void PerformAttack::on_kill() _DEBUG_MSG("Evaluating %s skill %s on kill\n", status_description(att_status).c_str(), skill_description(fd, on_kill_skill).c_str()); fd->skill_queue.emplace_back(att_status, on_kill_skill); resolve_skill(fd); + if(fd->end) { break; } } } } -template<> -void PerformAttack::oa_berserk() { def_status->m_berserk += def_status->m_card->m_berserk_oa; } - template<> void PerformAttack::crush_leech() { - if(att_status->m_card->m_crush > 0 && killed_by_attack) + if(att_status->m_card->m_crush > 0 && killed_by_attack && skill_activate(fd, att_status, nullptr)) { + // perform_skill_crush CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall if (def_status != nullptr) { @@ -1101,7 +1251,7 @@ void PerformAttack::crush_leech() remove_commander_hp(fd, fd->tip->commander, att_status->m_card->m_crush); } } - if(att_status->m_card->m_leech > 0 && att_status->m_hp > 0 && !att_status->m_diseased) + if(att_status->m_card->m_leech > 0 && skill_activate(fd, att_status, nullptr)) { _DEBUG_MSG("%s leeches %u health\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech)); add_hp(fd, att_status, std::min(att_dmg, att_status->m_card->m_leech)); @@ -1113,8 +1263,13 @@ void attack_phase(Field* fd) { CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); - unsigned num_attacks(att_status->m_card->m_flurry > 0 && fd->flip() ? att_status->m_card->m_flurry + 1 : 1); - if(num_attacks > 1) { _DEBUG_MSG("%s activates flurry\n", status_description(att_status).c_str()); } + att_status->m_attacked = true; + unsigned num_attacks(1); + if(att_status->m_card->m_flurry > 0 && skill_activate(fd, att_status, nullptr)) + { + _DEBUG_MSG("%s activates flurry\n", status_description(att_status).c_str()); + num_attacks += att_status->m_card->m_flurry; + } for(unsigned attack_index(0); attack_index < num_attacks && !att_status->m_jammed && !att_status->m_frozen && att_status->m_hp > 0 && fd->tip->commander.m_hp > 0; ++attack_index) { // 3 possibilities: @@ -1122,16 +1277,18 @@ void attack_phase(Field* fd) // - 2. swipe attack the assault in front and adjacent assaults if any // - 3. attack against the commander or walls (if there is no assault or if the attacker has the fear attribute) // Check if attack mode is 1. or 2. (there is a living assault card in front, and no fear) - if(alive_assault(def_assaults, fd->current_ci) && !att_status->m_card->m_fear) + if(alive_assault(def_assaults, fd->current_ci) && !(att_status->m_card->m_fear && skill_activate(fd, att_status, nullptr))) { // attack mode 1. - if(!att_status->m_card->m_swipe) + if(!(att_status->m_card->m_swipe && skill_activate(fd, att_status, nullptr))) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); } // attack mode 2. else { + // perform_skill_swipe + _DEBUG_MSG("%s swipes\n", status_description(att_status).c_str()); // attack the card on the left if(fd->current_ci > 0 && alive_assault(def_assaults, fd->current_ci - 1)) { @@ -1194,18 +1351,15 @@ template inline bool skill_predicate(Field* fd, CardStatus* c) { assert(false); return(false); } -// For the active player, delay == 0 or blitzing; for the inactive player, delay <= 1. -inline bool can_act(Field* fd, CardStatus* c) { return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen && (fd->tapi == c->m_player ? c->m_delay == 0 || c->m_blitzing : c->m_delay <= 1)); } - template<> inline bool skill_predicate(Field* fd, CardStatus* c) { - if(can_act(fd, c)) + if(!c->m_attacked && can_act(fd, c)) { for(auto& s: c->m_card->m_skills) { // Any quantifiable skill except augment - if(std::get<1>(s) > 0 && std::get<0>(s) != augment && std::get<0>(s) != augment_all && std::get<0>(s) != summon) { return(true); } + if(std::get<1>(s) > 0 && std::get<0>(s) != augment && std::get<0>(s) != summon) { return(true); } } } return(false); @@ -1258,7 +1412,7 @@ inline bool skill_predicate(Field* fd, CardStatus* c) template<> inline bool skill_predicate(Field* fd, CardStatus* c) -{ return(!c->m_immobilized && can_act(fd, c)); } +{ return(!c->m_immobilized && !c->m_attacked && can_act(fd, c)); } template<> inline bool skill_predicate(Field* fd, CardStatus* c) @@ -1357,7 +1511,7 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - c->m_jammed = fd->flip(); + c->m_jammed = true; } template<> @@ -1387,8 +1541,7 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { -// _DEBUG_MSG("shock %u. hp %u -> %u.\n", v, c->m_hp, safe_minus(c->m_hp, v)); - c->m_hp = safe_minus(c->m_hp, v); + remove_commander_hp(fd, *c, v); } template<> @@ -1452,54 +1605,11 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector return(array_head); } -template -inline unsigned select_rally_like(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) -{ - unsigned array_head{0}; - unsigned card_index(fd->current_phase == Field::assaults_phase ? fd->current_ci : 0); - if(std::get<2>(s) == allfactions) - { - for(; card_index < cards.size(); ++card_index) - { - if(skill_predicate(fd, cards[card_index])) - { - fd->selection_array[array_head] = cards[card_index]; - ++array_head; - } - } - } - else - { - for(; card_index < cards.size(); ++card_index) - { - if(cards[card_index]->m_faction == std::get<2>(s) && - skill_predicate(fd, cards[card_index])) - { - fd->selection_array[array_head] = cards[card_index]; - ++array_head; - } - } - } - return(array_head); -} - -template<> -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) -{ - return(select_rally_like(fd, src_status, cards, s)); -} - -template<> -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) -{ - return(select_rally_like(fd, src_status, cards, s)); -} - template<> inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) { // mimiced supply by a structure, etc ? - if(!(src_status && src_status->m_card->m_type == CardType::assault)) { return(0); } + if(!(src_status->m_card->m_type == CardType::assault)) { return(0); } unsigned array_head{0}; const unsigned min_index(src_status->m_index - (src_status->m_index == 0 ? 0 : 1)); const unsigned max_index(src_status->m_index + (src_status->m_index == cards.size() - 1 ? 0 : 1)); @@ -1540,22 +1650,22 @@ inline unsigned select_infuse(Field* fd, const SkillSpec& s) inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src_status) { - return(fd->players[src_status ? (src_status->m_chaosed ? src_status->m_player : opponent(src_status->m_player)) : fd->tipi]->assaults.m_indirect); + return(fd->players[src_status->m_chaosed ? src_status->m_player : opponent(src_status->m_player)]->assaults.m_indirect); } inline std::vector& skill_targets_allied_assault(Field* fd, CardStatus* src_status) { - return(fd->players[src_status ? src_status->m_player : fd->tapi]->assaults.m_indirect); + return(fd->players[src_status->m_player]->assaults.m_indirect); } inline std::vector& skill_targets_hostile_structure(Field* fd, CardStatus* src_status) { - return(fd->players[src_status ? (src_status->m_chaosed ? src_status->m_player : opponent(src_status->m_player)) : fd->tipi]->structures.m_indirect); + return(fd->players[src_status->m_chaosed ? src_status->m_player : opponent(src_status->m_player)]->structures.m_indirect); } inline std::vector& skill_targets_allied_structure(Field* fd, CardStatus* src_status) { - return(fd->players[src_status ? src_status->m_player : fd->tapi]->structures.m_indirect); + return(fd->players[src_status->m_player]->structures.m_indirect); } template @@ -1587,7 +1697,12 @@ template<> std::vector& skill_targets(Field* fd, CardStatus* s { return(skill_targets_hostile_assault(fd, src_status)); } template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } +{ + if(fd->effect == Effect::copycat) + { return(skill_targets_allied_assault(fd, src_status)); } + else + { return(skill_targets_hostile_assault(fd, src_status)); } +} template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } @@ -1624,147 +1739,108 @@ void maybeTriggerRegen(Field* fd) template<> void maybeTriggerRegen(Field* fd) { - fd->skill_queue.emplace_front(nullptr, std::make_tuple(trigger_regen, 0, allfactions)); + fd->skill_queue.emplace_front(nullptr, std::make_tuple(trigger_regen, 0, allfactions, false)); } -template -CardStatus* get_target_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +unsigned get_target_hostile_index(Field* fd, CardStatus* src_status, unsigned selection_array_size) { - std::vector& cards(skill_targets(fd, src_status)); - unsigned array_head{select_fast(fd, src_status, cards, s)}; - if(array_head > 0) + unsigned rand_index(fd->rand(0, selection_array_size - 1)); + // intercept + if(!src_status->m_chaosed) { - unsigned rand_index(fd->rand(0, array_head - 1)); - CardStatus* c(fd->selection_array[rand_index]); - // intercept - if(src_status && !src_status->m_chaosed) + CardStatus* status(fd->selection_array[rand_index]); + if(rand_index > 0) { - CardStatus* intercept_card(nullptr); - if(rand_index > 0) + CardStatus* left_status(fd->selection_array[rand_index - 1]); + if(left_status->m_card->m_intercept && left_status->m_index == status->m_index - 1 && skill_activate(fd, left_status, status)) { - CardStatus* left_status(fd->selection_array[rand_index-1]); - if(left_status->m_card->m_intercept && left_status->m_index == c->m_index-1) - { - intercept_card = left_status; - } + _DEBUG_MSG("%s intercepts for %s\n", status_description(left_status).c_str(), status_description(status).c_str()); + return(rand_index - 1); } - if(rand_index+1 < array_head && !intercept_card) + } + if(rand_index + 1 < selection_array_size) + { + CardStatus* right_status(fd->selection_array[rand_index + 1]); + if(right_status->m_card->m_intercept && right_status->m_index == status->m_index + 1 && skill_activate(fd, right_status, status)) { - CardStatus* right_status(fd->selection_array[rand_index+1]); - if(right_status->m_card->m_intercept && right_status->m_index == c->m_index+1) - { - intercept_card = right_status; - } + _DEBUG_MSG("%s intercepts for %s\n", status_description(right_status).c_str(), status_description(status).c_str()); + return(rand_index + 1); } - if(intercept_card) { c = intercept_card; } } - return(c); } - return(nullptr); + return(rand_index); } -bool negate_by_evade(Field* fd, CardStatus* c) +template +bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_status, const SkillSpec& s, bool evadable) { - if(c->m_card->m_evade && fd->flip()) + if(skill_activate(fd, src_status, dst_status)) { - _DEBUG_MSG("%s evades\n", status_description(c).c_str()); - return(true); + _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); + if(evadable && dst_status->m_card->m_evade && skill_activate(fd, dst_status, src_status)) + { + _DEBUG_MSG("%s evades\n", status_description(dst_status).c_str()); + } + else + { + perform_skill(fd, dst_status, std::get<1>(s)); + return(true); + } } - else - { return(false); } + return(false); } -template -void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +bool check_and_perform_recharge(Field* fd, CardStatus* src_status) { - // null status = action card - CardStatus* c(get_target_hostile_fast(fd, src_status, s)); - if(c) + if(skill_activate(fd, src_status, nullptr)) { - _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); - if((src_status && src_status->m_chaosed) || !negate_by_evade(fd, c)) - { - perform_skill(fd, c, std::get<1>(s)); - // payback - if(c->m_card->m_payback && - src_status && - src_status->m_card->m_type == CardType::assault && - !src_status->m_chaosed && - src_status->m_hp > 0 && - fd->flip()) - { - if(skill_predicate(fd, src_status)) - { - _DEBUG_MSG("Payback (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); - // payback skill - if(!negate_by_evade(fd, c)) { perform_skill(fd, src_status, std::get<1>(s)); } - } - } - } + // perform_skill_recharge + _DEBUG_MSG("%s recharges\n", status_description(src_status).c_str()); + fd->tap->deck->place_at_bottom(src_status->m_card); + return(true); } - maybeTriggerRegen::T>(fd); - prepend_on_death(fd); + return(false); } -template -void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +bool check_and_perform_refresh(Field* fd, CardStatus* src_status) { - std::vector& cards(skill_targets(fd, src_status)); - unsigned array_head{select_fast(fd, src_status, cards, s)}; - if(array_head > 0) + if(skill_activate(fd, src_status, nullptr)) { - CardStatus* c(fd->selection_array[fd->rand(0, array_head - 1)]); - _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); - perform_skill(fd, c, std::get<1>(s)); - if(c->m_card->m_tribute && - src_status && - src_status->m_card->m_type == CardType::assault && - src_status != c && - src_status->m_hp > 0 && - fd->flip()) - { - if(skill_predicate(fd, src_status)) - { - _DEBUG_MSG("Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); - perform_skill(fd, src_status, std::get<1>(s)); - } - } - - // check emulate - Hand* opp = fd->players[opponent(c->m_player)]; - if(opp->assaults.size() > c->m_index) - { - CardStatus& emulator = opp->assaults[c->m_index]; - if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator)) - { - _DEBUG_MSG("Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); - perform_skill(fd, &emulator, std::get<1>(s)); - } - } + // perform_skill_refresh + _DEBUG_MSG("%s refreshes, hp -> %u\n", status_description(src_status).c_str(), src_status->m_card->m_health); + add_hp(fd, src_status, src_status->m_card->m_health); + return(true); } + return(false); } -template -void perform_global_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +template +void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { std::vector& cards(skill_targets(fd, src_status)); - unsigned array_head{select_fast(fd, src_status, cards, s)}; + unsigned selection_array_size{select_fast(fd, src_status, cards, s)}, index_start, index_end; + if(selection_array_size == 0) + { + return; + } + if(std::get<3>(s)) // target all + { + index_start = 0; + index_end = selection_array_size - 1; + } + else + { + index_start = index_end = get_target_hostile_index(fd, src_status, selection_array_size); + } unsigned payback_count(0); - for(unsigned s_index(0); s_index < array_head; ++s_index) + for(unsigned s_index(index_start); s_index <= index_end; ++s_index) { CardStatus* c(fd->selection_array[s_index]); - _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); - if((src_status && src_status->m_chaosed) || !negate_by_evade(fd, c)) + if(check_and_perform_skill(fd, src_status, c, s, true)) { - perform_skill(fd, c, std::get<1>(s)); - // payback - if(c->m_card->m_payback && - src_status && - src_status->m_card->m_type == CardType::assault && - !src_status->m_chaosed && - src_status->m_hp > 0 && - fd->flip()) + if(c->m_card->m_payback && skill_activate(fd, c, src_status)) { + _DEBUG_MSG("%s paybacks.\n", status_description(c).c_str()); ++payback_count; } } @@ -1772,45 +1848,48 @@ void perform_global_hostile_fast(Field* fd, CardStatus* src_status, const SkillS for(unsigned i(0); i < payback_count && skill_predicate(fd, src_status); ++i) { _DEBUG_MSG("Payback (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); - if(!negate_by_evade(fd, src_status)) { perform_skill(fd, src_status, std::get<1>(s)); } + perform_skill(fd, src_status, std::get<1>(s)); } maybeTriggerRegen::T>(fd); prepend_on_death(fd); } -template -void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +template +void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { std::vector& cards(skill_targets(fd, src_status)); - unsigned array_head{select_fast(fd, src_status, cards, s)}; - for(unsigned s_index(0); s_index < array_head; ++s_index) + unsigned selection_array_size{select_fast(fd, src_status, cards, s)}, index_start, index_end; + if(selection_array_size == 0) { return; } + if(std::get<3>(s) || skill_id == supply) // target all or supply + { + index_start = 0; + index_end = selection_array_size - 1; + } + else + { + index_start = index_end = fd->rand(0, selection_array_size - 1); + } + for(unsigned s_index(index_start); s_index <= index_end; ++s_index) { CardStatus* c(fd->selection_array[s_index]); - _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(c).c_str()); - perform_skill(fd, c, std::get<1>(s)); - if(c->m_card->m_tribute && - src_status && - src_status->m_card->m_type == CardType::assault && - src_status != c && - src_status->m_hp > 0 && - fd->flip()) + if(check_and_perform_skill(fd, src_status, c, s, false)) { - if(skill_predicate(fd, src_status)) + if(c->m_card->m_tribute && skill_predicate(fd, src_status) && skill_activate(fd, c, src_status)) { _DEBUG_MSG("Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); perform_skill(fd, src_status, std::get<1>(s)); } - } - // check emulate - Hand* opp = fd->players[opponent(c->m_player)]; - if(opp->assaults.size() > c->m_index) - { - CardStatus& emulator = opp->assaults[c->m_index]; - if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator)) + // check emulate + Hand* opp = fd->players[opponent(c->m_player)]; + if(opp->assaults.size() > c->m_index) { - _DEBUG_MSG("Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); - perform_skill(fd, &emulator, std::get<1>(s)); + CardStatus& emulator = opp->assaults[c->m_index]; + if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator) && skill_activate(fd, &emulator, nullptr)) + { + _DEBUG_MSG("Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); + perform_skill(fd, &emulator, std::get<1>(s)); + } } } } @@ -1818,9 +1897,7 @@ void perform_global_allied_fast(Field* fd, CardStatus* src_status, const SkillSp void perform_backfire(Field* fd, CardStatus* src_status, const SkillSpec& s) { - CardStatus* c(&fd->players[src_status->m_player]->commander); - _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), std::get<1>(s), status_description(c).c_str()); - perform_skill(fd, c, std::get<1>(s)); + check_and_perform_skill(fd, src_status, &fd->players[src_status->m_player]->commander, s, false); } void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) @@ -1847,15 +1924,15 @@ void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) if(array_head > 0) { CardStatus* c(fd->selection_array[fd->rand(0, array_head - 1)]); - // check evade for enemy assaults only - if(c->m_player == src_status->m_player) - { - _DEBUG_MSG("%s %s on %s\n", status_description(src_status).c_str(), skill_names[infuse].c_str(), status_description(c).c_str()); - if(!negate_by_evade(fd, c)) { perform_skill(fd, c, std::get<1>(s)); } - } + check_and_perform_skill(fd, src_status, c, s, true); } } +inline void perform_recharge(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + check_and_perform_recharge(fd, src_status); +} + // a summoned card's on play skills seem to be evaluated before any other skills on the skill queue. inline void prepend_skills(Field* fd, CardStatus* status) { @@ -1864,8 +1941,11 @@ inline void prepend_skills(Field* fd, CardStatus* status) fd->skill_queue.emplace_front(status, skill); } } -void summon_card(Field* fd, unsigned player, const Card* summoned) + +void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) { + unsigned player = src_status->m_player; + const Card* summoned = fd->cards.by_id(std::get<1>(s)); assert(summoned->m_type == CardType::assault || summoned->m_type == CardType::structure); Hand* hand{fd->players[player]}; if(hand->assaults.size() + hand->structures.size() < 100) @@ -1875,31 +1955,27 @@ void summon_card(Field* fd, unsigned player, const Card* summoned) card_status.set(summoned); card_status.m_index = storage->size() - 1; card_status.m_player = player; + card_status.m_summoned = true; _DEBUG_MSG("Summon %s %u [%s]\n", cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd, summoned).c_str()); prepend_skills(fd, &card_status); - if(card_status.m_card->m_blitz && - fd->players[opponent(player)]->assaults.size() > card_status.m_index && - fd->players[opponent(player)]->assaults[card_status.m_index].m_hp > 0 && - fd->players[opponent(player)]->assaults[card_status.m_index].m_delay == 0) + if(card_status.m_card->m_blitz && skill_activate(fd, src_status, nullptr)) { card_status.m_blitzing = true; } - - if(fd->effect == Effect::toxic) - { - card_status.m_poisoned = 1; - } - else if(fd->effect == Effect::decay) + if(summoned->m_type == CardType::assault) { - card_status.m_poisoned = 1; - card_status.m_diseased = true; + if(fd->effect == Effect::toxic) + { + card_status.m_poisoned = 1; + } + else if(fd->effect == Effect::decay) + { + card_status.m_poisoned = 1; + card_status.m_diseased = true; + } } } } -void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - summon_card(fd, src_status ? src_status->m_player : fd->tapi, fd->cards.by_id(std::get<1>(s))); -} void perform_trigger_regen(Field* fd, CardStatus* src_status, const SkillSpec& s) { @@ -1908,8 +1984,7 @@ void perform_trigger_regen(Field* fd, CardStatus* src_status, const SkillSpec& s void perform_shock(Field* fd, CardStatus* src_status, const SkillSpec& s) { - _DEBUG_MSG("%s shocks %s for %u damage\n", status_description(src_status).c_str(), status_description(&fd->tip->commander).c_str(), std::get<1>(s)); - perform_skill(fd, &fd->tip->commander, std::get<1>(s)); + check_and_perform_skill(fd, src_status, &fd->tip->commander, s, false); } // Special rules for mimic : @@ -1921,45 +1996,35 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // mimic cannot be triggered by anything. So it should be the only skill in the unresolved skill table. // so we can probably clear it safely. This is necessary, because mimic calls resolve_skill as well (infinite loop). fd->skill_queue.clear(); - CardStatus* c(nullptr); - if(fd->effect == Effect::copycat) + std::vector& cards(skill_targets(fd, src_status)); + unsigned selection_array_size{select_fast(fd, src_status, cards, s)}; + if(selection_array_size == 0) { - std::vector& cards(skill_targets_allied_assault(fd, src_status)); - unsigned array_head{select_fast(fd, src_status, cards, s)}; - if(array_head > 0) - { - c = fd->selection_array[fd->rand(0, array_head - 1)]; - _DEBUG_MSG("%s on %s\n", skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); - } + return; } - else + CardStatus* c(fd->selection_array[get_target_hostile_index(fd, src_status, selection_array_size)]); + _DEBUG_MSG("%s on %s\n", skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); + // evade check for mimic + // individual skills are subject to evade checks too, + // but resolve_skill will handle those. + if(c->m_card->m_evade && skill_activate(fd, c, src_status)) { - c = get_target_hostile_fast(fd, src_status, s); - // evade check for mimic - // individual skills are subject to evade checks too, - // but resolve_skill will handle those. - if(c && (!src_status || !src_status->m_chaosed)) - { - _DEBUG_MSG("%s on %s\n", skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); - if(negate_by_evade(fd, c)) { return; } - } + _DEBUG_MSG("%s evades\n", status_description(c).c_str()); + return; } - - if(c) + for(auto skill: c->m_card->m_skills) { - for(auto skill: c->m_card->m_skills) + if(src_status && src_status->m_card->m_type == CardType::assault && src_status->m_hp == 0) + { break; } + if(std::get<0>(skill) != mimic && + (std::get<0>(skill) != supply || (src_status && src_status->m_card->m_type == CardType::assault))) { - if(src_status && src_status->m_card->m_type == CardType::assault && src_status->m_hp == 0) - { break; } - if(std::get<0>(skill) != mimic && - (std::get<0>(skill) != supply || (src_status && src_status->m_card->m_type == CardType::assault))) - { - SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions); - _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd, skill).c_str()); - fd->skill_queue.emplace_back(src_status, src_status && src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); - resolve_skill(fd); - check_regeneration(fd); - } + SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill)); + _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd, skill).c_str()); + fd->skill_queue.emplace_back(src_status, src_status && src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); + resolve_skill(fd); + if(fd->end) { break; } + check_regeneration(fd); } } } @@ -1967,47 +2032,35 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) void fill_skill_table() { skill_table[augment] = perform_targetted_allied_fast; - skill_table[augment_all] = perform_global_allied_fast; skill_table[backfire] = perform_backfire; skill_table[chaos] = perform_targetted_hostile_fast; - skill_table[chaos_all] = perform_global_hostile_fast; skill_table[cleanse] = perform_targetted_allied_fast; - skill_table[cleanse_all] = perform_global_allied_fast; skill_table[enfeeble] = perform_targetted_hostile_fast; - skill_table[enfeeble_all] = perform_global_hostile_fast; skill_table[freeze] = perform_targetted_hostile_fast; - skill_table[freeze_all] = perform_global_hostile_fast; skill_table[heal] = perform_targetted_allied_fast; - skill_table[heal_all] = perform_global_allied_fast; skill_table[infuse] = perform_infuse; skill_table[jam] = perform_targetted_hostile_fast; - skill_table[jam_all] = perform_global_hostile_fast; skill_table[mimic] = perform_mimic; skill_table[protect] = perform_targetted_allied_fast; - skill_table[protect_all] = perform_global_allied_fast; skill_table[rally] = perform_targetted_allied_fast; - skill_table[rally_all] = perform_global_allied_fast; + skill_table[recharge] = perform_recharge; skill_table[repair] = perform_targetted_allied_fast; - skill_table[repair_all] = perform_global_allied_fast; skill_table[rush] = perform_targetted_allied_fast; skill_table[shock] = perform_shock; skill_table[siege] = perform_targetted_hostile_fast; - skill_table[siege_all] = perform_global_hostile_fast; - skill_table[supply] = perform_global_allied_fast; + skill_table[supply] = perform_targetted_allied_fast; skill_table[strike] = perform_targetted_hostile_fast; - skill_table[strike_all] = perform_global_hostile_fast; skill_table[summon] = perform_summon; skill_table[temporary_split] = perform_targetted_allied_fast; skill_table[trigger_regen] = perform_trigger_regen; skill_table[weaken] = perform_targetted_hostile_fast; - skill_table[weaken_all] = perform_global_hostile_fast; } //------------------------------------------------------------------------------ // Utility functions for modify_cards. -// Adds the skill " " to all assaults, -// except those who have any instance of either or . -inline void maybe_add_to_assaults(Cards& cards, ActiveSkill new_skill, unsigned magnitude, ActiveSkill conflict) +// Adds the skill " " to all assaults, +// except those who have any instance of . +inline void assaults_gain_skill_if_have_not(Cards& cards, Skill new_skill, unsigned magnitude, bool all) { for(Card* card: cards.cards) { @@ -2019,8 +2072,7 @@ inline void maybe_add_to_assaults(Cards& cards, ActiveSkill new_skill, unsigned bool conflict(false); for(auto& skill: card->m_skills) { - if(std::get<0>(skill) == new_skill || - std::get<0>(skill) == conflict) + if(std::get<0>(skill) == new_skill) { conflict = true; } @@ -2028,28 +2080,15 @@ inline void maybe_add_to_assaults(Cards& cards, ActiveSkill new_skill, unsigned if(!conflict) { - card->add_skill(new_skill, magnitude, allfactions); + card->add_skill(new_skill, magnitude, allfactions, all); } } } -// Adds the skill " " to all commanders. -inline void add_to_commanders(Cards& cards, ActiveSkill skill, unsigned magnitude) -{ - for(Card* card: cards.cards) - { - if(card->m_type != CardType::commander) - { - continue; - } - card->add_skill(skill, magnitude, allfactions); - } -} - -// Adds the skill " " to all commanders. -// If the commander has an instance of either or in its skill list, +// Adds the skill " " to all commanders. +// If the commander has an instance of in its skill list, // the new skill replaces it. // Otherwise, the new skill is added on to the end. -inline void replace_on_commanders(Cards& cards, ActiveSkill old_skill, ActiveSkill new_skill, unsigned magnitude) +inline void commanders_gain_skill(Cards& cards, Skill new_skill, unsigned magnitude, bool all) { for(Card* card: cards.cards) { @@ -2061,17 +2100,16 @@ inline void replace_on_commanders(Cards& cards, ActiveSkill old_skill, ActiveSki bool replaced(false); for(auto& skill: card->m_skills) { - if(std::get<0>(skill) == old_skill || - std::get<0>(skill) == new_skill) + if(std::get<0>(skill) == new_skill) { - skill = std::make_tuple(new_skill, magnitude, allfactions); + skill = std::make_tuple(new_skill, magnitude, allfactions, all); replaced = true; } } if(!replaced) { - card->add_skill(new_skill, magnitude, allfactions); + card->add_skill(new_skill, magnitude, allfactions, all); } } } @@ -2083,7 +2121,7 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::none: break; case Effect::time_surge: - add_to_commanders(cards, rush, 1); + commanders_gain_skill(cards, rush, 1, false); break; case Effect::copycat: // Do nothing; this is implemented in perform_mimic @@ -2122,20 +2160,20 @@ void modify_cards(Cards& cards, enum Effect effect) // Do nothing; these are implemented in the temporary_split skill break; case Effect::friendly_fire: - replace_on_commanders(cards, chaos, chaos_all, 0); - maybe_add_to_assaults(cards, strike, 1, strike_all); + commanders_gain_skill(cards, chaos, 0, true); + assaults_gain_skill_if_have_not(cards, strike, 1, false); break; case Effect::genesis: // Do nothing; this is implemented in play break; case Effect::decrepit: - replace_on_commanders(cards, enfeeble, enfeeble_all, 1); + commanders_gain_skill(cards, enfeeble, 1, true); break; case Effect::forcefield: - replace_on_commanders(cards, protect, protect_all, 1); + commanders_gain_skill(cards, protect, 1, true); break; case Effect::chilling_touch: - add_to_commanders(cards, freeze, 0); + commanders_gain_skill(cards, freeze, 0, false); break; case Effect::toxic: // Do nothing; this is implemented in PlayCard::fieldEffects diff --git a/sim.h b/sim.h index 4676c953..6ce91d2f 100644 --- a/sim.h +++ b/sim.h @@ -13,6 +13,7 @@ class Card; class Cards; class DeckIface; class Field; +class Achievement; extern bool debug_print; extern bool debug_line; @@ -110,6 +111,8 @@ struct CardStatus unsigned m_rallied; unsigned m_weakened; bool m_temporary_split; + bool m_summoned; // is this card summoned? + bool m_attacked; // has this card attacked in the turn? CardStatus() {} CardStatus(const Card* card); @@ -158,6 +161,7 @@ class Field unsigned turn; gamemode_t gamemode; const enum Effect effect; + const Achievement& achievement; // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque > skill_queue; @@ -180,15 +184,17 @@ class Field unsigned points_since_last_decision; unsigned fusion_count; + std::vector achievement_counter; - Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t _gamemode, enum Effect _effect) : + Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, enum Effect effect_, const Achievement& achievement_) : end{false}, re(re_), cards(cards_), players{{&hand1, &hand2}}, turn(1), - gamemode(_gamemode), - effect(_effect) + gamemode(gamemode_), + effect(effect_), + achievement(achievement_) { } @@ -208,6 +214,22 @@ class Field assert(v.size() > 0); return(v[this->rand(0, v.size() - 1)]); } + + template + inline void inc_counter(T& container, unsigned key) + { + auto x = container.find(key); + if(x != container.end()) + { + ++ achievement_counter[x->second]; +#if 0 + if(achievement.req_counter[x->second].predict_monoinc(achievement_counter[x->second]) < 0) + { + end = true; + } +#endif + } + } }; #endif diff --git a/tyrant.cpp b/tyrant.cpp index 3de81418..cb50188d 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -2,24 +2,40 @@ #include -const std::string faction_names[num_factions] = +const std::string faction_names[Faction::num_factions] = { "", "bloodthirsty", "imperial", "raider", "righteous", "xeno" }; -std::string skill_names[num_skills] = -{"augment", "augment_all", "backfire", "chaos", "chaos_all", "cleanse", "cleanse_all", "enfeeble", "enfeeble_all", - "freeze", "freeze_all", "heal", "heal_all", "infuse", "jam", "jam_all", - "mimic", "protect", "protect_all", "rally", "rally_all", "repair", "repair_all", "rush", "shock", - "siege", "siege_all", "strike", "strike_all", "summon", "supply", - "temporary_split", - "trigger_regen", - "weaken", "weaken_all"}; +std::string skill_names[Skill::num_skills] = +{ + // Activation (including Destroyed): + "augment", "backfire", "chaos", "cleanse", "enfeeble", + "freeze", "heal", "infuse", "jam", + "mimic", "protect", "rally", "recharge", "repair", "rush", "shock", + "siege", "strike", "summon", "supply", + "temporary_split", + "trigger_regen", + "weaken", + // Combat-Modifier: + "antiair", "burst", "fear", "flurry", "pierce", "swipe", "valor", + // Damage-Dependant: + "berserk", "crush", "disease", "immobilize", "leech", "poison", "siphon", + // Defensive: + "armored", "counter", "emulate", "evade", "flying", "intercept", "payback", "refresh", "regenerate", "tribute", "wall", + // Triggered: + "blitz", + // Static (ignored): + /* "blizzard", "fusion", "mist", */ + // Misc: + "0", +}; -std::set helpful_skills{ - augment, augment_all, cleanse, cleanse_all, heal, heal_all, protect, protect_all, rally, rally_all, - /*repair, repair_all, rush, */supply, +std::set helpful_skills{ + augment, cleanse, heal, protect, rally, repair, rush, supply, }; -std::string cardtype_names[CardType::num_cardtypes]{"Action", "Assault", "Commander", "Structure"}; +std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Structure", "Action", }; + +std::string rarity_names[5]{"", "common", "uncommon", "rare", "legendary", }; std::string effect_names[Effect::num_effects] = { "None", diff --git a/tyrant.h b/tyrant.h index 96b9b4a9..be80fa8d 100644 --- a/tyrant.h +++ b/tyrant.h @@ -17,29 +17,44 @@ enum Faction }; extern const std::string faction_names[num_factions]; -enum ActiveSkill -{augment, augment_all, backfire, chaos, chaos_all, cleanse, cleanse_all, enfeeble, enfeeble_all, - freeze, freeze_all, heal, heal_all, infuse, jam, jam_all, - mimic, protect, protect_all, rally, rally_all, repair, repair_all, rush, shock, - siege, siege_all, strike, strike_all, summon, supply, - temporary_split, // not actually a skill; handles Clone Project/Experiment - trigger_regen, // not actually a skill; handles regeneration after strike/siege - weaken, weaken_all, num_skills}; +enum Skill +{ + // Activation (including Destroyed): + augment, backfire, chaos, cleanse, enfeeble, freeze, heal, infuse, jam, + mimic, protect, rally, recharge, repair, rush, shock, siege, strike, summon, supply, + temporary_split, // not actually a skill; handles Clone Project/Experiment + trigger_regen, // not actually a skill; handles regeneration after strike/siege + weaken, + // Combat-Modifier: + antiair, burst, fear, flurry, pierce, swipe, valor, + // Damage-Dependant: + berserk, crush, disease, immobilize, leech, poison, siphon, + // Defensive: + armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, tribute, wall, + // Triggered: + blitz, + // Static, ignored: + /* blizzard, fusion, mist, */ + // Misc: + attack, + num_skills}; extern std::string skill_names[num_skills]; -extern std::set helpful_skills; +extern std::set helpful_skills; namespace CardType { enum CardType { - action, - assault, commander, + assault, structure, + action, num_cardtypes }; } extern std::string cardtype_names[CardType::num_cardtypes]; +extern std::string rarity_names[5]; + enum Effect { none, time_surge, @@ -82,15 +97,9 @@ struct skillTriggersRegen { typedef false_ T; }; template<> struct skillTriggersRegen { typedef true_ T; }; -template<> -struct skillTriggersRegen { typedef true_ T; }; - template<> struct skillTriggersRegen { typedef true_ T; }; -template<> -struct skillTriggersRegen { typedef true_ T; }; - enum SkillSourceType { source_hostile, @@ -100,6 +109,6 @@ enum SkillSourceType source_chaos }; -typedef std::tuple SkillSpec; +typedef std::tuple SkillSpec; #endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 473dbfad..80ca3fcd 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -31,6 +31,7 @@ #include "card.h" #include "cards.h" #include "deck.h" +#include "achievement.h" #include "read.h" #include "sim.h" #include "tyrant.h" @@ -88,11 +89,6 @@ std::string card_id_name(const Card* card) //------------------------------------------------------------------------------ DeckIface* find_deck(const Decks& decks, std::string name) { - auto it1 = decks.mission_decks_by_name.find(name); - if(it1 != decks.mission_decks_by_name.end()) - { - return(it1->second); - } auto it2 = decks.raid_decks_by_name.find(name); if(it2 != decks.raid_decks_by_name.end()) { @@ -306,8 +302,9 @@ struct SimulationData std::vector factors; gamemode_t gamemode; enum Effect effect; + const Achievement& achievement; - SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_def_decks_, std::vector factors_, gamemode_t gamemode_, enum Effect effect_) : + SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_def_decks_, std::vector factors_, gamemode_t gamemode_, enum Effect effect_, const Achievement& achievement_) : re(seed), cards(cards_), decks(decks_), @@ -316,7 +313,8 @@ struct SimulationData def_decks(num_def_decks_), factors(factors_), gamemode(gamemode_), - effect(effect_) + effect(effect_), + achievement(achievement_) { for(auto def_deck: def_decks) { @@ -347,7 +345,7 @@ struct SimulationData { att_hand.reset(re); def_hand->reset(re); - Field fd(re, cards, att_hand, *def_hand, gamemode, effect); + Field fd(re, cards, att_hand, *def_hand, gamemode, effect, achievement); unsigned result(play(&fd)); res.emplace_back(result); } @@ -377,8 +375,9 @@ class Process std::vector factors; gamemode_t gamemode; enum Effect effect; + Achievement achievement; - Process(unsigned _num_threads, const Cards& cards_, const Decks& decks_, DeckIface* att_deck_, std::vector _def_decks, std::vector _factors, gamemode_t _gamemode, enum Effect _effect) : + Process(unsigned _num_threads, const Cards& cards_, const Decks& decks_, DeckIface* att_deck_, std::vector _def_decks, std::vector _factors, gamemode_t _gamemode, enum Effect _effect, const Achievement& achievement_) : num_threads(_num_threads), main_barrier(num_threads+1), cards(cards_), @@ -387,13 +386,14 @@ class Process def_decks(_def_decks), factors(_factors), gamemode(_gamemode), - effect(_effect) + effect(_effect), + achievement(achievement_) { destroy_threads = false; unsigned seed(time(0)); for(unsigned i(0); i < num_threads; ++i) { - threads_data.push_back(new SimulationData(seed + i, cards, decks, def_decks.size(), factors, gamemode, effect)); + threads_data.push_back(new SimulationData(seed + i, cards, decks, def_decks.size(), factors, gamemode, effect, achievement)); threads.push_back(new boost::thread(thread_evaluate, std::ref(main_barrier), std::ref(shared_mutex), std::ref(*threads_data.back()), std::ref(*this), i)); } } @@ -1005,35 +1005,41 @@ void print_available_decks(const Decks& decks) void usage(int argc, char** argv) { - std::cout << "usage: " << argv[0] << " [optional flags] [brute ] [climb ]\n"; - std::cout << "\n"; - std::cout << ": the deck name of a custom deck.\n"; - std::cout << ": semicolon separated list of defense decks, syntax:\n"; - std::cout << " deckname1[:factor1];deckname2[:factor2];...\n"; - std::cout << " where deckname is the name of a mission, raid, or custom deck, and factor is optional. The default factor is 1.\n"; - std::cout << " example: \'fear:0.2;slowroll:0.8\' means fear is the defense deck 20% of the time, while slowroll is the defense deck 80% of the time.\n"; - std::cout << "\n"; - std::cout << "Flags:\n"; - std::cout << " -a: optimize for ANP instead of win rate.\n"; - std::cout << " -c: don't try to optimize the commander.\n"; - std::cout << " -e : set the battleground effect.\n"; - std::cout << " -fixedlen: prevent hill climbing from changing the number of cards.\n"; - std::cout << " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n"; - std::cout << " -o=: restrict hill climbing to the owned cards listed in .\n"; - std::cout << " -q: quest mode. Removes faction restrictions from defending commanders and automatically sets quest effect.\n"; - std::cout << " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n"; - std::cout << " -s: use surge (default is fight).\n"; - std::cout << " -t : set the number of threads, default is 4.\n"; - std::cout << " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n"; - std::cout << " -wintie: attacker wins if turns run out (default is defeated; can be used for def deck simulation).\n"; - std::cout << "Operations:\n"; - std::cout << "brute : find the best combination of different cards, using up to battles to evaluate a deck.\n"; - std::cout << "climb : perform hill-climbing starting from the given attack deck, using up to battles to evaluate a deck.\n"; - std::cout << "sim : simulate battles to evaluate a deck.\n"; + std::cout << "usage: " << argv[0] << " Attacker Defender [Flags] [Operations]\n" + "\n" + "Attacker:\n" + " the deck name/hash of a custom deck.\n" + "\n" + "Defender:\n" + " semicolon separated list of defense decks, syntax:\n" + " deck1[:factor1];deck2[:factor2];...\n" + " where deck is the name/hash of a mission, raid, or custom deck, and factor is optional. The default factor is 1.\n" + " example: \'fear:0.2;slowroll:0.8\' means fear is the defense deck 20% of the time, while slowroll is the defense deck 80% of the time.\n" + "\n" + "Flags:\n" + " -a: optimize for ANP instead of win rate.\n" + " -A : optimize for the achievement specified by either id or name.\n" + " -c: don't try to optimize the commander.\n" + " -e : set the battleground effect.\n" + " -fixedlen: prevent hill climbing from changing the number of cards.\n" + " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n" + " -o=: restrict hill climbing to the owned cards listed in .\n" + " -q: quest mode. automatically sets quest effect.\n" + " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" + " -s: use surge (default is fight).\n" + " -t : set the number of threads, default is 4.\n" + " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n" + " -wintie: attacker wins if turns run out (default is defeated; can be used for def deck simulation).\n" + "\n" + "Operations:\n" + " brute : find the best combination of different cards, using up to battles to evaluate a deck.\n" + " climb : perform hill-climbing starting from the given attack deck, using up to battles to evaluate a deck.\n" + " sim : simulate battles to evaluate a deck.\n" #ifndef NDEBUG - std::cout << "debug: testing purpose only. very verbose output. only one battle.\n"; - std::cout << "debuguntil : testing purpose only. fight until the last fight results in range [, ]. recommend to redirect output.\n"; + " debug: testing purpose only. very verbose output. only one battle.\n" + " debuguntil : testing purpose only. fight until the last fight results in range [, ]. recommend to redirect output.\n" #endif + ; } int main(int argc, char** argv) @@ -1046,6 +1052,7 @@ int main(int argc, char** argv) Cards cards; read_cards(cards); Decks decks; + Achievement achievement; load_decks_xml(decks, cards); load_decks(decks, cards); fill_skill_table(); @@ -1056,31 +1063,7 @@ int main(int argc, char** argv) return(4); } std::string att_deck_name{argv[1]}; - std::vector def_decks; - std::vector def_decks_factors; auto deck_list_parsed = parse_deck_list(argv[2]); - for(auto deck_parsed: deck_list_parsed) - { - DeckIface* def_deck = find_deck(decks, deck_parsed.first); - if(def_deck == nullptr) - { - try - { def_deck = hash_to_deck(deck_parsed.first.c_str(), cards); } - catch(const std::runtime_error& e) - { - std::cerr << "Error: Deck hash " << deck_parsed.first << ": " << e.what() << std::endl; - return(5); - } - if(def_deck == nullptr) - { - std::cerr << "Error: Invalid defense deck name/hash " << deck_parsed.first << ". Available decks:" << std::endl; - print_available_decks(decks); - return(5); - } - } - def_decks.push_back(def_deck); - def_decks_factors.push_back(deck_parsed.second); - } enum Effect effect = Effect::none; std::map effect_map; @@ -1096,6 +1079,19 @@ int main(int argc, char** argv) { use_anp = true; } + else if(strcmp(argv[argIndex], "-A") == 0) + { + try + { + read_achievement(decks, cards, achievement, argv[argIndex + 1]); + } + catch(const std::runtime_error& e) + { + std::cerr << "Error: Achievement " << argv[argIndex + 1] << ": " << e.what() << std::endl; + return(1); + } + argIndex += 1; + } else if(strcmp(argv[argIndex], "-c") == 0) { keep_commander = true; @@ -1128,18 +1124,6 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "-q") == 0) { - // Strip faction restrictions from commanders of all defending decks - // The assumption is that the attacking deck won't use these commanders. - // If the attacking deck does, its commander will receive this modification as well. - for(auto def_deck: def_decks) - { - // Can't directly use def_deck->commander, as it is const. - unsigned commander_id(def_deck->commander->m_id); - for(auto& skill: cards.cards_by_id[commander_id]->m_skills) - { - skill = std::make_tuple(std::get<0>(skill), std::get<1>(skill), allfactions); - } - } // Set quest effect: for(auto deck_parsed: deck_list_parsed) { @@ -1182,17 +1166,17 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "brute") == 0) { - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex+1]), (unsigned)atoi(argv[argIndex+2]), bruteforce)); + todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), bruteforce)); argIndex += 2; } else if(strcmp(argv[argIndex], "climb") == 0) { - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex+1]), 0u, climb)); + todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, climb)); argIndex += 1; } else if(strcmp(argv[argIndex], "sim") == 0) { - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex+1]), 0u, simulate)); + todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); argIndex += 1; } else if(strcmp(argv[argIndex], "debug") == 0) @@ -1207,7 +1191,7 @@ int main(int argc, char** argv) num_threads = 1; // fight until the last battle results in range [min_score, max_score]. // E.g., 0 0: until lose; 1 1: until win (non-ANP); 10 25: until win (ANP). - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex+1]), (unsigned)atoi(argv[argIndex+2]), fightuntil)); + todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), fightuntil)); argIndex += 2; } else @@ -1241,7 +1225,45 @@ int main(int argc, char** argv) return(5); } } - print_deck(*att_deck); + + std::vector def_decks; + std::vector def_decks_factors; + for(auto deck_parsed: deck_list_parsed) + { + DeckIface* def_deck{nullptr}; + auto it1 = decks.mission_decks_by_name.find(deck_parsed.first); + if(it1 != decks.mission_decks_by_name.end()) + { + if(!achievement.mission_condition.check(decks.mission_id_by_name[deck_parsed.first])) + { + std::cerr << "Error: Wrong mission [" << deck_parsed.first << "] for achievement." << std::endl; + return(1); + } + def_deck = it1->second; + } + if(def_deck == nullptr) + { + def_deck = find_deck(decks, deck_parsed.first); + } + if(def_deck == nullptr) + { + try + { def_deck = hash_to_deck(deck_parsed.first.c_str(), cards); } + catch(const std::runtime_error& e) + { + std::cerr << "Error: Deck hash " << deck_parsed.first << ": " << e.what() << std::endl; + return(5); + } + if(def_deck == nullptr) + { + std::cerr << "Error: Invalid defense deck name/hash " << deck_parsed.first << ". Available decks:" << std::endl; + print_available_decks(decks); + return(5); + } + } + def_decks.push_back(def_deck); + def_decks_factors.push_back(deck_parsed.second); + } std::shared_ptr att_deck_ordered; if(ordered) @@ -1250,8 +1272,9 @@ int main(int argc, char** argv) } modify_cards(cards, effect); + print_deck(*att_deck); - Process p(num_threads, cards, decks, ordered ? att_deck_ordered.get() : att_deck, def_decks, def_decks_factors, gamemode, effect); + Process p(num_threads, cards, decks, ordered ? att_deck_ordered.get() : att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); { //ScopeClock timer; for(auto op: todo) diff --git a/xml.cpp b/xml.cpp index d0d0bd56..c5d54439 100644 --- a/xml.cpp +++ b/xml.cpp @@ -4,10 +4,12 @@ #include #include #include +#include #include "rapidxml.hpp" #include "card.h" #include "cards.h" #include "deck.h" +#include "achievement.h" #include "tyrant.h" //---------------------- $20 cards.xml parsing --------------------------------- // Sets: 1 enclave; 2 nexus; 3 blight; 4 purity; 5 homeworld; @@ -28,6 +30,15 @@ Faction map_to_faction(unsigned i) allfactions); } +CardType::CardType map_to_type(unsigned i) +{ + return(i == 1 ? CardType::commander : + i == 2 ? CardType::assault : + i == 4 ? CardType::structure : + i == 8 ? CardType::action : + CardType::num_cardtypes); +} + Faction skill_faction(xml_node<>* skill) { unsigned unmapped_faction(0); @@ -50,75 +61,20 @@ unsigned skill_value(xml_node<>* skill) return(value); } -template -struct GlobalSkill -{ - enum { type = 99 }; -}; - -template<> struct GlobalSkill { enum {type = augment_all}; }; -template<> struct GlobalSkill { enum {type = chaos_all}; }; -template<> struct GlobalSkill { enum {type = cleanse_all}; }; -template<> struct GlobalSkill { enum {type = enfeeble_all}; }; -template<> struct GlobalSkill { enum {type = freeze_all}; }; -template<> struct GlobalSkill { enum {type = heal_all}; }; -template<> struct GlobalSkill { enum {type = jam_all}; }; -template<> struct GlobalSkill { enum {type = protect_all}; }; -template<> struct GlobalSkill { enum {type = rally_all}; }; -template<> struct GlobalSkill { enum {type = repair_all}; }; -template<> struct GlobalSkill { enum {type = siege_all}; }; -template<> struct GlobalSkill { enum {type = strike_all}; }; -template<> struct GlobalSkill { enum {type = weaken_all}; }; - -template -bool handle_global_skill(xml_node<>* node, Card* card) -{ - bool played(node->first_attribute("played")); - bool died(node->first_attribute("died")); - bool attacked(node->first_attribute("attacked")); - bool kill(node->first_attribute("kill")); - if(node->first_attribute("all")) - { - if(played) - { - card->add_played_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); - if(died) {card->add_died_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } - } - else if(died) {card->add_died_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } - else if(attacked) {card->add_attacked_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } - else if(kill) {card->add_kill_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } - else {card->add_skill(ActiveSkill(GlobalSkill), skill_value(node), skill_faction(node)); } - return(true); - } - return(false); -} - -template<> -bool handle_global_skill<99>(xml_node<>* node, Card* card) -{ - return(false); -} - -template +template void handle_skill(xml_node<>* node, Card* card) { + bool all(node->first_attribute("all")); bool played(node->first_attribute("played")); - bool died(node->first_attribute("died")); bool attacked(node->first_attribute("attacked")); bool kill(node->first_attribute("kill")); - if(handle_global_skill::type>(node, card)) {} - else - { - if(played) - { - card->add_played_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); - if(died) {card->add_died_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } - } - else if(died) {card->add_died_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } - else if(attacked) {card->add_attacked_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } - else if(kill) {card->add_kill_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } - else {card->add_skill(ActiveSkill(Skill), skill_value(node), skill_faction(node)); } - } + bool died(node->first_attribute("died")); + bool normal(!(played || died || attacked || kill)); + if(played) { card->add_played_skill(skill, skill_value(node), skill_faction(node), all); } + if(attacked) {card->add_attacked_skill(skill, skill_value(node), skill_faction(node), all); } + if(kill) {card->add_kill_skill(skill, skill_value(node), skill_faction(node), all); } + if(died) {card->add_died_skill(skill, skill_value(node), skill_faction(node), all); } + if(normal) {card->add_skill(skill, skill_value(node), skill_faction(node), all); } } //------------------------------------------------------------------------------ void load_decks_xml(Decks& decks, Cards& cards) @@ -148,6 +104,7 @@ void load_decks_xml(Decks& decks, Cards& cards) std::cout << "\nException while loading decks from file quests.xml\n"; } } + //------------------------------------------------------------------------------ void parse_file(const char* filename, std::vector& buffer, xml_document<>& doc) { @@ -223,14 +180,17 @@ void read_cards(Cards& cards) c->m_id = id; c->m_base_id = id; c->m_name = name_node->value(); + // So far, commanders have attack_node (value == 0) if(id < 1000) { c->m_type = CardType::assault; } else if(id < 2000) { c->m_type = CardType::commander; } else if(id < 3000) { c->m_type = CardType::structure; } - else + else if(id < 4000) { c->m_type = CardType::action; } + else + { c->m_type = cost_node ? (attack_node ? CardType::assault : CardType::structure) : (health_node ? CardType::commander : CardType::action); } if(attack_node) { c->m_attack = atoi(attack_node->value()); } if(health_node) { c->m_health = atoi(health_node->value()); } if(cost_node) { c->m_delay = atoi(cost_node->value()); } @@ -294,8 +254,6 @@ void read_cards(Cards& cards) if(attacked) { c->m_poison_oa = atoi(skill->first_attribute("x")->value()); } else {c->m_poison = atoi(skill->first_attribute("x")->value()); } } - if(strcmp(skill->first_attribute("id")->value(), "recharge") == 0) - { c->m_recharge = true; } if(strcmp(skill->first_attribute("id")->value(), "refresh") == 0) { c->m_refresh = true; } if(strcmp(skill->first_attribute("id")->value(), "regenerate") == 0) @@ -336,6 +294,8 @@ void read_cards(Cards& cards) { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "rally") == 0) { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "recharge") == 0) + { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "repair") == 0) { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "rush") == 0) @@ -403,6 +363,8 @@ void read_missions(Decks& decks, Cards& cards, std::string filename) DeckRandom* deck = &decks.mission_decks.back(); decks.mission_decks_by_id[id] = deck; decks.mission_decks_by_name[deck_name] = deck; + decks.mission_name_by_id[id] = deck_name; + decks.mission_id_by_name[deck_name] = id; } } } @@ -536,3 +498,138 @@ void read_quests(Decks& decks, Cards& cards, std::string filename) } } } +//------------------------------------------------------------------------------ +extern unsigned turn_limit; +Comparator get_comparator(xml_node<>* node, Comparator default_comparator) +{ + xml_attribute<>* compare(node->first_attribute("compare")); + if(!compare) { return default_comparator; } + else if(strcmp(compare->value(), "equal") == 0) { return equal; } + else if(strcmp(compare->value(), "great_equal") == 0) { return great_equal; } + else if(strcmp(compare->value(), "less_equal") == 0) { return less_equal; } + else { throw std::runtime_error(std::string("Not implemented: compare=\"") + compare->value() + "\""); } +} + +void read_achievement(Decks& decks, Cards& cards, Achievement& achievement, const char* achievement_id_name, std::string filename/* = "achievements.xml"*/) +{ + std::vector buffer; + xml_document<> doc; + parse_file(filename.c_str(), buffer, doc); + xml_node<>* root = doc.first_node(); + + if(!root) + { + throw std::runtime_error("Failed to parse " + filename); + } + + std::map skill_map; + for(unsigned i(0); i < Skill::num_skills; ++i) + { + skill_map[skill_names[i]] = i; + } + + for(xml_node<>* achievement_node = root->first_node(); + achievement_node; + achievement_node = achievement_node->next_sibling()) + { + if(strcmp(achievement_node->name(), "achievement") != 0) { continue; } + xml_node<>* id_node(achievement_node->first_node("id")); + xml_node<>* name_node(achievement_node->first_node("name")); + if(!id_node || !name_node || (strcmp(id_node->value(), achievement_id_name) != 0 && strcmp(name_node->value(), achievement_id_name) != 0)) { continue; } + std::cout << "Achievement " << id_node->value() << " " << name_node->value() << ": " << achievement_node->first_node("desc")->value() << std::endl; + xml_node<>* type_node(achievement_node->first_node("type")); + xml_attribute<>* mission_id(type_node ? type_node->first_attribute("mission_id") : NULL); + if(!type_node || !mission_id) + { + throw std::runtime_error("Must be 'mission' type."); + } + assert(strcmp(type_node->first_attribute("winner")->value(), "1") == 0); + if(strcmp(mission_id->value(), "*") != 0) + { + achievement.mission_condition.init(atoi(mission_id->value()), get_comparator(type_node, equal)); + std::cout << " Mission" << achievement.mission_condition.str() << " (" << decks.mission_name_by_id[atoi(mission_id->value())] << ") and win" << std::endl; + } + for (xml_node<>* req_node = achievement_node->first_node("req"); + req_node; + req_node = req_node->next_sibling("req")) + { + Comparator comparator = get_comparator(req_node, great_equal); + xml_attribute<>* skill_id(req_node->first_attribute("skill_id")); + xml_attribute<>* unit_id(req_node->first_attribute("unit_id")); + xml_attribute<>* unit_type(req_node->first_attribute("unit_type")); + xml_attribute<>* unit_race(req_node->first_attribute("unit_race")); + xml_attribute<>* unit_rarity(req_node->first_attribute("unit_rarity")); + xml_attribute<>* num_turns(req_node->first_attribute("num_turns")); + xml_attribute<>* num_used(req_node->first_attribute("num_used")); + xml_attribute<>* num_played(req_node->first_attribute("num_played")); + xml_attribute<>* num_killed(req_node->first_attribute("num_killed")); + if(num_turns && comparator == less_equal) + { + turn_limit = atoi(num_turns->value()); + std::cout << " Turns <= " << turn_limit << std::endl; + } + else if(skill_id && num_used) + { + auto x = skill_map.find(skill_id->value()); + if(x == skill_map.end()) + { + throw std::runtime_error(std::string("Unknown skill ") + skill_id->value()); + } + achievement.skill_used[x->second] = achievement.req_counter.size(); + achievement.req_counter.emplace_back(atoi(num_used->value()), comparator); + std::cout << " Use skills: " << skill_id->value() << achievement.req_counter.back().str() << std::endl; + } + else if(unit_id && num_played) + { + achievement.unit_played[atoi(unit_id->value())] = achievement.req_counter.size(); + achievement.req_counter.emplace_back(atoi(num_played->value()), comparator); + std::cout << " Play units: " << cards.by_id(atoi(unit_id->value()))->m_name << achievement.req_counter.back().str() << std::endl; + } + else if(unit_type && num_played) + { + auto i = map_to_type(atoi(unit_type->value())); + if(i == CardType::num_cardtypes) + { + throw std::runtime_error(std::string("Unknown unit_type ") + unit_type->value()); + } + achievement.unit_type_played[i] = achievement.req_counter.size(); + achievement.req_counter.emplace_back(atoi(num_played->value()), comparator); + std::cout << " Play units of type: " << cardtype_names[i] << achievement.req_counter.back().str() << std::endl; + } + else if(unit_race && num_played) + { + auto i = map_to_faction(atoi(unit_race->value())); + if(i == Faction::allfactions) + { + throw std::runtime_error(std::string("Invalid unit_race ") + unit_race->value()); + } + achievement.unit_faction_played[i] = achievement.req_counter.size(); + achievement.req_counter.emplace_back(atoi(num_played->value()), comparator); + std::cout << " Play units of race (faction): " << faction_names[i] << achievement.req_counter.back().str() << std::endl; + } + else if(unit_rarity && num_played) + { + achievement.unit_rarity_played[atoi(unit_rarity->value())] = achievement.req_counter.size(); + achievement.req_counter.emplace_back(atoi(num_played->value()), comparator); + std::cout << " Play units of rarity: " << rarity_names[atoi(unit_rarity->value())] << achievement.req_counter.back().str() << std::endl; + } + else if(unit_type && num_killed) + { + auto i = map_to_type(atoi(unit_type->value())); + if(i == CardType::num_cardtypes) + { + throw std::runtime_error(std::string("Unknown unit_type ") + unit_type->value()); + } + achievement.unit_type_killed[i] = achievement.req_counter.size(); + achievement.req_counter.emplace_back(atoi(num_killed->value()), comparator); + std::cout << " Kill units of type: " << cardtype_names[i] << achievement.req_counter.back().str() << std::endl; + } + else + { + throw std::runtime_error("Not implemented."); + } + } + return; + } + throw std::runtime_error("No such achievement."); +} diff --git a/xml.h b/xml.h index 7969aa63..80acdb51 100644 --- a/xml.h +++ b/xml.h @@ -5,11 +5,13 @@ class Cards; class Decks; +class Achievement; void load_decks_xml(Decks& decks, Cards& cards); void read_cards(Cards& cards); void read_missions(Decks& decks, Cards& cards, std::string filename); void read_raids(Decks& decks, Cards& cards, std::string filename); void read_quests(Decks& decks, Cards& cards, std::string filename); +void read_achievement(Decks& decks, Cards& cards, Achievement& achievement, const char* achievement_name, std::string filename="achievements.xml"); #endif From a7d0d3852df2a46c2e3a3fcc7d7f6971f089aeb6 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 19 Feb 2013 00:37:50 +0800 Subject: [PATCH 074/406] Fix on-attacked bug. Print out defender decks. --- achievement.h | 2 + deck.cpp | 147 +++++++++++++++++----------- deck.h | 126 +++++++++++------------- read.cpp | 12 ++- read.h | 6 +- sim.cpp | 19 ++-- sim.h | 6 +- tyrant.cpp | 2 + tyrant.h | 15 ++- tyrant_optimize.cpp | 232 ++++++++++++++++++-------------------------- xml.cpp | 28 ++++-- 11 files changed, 298 insertions(+), 297 deletions(-) diff --git a/achievement.h b/achievement.h index 1c211c5d..a7258181 100644 --- a/achievement.h +++ b/achievement.h @@ -71,6 +71,8 @@ struct Achievement std::map unit_faction_played; std::map unit_rarity_played; std::map unit_type_killed; + + Achievement() : id(0) {} }; #endif diff --git a/deck.cpp b/deck.cpp index e1b7fe62..d9982c49 100644 --- a/deck.cpp +++ b/deck.cpp @@ -1,6 +1,7 @@ #include "deck.h" #include +#include #include #include "card.h" @@ -25,23 +26,26 @@ void partial_shuffle(RandomAccessIterator first, RandomAccessIterator middle, } } -namespace boost +//------------------------------------------------------------------------------ +std::string deck_hash(const Card* commander, const std::vector& cards) { -namespace range -{ -template -void shuffle(Range& range, UniformRandomNumberGenerator&& rand) -{ - std::shuffle(boost::begin(range), boost::end(range), rand); + std::string base64= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::stringstream ios; + ios << base64[commander->m_id / 64]; + ios << base64[commander->m_id % 64]; + for(const Card* card: cards) + { + ios << base64[card->m_id / 64]; + ios << base64[card->m_id % 64]; + } + return ios.str(); } -} // namespace range -using range::shuffle; -} // namespace boost - namespace range = boost::range; -DeckRandom::DeckRandom(const Cards& all_cards, const std::vector& names) +void Deck::set(const Cards& all_cards, const std::vector& names) { + commander = nullptr; + strategy = DeckStrategy::random; for(auto name: names) { auto card_it(all_cards.player_cards_by_name.find(name)); @@ -75,8 +79,10 @@ DeckRandom::DeckRandom(const Cards& all_cards, const std::vector& n } } -DeckRandom::DeckRandom(const Cards& all_cards, const std::vector& ids) +void Deck::set(const Cards& all_cards, const std::vector& ids) { + commander = nullptr; + strategy = DeckStrategy::random; for(auto id: ids) { const Card* card{all_cards.by_id(id)}; @@ -102,66 +108,73 @@ DeckRandom::DeckRandom(const Cards& all_cards, const std::vector& ids) } } -DeckIface* DeckRandom::clone() const -{ - return(new DeckRandom(*this)); -} - -const Card* DeckRandom::get_commander() +std::string Deck::short_description() const { - return(commander); + std::stringstream ios; + ios << decktype_names[decktype]; + if(id > 0) { ios << " " << id; } + if(!name.empty()) { ios << " \"" << name << "\""; } + return ios.str(); } -const Card* DeckRandom::next() +std::string Deck::long_description() const { - if(!shuffled_cards.empty()) + std::stringstream ios; + ios << short_description(); + ios << ":"; + if(raid_cards.empty()) { ios << " " << deck_hash(commander, cards); } + ios << std::endl; + if(commander) { - const Card* card = shuffled_cards.front(); - shuffled_cards.pop_front(); - return(card); + ios << commander->m_name << "\n"; } else { - return(nullptr); + ios << "No commander\n"; } -} - -void DeckRandom::shuffle(std::mt19937& re) -{ - shuffled_cards.clear(); - boost::insert(shuffled_cards, shuffled_cards.end(), cards); - for(auto& card_pool: raid_cards) + if(!raid_cards.empty()) { - assert(card_pool.first <= card_pool.second.size()); - partial_shuffle(card_pool.second.begin(), card_pool.second.begin() + card_pool.first, card_pool.second.end(), re); - shuffled_cards.insert(shuffled_cards.end(), card_pool.second.begin(), card_pool.second.begin() + card_pool.first); + ios << "Always include:\n"; } - boost::shuffle(shuffled_cards, re); -} - -void DeckRandom::place_at_bottom(const Card* card) -{ - shuffled_cards.push_back(card); + for(const Card* card: cards) + { + ios << " " << card->m_name << std::endl; + } + for(auto& pool: raid_cards) + { + ios << pool.first << " from:\n"; + for(auto& card: pool.second) + { + ios << " " << card->m_name << "\n"; + } + } + return ios.str(); } -DeckOrdered* DeckOrdered::clone() const +Deck* Deck::clone() const { - return(new DeckOrdered(*this)); + return(new Deck(*this)); } -const Card* DeckOrdered::get_commander() +const Card* Deck::get_commander() { return(commander); } -const Card* DeckOrdered::next() +const Card* Deck::next() { if(shuffled_cards.empty()) { return(nullptr); } - else + else if(strategy == DeckStrategy::random) + { + 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 { @@ -169,7 +182,7 @@ const Card* DeckOrdered::next() if(!card1_order->second.empty()) { auto card2_order = order.find(card2->m_id); - if(!card1_order->second.empty()) + if(!card2_order->second.empty()) { return(*card1_order->second.begin() < *card2_order->second.begin()); } @@ -192,23 +205,43 @@ const Card* DeckOrdered::next() } return(card); } + throw std::runtime_error("Unknown strategy for deck."); } -void DeckOrdered::shuffle(std::mt19937& re) +void Deck::shuffle(std::mt19937& re) { - unsigned i = 0; - order.clear(); - for(auto card: cards) + shuffled_cards.clear(); + boost::insert(shuffled_cards, shuffled_cards.end(), cards); + if(!raid_cards.empty()) { - order[card->m_id].push_back(i); - ++i; + if(strategy != DeckStrategy::random) + { + throw std::runtime_error("Support only random strategy for raid/quest deck."); + } + for(auto& card_pool: raid_cards) + { + assert(card_pool.first <= card_pool.second.size()); + partial_shuffle(card_pool.second.begin(), card_pool.second.begin() + card_pool.first, card_pool.second.end(), re); + shuffled_cards.insert(shuffled_cards.end(), card_pool.second.begin(), card_pool.second.begin() + card_pool.first); + } + } + if(strategy == DeckStrategy::ordered) + { + unsigned i = 0; + order.clear(); + for(auto card: cards) + { + order[card->m_id].push_back(i); + ++i; + } + shuffled_cards.clear(); + range::insert(shuffled_cards, shuffled_cards.end(), cards); } - shuffled_cards.clear(); - range::insert(shuffled_cards, shuffled_cards.end(), cards); std::shuffle(shuffled_cards.begin(), shuffled_cards.end(), re); } -void DeckOrdered::place_at_bottom(const Card* card) +void Deck::place_at_bottom(const Card* card) { shuffled_cards.push_back(card); } + diff --git a/deck.h b/deck.h index 2132f580..87c7eae1 100644 --- a/deck.h +++ b/deck.h @@ -6,91 +6,77 @@ #include #include #include +#include "tyrant.h" class Card; class Cards; +std::string deck_hash(const Card* commander, const std::vector& cards); + //---------------------- $30 Deck: a commander + a sequence of cards ----------- // Can be shuffled. // Implementations: random player and raid decks, ordered player decks. //------------------------------------------------------------------------------ -struct DeckIface +namespace DeckStrategy { - const Card* commander; - std::vector cards; - - DeckIface() : - commander{nullptr} - {} - - DeckIface(const Card* commander_, - std::vector cards_) : - commander(commander_), - cards(std::begin(cards_), std::end(cards_)) - {} - ; - virtual ~DeckIface() {}; - virtual DeckIface* clone() const = 0; - virtual const Card* get_commander() = 0; - virtual const Card* next() = 0; - virtual void shuffle(std::mt19937& re) = 0; - // Special case for recharge (behemoth raid's ability). - virtual void place_at_bottom(const Card*) = 0; -}; -//------------------------------------------------------------------------------ -struct DeckRandom : DeckIface +enum DeckStrategy { - std::vector > > raid_cards; - std::deque shuffled_cards; - - DeckRandom( - const Card* commander_, - const std::vector& cards_, - std::vector > > raid_cards_ = - std::vector > >()) : - DeckIface(commander_, cards_), - raid_cards(raid_cards_) - { - } - - DeckRandom(const DeckIface& other) : - DeckIface(other) - { - } - - DeckRandom(const Cards& all_cards, const std::vector& names); - DeckRandom(const Cards& all_cards, const std::vector& ids); - - ~DeckRandom() {} - - virtual DeckIface* clone() const; - const Card* get_commander(); - const Card* next(); - void shuffle(std::mt19937& re); - void place_at_bottom(const Card* card); + random, + ordered, + num_deckstrategies }; +} //------------------------------------------------------------------------------ // No support for ordered raid decks -struct DeckOrdered : DeckIface +struct Deck { + DeckType::DeckType decktype; + unsigned id; + std::string name; + DeckStrategy::DeckStrategy strategy; + + const Card* commander; + std::vector cards; + std::deque shuffled_cards; // card id -> card order std::map > order; + std::vector > > raid_cards; - DeckOrdered(const Card* commander_, std::vector cards_) : - DeckIface(commander_, cards_), - shuffled_cards(cards.begin(), cards.end()) + Deck( + DeckType::DeckType decktype_ = DeckType::deck, + unsigned id_ = 0, + std::string name_ = "", + DeckStrategy::DeckStrategy strategy_ = DeckStrategy::random) : + decktype(decktype_), + id(id_), + name(name_), + strategy(strategy_), + commander(nullptr) { } - DeckOrdered(const DeckIface& other) : - DeckIface(other) + ~Deck() {} + + void set( + const Card* commander_, + const std::vector& cards_, + std::vector > > raid_cards_ = + std::vector > >()) { + commander = commander_; +// cards = cards_; +// raid_cards = raid_cards_; + cards = std::vector(std::begin(cards_), std::end(cards_)); + raid_cards = std::vector > >(raid_cards_); } - ~DeckOrdered() {} + void set(const Cards& all_cards, const std::vector& names); + void set(const Cards& all_cards, const std::vector& ids); - virtual DeckOrdered* clone() const; + Deck* clone() const; + std::string short_description() const; + std::string long_description() const; const Card* get_commander(); const Card* next(); void shuffle(std::mt19937& re); @@ -100,18 +86,18 @@ struct DeckOrdered : DeckIface // + also the custom decks struct Decks { - std::map custom_decks; - std::list mission_decks; - std::map mission_decks_by_id; - std::map mission_decks_by_name; + std::map custom_decks; + std::list mission_decks; + std::map mission_decks_by_id; + std::map mission_decks_by_name; std::map mission_name_by_id; std::map mission_id_by_name; - std::list raid_decks; - std::map raid_decks_by_id; - std::map raid_decks_by_name; - std::list quest_decks; - std::map quest_decks_by_id; - std::map quest_decks_by_name; + std::list raid_decks; + std::map raid_decks_by_id; + std::map raid_decks_by_name; + std::list quest_decks; + std::map quest_decks_by_id; + std::map quest_decks_by_name; std::map quest_effects_by_id; std::map quest_effects_by_name; diff --git a/read.cpp b/read.cpp index ca53787a..49dcf353 100644 --- a/read.cpp +++ b/read.cpp @@ -54,14 +54,16 @@ bool hash_to_ids(const char* hash, size_t pairs, // Constructs and returns a deck from `hash'. // The caller is responsible for freeing the deck. -DeckIface* hash_to_deck(const char* hash, const Cards& cards) +Deck* hash_to_deck(const char* hash, const Cards& cards) { std::vector ids; if(strlen(hash) % 2 > 0) { return(nullptr); } size_t pairs = strlen(hash) / 2; if(!hash_to_ids(hash, pairs, ids)) { return(nullptr); } - return new DeckRandom(cards, ids); + Deck* deck = new Deck{}; + deck->set(cards, ids); + return deck; } void load_decks(Decks& decks, Cards& cards) @@ -142,7 +144,7 @@ template Iterator read_toke // Error codes: // 2 -> file not readable // 3 -> error while parsing file -unsigned read_custom_decks(Cards& cards, std::string filename, std::map& custom_decks) +unsigned read_custom_decks(Cards& cards, std::string filename, std::map& custom_decks) { std::ifstream decks_file(filename.c_str()); if(!decks_file.is_open()) @@ -239,7 +241,9 @@ unsigned read_custom_decks(Cards& cards, std::string filename, std::mapset(cards, card_ids); + custom_decks.insert({*deck_name, deck}); } } } diff --git a/read.h b/read.h index c2282fd4..61c38b3a 100644 --- a/read.h +++ b/read.h @@ -7,12 +7,12 @@ class Cards; class Decks; -class DeckIface; +class Deck; -DeckIface* hash_to_deck(const char* hash, const Cards& cards); +Deck* hash_to_deck(const char* hash, const Cards& cards); void load_decks(Decks& decks, Cards& cards); std::vector > parse_deck_list(std::string list_string); -unsigned read_custom_decks(Cards& cards, std::string filename, std::map& custom_decks); +unsigned read_custom_decks(Cards& cards, std::string filename, std::map& custom_decks); void read_owned_cards(Cards& cards, std::map& owned_cards, const char *filename); #endif diff --git a/sim.cpp b/sim.cpp index 42bee0db..1823b26b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1030,7 +1030,9 @@ struct PerformAttack template void op() { - att_dmg = calculate_attack_damage(); + unsigned pre_modifier_dmg = attack_power(att_status); + if(pre_modifier_dmg == 0) { return; } + modify_attack_damage(pre_modifier_dmg); // Evaluation order: // assaults only: fly check // assaults only: immobilize @@ -1087,19 +1089,17 @@ struct PerformAttack } template - unsigned calculate_attack_damage() + void modify_attack_damage(unsigned pre_modifier_dmg) { const Card& att_card(*att_status->m_card); const Card& def_card(*def_status->m_card); assert(att_card.m_type == CardType::assault); - // pre modifier damage - unsigned damage(attack_power(att_status)); - if(damage == 0) { return(0); } + assert(pre_modifier_dmg > 0); unsigned valor_damage{att_card.m_valor && skill_activate(fd, att_status, nullptr) ? att_card.m_valor : 0}; unsigned antiair_damage{att_card.m_antiair > 0 && skill_activate(fd, att_status, def_status) ? att_card.m_antiair : 0}; unsigned burst_damage{att_card.m_burst > 0 && skill_activate(fd, att_status, def_status) ? att_card.m_burst : 0}; - unsigned modified_damage = safe_minus( - damage // pre-modifier damage + att_dmg = safe_minus( + pre_modifier_dmg + valor_damage + def_status->m_enfeebled // enfeeble + antiair_damage @@ -1123,10 +1123,9 @@ struct PerformAttack if(def_status->m_protected > 0) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->m_protected) + "(protected)"; } if(!reduced_desc.empty() && att_card.m_pierce > 0) { reduced_desc += "-" + to_string(att_card.m_pierce) + "(pierce)"; } if(!reduced_desc.empty()) { desc += "-(" + reduced_desc + ")"; } - if(!desc.empty()) { desc += "=" + to_string(modified_damage); } - _DEBUG_MSG("%s attacks %s for %u%s damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), damage, desc.c_str()); + if(!desc.empty()) { desc += "=" + to_string(att_dmg); } + _DEBUG_MSG("%s attacks %s for %u%s damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), pre_modifier_dmg, desc.c_str()); } - return(modified_damage); } template diff --git a/sim.h b/sim.h index 6ce91d2f..6766ca34 100644 --- a/sim.h +++ b/sim.h @@ -11,7 +11,7 @@ class Card; class Cards; -class DeckIface; +class Deck; class Field; class Achievement; @@ -128,7 +128,7 @@ class Hand { public: - Hand(DeckIface* deck_) : + Hand(Deck* deck_) : deck(deck_), assaults(15), structures(15) @@ -137,7 +137,7 @@ class Hand void reset(std::mt19937& re); - DeckIface* deck; + Deck* deck; CardStatus commander; Storage assaults; Storage structures; diff --git a/tyrant.cpp b/tyrant.cpp index cb50188d..dc92a606 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -37,6 +37,8 @@ std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Str std::string rarity_names[5]{"", "common", "uncommon", "rare", "legendary", }; +std::string decktype_names[DeckType::num_decktypes]{"Deck", "Mission", "Raid", "Quest", "Custom Deck", }; + std::string effect_names[Effect::num_effects] = { "None", "Time Surge", diff --git a/tyrant.h b/tyrant.h index be80fa8d..66e563a5 100644 --- a/tyrant.h +++ b/tyrant.h @@ -55,6 +55,19 @@ extern std::string cardtype_names[CardType::num_cardtypes]; extern std::string rarity_names[5]; +namespace DeckType { +enum DeckType { + deck, + mission, + raid, + quest, + custom_deck, + num_decktypes +}; +} + +extern std::string decktype_names[DeckType::num_decktypes]; + enum Effect { none, time_surge, @@ -109,6 +122,6 @@ enum SkillSourceType source_chaos }; -typedef std::tuple SkillSpec; +typedef std::tuple SkillSpec; #endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 80ca3fcd..a8c13267 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -42,37 +42,6 @@ namespace { bool use_anp{false}; } using namespace std::placeholders; //------------------------------------------------------------------------------ -void print_deck(DeckIface& deck) -{ - std::cout << "Deck:" << std::endl; - if(deck.commander) - { - std::cout << deck.commander->m_name << "\n"; - } - else - { - std::cout << "No commander\n"; - } - for(const Card* card: deck.cards) - { - std::cout << " " << card->m_name << "\n" << std::flush; - } -} -//------------------------------------------------------------------------------ -std::string deck_hash(const Card* commander, const std::vector& cards) -{ - std::string base64= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - std::stringstream ios; - ios << base64[commander->m_id / 64]; - ios << base64[commander->m_id % 64]; - for(const Card* card: cards) - { - ios << base64[card->m_id / 64]; - ios << base64[card->m_id % 64]; - } - return ios.str(); -} -//------------------------------------------------------------------------------ std::string card_id_name(const Card* card) { std::stringstream ios; @@ -87,8 +56,13 @@ std::string card_id_name(const Card* card) return ios.str(); } //------------------------------------------------------------------------------ -DeckIface* find_deck(const Decks& decks, std::string name) +Deck* find_deck(const Decks& decks, const Cards& cards, std::string name) { + auto it1 = decks.mission_decks_by_name.find(name); + if(it1 != decks.mission_decks_by_name.end()) + { + return(it1->second); + } auto it2 = decks.raid_decks_by_name.find(name); if(it2 != decks.raid_decks_by_name.end()) { @@ -104,7 +78,7 @@ DeckIface* find_deck(const Decks& decks, std::string name) { return(it3->second); } - return(nullptr); + return(hash_to_deck(name.c_str(), cards)); } //---------------------- $80 deck optimization --------------------------------- //------------------------------------------------------------------------------ @@ -187,7 +161,7 @@ std::set top_commanders{ 1225, // Yuletta }; //------------------------------------------------------------------------------ -bool suitable_non_commander(DeckIface& deck, unsigned slot, const Card* card) +bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) { assert(card->m_type != CardType::commander); if(use_owned_cards) @@ -295,9 +269,9 @@ struct SimulationData std::mt19937 re; const Cards& cards; const Decks& decks; - std::shared_ptr att_deck; + std::shared_ptr att_deck; Hand att_hand; - std::vector > def_decks; + std::vector > def_decks; std::vector def_hands; std::vector factors; gamemode_t gamemode; @@ -327,7 +301,7 @@ struct SimulationData for(auto hand: def_hands) { delete(hand); } } - void set_decks(const DeckIface* const att_deck_, std::vector const & def_decks_) + void set_decks(const Deck* const att_deck_, std::vector const & def_decks_) { att_deck.reset(att_deck_->clone()); att_hand.deck = att_deck.get(); @@ -370,14 +344,14 @@ class Process boost::mutex shared_mutex; const Cards& cards; const Decks& decks; - DeckIface* att_deck; - const std::vector def_decks; + Deck* att_deck; + const std::vector def_decks; std::vector factors; gamemode_t gamemode; enum Effect effect; Achievement achievement; - Process(unsigned _num_threads, const Cards& cards_, const Decks& decks_, DeckIface* att_deck_, std::vector _def_decks, std::vector _factors, gamemode_t _gamemode, enum Effect _effect, const Achievement& achievement_) : + Process(unsigned _num_threads, const Cards& cards_, const Decks& decks_, Deck* att_deck_, std::vector _def_decks, std::vector _factors, gamemode_t _gamemode, enum Effect _effect, const Achievement& achievement_) : num_threads(_num_threads), main_barrier(num_threads+1), cards(cards_), @@ -552,7 +526,7 @@ void print_deck_inline(const double score, const Card *commander, std::vectorcards = best_cards; } - std::cout << "Best Deck: "; + std::cout << "Optimized Deck: "; print_deck_inline(best_score, best_commander, best_cards); } //------------------------------------------------------------------------------ -void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& proc) +void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) { auto results = proc.evaluate(num_iterations); print_score_info(results, proc.factors); @@ -758,7 +732,7 @@ void hill_climbing_ordered(unsigned num_iterations, DeckOrdered* d1, Process& pr if(best_score == best_possible) { break; } } } - std::cout << "Best Deck: "; + std::cout << "Optimized Deck: "; print_deck_inline(best_score, best_commander, best_cards); } //------------------------------------------------------------------------------ @@ -825,7 +799,7 @@ class Combination }; //------------------------------------------------------------------------------ static unsigned total_num_combinations_test(0); -inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsigned num_iterations, const std::vector& card_indices, std::vector& cards, const Card* commander, Process& proc, double& best_score, boost::optional& best_deck) +inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsigned num_iterations, const std::vector& card_indices, std::vector& cards, const Card* commander, Process& proc, double& best_score, boost::optional& best_deck) { assert(card_indices.size() > 0); assert(card_indices.size() <= deck_size); @@ -858,8 +832,9 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig std::vector deck_cards = unique_cards; std::vector combined_cards(num_cards_to_combine, cards_to_combine[0]); deck_cards.insert(deck_cards.end(), combined_cards.begin(), combined_cards.end()); - DeckRandom deck(commander, deck_cards); - (*dynamic_cast(proc.att_deck)) = deck; + Deck deck{}; + deck.set(commander, deck_cards); + (*dynamic_cast(proc.att_deck)) = deck; auto new_results = proc.compare(num_iterations, best_score); double new_score = compute_score(new_results, proc.factors); if(new_score > best_score) @@ -897,7 +872,8 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig //std::cout << "\n" << std::flush; //std::cout << std::flush; assert(deck_cards.size() == deck_size); - DeckRandom deck(commander, deck_cards); + Deck deck{}; + deck.set(commander, deck_cards); *proc.att_deck = deck; auto new_results = proc.compare(num_iterations, best_score); double new_score = compute_score(new_results, proc.factors); @@ -934,13 +910,13 @@ void exhaustive_k(unsigned num_iterations, unsigned var_k, Process& proc) const std::vector& indices = cardIndices.getIndices(); bool finished(false); double best_score{0}; - boost::optional best_deck; - unsigned num_cards = ((DeckRandom*)proc.att_deck)->cards.size(); + boost::optional best_deck; + unsigned num_cards = ((Deck*)proc.att_deck)->cards.size(); while(!finished) { if(keep_commander) { - try_all_ratio_combinations(num_cards, var_k, num_iterations, indices, ass_structs, ((DeckRandom*)proc.att_deck)->commander, proc, best_score, best_deck); + try_all_ratio_combinations(num_cards, var_k, num_iterations, indices, ass_structs, ((Deck*)proc.att_deck)->commander, proc, best_score, best_deck); } else { @@ -964,42 +940,19 @@ enum Operation { fightuntil }; //------------------------------------------------------------------------------ -// void print_raid_deck(DeckRandom& deck) -// { -// std::cout << "--------------- Raid ---------------\n"; -// std::cout << "Commander:\n"; -// std::cout << " " << deck.m_commander->m_name << "\n"; -// std::cout << "Always include:\n"; -// for(auto& card: deck.m_cards) -// { -// std::cout << " " << card->m_name << "\n"; -// } -// for(auto& pool: deck.m_raid_cards) -// { -// std::cout << pool.first << " from:\n"; -// for(auto& card: pool.second) -// { -// std::cout << " " << card->m_name << "\n"; -// } -// } -// } -//------------------------------------------------------------------------------ -void print_available_decks(const Decks& decks) +void print_available_decks(const Decks& decks, bool allow_card_pool) { - std::cout << "Mission decks:\n"; - for(auto it: decks.mission_decks_by_name) - { - std::cout << " " << it.first << "\n"; - } - std::cout << "Raid decks:\n"; - for(auto it: decks.raid_decks_by_name) - { - std::cout << " " << it.first << "\n"; - } - std::cout << "Custom decks:\n"; - for(auto it: decks.custom_decks) + std::cout << "Available decks: (use double-quoted name)" << std::endl; + std::cout << "(All missions, omitted because the list is too long.)" << std::endl; +#if 0 + for(auto it: boost::join(boost::join(boost::join(decks.mission_decks_by_name, decks.raid_decks_by_name), decks.quest_decks_by_name), decks.custom_decks)) +#endif + for(auto it: boost::join(boost::join(decks.raid_decks_by_name, decks.quest_decks_by_name), decks.custom_decks)) { - std::cout << " " << it.first << "\n"; + if(allow_card_pool || it.second->raid_cards.empty()) + { + std::cout << it.second->short_description() << "\n"; + } } } @@ -1059,7 +1012,7 @@ int main(int argc, char** argv) if(argc <= 2) { - print_available_decks(decks); + print_available_decks(decks, true); return(4); } std::string att_deck_name{argv[1]}; @@ -1201,80 +1154,81 @@ int main(int argc, char** argv) } } - DeckIface* att_deck{nullptr}; - auto custom_deck_it = decks.custom_decks.find(att_deck_name); - if(custom_deck_it != decks.custom_decks.end()) + modify_cards(cards, effect); + + Deck* att_deck{nullptr}; + try { - att_deck = custom_deck_it->second; + att_deck = find_deck(decks, cards, att_deck_name); } - else + catch(const std::runtime_error& e) + { + std::cerr << "Error: Deck hash " << att_deck_name << ": " << e.what() << std::endl; + return(5); + } + if(att_deck == nullptr) + { + std::cerr << "Error: Invalid attack deck name/hash " << att_deck_name << ".\n"; + } + else if(!att_deck->raid_cards.empty()) + { + std::cerr << "Error: Invalid attack deck " << att_deck_name << ": has optional cards.\n"; + att_deck = nullptr; + } + if(att_deck == nullptr) + { + print_available_decks(decks, false); + return(5); + } + if(ordered) { - try - { att_deck = hash_to_deck(att_deck_name.c_str(), cards); } - catch(const std::runtime_error& e) - { - std::cerr << "Error: Deck hash " << att_deck_name << ": " << e.what() << std::endl; - return(5); - } if(att_deck == nullptr) - { - std::cerr << "Error: Invalid attack deck name/hash " << att_deck_name << ". Available decks:" << std::endl; - std::cerr << "Custom decks:" << std::endl; - for(auto it: decks.custom_decks) - { std::cerr << " " << it.first << std::endl; } - return(5); - } + std::cerr << "No!" << std::endl; + att_deck->strategy = DeckStrategy::ordered; } + std::cout << "Attacker:" << std::endl; + std::cout << att_deck->long_description() << std::endl; - std::vector def_decks; + std::cout << "Defender:" << std::endl; + std::vector def_decks; std::vector def_decks_factors; for(auto deck_parsed: deck_list_parsed) { - DeckIface* def_deck{nullptr}; - auto it1 = decks.mission_decks_by_name.find(deck_parsed.first); - if(it1 != decks.mission_decks_by_name.end()) + Deck* def_deck{nullptr}; + try { - if(!achievement.mission_condition.check(decks.mission_id_by_name[deck_parsed.first])) - { - std::cerr << "Error: Wrong mission [" << deck_parsed.first << "] for achievement." << std::endl; - return(1); - } - def_deck = it1->second; + def_deck = find_deck(decks, cards, deck_parsed.first); } - if(def_deck == nullptr) + catch(const std::runtime_error& e) { - def_deck = find_deck(decks, deck_parsed.first); + std::cerr << "Error: Deck hash " << deck_parsed.first << ": " << e.what() << std::endl; + return(5); } if(def_deck == nullptr) { - try - { def_deck = hash_to_deck(deck_parsed.first.c_str(), cards); } - catch(const std::runtime_error& e) + std::cerr << "Error: Invalid defense deck name/hash " << deck_parsed.first << ".\n"; + print_available_decks(decks, true); + return(5); + } + def_decks.push_back(def_deck); + def_decks_factors.push_back(deck_parsed.second); + std::cout << def_deck->long_description() << std::endl; + if(achievement.id > 0) + { + if(def_deck->decktype != DeckType::mission) { - std::cerr << "Error: Deck hash " << deck_parsed.first << ": " << e.what() << std::endl; - return(5); + std::cerr << "Error: Defender must be mission for achievement." << std::endl; + return(1); } - if(def_deck == nullptr) + if(!achievement.mission_condition.check(def_deck->id)) { - std::cerr << "Error: Invalid defense deck name/hash " << deck_parsed.first << ". Available decks:" << std::endl; - print_available_decks(decks); - return(5); + std::cerr << "Error: Wrong mission [" << deck_parsed.first << "] for achievement: " << achievement.mission_condition.str() << "." << std::endl; + return(1); } } - def_decks.push_back(def_deck); - def_decks_factors.push_back(deck_parsed.second); } - std::shared_ptr att_deck_ordered; - if(ordered) - { - att_deck_ordered = std::make_shared(*att_deck); - } - - modify_cards(cards, effect); - print_deck(*att_deck); - - Process p(num_threads, cards, decks, ordered ? att_deck_ordered.get() : att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); + Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); { //ScopeClock timer; for(auto op: todo) @@ -1292,7 +1246,7 @@ int main(int argc, char** argv) } else { - hill_climbing_ordered(std::get<0>(op), att_deck_ordered.get(), p); + hill_climbing_ordered(std::get<0>(op), att_deck, p); } break; } diff --git a/xml.cpp b/xml.cpp index c5d54439..2fe81eb2 100644 --- a/xml.cpp +++ b/xml.cpp @@ -158,7 +158,7 @@ void read_cards(Cards& cards) if(strcmp(card->name(), "unit") == 0) { xml_node<>* id_node(card->first_node("id")); - int id(id_node ? atoi(id_node->value()) : -1); + unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(card->first_node("name")); xml_node<>* attack_node(card->first_node("attack")); xml_node<>* health_node(card->first_node("health")); @@ -346,7 +346,8 @@ void read_missions(Decks& decks, Cards& cards, std::string filename) { std::vector card_ids; xml_node<>* id_node(mission_node->first_node("id")); - int id(id_node ? atoi(id_node->value()) : -1); + assert(id_node); + unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(mission_node->first_node("name")); std::string deck_name{name_node->value()}; xml_node<>* commander_node(mission_node->first_node("commander")); @@ -359,8 +360,9 @@ void read_missions(Decks& decks, Cards& cards, std::string filename) unsigned card_id{static_cast(atoi(card_node->value()))}; card_ids.push_back(card_id); } - decks.mission_decks.push_back(DeckRandom{cards, card_ids}); - DeckRandom* deck = &decks.mission_decks.back(); + decks.mission_decks.push_back(Deck{DeckType::mission, id, deck_name}); + Deck* deck = &decks.mission_decks.back(); + deck->set(cards, card_ids); decks.mission_decks_by_id[id] = deck; decks.mission_decks_by_name[deck_name] = deck; decks.mission_name_by_id[id] = deck_name; @@ -390,7 +392,8 @@ void read_raids(Decks& decks, Cards& cards, std::string filename) std::vector always_cards; std::vector > > some_cards; xml_node<>* id_node(raid_node->first_node("id")); - int id(id_node ? atoi(id_node->value()) : -1); + assert(id_node); + unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(raid_node->first_node("name")); std::string deck_name{name_node->value()}; xml_node<>* commander_node(raid_node->first_node("commander")); @@ -432,8 +435,9 @@ void read_raids(Decks& decks, Cards& cards, std::string filename) some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); } } - decks.raid_decks.push_back(DeckRandom{commander_card, always_cards, some_cards}); - DeckRandom* deck = &decks.raid_decks.back(); + decks.raid_decks.push_back(Deck{DeckType::raid, id, deck_name}); + Deck* deck = &decks.raid_decks.back(); + deck->set(commander_card, always_cards, some_cards); decks.raid_decks_by_id[id] = deck; decks.raid_decks_by_name[deck_name] = deck; } @@ -463,7 +467,8 @@ void read_quests(Decks& decks, Cards& cards, std::string filename) { std::vector > > some_cards; xml_node<>* id_node(quest_node->first_node("id")); - int id(id_node ? atoi(id_node->value()) : -1); + assert(id_node); + unsigned id(id_node ? atoi(id_node->value()) : 0); std::string deck_name{"Step " + std::string{id_node->value()}}; xml_node<>* commander_node(quest_node->first_node("commander")); const Card* commander_card{cards.by_id(atoi(commander_node->value()))}; @@ -489,8 +494,9 @@ void read_quests(Decks& decks, Cards& cards, std::string filename) some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); } } - decks.quest_decks.push_back(DeckRandom{commander_card, always_cards, some_cards}); - DeckRandom* deck = &decks.quest_decks.back(); + decks.quest_decks.push_back(Deck{DeckType::quest, id, deck_name}); + Deck* deck = &decks.quest_decks.back(); + deck->set(commander_card, always_cards, some_cards); decks.quest_decks_by_id[id] = deck; decks.quest_effects_by_id[id] = battleground_id; decks.quest_decks_by_name[deck_name] = deck; @@ -536,6 +542,8 @@ void read_achievement(Decks& decks, Cards& cards, Achievement& achievement, cons xml_node<>* id_node(achievement_node->first_node("id")); xml_node<>* name_node(achievement_node->first_node("name")); if(!id_node || !name_node || (strcmp(id_node->value(), achievement_id_name) != 0 && strcmp(name_node->value(), achievement_id_name) != 0)) { continue; } + achievement.id = atoi(id_node->value()); + achievement.name = name_node->value(); std::cout << "Achievement " << id_node->value() << " " << name_node->value() << ": " << achievement_node->first_node("desc")->value() << std::endl; xml_node<>* type_node(achievement_node->first_node("type")); xml_attribute<>* mission_id(type_node ? type_node->first_attribute("mission_id") : NULL); From f3ee8814c255514f09a9e600cc62330b5dd93bcd Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 19 Feb 2013 12:59:45 +0800 Subject: [PATCH 075/406] Fix bug: weaken on attacked. --- deck.cpp | 6 +++++- deck.h | 6 ++---- sim.cpp | 3 ++- tyrant_optimize.cpp | 14 +++++++------- xml.cpp | 7 ++----- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/deck.cpp b/deck.cpp index d9982c49..311f445e 100644 --- a/deck.cpp +++ b/deck.cpp @@ -124,6 +124,10 @@ std::string Deck::long_description() const ios << ":"; if(raid_cards.empty()) { ios << " " << deck_hash(commander, cards); } ios << std::endl; + if(effect != Effect::none) + { + ios << "Effect: " << effect_names[effect] << "\n"; + } if(commander) { ios << commander->m_name << "\n"; @@ -132,7 +136,7 @@ std::string Deck::long_description() const { ios << "No commander\n"; } - if(!raid_cards.empty()) + if(!cards.empty() && !raid_cards.empty()) { ios << "Always include:\n"; } diff --git a/deck.h b/deck.h index 87c7eae1..9b9fb2a5 100644 --- a/deck.h +++ b/deck.h @@ -34,6 +34,7 @@ struct Deck unsigned id; std::string name; DeckStrategy::DeckStrategy strategy; + Effect effect; // for quests const Card* commander; std::vector cards; @@ -52,6 +53,7 @@ struct Deck id(id_), name(name_), strategy(strategy_), + effect(Effect::none), commander(nullptr) { } @@ -90,16 +92,12 @@ struct Decks std::list mission_decks; std::map mission_decks_by_id; std::map mission_decks_by_name; - std::map mission_name_by_id; - std::map mission_id_by_name; std::list raid_decks; std::map raid_decks_by_id; std::map raid_decks_by_name; std::list quest_decks; std::map quest_decks_by_id; std::map quest_decks_by_name; - std::map quest_effects_by_id; - std::map quest_effects_by_name; ~Decks() { diff --git a/sim.cpp b/sim.cpp index 1823b26b..b9ee3181 100644 --- a/sim.cpp +++ b/sim.cpp @@ -231,6 +231,7 @@ std::string CardStatus::description() if(m_enfeebled > 0) { desc += ", enfeebled " + to_string(m_enfeebled); } if(m_poisoned > 0) { desc += ", poisoned " + to_string(m_poisoned); } if(m_protected > 0) { desc += ", protected " + to_string(m_protected); } +// if(m_attacked) { desc += ", attacked"; } desc += "]"; return(desc); } @@ -1441,7 +1442,7 @@ inline bool skill_predicate(Field* fd, CardStatus* c) template<> inline bool skill_predicate(Field* fd, CardStatus* c) -{ return(!c->m_immobilized && attack_power(c) > 0 && can_act(fd, c)); } +{ return(!c->m_immobilized && !c->m_attacked && attack_power(c) > 0 && can_act(fd, c)); } template inline void perform_skill(Field* fd, CardStatus* c, unsigned v) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index a8c13267..6a95e636 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -557,7 +557,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) { // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); - if(commander_candidate == best_commander) { continue; } + if(commander_candidate->m_base_id == best_commander->m_base_id) { continue; } if(!suitable_commander(commander_candidate)) { continue; } // Place it in the deck d1->commander = commander_candidate; @@ -587,7 +587,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) { // Various checks to check if the card is accepted assert(card_candidate->m_type != CardType::commander); - if(slot_i < best_cards.size() && card_candidate == best_cards[slot_i]) { continue; } + if(slot_i < best_cards.size() && card_candidate->m_base_id == best_cards[slot_i]->m_base_id) { continue; } if(!suitable_non_commander(*d1, slot_i, card_candidate)) { continue; } // Place it in the deck if(slot_i == d1->cards.size()) @@ -663,7 +663,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) if(best_score == best_possible) { break; } // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); - if(commander_candidate == best_commander) { continue; } + if(commander_candidate->m_base_id == best_commander->m_base_id) { continue; } if(!suitable_commander(commander_candidate)) { continue; } // Place it in the deck d1->commander = commander_candidate; @@ -696,7 +696,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) if(card_candidate) { // Various checks to check if the card is accepted - if(to_slot < best_cards.size() && card_candidate == best_cards[to_slot]) { continue; } + if(to_slot < best_cards.size() && card_candidate->m_base_id == best_cards[to_slot]->m_base_id) { continue; } if(!suitable_non_commander(*d1, from_slot, card_candidate)) { continue; } // Place it in the deck if(from_slot < d1->cards.size()) @@ -1080,13 +1080,13 @@ int main(int argc, char** argv) // Set quest effect: for(auto deck_parsed: deck_list_parsed) { - auto effect_id = decks.quest_effects_by_name.find(deck_parsed.first); - if(effect_id == decks.quest_effects_by_name.end()) + auto deck_it = decks.quest_decks_by_name.find(deck_parsed.first); + if(deck_it == decks.quest_decks_by_name.end() || deck_it->second->effect == Effect::none) { std::cout << "WARNING: The deck '" << deck_parsed.first << "' has no battleground effect! Are you sure it's a quest deck?\n"; continue; } - enum Effect this_effect = static_cast(effect_id->second); + enum Effect this_effect = deck_it->second->effect; if(effect != Effect::none && this_effect != effect) { std::cout << "ERROR: Inconsistent effects! Had " << effect << ", now have " << this_effect << "\n"; diff --git a/xml.cpp b/xml.cpp index 2fe81eb2..904c3c23 100644 --- a/xml.cpp +++ b/xml.cpp @@ -365,8 +365,6 @@ void read_missions(Decks& decks, Cards& cards, std::string filename) deck->set(cards, card_ids); decks.mission_decks_by_id[id] = deck; decks.mission_decks_by_name[deck_name] = deck; - decks.mission_name_by_id[id] = deck_name; - decks.mission_id_by_name[deck_name] = id; } } } @@ -496,11 +494,10 @@ void read_quests(Decks& decks, Cards& cards, std::string filename) } decks.quest_decks.push_back(Deck{DeckType::quest, id, deck_name}); Deck* deck = &decks.quest_decks.back(); + deck->effect = static_cast(battleground_id); deck->set(commander_card, always_cards, some_cards); decks.quest_decks_by_id[id] = deck; - decks.quest_effects_by_id[id] = battleground_id; decks.quest_decks_by_name[deck_name] = deck; - decks.quest_effects_by_name[deck_name] = battleground_id; } } } @@ -555,7 +552,7 @@ void read_achievement(Decks& decks, Cards& cards, Achievement& achievement, cons if(strcmp(mission_id->value(), "*") != 0) { achievement.mission_condition.init(atoi(mission_id->value()), get_comparator(type_node, equal)); - std::cout << " Mission" << achievement.mission_condition.str() << " (" << decks.mission_name_by_id[atoi(mission_id->value())] << ") and win" << std::endl; + std::cout << " Mission" << achievement.mission_condition.str() << " (" << decks.mission_decks_by_id[atoi(mission_id->value())]->name << ") and win" << std::endl; } for (xml_node<>* req_node = achievement_node->first_node("req"); req_node; From 88106a99b444414d9d4dd65d7739b568c96deabf Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 19 Feb 2013 17:34:45 +0800 Subject: [PATCH 076/406] Remove -q option. Automatically detect quests. --- deck.h | 21 +---- read.cpp | 19 ++-- read.h | 2 +- tyrant_optimize.cpp | 91 ++++++++----------- xml.cpp | 207 +++++++++++++++++--------------------------- xml.h | 10 +-- 6 files changed, 135 insertions(+), 215 deletions(-) diff --git a/deck.h b/deck.h index 9b9fb2a5..3a114b34 100644 --- a/deck.h +++ b/deck.h @@ -88,24 +88,9 @@ struct Deck // + also the custom decks struct Decks { - std::map custom_decks; - std::list mission_decks; - std::map mission_decks_by_id; - std::map mission_decks_by_name; - std::list raid_decks; - std::map raid_decks_by_id; - std::map raid_decks_by_name; - std::list quest_decks; - std::map quest_decks_by_id; - std::map quest_decks_by_name; - - ~Decks() - { - for(auto& obj: custom_decks) - { - delete(obj.second); - } - } + std::list decks; + std::map by_name; + std::map mission_names_by_id; }; #endif diff --git a/read.cpp b/read.cpp index 49dcf353..7faf0aab 100644 --- a/read.cpp +++ b/read.cpp @@ -72,7 +72,7 @@ void load_decks(Decks& decks, Cards& cards) { try { - read_custom_decks(cards, std::string{"Custom.txt"}, decks.custom_decks); + read_custom_decks(decks, cards, "Custom.txt"); } catch(const std::runtime_error& e) { @@ -144,7 +144,7 @@ template Iterator read_toke // Error codes: // 2 -> file not readable // 3 -> error while parsing file -unsigned read_custom_decks(Cards& cards, std::string filename, std::map& custom_decks) +unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) { std::ifstream decks_file(filename.c_str()); if(!decks_file.is_open()) @@ -185,6 +185,11 @@ unsigned read_custom_decks(Cards& cards, std::string filename, std::mapsecond->short_description() << std::endl; + } ++token_iter; for(; token_iter != deck_tokens.end(); ++token_iter) { @@ -239,12 +244,10 @@ unsigned read_custom_decks(Cards& cards, std::string filename, std::mapset(cards, card_ids); - custom_decks.insert({*deck_name, deck}); - } + decks.decks.push_back(Deck{DeckType::custom_deck, num_line, *deck_name}); + Deck* deck = &decks.decks.back(); + deck->set(cards, card_ids); + decks.by_name[*deck_name] = deck; } } } diff --git a/read.h b/read.h index 61c38b3a..2a71e470 100644 --- a/read.h +++ b/read.h @@ -12,7 +12,7 @@ class Deck; Deck* hash_to_deck(const char* hash, const Cards& cards); void load_decks(Decks& decks, Cards& cards); std::vector > parse_deck_list(std::string list_string); -unsigned read_custom_decks(Cards& cards, std::string filename, std::map& custom_decks); +unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename); void read_owned_cards(Cards& cards, std::map& owned_cards, const char *filename); #endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 6a95e636..e96ff805 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -58,25 +58,10 @@ std::string card_id_name(const Card* card) //------------------------------------------------------------------------------ Deck* find_deck(const Decks& decks, const Cards& cards, std::string name) { - auto it1 = decks.mission_decks_by_name.find(name); - if(it1 != decks.mission_decks_by_name.end()) + auto it = decks.by_name.find(name); + if(it != decks.by_name.end()) { - return(it1->second); - } - auto it2 = decks.raid_decks_by_name.find(name); - if(it2 != decks.raid_decks_by_name.end()) - { - return(it2->second); - } - auto it4 = decks.quest_decks_by_name.find(name); - if(it4 != decks.quest_decks_by_name.end()) - { - return(it4->second); - } - auto it3 = decks.custom_decks.find(name); - if(it3 != decks.custom_decks.end()) - { - return(it3->second); + return(it->second); } return(hash_to_deck(name.c_str(), cards)); } @@ -944,14 +929,11 @@ void print_available_decks(const Decks& decks, bool allow_card_pool) { std::cout << "Available decks: (use double-quoted name)" << std::endl; std::cout << "(All missions, omitted because the list is too long.)" << std::endl; -#if 0 - for(auto it: boost::join(boost::join(boost::join(decks.mission_decks_by_name, decks.raid_decks_by_name), decks.quest_decks_by_name), decks.custom_decks)) -#endif - for(auto it: boost::join(boost::join(decks.raid_decks_by_name, decks.quest_decks_by_name), decks.custom_decks)) + for(auto& deck: decks.decks) { - if(allow_card_pool || it.second->raid_cards.empty()) + if(deck.decktype != DeckType::mission && (allow_card_pool || deck.raid_cards.empty())) { - std::cout << it.second->short_description() << "\n"; + std::cout << deck.short_description() << "\n"; } } } @@ -973,11 +955,10 @@ void usage(int argc, char** argv) " -a: optimize for ANP instead of win rate.\n" " -A : optimize for the achievement specified by either id or name.\n" " -c: don't try to optimize the commander.\n" - " -e : set the battleground effect.\n" + " -e : set the battleground effect. effect is automatically set for quests.\n" " -fixedlen: prevent hill climbing from changing the number of cards.\n" " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n" " -o=: restrict hill climbing to the owned cards listed in .\n" - " -q: quest mode. automatically sets quest effect.\n" " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" " -s: use surge (default is fight).\n" " -t : set the number of threads, default is 4.\n" @@ -1075,26 +1056,6 @@ int main(int argc, char** argv) read_owned_cards(cards, owned_cards, argv[argIndex] + 3); use_owned_cards = true; } - else if(strcmp(argv[argIndex], "-q") == 0) - { - // Set quest effect: - for(auto deck_parsed: deck_list_parsed) - { - auto deck_it = decks.quest_decks_by_name.find(deck_parsed.first); - if(deck_it == decks.quest_decks_by_name.end() || deck_it->second->effect == Effect::none) - { - std::cout << "WARNING: The deck '" << deck_parsed.first << "' has no battleground effect! Are you sure it's a quest deck?\n"; - continue; - } - enum Effect this_effect = deck_it->second->effect; - if(effect != Effect::none && this_effect != effect) - { - std::cout << "ERROR: Inconsistent effects! Had " << effect << ", now have " << this_effect << "\n"; - return(7); - } - effect = this_effect; - } - } else if(strcmp(argv[argIndex], "-r") == 0) { ordered = true; @@ -1154,8 +1115,6 @@ int main(int argc, char** argv) } } - modify_cards(cards, effect); - Deck* att_deck{nullptr}; try { @@ -1186,10 +1145,12 @@ int main(int argc, char** argv) std::cerr << "No!" << std::endl; att_deck->strategy = DeckStrategy::ordered; } - std::cout << "Attacker:" << std::endl; - std::cout << att_deck->long_description() << std::endl; - std::cout << "Defender:" << std::endl; + boost::optional quest_effect; + if(effect != Effect::none) + { + quest_effect = effect; + } std::vector def_decks; std::vector def_decks_factors; for(auto deck_parsed: deck_list_parsed) @@ -1210,9 +1171,6 @@ int main(int argc, char** argv) print_available_decks(decks, true); return(5); } - def_decks.push_back(def_deck); - def_decks_factors.push_back(deck_parsed.second); - std::cout << def_deck->long_description() << std::endl; if(achievement.id > 0) { if(def_deck->decktype != DeckType::mission) @@ -1222,10 +1180,33 @@ int main(int argc, char** argv) } if(!achievement.mission_condition.check(def_deck->id)) { - std::cerr << "Error: Wrong mission [" << deck_parsed.first << "] for achievement: " << achievement.mission_condition.str() << "." << std::endl; + std::cerr << "Error: Wrong mission [" << deck_parsed.first << "] for achievement." << std::endl; return(1); } } + // Set quest effect: + Effect this_effect = def_deck->effect; + if(this_effect != Effect::none) + { + if(quest_effect && *quest_effect != this_effect) + { + std::cout << "ERROR: Inconsistent effects: " << effect_names[*quest_effect] << " and " << effect_names[this_effect] << ".\n"; + return(7); + } + quest_effect = this_effect; + } + def_decks.push_back(def_deck); + def_decks_factors.push_back(deck_parsed.second); + } + + effect = quest_effect.get_value_or(effect); + modify_cards(cards, effect); + std::cout << "Attacker:" << std::endl; + std::cout << att_deck->long_description() << std::endl; + std::cout << "Defender:" << std::endl; + for(auto def_deck: def_decks) + { + std::cout << def_deck->long_description() << std::endl; } Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); diff --git a/xml.cpp b/xml.cpp index 904c3c23..850960d5 100644 --- a/xml.cpp +++ b/xml.cpp @@ -77,7 +77,7 @@ void handle_skill(xml_node<>* node, Card* card) if(normal) {card->add_skill(skill, skill_value(node), skill_faction(node), all); } } //------------------------------------------------------------------------------ -void load_decks_xml(Decks& decks, Cards& cards) +void load_decks_xml(Decks& decks, const Cards& cards) { try { @@ -158,6 +158,7 @@ void read_cards(Cards& cards) if(strcmp(card->name(), "unit") == 0) { xml_node<>* id_node(card->first_node("id")); + assert(id_node); unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(card->first_node("name")); xml_node<>* attack_node(card->first_node("attack")); @@ -326,7 +327,51 @@ void read_cards(Cards& cards) // std::cout << "nb mission cards: " << cards.mission_cards.size() << "\n"; } //------------------------------------------------------------------------------ -void read_missions(Decks& decks, Cards& cards, std::string filename) +Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::DeckType decktype, unsigned id, std::string& deck_name) +{ + xml_node<>* commander_node(node->first_node("commander")); + const Card* commander_card{cards.by_id(atoi(commander_node->value()))}; + xml_node<>* deck_node(node->first_node("deck")); + xml_node<>* always_node{deck_node->first_node("always_include")}; + std::vector always_cards; + for(xml_node<>* card_node = (always_node ? always_node : deck_node)->first_node("card"); + card_node; + card_node = card_node->next_sibling("card")) + { + unsigned card_id{static_cast(atoi(card_node->value()))}; + always_cards.push_back(cards.by_id(card_id)); + } + std::vector > > some_cards; + for(xml_node<>* pool_node = deck_node->first_node("card_pool"); + pool_node; + pool_node = pool_node->next_sibling("card_pool")) + { + unsigned num_cards_from_pool{static_cast(atoi(pool_node->first_attribute("amount")->value()))}; + std::vector cards_from_pool; + + for(xml_node<>* card_node = pool_node->first_node("card"); + card_node; + card_node = card_node->next_sibling("card")) + { + unsigned card_id{static_cast(atoi(card_node->value()))}; + // Special case Arctis Vanguard id 0 because of stray ` character. + // Don't continue on other raids because I want to be notified of other errors. + if(card_id == 0 && decktype == DeckType::raid && id == 1) + { + continue; + } + cards_from_pool.push_back(cards.by_id(card_id)); + } + some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); + } + decks.decks.push_back(Deck{decktype, id, deck_name}); + Deck* deck = &decks.decks.back(); + deck->set(commander_card, always_cards, some_cards); + decks.by_name[deck_name] = deck; + return deck; +} +//------------------------------------------------------------------------------ +void read_missions(Decks& decks, const Cards& cards, std::string filename) { std::vector buffer; xml_document<> doc; @@ -338,38 +383,22 @@ void read_missions(Decks& decks, Cards& cards, std::string filename) return; } - for(xml_node<>* mission_node = root->first_node(); + for(xml_node<>* mission_node = root->first_node("mission"); mission_node; - mission_node = mission_node->next_sibling()) + mission_node = mission_node->next_sibling("mission")) { - if(strcmp(mission_node->name(), "mission") == 0) - { - std::vector card_ids; - xml_node<>* id_node(mission_node->first_node("id")); - assert(id_node); - unsigned id(id_node ? atoi(id_node->value()) : 0); - xml_node<>* name_node(mission_node->first_node("name")); - std::string deck_name{name_node->value()}; - xml_node<>* commander_node(mission_node->first_node("commander")); - card_ids.push_back(atoi(commander_node->value())); - xml_node<>* deck_node(mission_node->first_node("deck")); - for(xml_node<>* card_node = deck_node->first_node(); - card_node; - card_node = card_node->next_sibling()) - { - unsigned card_id{static_cast(atoi(card_node->value()))}; - card_ids.push_back(card_id); - } - decks.mission_decks.push_back(Deck{DeckType::mission, id, deck_name}); - Deck* deck = &decks.mission_decks.back(); - deck->set(cards, card_ids); - decks.mission_decks_by_id[id] = deck; - decks.mission_decks_by_name[deck_name] = deck; - } + std::vector card_ids; + xml_node<>* id_node(mission_node->first_node("id")); + assert(id_node); + unsigned id(id_node ? atoi(id_node->value()) : 0); + xml_node<>* name_node(mission_node->first_node("name")); + std::string deck_name{name_node->value()}; + read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name); + decks.mission_names_by_id[id] = deck_name; } } //------------------------------------------------------------------------------ -void read_raids(Decks& decks, Cards& cards, std::string filename) +void read_raids(Decks& decks, const Cards& cards, std::string filename) { std::vector buffer; xml_document<> doc; @@ -381,68 +410,20 @@ void read_raids(Decks& decks, Cards& cards, std::string filename) return; } - for(xml_node<>* raid_node = root->first_node(); + for(xml_node<>* raid_node = root->first_node("raid"); raid_node; - raid_node = raid_node->next_sibling()) + raid_node = raid_node->next_sibling("raid")) { - if(strcmp(raid_node->name(), "raid") == 0) - { - std::vector always_cards; - std::vector > > some_cards; - xml_node<>* id_node(raid_node->first_node("id")); - assert(id_node); - unsigned id(id_node ? atoi(id_node->value()) : 0); - xml_node<>* name_node(raid_node->first_node("name")); - std::string deck_name{name_node->value()}; - xml_node<>* commander_node(raid_node->first_node("commander")); - const Card* commander_card{cards.by_id(atoi(commander_node->value()))}; - xml_node<>* deck_node(raid_node->first_node("deck")); - xml_node<>* always_node{deck_node->first_node("always_include")}; - if(always_node) - { - for(xml_node<>* card_node = always_node->first_node(); - card_node; - card_node = card_node->next_sibling()) - { - unsigned card_id{static_cast(atoi(card_node->value()))}; - always_cards.push_back(cards.by_id(card_id)); - } - } - for(xml_node<>* pool_node = always_node->next_sibling(); - pool_node; - pool_node = pool_node->next_sibling()) - { - if(strcmp(pool_node->name(), "card_pool") == 0) - { - unsigned num_cards_from_pool{static_cast(atoi(pool_node->first_attribute("amount")->value()))}; - std::vector cards_from_pool; - - for(xml_node<>* card_node = pool_node->first_node(); - card_node; - card_node = card_node->next_sibling()) - { - unsigned card_id{static_cast(atoi(card_node->value()))}; - // Special case Arctis Vanguard id 0 because of stray ` character. - // Don't continue on other raids because I want to be notified of other errors. - if(card_id == 0 && id == 1) - { - continue; - } - cards_from_pool.push_back(cards.by_id(card_id)); - } - some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); - } - } - decks.raid_decks.push_back(Deck{DeckType::raid, id, deck_name}); - Deck* deck = &decks.raid_decks.back(); - deck->set(commander_card, always_cards, some_cards); - decks.raid_decks_by_id[id] = deck; - decks.raid_decks_by_name[deck_name] = deck; - } + xml_node<>* id_node(raid_node->first_node("id")); + assert(id_node); + unsigned id(id_node ? atoi(id_node->value()) : 0); + xml_node<>* name_node(raid_node->first_node("name")); + std::string deck_name{name_node->value()}; + read_deck(decks, cards, raid_node, DeckType::raid, id, deck_name); } } //------------------------------------------------------------------------------ -void read_quests(Decks& decks, Cards& cards, std::string filename) +void read_quests(Decks& decks, const Cards& cards, std::string filename) { std::vector buffer; xml_document<> doc; @@ -457,48 +438,18 @@ void read_quests(Decks& decks, Cards& cards, std::string filename) // Seems always_cards is empty for all quests. std::vector always_cards; - for(xml_node<>* quest_node = root->first_node(); + for(xml_node<>* quest_node = root->first_node("step"); quest_node; - quest_node = quest_node->next_sibling()) + quest_node = quest_node->next_sibling("step")) { - if(strcmp(quest_node->name(), "step") == 0) - { - std::vector > > some_cards; - xml_node<>* id_node(quest_node->first_node("id")); - assert(id_node); - unsigned id(id_node ? atoi(id_node->value()) : 0); - std::string deck_name{"Step " + std::string{id_node->value()}}; - xml_node<>* commander_node(quest_node->first_node("commander")); - const Card* commander_card{cards.by_id(atoi(commander_node->value()))}; - xml_node<>* battleground_id_node(quest_node->first_node("battleground_id")); - int battleground_id(battleground_id_node ? atoi(battleground_id_node->value()) : -1); - xml_node<>* deck_node(quest_node->first_node("deck")); - for(xml_node<>* pool_node = deck_node->first_node("card_pool"); - pool_node; - pool_node = pool_node->next_sibling()) - { - if(strcmp(pool_node->name(), "card_pool") == 0) - { - unsigned num_cards_from_pool{static_cast(atoi(pool_node->first_attribute("amount")->value()))}; - std::vector cards_from_pool; - - for(xml_node<>* card_node = pool_node->first_node(); - card_node; - card_node = card_node->next_sibling()) - { - unsigned card_id{static_cast(atoi(card_node->value()))}; - cards_from_pool.push_back(cards.by_id(card_id)); - } - some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); - } - } - decks.quest_decks.push_back(Deck{DeckType::quest, id, deck_name}); - Deck* deck = &decks.quest_decks.back(); - deck->effect = static_cast(battleground_id); - deck->set(commander_card, always_cards, some_cards); - decks.quest_decks_by_id[id] = deck; - decks.quest_decks_by_name[deck_name] = deck; - } + xml_node<>* id_node(quest_node->first_node("id")); + assert(id_node); + unsigned id(id_node ? atoi(id_node->value()) : 0); + std::string deck_name{"Step " + std::string{id_node->value()}}; + xml_node<>* battleground_id_node(quest_node->first_node("battleground_id")); + int battleground_id(battleground_id_node ? atoi(battleground_id_node->value()) : -1); + Deck* deck = read_deck(decks, cards, quest_node, DeckType::quest, id, deck_name); + deck->effect = static_cast(battleground_id); } } //------------------------------------------------------------------------------ @@ -513,7 +464,7 @@ Comparator get_comparator(xml_node<>* node, Comparator default_comparator) else { throw std::runtime_error(std::string("Not implemented: compare=\"") + compare->value() + "\""); } } -void read_achievement(Decks& decks, Cards& cards, Achievement& achievement, const char* achievement_id_name, std::string filename/* = "achievements.xml"*/) +void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement, const char* achievement_id_name, std::string filename/* = "achievements.xml"*/) { std::vector buffer; xml_document<> doc; @@ -552,7 +503,7 @@ void read_achievement(Decks& decks, Cards& cards, Achievement& achievement, cons if(strcmp(mission_id->value(), "*") != 0) { achievement.mission_condition.init(atoi(mission_id->value()), get_comparator(type_node, equal)); - std::cout << " Mission" << achievement.mission_condition.str() << " (" << decks.mission_decks_by_id[atoi(mission_id->value())]->name << ") and win" << std::endl; + std::cout << " Mission" << achievement.mission_condition.str() << " (" << decks.mission_names_by_id[atoi(mission_id->value())] << ") and win" << std::endl; } for (xml_node<>* req_node = achievement_node->first_node("req"); req_node; diff --git a/xml.h b/xml.h index 80acdb51..aef08580 100644 --- a/xml.h +++ b/xml.h @@ -7,11 +7,11 @@ class Cards; class Decks; class Achievement; -void load_decks_xml(Decks& decks, Cards& cards); +void load_decks_xml(Decks& decks, const Cards& cards); void read_cards(Cards& cards); -void read_missions(Decks& decks, Cards& cards, std::string filename); -void read_raids(Decks& decks, Cards& cards, std::string filename); -void read_quests(Decks& decks, Cards& cards, std::string filename); -void read_achievement(Decks& decks, Cards& cards, Achievement& achievement, const char* achievement_name, std::string filename="achievements.xml"); +void read_missions(Decks& decks, const Cards& cards, std::string filename); +void read_raids(Decks& decks, const Cards& cards, std::string filename); +void read_quests(Decks& decks, const Cards& cards, std::string filename); +void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement, const char* achievement_name, std::string filename="achievements.xml"); #endif From a7228f0e424075f4d50b5b915cd6fb1cab00d029 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 20 Feb 2013 12:28:29 +0800 Subject: [PATCH 077/406] More flexible card format. --- read.cpp | 214 ++++++++++++++++++++------------------------ sim.cpp | 2 +- tyrant_optimize.cpp | 4 +- xml.cpp | 2 +- 4 files changed, 99 insertions(+), 123 deletions(-) diff --git a/read.cpp b/read.cpp index 7faf0aab..610a0a7e 100644 --- a/read.cpp +++ b/read.cpp @@ -2,12 +2,12 @@ #include #include -#include #include #include #include #include #include +#include #include "card.h" #include "cards.h" @@ -76,7 +76,7 @@ void load_decks(Decks& decks, Cards& cards) } catch(const std::runtime_error& e) { - std::cout << "Exception while loading custom decks: " << e.what() << "\n"; + std::cerr << "Exception while loading custom decks: " << e.what() << "\n"; } } } @@ -128,7 +128,7 @@ template Iterator recede_until(Iterator it, return(it_beg); } -template Iterator read_token(Iterator it, Iterator it_end, Functor f, boost::optional& token) +template Iterator read_token(Iterator it, Iterator it_end, Functor f, Token& token) { Iterator token_start = advance_until(it, it_end, [](const char& c){return(c != ' ');}); Iterator token_end_after_spaces = advance_until(token_start, it_end, f); @@ -136,20 +136,53 @@ template Iterator read_toke { Iterator token_end = recede_until(token_end_after_spaces, token_start, [](const char& c){return(c != ' ');}); token = boost::lexical_cast(std::string{token_start, token_end}); - return(token_end_after_spaces); } return(token_end_after_spaces); } +void parse_card_spec(Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num) +{ + auto card_spec_iter = card_spec.begin(); + card_id = 0; + card_num = 1; + std::string card_name; + card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c=='#' || c=='(' || c=='\r');}, card_name); + if(card_name.empty()) + { + throw std::runtime_error("no card name"); + } + // If card name is not found, try find card id quoted in '[]' in name, ignoring other characters. + auto card_it = cards.player_cards_by_name.find(card_name); + auto card_id_iter = advance_until(card_name.begin(), card_name.end(), [](char c){return(c=='[');}); + if(card_it != cards.player_cards_by_name.end()) + { + card_id = card_it->second->m_id; + } + else if(card_id_iter != card_name.end()) + { + ++ card_id_iter; + card_id_iter = read_token(card_id_iter, card_name.end(), [](char c){return(c==']');}, card_id); + } + if(card_spec_iter != card_spec.end() && (*card_spec_iter == '#' || *card_spec_iter == '(')) + { + ++card_spec_iter; + card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c < '0' || c > '9');}, card_num); + } + if(card_id == 0) + { + throw std::runtime_error("card not found"); + } +} + // Error codes: // 2 -> file not readable // 3 -> error while parsing file unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) { - std::ifstream decks_file(filename.c_str()); + std::ifstream decks_file(filename); if(!decks_file.is_open()) { - std::cerr << "File " << filename << " could not be opened\n"; + std::cerr << "Error: Custom deck file " << filename << " could not be opened\n"; return(2); } unsigned num_line(0); @@ -162,99 +195,57 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) std::string deck_string; getline(decks_file, deck_string); ++num_line; - if(deck_string.size() > 0) + if(deck_string.size() == 0 || strncmp(deck_string.c_str(), "//", 2) == 0) { - if(strncmp(deck_string.c_str(), "//", 2) == 0) - { - continue; - } - boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; - auto token_iter = deck_tokens.begin(); - boost::optional deck_name; - if(token_iter != deck_tokens.end()) + continue; + } + boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; + auto token_iter = deck_tokens.begin(); + std::string deck_name; + if(token_iter == deck_tokens.end()) + { + std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ", could not read the deck name.\n"; + continue; + } + read_token(token_iter->begin(), token_iter->end(), [](char c){return(false);}, deck_name); + if(deck_name.empty()) + { + std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ", could not read the deck name.\n"; + continue; + } + auto deck_iter = decks.by_name.find(deck_name); + if(deck_iter != decks.by_name.end()) + { + std::cerr << "Warning in custom deck file " << filename << " at line " << num_line << ", name conflicts, overrides " << deck_iter->second->short_description() << std::endl; + } + ++token_iter; + for(; token_iter != deck_tokens.end(); ++token_iter) + { + std::string card_spec(*token_iter); + try { - read_token(token_iter->begin(), token_iter->end(), [](char c){return(false);}, deck_name); - if(!deck_name || (*deck_name).size() == 0) + unsigned card_id{0}; + unsigned card_num{1}; + parse_card_spec(cards, card_spec, card_id, card_num); + for(unsigned i(0); i < card_num; ++i) { - std::cerr << "Error in file " << filename << " at line " << num_line << ", could not read the deck name.\n"; - continue; + card_ids.push_back(card_id); } } - else - { - std::cerr << "Error in file " << filename << " at line " << num_line << ", could not read the deck name.\n"; - continue; - } - auto deck_iter = decks.by_name.find(*deck_name); - if(deck_iter != decks.by_name.end()) - { - std::cerr << "Warning in file " << filename << " at line " << num_line << ", name conflicts, overrides " << deck_iter->second->short_description() << std::endl; - } - ++token_iter; - for(; token_iter != deck_tokens.end(); ++token_iter) + catch(std::exception& e) { - std::string card_spec(*token_iter); - try - { - auto card_spec_iter = card_spec.begin(); - boost::optional card_name; - card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c=='[' || c=='#' || c=='\r');}, card_name); - if(!card_name) - { - std::cerr << "Error in file " << filename << " at line " << num_line << " while parsing card " << card_spec << " in deck " << deck_name << "\n"; - break; - } - else - { - boost::optional card_id; - if(*card_spec_iter == '[') - { - ++card_spec_iter; - card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c==']');}, card_id); - card_spec_iter = advance_until(card_spec_iter, card_spec.end(), [](char c){return(c!=' ');}); - } - boost::optional card_num; - if(*card_spec_iter == '#') - { - ++card_spec_iter; - card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c < '0' || c > '9');}, card_num); - } - unsigned resolved_id{card_id ? *card_id : 0}; - if(resolved_id == 0) - { - auto card_it = cards.player_cards_by_name.find(*card_name); - if(card_it != cards.player_cards_by_name.end()) - { - resolved_id = card_it->second->m_id; - } - else - { - std::cerr << "Error in file " << filename << " at line " << num_line << " while parsing card " << card_spec << " in deck " << *deck_name << ": card not found\n"; - break; - } - } - for(unsigned i(0); i < (card_num ? *card_num : 1); ++i) - { - card_ids.push_back(resolved_id); - } - } - } - catch(boost::bad_lexical_cast e) - { - std::cerr << "Error in file " << filename << " at line " << num_line << " while parsing card " << card_spec << " in deck " << deck_name << "\n"; - } + std::cerr << "Error in custom deck file " << filename << " at line " << num_line << " while parsing card '" << card_spec << "' in deck " << deck_name << ": " << e.what() << "\n"; } - decks.decks.push_back(Deck{DeckType::custom_deck, num_line, *deck_name}); - Deck* deck = &decks.decks.back(); - deck->set(cards, card_ids); - decks.by_name[*deck_name] = deck; } + decks.decks.push_back(Deck{DeckType::custom_deck, num_line, deck_name}); + Deck* deck = &decks.decks.back(); + deck->set(cards, card_ids); + decks.by_name[deck_name] = deck; } } catch (std::ifstream::failure e) { - std::cerr << "Exception while parsing the file " << filename << " (badbit is set).\n"; - e.what(); + std::cerr << "Exception while parsing the custom deck file " << filename << " (badbit is set): " << e.what() << ".\n"; return(3); } return(0); @@ -263,50 +254,37 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) void read_owned_cards(Cards& cards, std::map& owned_cards, const char *filename) { std::ifstream owned_file{filename}; - if(!owned_file.good()) { - std::cerr << "Warning: The file '" << filename << "' does not exist. This will result in you not owning any cards.\n"; + std::cerr << "Warning: Owned cards file '" << filename << "' does not exist.\n"; return; } - - std::string owned_str{(std::istreambuf_iterator(owned_file)), std::istreambuf_iterator()}; - boost::tokenizer > tok{owned_str, boost::char_delimiters_separator{false, "()\n", ""}}; - for(boost::tokenizer >::iterator beg=tok.begin(); beg!=tok.end();++beg) + unsigned num_line(0); + while(owned_file && !owned_file.eof()) { - std::string name{*beg}; - Card *card = NULL; - if(name[0] == '[') + std::string card_spec; + getline(owned_file, card_spec); + ++num_line; + if(card_spec.size() == 0 || strncmp(card_spec.c_str(), "//", 2) == 0) { - auto card_itr = cards.cards_by_id.find(atoi(name.substr(1).c_str())); - if(card_itr != cards.cards_by_id.end()) - { - card = card_itr->second; - } + continue; } - else + try { - auto pos = name.find(','); + // Remove ',' from card names + auto pos = card_spec.find(','); if(pos != std::string::npos) { - name.erase(pos, 1); - } - auto card_itr = cards.player_cards_by_name.find(name); - if(card_itr != cards.player_cards_by_name.end()) - { - card = card_itr->second; + card_spec.erase(pos, 1); } + unsigned card_id{0}; + unsigned card_num{1}; + parse_card_spec(cards, card_spec, card_id, card_num); + owned_cards[card_id] = card_num; } - ++beg; - assert(beg != tok.end()); - unsigned num{static_cast(atoi((*beg).c_str()))}; - if(card) - { - owned_cards[card->m_id] = num; - } - else + catch(std::exception& e) { - std::cerr << "Error in file ownedcards.txt, the card \"" << name << "\" does not seem to be a valid card.\n"; + std::cerr << "Error in owned cards file " << filename << " at line " << num_line << " while parsing card '" << card_spec << "': " << e.what() << "\n"; } } } diff --git a/sim.cpp b/sim.cpp index b9ee3181..37d4feae 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1671,7 +1671,7 @@ inline std::vector& skill_targets_allied_structure(Field* fd, CardS template std::vector& skill_targets(Field* fd, CardStatus* src_status) { - std::cout << "skill_targets: Error: no specialization for " << skill_names[skill] << "\n"; + std::cerr << "skill_targets: Error: no specialization for " << skill_names[skill] << "\n"; throw; } diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index e96ff805..b11b08f0 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1141,8 +1141,6 @@ int main(int argc, char** argv) } if(ordered) { - if(att_deck == nullptr) - std::cerr << "No!" << std::endl; att_deck->strategy = DeckStrategy::ordered; } @@ -1190,7 +1188,7 @@ int main(int argc, char** argv) { if(quest_effect && *quest_effect != this_effect) { - std::cout << "ERROR: Inconsistent effects: " << effect_names[*quest_effect] << " and " << effect_names[this_effect] << ".\n"; + std::cerr << "Error: Inconsistent effects: " << effect_names[*quest_effect] << " and " << effect_names[this_effect] << ".\n"; return(7); } quest_effect = this_effect; diff --git a/xml.cpp b/xml.cpp index 850960d5..f6f870cd 100644 --- a/xml.cpp +++ b/xml.cpp @@ -131,7 +131,7 @@ void parse_file(const char* filename, std::vector& buffer, xml_document<>& } catch(rapidxml::parse_error& e) { - std::cout << "Parse error exception.\n"; + std::cerr << "Parse error exception.\n"; std::cout << e.what(); throw(e); } From ea2a9a204983c54bc296596a1b5aeecd2f1f105e Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 20 Feb 2013 16:23:02 +0800 Subject: [PATCH 078/406] Support card id > 4000. --- deck.cpp | 31 +++++++++++++++++++++++++++++-- read.cpp | 48 ++++++++++++++++++++++++++++-------------------- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/deck.cpp b/deck.cpp index 311f445e..7a0f997b 100644 --- a/deck.cpp +++ b/deck.cpp @@ -33,10 +33,37 @@ std::string deck_hash(const Card* commander, const std::vector& car std::stringstream ios; ios << base64[commander->m_id / 64]; ios << base64[commander->m_id % 64]; + unsigned last_id = 0; + unsigned num_repeat = 0; for(const Card* card: cards) { - ios << base64[card->m_id / 64]; - ios << base64[card->m_id % 64]; + unsigned card_id(card->m_id); + if(card_id == last_id) + { + ++ num_repeat; + } + else + { + if(num_repeat > 1) + { + ios << base64[(num_repeat + 4000) / 64]; + ios << base64[(num_repeat + 4000) % 64]; + } + last_id = card_id; + num_repeat = 1; + if(card_id > 4000) + { + ios << '-'; + card_id -= 4000; + } + ios << base64[card_id / 64]; + ios << base64[card_id % 64]; + } + } + if(num_repeat > 1) + { + ios << base64[(num_repeat + 4000) / 64]; + ios << base64[(num_repeat + 4000) % 64]; } return ios.str(); } diff --git a/read.cpp b/read.cpp index 610a0a7e..3df43adc 100644 --- a/read.cpp +++ b/read.cpp @@ -19,27 +19,39 @@ const char* base64_chars = "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; -// Converts `pairs' pairs of cards in `hash' to a deck. +// Converts cards in `hash' to a deck. // Stores resulting card IDs in `ids'. -bool hash_to_ids(const char* hash, size_t pairs, - std::vector& ids) +bool hash_to_ids(const char* hash, std::vector& ids) { unsigned int last_id = 0; + const char* pc = hash; - for (size_t i = 0; i < pairs; ++i) + while(*pc) { - const char* p0 = strchr(base64_chars, hash[2 * i]); - const char* p1 = strchr(base64_chars, hash[2 * i + 1]); + unsigned id_plus = 0; + if(*pc == '-') + { + ++ pc; + id_plus = 4000; + } + if(!*pc || !*(pc + 1)) + { + return(false); + } + const char* p0 = strchr(base64_chars, *pc); + const char* p1 = strchr(base64_chars, *(pc + 1)); if (!p0 || !p1) { return(false); } + pc += 2; size_t index0 = p0 - base64_chars; size_t index1 = p1 - base64_chars; unsigned int id = (index0 << 6) + index1; - if (id < 4000) + if (id < 4001) { + id += id_plus; ids.push_back(id); last_id = id; } @@ -57,9 +69,7 @@ bool hash_to_ids(const char* hash, size_t pairs, Deck* hash_to_deck(const char* hash, const Cards& cards) { std::vector ids; - if(strlen(hash) % 2 > 0) { return(nullptr); } - size_t pairs = strlen(hash) / 2; - if(!hash_to_ids(hash, pairs, ids)) { return(nullptr); } + if(!hash_to_ids(hash, ids)) { return(nullptr); } Deck* deck = new Deck{}; deck->set(cards, ids); @@ -70,14 +80,7 @@ void load_decks(Decks& decks, Cards& cards) { if(boost::filesystem::exists("Custom.txt")) { - try - { - read_custom_decks(decks, cards, "Custom.txt"); - } - catch(const std::runtime_error& e) - { - std::cerr << "Exception while loading custom decks: " << e.what() << "\n"; - } + read_custom_decks(decks, cards, "Custom.txt"); } } @@ -243,9 +246,14 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) decks.by_name[deck_name] = deck; } } - catch (std::ifstream::failure e) + catch (std::exception& e) { - std::cerr << "Exception while parsing the custom deck file " << filename << " (badbit is set): " << e.what() << ".\n"; + std::cerr << "Exception while parsing the custom deck file " << filename; + if(num_line > 0) + { + std::cerr << " at line " << num_line; + } + std::cerr << ": " << e.what() << ".\n"; return(3); } return(0); From ee48492df896e80a7981bdcdd839f07412be3b71 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 21 Feb 2013 09:21:51 +0800 Subject: [PATCH 079/406] Fix bug: turnlimit. --- sim.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index 37d4feae..7e37b873 100644 --- a/sim.cpp +++ b/sim.cpp @@ -622,11 +622,15 @@ unsigned play(Field* fd) // Evaluate skills evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); // Attack - if(!current_status.m_immobilized && current_status.m_hp > 0) + if(!fd->end && !current_status.m_immobilized && current_status.m_hp > 0) { attack_phase(fd); } } + if(fd->end) + { + break; + } _DEBUG_MSG("TURN %u ends for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); std::swap(fd->tapi, fd->tipi); std::swap(fd->tap, fd->tip); @@ -642,7 +646,7 @@ unsigned play(Field* fd) print_achievement_results(fd); } // defender wins - if(fd->players[0]->commander.m_hp == 0 || (!win_tie && fd->turn > turn_limit)) + if(fd->players[0]->commander.m_hp == 0) { _DEBUG_MSG("Defender wins.\n"); return(0); @@ -654,7 +658,7 @@ unsigned play(Field* fd) return(0); } // attacker wins - if((fd->players[1]->commander.m_hp == 0 || (win_tie && fd->turn > turn_limit))) + if(fd->players[1]->commander.m_hp == 0) { // ANP: Speedy if last_decision + 10 > turn. // fd->turn has advanced once past the actual turn the battle has ended. @@ -668,6 +672,11 @@ unsigned play(Field* fd) _DEBUG_MSG("Attacker wins.\n"); return(10 + (speedy ? 5 : 0) + fd->points_since_last_decision); } + if (fd->turn > turn_limit) + { + _DEBUG_MSG("Stall after %u turns.\n", turn_limit); + return(win_tie ? 1 : 0); + } // Huh? How did we get here? assert(false); @@ -1062,6 +1071,11 @@ struct PerformAttack if(skill_activate(fd, att_status, def_status)) { attack_damage(); + if(fd->end) + { + // Commander dies? + return; + } } siphon_poison_disease(); on_kill(); From 56621a1624c6dbb1b5c23069100881b37aaf0a76 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 22 Feb 2013 09:42:56 +0800 Subject: [PATCH 080/406] Fix bug: summoned unit blitzs. --- card.h | 6 ++-- sim.cpp | 89 ++++++++++++++++++++++++++++++++++-------------------- tyrant.cpp | 24 +++++++-------- tyrant.h | 2 +- xml.cpp | 2 ++ 5 files changed, 75 insertions(+), 48 deletions(-) diff --git a/card.h b/card.h index f7853a2b..5403bc79 100644 --- a/card.h +++ b/card.h @@ -12,6 +12,7 @@ class Card m_antiair(0), m_armored(0), m_attack(0), + m_base_id(0), m_berserk(0), m_berserk_oa(0), m_blitz(false), @@ -30,10 +31,10 @@ class Card m_fusion(false), m_health(0), m_id(0), - m_base_id(0), m_immobilize(false), m_intercept(false), m_leech(0), + m_legion(0), m_name(""), m_payback(false), m_pierce(0), @@ -69,6 +70,7 @@ class Card unsigned m_antiair; unsigned m_armored; unsigned m_attack; + unsigned m_base_id; // Cards sharing the same name share a unique base id. (for "unique" check) unsigned m_berserk; unsigned m_berserk_oa; bool m_blitz; @@ -87,10 +89,10 @@ class Card bool m_fusion; unsigned m_health; unsigned m_id; - unsigned m_base_id; // Cards sharing the same name share a unique base id. (for "unique" check) bool m_immobilize; bool m_intercept; unsigned m_leech; + unsigned m_legion; std::string m_name; bool m_payback; unsigned m_pierce; diff --git a/sim.cpp b/sim.cpp index 7e37b873..1dd94d79 100644 --- a/sim.cpp +++ b/sim.cpp @@ -362,6 +362,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector if(fd->end) { break; } } } +bool check_and_perform_blitz(Field* fd, CardStatus* src_status); struct PlayCard { const Card* card; @@ -461,10 +462,7 @@ void PlayCard::setStorage() template <> void PlayCard::blitz() { - if(card->m_blitz && fd->tip->assaults.size() > status->m_index && fd->tip->assaults[status->m_index].m_hp > 0 && fd->tip->assaults[status->m_index].m_delay == 0) - { - status->m_blitzing = true; - } + check_and_perform_blitz(fd, status); } // assault template <> @@ -567,6 +565,7 @@ unsigned play(Field* fd) break; } } + // Evaluate commander fd->current_phase = Field::commander_phase; evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); @@ -871,7 +870,7 @@ void check_regeneration(Field* fd) CardStatus& status = *fd->killed_with_regen[i]; if(skill_activate(fd, &status, nullptr)) { - _DEBUG_MSG("%s regenerates, hp 0 -> %u\n", status_description(&status).c_str(), status.m_card->m_health); + _DEBUG_MSG("%s regenerates with %u health\n", status_description(&status).c_str(), status.m_card->m_health); add_hp(fd, &status, status.m_card->m_regenerate); } } @@ -1054,7 +1053,7 @@ struct PerformAttack // check regeneration if(att_dmg > 0 && def_status->m_card->m_flying && skill_activate(fd, def_status, att_status)) { - _DEBUG_MSG("%s dodges with flying\n", status_description(def_status).c_str()); + _DEBUG_MSG("%s dodges with Flying\n", status_description(def_status).c_str()); return; } @@ -1273,6 +1272,18 @@ void PerformAttack::crush_leech() } // General attack phase by the currently evaluated assault, taking into accounts exotic stuff such as flurry,swipe,etc. +void attack_commander(Field* fd, CardStatus* att_status) +{ + CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall + if(def_status != nullptr) + { + PerformAttack{fd, att_status, def_status}.op(); + } + else + { + PerformAttack{fd, att_status, &fd->tip->commander}.op(); + } +} void attack_phase(Field* fd) { CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card @@ -1281,7 +1292,7 @@ void attack_phase(Field* fd) unsigned num_attacks(1); if(att_status->m_card->m_flurry > 0 && skill_activate(fd, att_status, nullptr)) { - _DEBUG_MSG("%s activates flurry\n", status_description(att_status).c_str()); + _DEBUG_MSG("%s activates Flurry\n", status_description(att_status).c_str()); num_attacks += att_status->m_card->m_flurry; } for(unsigned attack_index(0); attack_index < num_attacks && !att_status->m_jammed && !att_status->m_frozen && att_status->m_hp > 0 && fd->tip->commander.m_hp > 0; ++attack_index) @@ -1289,6 +1300,8 @@ void attack_phase(Field* fd) // 3 possibilities: // - 1. attack against the assault in front // - 2. swipe attack the assault in front and adjacent assaults if any + // * Attack Commander/wall if opposing Assault is already dead before Swipe finishes (Swipe will attempt to attack the third Assault) + // See http://www.kongregate.com/forums/65-tyrant/topics/289416?page=22#posts-6861970 // - 3. attack against the commander or walls (if there is no assault or if the attacker has the fear attribute) // Check if attack mode is 1. or 2. (there is a living assault card in front, and no fear) if(alive_assault(def_assaults, fd->current_ci) && !(att_status->m_card->m_fear && skill_activate(fd, att_status, nullptr))) @@ -1302,19 +1315,27 @@ void attack_phase(Field* fd) else { // perform_skill_swipe - _DEBUG_MSG("%s swipes\n", status_description(att_status).c_str()); + _DEBUG_MSG("%s activates Swipe\n", status_description(att_status).c_str()); // attack the card on the left if(fd->current_ci > 0 && alive_assault(def_assaults, fd->current_ci - 1)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(); } + if(fd->end) + { return; } // stille alive? attack the card in front - if(fd->tip->commander.m_hp > 0 && att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci)) + if(att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); } + else + { + attack_commander(fd, att_status); + } + if(fd->end) + { return; } // still alive? attack the card on the right - if(fd->tip->commander.m_hp > 0 && att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci + 1)) + if(!fd->end && att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci + 1)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(); } @@ -1323,15 +1344,7 @@ void attack_phase(Field* fd) // attack mode 3. else { - CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall - if(def_status != nullptr) - { - PerformAttack{fd, att_status, def_status}.op(); - } - else - { - PerformAttack{fd, att_status, &fd->tip->commander}.op(); - } + attack_commander(fd, att_status); } } } @@ -1804,12 +1817,23 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ return(false); } +bool check_and_perform_blitz(Field* fd, CardStatus* src_status) +{ + if(skill_activate(fd, src_status, nullptr)) + { + _DEBUG_MSG("%s activates Blitz opposing %s\n", status_description(src_status).c_str(), status_description(&fd->tip->assaults[src_status->m_index]).c_str()); + src_status->m_blitzing = true; + return(true); + } + return(false); +} + bool check_and_perform_recharge(Field* fd, CardStatus* src_status) { if(skill_activate(fd, src_status, nullptr)) { // perform_skill_recharge - _DEBUG_MSG("%s recharges\n", status_description(src_status).c_str()); + _DEBUG_MSG("%s activates Recharge\n", status_description(src_status).c_str()); fd->tap->deck->place_at_bottom(src_status->m_card); return(true); } @@ -1972,9 +1996,9 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) card_status.m_summoned = true; _DEBUG_MSG("Summon %s %u [%s]\n", cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd, summoned).c_str()); prepend_skills(fd, &card_status); - if(card_status.m_card->m_blitz && skill_activate(fd, src_status, nullptr)) + if(card_status.m_card->m_blitz) { - card_status.m_blitzing = true; + check_and_perform_blitz(fd, &card_status); } if(summoned->m_type == CardType::assault) { @@ -2028,18 +2052,17 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) } for(auto skill: c->m_card->m_skills) { - if(src_status && src_status->m_card->m_type == CardType::assault && src_status->m_hp == 0) + if(src_status->m_card->m_type == CardType::assault && src_status->m_hp == 0) { break; } - if(std::get<0>(skill) != mimic && - (std::get<0>(skill) != supply || (src_status && src_status->m_card->m_type == CardType::assault))) - { - SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill)); - _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd, skill).c_str()); - fd->skill_queue.emplace_back(src_status, src_status && src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); - resolve_skill(fd); - if(fd->end) { break; } - check_regeneration(fd); - } + if(std::get<0>(skill) == mimic || + (std::get<0>(skill) == supply && src_status->m_card->m_type != CardType::assault)) + { continue; } + SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill)); + _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd, skill).c_str()); + fd->skill_queue.emplace_back(src_status, src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); + resolve_skill(fd); + if(fd->end) { break; } + check_regeneration(fd); } } //------------------------------------------------------------------------------ diff --git a/tyrant.cpp b/tyrant.cpp index dc92a606..418f5b7d 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -7,24 +7,24 @@ const std::string faction_names[Faction::num_factions] = std::string skill_names[Skill::num_skills] = { - // Activation (including Destroyed): - "augment", "backfire", "chaos", "cleanse", "enfeeble", - "freeze", "heal", "infuse", "jam", - "mimic", "protect", "rally", "recharge", "repair", "rush", "shock", - "siege", "strike", "summon", "supply", + // Activation (Including Destroyed): + "Augment", "Backfire", "Chaos", "Cleanse", "Enfeeble", + "Freeze", "Heal", "Infuse", "Jam", + "Mimic", "Protect", "Rally", "Recharge", "Repair", "Rush", "Shock", + "Siege", "Strike", "Summon", "Supply", "temporary_split", "trigger_regen", - "weaken", + "Weaken", // Combat-Modifier: - "antiair", "burst", "fear", "flurry", "pierce", "swipe", "valor", + "Antiair", "Burst", "Fear", "Flurry", "Pierce", "Swipe", "Valor", // Damage-Dependant: - "berserk", "crush", "disease", "immobilize", "leech", "poison", "siphon", + "Berserk", "Crush", "Disease", "Immobilize", "Leech", "Poison", "Siphon", // Defensive: - "armored", "counter", "emulate", "evade", "flying", "intercept", "payback", "refresh", "regenerate", "tribute", "wall", + "Armored", "Counter", "Emulate", "Evade", "Flying", "Intercept", "Payback", "Refresh", "Regenerate", "Tribute", "Wall", // Triggered: - "blitz", - // Static (ignored): - /* "blizzard", "fusion", "mist", */ + "Blitz", "Legion", + // Static (Ignored): + /* "Blizzard", "Fusion", "Mist", */ // Misc: "0", }; diff --git a/tyrant.h b/tyrant.h index 66e563a5..65f0a9e8 100644 --- a/tyrant.h +++ b/tyrant.h @@ -32,7 +32,7 @@ enum Skill // Defensive: armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, tribute, wall, // Triggered: - blitz, + blitz, legion, // Static, ignored: /* blizzard, fusion, mist, */ // Misc: diff --git a/xml.cpp b/xml.cpp index f6f870cd..bd7d1a13 100644 --- a/xml.cpp +++ b/xml.cpp @@ -245,6 +245,8 @@ void read_cards(Cards& cards) { c->m_intercept = true; } if(strcmp(skill->first_attribute("id")->value(), "leech") == 0) { c->m_leech = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "legion") == 0) + { c->m_legion = atoi(skill->first_attribute("x")->value()); } if(strcmp(skill->first_attribute("id")->value(), "payback") == 0) { c->m_payback = true; } if(strcmp(skill->first_attribute("id")->value(), "pierce") == 0) From 522f1f98bafc44eea25b784c822efaac245572e6 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 22 Feb 2013 14:55:54 +0800 Subject: [PATCH 081/406] Fix bug: backfire damage should not count in points (ANP). Fix bug: condition of 'speedy' in points. Fix bug: blitz. --- sim.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/sim.cpp b/sim.cpp index 1dd94d79..953c87bc 100644 --- a/sim.cpp +++ b/sim.cpp @@ -462,7 +462,10 @@ void PlayCard::setStorage() template <> void PlayCard::blitz() { - check_and_perform_blitz(fd, status); + if(status->m_card->m_blitz) + { + check_and_perform_blitz(fd, status); + } } // assault template <> @@ -659,11 +662,8 @@ unsigned play(Field* fd) // attacker wins if(fd->players[1]->commander.m_hp == 0) { - // ANP: Speedy if last_decision + 10 > turn. - // fd->turn has advanced once past the actual turn the battle has ended. - // So we were speedy if last_decision + 10 > (fd->turn - 1), - // or, equivalently, if last_decision + 10 >= fd->turn. - bool speedy = fd->last_decision_turn + 10 >= fd->turn; + // ANP: Speedy if turn < last_decision + 10. + bool speedy = fd->turn < fd->last_decision_turn + 10; if(fd->points_since_last_decision > 10) { fd->points_since_last_decision = 10; @@ -1003,7 +1003,7 @@ inline bool alive_assault(Storage& assaults, unsigned index) return(assaults.size() > index && assaults[index].m_hp > 0); } -void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg) +void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg, bool count_points) { assert(status.m_hp > 0); assert(status.m_card->m_type == CardType::commander); @@ -1011,7 +1011,7 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg) status.m_hp = safe_minus(status.m_hp, dmg); // ANP: If commander is enemy's, player gets points equal to damage. // Points are awarded for overkill, so it is correct to simply add dmg. - if(status.m_player == 1) + if(count_points && status.m_player == 1) { fd->points_since_last_decision += dmg; } @@ -1203,7 +1203,7 @@ void PerformAttack::immobilize() template<> void PerformAttack::attack_damage() { - remove_commander_hp(fd, *def_status, att_dmg); + remove_commander_hp(fd, *def_status, att_dmg, true); } template<> @@ -1261,7 +1261,7 @@ void PerformAttack::crush_leech() else { _DEBUG_MSG("%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(&fd->tip->commander).c_str(), att_status->m_card->m_crush); - remove_commander_hp(fd, fd->tip->commander, att_status->m_card->m_crush); + remove_commander_hp(fd, fd->tip->commander, att_status->m_card->m_crush, true); } } if(att_status->m_card->m_leech > 0 && skill_activate(fd, att_status, nullptr)) @@ -1484,8 +1484,8 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - // TODO backfire damage counts in ANP? - remove_commander_hp(fd, *c, v); + // backfire damage not count in ANP. + remove_commander_hp(fd, *c, v, false); } template<> @@ -1568,7 +1568,8 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - remove_commander_hp(fd, *c, v); + // shock damage counts in ANP. (if attacker ever has the skill) + remove_commander_hp(fd, *c, v, true); } template<> From 8bbb02074df8dbc8190d98ed904bbe51579bd7d9 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 23 Feb 2013 22:57:50 +0800 Subject: [PATCH 082/406] Add Legion skill. --- sim.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++------ sim.h | 1 + 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/sim.cpp b/sim.cpp index 953c87bc..2ae750bf 100644 --- a/sim.cpp +++ b/sim.cpp @@ -495,6 +495,7 @@ void PlayCard::onPlaySkills() } //------------------------------------------------------------------------------ void turn_start_phase(Field* fd); +void evaluate_legion(Field* fd); void prepend_on_death(Field* fd); bool check_and_perform_refresh(Field* fd, CardStatus* src_status); // return value : (raid points) -> attacker wins, 0 -> defender wins @@ -569,6 +570,10 @@ unsigned play(Field* fd) } } + // Evaluate Legion skill + fd->current_phase = Field::legion_phase; + evaluate_legion(fd); + // Evaluate commander fd->current_phase = Field::commander_phase; evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); @@ -684,6 +689,8 @@ unsigned play(Field* fd) // For the active player, delay == 0 or blitzing; for the inactive player, delay <= 1. inline bool can_act(Field* fd, CardStatus* c) { return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen && (fd->tapi == c->m_player ? c->m_delay == 0 || c->m_blitzing : c->m_delay <= 1)); } +// Can be healed / repaired +inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } // Check if a skill actually activated. template @@ -753,6 +760,10 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(c->m_hp > 0 && !c->m_diseased); } +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ return(can_be_healed(c) || (!c->m_immobilized && can_act(fd, c))); } + template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { @@ -768,9 +779,7 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return(c->m_hp < c->m_card->m_health && !c->m_diseased); -} +{ return(can_be_healed(c)); } template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) @@ -979,6 +988,40 @@ void turn_start_phase(Field* fd) // Regen from poison check_regeneration(fd); } +void evaluate_legion(Field* fd) +{ + // Not subject to Mimic / Emulate / Augment + // Not affected by Jam / Freeze + // Legion-rally effect is prevented by Immobilize + // Honor Infused faction + auto& assaults = fd->tap->assaults; + for(fd->current_ci = 0; fd->current_ci < assaults.size(); ++fd->current_ci) + { + CardStatus* status(&assaults[fd->current_ci]); + if(status->m_card->m_legion > 0) + { + unsigned legion_size{0}; + legion_size += status->m_index > 0 && assaults[status->m_index - 1].m_hp > 0 && assaults[status->m_index - 1].m_faction == status->m_faction; + legion_size += status->m_index + 1 < assaults.size() && assaults[status->m_index + 1].m_hp > 0 && assaults[status->m_index + 1].m_faction == status->m_faction; + if(legion_size > 0 && skill_activate(fd, status, nullptr)) + { + unsigned legion_value = status->m_card->m_legion * legion_size; + bool do_heal = can_be_healed(status); + bool do_rally = !status->m_immobilized && can_act(fd, status); + _DEBUG_MSG("%s activates Legion %u, %s%s%s by %u\n", status_description(status).c_str(), status->m_card->m_legion, + do_heal ? "healed" : "", do_heal && do_rally ? " and " : "", do_rally ? "rallied" : "", legion_value); + if(do_heal) + { + add_hp(fd, status, legion_value); + } + if(do_rally) + { + status->m_rallied += legion_value; + } + } + } + } +} //---------------------- $50 attack by assault card implementation ------------- // Counter damage dealt to the attacker (att) by defender (def) // pre-condition: only valid if m_card->m_counter > 0 @@ -1419,7 +1462,7 @@ inline bool skill_predicate(Field* fd, CardStatus* c) template<> inline bool skill_predicate(Field* fd, CardStatus* c) -{ return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } +{ return(can_be_healed(c)); } template<> inline bool skill_predicate(Field* fd, CardStatus* c) @@ -1443,7 +1486,7 @@ inline bool skill_predicate(Field* fd, CardStatus* c) template<> inline bool skill_predicate(Field* fd, CardStatus* c) -{ return(c->m_hp > 0 && c->m_hp < c->m_card->m_health); } +{ return(can_be_healed(c)); } template<> inline bool skill_predicate(Field* fd, CardStatus* c) @@ -1459,7 +1502,7 @@ inline bool skill_predicate(Field* fd, CardStatus* c) template<> inline bool skill_predicate(Field* fd, CardStatus* c) -{ return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } +{ return(can_be_healed(c)); } template<> inline bool skill_predicate(Field* fd, CardStatus* c) diff --git a/sim.h b/sim.h index 6766ca34..58ef5363 100644 --- a/sim.h +++ b/sim.h @@ -170,6 +170,7 @@ class Field enum phase { playcard_phase, + legion_phase, commander_phase, structures_phase, assaults_phase From 904e6dbb3f160f28eabe3fde33c5d1395cb5e30c Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 24 Feb 2013 21:34:45 +0800 Subject: [PATCH 083/406] Fix Bug: use skill_id (not skill_name) for achievements. --- tyrant.cpp | 2 +- xml.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tyrant.cpp b/tyrant.cpp index 418f5b7d..213ad850 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -16,7 +16,7 @@ std::string skill_names[Skill::num_skills] = "trigger_regen", "Weaken", // Combat-Modifier: - "Antiair", "Burst", "Fear", "Flurry", "Pierce", "Swipe", "Valor", + "AntiAir", "Burst", "Fear", "Flurry", "Pierce", "Swipe", "Valor", // Damage-Dependant: "Berserk", "Crush", "Disease", "Immobilize", "Leech", "Poison", "Siphon", // Defensive: diff --git a/xml.cpp b/xml.cpp index bd7d1a13..71f7a218 100644 --- a/xml.cpp +++ b/xml.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "rapidxml.hpp" #include "card.h" #include "cards.h" @@ -481,7 +482,9 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement std::map skill_map; for(unsigned i(0); i < Skill::num_skills; ++i) { - skill_map[skill_names[i]] = i; + std::string skill_id{skill_names[i]}; + std::transform(skill_id.begin(), skill_id.end(), skill_id.begin(), ::tolower); + skill_map[skill_id] = i; } for(xml_node<>* achievement_node = root->first_node(); From e86ebf0def230545014cc71b789fff7a82487abf Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 24 Feb 2013 21:46:01 +0800 Subject: [PATCH 084/406] Fix bug: surge points. --- sim.cpp | 2 +- tyrant_optimize.cpp | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/sim.cpp b/sim.cpp index 2ae750bf..ca2c5546 100644 --- a/sim.cpp +++ b/sim.cpp @@ -674,7 +674,7 @@ unsigned play(Field* fd) fd->points_since_last_decision = 10; } _DEBUG_MSG("Attacker wins.\n"); - return(10 + (speedy ? 5 : 0) + fd->points_since_last_decision); + return(10 + (speedy ? 5 : 0) + (fd->gamemode == surge ? 20 : 0) + fd->points_since_last_decision); } if (fd->turn > turn_limit) { diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index b11b08f0..bfbdfecc 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -38,7 +38,10 @@ #include "xml.h" //#include "timer.hpp" -namespace { bool use_anp{false}; } +namespace { + bool use_anp{false}; + gamemode_t gamemode{fight}; +} using namespace std::placeholders; //------------------------------------------------------------------------------ @@ -401,6 +404,7 @@ void thread_evaluate(boost::barrier& main_barrier, unsigned thread_id) { bool use_anp_local{use_anp}; + gamemode_t gamemode_local{gamemode}; while(true) { main_barrier.wait(); @@ -458,7 +462,7 @@ void thread_evaluate(boost::barrier& main_barrier, { // TODO: Fix this solution for ANP // Get a loose, rather than no, upper bound. - static const double best_possible = 25; + double best_possible = gamemode_local == surge ? 45 : 25; compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum / best_possible, 0.01) * best_possible < thread_prev_score); } else @@ -528,7 +532,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; bool eval_commander = true; - double best_possible = use_anp ? 25 : 1; + double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(10, d1->cards.size() + (fixed_len ? 0 : 1))) { if(deck_has_been_improved) @@ -633,7 +637,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; bool eval_commander = true; - double best_possible = use_anp ? 25 : 1; + double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(10, d1->cards.size() + (fixed_len ? 0 : 1))) { if(deck_has_been_improved) @@ -981,7 +985,6 @@ int main(int argc, char** argv) if(argc == 1) { usage(argc, argv); return(0); } debug_print = getenv("DEBUG_PRINT"); unsigned num_threads = (debug_print || getenv("DEBUG")) ? 1 : 4; - gamemode_t gamemode = fight; bool ordered = false; Cards cards; read_cards(cards); @@ -1097,7 +1100,7 @@ int main(int argc, char** argv) { debug_print = true; num_threads = 1; - todo.push_back(std::make_tuple(0u, 25u, fightuntil)); + todo.push_back(std::make_tuple(0u, 45u, fightuntil)); } else if(strcmp(argv[argIndex], "debuguntil") == 0) { From 9d3a1f5f999f63d2219e15a3310e2e1e32520c93 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 24 Feb 2013 21:47:17 +0800 Subject: [PATCH 085/406] Print shorter description for Defender decks if more than two. --- tyrant_optimize.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index bfbdfecc..48844634 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1207,7 +1207,14 @@ int main(int argc, char** argv) std::cout << "Defender:" << std::endl; for(auto def_deck: def_decks) { - std::cout << def_deck->long_description() << std::endl; + if(def_decks.size() > 2) + { + std::cout << def_deck->short_description() << std::endl; + } + else + { + std::cout << def_deck->long_description() << std::endl; + } } Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); From c4a463910a2fbcdebfbbc0de8d1b8b44c215cce9 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 25 Feb 2013 10:20:01 +0800 Subject: [PATCH 086/406] Fix bug: Count uses of payback for achievements. --- sim.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sim.cpp b/sim.cpp index ca2c5546..79e1e494 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1914,24 +1914,19 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski { index_start = index_end = get_target_hostile_index(fd, src_status, selection_array_size); } - unsigned payback_count(0); for(unsigned s_index(index_start); s_index <= index_end; ++s_index) { CardStatus* c(fd->selection_array[s_index]); if(check_and_perform_skill(fd, src_status, c, s, true)) { - if(c->m_card->m_payback && skill_activate(fd, c, src_status)) + // Payback + if(c->m_card->m_payback && skill_predicate(fd, src_status) && skill_activate(fd, c, src_status)) { - _DEBUG_MSG("%s paybacks.\n", status_description(c).c_str()); - ++payback_count; + _DEBUG_MSG("%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); + perform_skill(fd, src_status, std::get<1>(s)); } } } - for(unsigned i(0); i < payback_count && skill_predicate(fd, src_status); ++i) - { - _DEBUG_MSG("Payback (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); - perform_skill(fd, src_status, std::get<1>(s)); - } maybeTriggerRegen::T>(fd); prepend_on_death(fd); } From b0b4e1ef58784fe0aeec12b84ccf718f6c0c1f7e Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 25 Feb 2013 16:56:31 +0800 Subject: [PATCH 087/406] Fix bug: Commander needs to be damaged to use Siphon. --- sim.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sim.cpp b/sim.cpp index 79e1e494..650a14bd 100644 --- a/sim.cpp +++ b/sim.cpp @@ -787,6 +787,10 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(c->m_hp == 0 && !c->m_diseased && fd->flip()); } +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ return(can_be_healed(&fd->players[c->m_player]->commander)); } + template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { return(ref->m_card->m_type == CardType::assault && ref != c && ref->m_hp > 0 && fd->flip()); } From 9e8105b8f8f2c71691619e8359ca83060bcc12fa Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 26 Feb 2013 13:14:02 +0800 Subject: [PATCH 088/406] Fix Legion skill. --- sim.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index 650a14bd..eba3f2a5 100644 --- a/sim.cpp +++ b/sim.cpp @@ -995,8 +995,7 @@ void turn_start_phase(Field* fd) void evaluate_legion(Field* fd) { // Not subject to Mimic / Emulate / Augment - // Not affected by Jam / Freeze - // Legion-rally effect is prevented by Immobilize + // Not prevented by Jam / Freeze / Immobilize // Honor Infused faction auto& assaults = fd->tap->assaults; for(fd->current_ci = 0; fd->current_ci < assaults.size(); ++fd->current_ci) @@ -1011,7 +1010,7 @@ void evaluate_legion(Field* fd) { unsigned legion_value = status->m_card->m_legion * legion_size; bool do_heal = can_be_healed(status); - bool do_rally = !status->m_immobilized && can_act(fd, status); + bool do_rally = !status->m_immobilized && c->m_hp > 0 && (fd->tapi == c->m_player ? c->m_delay == 0 || c->m_blitzing : c->m_delay <= 1); _DEBUG_MSG("%s activates Legion %u, %s%s%s by %u\n", status_description(status).c_str(), status->m_card->m_legion, do_heal ? "healed" : "", do_heal && do_rally ? " and " : "", do_rally ? "rallied" : "", legion_value); if(do_heal) From d735f19c8d680c70f389684f4b9eff33db92093a Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 26 Feb 2013 18:36:32 +0800 Subject: [PATCH 089/406] Fix typo. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index eba3f2a5..db15b28f 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1010,7 +1010,7 @@ void evaluate_legion(Field* fd) { unsigned legion_value = status->m_card->m_legion * legion_size; bool do_heal = can_be_healed(status); - bool do_rally = !status->m_immobilized && c->m_hp > 0 && (fd->tapi == c->m_player ? c->m_delay == 0 || c->m_blitzing : c->m_delay <= 1); + bool do_rally = !status->m_immobilized && status->m_hp > 0 && (fd->tapi == status->m_player ? status->m_delay == 0 || status->m_blitzing : status->m_delay <= 1); _DEBUG_MSG("%s activates Legion %u, %s%s%s by %u\n", status_description(status).c_str(), status->m_card->m_legion, do_heal ? "healed" : "", do_heal && do_rally ? " and " : "", do_rally ? "rallied" : "", legion_value); if(do_heal) From 7bca871bddbfe15145fc68aadfcd9c16edeb8121 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 4 Mar 2013 17:45:08 +0800 Subject: [PATCH 090/406] Fix bug: Should not count for Fear when (pre-modifier) attack damage = 0. --- sim.cpp | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/sim.cpp b/sim.cpp index db15b28f..b488cb6b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1083,10 +1083,8 @@ struct PerformAttack {} template - void op() + void op(unsigned pre_modifier_dmg) { - unsigned pre_modifier_dmg = attack_power(att_status); - if(pre_modifier_dmg == 0) { return; } modify_attack_damage(pre_modifier_dmg); // Evaluation order: // assaults only: fly check @@ -1113,14 +1111,11 @@ struct PerformAttack if(att_dmg > 0) { immobilize(); - if(skill_activate(fd, att_status, def_status)) + attack_damage(); + if(fd->end) { - attack_damage(); - if(fd->end) - { - // Commander dies? - return; - } + // Commander dies? + return; } siphon_poison_disease(); on_kill(); @@ -1318,16 +1313,16 @@ void PerformAttack::crush_leech() } // General attack phase by the currently evaluated assault, taking into accounts exotic stuff such as flurry,swipe,etc. -void attack_commander(Field* fd, CardStatus* att_status) +void attack_commander(Field* fd, CardStatus* att_status, unsigned pre_modifier_dmg) { CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall if(def_status != nullptr) { - PerformAttack{fd, att_status, def_status}.op(); + PerformAttack{fd, att_status, def_status}.op(pre_modifier_dmg); } else { - PerformAttack{fd, att_status, &fd->tip->commander}.op(); + PerformAttack{fd, att_status, &fd->tip->commander}.op(pre_modifier_dmg); } } void attack_phase(Field* fd) @@ -1335,6 +1330,9 @@ void attack_phase(Field* fd) CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); att_status->m_attacked = true; + unsigned pre_modifier_dmg = attack_power(att_status); + if(pre_modifier_dmg == 0) { return; } + skill_activate(fd, att_status, nullptr); unsigned num_attacks(1); if(att_status->m_card->m_flurry > 0 && skill_activate(fd, att_status, nullptr)) { @@ -1355,7 +1353,7 @@ void attack_phase(Field* fd) // attack mode 1. if(!(att_status->m_card->m_swipe && skill_activate(fd, att_status, nullptr))) { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(pre_modifier_dmg); } // attack mode 2. else @@ -1365,32 +1363,32 @@ void attack_phase(Field* fd) // attack the card on the left if(fd->current_ci > 0 && alive_assault(def_assaults, fd->current_ci - 1)) { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(); + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(pre_modifier_dmg); } if(fd->end) { return; } // stille alive? attack the card in front if(att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci)) { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(pre_modifier_dmg); } else { - attack_commander(fd, att_status); + attack_commander(fd, att_status, pre_modifier_dmg); } if(fd->end) { return; } // still alive? attack the card on the right if(!fd->end && att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci + 1)) { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(); + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(pre_modifier_dmg); } } } // attack mode 3. else { - attack_commander(fd, att_status); + attack_commander(fd, att_status, pre_modifier_dmg); } } } From d85bb5aaecb9bf2fc47bffab214e8c6591f6b18b Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 4 Mar 2013 17:57:30 +0800 Subject: [PATCH 091/406] Print out short description of decks unless in debug mode. --- deck.cpp | 6 ++---- tyrant_optimize.cpp | 13 ++----------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/deck.cpp b/deck.cpp index 7a0f997b..dd6450b3 100644 --- a/deck.cpp +++ b/deck.cpp @@ -141,16 +141,14 @@ std::string Deck::short_description() const ios << decktype_names[decktype]; if(id > 0) { ios << " " << id; } if(!name.empty()) { ios << " \"" << name << "\""; } + if(raid_cards.empty()) { ios << ": " << deck_hash(commander, cards); } return ios.str(); } std::string Deck::long_description() const { std::stringstream ios; - ios << short_description(); - ios << ":"; - if(raid_cards.empty()) { ios << " " << deck_hash(commander, cards); } - ios << std::endl; + ios << short_description() << std::endl; if(effect != Effect::none) { ios << "Effect: " << effect_names[effect] << "\n"; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 48844634..5a1c5512 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1202,19 +1202,10 @@ int main(int argc, char** argv) effect = quest_effect.get_value_or(effect); modify_cards(cards, effect); - std::cout << "Attacker:" << std::endl; - std::cout << att_deck->long_description() << std::endl; - std::cout << "Defender:" << std::endl; + std::cout << "Attacker: " << (debug_print ? att_deck->long_description() : att_deck->short_description()) << std::endl; for(auto def_deck: def_decks) { - if(def_decks.size() > 2) - { - std::cout << def_deck->short_description() << std::endl; - } - else - { - std::cout << def_deck->long_description() << std::endl; - } + std::cout << "Defender: " << (debug_print ? def_deck->long_description() : def_deck->short_description()) << std::endl; } Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); From 52d140326ff436b5e44a360f48d72cc46c0fa3cf Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 5 Mar 2013 16:58:37 +0800 Subject: [PATCH 092/406] Fix bug: Count the uses of skills for achievements. --- sim.cpp | 290 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 178 insertions(+), 112 deletions(-) diff --git a/sim.cpp b/sim.cpp index b488cb6b..50e668f3 100644 --- a/sim.cpp +++ b/sim.cpp @@ -608,13 +608,14 @@ unsigned play(Field* fd) continue; } // Special case: check for split (tartarus swarm raid, or clone battlefield effects) - // TODO skill_activate if((current_status.m_temporary_split || current_status.m_card->m_split) && fd->tap->assaults.size() + fd->tap->structures.size() < 100) { + // TODO count_achievement(fd, current_status) CardStatus& status_split(fd->tap->assaults.add_back()); status_split.set(current_status.m_card); status_split.m_index = fd->tap->assaults.size() - 1; status_split.m_player = fd->tapi; + status_split.m_summoned = true; _DEBUG_MSG("%s splits %s\n", status_description(¤t_status).c_str(), status_description(&status_split).c_str()); for(auto& skill: status_split.m_card->m_skills_on_play) { @@ -623,8 +624,11 @@ unsigned play(Field* fd) resolve_skill(fd); if(fd->end) { break; } } + if(status_split.m_card->m_blitz) + { + check_and_perform_blitz(fd, &status_split); + } // TODO: Use summon to implement split? - // TODO: Determine whether we need to check for Blitz for the newly-Split unit } // Evaluate skills evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); @@ -692,7 +696,7 @@ inline bool can_act(Field* fd, CardStatus* c) { return(c->m_hp > 0 && !c->m_jamm // Can be healed / repaired inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } -// Check if a skill actually activated. +// Check if a skill actually proc'ed. template inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { return(true); } @@ -757,13 +761,9 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return(c->m_hp > 0 && !c->m_diseased); + return(can_be_healed(c)); } -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ return(can_be_healed(c) || (!c->m_immobilized && can_act(fd, c))); } - template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { @@ -812,19 +812,15 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) } template -inline bool skill_activate(Field* fd, CardStatus* c, CardStatus* ref) +inline bool count_achievement(Field* fd, const CardStatus* c) { - if(skill_check(fd, c, ref)) + if(c->m_player == 0) { - if(c->m_player == 0) - { - fd->inc_counter(fd->achievement.skill_used, skill_id); - } - return(true); + fd->inc_counter(fd->achievement.skill_used, skill_id); } - else - { return(false); } + return(true); } + //------------------------------------------------------------------------------ // All the stuff that happens at the beginning of a turn, before a card is played // returns true iff the card died. @@ -880,11 +876,12 @@ void check_regeneration(Field* fd) { for(unsigned i(0); i < fd->killed_with_regen.size(); ++i) { - CardStatus& status = *fd->killed_with_regen[i]; - if(skill_activate(fd, &status, nullptr)) + CardStatus* status = fd->killed_with_regen[i]; + if(skill_check(fd, status, nullptr)) { - _DEBUG_MSG("%s regenerates with %u health\n", status_description(&status).c_str(), status.m_card->m_health); - add_hp(fd, &status, status.m_card->m_regenerate); + count_achievement(fd, status); + _DEBUG_MSG("%s regenerates with %u health\n", status_description(status).c_str(), status->m_card->m_health); + add_hp(fd, status, status->m_card->m_regenerate); } } fd->killed_with_regen.clear(); @@ -1001,27 +998,35 @@ void evaluate_legion(Field* fd) for(fd->current_ci = 0; fd->current_ci < assaults.size(); ++fd->current_ci) { CardStatus* status(&assaults[fd->current_ci]); - if(status->m_card->m_legion > 0) + if(status->m_card->m_legion == 0) { - unsigned legion_size{0}; - legion_size += status->m_index > 0 && assaults[status->m_index - 1].m_hp > 0 && assaults[status->m_index - 1].m_faction == status->m_faction; - legion_size += status->m_index + 1 < assaults.size() && assaults[status->m_index + 1].m_hp > 0 && assaults[status->m_index + 1].m_faction == status->m_faction; - if(legion_size > 0 && skill_activate(fd, status, nullptr)) - { - unsigned legion_value = status->m_card->m_legion * legion_size; - bool do_heal = can_be_healed(status); - bool do_rally = !status->m_immobilized && status->m_hp > 0 && (fd->tapi == status->m_player ? status->m_delay == 0 || status->m_blitzing : status->m_delay <= 1); - _DEBUG_MSG("%s activates Legion %u, %s%s%s by %u\n", status_description(status).c_str(), status->m_card->m_legion, - do_heal ? "healed" : "", do_heal && do_rally ? " and " : "", do_rally ? "rallied" : "", legion_value); - if(do_heal) - { - add_hp(fd, status, legion_value); - } - if(do_rally) - { - status->m_rallied += legion_value; - } - } + continue; + } + unsigned legion_size(0); + legion_size += status->m_index > 0 && assaults[status->m_index - 1].m_hp > 0 && assaults[status->m_index - 1].m_faction == status->m_faction; + legion_size += status->m_index + 1 < assaults.size() && assaults[status->m_index + 1].m_hp > 0 && assaults[status->m_index + 1].m_faction == status->m_faction; + if(legion_size == 0) + { + continue; + } + // skill_check + bool do_heal = can_be_healed(status); + bool do_rally = status->m_hp > 0 && (fd->tapi == status->m_player ? status->m_delay == 0 || status->m_blitzing : status->m_delay <= 1); + if(!do_heal && !do_rally) + { + continue; + } + count_achievement(fd, status); + unsigned legion_value = status->m_card->m_legion * legion_size; + _DEBUG_MSG("%s activates Legion %u, %s%s%s by %u\n", status_description(status).c_str(), status->m_card->m_legion, + do_heal ? "healed" : "", do_heal && do_rally ? " and " : "", do_rally ? "rallied" : "", legion_value); + if(do_heal) + { + add_hp(fd, status, legion_value); + } + if(do_rally) + { + status->m_rallied += legion_value; } } } @@ -1039,7 +1044,11 @@ inline CardStatus* select_first_enemy_wall(Field* fd) for(unsigned i(0); i < fd->tip->structures.size(); ++i) { CardStatus& c(fd->tip->structures[i]); - if(c.m_card->m_wall && c.m_hp > 0 && skill_activate(fd, &c, nullptr)) { return(&c); } + if(c.m_card->m_wall && c.m_hp > 0 && skill_check(fd, &c, nullptr)) + { + count_achievement(fd, &c); + return(&c); + } } return(nullptr); } @@ -1095,8 +1104,9 @@ struct PerformAttack // counter, berserk // assaults only: (crush, leech if still alive) // check regeneration - if(att_dmg > 0 && def_status->m_card->m_flying && skill_activate(fd, def_status, att_status)) + if(att_dmg > 0 && def_status->m_card->m_flying && skill_check(fd, def_status, att_status)) { + count_achievement(fd, def_status); _DEBUG_MSG("%s dodges with Flying\n", status_description(def_status).c_str()); return; } @@ -1121,8 +1131,9 @@ struct PerformAttack on_kill(); } on_attacked(); - if(att_status->m_hp > 0 && def_status->m_card->m_counter > 0 && skill_activate(fd, def_status, att_status)) + if(att_status->m_hp > 0 && def_status->m_card->m_counter > 0 && skill_check(fd, def_status, att_status)) { + count_achievement(fd, def_status); // perform_skill_counter unsigned counter_dmg(counter_damage(att_status, def_status)); _DEBUG_MSG("%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); @@ -1130,8 +1141,9 @@ struct PerformAttack } if(att_dmg > 0) { - if(att_status->m_hp > 0 && att_status->m_card->m_berserk > 0 && skill_activate(fd, att_status, nullptr)) + if(att_status->m_hp > 0 && att_status->m_card->m_berserk > 0 && skill_check(fd, att_status, nullptr)) { + count_achievement(fd, att_status); // perform_skill_berserk att_status->m_berserk += att_status->m_card->m_berserk; } @@ -1150,37 +1162,63 @@ struct PerformAttack const Card& def_card(*def_status->m_card); assert(att_card.m_type == CardType::assault); assert(pre_modifier_dmg > 0); - unsigned valor_damage{att_card.m_valor && skill_activate(fd, att_status, nullptr) ? att_card.m_valor : 0}; - unsigned antiair_damage{att_card.m_antiair > 0 && skill_activate(fd, att_status, def_status) ? att_card.m_antiair : 0}; - unsigned burst_damage{att_card.m_burst > 0 && skill_activate(fd, att_status, def_status) ? att_card.m_burst : 0}; - att_dmg = safe_minus( - pre_modifier_dmg - + valor_damage - + def_status->m_enfeebled // enfeeble - + antiair_damage - + burst_damage - // armor + protect + pierce - , safe_minus(def_card.m_armored + def_status->m_protected, att_card.m_pierce)); + att_dmg = pre_modifier_dmg; + // enhance damage + std::string desc; + if(att_card.m_valor > 0 && skill_check(fd, att_status, nullptr)) + { + count_achievement(fd, att_status); + if(debug_print) { desc += "+" + to_string(att_card.m_valor) + "(valor)"; } + att_dmg += att_card.m_valor; + } + if(att_card.m_antiair > 0 && skill_check(fd, att_status, def_status)) + { + count_achievement(fd, att_status); + if(debug_print) { desc += "+" + to_string(att_card.m_antiair) + "(antiair)"; } + att_dmg += att_card.m_antiair; + } + if(att_card.m_burst > 0 && skill_check(fd, att_status, def_status)) + { + count_achievement(fd, att_status); + if(debug_print) { desc += "+" + to_string(att_card.m_burst) + "(burst)"; } + att_dmg += att_card.m_burst; + } + if(def_status->m_enfeebled > 0) + { + if(debug_print) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } + att_dmg += def_status->m_enfeebled; + } + // prevent damage + std::string reduced_desc; + unsigned reduced_dmg(0); if(def_card.m_armored > 0) { - skill_activate(fd, def_status, nullptr); + // Armored counts if not totally cancelled by Pierce. TODO how if Armored + Proteced > Pierce? + if(def_card.m_armored > att_card.m_pierce) + { + count_achievement(fd, def_status); + } + if(debug_print) { reduced_desc += to_string(def_card.m_armored) + "(armored)"; } + reduced_dmg += def_card.m_armored; + } + if(def_status->m_protected > 0) + { + if(debug_print) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->m_protected) + "(protected)"; } + reduced_dmg += def_status->m_protected; + } + if(reduced_dmg > 0 && att_card.m_pierce > 0) + { + // TODO No Pierce achievement yet, so no count_achievement(fd, att_status) + if(debug_print) { reduced_desc += "-" + to_string(att_card.m_pierce) + "(pierce)"; } + reduced_dmg = safe_minus(reduced_dmg, att_card.m_pierce); } - // TODO skill_activate if(debug_print) { - std::string desc; - if(valor_damage > 0) { desc += "+" + to_string(valor_damage) + "(valor)"; } - if(def_status->m_enfeebled > 0) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } - if(antiair_damage > 0) { desc += "+" + to_string(antiair_damage) + "(antiair)"; } - if(burst_damage) { desc += "+" + to_string(burst_damage) + "(burst)"; } - std::string reduced_desc; - if(def_card.m_armored > 0) { reduced_desc += to_string(def_card.m_armored) + "(armored)"; } - if(def_status->m_protected > 0) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->m_protected) + "(protected)"; } - if(!reduced_desc.empty() && att_card.m_pierce > 0) { reduced_desc += "-" + to_string(att_card.m_pierce) + "(pierce)"; } - if(!reduced_desc.empty()) { desc += "-(" + reduced_desc + ")"; } + if(!reduced_desc.empty()) { desc += "-[" + reduced_desc + "]"; } if(!desc.empty()) { desc += "=" + to_string(att_dmg); } _DEBUG_MSG("%s attacks %s for %u%s damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), pre_modifier_dmg, desc.c_str()); } + att_dmg = safe_minus(att_dmg, reduced_dmg); } template @@ -1202,20 +1240,23 @@ struct PerformAttack template void on_attacked() { - if(def_status->m_card->m_poison_oa > att_status->m_poisoned && skill_activate(fd, def_status, att_status)) + if(def_status->m_card->m_poison_oa > att_status->m_poisoned && skill_check(fd, def_status, att_status)) { + count_achievement(fd, def_status); unsigned v = def_status->m_card->m_poison_oa; _DEBUG_MSG("%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); att_status->m_poisoned = v; } - if(def_status->m_card->m_disease_oa && skill_activate(fd, def_status, att_status)) + if(def_status->m_card->m_disease_oa && skill_check(fd, def_status, att_status)) { + count_achievement(fd, def_status); // perform_skill_disease _DEBUG_MSG("%s (on attacked) diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); att_status->m_diseased = true; } - if(def_status->m_hp > 0 && def_status->m_card->m_berserk_oa > 0 && skill_activate(fd, def_status, nullptr)) - { + if(def_status->m_hp > 0 && def_status->m_card->m_berserk_oa > 0 && skill_check(fd, def_status, nullptr)) + { + count_achievement(fd, def_status); def_status->m_berserk += def_status->m_card->m_berserk_oa; } for(auto& oa_skill: def_status->m_card->m_skills_on_attacked) @@ -1234,8 +1275,9 @@ struct PerformAttack template<> void PerformAttack::immobilize() { - if(att_status->m_card->m_immobilize && skill_activate(fd, att_status, def_status)) + if(att_status->m_card->m_immobilize && skill_check(fd, att_status, def_status)) { + count_achievement(fd, att_status); _DEBUG_MSG("%s immobilizes %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_immobilized = true; } @@ -1250,22 +1292,25 @@ void PerformAttack::attack_damage() template<> void PerformAttack::siphon_poison_disease() { - if(att_status->m_card->m_siphon > 0 && skill_activate(fd, att_status, def_status)) + if(att_status->m_card->m_siphon > 0 && skill_check(fd, att_status, def_status)) { + count_achievement(fd, att_status); // perform_skill_siphon unsigned v = std::min(att_dmg, att_status->m_card->m_siphon); _DEBUG_MSG("%s siphons %u health for %s\n", status_description(att_status).c_str(), v, status_description(&fd->tap->commander).c_str()); add_hp(fd, &fd->tap->commander, v); } - if(att_status->m_card->m_poison > def_status->m_poisoned && skill_activate(fd, att_status, def_status)) + if(att_status->m_card->m_poison > def_status->m_poisoned && skill_check(fd, att_status, def_status)) { + count_achievement(fd, att_status); // perform_skill_poison unsigned v = att_status->m_card->m_poison; _DEBUG_MSG("%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); def_status->m_poisoned = v; } - if(att_status->m_card->m_disease && skill_activate(fd, att_status, def_status)) + if(att_status->m_card->m_disease && skill_check(fd, att_status, def_status)) { + count_achievement(fd, att_status); // perform_skill_disease _DEBUG_MSG("%s diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); def_status->m_diseased = true; @@ -1290,8 +1335,9 @@ void PerformAttack::on_kill() template<> void PerformAttack::crush_leech() { - if(att_status->m_card->m_crush > 0 && killed_by_attack && skill_activate(fd, att_status, nullptr)) + if(att_status->m_card->m_crush > 0 && killed_by_attack && skill_check(fd, att_status, nullptr)) { + count_achievement(fd, att_status); // perform_skill_crush CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall if (def_status != nullptr) @@ -1305,8 +1351,9 @@ void PerformAttack::crush_leech() remove_commander_hp(fd, fd->tip->commander, att_status->m_card->m_crush, true); } } - if(att_status->m_card->m_leech > 0 && skill_activate(fd, att_status, nullptr)) + if(att_status->m_card->m_leech > 0 && skill_check(fd, att_status, nullptr)) { + count_achievement(fd, att_status); _DEBUG_MSG("%s leeches %u health\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech)); add_hp(fd, att_status, std::min(att_dmg, att_status->m_card->m_leech)); } @@ -1332,10 +1379,11 @@ void attack_phase(Field* fd) att_status->m_attacked = true; unsigned pre_modifier_dmg = attack_power(att_status); if(pre_modifier_dmg == 0) { return; } - skill_activate(fd, att_status, nullptr); + count_achievement(fd, att_status); unsigned num_attacks(1); - if(att_status->m_card->m_flurry > 0 && skill_activate(fd, att_status, nullptr)) + if(att_status->m_card->m_flurry > 0 && skill_check(fd, att_status, nullptr)) { + count_achievement(fd, att_status); _DEBUG_MSG("%s activates Flurry\n", status_description(att_status).c_str()); num_attacks += att_status->m_card->m_flurry; } @@ -1348,10 +1396,10 @@ void attack_phase(Field* fd) // See http://www.kongregate.com/forums/65-tyrant/topics/289416?page=22#posts-6861970 // - 3. attack against the commander or walls (if there is no assault or if the attacker has the fear attribute) // Check if attack mode is 1. or 2. (there is a living assault card in front, and no fear) - if(alive_assault(def_assaults, fd->current_ci) && !(att_status->m_card->m_fear && skill_activate(fd, att_status, nullptr))) + if(alive_assault(def_assaults, fd->current_ci) && !(att_status->m_card->m_fear && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) { // attack mode 1. - if(!(att_status->m_card->m_swipe && skill_activate(fd, att_status, nullptr))) + if(!(att_status->m_card->m_swipe && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(pre_modifier_dmg); } @@ -1824,8 +1872,9 @@ unsigned get_target_hostile_index(Field* fd, CardStatus* src_status, unsigned se if(rand_index > 0) { CardStatus* left_status(fd->selection_array[rand_index - 1]); - if(left_status->m_card->m_intercept && left_status->m_index == status->m_index - 1 && skill_activate(fd, left_status, status)) + if(left_status->m_card->m_intercept && left_status->m_index == status->m_index - 1 && skill_check(fd, left_status, status)) { + count_achievement(fd, left_status); _DEBUG_MSG("%s intercepts for %s\n", status_description(left_status).c_str(), status_description(status).c_str()); return(rand_index - 1); } @@ -1833,8 +1882,9 @@ unsigned get_target_hostile_index(Field* fd, CardStatus* src_status, unsigned se if(rand_index + 1 < selection_array_size) { CardStatus* right_status(fd->selection_array[rand_index + 1]); - if(right_status->m_card->m_intercept && right_status->m_index == status->m_index + 1 && skill_activate(fd, right_status, status)) + if(right_status->m_card->m_intercept && right_status->m_index == status->m_index + 1 && skill_check(fd, right_status, status)) { + count_achievement(fd, right_status); _DEBUG_MSG("%s intercepts for %s\n", status_description(right_status).c_str(), status_description(status).c_str()); return(rand_index + 1); } @@ -1844,28 +1894,32 @@ unsigned get_target_hostile_index(Field* fd, CardStatus* src_status, unsigned se } template -bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_status, const SkillSpec& s, bool evadable) +bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_status, const SkillSpec& s, bool is_evadable, bool is_count_achievement) { - if(skill_activate(fd, src_status, dst_status)) + if(skill_check(fd, src_status, dst_status)) { - _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); - if(evadable && dst_status->m_card->m_evade && skill_activate(fd, dst_status, src_status)) + if(is_evadable && dst_status->m_card->m_evade && skill_check(fd, dst_status, src_status)) { - _DEBUG_MSG("%s evades\n", status_description(dst_status).c_str()); + count_achievement(fd, dst_status); + _DEBUG_MSG("%s %s (%u) on %s but it evades\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); + return(false); } - else + if(is_count_achievement) { - perform_skill(fd, dst_status, std::get<1>(s)); - return(true); + count_achievement(fd, src_status); } + _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); + perform_skill(fd, dst_status, std::get<1>(s)); + return(true); } return(false); } bool check_and_perform_blitz(Field* fd, CardStatus* src_status) { - if(skill_activate(fd, src_status, nullptr)) + if(skill_check(fd, src_status, nullptr)) { + count_achievement(fd, src_status); _DEBUG_MSG("%s activates Blitz opposing %s\n", status_description(src_status).c_str(), status_description(&fd->tip->assaults[src_status->m_index]).c_str()); src_status->m_blitzing = true; return(true); @@ -1875,9 +1929,9 @@ bool check_and_perform_blitz(Field* fd, CardStatus* src_status) bool check_and_perform_recharge(Field* fd, CardStatus* src_status) { - if(skill_activate(fd, src_status, nullptr)) + if(skill_check(fd, src_status, nullptr)) { - // perform_skill_recharge + count_achievement(fd, src_status); _DEBUG_MSG("%s activates Recharge\n", status_description(src_status).c_str()); fd->tap->deck->place_at_bottom(src_status->m_card); return(true); @@ -1887,9 +1941,9 @@ bool check_and_perform_recharge(Field* fd, CardStatus* src_status) bool check_and_perform_refresh(Field* fd, CardStatus* src_status) { - if(skill_activate(fd, src_status, nullptr)) + if(skill_check(fd, src_status, nullptr)) { - // perform_skill_refresh + count_achievement(fd, src_status); _DEBUG_MSG("%s refreshes, hp -> %u\n", status_description(src_status).c_str(), src_status->m_card->m_health); add_hp(fd, src_status, src_status->m_card->m_health); return(true); @@ -1915,14 +1969,18 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski { index_start = index_end = get_target_hostile_index(fd, src_status, selection_array_size); } + bool is_count_achievement(true); for(unsigned s_index(index_start); s_index <= index_end; ++s_index) { CardStatus* c(fd->selection_array[s_index]); - if(check_and_perform_skill(fd, src_status, c, s, true)) + if(check_and_perform_skill(fd, src_status, c, s, true, is_count_achievement)) { + // Count at most once even targeting "All" + is_count_achievement = false; // Payback - if(c->m_card->m_payback && skill_predicate(fd, src_status) && skill_activate(fd, c, src_status)) + if(c->m_card->m_payback && skill_predicate(fd, src_status) && skill_check(fd, c, src_status)) { + count_achievement(fd, c); _DEBUG_MSG("%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); perform_skill(fd, src_status, std::get<1>(s)); } @@ -1947,24 +2005,29 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil { index_start = index_end = fd->rand(0, selection_array_size - 1); } + bool is_count_achievement(true); for(unsigned s_index(index_start); s_index <= index_end; ++s_index) { CardStatus* c(fd->selection_array[s_index]); - if(check_and_perform_skill(fd, src_status, c, s, false)) + if(check_and_perform_skill(fd, src_status, c, s, false, is_count_achievement)) { - if(c->m_card->m_tribute && skill_predicate(fd, src_status) && skill_activate(fd, c, src_status)) + // Count at most once even targeting "All" + is_count_achievement = false; + // Tribute + if(c->m_card->m_tribute && skill_predicate(fd, src_status) && skill_check(fd, c, src_status)) { + count_achievement(fd, c); _DEBUG_MSG("Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); perform_skill(fd, src_status, std::get<1>(s)); } - - // check emulate + // Emulate Hand* opp = fd->players[opponent(c->m_player)]; if(opp->assaults.size() > c->m_index) { CardStatus& emulator = opp->assaults[c->m_index]; - if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator) && skill_activate(fd, &emulator, nullptr)) + if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator) && skill_check(fd, &emulator, nullptr)) { + count_achievement(fd, &emulator); _DEBUG_MSG("Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); perform_skill(fd, &emulator, std::get<1>(s)); } @@ -1975,7 +2038,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil void perform_backfire(Field* fd, CardStatus* src_status, const SkillSpec& s) { - check_and_perform_skill(fd, src_status, &fd->players[src_status->m_player]->commander, s, false); + check_and_perform_skill(fd, src_status, &fd->players[src_status->m_player]->commander, s, false, true); } void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) @@ -2002,7 +2065,7 @@ void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) if(array_head > 0) { CardStatus* c(fd->selection_array[fd->rand(0, array_head - 1)]); - check_and_perform_skill(fd, src_status, c, s, true); + check_and_perform_skill(fd, src_status, c, s, true, true); } } @@ -2028,13 +2091,14 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) Hand* hand{fd->players[player]}; if(hand->assaults.size() + hand->structures.size() < 100) { + count_achievement(fd, src_status); Storage* storage{summoned->m_type == CardType::assault ? &hand->assaults : &hand->structures}; CardStatus& card_status(storage->add_back()); card_status.set(summoned); card_status.m_index = storage->size() - 1; card_status.m_player = player; card_status.m_summoned = true; - _DEBUG_MSG("Summon %s %u [%s]\n", cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd, summoned).c_str()); + _DEBUG_MSG("%s Summon %s %u [%s]\n", status_description(src_status).c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd, summoned).c_str()); prepend_skills(fd, &card_status); if(card_status.m_card->m_blitz) { @@ -2062,7 +2126,7 @@ void perform_trigger_regen(Field* fd, CardStatus* src_status, const SkillSpec& s void perform_shock(Field* fd, CardStatus* src_status, const SkillSpec& s) { - check_and_perform_skill(fd, src_status, &fd->tip->commander, s, false); + check_and_perform_skill(fd, src_status, &fd->tip->commander, s, false, true); } // Special rules for mimic : @@ -2081,18 +2145,20 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) return; } CardStatus* c(fd->selection_array[get_target_hostile_index(fd, src_status, selection_array_size)]); - _DEBUG_MSG("%s on %s\n", skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); // evade check for mimic // individual skills are subject to evade checks too, // but resolve_skill will handle those. - if(c->m_card->m_evade && skill_activate(fd, c, src_status)) + if(c->m_card->m_evade && skill_check(fd, c, src_status)) { - _DEBUG_MSG("%s evades\n", status_description(c).c_str()); + count_achievement(fd, c); + _DEBUG_MSG("%s %s on %s but it evades\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); return; } + count_achievement(fd, src_status); + _DEBUG_MSG("%s %s on %s\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); for(auto skill: c->m_card->m_skills) { - if(src_status->m_card->m_type == CardType::assault && src_status->m_hp == 0) + if(src_status->m_card->m_type != CardType::action && src_status->m_hp == 0) { break; } if(std::get<0>(skill) == mimic || (std::get<0>(skill) == supply && src_status->m_card->m_type != CardType::assault)) From 146b91877f0cc351f835fd7e13016eb5af18c958 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 5 Mar 2013 17:45:59 +0800 Subject: [PATCH 093/406] Fix bug: Count commander as played for achievements. --- sim.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sim.cpp b/sim.cpp index 50e668f3..f9441838 100644 --- a/sim.cpp +++ b/sim.cpp @@ -517,6 +517,9 @@ unsigned play(Field* fd) unsigned p0_size = fd->players[0]->deck->cards.size(); fd->last_decision_turn = p0_size * 2 - (surge ? 2 : 3); + // Count commander as played for achievements (not count in type / faction / rarity requirements) + fd->inc_counter(fd->achievement.unit_played, fd->players[0]->commander.m_card->m_id); + // Shuffle deck while(fd->turn <= turn_limit && !fd->end) { From ef4e003252fd37f2415d3bfe9953814b94051361 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 5 Mar 2013 17:55:50 +0800 Subject: [PATCH 094/406] Fix bug: Hill-climbing wrongly prevented a card to be replaced by its upgraded version. --- tyrant_optimize.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 5a1c5512..f75c5f16 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -182,7 +182,7 @@ bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) { for(unsigned i(0); i < deck.cards.size(); ++i) { - if(i != slot && deck.cards[i]->m_base_id == card->m_base_id) + if(i != slot && deck.cards[i]->m_name == card->m_name) { return(false); } @@ -546,7 +546,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) { // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); - if(commander_candidate->m_base_id == best_commander->m_base_id) { continue; } + if(commander_candidate->m_name == best_commander->m_name) { continue; } if(!suitable_commander(commander_candidate)) { continue; } // Place it in the deck d1->commander = commander_candidate; @@ -576,7 +576,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) { // Various checks to check if the card is accepted assert(card_candidate->m_type != CardType::commander); - if(slot_i < best_cards.size() && card_candidate->m_base_id == best_cards[slot_i]->m_base_id) { continue; } + if(slot_i < best_cards.size() && card_candidate->m_name == best_cards[slot_i]->m_name) { continue; } if(!suitable_non_commander(*d1, slot_i, card_candidate)) { continue; } // Place it in the deck if(slot_i == d1->cards.size()) @@ -652,7 +652,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) if(best_score == best_possible) { break; } // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); - if(commander_candidate->m_base_id == best_commander->m_base_id) { continue; } + if(commander_candidate->m_name == best_commander->m_name) { continue; } if(!suitable_commander(commander_candidate)) { continue; } // Place it in the deck d1->commander = commander_candidate; @@ -685,7 +685,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) if(card_candidate) { // Various checks to check if the card is accepted - if(to_slot < best_cards.size() && card_candidate->m_base_id == best_cards[to_slot]->m_base_id) { continue; } + if(to_slot < best_cards.size() && card_candidate->m_name == best_cards[to_slot]->m_name) { continue; } if(!suitable_non_commander(*d1, from_slot, card_candidate)) { continue; } // Place it in the deck if(from_slot < d1->cards.size()) From db5972089b08c292c9ef0df5d19181964f4fbd01 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 6 Mar 2013 15:56:38 +0800 Subject: [PATCH 095/406] Code compatibility. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index f9441838..89ce842b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -412,7 +412,7 @@ struct PlayCard fd->inc_counter(fd->achievement.unit_faction_played, card->m_faction); fd->inc_counter(fd->achievement.unit_rarity_played, card->m_rarity); } - _DEBUG_MSG("%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), storage->size() - 1, card_description(fd, card).c_str()); + _DEBUG_MSG("%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), static_cast(storage->size() - 1), card_description(fd, card).c_str()); } // all except assault: noop From cbc5ef4ce350320fe3f981cee46bf04fc93de78f Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 6 Mar 2013 15:58:48 +0800 Subject: [PATCH 096/406] Count as multiple uses of attack for Flurry and/or Swipe. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 89ce842b..b5ee19df 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1097,6 +1097,7 @@ struct PerformAttack template void op(unsigned pre_modifier_dmg) { + count_achievement(fd, att_status); modify_attack_damage(pre_modifier_dmg); // Evaluation order: // assaults only: fly check @@ -1382,7 +1383,6 @@ void attack_phase(Field* fd) att_status->m_attacked = true; unsigned pre_modifier_dmg = attack_power(att_status); if(pre_modifier_dmg == 0) { return; } - count_achievement(fd, att_status); unsigned num_attacks(1); if(att_status->m_card->m_flurry > 0 && skill_check(fd, att_status, nullptr)) { From f5331beafe2979c73c7c10e207fdf4184add70c1 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 8 Mar 2013 16:36:02 +0800 Subject: [PATCH 097/406] Improve output. --- sim.h | 7 +++ tyrant_optimize.cpp | 131 ++++++++++++++++++++++++++++---------------- 2 files changed, 92 insertions(+), 46 deletions(-) diff --git a/sim.h b/sim.h index 58ef5363..4db621d7 100644 --- a/sim.h +++ b/sim.h @@ -23,6 +23,13 @@ extern bool win_tie; void fill_skill_table(); unsigned play(Field* fd); void modify_cards(Cards& cards, enum Effect effect); +//---------------------- Represent Simulation Results ---------------------------- +// TODO enrich Results and make play() return Results +struct Results +{ + unsigned wins = 0; + unsigned points = 0; +}; // Pool-based indexed storage. //---------------------- Pool-based indexed storage ---------------------------- template diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index f75c5f16..a606e20c 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -209,40 +209,18 @@ bool suitable_commander(const Card* card) return(true); } //------------------------------------------------------------------------------ -double compute_efficiency(const std::pair , unsigned>& results) +double compute_score(const std::pair , unsigned>& results, std::vector& factors) { - if(results.second == 0) { return(0.); } double sum{0.}; for(unsigned index(0); index < results.first.size(); ++index) { - sum += (double)results.second / results.first[index]; + sum += (use_anp ? results.first[index].points : results.first[index].wins) * factors[index]; } - return(results.first.size() / sum); -} -//------------------------------------------------------------------------------ -bool use_efficiency{false}; -double compute_score(const std::pair , unsigned>& results, std::vector& factors) -{ - double score{0.}; - if(use_efficiency) - { - score = compute_efficiency(results); - } - else - { - if(results.second == 0) { score = 0.; } - double sum{0.}; - for(unsigned index(0); index < results.first.size(); ++index) - { - sum += results.first[index] * factors[index]; - } - score = sum / std::accumulate(factors.begin(), factors.end(), 0.) / (double)results.second; - } - return(score); + return(sum / std::accumulate(factors.begin(), factors.end(), 0.) / (double)results.second); } //------------------------------------------------------------------------------ volatile unsigned thread_num_iterations{0}; // written by threads -std::vector thread_score; // written by threads +std::vector thread_results; // written by threads volatile unsigned thread_total{0}; // written by threads volatile double thread_prev_score{0.0}; volatile bool thread_compare{false}; @@ -368,23 +346,23 @@ class Process for(auto data: threads_data) { delete(data); } } - std::pair , unsigned> evaluate(unsigned num_iterations) + std::pair , unsigned> evaluate(unsigned num_iterations) { thread_num_iterations = num_iterations; - thread_score = std::vector(def_decks.size(), 0u); + thread_results = std::vector(def_decks.size()); thread_total = 0; thread_compare = false; // unlock all the threads main_barrier.wait(); // wait for the threads main_barrier.wait(); - return(std::make_pair(thread_score, thread_total)); + return(std::make_pair(thread_results, thread_total)); } - std::pair , unsigned> compare(unsigned num_iterations, double prev_score) + std::pair , unsigned> compare(unsigned num_iterations, double prev_score) { thread_num_iterations = num_iterations; - thread_score = std::vector(def_decks.size(), 0u); + thread_results = std::vector(def_decks.size()); thread_total = 0; thread_prev_score = prev_score; thread_compare = true; @@ -393,7 +371,7 @@ class Process main_barrier.wait(); // wait for the threads main_barrier.wait(); - return(std::make_pair(thread_score, thread_total)); + return(std::make_pair(thread_results, thread_total)); } }; //------------------------------------------------------------------------------ @@ -425,16 +403,15 @@ void thread_evaluate(boost::barrier& main_barrier, shared_mutex.unlock(); //>>>> std::vector result{sim.evaluate()}; shared_mutex.lock(); //<<<< - std::vector thread_score_local(thread_score.size(), 0); //! + std::vector thread_score_local(thread_results.size(), 0u); //! for(unsigned index(0); index < result.size(); ++index) { - // If not using ANP and we won, just count 1 win, not points. - if(!use_anp_local && result[index] != 0) + if (result[index] > 0) { - result[index] = 1; + thread_results[index].wins += 1; //! + thread_results[index].points += result[index]; //! } - thread_score[index] += result[index]; //! - thread_score_local[index] = thread_score[index]; // ! + thread_score_local[index] = (use_anp_local ? thread_results[index].points : thread_results[index].wins); // ! } ++thread_total; //! unsigned thread_total_local{thread_total}; //! @@ -481,20 +458,63 @@ void thread_evaluate(boost::barrier& main_barrier, } } //------------------------------------------------------------------------------ -void print_score_info(const std::pair , unsigned>& results, std::vector& factors) +void print_score_info(const std::pair , unsigned>& results, std::vector& factors) { if(use_anp) { - std::cout << "ANP: " << compute_score(results, factors) << std::endl; - return; + std::cout << "ANP: " << compute_score(results, factors); + if(results.first.size() > 1) + { + std::cout << " ("; + for(auto val: results.first) + { + std::cout << static_cast(val.points) / results.second << " "; + } + std::cout << "reps.)"; + } + std::cout << std::endl; + } + else + { + std::cout << "win%: " << compute_score(results, factors) * 100.0 << " ("; + for(auto val: results.first) + { + std::cout << val.wins << " "; + } + std::cout << "out of " << results.second << ")" << std::endl; + } +} +//------------------------------------------------------------------------------ +void print_results(const std::pair , unsigned>& results, std::vector& factors) +{ + double wins(0.); + double points(0.); + for(unsigned index(0); index < results.first.size(); ++index) + { + wins += results.first[index].wins * factors[index]; + points += results.first[index].points * factors[index]; } + wins /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; + points /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; - std::cout << "win%: " << compute_score(results, factors) * 100.0 << " ("; + std::cout << "win%: " << wins * 100.0 << " ("; for(auto val: results.first) { - std::cout << val << " "; + std::cout << val.wins << " "; + } + std::cout << "out of " << results.second << ")" << std::endl; + + std::cout << "ANP: " << points; + if(results.first.size() > 1) + { + std::cout << " ("; + for(auto val: results.first) + { + std::cout << static_cast(val.points) / results.second << " "; + } + std::cout << "reps.)"; } - std::cout << "out of " << results.second << ")\n" << std::flush; + std::cout << std::endl; } //------------------------------------------------------------------------------ void print_deck_inline(const double score, const Card *commander, std::vector cards) @@ -508,9 +528,28 @@ void print_deck_inline(const double score, const Card *commander, std::vectorm_name; + std::string last_name; + unsigned num_repeat(0); for(const Card* card: cards) { - std::cout << ", " << card->m_name; + if(card->m_name == last_name) + { + ++ num_repeat; + } + else + { + if(num_repeat > 1) + { + std::cout << " #" << num_repeat; + } + std::cout << ", " << card->m_name; + last_name = card->m_name; + num_repeat = 1; + } + } + if(num_repeat > 1) + { + std::cout << " #" << num_repeat; } std::cout << std::endl; } @@ -1232,7 +1271,7 @@ int main(int argc, char** argv) } case simulate: { auto results = p.evaluate(std::get<0>(op)); - print_score_info(results,p.factors); + print_results(results, p.factors); break; } case fightuntil: { From b6d62cdb14535b7bfdc4203ad57cb00f42babad8 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 8 Mar 2013 16:41:24 +0800 Subject: [PATCH 098/406] Simplify effect inconsistence check. --- tyrant_optimize.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index a606e20c..81b743f3 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1186,11 +1186,6 @@ int main(int argc, char** argv) att_deck->strategy = DeckStrategy::ordered; } - boost::optional quest_effect; - if(effect != Effect::none) - { - quest_effect = effect; - } std::vector def_decks; std::vector def_decks_factors; for(auto deck_parsed: deck_list_parsed) @@ -1228,18 +1223,17 @@ int main(int argc, char** argv) Effect this_effect = def_deck->effect; if(this_effect != Effect::none) { - if(quest_effect && *quest_effect != this_effect) + if(effect != Effect::none && effect != this_effect) { - std::cerr << "Error: Inconsistent effects: " << effect_names[*quest_effect] << " and " << effect_names[this_effect] << ".\n"; + std::cerr << "Error: Inconsistent effects: " << effect_names[effect] << " and " << effect_names[this_effect] << ".\n"; return(7); } - quest_effect = this_effect; + effect = this_effect; } def_decks.push_back(def_deck); def_decks_factors.push_back(deck_parsed.second); } - effect = quest_effect.get_value_or(effect); modify_cards(cards, effect); std::cout << "Attacker: " << (debug_print ? att_deck->long_description() : att_deck->short_description()) << std::endl; for(auto def_deck: def_decks) From ec07e631332df20312daa180302cca54c748cd90 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 8 Mar 2013 17:23:10 +0800 Subject: [PATCH 099/406] Support -e id. Support deck names like "Mission #id", "Raid #id" and "Quest #id". --- deck.cpp | 2 +- tyrant_optimize.cpp | 19 ++++++++++++++++++- xml.cpp | 3 +++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/deck.cpp b/deck.cpp index dd6450b3..b8f2101e 100644 --- a/deck.cpp +++ b/deck.cpp @@ -139,7 +139,7 @@ std::string Deck::short_description() const { std::stringstream ios; ios << decktype_names[decktype]; - if(id > 0) { ios << " " << id; } + if(id > 0) { ios << " #" << id; } if(!name.empty()) { ios << " \"" << name << "\""; } if(raid_cards.empty()) { ios << ": " << deck_hash(commander, cards); } return ios.str(); diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 81b743f3..85d135c5 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -980,6 +980,15 @@ void print_available_decks(const Decks& decks, bool allow_card_pool) } } } +//------------------------------------------------------------------------------ +void print_available_effects() +{ + std::cout << "Available effects:" << std::endl; + for(int i(1); i < Effect::num_effects; ++ i) + { + std::cout << i << " \"" << effect_names[i] << "\"" << std::endl; + } +} void usage(int argc, char** argv) { @@ -1046,6 +1055,9 @@ int main(int argc, char** argv) for(unsigned i(0); i < Effect::num_effects; ++i) { effect_map[effect_names[i]] = i; + std::stringstream ss; + ss << i; + effect_map[ss.str()] = i; } std::vector > todo; @@ -1078,7 +1090,8 @@ int main(int argc, char** argv) auto x = effect_map.find(arg_effect); if(x == effect_map.end()) { - std::cout << "The effect '" << arg_effect << "' was not found.\n"; + std::cout << "The effect '" << arg_effect << "' was not found. "; + print_available_effects(); return(6); } effect = static_cast(x->second); @@ -1240,6 +1253,10 @@ int main(int argc, char** argv) { std::cout << "Defender: " << (debug_print ? def_deck->long_description() : def_deck->short_description()) << std::endl; } + if(effect != Effect::none) + { + std::cout << "Effect: " << effect_names[effect] << std::endl; + } Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); { diff --git a/xml.cpp b/xml.cpp index 71f7a218..aa5e3ad9 100644 --- a/xml.cpp +++ b/xml.cpp @@ -371,6 +371,9 @@ Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::De Deck* deck = &decks.decks.back(); deck->set(commander_card, always_cards, some_cards); decks.by_name[deck_name] = deck; + std::stringstream alt_name; + alt_name << decktype_names[decktype] << " #" << id; + decks.by_name[alt_name.str()] = deck; return deck; } //------------------------------------------------------------------------------ From 18f5a22ae1efc5b15560162120040026e2fc7d8a Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 8 Mar 2013 18:06:26 +0800 Subject: [PATCH 100/406] Add version number for Fansite. --- tyrant.h | 2 ++ tyrant_optimize.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/tyrant.h b/tyrant.h index 65f0a9e8..670d0b00 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,6 +1,8 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED +#define TYRANT_OPTIMIZER_VERSION "1.0.0" + #include #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 85d135c5..ca2dcbdb 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1044,6 +1044,11 @@ int main(int argc, char** argv) if(argc <= 2) { + if(strcmp(argv[1], "-version") == 0) + { + std::cout << "Tyrant Optimizer " << TYRANT_OPTIMIZER_VERSION << std::endl; + return(0); + } print_available_decks(decks, true); return(4); } From 57e1be35f6b7281dffb5fbef5837664f1579b91f Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 9 Mar 2013 22:01:24 +0800 Subject: [PATCH 101/406] Support achievements "SMASH!" and "Rally Free Zone" --- achievement.h | 1 + sim.cpp | 32 ++++++++++++++++++++------------ sim.h | 16 ++++++++++++++++ tyrant.cpp | 4 ++++ tyrant.h | 8 ++++++++ xml.cpp | 7 +++++++ 6 files changed, 56 insertions(+), 12 deletions(-) diff --git a/achievement.h b/achievement.h index a7258181..6caf18d9 100644 --- a/achievement.h +++ b/achievement.h @@ -71,6 +71,7 @@ struct Achievement std::map unit_faction_played; std::map unit_rarity_played; std::map unit_type_killed; + std::map misc_req; Achievement() : id(0) {} }; diff --git a/sim.cpp b/sim.cpp index b5ee19df..9e9add0e 100644 --- a/sim.cpp +++ b/sim.cpp @@ -248,29 +248,33 @@ inline void print_achievement_results(Field* fd) return; } _DEBUG_MSG("Achievement:\n"); - for(auto i(fd->achievement.skill_used.begin()); i != fd->achievement.skill_used.end(); ++i) + for(auto i : fd->achievement.skill_used) { - _DEBUG_MSG(" Use skills: %s %u%s? %s\n", skill_names[i->first].c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + _DEBUG_MSG(" Use skills: %s %u%s? %s\n", skill_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } - for(auto i(fd->achievement.unit_played.begin()); i != fd->achievement.unit_played.end(); ++i) + for(auto i : fd->achievement.unit_played) { - _DEBUG_MSG(" Play units: %s %u%s? %s\n", fd->cards.by_id(i->first)->m_name.c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + _DEBUG_MSG(" Play units: %s %u%s? %s\n", fd->cards.by_id(i.first)->m_name.c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } - for(auto i(fd->achievement.unit_type_played.begin()); i != fd->achievement.unit_type_played.end(); ++i) + for(auto i : fd->achievement.unit_type_played) { - _DEBUG_MSG(" Play units of type: %s %u%s? %s\n", cardtype_names[i->first].c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + _DEBUG_MSG(" Play units of type: %s %u%s? %s\n", cardtype_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } - for(auto i(fd->achievement.unit_faction_played.begin()); i != fd->achievement.unit_faction_played.end(); ++i) + for(auto i : fd->achievement.unit_faction_played) { - _DEBUG_MSG(" Play units of faction: %s %u%s? %s\n", faction_names[i->first].c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + _DEBUG_MSG(" Play units of faction: %s %u%s? %s\n", faction_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } - for(auto i(fd->achievement.unit_rarity_played.begin()); i != fd->achievement.unit_rarity_played.end(); ++i) + for(auto i : fd->achievement.unit_rarity_played) { - _DEBUG_MSG(" Play units of rarity: %s %u%s? %s\n", rarity_names[i->first].c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + _DEBUG_MSG(" Play units of rarity: %s %u%s? %s\n", rarity_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } - for(auto i(fd->achievement.unit_type_killed.begin()); i != fd->achievement.unit_type_killed.end(); ++i) + for(auto i : fd->achievement.unit_type_killed) { - _DEBUG_MSG(" Kill units of type: %s %u%s? %s\n", cardtype_names[i->first].c_str(), fd->achievement_counter[i->second], fd->achievement.req_counter[i->second].str().c_str(), fd->achievement.req_counter[i->second].check(fd->achievement_counter[i->second]) ? "Yes" : "No"); + _DEBUG_MSG(" Kill units of type: %s %u%s? %s\n", cardtype_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); + } + for(auto i : fd->achievement.misc_req) + { + _DEBUG_MSG(" %s %u%s? %s\n", achievement_misc_req_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } } //------------------------------------------------------------------------------ @@ -1099,6 +1103,10 @@ struct PerformAttack { count_achievement(fd, att_status); modify_attack_damage(pre_modifier_dmg); + if(att_status->m_player == 0) + { + fd->update_max_counter(fd->achievement.misc_req, AchievementMiscReq::damage, att_dmg); + } // Evaluation order: // assaults only: fly check // assaults only: immobilize diff --git a/sim.h b/sim.h index 4db621d7..521e9a62 100644 --- a/sim.h +++ b/sim.h @@ -235,6 +235,22 @@ class Field { end = true; } +#endif + } + } + + template + inline void update_max_counter(T& container, unsigned key, unsigned value) + { + auto x = container.find(key); + if(x != container.end() && achievement_counter[x->second] < value) + { + achievement_counter[x->second] = value; +#if 0 + if(achievement.req_counter[x->second].predict_monoinc(achievement_counter[x->second]) < 0) + { + end = true; + } #endif } } diff --git a/tyrant.cpp b/tyrant.cpp index 213ad850..75dc6db9 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -60,3 +60,7 @@ std::string effect_names[Effect::num_effects] = { "Toxic", "Haunt", }; + +std::string achievement_misc_req_names[AchievementMiscReq::num_achievement_misc_reqs] = { + "Damage", +}; diff --git a/tyrant.h b/tyrant.h index 670d0b00..ae2bba5f 100644 --- a/tyrant.h +++ b/tyrant.h @@ -95,6 +95,14 @@ enum Effect { extern std::string effect_names[Effect::num_effects]; +enum AchievementMiscReq +{ + damage, + num_achievement_misc_reqs +}; + +extern std::string achievement_misc_req_names[num_achievement_misc_reqs]; + enum gamemode_t { fight, diff --git a/xml.cpp b/xml.cpp index aa5e3ad9..f1e8cbd7 100644 --- a/xml.cpp +++ b/xml.cpp @@ -527,6 +527,7 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement xml_attribute<>* num_used(req_node->first_attribute("num_used")); xml_attribute<>* num_played(req_node->first_attribute("num_played")); xml_attribute<>* num_killed(req_node->first_attribute("num_killed")); + xml_attribute<>* damage(req_node->first_attribute("damage")); if(num_turns && comparator == less_equal) { turn_limit = atoi(num_turns->value()); @@ -588,6 +589,12 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement achievement.req_counter.emplace_back(atoi(num_killed->value()), comparator); std::cout << " Kill units of type: " << cardtype_names[i] << achievement.req_counter.back().str() << std::endl; } + else if(damage) + { + achievement.misc_req[AchievementMiscReq::damage] = achievement.req_counter.size(); + achievement.req_counter.emplace_back(atoi(damage->value()), comparator); + std::cout << " " << achievement_misc_req_names[AchievementMiscReq::damage] << achievement.req_counter.back().str() << std::endl; + } else { throw std::runtime_error("Not implemented."); From 8640fe42d5c04b8343f3d664b1d3ea4f6927706c Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 11 Mar 2013 16:22:38 +0800 Subject: [PATCH 102/406] Commander's existing Chaos skill does not get replaced with Chaos All under 'Friendly Fire' battleground effect. --- sim.cpp | 61 ++++++++++++++------------------------------- tyrant.h | 2 +- tyrant_optimize.cpp | 10 ++++---- 3 files changed, 25 insertions(+), 48 deletions(-) diff --git a/sim.cpp b/sim.cpp index 9e9add0e..f9a1c2d7 100644 --- a/sim.cpp +++ b/sim.cpp @@ -2212,56 +2212,33 @@ void fill_skill_table() //------------------------------------------------------------------------------ // Utility functions for modify_cards. -// Adds the skill " " to all assaults, -// except those who have any instance of . -inline void assaults_gain_skill_if_have_not(Cards& cards, Skill new_skill, unsigned magnitude, bool all) +// Adds the skill " " to all cards of cardtype. +// If the card has an instance of in its skill list and replace_instance==true, the new skill replaces it. +// If the card has no instance of in its skill list, the new skill is added on to the end. +template +inline void cards_gain_skill(Cards& cards, Skill new_skill, unsigned magnitude, bool all, bool replace_instance) { for(Card* card: cards.cards) { - if(card->m_type != CardType::assault) + if(card->m_type != cardtype) { continue; } - bool conflict(false); + bool do_add_skill(true); for(auto& skill: card->m_skills) { if(std::get<0>(skill) == new_skill) { - conflict = true; - } - } - - if(!conflict) - { - card->add_skill(new_skill, magnitude, allfactions, all); - } - } -} -// Adds the skill " " to all commanders. -// If the commander has an instance of in its skill list, -// the new skill replaces it. -// Otherwise, the new skill is added on to the end. -inline void commanders_gain_skill(Cards& cards, Skill new_skill, unsigned magnitude, bool all) -{ - for(Card* card: cards.cards) - { - if(card->m_type != CardType::commander) - { - continue; - } - - bool replaced(false); - for(auto& skill: card->m_skills) - { - if(std::get<0>(skill) == new_skill) - { - skill = std::make_tuple(new_skill, magnitude, allfactions, all); - replaced = true; + if(replace_instance) + { + skill = std::make_tuple(new_skill, magnitude, allfactions, all); + } + do_add_skill = false; } } - if(!replaced) + if(do_add_skill) { card->add_skill(new_skill, magnitude, allfactions, all); } @@ -2275,7 +2252,7 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::none: break; case Effect::time_surge: - commanders_gain_skill(cards, rush, 1, false); + cards_gain_skill(cards, rush, 1, false, false); break; case Effect::copycat: // Do nothing; this is implemented in perform_mimic @@ -2314,20 +2291,20 @@ void modify_cards(Cards& cards, enum Effect effect) // Do nothing; these are implemented in the temporary_split skill break; case Effect::friendly_fire: - commanders_gain_skill(cards, chaos, 0, true); - assaults_gain_skill_if_have_not(cards, strike, 1, false); + cards_gain_skill(cards, strike, 1, false, false); + cards_gain_skill(cards, chaos, 0, true, false); break; case Effect::genesis: // Do nothing; this is implemented in play break; case Effect::decrepit: - commanders_gain_skill(cards, enfeeble, 1, true); + cards_gain_skill(cards, enfeeble, 1, true, true); break; case Effect::forcefield: - commanders_gain_skill(cards, protect, 1, true); + cards_gain_skill(cards, protect, 1, true, true); break; case Effect::chilling_touch: - commanders_gain_skill(cards, freeze, 0, false); + cards_gain_skill(cards, freeze, 0, false, false); break; case Effect::toxic: // Do nothing; this is implemented in PlayCard::fieldEffects diff --git a/tyrant.h b/tyrant.h index ae2bba5f..2c5fa053 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.0" +#define TYRANT_OPTIMIZER_VERSION "1.0.1" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index ca2dcbdb..9f1c8774 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1031,6 +1031,11 @@ void usage(int argc, char** argv) int main(int argc, char** argv) { if(argc == 1) { usage(argc, argv); return(0); } + if(argc <= 2 && strcmp(argv[1], "-version") == 0) + { + std::cout << "Tyrant Optimizer " << TYRANT_OPTIMIZER_VERSION << std::endl; + return(0); + } debug_print = getenv("DEBUG_PRINT"); unsigned num_threads = (debug_print || getenv("DEBUG")) ? 1 : 4; bool ordered = false; @@ -1044,11 +1049,6 @@ int main(int argc, char** argv) if(argc <= 2) { - if(strcmp(argv[1], "-version") == 0) - { - std::cout << "Tyrant Optimizer " << TYRANT_OPTIMIZER_VERSION << std::endl; - return(0); - } print_available_decks(decks, true); return(4); } From 548724f82055b2d0cfb617dc5ad9ad8444fa9d2b Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 11 Mar 2013 16:28:50 +0800 Subject: [PATCH 103/406] Augment affects 'On Attacked' skills. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index f9a1c2d7..2828a0f7 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1274,7 +1274,7 @@ struct PerformAttack for(auto& oa_skill: def_status->m_card->m_skills_on_attacked) { _DEBUG_MSG("Evaluating %s skill %s on attacked\n", status_description(def_status).c_str(), skill_description(fd, oa_skill).c_str()); - fd->skill_queue.emplace_back(def_status, oa_skill); + fd->skill_queue.emplace_back(def_status, def_status->m_augmented > 0 ? augmented_skill(def_status, oa_skill) : oa_skill); resolve_skill(fd); if(fd->end) { break; } } From 4344d0ff611fa14c5876055e9b85b50f9dfa4cdc Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 11 Mar 2013 16:31:52 +0800 Subject: [PATCH 104/406] Jammed Assaults cannot activate their On Death skills. --- sim.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sim.cpp b/sim.cpp index 2828a0f7..fda65e83 100644 --- a/sim.cpp +++ b/sim.cpp @@ -301,6 +301,10 @@ void prepend_on_death(Field* fd) { for(auto status: boost::adaptors::reverse(fd->killed_with_on_death)) { + if(status->m_jammed) + { + continue; + } for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_on_death)) { _DEBUG_MSG("On death skill pushed in front: %s\n", skill_description(fd, skill).c_str()); From 4b3a23ef27b18f4a6b2e06440ee37241532bebea Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 11 Mar 2013 17:23:25 +0800 Subject: [PATCH 105/406] Print out draw rate and loss rate for 'sim' operation. --- sim.cpp | 13 ++++--- sim.h | 25 +++++++++---- tyrant_optimize.cpp | 86 ++++++++++++++++++++++++++++++++------------- 3 files changed, 85 insertions(+), 39 deletions(-) diff --git a/sim.cpp b/sim.cpp index fda65e83..8410651f 100644 --- a/sim.cpp +++ b/sim.cpp @@ -290,7 +290,6 @@ void Hand::reset(std::mt19937& re) // the implementation of the attack by an assault card is in the next section; // the implementation of the active skills is in the section after that. unsigned turn_limit{50}; -bool win_tie{false}; //------------------------------------------------------------------------------ inline unsigned opponent(unsigned player) { @@ -507,7 +506,7 @@ void evaluate_legion(Field* fd); void prepend_on_death(Field* fd); bool check_and_perform_refresh(Field* fd, CardStatus* src_status); // return value : (raid points) -> attacker wins, 0 -> defender wins -unsigned play(Field* fd) +Results play(Field* fd) { fd->players[0]->commander.m_player = 0; fd->players[1]->commander.m_player = 1; @@ -671,13 +670,13 @@ unsigned play(Field* fd) if(fd->players[0]->commander.m_hp == 0) { _DEBUG_MSG("Defender wins.\n"); - return(0); + return {0, 0, 1, 0}; } // achievement: assuming winner='1' if (!made_achievement) { _DEBUG_MSG("Achievement fails.\n"); - return(0); + return {0, 0, 1, 0}; } // attacker wins if(fd->players[1]->commander.m_hp == 0) @@ -689,17 +688,17 @@ unsigned play(Field* fd) fd->points_since_last_decision = 10; } _DEBUG_MSG("Attacker wins.\n"); - return(10 + (speedy ? 5 : 0) + (fd->gamemode == surge ? 20 : 0) + fd->points_since_last_decision); + return {1, 0, 0, 10 + (speedy ? 5 : 0) + (fd->gamemode == surge ? 20 : 0) + fd->points_since_last_decision}; } if (fd->turn > turn_limit) { _DEBUG_MSG("Stall after %u turns.\n", turn_limit); - return(win_tie ? 1 : 0); + return {0, 1, 0, 0}; } // Huh? How did we get here? assert(false); - return 0; + return {0, 0, 0, 0}; } // For the active player, delay == 0 or blitzing; for the inactive player, delay <= 1. diff --git a/sim.h b/sim.h index 521e9a62..61f6d198 100644 --- a/sim.h +++ b/sim.h @@ -18,18 +18,29 @@ class Achievement; extern bool debug_print; extern bool debug_line; extern unsigned turn_limit; -extern bool win_tie; -void fill_skill_table(); -unsigned play(Field* fd); -void modify_cards(Cards& cards, enum Effect effect); //---------------------- Represent Simulation Results ---------------------------- -// TODO enrich Results and make play() return Results +template struct Results { - unsigned wins = 0; - unsigned points = 0; + result_type wins; + result_type draws; + result_type losses; + result_type points; + template + Results& operator+=(const Results& other) + { + wins += other.wins; + draws += other.draws; + losses += other.losses; + points += other.points; + return *this; + } }; + +void fill_skill_table(); +Results play(Field* fd); +void modify_cards(Cards& cards, enum Effect effect); // Pool-based indexed storage. //---------------------- Pool-based indexed storage ---------------------------- template diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 9f1c8774..e719ff1a 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -40,6 +40,7 @@ namespace { bool use_anp{false}; + bool win_tie{false}; gamemode_t gamemode{fight}; } @@ -209,18 +210,29 @@ bool suitable_commander(const Card* card) return(true); } //------------------------------------------------------------------------------ -double compute_score(const std::pair , unsigned>& results, std::vector& factors) +double compute_score(const std::pair> , unsigned>& results, std::vector& factors) { double sum{0.}; for(unsigned index(0); index < results.first.size(); ++index) { - sum += (use_anp ? results.first[index].points : results.first[index].wins) * factors[index]; + if(use_anp) + { + sum += results.first[index].points * factors[index]; + } + else if(win_tie) + { + sum += (results.first[index].wins + results.first[index].draws) * factors[index]; + } + else + { + sum += results.first[index].wins * factors[index]; + } } return(sum / std::accumulate(factors.begin(), factors.end(), 0.) / (double)results.second); } //------------------------------------------------------------------------------ volatile unsigned thread_num_iterations{0}; // written by threads -std::vector thread_results; // written by threads +std::vector> thread_results; // written by threads volatile unsigned thread_total{0}; // written by threads volatile double thread_prev_score{0.0}; volatile bool thread_compare{false}; @@ -278,15 +290,15 @@ struct SimulationData } } - inline std::vector evaluate() + inline std::vector> evaluate() { - std::vector res; + std::vector> res; for(Hand* def_hand: def_hands) { att_hand.reset(re); def_hand->reset(re); Field fd(re, cards, att_hand, *def_hand, gamemode, effect, achievement); - unsigned result(play(&fd)); + Results result(play(&fd)); res.emplace_back(result); } return(res); @@ -346,10 +358,10 @@ class Process for(auto data: threads_data) { delete(data); } } - std::pair , unsigned> evaluate(unsigned num_iterations) + std::pair> , unsigned> evaluate(unsigned num_iterations) { thread_num_iterations = num_iterations; - thread_results = std::vector(def_decks.size()); + thread_results = std::vector>(def_decks.size()); thread_total = 0; thread_compare = false; // unlock all the threads @@ -359,10 +371,10 @@ class Process return(std::make_pair(thread_results, thread_total)); } - std::pair , unsigned> compare(unsigned num_iterations, double prev_score) + std::pair> , unsigned> compare(unsigned num_iterations, double prev_score) { thread_num_iterations = num_iterations; - thread_results = std::vector(def_decks.size()); + thread_results = std::vector>(def_decks.size()); thread_total = 0; thread_prev_score = prev_score; thread_compare = true; @@ -401,17 +413,24 @@ void thread_evaluate(boost::barrier& main_barrier, { --thread_num_iterations; //! shared_mutex.unlock(); //>>>> - std::vector result{sim.evaluate()}; + std::vector> result{sim.evaluate()}; shared_mutex.lock(); //<<<< std::vector thread_score_local(thread_results.size(), 0u); //! for(unsigned index(0); index < result.size(); ++index) { - if (result[index] > 0) + thread_results[index] += result[index]; //! + if(use_anp_local) + { + thread_score_local[index] = thread_results[index].points; //! + } + else if(win_tie) { - thread_results[index].wins += 1; //! - thread_results[index].points += result[index]; //! + thread_score_local[index] = thread_results[index].wins + thread_results[index].draws; //! + } + else + { + thread_score_local[index] = thread_results[index].wins; //! } - thread_score_local[index] = (use_anp_local ? thread_results[index].points : thread_results[index].wins); // ! } ++thread_total; //! unsigned thread_total_local{thread_total}; //! @@ -458,7 +477,7 @@ void thread_evaluate(boost::barrier& main_barrier, } } //------------------------------------------------------------------------------ -void print_score_info(const std::pair , unsigned>& results, std::vector& factors) +void print_score_info(const std::pair> , unsigned>& results, std::vector& factors) { if(use_anp) { @@ -485,26 +504,43 @@ void print_score_info(const std::pair , unsigned>& results, } } //------------------------------------------------------------------------------ -void print_results(const std::pair , unsigned>& results, std::vector& factors) +void print_results(const std::pair> , unsigned>& results, std::vector& factors) { - double wins(0.); - double points(0.); + Results final{0, 0, 0, 0}; for(unsigned index(0); index < results.first.size(); ++index) { - wins += results.first[index].wins * factors[index]; - points += results.first[index].points * factors[index]; + final.wins += results.first[index].wins * factors[index]; + final.draws += results.first[index].draws * factors[index]; + final.losses += results.first[index].losses * factors[index]; + final.points += results.first[index].points * factors[index]; } - wins /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; - points /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; + final.wins /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; + final.draws /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; + final.losses /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; + final.points /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; - std::cout << "win%: " << wins * 100.0 << " ("; + std::cout << "win%: " << final.wins * 100.0 << " ("; for(auto val: results.first) { std::cout << val.wins << " "; } std::cout << "out of " << results.second << ")" << std::endl; - std::cout << "ANP: " << points; + std::cout << "draw%: " << final.draws * 100.0 << " ("; + for(auto val: results.first) + { + std::cout << val.draws << " "; + } + std::cout << "out of " << results.second << ")" << std::endl; + + std::cout << "loss%: " << final.losses * 100.0 << " ("; + for(auto val: results.first) + { + std::cout << val.losses << " "; + } + std::cout << "out of " << results.second << ")" << std::endl; + + std::cout << "ANP: " << final.points; if(results.first.size() > 1) { std::cout << " ("; From e2e75e68362041dabaab9273c287e9d56c889cb6 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 11 Mar 2013 18:35:17 +0800 Subject: [PATCH 106/406] Support all achievements of mission type. --- achievement.h | 1 + sim.cpp | 19 ++++++++++++++++--- sim.h | 14 ++++++++++++-- tyrant.cpp | 4 ++++ tyrant.h | 6 +++++- xml.cpp | 40 +++++++++++++++++++++++++++++++++------- 6 files changed, 71 insertions(+), 13 deletions(-) diff --git a/achievement.h b/achievement.h index 6caf18d9..3b6157bd 100644 --- a/achievement.h +++ b/achievement.h @@ -71,6 +71,7 @@ struct Achievement std::map unit_faction_played; std::map unit_rarity_played; std::map unit_type_killed; + std::map unit_with_skill_killed; std::map misc_req; Achievement() : id(0) {} diff --git a/sim.cpp b/sim.cpp index 8410651f..f08a3cf2 100644 --- a/sim.cpp +++ b/sim.cpp @@ -527,7 +527,7 @@ Results play(Field* fd) // Count commander as played for achievements (not count in type / faction / rarity requirements) fd->inc_counter(fd->achievement.unit_played, fd->players[0]->commander.m_card->m_id); - // Shuffle deck + fd->set_counter(fd->achievement.misc_req, AchievementMiscReq::turns, 1); while(fd->turn <= turn_limit && !fd->end) { fd->current_phase = Field::playcard_phase; @@ -656,6 +656,7 @@ Results play(Field* fd) std::swap(fd->tapi, fd->tipi); std::swap(fd->tap, fd->tip); ++fd->turn; + fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::turns); } bool made_achievement = true; for(unsigned i(0); made_achievement && i < fd->achievement.req_counter.size(); ++i) @@ -827,6 +828,10 @@ inline bool count_achievement(Field* fd, const CardStatus* c) if(c->m_player == 0) { fd->inc_counter(fd->achievement.skill_used, skill_id); + if(skill_id != Skill::attack) + { + fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::skill_activated); + } } return(true); } @@ -842,9 +847,16 @@ void remove_hp(Field* fd, CardStatus& status, unsigned dmg) if(status.m_hp == 0) { _DEBUG_MSG("%s dies\n", status_description(&status).c_str()); - if(status.m_player == 1 && !status.m_summoned) + if(status.m_player == 1) { - fd->inc_counter(fd->achievement.unit_type_killed, status.m_card->m_type); + if(!status.m_summoned) + { + fd->inc_counter(fd->achievement.unit_type_killed, status.m_card->m_type); + } + if(status.m_card->m_flying) + { + fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::unit_with_flying_killed); + } } if(status.m_card->m_skills_on_death.size() > 0) { @@ -1079,6 +1091,7 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg, bool count if(count_points && status.m_player == 1) { fd->points_since_last_decision += dmg; + fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::com_total, dmg); } if(status.m_hp == 0) { diff --git a/sim.h b/sim.h index 61f6d198..60c107e2 100644 --- a/sim.h +++ b/sim.h @@ -235,12 +235,22 @@ class Field } template - inline void inc_counter(T& container, unsigned key) + inline void set_counter(T& container, unsigned key, unsigned value) { auto x = container.find(key); if(x != container.end()) { - ++ achievement_counter[x->second]; + achievement_counter[x->second] = value; + } + } + + template + inline void inc_counter(T& container, unsigned key, unsigned value = 1) + { + auto x = container.find(key); + if(x != container.end()) + { + achievement_counter[x->second] += value; #if 0 if(achievement.req_counter[x->second].predict_monoinc(achievement_counter[x->second]) < 0) { diff --git a/tyrant.cpp b/tyrant.cpp index 75dc6db9..fa2d9b50 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -62,5 +62,9 @@ std::string effect_names[Effect::num_effects] = { }; std::string achievement_misc_req_names[AchievementMiscReq::num_achievement_misc_reqs] = { + "Kill units with skill: flying", + "Skill activated: (any)", + "Turns", "Damage", + "Total damage to the enemy Commander" }; diff --git a/tyrant.h b/tyrant.h index 2c5fa053..2967a9d4 100644 --- a/tyrant.h +++ b/tyrant.h @@ -97,7 +97,11 @@ extern std::string effect_names[Effect::num_effects]; enum AchievementMiscReq { - damage, + unit_with_flying_killed, // 104 Sky Control + skill_activated, // 105 Brute Strength + turns, // all "Speedy" and "Slow" + damage, // 168 SMASH!; 183 Rally Free Zone + com_total, // 169 Overkill; 170 EXTREME Overkill!!! num_achievement_misc_reqs }; diff --git a/xml.cpp b/xml.cpp index f1e8cbd7..7a326b65 100644 --- a/xml.cpp +++ b/xml.cpp @@ -527,13 +527,11 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement xml_attribute<>* num_used(req_node->first_attribute("num_used")); xml_attribute<>* num_played(req_node->first_attribute("num_played")); xml_attribute<>* num_killed(req_node->first_attribute("num_killed")); + xml_attribute<>* num_killed_with(req_node->first_attribute("num_killed_with")); xml_attribute<>* damage(req_node->first_attribute("damage")); - if(num_turns && comparator == less_equal) - { - turn_limit = atoi(num_turns->value()); - std::cout << " Turns <= " << turn_limit << std::endl; - } - else if(skill_id && num_used) + xml_attribute<>* com_total(req_node->first_attribute("com_total")); + xml_attribute<>* only(req_node->first_attribute("only")); + if(skill_id && num_used) { auto x = skill_map.find(skill_id->value()); if(x == skill_map.end()) @@ -566,7 +564,7 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement auto i = map_to_faction(atoi(unit_race->value())); if(i == Faction::allfactions) { - throw std::runtime_error(std::string("Invalid unit_race ") + unit_race->value()); + throw std::runtime_error(std::string("Unknown unit_race ") + unit_race->value()); } achievement.unit_faction_played[i] = achievement.req_counter.size(); achievement.req_counter.emplace_back(atoi(num_played->value()), comparator); @@ -589,12 +587,40 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement achievement.req_counter.emplace_back(atoi(num_killed->value()), comparator); std::cout << " Kill units of type: " << cardtype_names[i] << achievement.req_counter.back().str() << std::endl; } + else if(num_killed_with && skill_id && strcmp(skill_id->value(), "flying") == 0) + { + achievement.misc_req[AchievementMiscReq::unit_with_flying_killed] = achievement.req_counter.size(); + achievement.req_counter.emplace_back(atoi(num_killed_with->value()), comparator); + std::cout << " " << achievement_misc_req_names[AchievementMiscReq::unit_with_flying_killed] << achievement.req_counter.back().str() << std::endl; + } + else if(only && skill_id && strcmp(skill_id->value(), "0") == 0) + { + achievement.misc_req[AchievementMiscReq::skill_activated] = achievement.req_counter.size(); + achievement.req_counter.emplace_back(0, equal); + std::cout << " " << achievement_misc_req_names[AchievementMiscReq::skill_activated] << achievement.req_counter.back().str() << std::endl; + } + else if(num_turns) + { + if(comparator == less_equal) + { + turn_limit = atoi(num_turns->value()); + } + achievement.misc_req[AchievementMiscReq::turns] = achievement.req_counter.size(); + achievement.req_counter.emplace_back(atoi(num_turns->value()), comparator); + std::cout << " " << achievement_misc_req_names[AchievementMiscReq::turns] << achievement.req_counter.back().str() << std::endl; + } else if(damage) { achievement.misc_req[AchievementMiscReq::damage] = achievement.req_counter.size(); achievement.req_counter.emplace_back(atoi(damage->value()), comparator); std::cout << " " << achievement_misc_req_names[AchievementMiscReq::damage] << achievement.req_counter.back().str() << std::endl; } + else if(com_total) + { + achievement.misc_req[AchievementMiscReq::com_total] = achievement.req_counter.size(); + achievement.req_counter.emplace_back(atoi(com_total->value()), comparator); + std::cout << " " << achievement_misc_req_names[AchievementMiscReq::com_total] << achievement.req_counter.back().str() << std::endl; + } else { throw std::runtime_error("Not implemented."); From 69f1ce79bf97de92ffe3e49966356051af31e843 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 14 Mar 2013 09:10:54 +0800 Subject: [PATCH 107/406] Add Stun skill (in advance). --- card.h | 2 ++ sim.cpp | 24 ++++++++++++++++++------ sim.h | 1 + tyrant.cpp | 2 +- tyrant.h | 2 +- xml.cpp | 2 ++ 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/card.h b/card.h index 5403bc79..1a9c7a27 100644 --- a/card.h +++ b/card.h @@ -46,6 +46,7 @@ class Card m_set(0), m_siphon(0), m_split(false), + m_stun(false), m_swipe(false), m_tribute(false), m_unique(false), @@ -104,6 +105,7 @@ class Card int m_set; unsigned m_siphon; bool m_split; + bool m_stun; bool m_swipe; bool m_tribute; bool m_unique; diff --git a/sim.cpp b/sim.cpp index f08a3cf2..ccb4180b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -57,6 +57,7 @@ CardStatus::CardStatus(const Card* card) : m_poisoned(0), m_protected(0), m_rallied(0), + m_stunned(0), m_weakened(0), m_temporary_split(false), m_summoned(false), @@ -92,6 +93,7 @@ inline void CardStatus::set(const Card& card) m_protected = 0; m_rallied = 0; m_weakened = 0; + m_stunned = 0; m_temporary_split = false; m_summoned = false; m_attacked = false; @@ -231,6 +233,7 @@ std::string CardStatus::description() if(m_enfeebled > 0) { desc += ", enfeebled " + to_string(m_enfeebled); } if(m_poisoned > 0) { desc += ", poisoned " + to_string(m_poisoned); } if(m_protected > 0) { desc += ", protected " + to_string(m_protected); } + if(m_stunned > 0) { desc += ", stunned " + to_string(m_stunned); } // if(m_attacked) { desc += ", attacked"; } desc += "]"; return(desc); @@ -643,7 +646,7 @@ Results play(Field* fd) // Evaluate skills evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); // Attack - if(!fd->end && !current_status.m_immobilized && current_status.m_hp > 0) + if(!fd->end && !current_status.m_immobilized && !current_status.m_stunned && current_status.m_hp > 0) { attack_phase(fd); } @@ -979,6 +982,7 @@ void turn_start_phase(Field* fd) status.m_immobilized = false; status.m_jammed = false; status.m_rallied = 0; + if(status.m_stunned > 0) { -- status.m_stunned; } status.m_weakened = 0; status.m_temporary_split = false; status.m_attacked = false; @@ -1268,6 +1272,13 @@ struct PerformAttack template void on_attacked() { + if(att_status->m_card->m_type == CardType::assault && def_status->m_card->m_stun) + { + count_achievement(fd, def_status); + // perform_skill_stun + _DEBUG_MSG("%s stuns %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); + att_status->m_stunned = 2; + } if(def_status->m_card->m_poison_oa > att_status->m_poisoned && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); @@ -1414,7 +1425,7 @@ void attack_phase(Field* fd) _DEBUG_MSG("%s activates Flurry\n", status_description(att_status).c_str()); num_attacks += att_status->m_card->m_flurry; } - for(unsigned attack_index(0); attack_index < num_attacks && !att_status->m_jammed && !att_status->m_frozen && att_status->m_hp > 0 && fd->tip->commander.m_hp > 0; ++attack_index) + for(unsigned attack_index(0); attack_index < num_attacks && !att_status->m_jammed && !att_status->m_stunned && !att_status->m_frozen && att_status->m_hp > 0 && fd->tip->commander.m_hp > 0; ++attack_index) { // 3 possibilities: // - 1. attack against the assault in front @@ -1443,7 +1454,7 @@ void attack_phase(Field* fd) if(fd->end) { return; } // stille alive? attack the card in front - if(att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci)) + if(att_status->m_hp > 0 && !att_status->m_stunned && alive_assault(def_assaults, fd->current_ci)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(pre_modifier_dmg); } @@ -1454,7 +1465,7 @@ void attack_phase(Field* fd) if(fd->end) { return; } // still alive? attack the card on the right - if(!fd->end && att_status->m_hp > 0 && alive_assault(def_assaults, fd->current_ci + 1)) + if(!fd->end && att_status->m_hp > 0 && !att_status->m_stunned && alive_assault(def_assaults, fd->current_ci + 1)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(pre_modifier_dmg); } @@ -1558,7 +1569,7 @@ inline bool skill_predicate(Field* fd, CardStatus* c) template<> inline bool skill_predicate(Field* fd, CardStatus* c) -{ return(!c->m_immobilized && !c->m_attacked && can_act(fd, c)); } +{ return(!c->m_immobilized && !c->m_stunned && !c->m_attacked && can_act(fd, c)); } template<> inline bool skill_predicate(Field* fd, CardStatus* c) @@ -1588,7 +1599,7 @@ inline bool skill_predicate(Field* fd, CardStatus* c) template<> inline bool skill_predicate(Field* fd, CardStatus* c) -{ return(!c->m_immobilized && !c->m_attacked && attack_power(c) > 0 && can_act(fd, c)); } +{ return(!c->m_immobilized && !c->m_stunned && !c->m_attacked && attack_power(c) > 0 && can_act(fd, c)); } template inline void perform_skill(Field* fd, CardStatus* c, unsigned v) @@ -1627,6 +1638,7 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) c->m_immobilized = false; c->m_jammed = false; c->m_poisoned = 0; + c->m_stunned = 0; } template<> diff --git a/sim.h b/sim.h index 60c107e2..fc187e0c 100644 --- a/sim.h +++ b/sim.h @@ -127,6 +127,7 @@ struct CardStatus unsigned m_poisoned; unsigned m_protected; unsigned m_rallied; + unsigned m_stunned; unsigned m_weakened; bool m_temporary_split; bool m_summoned; // is this card summoned? diff --git a/tyrant.cpp b/tyrant.cpp index fa2d9b50..3bf17847 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -20,7 +20,7 @@ std::string skill_names[Skill::num_skills] = // Damage-Dependant: "Berserk", "Crush", "Disease", "Immobilize", "Leech", "Poison", "Siphon", // Defensive: - "Armored", "Counter", "Emulate", "Evade", "Flying", "Intercept", "Payback", "Refresh", "Regenerate", "Tribute", "Wall", + "Armored", "Counter", "Emulate", "Evade", "Flying", "Intercept", "Payback", "Refresh", "Regenerate", "Stun", "Tribute", "Wall", // Triggered: "Blitz", "Legion", // Static (Ignored): diff --git a/tyrant.h b/tyrant.h index 2967a9d4..fb4b6bfb 100644 --- a/tyrant.h +++ b/tyrant.h @@ -32,7 +32,7 @@ enum Skill // Damage-Dependant: berserk, crush, disease, immobilize, leech, poison, siphon, // Defensive: - armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, tribute, wall, + armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, tribute, wall, // Triggered: blitz, legion, // Static, ignored: diff --git a/xml.cpp b/xml.cpp index 7a326b65..a54175be 100644 --- a/xml.cpp +++ b/xml.cpp @@ -266,6 +266,8 @@ void read_cards(Cards& cards) { c->m_siphon = atoi(skill->first_attribute("x")->value()); } if(strcmp(skill->first_attribute("id")->value(), "split") == 0) { c->m_split = true; } + if(strcmp(skill->first_attribute("id")->value(), "stun") == 0) + { c->m_stun = true; } if(strcmp(skill->first_attribute("id")->value(), "swipe") == 0) { c->m_swipe = true; } if(strcmp(skill->first_attribute("id")->value(), "tribute") == 0) From 4ccf042ce66f09407367793c11c2e9c99a200e54 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 15 Mar 2013 13:30:45 +0800 Subject: [PATCH 108/406] Add Activation Modifier into SkillSpec. --- card.h | 10 +++--- sim.cpp | 96 +++++++++++++++++++++++++++--------------------------- tyrant.cpp | 6 ++-- tyrant.h | 20 +++++++++--- 4 files changed, 73 insertions(+), 59 deletions(-) diff --git a/card.h b/card.h index 1a9c7a27..0e4d22cb 100644 --- a/card.h +++ b/card.h @@ -58,15 +58,15 @@ class Card } void add_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills.push_back(std::make_tuple(v1, v2, v3, v4)); } + { m_skills.push_back(std::make_tuple(v1, v2, v3, v4, on_act)); } void add_played_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_play.push_back(std::make_tuple(v1, v2, v3, v4)); } + { m_skills_on_play.push_back(std::make_tuple(v1, v2, v3, v4, on_play)); } void add_died_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_death.push_back(std::make_tuple(v1, v2, v3, v4)); } + { m_skills_on_death.push_back(std::make_tuple(v1, v2, v3, v4, on_death)); } void add_attacked_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_attacked.push_back(std::make_tuple(v1, v2, v3, v4)); } + { m_skills_on_attacked.push_back(std::make_tuple(v1, v2, v3, v4, on_attacked)); } void add_kill_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_kill.push_back(std::make_tuple(v1, v2, v3, v4)); } + { m_skills_on_kill.push_back(std::make_tuple(v1, v2, v3, v4, on_kill)); } unsigned m_antiair; unsigned m_armored; diff --git a/sim.cpp b/sim.cpp index ccb4180b..4814dee7 100644 --- a/sim.cpp +++ b/sim.cpp @@ -118,7 +118,8 @@ std::string skill_description(Field* fd, const SkillSpec& s) return(skill_names[std::get<0>(s)] + (std::get<3>(s) ? " all" : "") + (std::get<2>(s) == allfactions ? "" : std::string(" ") + faction_names[std::get<2>(s)]) + - (std::get<1>(s) == 0 ? "" : std::string(" ") + to_string(std::get<1>(s)))); + (std::get<1>(s) == 0 ? "" : std::string(" ") + to_string(std::get<1>(s))) + + (std::get<4>(s) == on_act ? "" : std::string(" on ") + skill_activation_modifier_names[std::get<4>(s)])); } } //------------------------------------------------------------------------------ @@ -175,13 +176,13 @@ std::string card_description(Field* fd, const Card* c) if(c->m_valor > 0) { desc += ", valor " + to_string(c->m_valor); } if(c->m_wall) { desc += ", wall"; } for(auto& skill: c->m_skills) { desc += ", " + skill_description(fd, skill); } - for(auto& skill: c->m_skills_on_play) { desc += ", " + skill_description(fd, skill) + " on play"; } - for(auto& skill: c->m_skills_on_kill) { desc += ", " + skill_description(fd, skill) + " on kill"; } - if(c->m_berserk_oa > 0) { desc += ", berserk " + to_string(c->m_berserk_oa) + " on attacked"; } - if(c->m_disease_oa) { desc += ", disease on attacked"; } - if(c->m_poison_oa > 0) { desc += ", poison " + to_string(c->m_poison_oa) + " on attacked"; } - for(auto& skill: c->m_skills_on_attacked) { desc += ", " + skill_description(fd, skill) + " on attacked"; } - for(auto& skill: c->m_skills_on_death) { desc += ", " + skill_description(fd, skill) + " on death"; } + for(auto& skill: c->m_skills_on_play) { desc += ", " + skill_description(fd, skill); } + for(auto& skill: c->m_skills_on_kill) { desc += ", " + skill_description(fd, skill); } + if(c->m_berserk_oa > 0) { desc += ", berserk " + to_string(c->m_berserk_oa); } + if(c->m_disease_oa) { desc += ", disease on Attacked"; } + if(c->m_poison_oa > 0) { desc += ", poison " + to_string(c->m_poison_oa) + " on Attacked"; } + for(auto& skill: c->m_skills_on_attacked) { desc += ", " + skill_description(fd, skill); } + for(auto& skill: c->m_skills_on_death) { desc += ", " + skill_description(fd, skill); } return(desc); } //------------------------------------------------------------------------------ @@ -443,7 +444,7 @@ struct PlayCard { for(auto& skill: card->m_skills_on_play) { - _DEBUG_MSG("Evaluating %s skill %s on play\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); + _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(status, skill); resolve_skill(fd); if(fd->end) { break; } @@ -554,7 +555,7 @@ Results play(Field* fd) (fd->effect == Effect::clone_experiment && (fd->turn == 9 || fd->turn == 10))) { std::vector skills; - skills.emplace_back(temporary_split, 0, allfactions, false); + skills.emplace_back(temporary_split, 0, allfactions, false, on_act); // The skill doesn't actually come from the commander, // but we need to provide some source and it seemed most reasonable. evaluate_skills(fd, &fd->tap->commander, skills); @@ -595,7 +596,7 @@ Results play(Field* fd) { unsigned index(fd->rand(0, fd->cards.player_assaults.size() - 1)); std::vector skills; - skills.emplace_back(summon, fd->cards.player_assaults[index]->m_id, allfactions, false); + skills.emplace_back(summon, fd->cards.player_assaults[index]->m_id, allfactions, false, on_act); evaluate_skills(fd, &fd->tap->commander, skills); } @@ -632,7 +633,7 @@ Results play(Field* fd) _DEBUG_MSG("%s splits %s\n", status_description(¤t_status).c_str(), status_description(&status_split).c_str()); for(auto& skill: status_split.m_card->m_skills_on_play) { - _DEBUG_MSG("Evaluating %s skill %s on play\n", status_description(¤t_status).c_str(), skill_description(fd, skill).c_str()); + _DEBUG_MSG("Evaluating %s skill %s\n", status_description(¤t_status).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(&status_split, skill); resolve_skill(fd); if(fd->end) { break; } @@ -705,7 +706,7 @@ Results play(Field* fd) return {0, 0, 0, 0}; } -// For the active player, delay == 0 or blitzing; for the inactive player, delay <= 1. +// Can act by the end of next turn inline bool can_act(Field* fd, CardStatus* c) { return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen && (fd->tapi == c->m_player ? c->m_delay == 0 || c->m_blitzing : c->m_delay <= 1)); } // Can be healed / repaired inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } @@ -1300,7 +1301,7 @@ struct PerformAttack } for(auto& oa_skill: def_status->m_card->m_skills_on_attacked) { - _DEBUG_MSG("Evaluating %s skill %s on attacked\n", status_description(def_status).c_str(), skill_description(fd, oa_skill).c_str()); + _DEBUG_MSG("Evaluating %s skill %s\n", status_description(def_status).c_str(), skill_description(fd, oa_skill).c_str()); fd->skill_queue.emplace_back(def_status, def_status->m_augmented > 0 ? augmented_skill(def_status, oa_skill) : oa_skill); resolve_skill(fd); if(fd->end) { break; } @@ -1363,7 +1364,7 @@ void PerformAttack::on_kill() { for(auto& on_kill_skill: att_status->m_card->m_skills_on_kill) { - _DEBUG_MSG("Evaluating %s skill %s on kill\n", status_description(att_status).c_str(), skill_description(fd, on_kill_skill).c_str()); + _DEBUG_MSG("Evaluating %s skill %s\n", status_description(att_status).c_str(), skill_description(fd, on_kill_skill).c_str()); fd->skill_queue.emplace_back(att_status, on_kill_skill); resolve_skill(fd); if(fd->end) { break; } @@ -1505,11 +1506,11 @@ struct if_ }; template -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { assert(false); return(false); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { if(!c->m_attacked && can_act(fd, c)) { @@ -1523,11 +1524,11 @@ inline bool skill_predicate(Field* fd, CardStatus* c) } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(!c->m_chaosed && can_act(fd, c)); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0 && ( c->m_chaosed || @@ -1540,65 +1541,65 @@ inline bool skill_predicate(Field* fd, CardStatus* c) } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(can_be_healed(c)); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(c->m_faction != bloodthirsty); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(can_act(fd, c)); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(!c->m_immobilized && !c->m_stunned && !c->m_attacked && can_act(fd, c)); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(can_be_healed(c)); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(c->m_delay > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(can_be_healed(c)); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) // It is unnecessary to check for Blitz, since temporary_split status is // awarded before a card is played. { return(c->m_delay == 0 && c->m_hp > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c) +inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(!c->m_immobilized && !c->m_stunned && !c->m_attacked && attack_power(c) > 0 && can_act(fd, c)); } template @@ -1742,7 +1743,7 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector { for(auto card: cards) { - if(skill_predicate(fd, card)) + if(skill_predicate(fd, card, s)) { fd->selection_array[array_head] = card; ++array_head; @@ -1753,8 +1754,7 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector { for(auto card: cards) { - if(card->m_faction == std::get<2>(s) && - skill_predicate(fd, card)) + if(card->m_faction == std::get<2>(s) && skill_predicate(fd, card, s)) { fd->selection_array[array_head] = card; ++array_head; @@ -1774,7 +1774,7 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std const unsigned max_index(src_status->m_index + (src_status->m_index == cards.size() - 1 ? 0 : 1)); for(unsigned card_index(min_index); card_index <= max_index; ++card_index) { - if(skill_predicate(fd, cards[card_index])) + if(skill_predicate(fd, cards[card_index], s)) { fd->selection_array[array_head] = cards[card_index]; ++array_head; @@ -1789,7 +1789,7 @@ inline unsigned select_infuse(Field* fd, const SkillSpec& s) // Select candidates among attacker's assaults for(auto card_status: fd->tap->assaults.m_indirect) { - if(skill_predicate(fd, card_status)) + if(skill_predicate(fd, card_status, s)) { fd->selection_array[array_head] = card_status; ++array_head; @@ -1798,7 +1798,7 @@ inline unsigned select_infuse(Field* fd, const SkillSpec& s) // Select candidates among defender's assaults for(auto card_status: fd->tip->assaults.m_indirect) { - if(skill_predicate(fd, card_status)) + if(skill_predicate(fd, card_status, s)) { fd->selection_array[array_head] = card_status; ++array_head; @@ -1898,7 +1898,7 @@ void maybeTriggerRegen(Field* fd) template<> void maybeTriggerRegen(Field* fd) { - fd->skill_queue.emplace_front(nullptr, std::make_tuple(trigger_regen, 0, allfactions, false)); + fd->skill_queue.emplace_front(nullptr, std::make_tuple(trigger_regen, 0, allfactions, false, on_act)); } unsigned get_target_hostile_index(Field* fd, CardStatus* src_status, unsigned selection_array_size) @@ -2017,7 +2017,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski // Count at most once even targeting "All" is_count_achievement = false; // Payback - if(c->m_card->m_payback && skill_predicate(fd, src_status) && skill_check(fd, c, src_status)) + if(c->m_card->m_payback && skill_predicate(fd, src_status, s) && skill_check(fd, c, src_status)) { count_achievement(fd, c); _DEBUG_MSG("%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); @@ -2053,7 +2053,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil // Count at most once even targeting "All" is_count_achievement = false; // Tribute - if(c->m_card->m_tribute && skill_predicate(fd, src_status) && skill_check(fd, c, src_status)) + if(c->m_card->m_tribute && skill_predicate(fd, src_status, s) && skill_check(fd, c, src_status)) { count_achievement(fd, c); _DEBUG_MSG("Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); @@ -2064,7 +2064,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil if(opp->assaults.size() > c->m_index) { CardStatus& emulator = opp->assaults[c->m_index]; - if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator) && skill_check(fd, &emulator, nullptr)) + if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator, s) && skill_check(fd, &emulator, nullptr)) { count_achievement(fd, &emulator); _DEBUG_MSG("Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); @@ -2086,7 +2086,7 @@ void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) // Select candidates among attacker's assaults for(auto card_status: fd->players[src_status->m_player]->assaults.m_indirect) { - if(skill_predicate(fd, card_status)) + if(skill_predicate(fd, card_status, s)) { fd->selection_array[array_head] = card_status; ++array_head; @@ -2095,7 +2095,7 @@ void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) // Select candidates among defender's assaults for(auto card_status: fd->players[opponent(src_status->m_player)]->assaults.m_indirect) { - if(skill_predicate(fd, card_status)) + if(skill_predicate(fd, card_status, s)) { fd->selection_array[array_head] = card_status; ++array_head; @@ -2202,7 +2202,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) if(std::get<0>(skill) == mimic || (std::get<0>(skill) == supply && src_status->m_card->m_type != CardType::assault)) { continue; } - SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill)); + SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill), on_act); _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(src_status, src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); resolve_skill(fd); @@ -2260,7 +2260,7 @@ inline void cards_gain_skill(Cards& cards, Skill new_skill, unsigned magnitude, { if(replace_instance) { - skill = std::make_tuple(new_skill, magnitude, allfactions, all); + skill = std::make_tuple(new_skill, magnitude, allfactions, all, on_act); } do_add_skill = false; } diff --git a/tyrant.cpp b/tyrant.cpp index 3bf17847..c8da5c3f 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -7,6 +7,8 @@ const std::string faction_names[Faction::num_factions] = std::string skill_names[Skill::num_skills] = { + // Attack: + "0", // Activation (Including Destroyed): "Augment", "Backfire", "Chaos", "Cleanse", "Enfeeble", "Freeze", "Heal", "Infuse", "Jam", @@ -25,14 +27,14 @@ std::string skill_names[Skill::num_skills] = "Blitz", "Legion", // Static (Ignored): /* "Blizzard", "Fusion", "Mist", */ - // Misc: - "0", }; std::set helpful_skills{ augment, cleanse, heal, protect, rally, repair, rush, supply, }; +std::string skill_activation_modifier_names[num_skill_activation_modifiers] = {"", "Play", "Attacked", "Kill", "Death", }; + std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Structure", "Action", }; std::string rarity_names[5]{"", "common", "uncommon", "rare", "legendary", }; diff --git a/tyrant.h b/tyrant.h index fb4b6bfb..9101f8fa 100644 --- a/tyrant.h +++ b/tyrant.h @@ -21,6 +21,8 @@ extern const std::string faction_names[num_factions]; enum Skill { + // Attack: + attack, // Activation (including Destroyed): augment, backfire, chaos, cleanse, enfeeble, freeze, heal, infuse, jam, mimic, protect, rally, recharge, repair, rush, shock, siege, strike, summon, supply, @@ -37,12 +39,22 @@ enum Skill blitz, legion, // Static, ignored: /* blizzard, fusion, mist, */ - // Misc: - attack, - num_skills}; + num_skills +}; extern std::string skill_names[num_skills]; extern std::set helpful_skills; +enum SkillActivationModifier +{ + on_act, + on_play, + on_attacked, + on_kill, + on_death, + num_skill_activation_modifiers +}; +extern std::string skill_activation_modifier_names[num_skill_activation_modifiers]; + namespace CardType { enum CardType { commander, @@ -136,6 +148,6 @@ enum SkillSourceType source_chaos }; -typedef std::tuple SkillSpec; +typedef std::tuple SkillSpec; #endif From 30cf48fd74d980e16c6f73af2cb8d831edd24ffc Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 15 Mar 2013 13:33:24 +0800 Subject: [PATCH 109/406] Hardcode cards with id 4000+ as assaults. --- xml.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xml.cpp b/xml.cpp index a54175be..c5e5c630 100644 --- a/xml.cpp +++ b/xml.cpp @@ -191,6 +191,8 @@ void read_cards(Cards& cards) { c->m_type = CardType::structure; } else if(id < 4000) { c->m_type = CardType::action; } + else if(id < 5000) + { c->m_type = CardType::assault; } else { c->m_type = cost_node ? (attack_node ? CardType::assault : CardType::structure) : (health_node ? CardType::commander : CardType::action); } if(attack_node) { c->m_attack = atoi(attack_node->value()); } From a0f90eded530ac0279624434e1f84cb3d8d42f7a Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 15 Mar 2013 18:00:26 +0800 Subject: [PATCH 110/406] Fix bugs: * Jam/Chaos/Weaken on Attacked and Weaken on Death should only target such Assault cards that neither have attacked nor are attacking. * Jam/Chaos on Death should only target such Assault cards that have not attacked. * Chaosed Jam/Chaos/Weaken should be able to target such Assault cards that have attacked. * Cleanse should target immobilized/stunned Assault cards. --- sim.cpp | 63 ++++++++++++++++++++++++++++++++++++-------------------- sim.h | 9 +++++++- tyrant.h | 2 +- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/sim.cpp b/sim.cpp index 4814dee7..fcf443e1 100644 --- a/sim.cpp +++ b/sim.cpp @@ -61,7 +61,7 @@ CardStatus::CardStatus(const Card* card) : m_weakened(0), m_temporary_split(false), m_summoned(false), - m_attacked(false) + m_step(CardStep::none) { } @@ -96,7 +96,7 @@ inline void CardStatus::set(const Card& card) m_stunned = 0; m_temporary_split = false; m_summoned = false; - m_attacked = false; + m_step = CardStep::none; } //------------------------------------------------------------------------------ inline unsigned safe_minus(unsigned x, unsigned y) @@ -235,7 +235,7 @@ std::string CardStatus::description() if(m_poisoned > 0) { desc += ", poisoned " + to_string(m_poisoned); } if(m_protected > 0) { desc += ", protected " + to_string(m_protected); } if(m_stunned > 0) { desc += ", stunned " + to_string(m_stunned); } -// if(m_attacked) { desc += ", attacked"; } +// if(m_step != CardStep::none) { desc += ", Step " + to_string(static_cast(m_step)); } desc += "]"; return(desc); } @@ -326,7 +326,10 @@ void resolve_skill(Field* fd) auto& status(std::get<0>(skill_instance)); auto& skill(std::get<1>(skill_instance)); fd->skill_queue.pop_front(); - skill_table[std::get<0>(skill)](fd, status, skill); + if(!status || !status->m_jammed) + { + skill_table[std::get<0>(skill)](fd, status, skill); + } } } //------------------------------------------------------------------------------ @@ -505,9 +508,18 @@ void PlayCard::onPlaySkills() } } //------------------------------------------------------------------------------ +inline bool is_attacking_or_has_attacked(CardStatus* c) { return(c->m_step >= CardStep::attacking); } +inline bool is_attacking(CardStatus* c) { return(c->m_step == CardStep::attacking); } +inline bool has_attacked(CardStatus* c) { return(c->m_step == CardStep::attacked); } +// Can act by the end of next turn +inline bool can_act(Field* fd, CardStatus* c) { return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen && (fd->tapi == c->m_player ? c->m_delay == 0 || c->m_blitzing : c->m_delay <= 1)); } +// Can attack by the end of next turn +inline bool can_attack(Field* fd, CardStatus* c) { return(can_act(fd, c) && !c->m_immobilized && !c->m_stunned); } +// Can be healed / repaired +inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } +//------------------------------------------------------------------------------ void turn_start_phase(Field* fd); void evaluate_legion(Field* fd); -void prepend_on_death(Field* fd); bool check_and_perform_refresh(Field* fd, CardStatus* src_status); // return value : (raid points) -> attacker wins, 0 -> defender wins Results play(Field* fd) @@ -616,7 +628,7 @@ Results play(Field* fd) { // ca: current assault CardStatus& current_status(fd->tap->assaults[fd->current_ci]); - if((current_status.m_delay > 0 && !current_status.m_blitzing) || current_status.m_hp == 0 || current_status.m_jammed || current_status.m_frozen) + if(!can_act(fd, ¤t_status)) { //_DEBUG_MSG("! Assault %u (%s) hp: %u, jammed %u\n", card_index, current_status.m_card->m_name.c_str(), current_status.m_hp, current_status.m_jammed); continue; @@ -649,8 +661,10 @@ Results play(Field* fd) // Attack if(!fd->end && !current_status.m_immobilized && !current_status.m_stunned && current_status.m_hp > 0) { + current_status.m_step = CardStep::attacking; attack_phase(fd); } + current_status.m_step = CardStep::attacked; } if(fd->end) { @@ -706,11 +720,6 @@ Results play(Field* fd) return {0, 0, 0, 0}; } -// Can act by the end of next turn -inline bool can_act(Field* fd, CardStatus* c) { return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen && (fd->tapi == c->m_player ? c->m_delay == 0 || c->m_blitzing : c->m_delay <= 1)); } -// Can be healed / repaired -inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } - // Check if a skill actually proc'ed. template inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) @@ -986,7 +995,7 @@ void turn_start_phase(Field* fd) if(status.m_stunned > 0) { -- status.m_stunned; } status.m_weakened = 0; status.m_temporary_split = false; - status.m_attacked = false; + status.m_step = CardStep::none; if(status.m_card->m_refresh) { check_and_perform_refresh(fd, &status); @@ -1416,7 +1425,6 @@ void attack_phase(Field* fd) { CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); - att_status->m_attacked = true; unsigned pre_modifier_dmg = attack_power(att_status); if(pre_modifier_dmg == 0) { return; } unsigned num_attacks(1); @@ -1426,7 +1434,7 @@ void attack_phase(Field* fd) _DEBUG_MSG("%s activates Flurry\n", status_description(att_status).c_str()); num_attacks += att_status->m_card->m_flurry; } - for(unsigned attack_index(0); attack_index < num_attacks && !att_status->m_jammed && !att_status->m_stunned && !att_status->m_frozen && att_status->m_hp > 0 && fd->tip->commander.m_hp > 0; ++attack_index) + for(unsigned attack_index(0); attack_index < num_attacks && can_attack(fd, att_status) && fd->tip->commander.m_hp > 0; ++attack_index) { // 3 possibilities: // - 1. attack against the assault in front @@ -1455,7 +1463,7 @@ void attack_phase(Field* fd) if(fd->end) { return; } // stille alive? attack the card in front - if(att_status->m_hp > 0 && !att_status->m_stunned && alive_assault(def_assaults, fd->current_ci)) + if(can_attack(fd, att_status) && alive_assault(def_assaults, fd->current_ci)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(pre_modifier_dmg); } @@ -1466,7 +1474,7 @@ void attack_phase(Field* fd) if(fd->end) { return; } // still alive? attack the card on the right - if(!fd->end && att_status->m_hp > 0 && !att_status->m_stunned && alive_assault(def_assaults, fd->current_ci + 1)) + if(!fd->end && can_attack(fd, att_status) && alive_assault(def_assaults, fd->current_ci + 1)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(pre_modifier_dmg); } @@ -1512,7 +1520,7 @@ inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { - if(!c->m_attacked && can_act(fd, c)) + if(can_act(fd, c) && !is_attacking_or_has_attacked(c)) { for(auto& s: c->m_card->m_skills) { @@ -1525,7 +1533,10 @@ inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) -{ return(!c->m_chaosed && can_act(fd, c)); } +{ + const auto& mod = std::get<4>(s); + return(!c->m_chaosed && can_act(fd, c) && !(mod == on_attacked && is_attacking_or_has_attacked(c)) && !(mod == on_death && has_attacked(c))); +} template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) @@ -1535,8 +1546,10 @@ inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& c->m_diseased || c->m_enfeebled > 0 || (c->m_frozen && c->m_delay == 0) || + c->m_immobilized || c->m_jammed || - c->m_poisoned + c->m_poisoned || + c->m_stunned )); } @@ -1558,7 +1571,10 @@ inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) -{ return(can_act(fd, c)); } +{ + const auto& mod = std::get<4>(s); + return(can_act(fd, c) && !(mod == on_attacked && is_attacking_or_has_attacked(c)) && !(mod == on_death && has_attacked(c))); +} template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) @@ -1570,7 +1586,7 @@ inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) -{ return(!c->m_immobilized && !c->m_stunned && !c->m_attacked && can_act(fd, c)); } +{ return(can_attack(fd, c) && !is_attacking_or_has_attacked(c)); } template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) @@ -1600,7 +1616,10 @@ inline bool skill_predicate(Field* fd, CardStatus* c, const Ski template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) -{ return(!c->m_immobilized && !c->m_stunned && !c->m_attacked && attack_power(c) > 0 && can_act(fd, c)); } +{ + const auto& mod = std::get<4>(s); + return(can_attack(fd, c) && attack_power(c) > 0 && !((mod == on_attacked || mod == on_death) && is_attacking_or_has_attacked(c))); +} template inline void perform_skill(Field* fd, CardStatus* c, unsigned v) diff --git a/sim.h b/sim.h index fc187e0c..ed8ea9a5 100644 --- a/sim.h +++ b/sim.h @@ -106,6 +106,13 @@ class Storage boost::pool<> m_pool; }; //------------------------------------------------------------------------------ +enum class CardStep +{ + none, + attacking, + attacked, +}; +//------------------------------------------------------------------------------ struct CardStatus { const Card* m_card; @@ -131,7 +138,7 @@ struct CardStatus unsigned m_weakened; bool m_temporary_split; bool m_summoned; // is this card summoned? - bool m_attacked; // has this card attacked in the turn? + CardStep m_step; CardStatus() {} CardStatus(const Card* card); diff --git a/tyrant.h b/tyrant.h index 9101f8fa..b22eebd1 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.1" +#define TYRANT_OPTIMIZER_VERSION "1.0.2" #include #include From 92f7cdb378bcf460df6e4ebfc67d9d934aff1868 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 18 Mar 2013 18:37:21 +0800 Subject: [PATCH 111/406] Support +N/-N in ownedcards.txt. --- read.cpp | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/read.cpp b/read.cpp index 3df43adc..1a6afb53 100644 --- a/read.cpp +++ b/read.cpp @@ -143,11 +143,13 @@ template Iterator read_toke return(token_end_after_spaces); } -void parse_card_spec(Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num) +// num_sign = 0 if card_num is "N"; = +1 if "+N"; = -1 if "-N" +void parse_card_spec(Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign) { auto card_spec_iter = card_spec.begin(); card_id = 0; card_num = 1; + num_sign = 0; std::string card_name; card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c=='#' || c=='(' || c=='\r');}, card_name); if(card_name.empty()) @@ -169,6 +171,19 @@ void parse_card_spec(Cards& cards, std::string& card_spec, unsigned& card_id, un if(card_spec_iter != card_spec.end() && (*card_spec_iter == '#' || *card_spec_iter == '(')) { ++card_spec_iter; + if(card_spec_iter != card_spec.end()) + { + if(*card_spec_iter == '+') + { + num_sign = +1; + ++card_spec_iter; + } + else if(*card_spec_iter == '-') + { + num_sign = -1; + ++card_spec_iter; + } + } card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c < '0' || c > '9');}, card_num); } if(card_id == 0) @@ -229,7 +244,9 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) { unsigned card_id{0}; unsigned card_num{1}; - parse_card_spec(cards, card_spec, card_id, card_num); + signed num_sign{0}; + parse_card_spec(cards, card_spec, card_id, card_num, num_sign); + assert(num_sign == 0); for(unsigned i(0); i < card_num; ++i) { card_ids.push_back(card_id); @@ -287,8 +304,20 @@ void read_owned_cards(Cards& cards, std::map& owned_cards, c } unsigned card_id{0}; unsigned card_num{1}; - parse_card_spec(cards, card_spec, card_id, card_num); - owned_cards[card_id] = card_num; + signed num_sign{0}; + parse_card_spec(cards, card_spec, card_id, card_num, num_sign); + if(num_sign == 0) + { + owned_cards[card_id] = card_num; + } + else if(num_sign > 0) + { + owned_cards[card_id] += card_num; + } + else if(num_sign < 0) + { + owned_cards[card_id] = owned_cards[card_id] > card_num ? owned_cards[card_id] - card_num : 0; + } } catch(std::exception& e) { From 4704d74afb0d7a021cccdc157d015f25bfedb4db Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 20 Mar 2013 12:07:50 +0800 Subject: [PATCH 112/406] Fix Stun skill. --- sim.cpp | 63 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/sim.cpp b/sim.cpp index fcf443e1..df8b566e 100644 --- a/sim.cpp +++ b/sim.cpp @@ -815,6 +815,10 @@ template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { return(can_be_healed(&fd->players[c->m_player]->commander)); } +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ return(ref->m_card->m_type == CardType::assault); } + template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { return(ref->m_card->m_type == CardType::assault && ref != c && ref->m_hp > 0 && fd->flip()); } @@ -1128,11 +1132,11 @@ struct PerformAttack fd(fd_), att_status(att_status_), def_status(def_status_), att_dmg(0), killed_by_attack(false) {} - template + template void op(unsigned pre_modifier_dmg) { count_achievement(fd, att_status); - modify_attack_damage(pre_modifier_dmg); + modify_attack_damage(pre_modifier_dmg); if(att_status->m_player == 0) { fd->update_max_counter(fd->achievement.misc_req, AchievementMiscReq::damage, att_dmg); @@ -1162,34 +1166,44 @@ struct PerformAttack } if(att_dmg > 0) { - immobilize(); - attack_damage(); + immobilize(); + attack_damage(); if(fd->end) { // Commander dies? return; } - siphon_poison_disease(); - on_kill(); - } - on_attacked(); - if(att_status->m_hp > 0 && def_status->m_card->m_counter > 0 && skill_check(fd, def_status, att_status)) - { - count_achievement(fd, def_status); - // perform_skill_counter - unsigned counter_dmg(counter_damage(att_status, def_status)); - _DEBUG_MSG("%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); - remove_hp(fd, *att_status, counter_dmg); + siphon_poison_disease(); + on_kill(); } + on_attacked(); if(att_dmg > 0) { - if(att_status->m_hp > 0 && att_status->m_card->m_berserk > 0 && skill_check(fd, att_status, nullptr)) + if(att_status->m_hp > 0) { - count_achievement(fd, att_status); - // perform_skill_berserk - att_status->m_berserk += att_status->m_card->m_berserk; + if(def_status->m_card->m_stun && skill_check(fd, def_status, att_status)) + { + count_achievement(fd, def_status); + // perform_skill_stun + _DEBUG_MSG("%s stuns %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); + att_status->m_stunned = 2; + } + if(def_status->m_card->m_counter > 0 && skill_check(fd, def_status, att_status)) + { + count_achievement(fd, def_status); + // perform_skill_counter + unsigned counter_dmg(counter_damage(att_status, def_status)); + _DEBUG_MSG("%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); + remove_hp(fd, *att_status, counter_dmg); + } + if(att_status->m_card->m_berserk > 0 && skill_check(fd, att_status, nullptr)) + { + count_achievement(fd, att_status); + // perform_skill_berserk + att_status->m_berserk += att_status->m_card->m_berserk; + } } - crush_leech(); + crush_leech(); } prepend_on_death(fd); @@ -1279,16 +1293,9 @@ struct PerformAttack template void on_kill() {} - template + template void on_attacked() { - if(att_status->m_card->m_type == CardType::assault && def_status->m_card->m_stun) - { - count_achievement(fd, def_status); - // perform_skill_stun - _DEBUG_MSG("%s stuns %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); - att_status->m_stunned = 2; - } if(def_status->m_card->m_poison_oa > att_status->m_poisoned && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); From e11e6ca5ebf2df8fa8897b01a92658550bf50308 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 20 Mar 2013 12:08:49 +0800 Subject: [PATCH 113/406] Blitz skill should be activated before on Play skills. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index df8b566e..e8e17056 100644 --- a/sim.cpp +++ b/sim.cpp @@ -396,8 +396,8 @@ struct PlayCard { setStorage(); placeCard(); - onPlaySkills(); blitz(); + onPlaySkills(); fieldEffects(); return(true); } From b49ec502f302b6f7570db9bbbe336924619015d8 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 20 Mar 2013 14:10:09 +0800 Subject: [PATCH 114/406] Support card list in command line. --- deck.cpp | 37 ------------------ deck.h | 3 -- read.cpp | 95 +++++++++++++++++++++++---------------------- read.h | 2 +- tyrant.h | 2 +- tyrant_optimize.cpp | 9 +++-- 6 files changed, 56 insertions(+), 92 deletions(-) diff --git a/deck.cpp b/deck.cpp index b8f2101e..b4fd6895 100644 --- a/deck.cpp +++ b/deck.cpp @@ -69,43 +69,6 @@ std::string deck_hash(const Card* commander, const std::vector& car } namespace range = boost::range; -void Deck::set(const Cards& all_cards, const std::vector& names) -{ - commander = nullptr; - strategy = DeckStrategy::random; - for(auto name: names) - { - auto card_it(all_cards.player_cards_by_name.find(name)); - if(card_it == all_cards.player_cards_by_name.end()) - { - throw std::runtime_error("While constructing a deck: the card " + name + " was not found."); - } - else - { - const Card* card{card_it->second}; - if(card->m_type == CardType::commander) - { - if(commander == nullptr) - { - commander = card; - } - else - { - throw std::runtime_error("While constructing a deck: two commanders detected (" + name + " and " + commander->m_name + ")"); - } - } - else - { - cards.emplace_back(card); - } - } - } - if(commander == nullptr) - { - throw std::runtime_error("While constructing a deck: no commander found"); - } -} - void Deck::set(const Cards& all_cards, const std::vector& ids) { commander = nullptr; diff --git a/deck.h b/deck.h index 3a114b34..816cc6ed 100644 --- a/deck.h +++ b/deck.h @@ -67,13 +67,10 @@ struct Deck std::vector > >()) { commander = commander_; -// cards = cards_; -// raid_cards = raid_cards_; cards = std::vector(std::begin(cards_), std::end(cards_)); raid_cards = std::vector > >(raid_cards_); } - void set(const Cards& all_cards, const std::vector& names); void set(const Cards& all_cards, const std::vector& ids); Deck* clone() const; diff --git a/read.cpp b/read.cpp index 1a6afb53..373805f6 100644 --- a/read.cpp +++ b/read.cpp @@ -21,7 +21,7 @@ const char* base64_chars = // Converts cards in `hash' to a deck. // Stores resulting card IDs in `ids'. -bool hash_to_ids(const char* hash, std::vector& ids) +void hash_to_ids(const char* hash, std::vector& ids) { unsigned int last_id = 0; const char* pc = hash; @@ -36,13 +36,13 @@ bool hash_to_ids(const char* hash, std::vector& ids) } if(!*pc || !*(pc + 1)) { - return(false); + 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) { - return(false); + throw std::runtime_error("Invalid hash character"); } pc += 2; size_t index0 = p0 - base64_chars; @@ -60,20 +60,42 @@ bool hash_to_ids(const char* hash, std::vector& ids) ids.push_back(last_id); } } - return(true); } +} // end of namespace + +void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign); +void namelist_to_ids(const Cards& all_cards, const std::string& deck_string, std::vector& ids) +{ + boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; + auto token_iter = deck_tokens.begin(); + for(; token_iter != deck_tokens.end(); ++token_iter) + { + std::string card_spec(*token_iter); + unsigned card_id{0}; + unsigned card_num{1}; + signed num_sign{0}; + parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign); + assert(num_sign == 0); + for(unsigned i(0); i < card_num; ++i) + { + ids.push_back(card_id); + } + } } -// Constructs and returns a deck from `hash'. -// The caller is responsible for freeing the deck. -Deck* hash_to_deck(const char* hash, const Cards& cards) +// Convert `str' (either hash or name list) to ids. +std::vector deck_string_to_ids(const Cards& cards, const std::string &str) { std::vector ids; - if(!hash_to_ids(hash, ids)) { return(nullptr); } - - Deck* deck = new Deck{}; - deck->set(cards, ids); - return deck; + if(str.find_first_of(":,") == std::string::npos) + { + hash_to_ids(str.c_str(), ids); + } + else + { + namelist_to_ids(cards, str, ids); + } + return(ids); } void load_decks(Decks& decks, Cards& cards) @@ -144,7 +166,7 @@ template Iterator read_toke } // num_sign = 0 if card_num is "N"; = +1 if "+N"; = -1 if "-N" -void parse_card_spec(Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign) +void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign) { auto card_spec_iter = card_spec.begin(); card_id = 0; @@ -188,7 +210,7 @@ void parse_card_spec(Cards& cards, std::string& card_spec, unsigned& card_id, un } if(card_id == 0) { - throw std::runtime_error("card not found"); + throw std::runtime_error("Card not found: " + card_name); } } @@ -209,7 +231,6 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) { while(decks_file && !decks_file.eof()) { - std::vector card_ids; std::string deck_string; getline(decks_file, deck_string); ++num_line; @@ -217,16 +238,9 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) { continue; } - boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; - auto token_iter = deck_tokens.begin(); std::string deck_name; - if(token_iter == deck_tokens.end()) - { - std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ", could not read the deck name.\n"; - continue; - } - read_token(token_iter->begin(), token_iter->end(), [](char c){return(false);}, deck_name); - if(deck_name.empty()) + auto deck_string_iter = read_token(deck_string.begin(), deck_string.end(), [](char c){return(strchr(":,", c));}, deck_name); + if(deck_string_iter == deck_string.end() || deck_name.empty()) { std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ", could not read the deck name.\n"; continue; @@ -236,31 +250,18 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) { std::cerr << "Warning in custom deck file " << filename << " at line " << num_line << ", name conflicts, overrides " << deck_iter->second->short_description() << std::endl; } - ++token_iter; - for(; token_iter != deck_tokens.end(); ++token_iter) + decks.decks.push_back(Deck{DeckType::custom_deck, num_line, deck_name}); + try { - std::string card_spec(*token_iter); - try - { - unsigned card_id{0}; - unsigned card_num{1}; - signed num_sign{0}; - parse_card_spec(cards, card_spec, card_id, card_num, num_sign); - assert(num_sign == 0); - for(unsigned i(0); i < card_num; ++i) - { - card_ids.push_back(card_id); - } - } - catch(std::exception& e) - { - std::cerr << "Error in custom deck file " << filename << " at line " << num_line << " while parsing card '" << card_spec << "' in deck " << deck_name << ": " << e.what() << "\n"; - } + Deck* deck = &decks.decks.back(); + deck->set(cards, deck_string_to_ids(cards, std::string{deck_string_iter, deck_string.end()})); + decks.by_name[deck_name] = deck; + } + catch(std::exception& e) + { + std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ": Deck " << deck_name << ": " << e.what() << std::endl; + decks.decks.pop_back(); } - decks.decks.push_back(Deck{DeckType::custom_deck, num_line, deck_name}); - Deck* deck = &decks.decks.back(); - deck->set(cards, card_ids); - decks.by_name[deck_name] = deck; } } catch (std::exception& e) diff --git a/read.h b/read.h index 2a71e470..036ee26f 100644 --- a/read.h +++ b/read.h @@ -9,7 +9,7 @@ class Cards; class Decks; class Deck; -Deck* hash_to_deck(const char* hash, const Cards& cards); +std::vector deck_string_to_ids(const Cards& cards, const std::string &str); void load_decks(Decks& decks, Cards& cards); std::vector > parse_deck_list(std::string list_string); unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename); diff --git a/tyrant.h b/tyrant.h index b22eebd1..38ee8f7b 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.2" +#define TYRANT_OPTIMIZER_VERSION "1.0.3" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index e719ff1a..56480b14 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -67,7 +67,10 @@ Deck* find_deck(const Decks& decks, const Cards& cards, std::string name) { return(it->second); } - return(hash_to_deck(name.c_str(), cards)); + std::vector ids{deck_string_to_ids(cards, name)}; + Deck* deck{new Deck()}; + deck->set(cards, ids); + return(deck); } //---------------------- $80 deck optimization --------------------------------- //------------------------------------------------------------------------------ @@ -1031,12 +1034,12 @@ void usage(int argc, char** argv) std::cout << "usage: " << argv[0] << " Attacker Defender [Flags] [Operations]\n" "\n" "Attacker:\n" - " the deck name/hash of a custom deck.\n" + " the name/hash/cards of a custom deck.\n" "\n" "Defender:\n" " semicolon separated list of defense decks, syntax:\n" " deck1[:factor1];deck2[:factor2];...\n" - " where deck is the name/hash of a mission, raid, or custom deck, and factor is optional. The default factor is 1.\n" + " where deck is the name/hash/cards of a mission, raid, quest or custom deck, and factor is optional. The default factor is 1.\n" " example: \'fear:0.2;slowroll:0.8\' means fear is the defense deck 20% of the time, while slowroll is the defense deck 80% of the time.\n" "\n" "Flags:\n" From 7747b41389ea1754226bc8b718cbb4eb49a69d0a Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 20 Mar 2013 18:50:37 +0800 Subject: [PATCH 115/406] Support card abbreviations. --- cards.cpp | 53 +++++++++++-- cards.h | 3 + deck.cpp | 142 +++++++++++++++++++++++++++++++++- deck.h | 5 ++ read.cpp | 181 ++++++++++++++++++-------------------------- read.h | 3 +- tyrant_optimize.cpp | 17 +++-- 7 files changed, 284 insertions(+), 120 deletions(-) diff --git a/cards.cpp b/cards.cpp index d272481c..0c787787 100644 --- a/cards.cpp +++ b/cards.cpp @@ -1,8 +1,11 @@ #include "cards.h" +#include #include #include #include +#include +#include #include "card.h" #include "tyrant.h" @@ -15,6 +18,35 @@ std::string to_string(T val) return s.str(); } +std::string simplify_name(const std::string& card_name) +{ + std::string simple_name; + for(auto c : card_name) + { + if(!strchr(";:, \"'-", c)) + { + simple_name += ::tolower(c); + } + } + return(simple_name); +} + +std::list get_abbreviations(const std::string& name) +{ + std::list abbr_list; + boost::tokenizer > word_token{name, boost::char_delimiters_separator{false, " ", ""}}; + std::string initial; + auto token_iter = word_token.begin(); + for(; token_iter != word_token.end(); ++token_iter) + { + abbr_list.push_back(simplify_name(std::string{token_iter->begin(), token_iter->end()})); + initial += *token_iter->begin(); + } + abbr_list.push_back(simplify_name(initial)); + return(abbr_list); +} + +//------------------------------------------------------------------------------ Cards::~Cards() { for(Card* c: cards) { delete(c); } @@ -44,8 +76,9 @@ void Cards::organize() player_actions.clear(); for(Card* card: cards) { - auto pos = card->m_name.find(','); - if(pos != std::string::npos) + // Remove delimiters from card names + size_t pos; + while((pos = card->m_name.find_first_of(";:,")) != std::string::npos) { card->m_name.erase(pos, 1); } @@ -81,15 +114,25 @@ void Cards::organize() break; } } - if(player_cards_by_name.find(card->m_name) == player_cards_by_name.end()) + std::string simple_name{simplify_name(card->m_name)}; + if(player_cards_by_name.find(simple_name) == player_cards_by_name.end()) { - player_cards_by_name[card->m_name] = card; + player_cards_by_name[simple_name] = card; } } } for(Card* card: cards) { - std::string base_name = card->m_name; + // generate abbreviations + for(auto&& abbr_name : get_abbreviations(card->m_name)) + { + if(abbr_name.length() > 1 && player_cards_by_name.find(abbr_name) == player_cards_by_name.end()) + { + player_cards_abbr[abbr_name] = card->m_name; + } + } + // update base_id + std::string base_name{simplify_name(card->m_name)}; if(card->m_set == 5002) { base_name.erase(base_name.size() - 1); diff --git a/cards.h b/cards.h index 60e7329b..3a02d766 100644 --- a/cards.h +++ b/cards.h @@ -19,8 +19,11 @@ struct Cards std::vector player_assaults; std::vector player_structures; std::vector player_actions; + std::map player_cards_abbr; const Card * by_id(unsigned id) const; void organize(); }; +std::string simplify_name(const std::string& card_name); + #endif diff --git a/deck.cpp b/deck.cpp index b4fd6895..ec978dc1 100644 --- a/deck.cpp +++ b/deck.cpp @@ -1,11 +1,13 @@ #include "deck.h" #include +#include #include #include #include "card.h" #include "cards.h" +#include "read.h" template void partial_shuffle(RandomAccessIterator first, RandomAccessIterator middle, @@ -67,6 +69,84 @@ std::string deck_hash(const Card* commander, const std::vector& car } return ios.str(); } +//------------------------------------------------------------------------------ +namespace { +const char* base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +// Converts cards in `hash' to a deck. +// Stores resulting card IDs in `ids'. +void hash_to_ids(const char* hash, std::vector& ids) +{ + unsigned int last_id = 0; + const char* pc = hash; + + while(*pc) + { + unsigned id_plus = 0; + if(*pc == '-') + { + ++ pc; + id_plus = 4000; + } + 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); + } + } +} +} // end of namespace + +void namelist_to_ids(const Cards& all_cards, const std::string& deck_string, std::vector& ids) +{ + boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; + auto token_iter = deck_tokens.begin(); + for(; token_iter != deck_tokens.end(); ++token_iter) + { + std::string card_spec(*token_iter); + unsigned card_id{0}; + unsigned card_num{1}; + signed num_sign{0}; + try + { + parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign); + assert(num_sign == 0); + for(unsigned i(0); i < card_num; ++i) + { + ids.push_back(card_id); + } + } + catch(std::exception& e) + { + std::cerr << "Ignore card: " << e.what() << std::endl; + continue; + } + } +} + namespace range = boost::range; void Deck::set(const Cards& all_cards, const std::vector& ids) @@ -98,13 +178,73 @@ void Deck::set(const Cards& all_cards, const std::vector& ids) } } +void Deck::set(const Cards& all_cards, const std::string& deck_string_) +{ + deck_string = deck_string_; +} + +void Deck::resolve(const Cards& all_cards) +{ + if(commander == nullptr) + { + std::vector ids; + if(deck_string.find_first_of(":,") == std::string::npos) + { + try + { + hash_to_ids(deck_string.c_str(), ids); + } + catch(std::exception& e) + { + std::cerr << "Error while resolving " << short_description() << ": " << e.what() << std::endl; + throw; + } + } + else + { + boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; + auto token_iter = deck_tokens.begin(); + for(; token_iter != deck_tokens.end(); ++token_iter) + { + std::string card_spec(*token_iter); + unsigned card_id{0}; + unsigned card_num{1}; + signed num_sign{0}; + try + { + parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign); + assert(num_sign == 0); + for(unsigned i(0); i < card_num; ++i) + { + ids.push_back(card_id); + } + } + catch(std::exception& e) + { + std::cerr << "Warning while resolving " << short_description() << ": " << e.what() << std::endl; + continue; + } + } + } + set(all_cards, ids); + deck_string.clear(); + } +} + std::string Deck::short_description() const { std::stringstream ios; ios << decktype_names[decktype]; if(id > 0) { ios << " #" << id; } if(!name.empty()) { ios << " \"" << name << "\""; } - if(raid_cards.empty()) { ios << ": " << deck_hash(commander, cards); } + if(deck_string.empty()) + { + if(raid_cards.empty()) { ios << ": " << deck_hash(commander, cards); } + } + else + { + ios << ": " << deck_string; + } return ios.str(); } diff --git a/deck.h b/deck.h index 816cc6ed..a7ee9d60 100644 --- a/deck.h +++ b/deck.h @@ -44,6 +44,8 @@ struct Deck std::map > order; std::vector > > raid_cards; + std::string deck_string; + Deck( DeckType::DeckType decktype_ = DeckType::deck, unsigned id_ = 0, @@ -72,6 +74,8 @@ struct Deck } void set(const Cards& all_cards, const std::vector& ids); + void set(const Cards& all_cards, const std::string& deck_string_); + void resolve(const Cards& all_cards); Deck* clone() const; std::string short_description() const; @@ -88,6 +92,7 @@ struct Decks std::list decks; std::map by_name; std::map mission_names_by_id; + std::map custom_decks; }; #endif diff --git a/read.cpp b/read.cpp index 373805f6..ec5a53e5 100644 --- a/read.cpp +++ b/read.cpp @@ -13,91 +13,6 @@ #include "cards.h" #include "deck.h" -namespace { -const char* base64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - -// Converts cards in `hash' to a deck. -// Stores resulting card IDs in `ids'. -void hash_to_ids(const char* hash, std::vector& ids) -{ - unsigned int last_id = 0; - const char* pc = hash; - - while(*pc) - { - unsigned id_plus = 0; - if(*pc == '-') - { - ++ pc; - id_plus = 4000; - } - 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); - } - } -} -} // end of namespace - -void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign); -void namelist_to_ids(const Cards& all_cards, const std::string& deck_string, std::vector& ids) -{ - boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; - auto token_iter = deck_tokens.begin(); - for(; token_iter != deck_tokens.end(); ++token_iter) - { - std::string card_spec(*token_iter); - unsigned card_id{0}; - unsigned card_num{1}; - signed num_sign{0}; - parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign); - assert(num_sign == 0); - for(unsigned i(0); i < card_num; ++i) - { - ids.push_back(card_id); - } - } -} - -// Convert `str' (either hash or name list) to ids. -std::vector deck_string_to_ids(const Cards& cards, const std::string &str) -{ - std::vector ids; - if(str.find_first_of(":,") == std::string::npos) - { - hash_to_ids(str.c_str(), ids); - } - else - { - namelist_to_ids(cards, str, ids); - } - return(ids); -} - void load_decks(Decks& decks, Cards& cards) { if(boost::filesystem::exists("Custom.txt")) @@ -179,8 +94,15 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ throw std::runtime_error("no card name"); } // If card name is not found, try find card id quoted in '[]' in name, ignoring other characters. - auto card_it = cards.player_cards_by_name.find(card_name); - auto card_id_iter = advance_until(card_name.begin(), card_name.end(), [](char c){return(c=='[');}); + std::string simple_name{simplify_name(card_name)}; + auto abbr_it = cards.player_cards_abbr.find(simple_name); + if(abbr_it != cards.player_cards_abbr.end()) + { + std::cout << "Recognize abbreviation " << card_name << ": " << abbr_it->second << std::endl; + simple_name = simplify_name(abbr_it->second); + } + auto card_it = cards.player_cards_by_name.find(simple_name); + auto card_id_iter = advance_until(simple_name.begin(), simple_name.end(), [](char c){return(c=='[');}); if(card_it != cards.player_cards_by_name.end()) { card_id = card_it->second->m_id; @@ -188,7 +110,7 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ else if(card_id_iter != card_name.end()) { ++ card_id_iter; - card_id_iter = read_token(card_id_iter, card_name.end(), [](char c){return(c==']');}, card_id); + card_id_iter = read_token(card_id_iter, simple_name.end(), [](char c){return(c==']');}, card_id); } if(card_spec_iter != card_spec.end() && (*card_spec_iter == '#' || *card_spec_iter == '(')) { @@ -210,10 +132,67 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ } if(card_id == 0) { - throw std::runtime_error("Card not found: " + card_name); + throw std::runtime_error("Unknown card: " + card_name); } } +unsigned read_card_abbrs(Cards& cards, const std::string& filename) +{ + if(!boost::filesystem::exists(filename)) + { + return(0); + } + std::ifstream abbr_file(filename); + if(!abbr_file.is_open()) + { + std::cerr << "Error: Card abbreviation file " << filename << " could not be opened\n"; + return(2); + } + unsigned num_line(0); + abbr_file.exceptions(std::ifstream::badbit); + try + { + while(abbr_file && !abbr_file.eof()) + { + std::string abbr_string; + getline(abbr_file, abbr_string); + ++num_line; + if(abbr_string.size() == 0 || strncmp(abbr_string.c_str(), "//", 2) == 0) + { + continue; + } + std::string abbr_name; + auto abbr_string_iter = read_token(abbr_string.begin(), abbr_string.end(), [](char c){return(strchr(":", c));}, abbr_name); + if(abbr_string_iter == abbr_string.end() || abbr_name.empty()) + { + std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ", could not read the deck name.\n"; + continue; + } + abbr_string_iter = advance_until(abbr_string_iter + 1, abbr_string.end(), [](const char& c){return(c != ' ');}); + if(cards.player_cards_by_name.find(abbr_name) != cards.player_cards_by_name.end()) + { + std::cerr << "Warning in card abbreviation file " << filename << " at line " << num_line << ": ignored because the name has been used by an existing card." << std::endl; + } + else + { + cards.player_cards_abbr[abbr_name] = std::string{abbr_string_iter, abbr_string.end()}; + } + } + } + catch (std::exception& e) + { + std::cerr << "Exception while parsing the card abbreviation file " << filename; + if(num_line > 0) + { + std::cerr << " at line " << num_line; + } + std::cerr << ": " << e.what() << ".\n"; + return(3); + } + return(0); +} + + // Error codes: // 2 -> file not readable // 3 -> error while parsing file @@ -245,23 +224,19 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ", could not read the deck name.\n"; continue; } + deck_string_iter = advance_until(deck_string_iter + 1, deck_string.end(), [](const char& c){return(c != ' ');}); auto deck_iter = decks.by_name.find(deck_name); if(deck_iter != decks.by_name.end()) { std::cerr << "Warning in custom deck file " << filename << " at line " << num_line << ", name conflicts, overrides " << deck_iter->second->short_description() << std::endl; } decks.decks.push_back(Deck{DeckType::custom_deck, num_line, deck_name}); - try - { - Deck* deck = &decks.decks.back(); - deck->set(cards, deck_string_to_ids(cards, std::string{deck_string_iter, deck_string.end()})); - decks.by_name[deck_name] = deck; - } - catch(std::exception& e) - { - std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ": Deck " << deck_name << ": " << e.what() << std::endl; - decks.decks.pop_back(); - } + Deck* deck = &decks.decks.back(); + deck->set(cards, std::string{deck_string_iter, deck_string.end()}); + decks.by_name[deck_name] = deck; + std::stringstream alt_name; + alt_name << decktype_names[deck->decktype] << " #" << deck->id; + decks.by_name[alt_name.str()] = deck; } } catch (std::exception& e) @@ -297,12 +272,6 @@ void read_owned_cards(Cards& cards, std::map& owned_cards, c } try { - // Remove ',' from card names - auto pos = card_spec.find(','); - if(pos != std::string::npos) - { - card_spec.erase(pos, 1); - } unsigned card_id{0}; unsigned card_num{1}; signed num_sign{0}; diff --git a/read.h b/read.h index 036ee26f..6e6cdaef 100644 --- a/read.h +++ b/read.h @@ -9,10 +9,11 @@ class Cards; class Decks; class Deck; -std::vector deck_string_to_ids(const Cards& cards, const std::string &str); +void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign); void load_decks(Decks& decks, Cards& cards); std::vector > parse_deck_list(std::string list_string); unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename); void read_owned_cards(Cards& cards, std::map& owned_cards, const char *filename); +unsigned read_card_abbrs(Cards& cards, const std::string& filename); #endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 56480b14..4af62258 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -60,16 +60,18 @@ std::string card_id_name(const Card* card) return ios.str(); } //------------------------------------------------------------------------------ -Deck* find_deck(const Decks& decks, const Cards& cards, std::string name) +Deck* find_deck(Decks& decks, const Cards& cards, std::string deck_name) { - auto it = decks.by_name.find(name); + auto it = decks.by_name.find(deck_name); if(it != decks.by_name.end()) { + it->second->resolve(cards); return(it->second); } - std::vector ids{deck_string_to_ids(cards, name)}; - Deck* deck{new Deck()}; - deck->set(cards, ids); + decks.decks.push_back(Deck{}); + Deck* deck = &decks.decks.back(); + deck->set(cards, deck_name); + deck->resolve(cards); return(deck); } //---------------------- $80 deck optimization --------------------------------- @@ -1080,6 +1082,7 @@ int main(int argc, char** argv) bool ordered = false; Cards cards; read_cards(cards); + read_card_abbrs(cards, "cardabbrs.txt"); Decks decks; Achievement achievement; load_decks_xml(decks, cards); @@ -1221,7 +1224,7 @@ int main(int argc, char** argv) } catch(const std::runtime_error& e) { - std::cerr << "Error: Deck hash " << att_deck_name << ": " << e.what() << std::endl; + std::cerr << "Error: Deck " << att_deck_name << ": " << e.what() << std::endl; return(5); } if(att_deck == nullptr) @@ -1254,7 +1257,7 @@ int main(int argc, char** argv) } catch(const std::runtime_error& e) { - std::cerr << "Error: Deck hash " << deck_parsed.first << ": " << e.what() << std::endl; + std::cerr << "Error: Deck " << deck_parsed.first << ": " << e.what() << std::endl; return(5); } if(def_deck == nullptr) From 0c0ce3e21139edac98c79be37604459b81f2aeca Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 20 Mar 2013 21:30:58 +0800 Subject: [PATCH 116/406] Reduce duplicate "Recognize abbreviation" info. Fix error info for unknown card. --- read.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/read.cpp b/read.cpp index ec5a53e5..92490efc 100644 --- a/read.cpp +++ b/read.cpp @@ -83,6 +83,7 @@ template Iterator read_toke // num_sign = 0 if card_num is "N"; = +1 if "+N"; = -1 if "-N" void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign) { + static std::set recognized_abbr; auto card_spec_iter = card_spec.begin(); card_id = 0; card_num = 1; @@ -98,7 +99,11 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ auto abbr_it = cards.player_cards_abbr.find(simple_name); if(abbr_it != cards.player_cards_abbr.end()) { - std::cout << "Recognize abbreviation " << card_name << ": " << abbr_it->second << std::endl; + if(recognized_abbr.count(card_name) == 0) + { + std::cout << "Recognize abbreviation " << card_name << ": " << abbr_it->second << std::endl; + recognized_abbr.insert(card_name); + } simple_name = simplify_name(abbr_it->second); } auto card_it = cards.player_cards_by_name.find(simple_name); @@ -107,7 +112,7 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ { card_id = card_it->second->m_id; } - else if(card_id_iter != card_name.end()) + else if(card_id_iter != simple_name.end()) { ++ card_id_iter; card_id_iter = read_token(card_id_iter, simple_name.end(), [](char c){return(c==']');}, card_id); From c206e0f616acd44a468fce83aae71e4924e51a63 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 27 Mar 2013 17:19:46 +0800 Subject: [PATCH 117/406] Kill regenerated units should not count in achievements. --- sim.cpp | 15 +++++++++------ sim.h | 3 ++- tyrant.h | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/sim.cpp b/sim.cpp index e8e17056..e5ee6524 100644 --- a/sim.cpp +++ b/sim.cpp @@ -60,7 +60,8 @@ CardStatus::CardStatus(const Card* card) : m_stunned(0), m_weakened(0), m_temporary_split(false), - m_summoned(false), + m_is_summoned(false), + m_has_regenerated(false), m_step(CardStep::none) { } @@ -95,7 +96,8 @@ inline void CardStatus::set(const Card& card) m_weakened = 0; m_stunned = 0; m_temporary_split = false; - m_summoned = false; + m_is_summoned = false; + m_has_regenerated = false; m_step = CardStep::none; } //------------------------------------------------------------------------------ @@ -641,7 +643,7 @@ Results play(Field* fd) status_split.set(current_status.m_card); status_split.m_index = fd->tap->assaults.size() - 1; status_split.m_player = fd->tapi; - status_split.m_summoned = true; + status_split.m_is_summoned = true; _DEBUG_MSG("%s splits %s\n", status_description(¤t_status).c_str(), status_description(&status_split).c_str()); for(auto& skill: status_split.m_card->m_skills_on_play) { @@ -864,9 +866,9 @@ void remove_hp(Field* fd, CardStatus& status, unsigned dmg) if(status.m_hp == 0) { _DEBUG_MSG("%s dies\n", status_description(&status).c_str()); - if(status.m_player == 1) + if(status.m_player == 1 && !status.m_has_regenerated) { - if(!status.m_summoned) + if(!status.m_is_summoned) { fd->inc_counter(fd->achievement.unit_type_killed, status.m_card->m_type); } @@ -921,6 +923,7 @@ void check_regeneration(Field* fd) count_achievement(fd, status); _DEBUG_MSG("%s regenerates with %u health\n", status_description(status).c_str(), status->m_card->m_health); add_hp(fd, status, status->m_card->m_regenerate); + status->m_has_regenerated = true; } } fd->killed_with_regen.clear(); @@ -2162,7 +2165,7 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) card_status.set(summoned); card_status.m_index = storage->size() - 1; card_status.m_player = player; - card_status.m_summoned = true; + card_status.m_is_summoned = true; _DEBUG_MSG("%s Summon %s %u [%s]\n", status_description(src_status).c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd, summoned).c_str()); prepend_skills(fd, &card_status); if(card_status.m_card->m_blitz) diff --git a/sim.h b/sim.h index ed8ea9a5..3f588a07 100644 --- a/sim.h +++ b/sim.h @@ -137,7 +137,8 @@ struct CardStatus unsigned m_stunned; unsigned m_weakened; bool m_temporary_split; - bool m_summoned; // is this card summoned? + bool m_is_summoned; // is this card summoned (or split)? + bool m_has_regenerated; // has this card regenerated? CardStep m_step; CardStatus() {} diff --git a/tyrant.h b/tyrant.h index 38ee8f7b..62027efe 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.3" +#define TYRANT_OPTIMIZER_VERSION "1.0.4" #include #include From f881e9db88907de9f5e9fd0b838445b6170a8c9a Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 27 Mar 2013 17:21:19 +0800 Subject: [PATCH 118/406] Paybacked Jam should have 50% chance to proc. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index e5ee6524..d1d4d15d 100644 --- a/sim.cpp +++ b/sim.cpp @@ -2046,7 +2046,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski // Count at most once even targeting "All" is_count_achievement = false; // Payback - if(c->m_card->m_payback && skill_predicate(fd, src_status, s) && skill_check(fd, c, src_status)) + if(c->m_card->m_payback && skill_predicate(fd, src_status, s) && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) { count_achievement(fd, c); _DEBUG_MSG("%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); From f7054ba063db08196e3d1d06b73ee599d2ba5ddb Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 28 Mar 2013 10:57:35 +0800 Subject: [PATCH 119/406] A unique card and its upgraded version should not appear in one deck. --- tyrant_optimize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 4af62258..b80180ba 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -188,7 +188,7 @@ bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) { for(unsigned i(0); i < deck.cards.size(); ++i) { - if(i != slot && deck.cards[i]->m_name == card->m_name) + if(i != slot && deck.cards[i]->m_base_id == card->m_base_id) { return(false); } From be7ce68c15ee587e92a685ef86ebaec14a79f22e Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 28 Mar 2013 16:49:37 +0800 Subject: [PATCH 120/406] Support more deck strategies. --- deck.cpp | 9 +++++---- deck.h | 1 + tyrant_optimize.cpp | 29 ++++++++++++++++++++--------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/deck.cpp b/deck.cpp index ec978dc1..fcf815fc 100644 --- a/deck.cpp +++ b/deck.cpp @@ -300,7 +300,7 @@ const Card* Deck::next() { return(nullptr); } - else if(strategy == DeckStrategy::random) + else if(strategy == DeckStrategy::random || strategy == DeckStrategy::exact_ordered) { const Card* card = shuffled_cards.front(); shuffled_cards.pop_front(); @@ -366,10 +366,11 @@ void Deck::shuffle(std::mt19937& re) order[card->m_id].push_back(i); ++i; } - shuffled_cards.clear(); - range::insert(shuffled_cards, shuffled_cards.end(), cards); } - std::shuffle(shuffled_cards.begin(), shuffled_cards.end(), re); + if(strategy != DeckStrategy::exact_ordered) + { + std::shuffle(shuffled_cards.begin(), shuffled_cards.end(), re); + } } void Deck::place_at_bottom(const Card* card) diff --git a/deck.h b/deck.h index a7ee9d60..0b69c1c2 100644 --- a/deck.h +++ b/deck.h @@ -23,6 +23,7 @@ enum DeckStrategy { random, ordered, + exact_ordered, num_deckstrategies }; } diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index b80180ba..ca32b5f0 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1079,7 +1079,8 @@ int main(int argc, char** argv) } debug_print = getenv("DEBUG_PRINT"); unsigned num_threads = (debug_print || getenv("DEBUG")) ? 1 : 4; - bool ordered = false; + DeckStrategy::DeckStrategy att_strategy(DeckStrategy::random); + DeckStrategy::DeckStrategy def_strategy(DeckStrategy::random); Cards cards; read_cards(cards); read_card_abbrs(cards, "cardabbrs.txt"); @@ -1158,11 +1159,23 @@ int main(int argc, char** argv) read_owned_cards(cards, owned_cards, argv[argIndex] + 3); use_owned_cards = true; } - else if(strcmp(argv[argIndex], "-r") == 0) + else if(strcmp(argv[argIndex], "-r") == 0 || strcmp(argv[argIndex], "ordered") == 0) { - ordered = true; + att_strategy = DeckStrategy::ordered; } - else if(strcmp(argv[argIndex], "-s") == 0) + else if(strcmp(argv[argIndex], "exact-ordered") == 0) + { + att_strategy = DeckStrategy::exact_ordered; + } + else if(strcmp(argv[argIndex], "defender:ordered") == 0) + { + def_strategy = DeckStrategy::ordered; + } + else if(strcmp(argv[argIndex], "defender:exact-ordered") == 0) + { + def_strategy = DeckStrategy::exact_ordered; + } + else if(strcmp(argv[argIndex], "-s") == 0 || strcmp(argv[argIndex], "surge") == 0) { gamemode = surge; } @@ -1241,10 +1254,7 @@ int main(int argc, char** argv) print_available_decks(decks, false); return(5); } - if(ordered) - { - att_deck->strategy = DeckStrategy::ordered; - } + att_deck->strategy = att_strategy; std::vector def_decks; std::vector def_decks_factors; @@ -1290,6 +1300,7 @@ int main(int argc, char** argv) } effect = this_effect; } + def_deck->strategy = def_strategy; def_decks.push_back(def_deck); def_decks_factors.push_back(deck_parsed.second); } @@ -1317,7 +1328,7 @@ int main(int argc, char** argv) break; } case climb: { - if(!ordered) + if(att_strategy == DeckStrategy::random) { hill_climbing(std::get<0>(op), att_deck, p); } From 7adaaa18470a1214f3081da2af1d089b9e23cc06 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 29 Mar 2013 09:44:56 +0800 Subject: [PATCH 121/406] Fix bug: speedy bonus in net points. --- sim.cpp | 2 +- tyrant.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index d1d4d15d..d0763331 100644 --- a/sim.cpp +++ b/sim.cpp @@ -540,7 +540,7 @@ Results play(Field* fd) // ANP: Last decision point is second-to-last card played. fd->points_since_last_decision = 0; unsigned p0_size = fd->players[0]->deck->cards.size(); - fd->last_decision_turn = p0_size * 2 - (surge ? 2 : 3); + fd->last_decision_turn = p0_size * 2 - (fd->gamemode == surge ? 2 : 3); // Count commander as played for achievements (not count in type / faction / rarity requirements) fd->inc_counter(fd->achievement.unit_played, fd->players[0]->commander.m_card->m_id); diff --git a/tyrant.h b/tyrant.h index 62027efe..ab3f207e 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.4" +#define TYRANT_OPTIMIZER_VERSION "1.0.5" #include #include From 96e2a52be7c631b28d295f22bb4cff23e8280789 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 29 Mar 2013 10:15:54 +0800 Subject: [PATCH 122/406] Friendly unit should not Intercept Mimic during Copycat. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index d0763331..6f3a485e 100644 --- a/sim.cpp +++ b/sim.cpp @@ -2212,7 +2212,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) { return; } - CardStatus* c(fd->selection_array[get_target_hostile_index(fd, src_status, selection_array_size)]); + CardStatus* c(fd->selection_array[fd->effect == Effect::copycat ? fd->rand(0, selection_array_size - 1) : get_target_hostile_index(fd, src_status, selection_array_size)]); // evade check for mimic // individual skills are subject to evade checks too, // but resolve_skill will handle those. From fef7ca72ebb7fd46a29c63af74b3e6becbe5830e Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 29 Mar 2013 16:21:26 +0800 Subject: [PATCH 123/406] Fix bug: Attack raised by Berserk should apply to Flurried attacks. --- sim.cpp | 25 +++++++++++++------------ tyrant.h | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/sim.cpp b/sim.cpp index 6f3a485e..89e8d06b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1136,8 +1136,10 @@ struct PerformAttack {} template - void op(unsigned pre_modifier_dmg) + void op() { + unsigned pre_modifier_dmg = attack_power(att_status); + if(pre_modifier_dmg == 0) { return; } count_achievement(fd, att_status); modify_attack_damage(pre_modifier_dmg); if(att_status->m_player == 0) @@ -1419,24 +1421,23 @@ void PerformAttack::crush_leech() } // General attack phase by the currently evaluated assault, taking into accounts exotic stuff such as flurry,swipe,etc. -void attack_commander(Field* fd, CardStatus* att_status, unsigned pre_modifier_dmg) +void attack_commander(Field* fd, CardStatus* att_status) { CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall if(def_status != nullptr) { - PerformAttack{fd, att_status, def_status}.op(pre_modifier_dmg); + PerformAttack{fd, att_status, def_status}.op(); } else { - PerformAttack{fd, att_status, &fd->tip->commander}.op(pre_modifier_dmg); + PerformAttack{fd, att_status, &fd->tip->commander}.op(); } } void attack_phase(Field* fd) { CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); - unsigned pre_modifier_dmg = attack_power(att_status); - if(pre_modifier_dmg == 0) { return; } + if(attack_power(att_status) == 0) { return; } unsigned num_attacks(1); if(att_status->m_card->m_flurry > 0 && skill_check(fd, att_status, nullptr)) { @@ -1458,7 +1459,7 @@ void attack_phase(Field* fd) // attack mode 1. if(!(att_status->m_card->m_swipe && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(pre_modifier_dmg); + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); } // attack mode 2. else @@ -1468,32 +1469,32 @@ void attack_phase(Field* fd) // attack the card on the left if(fd->current_ci > 0 && alive_assault(def_assaults, fd->current_ci - 1)) { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(pre_modifier_dmg); + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(); } if(fd->end) { return; } // stille alive? attack the card in front if(can_attack(fd, att_status) && alive_assault(def_assaults, fd->current_ci)) { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(pre_modifier_dmg); + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); } else { - attack_commander(fd, att_status, pre_modifier_dmg); + attack_commander(fd, att_status); } if(fd->end) { return; } // still alive? attack the card on the right if(!fd->end && can_attack(fd, att_status) && alive_assault(def_assaults, fd->current_ci + 1)) { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(pre_modifier_dmg); + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(); } } } // attack mode 3. else { - attack_commander(fd, att_status, pre_modifier_dmg); + attack_commander(fd, att_status); } } } diff --git a/tyrant.h b/tyrant.h index ab3f207e..1d20ac4b 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.5" +#define TYRANT_OPTIMIZER_VERSION "1.0.6" #include #include From 3a72966f46a9ea623186ddbdac49cda7eda74df2 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 29 Mar 2013 17:16:22 +0800 Subject: [PATCH 124/406] Enfeeble should be wiped out from all Assaults at the end of the turn. --- sim.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sim.cpp b/sim.cpp index 89e8d06b..75159f22 100644 --- a/sim.cpp +++ b/sim.cpp @@ -368,7 +368,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector assert(fd->skill_queue.size() == 0); for(auto& skill: skills) { - _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); bool fusion_active = status->m_card->m_fusion && status->m_player == fd->tapi && fd->fusion_count >= 3; auto& augmented_s = status->m_augmented > 0 ? augmented_skill(status, skill) : skill; auto& fusioned_s = fusion_active ? fusioned_skill(augmented_s) : augmented_s; @@ -449,7 +449,7 @@ struct PlayCard { for(auto& skill: card->m_skills_on_play) { - _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(status, skill); resolve_skill(fd); if(fd->end) { break; } @@ -503,7 +503,7 @@ void PlayCard::onPlaySkills() { for(auto& skill: card->m_skills) { - _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(status, skill); resolve_skill(fd); if(fd->end) { break; } @@ -647,7 +647,7 @@ Results play(Field* fd) _DEBUG_MSG("%s splits %s\n", status_description(¤t_status).c_str(), status_description(&status_split).c_str()); for(auto& skill: status_split.m_card->m_skills_on_play) { - _DEBUG_MSG("Evaluating %s skill %s\n", status_description(¤t_status).c_str(), skill_description(fd, skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(¤t_status).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(&status_split, skill); resolve_skill(fd); if(fd->end) { break; } @@ -995,6 +995,7 @@ void turn_start_phase(Field* fd) status.m_augmented = 0; status.m_blitzing = false; status.m_chaosed = false; + status.m_enfeebled = 0; status.m_frozen = false; status.m_immobilized = false; status.m_jammed = false; @@ -1322,7 +1323,7 @@ struct PerformAttack } for(auto& oa_skill: def_status->m_card->m_skills_on_attacked) { - _DEBUG_MSG("Evaluating %s skill %s\n", status_description(def_status).c_str(), skill_description(fd, oa_skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(def_status).c_str(), skill_description(fd, oa_skill).c_str()); fd->skill_queue.emplace_back(def_status, def_status->m_augmented > 0 ? augmented_skill(def_status, oa_skill) : oa_skill); resolve_skill(fd); if(fd->end) { break; } @@ -1385,7 +1386,7 @@ void PerformAttack::on_kill() { for(auto& on_kill_skill: att_status->m_card->m_skills_on_kill) { - _DEBUG_MSG("Evaluating %s skill %s\n", status_description(att_status).c_str(), skill_description(fd, on_kill_skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(att_status).c_str(), skill_description(fd, on_kill_skill).c_str()); fd->skill_queue.emplace_back(att_status, on_kill_skill); resolve_skill(fd); if(fd->end) { break; } @@ -2233,7 +2234,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) (std::get<0>(skill) == supply && src_status->m_card->m_type != CardType::assault)) { continue; } SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill), on_act); - _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd, skill).c_str()); +// _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(src_status, src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); resolve_skill(fd); if(fd->end) { break; } From fb8b15805c2e7ea237f35db4a7144c6c588fa332 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 31 Mar 2013 11:07:52 +0800 Subject: [PATCH 125/406] Print out unordered decks in sorted and consolidated way. --- deck.cpp | 8 ++++++-- deck.h | 2 +- tyrant_optimize.cpp | 34 +++++++++++++++++++--------------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/deck.cpp b/deck.cpp index fcf815fc..4ecb0737 100644 --- a/deck.cpp +++ b/deck.cpp @@ -29,12 +29,16 @@ void partial_shuffle(RandomAccessIterator first, RandomAccessIterator middle, } //------------------------------------------------------------------------------ -std::string deck_hash(const Card* commander, const std::vector& cards) +std::string deck_hash(const Card* commander, std::vector cards, bool is_ordered) { std::string base64= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; std::stringstream ios; ios << base64[commander->m_id / 64]; ios << base64[commander->m_id % 64]; + if(!is_ordered) + { + std::sort(cards.begin(), cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); + } unsigned last_id = 0; unsigned num_repeat = 0; for(const Card* card: cards) @@ -239,7 +243,7 @@ std::string Deck::short_description() const if(!name.empty()) { ios << " \"" << name << "\""; } if(deck_string.empty()) { - if(raid_cards.empty()) { ios << ": " << deck_hash(commander, cards); } + if(raid_cards.empty()) { ios << ": " << deck_hash(commander, cards, strategy == DeckStrategy::ordered || strategy == DeckStrategy::exact_ordered); } } else { diff --git a/deck.h b/deck.h index 0b69c1c2..ca46f1c7 100644 --- a/deck.h +++ b/deck.h @@ -11,7 +11,7 @@ class Card; class Cards; -std::string deck_hash(const Card* commander, const std::vector& cards); +std::string deck_hash(const Card* commander, std::vector cards, bool is_ordered); //---------------------- $30 Deck: a commander + a sequence of cards ----------- // Can be shuffled. diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index ca32b5f0..12443283 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -558,7 +558,7 @@ void print_results(const std::pair> , unsigned>& r std::cout << std::endl; } //------------------------------------------------------------------------------ -void print_deck_inline(const double score, const Card *commander, std::vector cards) +void print_deck_inline(const double score, const Card *commander, std::vector cards, bool is_ordered) { if(use_anp) { @@ -569,6 +569,10 @@ void print_deck_inline(const double score, const Card *commander, std::vectorm_name; + if(!is_ordered) + { + std::sort(cards.begin(), cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); + } std::string last_name; unsigned num_repeat(0); for(const Card* card: cards) @@ -608,7 +612,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) if(!fixed_len) { non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); } const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; - print_deck_inline(best_score, best_commander, best_cards); + print_deck_inline(best_score, best_commander, best_cards, false); std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; bool eval_commander = true; @@ -640,9 +644,9 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) best_score = current_score; best_commander = commander_candidate; deck_has_been_improved = true; - std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards) << " commander -> " << card_id_name(commander_candidate) << ": "; + std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards, false) << " commander -> " << card_id_name(commander_candidate) << ": "; print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards); + print_deck_inline(best_score, best_commander, best_cards, false); } } // Now that all commanders are evaluated, take the best one @@ -680,7 +684,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) // Is it better ? if(current_score > best_score) { - std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards) << " " << slot_i << " " << card_id_name(slot_i < best_cards.size() ? best_cards[slot_i] : NULL) << + std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards, false) << " " << card_id_name(slot_i < best_cards.size() ? best_cards[slot_i] : NULL) << " -> " << card_id_name(card_candidate) << ": "; // Then update best score/slot, print stuff best_score = current_score; @@ -688,7 +692,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) eval_commander = true; deck_has_been_improved = true; print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards); + print_deck_inline(best_score, best_commander, best_cards, false); } d1->cards = best_cards; if(best_score == best_possible) { break; } @@ -697,7 +701,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) d1->cards = best_cards; } std::cout << "Optimized Deck: "; - print_deck_inline(best_score, best_commander, best_cards); + print_deck_inline(best_score, best_commander, best_cards, false); } //------------------------------------------------------------------------------ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) @@ -713,7 +717,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) if(!fixed_len) { non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); } const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; - print_deck_inline(best_score, best_commander, best_cards); + print_deck_inline(best_score, best_commander, best_cards, true); std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; bool eval_commander = true; @@ -746,9 +750,9 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) best_score = current_score; best_commander = commander_candidate; deck_has_been_improved = true; - std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards) << " commander -> " << card_id_name(commander_candidate) << ": "; + std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards, true) << " commander -> " << card_id_name(commander_candidate) << ": "; print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards); + print_deck_inline(best_score, best_commander, best_cards, true); } } // Now that all commanders are evaluated, take the best one @@ -787,14 +791,14 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) if(current_score > best_score) { // Then update best score/slot, print stuff - std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards) << " " << from_slot << " " << card_id_name(from_slot < best_cards.size() ? best_cards[from_slot] : NULL) << + std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards, true) << " " << from_slot << " " << card_id_name(from_slot < best_cards.size() ? best_cards[from_slot] : NULL) << " -> " << to_slot << " " << card_id_name(card_candidate) << ": "; best_score = current_score; best_cards = d1->cards; eval_commander = true; deck_has_been_improved = true; print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards); + print_deck_inline(best_score, best_commander, best_cards, true); } d1->cards = best_cards; } @@ -802,7 +806,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) } } std::cout << "Optimized Deck: "; - print_deck_inline(best_score, best_commander, best_cards); + print_deck_inline(best_score, best_commander, best_cards, true); } //------------------------------------------------------------------------------ // Implements iteration over all combination of k elements from n elements. @@ -911,7 +915,7 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig best_score = new_score; best_deck = deck; print_score_info(new_results, proc.factors); - print_deck_inline(best_score, commander, deck_cards); + print_deck_inline(best_score, commander, deck_cards, false); } //++num; // num_cards = num_cards_to_combine ... @@ -951,7 +955,7 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig best_score = new_score; best_deck = deck; print_score_info(new_results, proc.factors); - print_deck_inline(best_score, commander, deck_cards); + print_deck_inline(best_score, commander, deck_cards, false); } ++total_num_combinations_test; finished = cardAmounts.next(); From 9e7606db0680cd92b09331bf2850f84ecbe7ab22 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 31 Mar 2013 16:38:45 +0800 Subject: [PATCH 126/406] Fix bug: Intercept should not be activated if the intercepted skill is not proc'd. (Achievement Misdirection) --- sim.cpp | 80 +++++++++++++++++++++++++++++++------------------------- tyrant.h | 2 +- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/sim.cpp b/sim.cpp index 75159f22..d32f27e2 100644 --- a/sim.cpp +++ b/sim.cpp @@ -722,6 +722,15 @@ Results play(Field* fd) return {0, 0, 0, 0}; } +// Roll a coin in case an Activation skill has 50% chance to proc. +template +inline bool skill_roll(Field* fd) +{ return(true); } + +template<> +inline bool skill_roll(Field* fd) +{ return(fd->flip()); } + // Check if a skill actually proc'ed. template inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) @@ -757,31 +766,19 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return(c->m_player != ref->m_player && fd->flip()); -} - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return(fd->flip()); + return(c->m_player != ref->m_player); } template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return(!ref->m_card->m_flying && !ref->m_card->m_antiair > 0 && (fd->effect == Effect::high_skies || fd->flip())); + return(!ref->m_card->m_flying && !ref->m_card->m_antiair > 0); } template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return(!ref->m_immobilized && can_act(fd, ref) && fd->flip()); -} - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return(fd->flip()); + return(!ref->m_immobilized && can_act(fd, ref)); } template<> @@ -794,13 +791,7 @@ template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { // Never payback allied units (chaosed). - return(ref->m_card->m_type == CardType::assault && c->m_player != ref->m_player && ref->m_hp > 0 && fd->flip()); -} - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return(fd->flip()); + return(ref->m_card->m_type == CardType::assault && c->m_player != ref->m_player && ref->m_hp > 0); } template<> @@ -810,20 +801,26 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return(c->m_hp == 0 && !c->m_diseased && fd->flip()); + return(c->m_hp == 0 && !c->m_diseased); } template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ return(can_be_healed(&fd->players[c->m_player]->commander)); } +{ + return(can_be_healed(&fd->players[c->m_player]->commander)); +} template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ return(ref->m_card->m_type == CardType::assault); } +{ + return(ref->m_card->m_type == CardType::assault); +} template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ return(ref->m_card->m_type == CardType::assault && ref != c && ref->m_hp > 0 && fd->flip()); } +{ + return(ref->m_card->m_type == CardType::assault && ref != c && ref->m_hp > 0); +} template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) @@ -918,7 +915,7 @@ void check_regeneration(Field* fd) for(unsigned i(0); i < fd->killed_with_regen.size(); ++i) { CardStatus* status = fd->killed_with_regen[i]; - if(skill_check(fd, status, nullptr)) + if(fd->flip() && skill_check(fd, status, nullptr)) { count_achievement(fd, status); _DEBUG_MSG("%s regenerates with %u health\n", status_description(status).c_str(), status->m_card->m_health); @@ -1156,7 +1153,7 @@ struct PerformAttack // counter, berserk // assaults only: (crush, leech if still alive) // check regeneration - if(att_dmg > 0 && def_status->m_card->m_flying && skill_check(fd, def_status, att_status)) + if(att_dmg > 0 && def_status->m_card->m_flying && (fd->effect == Effect::high_skies || fd->flip()) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); _DEBUG_MSG("%s dodges with Flying\n", status_description(def_status).c_str()); @@ -1337,7 +1334,7 @@ struct PerformAttack template<> void PerformAttack::immobilize() { - if(att_status->m_card->m_immobilize && skill_check(fd, att_status, def_status)) + if(att_status->m_card->m_immobilize && fd->flip() && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); _DEBUG_MSG("%s immobilizes %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); @@ -1440,7 +1437,7 @@ void attack_phase(Field* fd) Storage& def_assaults(fd->tip->assaults); if(attack_power(att_status) == 0) { return; } unsigned num_attacks(1); - if(att_status->m_card->m_flurry > 0 && skill_check(fd, att_status, nullptr)) + if(att_status->m_card->m_flurry > 0 && fd->flip() && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); _DEBUG_MSG("%s activates Flurry\n", status_description(att_status).c_str()); @@ -1968,7 +1965,7 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ { if(skill_check(fd, src_status, dst_status)) { - if(is_evadable && dst_status->m_card->m_evade && skill_check(fd, dst_status, src_status)) + if(is_evadable && dst_status->m_card->m_evade && fd->flip() && skill_check(fd, dst_status, src_status)) { count_achievement(fd, dst_status); _DEBUG_MSG("%s %s (%u) on %s but it evades\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); @@ -1999,7 +1996,7 @@ bool check_and_perform_blitz(Field* fd, CardStatus* src_status) bool check_and_perform_recharge(Field* fd, CardStatus* src_status) { - if(skill_check(fd, src_status, nullptr)) + if(fd->flip() && skill_check(fd, src_status, nullptr)) { count_achievement(fd, src_status); _DEBUG_MSG("%s activates Recharge\n", status_description(src_status).c_str()); @@ -2037,18 +2034,26 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski } else { + if(!skill_roll(fd)) + { + return; + } index_start = index_end = get_target_hostile_index(fd, src_status, selection_array_size); } bool is_count_achievement(true); for(unsigned s_index(index_start); s_index <= index_end; ++s_index) { + if(std::get<3>(s) && !skill_roll(fd)) + { + continue; + } CardStatus* c(fd->selection_array[s_index]); if(check_and_perform_skill(fd, src_status, c, s, true, is_count_achievement)) { // Count at most once even targeting "All" is_count_achievement = false; // Payback - if(c->m_card->m_payback && skill_predicate(fd, src_status, s) && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) + if(c->m_card->m_payback && skill_predicate(fd, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_roll(fd) && skill_check(fd, src_status, c)) { count_achievement(fd, c); _DEBUG_MSG("%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); @@ -2078,13 +2083,18 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil bool is_count_achievement(true); for(unsigned s_index(index_start); s_index <= index_end; ++s_index) { + // So far no friendly activation skill needs to roll 50% but check it for completeness. + if(!skill_roll(fd)) + { + continue; + } CardStatus* c(fd->selection_array[s_index]); if(check_and_perform_skill(fd, src_status, c, s, false, is_count_achievement)) { // Count at most once even targeting "All" is_count_achievement = false; // Tribute - if(c->m_card->m_tribute && skill_predicate(fd, src_status, s) && skill_check(fd, c, src_status)) + if(c->m_card->m_tribute && skill_predicate(fd, src_status, s) && fd->flip() && skill_check(fd, c, src_status)) { count_achievement(fd, c); _DEBUG_MSG("Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); @@ -2218,7 +2228,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // evade check for mimic // individual skills are subject to evade checks too, // but resolve_skill will handle those. - if(c->m_card->m_evade && skill_check(fd, c, src_status)) + if(c->m_card->m_evade && fd->flip() && skill_check(fd, c, src_status)) { count_achievement(fd, c); _DEBUG_MSG("%s %s on %s but it evades\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); diff --git a/tyrant.h b/tyrant.h index 1d20ac4b..9b8a6f79 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.6" +#define TYRANT_OPTIMIZER_VERSION "1.0.7" #include #include From 57febc25efc3621727827d3da63a2e4f0007ee05 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 1 Apr 2013 13:35:28 +0800 Subject: [PATCH 127/406] Paybacked Jam should have 100% chance to proc. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index d32f27e2..f1ca89a0 100644 --- a/sim.cpp +++ b/sim.cpp @@ -2053,7 +2053,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski // Count at most once even targeting "All" is_count_achievement = false; // Payback - if(c->m_card->m_payback && skill_predicate(fd, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_roll(fd) && skill_check(fd, src_status, c)) + if(c->m_card->m_payback && skill_predicate(fd, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) { count_achievement(fd, c); _DEBUG_MSG("%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); From acd3e0fbbba909862348c2df204eaafe4cf0638b Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 1 Apr 2013 14:14:27 +0800 Subject: [PATCH 128/406] Fix bug: Flying should be checked before damage modifiers. (Burst/Valor/Armored/Pierce) --- sim.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sim.cpp b/sim.cpp index f1ca89a0..6b57fa96 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1139,13 +1139,9 @@ struct PerformAttack unsigned pre_modifier_dmg = attack_power(att_status); if(pre_modifier_dmg == 0) { return; } count_achievement(fd, att_status); - modify_attack_damage(pre_modifier_dmg); - if(att_status->m_player == 0) - { - fd->update_max_counter(fd->achievement.misc_req, AchievementMiscReq::damage, att_dmg); - } // Evaluation order: // assaults only: fly check + // modify damage // assaults only: immobilize // deal damage // assaults only: (siphon, poison, disease, on_kill) @@ -1153,13 +1149,19 @@ struct PerformAttack // counter, berserk // assaults only: (crush, leech if still alive) // check regeneration - if(att_dmg > 0 && def_status->m_card->m_flying && (fd->effect == Effect::high_skies || fd->flip()) && skill_check(fd, def_status, att_status)) + if(def_status->m_card->m_flying && (fd->effect == Effect::high_skies || fd->flip()) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); - _DEBUG_MSG("%s dodges with Flying\n", status_description(def_status).c_str()); + _DEBUG_MSG("%s attacks %s but it dodges with Flying\n", status_description(att_status).c_str(), status_description(def_status).c_str()); return; } + modify_attack_damage(pre_modifier_dmg); + if(att_status->m_player == 0) + { + fd->update_max_counter(fd->achievement.misc_req, AchievementMiscReq::damage, att_dmg); + } + // If Impenetrable, prevent attack damage against walls, // but still activate Counter! if(att_dmg > 0 && fd->effect == Effect::impenetrable && def_status->m_card->m_wall) From 8dd83cadf202b72a876fcb6a26681cd6d82a7bfc Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 1 Apr 2013 20:58:51 +0800 Subject: [PATCH 129/406] Rewrite Weaken/Jam/Chaos on Attacked/Death based on purei's tests and summary. --- card.h | 10 +++++----- sim.cpp | 53 +++++++++++++++++++++++++++++++++-------------------- tyrant.cpp | 2 +- tyrant.h | 10 ++++++---- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/card.h b/card.h index 0e4d22cb..9481ad15 100644 --- a/card.h +++ b/card.h @@ -58,15 +58,15 @@ class Card } void add_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills.push_back(std::make_tuple(v1, v2, v3, v4, on_act)); } + { m_skills.push_back(std::make_tuple(v1, v2, v3, v4, SkillMod::on_activate)); } void add_played_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_play.push_back(std::make_tuple(v1, v2, v3, v4, on_play)); } + { m_skills_on_play.push_back(std::make_tuple(v1, v2, v3, v4, SkillMod::on_play)); } void add_died_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_death.push_back(std::make_tuple(v1, v2, v3, v4, on_death)); } + { m_skills_on_death.push_back(std::make_tuple(v1, v2, v3, v4, SkillMod::on_death)); } void add_attacked_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_attacked.push_back(std::make_tuple(v1, v2, v3, v4, on_attacked)); } + { m_skills_on_attacked.push_back(std::make_tuple(v1, v2, v3, v4, SkillMod::on_attacked)); } void add_kill_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_kill.push_back(std::make_tuple(v1, v2, v3, v4, on_kill)); } + { m_skills_on_kill.push_back(std::make_tuple(v1, v2, v3, v4, SkillMod::on_kill)); } unsigned m_antiair; unsigned m_armored; diff --git a/sim.cpp b/sim.cpp index 6b57fa96..a4e59257 100644 --- a/sim.cpp +++ b/sim.cpp @@ -121,7 +121,7 @@ std::string skill_description(Field* fd, const SkillSpec& s) (std::get<3>(s) ? " all" : "") + (std::get<2>(s) == allfactions ? "" : std::string(" ") + faction_names[std::get<2>(s)]) + (std::get<1>(s) == 0 ? "" : std::string(" ") + to_string(std::get<1>(s))) + - (std::get<4>(s) == on_act ? "" : std::string(" on ") + skill_activation_modifier_names[std::get<4>(s)])); + skill_activation_modifier_names[std::get<4>(s)]); } } //------------------------------------------------------------------------------ @@ -513,10 +513,11 @@ void PlayCard::onPlaySkills() inline bool is_attacking_or_has_attacked(CardStatus* c) { return(c->m_step >= CardStep::attacking); } inline bool is_attacking(CardStatus* c) { return(c->m_step == CardStep::attacking); } inline bool has_attacked(CardStatus* c) { return(c->m_step == CardStep::attacked); } -// Can act by the end of next turn -inline bool can_act(Field* fd, CardStatus* c) { return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen && (fd->tapi == c->m_player ? c->m_delay == 0 || c->m_blitzing : c->m_delay <= 1)); } -// Can attack by the end of next turn -inline bool can_attack(Field* fd, CardStatus* c) { return(can_act(fd, c) && !c->m_immobilized && !c->m_stunned); } +inline bool is_jammed(CardStatus* c) { return(c->m_jammed || c->m_frozen); } +inline bool is_active(CardStatus* c) { return(c->m_delay == 0 || c->m_blitzing); } +inline bool is_active_next_turn(CardStatus* c) { return(c->m_delay <= 1); } +inline bool can_act(CardStatus* c) { return(c->m_hp > 0 && !is_jammed(c)); } +inline bool can_attack(CardStatus* c) { return(can_act(c) && !c->m_immobilized && !c->m_stunned); } // Can be healed / repaired inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } //------------------------------------------------------------------------------ @@ -569,7 +570,7 @@ Results play(Field* fd) (fd->effect == Effect::clone_experiment && (fd->turn == 9 || fd->turn == 10))) { std::vector skills; - skills.emplace_back(temporary_split, 0, allfactions, false, on_act); + skills.emplace_back(temporary_split, 0, allfactions, false, SkillMod::on_activate); // The skill doesn't actually come from the commander, // but we need to provide some source and it seemed most reasonable. evaluate_skills(fd, &fd->tap->commander, skills); @@ -610,7 +611,7 @@ Results play(Field* fd) { unsigned index(fd->rand(0, fd->cards.player_assaults.size() - 1)); std::vector skills; - skills.emplace_back(summon, fd->cards.player_assaults[index]->m_id, allfactions, false, on_act); + skills.emplace_back(summon, fd->cards.player_assaults[index]->m_id, allfactions, false, SkillMod::on_activate); evaluate_skills(fd, &fd->tap->commander, skills); } @@ -630,7 +631,7 @@ Results play(Field* fd) { // ca: current assault CardStatus& current_status(fd->tap->assaults[fd->current_ci]); - if(!can_act(fd, ¤t_status)) + if(!is_active(¤t_status) || !can_act(¤t_status)) { //_DEBUG_MSG("! Assault %u (%s) hp: %u, jammed %u\n", card_index, current_status.m_card->m_name.c_str(), current_status.m_hp, current_status.m_jammed); continue; @@ -775,10 +776,11 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(!ref->m_card->m_flying && !ref->m_card->m_antiair > 0); } +// Not yet support on Attacked/on Death. template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return(!ref->m_immobilized && can_act(fd, ref)); + return(!ref->m_immobilized && is_active(ref) && can_act(ref)); } template<> @@ -1445,7 +1447,7 @@ void attack_phase(Field* fd) _DEBUG_MSG("%s activates Flurry\n", status_description(att_status).c_str()); num_attacks += att_status->m_card->m_flurry; } - for(unsigned attack_index(0); attack_index < num_attacks && can_attack(fd, att_status) && fd->tip->commander.m_hp > 0; ++attack_index) + for(unsigned attack_index(0); attack_index < num_attacks && can_attack(att_status) && fd->tip->commander.m_hp > 0; ++attack_index) { // 3 possibilities: // - 1. attack against the assault in front @@ -1474,7 +1476,7 @@ void attack_phase(Field* fd) if(fd->end) { return; } // stille alive? attack the card in front - if(can_attack(fd, att_status) && alive_assault(def_assaults, fd->current_ci)) + if(can_attack(att_status) && alive_assault(def_assaults, fd->current_ci)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); } @@ -1485,7 +1487,7 @@ void attack_phase(Field* fd) if(fd->end) { return; } // still alive? attack the card on the right - if(!fd->end && can_attack(fd, att_status) && alive_assault(def_assaults, fd->current_ci + 1)) + if(!fd->end && can_attack(att_status) && alive_assault(def_assaults, fd->current_ci + 1)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(); } @@ -1531,7 +1533,7 @@ inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { - if(can_act(fd, c) && !is_attacking_or_has_attacked(c)) + if(can_act(c) && (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))) { for(auto& s: c->m_card->m_skills) { @@ -1546,7 +1548,10 @@ template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { const auto& mod = std::get<4>(s); - return(!c->m_chaosed && can_act(fd, c) && !(mod == on_attacked && is_attacking_or_has_attacked(c)) && !(mod == on_death && has_attacked(c))); + return(!c->m_chaosed && can_act(c) && + (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : + mod == SkillMod::on_death ? is_active(c) && !has_attacked(c) : + is_active(c) || is_active_next_turn(c))); } template<> @@ -1584,7 +1589,10 @@ template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { const auto& mod = std::get<4>(s); - return(can_act(fd, c) && !(mod == on_attacked && is_attacking_or_has_attacked(c)) && !(mod == on_death && has_attacked(c))); + return(can_act(c) && + (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : + mod == SkillMod::on_death ? is_active(c) && !has_attacked(c) : + is_active(c) || is_active_next_turn(c))); } template<> @@ -1597,7 +1605,9 @@ inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) -{ return(can_attack(fd, c) && !is_attacking_or_has_attacked(c)); } +{ + return(can_attack(c) && (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); +} template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) @@ -1629,7 +1639,10 @@ template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { const auto& mod = std::get<4>(s); - return(can_attack(fd, c) && attack_power(c) > 0 && !((mod == on_attacked || mod == on_death) && is_attacking_or_has_attacked(c))); + return(can_attack(c) && attack_power(c) > 0 && + (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : + mod == SkillMod::on_death ? is_active(c) && !has_attacked(c) : + is_active(c) || is_active_next_turn(c))); } template @@ -1928,7 +1941,7 @@ void maybeTriggerRegen(Field* fd) template<> void maybeTriggerRegen(Field* fd) { - fd->skill_queue.emplace_front(nullptr, std::make_tuple(trigger_regen, 0, allfactions, false, on_act)); + fd->skill_queue.emplace_front(nullptr, std::make_tuple(trigger_regen, 0, allfactions, false, SkillMod::on_activate)); } unsigned get_target_hostile_index(Field* fd, CardStatus* src_status, unsigned selection_array_size) @@ -2245,7 +2258,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) if(std::get<0>(skill) == mimic || (std::get<0>(skill) == supply && src_status->m_card->m_type != CardType::assault)) { continue; } - SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill), on_act); + SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill), SkillMod::on_activate); // _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd, skill).c_str()); fd->skill_queue.emplace_back(src_status, src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); resolve_skill(fd); @@ -2303,7 +2316,7 @@ inline void cards_gain_skill(Cards& cards, Skill new_skill, unsigned magnitude, { if(replace_instance) { - skill = std::make_tuple(new_skill, magnitude, allfactions, all, on_act); + skill = std::make_tuple(new_skill, magnitude, allfactions, all, SkillMod::on_activate); } do_add_skill = false; } diff --git a/tyrant.cpp b/tyrant.cpp index c8da5c3f..e6a528c3 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -33,7 +33,7 @@ std::set helpful_skills{ augment, cleanse, heal, protect, rally, repair, rush, supply, }; -std::string skill_activation_modifier_names[num_skill_activation_modifiers] = {"", "Play", "Attacked", "Kill", "Death", }; +std::string skill_activation_modifier_names[SkillMod::num_skill_activation_modifiers] = {"", " on Play", " on Attacked", " on Kill", " on Death", }; std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Structure", "Action", }; diff --git a/tyrant.h b/tyrant.h index 9b8a6f79..7edced90 100644 --- a/tyrant.h +++ b/tyrant.h @@ -44,16 +44,18 @@ enum Skill extern std::string skill_names[num_skills]; extern std::set helpful_skills; -enum SkillActivationModifier +namespace SkillMod { +enum SkillMod { - on_act, + on_activate, on_play, on_attacked, on_kill, on_death, num_skill_activation_modifiers }; -extern std::string skill_activation_modifier_names[num_skill_activation_modifiers]; +} +extern std::string skill_activation_modifier_names[SkillMod::num_skill_activation_modifiers]; namespace CardType { enum CardType { @@ -148,6 +150,6 @@ enum SkillSourceType source_chaos }; -typedef std::tuple SkillSpec; +typedef std::tuple SkillSpec; #endif From b8efa5d8e02a66606a056af6ba8d8fe83240ac26 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 2 Apr 2013 09:59:09 +0800 Subject: [PATCH 130/406] Add flag tournament. --- tyrant_optimize.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 12443283..8ded16a6 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1136,6 +1136,10 @@ int main(int argc, char** argv) { keep_commander = true; } + else if(strcmp(argv[argIndex], "tournament") == 0) + { + gamemode = tournament; + } else if(strcmp(argv[argIndex], "-e") == 0) { std::string arg_effect(argv[argIndex + 1]); From 30ab9742d4d82539ce9b4c9a42765a928857d34e Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 2 Apr 2013 10:00:13 +0800 Subject: [PATCH 131/406] Commander's existing Chaos skill DOES get replaced with Chaos All under 'Friendly Fire' battleground effect. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index a4e59257..496d4670 100644 --- a/sim.cpp +++ b/sim.cpp @@ -2376,7 +2376,7 @@ void modify_cards(Cards& cards, enum Effect effect) break; case Effect::friendly_fire: cards_gain_skill(cards, strike, 1, false, false); - cards_gain_skill(cards, chaos, 0, true, false); + cards_gain_skill(cards, chaos, 0, true, true); break; case Effect::genesis: // Do nothing; this is implemented in play From e303c5081b7a853f3dfd84ceb475fee2832bce8a Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 2 Apr 2013 13:19:34 +0800 Subject: [PATCH 132/406] Regenerated units should not count as killed for achievements. --- sim.cpp | 36 ++++++++++++++++++++++-------------- sim.h | 1 - tyrant.h | 2 +- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/sim.cpp b/sim.cpp index 496d4670..e62baeac 100644 --- a/sim.cpp +++ b/sim.cpp @@ -61,7 +61,6 @@ CardStatus::CardStatus(const Card* card) : m_weakened(0), m_temporary_split(false), m_is_summoned(false), - m_has_regenerated(false), m_step(CardStep::none) { } @@ -97,7 +96,6 @@ inline void CardStatus::set(const Card& card) m_stunned = 0; m_temporary_split = false; m_is_summoned = false; - m_has_regenerated = false; m_step = CardStep::none; } //------------------------------------------------------------------------------ @@ -857,6 +855,20 @@ inline bool count_achievement(Field* fd, const CardStatus* c) //------------------------------------------------------------------------------ // All the stuff that happens at the beginning of a turn, before a card is played // returns true iff the card died. +inline void count_killed_achievements(Field* fd, const CardStatus* status) +{ + if(status->m_player == 1) + { + if(!status->m_is_summoned) + { + fd->inc_counter(fd->achievement.unit_type_killed, status->m_card->m_type); + } + if(status->m_card->m_flying) + { + fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::unit_with_flying_killed); + } + } +} void remove_hp(Field* fd, CardStatus& status, unsigned dmg) { assert(status.m_hp > 0); @@ -865,17 +877,6 @@ void remove_hp(Field* fd, CardStatus& status, unsigned dmg) if(status.m_hp == 0) { _DEBUG_MSG("%s dies\n", status_description(&status).c_str()); - if(status.m_player == 1 && !status.m_has_regenerated) - { - if(!status.m_is_summoned) - { - fd->inc_counter(fd->achievement.unit_type_killed, status.m_card->m_type); - } - if(status.m_card->m_flying) - { - fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::unit_with_flying_killed); - } - } if(status.m_card->m_skills_on_death.size() > 0) { fd->killed_with_on_death.push_back(&status); @@ -884,6 +885,10 @@ void remove_hp(Field* fd, CardStatus& status, unsigned dmg) { fd->killed_with_regen.push_back(&status); } + else + { + count_killed_achievements(fd, &status); + } } } inline bool is_it_dead(CardStatus& c) @@ -922,7 +927,10 @@ void check_regeneration(Field* fd) count_achievement(fd, status); _DEBUG_MSG("%s regenerates with %u health\n", status_description(status).c_str(), status->m_card->m_health); add_hp(fd, status, status->m_card->m_regenerate); - status->m_has_regenerated = true; + } + else + { + count_killed_achievements(fd, status); } } fd->killed_with_regen.clear(); diff --git a/sim.h b/sim.h index 3f588a07..c1f5f08d 100644 --- a/sim.h +++ b/sim.h @@ -138,7 +138,6 @@ struct CardStatus unsigned m_weakened; bool m_temporary_split; bool m_is_summoned; // is this card summoned (or split)? - bool m_has_regenerated; // has this card regenerated? CardStep m_step; CardStatus() {} diff --git a/tyrant.h b/tyrant.h index 7edced90..c7154eec 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.7" +#define TYRANT_OPTIMIZER_VERSION "1.0.8" #include #include From e0c9b490be7bbbaa9861eaf20dbdea97e638f1c6 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 2 Apr 2013 13:49:30 +0800 Subject: [PATCH 133/406] Fix bug: Immobilize should target a assault with timer of at most 1. Immobilize should target a dying assault. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index e62baeac..f9da03fa 100644 --- a/sim.cpp +++ b/sim.cpp @@ -778,7 +778,7 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return(!ref->m_immobilized && is_active(ref) && can_act(ref)); + return(!ref->m_immobilized && !is_jammed(ref) && is_active_next_turn(ref)); } template<> From 6c0acc8c2ee157526d767f5d7e91443c3dc97bef Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 2 Apr 2013 15:42:08 +0800 Subject: [PATCH 134/406] Fix bug: ANP for one-card decks. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index f9da03fa..3467b36b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -539,7 +539,7 @@ Results play(Field* fd) // ANP: Last decision point is second-to-last card played. fd->points_since_last_decision = 0; unsigned p0_size = fd->players[0]->deck->cards.size(); - fd->last_decision_turn = p0_size * 2 - (fd->gamemode == surge ? 2 : 3); + fd->last_decision_turn = p0_size == 1 ? 0 : p0_size * 2 - (fd->gamemode == surge ? 2 : 3); // Count commander as played for achievements (not count in type / faction / rarity requirements) fd->inc_counter(fd->achievement.unit_played, fd->players[0]->commander.m_card->m_id); From 90e1730f187316fc6662f4e55bbb072a288f9200 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 3 Apr 2013 17:50:25 +0800 Subject: [PATCH 135/406] Improve display of starting decks. --- deck.cpp | 10 ++++++---- deck.h | 2 +- sim.cpp | 36 ++++++++++++++++++------------------ tyrant.h | 2 +- tyrant_optimize.cpp | 8 ++++++-- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/deck.cpp b/deck.cpp index 4ecb0737..237fffe8 100644 --- a/deck.cpp +++ b/deck.cpp @@ -252,7 +252,9 @@ std::string Deck::short_description() const return ios.str(); } -std::string Deck::long_description() const +extern std::string card_description(const Cards& cards, const Card* c); + +std::string Deck::long_description(const Cards& all_cards) const { std::stringstream ios; ios << short_description() << std::endl; @@ -262,7 +264,7 @@ std::string Deck::long_description() const } if(commander) { - ios << commander->m_name << "\n"; + ios << card_description(all_cards, commander) << "\n"; } else { @@ -274,14 +276,14 @@ std::string Deck::long_description() const } for(const Card* card: cards) { - ios << " " << card->m_name << std::endl; + ios << " " << card_description(all_cards, card) << "\n"; } for(auto& pool: raid_cards) { ios << pool.first << " from:\n"; for(auto& card: pool.second) { - ios << " " << card->m_name << "\n"; + ios << " " << card_description(all_cards, card) << "\n"; } } return ios.str(); diff --git a/deck.h b/deck.h index ca46f1c7..f53cf7c9 100644 --- a/deck.h +++ b/deck.h @@ -80,7 +80,7 @@ struct Deck Deck* clone() const; std::string short_description() const; - std::string long_description() const; + std::string long_description(const Cards& all_cards) const; const Card* get_commander(); const Card* next(); void shuffle(std::mt19937& re); diff --git a/sim.cpp b/sim.cpp index 3467b36b..51631dc2 100644 --- a/sim.cpp +++ b/sim.cpp @@ -108,12 +108,12 @@ inline int attack_power(CardStatus* att) return(safe_minus(att->m_card->m_attack + att->m_berserk + att->m_rallied, att->m_weakened)); } //------------------------------------------------------------------------------ -std::string skill_description(Field* fd, const SkillSpec& s) +std::string skill_description(const Cards& cards, const SkillSpec& s) { switch(std::get<0>(s)) { case summon: - return(skill_names[std::get<0>(s)] + " " + fd->cards.by_id(std::get<1>(s))->m_name.c_str()); + return(skill_names[std::get<0>(s)] + " " + cards.by_id(std::get<1>(s))->m_name.c_str()); default: return(skill_names[std::get<0>(s)] + (std::get<3>(s) ? " all" : "") + @@ -123,7 +123,7 @@ std::string skill_description(Field* fd, const SkillSpec& s) } } //------------------------------------------------------------------------------ -std::string card_description(Field* fd, const Card* c) +std::string card_description(const Cards& cards, const Card* c) { std::string desc; desc = c->m_name; @@ -175,14 +175,14 @@ std::string card_description(Field* fd, const Card* c) if(c->m_tribute) { desc += ", tribute"; } if(c->m_valor > 0) { desc += ", valor " + to_string(c->m_valor); } if(c->m_wall) { desc += ", wall"; } - for(auto& skill: c->m_skills) { desc += ", " + skill_description(fd, skill); } - for(auto& skill: c->m_skills_on_play) { desc += ", " + skill_description(fd, skill); } - for(auto& skill: c->m_skills_on_kill) { desc += ", " + skill_description(fd, skill); } + for(auto& skill: c->m_skills) { desc += ", " + skill_description(cards, skill); } + for(auto& skill: c->m_skills_on_play) { desc += ", " + skill_description(cards, skill); } + for(auto& skill: c->m_skills_on_kill) { desc += ", " + skill_description(cards, skill); } if(c->m_berserk_oa > 0) { desc += ", berserk " + to_string(c->m_berserk_oa); } if(c->m_disease_oa) { desc += ", disease on Attacked"; } if(c->m_poison_oa > 0) { desc += ", poison " + to_string(c->m_poison_oa) + " on Attacked"; } - for(auto& skill: c->m_skills_on_attacked) { desc += ", " + skill_description(fd, skill); } - for(auto& skill: c->m_skills_on_death) { desc += ", " + skill_description(fd, skill); } + for(auto& skill: c->m_skills_on_attacked) { desc += ", " + skill_description(cards, skill); } + for(auto& skill: c->m_skills_on_death) { desc += ", " + skill_description(cards, skill); } return(desc); } //------------------------------------------------------------------------------ @@ -310,7 +310,7 @@ void prepend_on_death(Field* fd) } for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_on_death)) { - _DEBUG_MSG("On death skill pushed in front: %s\n", skill_description(fd, skill).c_str()); + _DEBUG_MSG("On death skill pushed in front: %s\n", skill_description(fd->cards, skill).c_str()); fd->skill_queue.emplace_front(status, skill); } } @@ -366,7 +366,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector assert(fd->skill_queue.size() == 0); for(auto& skill: skills) { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); bool fusion_active = status->m_card->m_fusion && status->m_player == fd->tapi && fd->fusion_count >= 3; auto& augmented_s = status->m_augmented > 0 ? augmented_skill(status, skill) : skill; auto& fusioned_s = fusion_active ? fusioned_skill(augmented_s) : augmented_s; @@ -426,7 +426,7 @@ struct PlayCard fd->inc_counter(fd->achievement.unit_faction_played, card->m_faction); fd->inc_counter(fd->achievement.unit_rarity_played, card->m_rarity); } - _DEBUG_MSG("%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), static_cast(storage->size() - 1), card_description(fd, card).c_str()); + _DEBUG_MSG("%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), static_cast(storage->size() - 1), card_description(fd->cards, card).c_str()); } // all except assault: noop @@ -447,7 +447,7 @@ struct PlayCard { for(auto& skill: card->m_skills_on_play) { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); fd->skill_queue.emplace_back(status, skill); resolve_skill(fd); if(fd->end) { break; } @@ -501,7 +501,7 @@ void PlayCard::onPlaySkills() { for(auto& skill: card->m_skills) { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd, skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); fd->skill_queue.emplace_back(status, skill); resolve_skill(fd); if(fd->end) { break; } @@ -646,7 +646,7 @@ Results play(Field* fd) _DEBUG_MSG("%s splits %s\n", status_description(¤t_status).c_str(), status_description(&status_split).c_str()); for(auto& skill: status_split.m_card->m_skills_on_play) { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(¤t_status).c_str(), skill_description(fd, skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(¤t_status).c_str(), skill_description(fd->cards, skill).c_str()); fd->skill_queue.emplace_back(&status_split, skill); resolve_skill(fd); if(fd->end) { break; } @@ -1332,7 +1332,7 @@ struct PerformAttack } for(auto& oa_skill: def_status->m_card->m_skills_on_attacked) { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(def_status).c_str(), skill_description(fd, oa_skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(def_status).c_str(), skill_description(fd->cards, oa_skill).c_str()); fd->skill_queue.emplace_back(def_status, def_status->m_augmented > 0 ? augmented_skill(def_status, oa_skill) : oa_skill); resolve_skill(fd); if(fd->end) { break; } @@ -1395,7 +1395,7 @@ void PerformAttack::on_kill() { for(auto& on_kill_skill: att_status->m_card->m_skills_on_kill) { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(att_status).c_str(), skill_description(fd, on_kill_skill).c_str()); +// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(att_status).c_str(), skill_description(fd->cards, on_kill_skill).c_str()); fd->skill_queue.emplace_back(att_status, on_kill_skill); resolve_skill(fd); if(fd->end) { break; } @@ -2201,7 +2201,7 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) card_status.m_index = storage->size() - 1; card_status.m_player = player; card_status.m_is_summoned = true; - _DEBUG_MSG("%s Summon %s %u [%s]\n", status_description(src_status).c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd, summoned).c_str()); + _DEBUG_MSG("%s Summon %s %u [%s]\n", status_description(src_status).c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); prepend_skills(fd, &card_status); if(card_status.m_card->m_blitz) { @@ -2267,7 +2267,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) (std::get<0>(skill) == supply && src_status->m_card->m_type != CardType::assault)) { continue; } SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill), SkillMod::on_activate); -// _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd, skill).c_str()); +// _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd->cards, skill).c_str()); fd->skill_queue.emplace_back(src_status, src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); resolve_skill(fd); if(fd->end) { break; } diff --git a/tyrant.h b/tyrant.h index c7154eec..72842949 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.8" +#define TYRANT_OPTIMIZER_VERSION "1.0.9" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 8ded16a6..b409aee1 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1216,6 +1216,10 @@ int main(int argc, char** argv) todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); argIndex += 1; } + else if(strcmp(argv[argIndex], "verbose") == 0) + { + debug_print = true; + } else if(strcmp(argv[argIndex], "debug") == 0) { debug_print = true; @@ -1314,10 +1318,10 @@ int main(int argc, char** argv) } modify_cards(cards, effect); - std::cout << "Attacker: " << (debug_print ? att_deck->long_description() : att_deck->short_description()) << std::endl; + std::cout << "Attacker: " << (debug_print ? att_deck->long_description(cards) : att_deck->short_description()) << std::endl; for(auto def_deck: def_decks) { - std::cout << "Defender: " << (debug_print ? def_deck->long_description() : def_deck->short_description()) << std::endl; + std::cout << "Defender: " << (debug_print ? def_deck->long_description(cards) : def_deck->short_description()) << std::endl; } if(effect != Effect::none) { From ab4392f5797588c99edf766fd50d65c7caf67e37 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 4 Apr 2013 00:48:11 +0800 Subject: [PATCH 136/406] Fix bug: Jammed Assaults should be treated as already attacked when they skip their attack phase. --- sim.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sim.cpp b/sim.cpp index 51631dc2..e315c218 100644 --- a/sim.cpp +++ b/sim.cpp @@ -632,6 +632,7 @@ Results play(Field* fd) if(!is_active(¤t_status) || !can_act(¤t_status)) { //_DEBUG_MSG("! Assault %u (%s) hp: %u, jammed %u\n", card_index, current_status.m_card->m_name.c_str(), current_status.m_hp, current_status.m_jammed); + current_status.m_step = CardStep::attacked; continue; } // Special case: check for split (tartarus swarm raid, or clone battlefield effects) From 6142030ab9b632f5df31a179e8048f703d785b54 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 4 Apr 2013 14:08:56 +0800 Subject: [PATCH 137/406] Fix bug: on Kill skills should be affected by Augment/Infuse. --- sim.cpp | 111 +++++++++++++++++++++---------------------------------- tyrant.h | 2 +- 2 files changed, 44 insertions(+), 69 deletions(-) diff --git a/sim.cpp b/sim.cpp index e315c218..ac7abd4f 100644 --- a/sim.cpp +++ b/sim.cpp @@ -300,6 +300,33 @@ inline unsigned opponent(unsigned player) return((player + 1) % 2); } //------------------------------------------------------------------------------ +SkillSpec augmented_skill(const CardStatus* status, const SkillSpec& s) +{ + if (std::get<0>(s) == augment || std::get<0>(s) == summon || std::get<1>(s) == 0) + { + return s; + } + SkillSpec augmented_s = s; + std::get<1>(augmented_s) += status->m_augmented; + return(augmented_s); +} +SkillSpec fusioned_skill(const SkillSpec& s) +{ + SkillSpec fusioned_s = s; + std::get<1>(fusioned_s) *= 2; + return(fusioned_s); +} +SkillSpec infused_skill(const SkillSpec& s) +{ + if (std::get<2>(s) == allfactions || std::get<2>(s) == bloodthirsty || helpful_skills.find(std::get<0>(s)) == helpful_skills.end()) + { + return s; + } + SkillSpec infused_s = s; + std::get<2>(infused_s) = bloodthirsty; + return(infused_s); +} +//------------------------------------------------------------------------------ void prepend_on_death(Field* fd) { for(auto status: boost::adaptors::reverse(fd->killed_with_on_death)) @@ -324,42 +351,24 @@ void resolve_skill(Field* fd) { auto skill_instance(fd->skill_queue.front()); auto& status(std::get<0>(skill_instance)); - auto& skill(std::get<1>(skill_instance)); + const auto& skill(std::get<1>(skill_instance)); fd->skill_queue.pop_front(); - if(!status || !status->m_jammed) + if(!status) { skill_table[std::get<0>(skill)](fd, status, skill); } + else if(!status->m_jammed) + { + bool fusion_active = status->m_card->m_fusion && status->m_player == fd->tapi && fd->fusion_count >= 3; + auto& augmented_s = status->m_augmented > 0 ? augmented_skill(status, skill) : skill; + auto& fusioned_s = fusion_active ? fusioned_skill(augmented_s) : augmented_s; + auto& infused_s = status->m_infused ? infused_skill(fusioned_s) : fusioned_s; + skill_table[std::get<0>(skill)](fd, status, infused_s); + } } } //------------------------------------------------------------------------------ void attack_phase(Field* fd); -SkillSpec augmented_skill(CardStatus* status, const SkillSpec& s) -{ - if (std::get<0>(s) == augment || std::get<0>(s) == summon || std::get<1>(s) == 0) - { - return s; - } - SkillSpec augmented_s = s; - std::get<1>(augmented_s) += status->m_augmented; - return(augmented_s); -} -SkillSpec fusioned_skill(const SkillSpec& s) -{ - SkillSpec fusioned_s = s; - std::get<1>(fusioned_s) *= 2; - return(fusioned_s); -} -SkillSpec infused_skill(const SkillSpec& s) -{ - if (std::get<2>(s) == allfactions || std::get<2>(s) == bloodthirsty || helpful_skills.find(std::get<0>(s)) == helpful_skills.end()) - { - return s; - } - SkillSpec infused_s = s; - std::get<2>(infused_s) = bloodthirsty; - return(infused_s); -} void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills) { assert(status); @@ -367,11 +376,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector for(auto& skill: skills) { // _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); - bool fusion_active = status->m_card->m_fusion && status->m_player == fd->tapi && fd->fusion_count >= 3; - auto& augmented_s = status->m_augmented > 0 ? augmented_skill(status, skill) : skill; - auto& fusioned_s = fusion_active ? fusioned_skill(augmented_s) : augmented_s; - auto& infused_s = status->m_infused ? infused_skill(fusioned_s) : fusioned_s; - fd->skill_queue.emplace_back(status, infused_s); + fd->skill_queue.emplace_back(status, skill); resolve_skill(fd); if(fd->end) { break; } } @@ -445,13 +450,7 @@ struct PlayCard template void onPlaySkills() { - for(auto& skill: card->m_skills_on_play) - { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); - fd->skill_queue.emplace_back(status, skill); - resolve_skill(fd); - if(fd->end) { break; } - } + evaluate_skills(fd, status, card->m_skills_on_play); } }; // assault @@ -499,13 +498,7 @@ void PlayCard::fieldEffects() template <> void PlayCard::onPlaySkills() { - for(auto& skill: card->m_skills) - { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); - fd->skill_queue.emplace_back(status, skill); - resolve_skill(fd); - if(fd->end) { break; } - } + evaluate_skills(fd, status, card->m_skills); } //------------------------------------------------------------------------------ inline bool is_attacking_or_has_attacked(CardStatus* c) { return(c->m_step >= CardStep::attacking); } @@ -645,13 +638,7 @@ Results play(Field* fd) status_split.m_player = fd->tapi; status_split.m_is_summoned = true; _DEBUG_MSG("%s splits %s\n", status_description(¤t_status).c_str(), status_description(&status_split).c_str()); - for(auto& skill: status_split.m_card->m_skills_on_play) - { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(¤t_status).c_str(), skill_description(fd->cards, skill).c_str()); - fd->skill_queue.emplace_back(&status_split, skill); - resolve_skill(fd); - if(fd->end) { break; } - } + evaluate_skills(fd, &status_split, status_split.m_card->m_skills_on_play); if(status_split.m_card->m_blitz) { check_and_perform_blitz(fd, &status_split); @@ -1331,13 +1318,7 @@ struct PerformAttack count_achievement(fd, def_status); def_status->m_berserk += def_status->m_card->m_berserk_oa; } - for(auto& oa_skill: def_status->m_card->m_skills_on_attacked) - { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(def_status).c_str(), skill_description(fd->cards, oa_skill).c_str()); - fd->skill_queue.emplace_back(def_status, def_status->m_augmented > 0 ? augmented_skill(def_status, oa_skill) : oa_skill); - resolve_skill(fd); - if(fd->end) { break; } - } + evaluate_skills(fd, def_status, def_status->m_card->m_skills_on_attacked); } template @@ -1394,13 +1375,7 @@ void PerformAttack::on_kill() { if(killed_by_attack) { - for(auto& on_kill_skill: att_status->m_card->m_skills_on_kill) - { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(att_status).c_str(), skill_description(fd->cards, on_kill_skill).c_str()); - fd->skill_queue.emplace_back(att_status, on_kill_skill); - resolve_skill(fd); - if(fd->end) { break; } - } + evaluate_skills(fd, att_status, att_status->m_card->m_skills_on_kill); } } diff --git a/tyrant.h b/tyrant.h index 72842949..feaf9b1f 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.9" +#define TYRANT_OPTIMIZER_VERSION "1.0.10" #include #include From 588a88e021a30377ef77219e796a837a64bdc7f5 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 4 Apr 2013 14:13:23 +0800 Subject: [PATCH 138/406] Fix debug output. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index ac7abd4f..5d92a3bd 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1271,13 +1271,13 @@ struct PerformAttack if(debug_print) { reduced_desc += "-" + to_string(att_card.m_pierce) + "(pierce)"; } reduced_dmg = safe_minus(reduced_dmg, att_card.m_pierce); } + att_dmg = safe_minus(att_dmg, reduced_dmg); if(debug_print) { if(!reduced_desc.empty()) { desc += "-[" + reduced_desc + "]"; } if(!desc.empty()) { desc += "=" + to_string(att_dmg); } _DEBUG_MSG("%s attacks %s for %u%s damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), pre_modifier_dmg, desc.c_str()); } - att_dmg = safe_minus(att_dmg, reduced_dmg); } template From 3e8f277e8d8a874966f6ae3d081fca9021b3428c Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 5 Apr 2013 12:25:13 +0800 Subject: [PATCH 139/406] Fix bug: Split skill gained from Clone battleground effects should be activiated after other skills. --- sim.cpp | 81 +++++++++++++++++++++++++----------------------------- tyrant.cpp | 3 +- tyrant.h | 3 +- xml.cpp | 4 +-- 4 files changed, 42 insertions(+), 49 deletions(-) diff --git a/sim.cpp b/sim.cpp index 5d92a3bd..1967bcf2 100644 --- a/sim.cpp +++ b/sim.cpp @@ -170,7 +170,6 @@ std::string card_description(const Cards& cards, const Card* c) if(c->m_refresh) { desc += ", refresh"; } if(c->m_regenerate > 0) { desc += ", regenerate " + to_string(c->m_regenerate); } if(c->m_siphon > 0) { desc += ", siphon " + to_string(c->m_siphon); } - if(c->m_split) { desc += ", split"; } if(c->m_swipe) { desc += ", swipe"; } if(c->m_tribute) { desc += ", tribute"; } if(c->m_valor > 0) { desc += ", valor " + to_string(c->m_valor); } @@ -419,7 +418,6 @@ struct PlayCard status->set(card); status->m_index = storage->size() - 1; status->m_player = fd->tapi; - status->m_temporary_split = false; if(fd->turn == 1 && fd->gamemode == tournament && status->m_delay > 0) { ++status->m_delay; @@ -560,11 +558,24 @@ Results play(Field* fd) if(fd->effect == Effect::clone_project || (fd->effect == Effect::clone_experiment && (fd->turn == 9 || fd->turn == 10))) { - std::vector skills; - skills.emplace_back(temporary_split, 0, allfactions, false, SkillMod::on_activate); - // The skill doesn't actually come from the commander, - // but we need to provide some source and it seemed most reasonable. - evaluate_skills(fd, &fd->tap->commander, skills); + CardStatus* target(nullptr); + unsigned selection_array_size(0); + for(auto c: fd->tap->assaults.m_indirect) + { + if (c->m_delay == 0 && c->m_hp > 0) + { + ++ selection_array_size; + if (fd->rand(0, selection_array_size - 1) == 0) + { + target = c; + } + } + } + if(target != nullptr) + { + _DEBUG_MSG("%s gains skill Split until end of turn.\n", status_description(target).c_str()); + target->m_temporary_split = true; + } } // Play a card @@ -628,25 +639,15 @@ Results play(Field* fd) current_status.m_step = CardStep::attacked; continue; } - // Special case: check for split (tartarus swarm raid, or clone battlefield effects) - if((current_status.m_temporary_split || current_status.m_card->m_split) && fd->tap->assaults.size() + fd->tap->structures.size() < 100) - { - // TODO count_achievement(fd, current_status) - CardStatus& status_split(fd->tap->assaults.add_back()); - status_split.set(current_status.m_card); - status_split.m_index = fd->tap->assaults.size() - 1; - status_split.m_player = fd->tapi; - status_split.m_is_summoned = true; - _DEBUG_MSG("%s splits %s\n", status_description(¤t_status).c_str(), status_description(&status_split).c_str()); - evaluate_skills(fd, &status_split, status_split.m_card->m_skills_on_play); - if(status_split.m_card->m_blitz) - { - check_and_perform_blitz(fd, &status_split); - } - // TODO: Use summon to implement split? - } // Evaluate skills evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); + // Special case: check for temporary split (clone battlefield effects) + if(current_status.m_temporary_split) + { + std::vector skills; + skills.emplace_back(split, 0, allfactions, false, SkillMod::on_activate); + evaluate_skills(fd, ¤t_status, skills); + } // Attack if(!fd->end && !current_status.m_immobilized && !current_status.m_stunned && current_status.m_hp > 0) { @@ -1613,12 +1614,6 @@ template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { return(can_be_healed(c)); } -template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) -// It is unnecessary to check for Blitz, since temporary_split status is -// awarded before a card is played. -{ return(c->m_delay == 0 && c->m_hp > 0); } - template<> inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) { @@ -1750,12 +1745,6 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) add_hp(fd, c, v); } -template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) -{ - c->m_temporary_split = true; -} - template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { @@ -1908,9 +1897,6 @@ template<> std::vector& skill_targets(Field* fd, CardStatus template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_assault(fd, src_status)); } @@ -2162,6 +2148,15 @@ inline void prepend_skills(Field* fd, CardStatus* status) } } +template +void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s); + +void perform_split(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + perform_summon(fd, src_status, SkillSpec(summon, src_status->m_card->m_id, std::get<2>(s), std::get<3>(s), std::get<4>(s))); +} + +template void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) { unsigned player = src_status->m_player; @@ -2177,7 +2172,7 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) card_status.m_index = storage->size() - 1; card_status.m_player = player; card_status.m_is_summoned = true; - _DEBUG_MSG("%s Summon %s %u [%s]\n", status_description(src_status).c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); + _DEBUG_MSG("%s %s %s %u [%s]\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); prepend_skills(fd, &card_status); if(card_status.m_card->m_blitz) { @@ -2271,9 +2266,9 @@ void fill_skill_table() skill_table[shock] = perform_shock; skill_table[siege] = perform_targetted_hostile_fast; skill_table[supply] = perform_targetted_allied_fast; + skill_table[split] = perform_split; skill_table[strike] = perform_targetted_hostile_fast; - skill_table[summon] = perform_summon; - skill_table[temporary_split] = perform_targetted_allied_fast; + skill_table[summon] = perform_summon; skill_table[trigger_regen] = perform_trigger_regen; skill_table[weaken] = perform_targetted_hostile_fast; } @@ -2356,7 +2351,7 @@ void modify_cards(Cards& cards, enum Effect effect) break; case Effect::clone_project: case Effect::clone_experiment: - // Do nothing; these are implemented in the temporary_split skill + // Do nothing; these are implemented in play break; case Effect::friendly_fire: cards_gain_skill(cards, strike, 1, false, false); diff --git a/tyrant.cpp b/tyrant.cpp index e6a528c3..7a929b76 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -13,8 +13,7 @@ std::string skill_names[Skill::num_skills] = "Augment", "Backfire", "Chaos", "Cleanse", "Enfeeble", "Freeze", "Heal", "Infuse", "Jam", "Mimic", "Protect", "Rally", "Recharge", "Repair", "Rush", "Shock", - "Siege", "Strike", "Summon", "Supply", - "temporary_split", + "Siege", "Split", "Strike", "Summon", "Supply", "trigger_regen", "Weaken", // Combat-Modifier: diff --git a/tyrant.h b/tyrant.h index feaf9b1f..b6a29108 100644 --- a/tyrant.h +++ b/tyrant.h @@ -25,8 +25,7 @@ enum Skill attack, // Activation (including Destroyed): augment, backfire, chaos, cleanse, enfeeble, freeze, heal, infuse, jam, - mimic, protect, rally, recharge, repair, rush, shock, siege, strike, summon, supply, - temporary_split, // not actually a skill; handles Clone Project/Experiment + mimic, protect, rally, recharge, repair, rush, shock, siege, split, strike, summon, supply, trigger_regen, // not actually a skill; handles regeneration after strike/siege weaken, // Combat-Modifier: diff --git a/xml.cpp b/xml.cpp index c5e5c630..8f097256 100644 --- a/xml.cpp +++ b/xml.cpp @@ -266,8 +266,6 @@ void read_cards(Cards& cards) { c->m_regenerate = atoi(skill->first_attribute("x")->value()); } if(strcmp(skill->first_attribute("id")->value(), "siphon") == 0) { c->m_siphon = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "split") == 0) - { c->m_split = true; } if(strcmp(skill->first_attribute("id")->value(), "stun") == 0) { c->m_stun = true; } if(strcmp(skill->first_attribute("id")->value(), "swipe") == 0) @@ -312,6 +310,8 @@ void read_cards(Cards& cards) { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "siege") == 0) { handle_skill(skill, c); } + if(strcmp(skill->first_attribute("id")->value(), "split") == 0) + { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "strike") == 0) { handle_skill(skill, c); } if(strcmp(skill->first_attribute("id")->value(), "summon") == 0) From 920ffff132bb8c8d1fb8faf72ccc1ca0d54f3d8c Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 6 Apr 2013 19:11:17 +0800 Subject: [PATCH 140/406] Fix bug: Wrongly double Augment Mimicked skills. --- sim.cpp | 2 +- tyrant.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index 1967bcf2..0eb1a05c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -2239,7 +2239,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) { continue; } SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill), SkillMod::on_activate); // _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd->cards, skill).c_str()); - fd->skill_queue.emplace_back(src_status, src_status->m_augmented > 0 ? augmented_skill(src_status, mimic_s) : mimic_s); + fd->skill_queue.emplace_back(src_status, mimic_s); resolve_skill(fd); if(fd->end) { break; } check_regeneration(fd); diff --git a/tyrant.h b/tyrant.h index b6a29108..2072682b 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.10" +#define TYRANT_OPTIMIZER_VERSION "1.0.11" #include #include From cb4f9ef774598119a16b75a3e199eef391cd410e Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 7 Apr 2013 10:21:09 +0800 Subject: [PATCH 141/406] Fix bug: Split skill should not be Mimiced. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 0eb1a05c..9a11a357 100644 --- a/sim.cpp +++ b/sim.cpp @@ -2234,7 +2234,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) { if(src_status->m_card->m_type != CardType::action && src_status->m_hp == 0) { break; } - if(std::get<0>(skill) == mimic || + if(std::get<0>(skill) == mimic || std::get<0>(skill) == split || (std::get<0>(skill) == supply && src_status->m_card->m_type != CardType::assault)) { continue; } SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill), SkillMod::on_activate); From c819242d342160f2dbf7a262e2c748228c96595f Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 7 Apr 2013 16:58:42 +0800 Subject: [PATCH 142/406] Add detailed info for debug mode. --- deck.cpp | 1 + sim.cpp | 312 ++++++++++++++++++++------------------------ sim.h | 11 +- tyrant_optimize.cpp | 30 +++-- 4 files changed, 170 insertions(+), 184 deletions(-) diff --git a/deck.cpp b/deck.cpp index 237fffe8..48a6fe95 100644 --- a/deck.cpp +++ b/deck.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include diff --git a/sim.cpp b/sim.cpp index 9a11a357..8c18564a 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1,6 +1,7 @@ #include "sim.h" #include +#include #include #include #include @@ -20,23 +21,64 @@ std::string to_string(T val) return s.str(); } //---------------------- Debugging stuff --------------------------------------- -bool debug_print(false); +unsigned debug_print(0); +unsigned debug_cached(0); bool debug_line(false); +std::string debug_str; #ifndef NDEBUG -#define _DEBUG_MSG(format, args...) \ +#define _DEBUG_MSG(v, format, args...) \ { \ - if(debug_print) \ + if(__builtin_expect(debug_print >= v, false)) \ { \ if(debug_line) { printf("%i - " format, __LINE__ , ##args); } \ - else { printf(format, ##args); } \ + else if(debug_cached) { \ + char buf[4096]; \ + snprintf(buf, sizeof(buf), format, ##args); \ + debug_str += buf; \ + } \ + else { printf(format, ##args); } \ std::cout << std::flush; \ } \ } +#define _DEBUG_SELECTION(format, args...) \ + { \ + if(__builtin_expect(debug_print >= 2, 0)) \ + { \ + _DEBUG_MSG(2, "Possible targets of " format ":\n", ##args); \ + fd->print_selection_array(); \ + } \ + } #else -#define _DEBUG_MSG(format, args...) +#define _DEBUG_MSG(v, format, args...) +#define _DEBUG_SELECTION(format, args...) #endif //------------------------------------------------------------------------------ - +inline std::string status_description(CardStatus* status) +{ + return status->description(); +} +//------------------------------------------------------------------------------ +template +inline unsigned Field::make_selection_array(CardsIter first, CardsIter last, Functor f) +{ + this->selection_array.clear(); + for(auto c = first; c != last; ++c) + { + if (f(*c)) + { + this->selection_array.push_back(*c); + } + } + return(this->selection_array.size()); +} +inline void Field::print_selection_array() +{ + for(auto c: this->selection_array) + { + _DEBUG_MSG(2, "+ %s\n", status_description(c).c_str()); + } +} +//------------------------------------------------------------------------------ CardStatus::CardStatus(const Card* card) : m_card(card), m_index(0), @@ -239,46 +281,43 @@ std::string CardStatus::description() return(desc); } //------------------------------------------------------------------------------ -inline std::string status_description(CardStatus* status) -{ - return status->description(); -} -//------------------------------------------------------------------------------ inline void print_achievement_results(Field* fd) { +#ifndef NDEBUG if(fd->achievement.req_counter.size() == 0) { return; } - _DEBUG_MSG("Achievement:\n"); + _DEBUG_MSG(1, "Achievement:\n"); for(auto i : fd->achievement.skill_used) { - _DEBUG_MSG(" Use skills: %s %u%s? %s\n", skill_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); + _DEBUG_MSG(1, " Use skills: %s %u%s? %s\n", skill_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } for(auto i : fd->achievement.unit_played) { - _DEBUG_MSG(" Play units: %s %u%s? %s\n", fd->cards.by_id(i.first)->m_name.c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); + _DEBUG_MSG(1, " Play units: %s %u%s? %s\n", fd->cards.by_id(i.first)->m_name.c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } for(auto i : fd->achievement.unit_type_played) { - _DEBUG_MSG(" Play units of type: %s %u%s? %s\n", cardtype_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); + _DEBUG_MSG(1, " Play units of type: %s %u%s? %s\n", cardtype_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } for(auto i : fd->achievement.unit_faction_played) { - _DEBUG_MSG(" Play units of faction: %s %u%s? %s\n", faction_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); + _DEBUG_MSG(1, " Play units of faction: %s %u%s? %s\n", faction_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } for(auto i : fd->achievement.unit_rarity_played) { - _DEBUG_MSG(" Play units of rarity: %s %u%s? %s\n", rarity_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); + _DEBUG_MSG(1, " Play units of rarity: %s %u%s? %s\n", rarity_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } for(auto i : fd->achievement.unit_type_killed) { - _DEBUG_MSG(" Kill units of type: %s %u%s? %s\n", cardtype_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); + _DEBUG_MSG(1, " Kill units of type: %s %u%s? %s\n", cardtype_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } for(auto i : fd->achievement.misc_req) { - _DEBUG_MSG(" %s %u%s? %s\n", achievement_misc_req_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); + _DEBUG_MSG(1, " %s %u%s? %s\n", achievement_misc_req_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); } +#endif } //------------------------------------------------------------------------------ void Hand::reset(std::mt19937& re) @@ -336,7 +375,7 @@ void prepend_on_death(Field* fd) } for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_on_death)) { - _DEBUG_MSG("On death skill pushed in front: %s\n", skill_description(fd->cards, skill).c_str()); + _DEBUG_MSG(2, "On death skill pushed in front: %s\n", skill_description(fd->cards, skill).c_str()); fd->skill_queue.emplace_front(status, skill); } } @@ -374,7 +413,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector assert(fd->skill_queue.size() == 0); for(auto& skill: skills) { -// _DEBUG_MSG("Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); + _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); fd->skill_queue.emplace_back(status, skill); resolve_skill(fd); if(fd->end) { break; } @@ -429,7 +468,7 @@ struct PlayCard fd->inc_counter(fd->achievement.unit_faction_played, card->m_faction); fd->inc_counter(fd->achievement.unit_rarity_played, card->m_rarity); } - _DEBUG_MSG("%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), static_cast(storage->size() - 1), card_description(fd->cards, card).c_str()); + _DEBUG_MSG(1, "%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), static_cast(storage->size() - 1), card_description(fd->cards, card).c_str()); } // all except assault: noop @@ -540,8 +579,8 @@ Results play(Field* fd) { fd->current_phase = Field::playcard_phase; // Initialize stuff, remove dead cards - _DEBUG_MSG("------------------------------------------------------------------------\n"); - _DEBUG_MSG("TURN %u begins for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); + _DEBUG_MSG(1, "------------------------------------------------------------------------\n" + "TURN %u begins for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); // ANP: If it's the player's turn and he's making a decision, // reset his points to 0. if(fd->tapi == 0 && fd->turn <= fd->last_decision_turn) @@ -558,23 +597,12 @@ Results play(Field* fd) if(fd->effect == Effect::clone_project || (fd->effect == Effect::clone_experiment && (fd->turn == 9 || fd->turn == 10))) { - CardStatus* target(nullptr); - unsigned selection_array_size(0); - for(auto c: fd->tap->assaults.m_indirect) - { - if (c->m_delay == 0 && c->m_hp > 0) - { - ++ selection_array_size; - if (fd->rand(0, selection_array_size - 1) == 0) - { - target = c; - } - } - } - if(target != nullptr) + if(fd->make_selection_array(fd->tap->assaults.m_indirect.begin(), fd->tap->assaults.m_indirect.end(), [](CardStatus* c){return(c->m_delay == 0 && c->m_hp > 0);}) > 0) { - _DEBUG_MSG("%s gains skill Split until end of turn.\n", status_description(target).c_str()); - target->m_temporary_split = true; + _DEBUG_SELECTION("Clone effect"); + CardStatus* c(fd->selection_array[fd->rand(0, fd->selection_array.size() - 1)]); + _DEBUG_MSG(1, "%s gains skill Split until end of turn.\n", status_description(c).c_str()); + c->m_temporary_split = true; } } @@ -635,12 +663,13 @@ Results play(Field* fd) CardStatus& current_status(fd->tap->assaults[fd->current_ci]); if(!is_active(¤t_status) || !can_act(¤t_status)) { - //_DEBUG_MSG("! Assault %u (%s) hp: %u, jammed %u\n", card_index, current_status.m_card->m_name.c_str(), current_status.m_hp, current_status.m_jammed); + _DEBUG_MSG(2, "Assault %s cannot take action.\n", status_description(¤t_status).c_str()); current_status.m_step = CardStep::attacked; continue; } // Evaluate skills evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); + // Special case: check for temporary split (clone battlefield effects) if(current_status.m_temporary_split) { @@ -660,7 +689,7 @@ Results play(Field* fd) { break; } - _DEBUG_MSG("TURN %u ends for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); + _DEBUG_MSG(1, "TURN %u ends for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); std::swap(fd->tapi, fd->tipi); std::swap(fd->tap, fd->tip); ++fd->turn; @@ -678,13 +707,13 @@ Results play(Field* fd) // defender wins if(fd->players[0]->commander.m_hp == 0) { - _DEBUG_MSG("Defender wins.\n"); + _DEBUG_MSG(1, "Defender wins.\n"); return {0, 0, 1, 0}; } // achievement: assuming winner='1' if (!made_achievement) { - _DEBUG_MSG("Achievement fails.\n"); + _DEBUG_MSG(1, "Achievement fails.\n"); return {0, 0, 1, 0}; } // attacker wins @@ -696,12 +725,12 @@ Results play(Field* fd) { fd->points_since_last_decision = 10; } - _DEBUG_MSG("Attacker wins.\n"); + _DEBUG_MSG(1, "Attacker wins.\n"); return {1, 0, 0, 10 + (speedy ? 5 : 0) + (fd->gamemode == surge ? 20 : 0) + fd->points_since_last_decision}; } if (fd->turn > turn_limit) { - _DEBUG_MSG("Stall after %u turns.\n", turn_limit); + _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); return {0, 1, 0, 0}; } @@ -861,11 +890,11 @@ inline void count_killed_achievements(Field* fd, const CardStatus* status) void remove_hp(Field* fd, CardStatus& status, unsigned dmg) { assert(status.m_hp > 0); -// _DEBUG_MSG("%s takes %u damage\n", status_description(&status).c_str(), dmg); + _DEBUG_MSG(2, "%s takes %u damage\n", status_description(&status).c_str(), dmg); status.m_hp = safe_minus(status.m_hp, dmg); if(status.m_hp == 0) { - _DEBUG_MSG("%s dies\n", status_description(&status).c_str()); + _DEBUG_MSG(1, "%s dies\n", status_description(&status).c_str()); if(status.m_card->m_skills_on_death.size() > 0) { fd->killed_with_on_death.push_back(&status); @@ -886,7 +915,7 @@ inline bool is_it_dead(CardStatus& c) { if(c.m_card->m_type != CardType::action) { - _DEBUG_MSG("Dead: %s\n", status_description(&c).c_str()); + _DEBUG_MSG(1, "Dead and removed: %s\n", status_description(&c).c_str()); } return(true); } @@ -914,7 +943,7 @@ void check_regeneration(Field* fd) if(fd->flip() && skill_check(fd, status, nullptr)) { count_achievement(fd, status); - _DEBUG_MSG("%s regenerates with %u health\n", status_description(status).c_str(), status->m_card->m_health); + _DEBUG_MSG(1, "%s regenerates with %u health\n", status_description(status).c_str(), status->m_card->m_health); add_hp(fd, status, status->m_card->m_regenerate); } else @@ -946,12 +975,12 @@ void turn_start_phase(Field* fd) status.m_protected = 0; if(status.m_poisoned > 0) { - _DEBUG_MSG("%s takes poison damage\n", status_description(&status).c_str()); + _DEBUG_MSG(1, "%s takes poison damage\n", status_description(&status).c_str()); remove_hp(fd, status, status.m_poisoned); } if(status.m_delay > 0 && !status.m_frozen) { - _DEBUG_MSG("%s reduces its timer\n", status_description(&status).c_str()); + _DEBUG_MSG(1, "%s reduces its timer\n", status_description(&status).c_str()); --status.m_delay; } if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } @@ -970,7 +999,7 @@ void turn_start_phase(Field* fd) status.m_index = index; if(status.m_delay > 0) { - _DEBUG_MSG("%s reduces its timer\n", status_description(&status).c_str()); + _DEBUG_MSG(1, "%s reduces its timer\n", status_description(&status).c_str()); --status.m_delay; } if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } @@ -1058,7 +1087,7 @@ void evaluate_legion(Field* fd) } count_achievement(fd, status); unsigned legion_value = status->m_card->m_legion * legion_size; - _DEBUG_MSG("%s activates Legion %u, %s%s%s by %u\n", status_description(status).c_str(), status->m_card->m_legion, + _DEBUG_MSG(1, "%s activates Legion %u, %s%s%s by %u\n", status_description(status).c_str(), status->m_card->m_legion, do_heal ? "healed" : "", do_heal && do_rally ? " and " : "", do_rally ? "rallied" : "", legion_value); if(do_heal) { @@ -1102,7 +1131,7 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg, bool count { assert(status.m_hp > 0); assert(status.m_card->m_type == CardType::commander); - _DEBUG_MSG("%s takes %u damage\n", status_description(&status).c_str(), dmg); + _DEBUG_MSG(2, "%s takes %u damage\n", status_description(&status).c_str(), dmg); status.m_hp = safe_minus(status.m_hp, dmg); // ANP: If commander is enemy's, player gets points equal to damage. // Points are awarded for overkill, so it is correct to simply add dmg. @@ -1113,7 +1142,7 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg, bool count } if(status.m_hp == 0) { - _DEBUG_MSG("%s dies\n", status_description(&status).c_str()); + _DEBUG_MSG(1, "%s dies\n", status_description(&status).c_str()); fd->end = true; } } @@ -1151,7 +1180,7 @@ struct PerformAttack if(def_status->m_card->m_flying && (fd->effect == Effect::high_skies || fd->flip()) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); - _DEBUG_MSG("%s attacks %s but it dodges with Flying\n", status_description(att_status).c_str(), status_description(def_status).c_str()); + _DEBUG_MSG(1, "%s attacks %s but it dodges with Flying\n", status_description(att_status).c_str(), status_description(def_status).c_str()); return; } @@ -1165,7 +1194,7 @@ struct PerformAttack // but still activate Counter! if(att_dmg > 0 && fd->effect == Effect::impenetrable && def_status->m_card->m_wall) { - _DEBUG_MSG("%s is impenetrable\n", status_description(def_status).c_str()); + _DEBUG_MSG(1, "%s is impenetrable\n", status_description(def_status).c_str()); att_dmg = 0; } if(att_dmg > 0) @@ -1189,7 +1218,7 @@ struct PerformAttack { count_achievement(fd, def_status); // perform_skill_stun - _DEBUG_MSG("%s stuns %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); + _DEBUG_MSG(1, "%s stuns %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); att_status->m_stunned = 2; } if(def_status->m_card->m_counter > 0 && skill_check(fd, def_status, att_status)) @@ -1197,7 +1226,7 @@ struct PerformAttack count_achievement(fd, def_status); // perform_skill_counter unsigned counter_dmg(counter_damage(att_status, def_status)); - _DEBUG_MSG("%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); + _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, *att_status, counter_dmg); } if(att_status->m_card->m_berserk > 0 && skill_check(fd, att_status, nullptr)) @@ -1277,7 +1306,7 @@ struct PerformAttack { if(!reduced_desc.empty()) { desc += "-[" + reduced_desc + "]"; } if(!desc.empty()) { desc += "=" + to_string(att_dmg); } - _DEBUG_MSG("%s attacks %s for %u%s damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), pre_modifier_dmg, desc.c_str()); + _DEBUG_MSG(1, "%s attacks %s for %u%s damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), pre_modifier_dmg, desc.c_str()); } } @@ -1304,14 +1333,14 @@ struct PerformAttack { count_achievement(fd, def_status); unsigned v = def_status->m_card->m_poison_oa; - _DEBUG_MSG("%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); + _DEBUG_MSG(1, "%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); att_status->m_poisoned = v; } if(def_status->m_card->m_disease_oa && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); // perform_skill_disease - _DEBUG_MSG("%s (on attacked) diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); + _DEBUG_MSG(1, "%s (on attacked) diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); att_status->m_diseased = true; } if(def_status->m_hp > 0 && def_status->m_card->m_berserk_oa > 0 && skill_check(fd, def_status, nullptr)) @@ -1332,7 +1361,7 @@ void PerformAttack::immobilize() if(att_status->m_card->m_immobilize && fd->flip() && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); - _DEBUG_MSG("%s immobilizes %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); + _DEBUG_MSG(1, "%s immobilizes %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_immobilized = true; } } @@ -1351,7 +1380,7 @@ void PerformAttack::siphon_poison_disease() count_achievement(fd, att_status); // perform_skill_siphon unsigned v = std::min(att_dmg, att_status->m_card->m_siphon); - _DEBUG_MSG("%s siphons %u health for %s\n", status_description(att_status).c_str(), v, status_description(&fd->tap->commander).c_str()); + _DEBUG_MSG(1, "%s siphons %u health for %s\n", status_description(att_status).c_str(), v, status_description(&fd->tap->commander).c_str()); add_hp(fd, &fd->tap->commander, v); } if(att_status->m_card->m_poison > def_status->m_poisoned && skill_check(fd, att_status, def_status)) @@ -1359,14 +1388,14 @@ void PerformAttack::siphon_poison_disease() count_achievement(fd, att_status); // perform_skill_poison unsigned v = att_status->m_card->m_poison; - _DEBUG_MSG("%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); + _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); def_status->m_poisoned = v; } if(att_status->m_card->m_disease && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_disease - _DEBUG_MSG("%s diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); + _DEBUG_MSG(1, "%s diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); def_status->m_diseased = true; } } @@ -1390,19 +1419,19 @@ void PerformAttack::crush_leech() CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall if (def_status != nullptr) { - _DEBUG_MSG("%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), att_status->m_card->m_crush); + _DEBUG_MSG(1, "%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), att_status->m_card->m_crush); remove_hp(fd, *def_status, att_status->m_card->m_crush); } else { - _DEBUG_MSG("%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(&fd->tip->commander).c_str(), att_status->m_card->m_crush); + _DEBUG_MSG(1, "%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(&fd->tip->commander).c_str(), att_status->m_card->m_crush); remove_commander_hp(fd, fd->tip->commander, att_status->m_card->m_crush, true); } } if(att_status->m_card->m_leech > 0 && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); - _DEBUG_MSG("%s leeches %u health\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech)); + _DEBUG_MSG(1, "%s leeches %u health\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech)); add_hp(fd, att_status, std::min(att_dmg, att_status->m_card->m_leech)); } } @@ -1429,7 +1458,7 @@ void attack_phase(Field* fd) if(att_status->m_card->m_flurry > 0 && fd->flip() && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); - _DEBUG_MSG("%s activates Flurry\n", status_description(att_status).c_str()); + _DEBUG_MSG(1, "%s activates Flurry\n", status_description(att_status).c_str()); num_attacks += att_status->m_card->m_flurry; } for(unsigned attack_index(0); attack_index < num_attacks && can_attack(att_status) && fd->tip->commander.m_hp > 0; ++attack_index) @@ -1452,7 +1481,7 @@ void attack_phase(Field* fd) else { // perform_skill_swipe - _DEBUG_MSG("%s activates Swipe\n", status_description(att_status).c_str()); + _DEBUG_MSG(1, "%s activates Swipe\n", status_description(att_status).c_str()); // attack the card on the left if(fd->current_ci > 0 && alive_assault(def_assaults, fd->current_ci - 1)) { @@ -1729,7 +1758,6 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { -// _DEBUG_MSG(" hp %u -> %u.\n", c->m_hp, safe_minus(c->m_hp, v)); remove_hp(fd, *c, v); } @@ -1754,30 +1782,14 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) { - unsigned array_head{0}; if(std::get<2>(s) == allfactions) { - for(auto card: cards) - { - if(skill_predicate(fd, card, s)) - { - fd->selection_array[array_head] = card; - ++array_head; - } - } + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, s](CardStatus* c){return(skill_predicate(fd, c, s));})); } else { - for(auto card: cards) - { - if(card->m_faction == std::get<2>(s) && skill_predicate(fd, card, s)) - { - fd->selection_array[array_head] = card; - ++array_head; - } - } + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, s](CardStatus* c){return(c->m_faction == std::get<2>(s) && skill_predicate(fd, c, s));})); } - return(array_head); } template<> @@ -1785,42 +1797,15 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std { // mimiced supply by a structure, etc ? if(!(src_status->m_card->m_type == CardType::assault)) { return(0); } - unsigned array_head{0}; const unsigned min_index(src_status->m_index - (src_status->m_index == 0 ? 0 : 1)); const unsigned max_index(src_status->m_index + (src_status->m_index == cards.size() - 1 ? 0 : 1)); - for(unsigned card_index(min_index); card_index <= max_index; ++card_index) - { - if(skill_predicate(fd, cards[card_index], s)) - { - fd->selection_array[array_head] = cards[card_index]; - ++array_head; - } - } - return(array_head); + return(fd->make_selection_array(cards.begin() + min_index, cards.begin() + max_index + 1, [fd, s](CardStatus* c){return(skill_predicate(fd, c, s));})); } inline unsigned select_infuse(Field* fd, const SkillSpec& s) { - unsigned array_head{0}; - // Select candidates among attacker's assaults - for(auto card_status: fd->tap->assaults.m_indirect) - { - if(skill_predicate(fd, card_status, s)) - { - fd->selection_array[array_head] = card_status; - ++array_head; - } - } - // Select candidates among defender's assaults - for(auto card_status: fd->tip->assaults.m_indirect) - { - if(skill_predicate(fd, card_status, s)) - { - fd->selection_array[array_head] = card_status; - ++array_head; - } - } - return(array_head); + const auto &cards = boost::join(fd->tap->assaults.m_indirect, fd->tip->assaults.m_indirect); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, s](CardStatus* c){return(skill_predicate(fd, c, s));})); } inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src_status) @@ -1914,9 +1899,9 @@ void maybeTriggerRegen(Field* fd) fd->skill_queue.emplace_front(nullptr, std::make_tuple(trigger_regen, 0, allfactions, false, SkillMod::on_activate)); } -unsigned get_target_hostile_index(Field* fd, CardStatus* src_status, unsigned selection_array_size) +unsigned get_target_hostile_index(Field* fd, CardStatus* src_status) { - unsigned rand_index(fd->rand(0, selection_array_size - 1)); + unsigned rand_index(fd->rand(0, fd->selection_array.size() - 1)); // intercept if(!src_status->m_chaosed) { @@ -1927,17 +1912,17 @@ unsigned get_target_hostile_index(Field* fd, CardStatus* src_status, unsigned se if(left_status->m_card->m_intercept && left_status->m_index == status->m_index - 1 && skill_check(fd, left_status, status)) { count_achievement(fd, left_status); - _DEBUG_MSG("%s intercepts for %s\n", status_description(left_status).c_str(), status_description(status).c_str()); + _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(left_status).c_str(), status_description(status).c_str()); return(rand_index - 1); } } - if(rand_index + 1 < selection_array_size) + if(rand_index + 1 < fd->selection_array.size()) { CardStatus* right_status(fd->selection_array[rand_index + 1]); if(right_status->m_card->m_intercept && right_status->m_index == status->m_index + 1 && skill_check(fd, right_status, status)) { count_achievement(fd, right_status); - _DEBUG_MSG("%s intercepts for %s\n", status_description(right_status).c_str(), status_description(status).c_str()); + _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(right_status).c_str(), status_description(status).c_str()); return(rand_index + 1); } } @@ -1953,14 +1938,14 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ if(is_evadable && dst_status->m_card->m_evade && fd->flip() && skill_check(fd, dst_status, src_status)) { count_achievement(fd, dst_status); - _DEBUG_MSG("%s %s (%u) on %s but it evades\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); + _DEBUG_MSG(1, "%s %s (%u) on %s but it evades\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); return(false); } if(is_count_achievement) { count_achievement(fd, src_status); } - _DEBUG_MSG("%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); + _DEBUG_MSG(1, "%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); perform_skill(fd, dst_status, std::get<1>(s)); return(true); } @@ -1972,7 +1957,7 @@ bool check_and_perform_blitz(Field* fd, CardStatus* src_status) if(skill_check(fd, src_status, nullptr)) { count_achievement(fd, src_status); - _DEBUG_MSG("%s activates Blitz opposing %s\n", status_description(src_status).c_str(), status_description(&fd->tip->assaults[src_status->m_index]).c_str()); + _DEBUG_MSG(1, "%s activates Blitz opposing %s\n", status_description(src_status).c_str(), status_description(&fd->tip->assaults[src_status->m_index]).c_str()); src_status->m_blitzing = true; return(true); } @@ -1984,7 +1969,7 @@ bool check_and_perform_recharge(Field* fd, CardStatus* src_status) if(fd->flip() && skill_check(fd, src_status, nullptr)) { count_achievement(fd, src_status); - _DEBUG_MSG("%s activates Recharge\n", status_description(src_status).c_str()); + _DEBUG_MSG(1, "%s activates Recharge\n", status_description(src_status).c_str()); fd->tap->deck->place_at_bottom(src_status->m_card); return(true); } @@ -1996,7 +1981,7 @@ bool check_and_perform_refresh(Field* fd, CardStatus* src_status) if(skill_check(fd, src_status, nullptr)) { count_achievement(fd, src_status); - _DEBUG_MSG("%s refreshes, hp -> %u\n", status_description(src_status).c_str(), src_status->m_card->m_health); + _DEBUG_MSG(1, "%s refreshes, hp -> %u\n", status_description(src_status).c_str(), src_status->m_card->m_health); add_hp(fd, src_status, src_status->m_card->m_health); return(true); } @@ -2007,15 +1992,16 @@ template void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { std::vector& cards(skill_targets(fd, src_status)); - unsigned selection_array_size{select_fast(fd, src_status, cards, s)}, index_start, index_end; - if(selection_array_size == 0) + if(select_fast(fd, src_status, cards, s) == 0) { return; } + _DEBUG_SELECTION("%s", skill_names[skill_id].c_str()); + unsigned index_start, index_end; if(std::get<3>(s)) // target all { index_start = 0; - index_end = selection_array_size - 1; + index_end = fd->selection_array.size() - 1; } else { @@ -2023,7 +2009,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski { return; } - index_start = index_end = get_target_hostile_index(fd, src_status, selection_array_size); + index_start = index_end = get_target_hostile_index(fd, src_status); } bool is_count_achievement(true); for(unsigned s_index(index_start); s_index <= index_end; ++s_index) @@ -2041,7 +2027,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski if(c->m_card->m_payback && skill_predicate(fd, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) { count_achievement(fd, c); - _DEBUG_MSG("%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); + _DEBUG_MSG(1, "%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); perform_skill(fd, src_status, std::get<1>(s)); } } @@ -2054,16 +2040,20 @@ template void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { std::vector& cards(skill_targets(fd, src_status)); - unsigned selection_array_size{select_fast(fd, src_status, cards, s)}, index_start, index_end; - if(selection_array_size == 0) { return; } + if(select_fast(fd, src_status, cards, s) == 0) + { + return; + } + _DEBUG_SELECTION("%s", skill_names[skill_id].c_str()); + unsigned index_start, index_end; if(std::get<3>(s) || skill_id == supply) // target all or supply { index_start = 0; - index_end = selection_array_size - 1; + index_end = fd->selection_array.size() - 1; } else { - index_start = index_end = fd->rand(0, selection_array_size - 1); + index_start = index_end = fd->rand(0, fd->selection_array.size() - 1); } bool is_count_achievement(true); for(unsigned s_index(index_start); s_index <= index_end; ++s_index) @@ -2082,7 +2072,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil if(c->m_card->m_tribute && skill_predicate(fd, src_status, s) && fd->flip() && skill_check(fd, c, src_status)) { count_achievement(fd, c); - _DEBUG_MSG("Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); + _DEBUG_MSG(1, "Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); perform_skill(fd, src_status, std::get<1>(s)); } // Emulate @@ -2093,7 +2083,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator, s) && skill_check(fd, &emulator, nullptr)) { count_achievement(fd, &emulator); - _DEBUG_MSG("Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); + _DEBUG_MSG(1, "Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); perform_skill(fd, &emulator, std::get<1>(s)); } } @@ -2108,28 +2098,10 @@ void perform_backfire(Field* fd, CardStatus* src_status, const SkillSpec& s) void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) { - unsigned array_head{0}; - // Select candidates among attacker's assaults - for(auto card_status: fd->players[src_status->m_player]->assaults.m_indirect) - { - if(skill_predicate(fd, card_status, s)) - { - fd->selection_array[array_head] = card_status; - ++array_head; - } - } - // Select candidates among defender's assaults - for(auto card_status: fd->players[opponent(src_status->m_player)]->assaults.m_indirect) - { - if(skill_predicate(fd, card_status, s)) - { - fd->selection_array[array_head] = card_status; - ++array_head; - } - } - if(array_head > 0) + if(select_infuse(fd, s) > 0) { - CardStatus* c(fd->selection_array[fd->rand(0, array_head - 1)]); + _DEBUG_SELECTION("%s", skill_names[infuse].c_str()); + CardStatus* c(fd->selection_array[fd->rand(0, fd->selection_array.size() - 1)]); check_and_perform_skill(fd, src_status, c, s, true, true); } } @@ -2172,7 +2144,7 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) card_status.m_index = storage->size() - 1; card_status.m_player = player; card_status.m_is_summoned = true; - _DEBUG_MSG("%s %s %s %u [%s]\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); + _DEBUG_MSG(1, "%s %s %s %u [%s]\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); prepend_skills(fd, &card_status); if(card_status.m_card->m_blitz) { @@ -2213,23 +2185,23 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // so we can probably clear it safely. This is necessary, because mimic calls resolve_skill as well (infinite loop). fd->skill_queue.clear(); std::vector& cards(skill_targets(fd, src_status)); - unsigned selection_array_size{select_fast(fd, src_status, cards, s)}; - if(selection_array_size == 0) + if(select_fast(fd, src_status, cards, s) == 0) { return; } - CardStatus* c(fd->selection_array[fd->effect == Effect::copycat ? fd->rand(0, selection_array_size - 1) : get_target_hostile_index(fd, src_status, selection_array_size)]); + _DEBUG_SELECTION("%s", skill_names[mimic].c_str()); + CardStatus* c(fd->selection_array[fd->effect == Effect::copycat ? fd->rand(0, fd->selection_array.size() - 1) : get_target_hostile_index(fd, src_status)]); // evade check for mimic // individual skills are subject to evade checks too, // but resolve_skill will handle those. if(c->m_card->m_evade && fd->flip() && skill_check(fd, c, src_status)) { count_achievement(fd, c); - _DEBUG_MSG("%s %s on %s but it evades\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); + _DEBUG_MSG(1, "%s %s on %s but it evades\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); return; } count_achievement(fd, src_status); - _DEBUG_MSG("%s %s on %s\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); + _DEBUG_MSG(1, "%s %s on %s\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); for(auto skill: c->m_card->m_skills) { if(src_status->m_card->m_type != CardType::action && src_status->m_hp == 0) @@ -2238,7 +2210,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) (std::get<0>(skill) == supply && src_status->m_card->m_type != CardType::assault)) { continue; } SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill), SkillMod::on_activate); -// _DEBUG_MSG("Evaluating mimiced %s skill %s\n", status_description(c).c_str(), skill_description(fd->cards, skill).c_str()); + _DEBUG_MSG(2, "Evaluating %s mimiced skill %s\n", status_description(c).c_str(), skill_description(fd->cards, skill).c_str()); fd->skill_queue.emplace_back(src_status, mimic_s); resolve_skill(fd); if(fd->end) { break; } diff --git a/sim.h b/sim.h index c1f5f08d..c260e02b 100644 --- a/sim.h +++ b/sim.h @@ -2,6 +2,7 @@ #define SIM_H_INCLUDED #include +#include #include #include #include @@ -15,8 +16,10 @@ class Deck; class Field; class Achievement; -extern bool debug_print; +extern unsigned debug_print; +extern unsigned debug_cached; extern bool debug_line; +extern std::string debug_str; extern unsigned turn_limit; //---------------------- Represent Simulation Results ---------------------------- @@ -183,7 +186,7 @@ class Field unsigned tipi; // and inactive Hand* tap; Hand* tip; - std::array selection_array; + std::vector selection_array; unsigned turn; gamemode_t gamemode; const enum Effect effect; @@ -242,6 +245,10 @@ class Field return(v[this->rand(0, v.size() - 1)]); } + template + inline unsigned make_selection_array(CardsIter first, CardsIter last, Functor f); + inline void print_selection_array(); + template inline void set_counter(T& container, unsigned key, unsigned value) { diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index b409aee1..45869638 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1081,8 +1081,7 @@ int main(int argc, char** argv) std::cout << "Tyrant Optimizer " << TYRANT_OPTIMIZER_VERSION << std::endl; return(0); } - debug_print = getenv("DEBUG_PRINT"); - unsigned num_threads = (debug_print || getenv("DEBUG")) ? 1 : 4; + unsigned num_threads = 4; DeckStrategy::DeckStrategy att_strategy(DeckStrategy::random); DeckStrategy::DeckStrategy def_strategy(DeckStrategy::random); Cards cards; @@ -1197,6 +1196,10 @@ int main(int argc, char** argv) turn_limit = atoi(argv[argIndex+1]); argIndex += 1; } + else if(strcmp(argv[argIndex], "+v") == 0) + { + ++ debug_print; + } else if(strcmp(argv[argIndex], "-wintie") == 0) { win_tie = true; @@ -1216,20 +1219,12 @@ int main(int argc, char** argv) todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); argIndex += 1; } - else if(strcmp(argv[argIndex], "verbose") == 0) - { - debug_print = true; - } else if(strcmp(argv[argIndex], "debug") == 0) { - debug_print = true; - num_threads = 1; todo.push_back(std::make_tuple(0u, 45u, fightuntil)); } else if(strcmp(argv[argIndex], "debuguntil") == 0) { - debug_print = true; - num_threads = 1; // fight until the last battle results in range [min_score, max_score]. // E.g., 0 0: until lose; 1 1: until win (non-ANP); 10 25: until win (ANP). todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), fightuntil)); @@ -1356,13 +1351,24 @@ int main(int argc, char** argv) break; } case fightuntil: { + unsigned saved_num_threads = num_threads; + num_threads = 1; + ++ debug_print; + ++ debug_cached; while(1) { + debug_str.clear(); auto results = p.evaluate(1); - print_score_info(results, p.factors); double score = compute_score(results, p.factors); - if(score >= std::get<0>(op) && score <= std::get<1>(op)) { break; } + if(score >= std::get<0>(op) && score <= std::get<1>(op)) { + std::cout << debug_str << std::flush; + print_results(results, p.factors); + break; + } } + -- debug_cached; + -- debug_print; + num_threads = saved_num_threads; break; } } From fa0ca42c2fd94f53f42c4686e4431e2ec7db2db9 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 7 Apr 2013 17:52:36 +0800 Subject: [PATCH 143/406] If an assault is jammed when activating its own skills (Strike + Jam on Death), it should stop counting Flurry towards achievements. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 8c18564a..606af3ee 100644 --- a/sim.cpp +++ b/sim.cpp @@ -678,7 +678,7 @@ Results play(Field* fd) evaluate_skills(fd, ¤t_status, skills); } // Attack - if(!fd->end && !current_status.m_immobilized && !current_status.m_stunned && current_status.m_hp > 0) + if(!fd->end && can_attack(¤t_status)) { current_status.m_step = CardStep::attacking; attack_phase(fd); From 3a5b03d6c9e828548a06e1dae19e5cdabb4eadc6 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 8 Apr 2013 11:06:10 +0800 Subject: [PATCH 144/406] Fix display of summon skill. A battle should end as soon as any commander dies. --- sim.cpp | 56 +++++++++++++++++++++++++++----------------------------- sim.h | 2 +- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/sim.cpp b/sim.cpp index 606af3ee..ae4976e8 100644 --- a/sim.cpp +++ b/sim.cpp @@ -155,7 +155,17 @@ std::string skill_description(const Cards& cards, const SkillSpec& s) switch(std::get<0>(s)) { case summon: - return(skill_names[std::get<0>(s)] + " " + cards.by_id(std::get<1>(s))->m_name.c_str()); + if(std::get<1>(s) == 0) + { + // Summon X + return(skill_names[std::get<0>(s)] + " X"); + } + else + { + return(skill_names[std::get<0>(s)] + + " " + cards.by_id(std::get<1>(s))->m_name.c_str() + + skill_activation_modifier_names[std::get<4>(s)]); + } default: return(skill_names[std::get<0>(s)] + (std::get<3>(s) ? " all" : "") + @@ -416,7 +426,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); fd->skill_queue.emplace_back(status, skill); resolve_skill(fd); - if(fd->end) { break; } + if(__builtin_expect(fd->end, false)) { break; } } } bool check_and_perform_blitz(Field* fd, CardStatus* src_status); @@ -575,7 +585,7 @@ Results play(Field* fd) fd->inc_counter(fd->achievement.unit_played, fd->players[0]->commander.m_card->m_id); fd->set_counter(fd->achievement.misc_req, AchievementMiscReq::turns, 1); - while(fd->turn <= turn_limit && !fd->end) + while(__builtin_expect(fd->turn <= turn_limit && !fd->end, true)) { fd->current_phase = Field::playcard_phase; // Initialize stuff, remove dead cards @@ -628,6 +638,7 @@ Results play(Field* fd) break; } } + if(__builtin_expect(fd->end, false)) { break; } // Evaluate Legion skill fd->current_phase = Field::legion_phase; @@ -636,14 +647,7 @@ Results play(Field* fd) // Evaluate commander fd->current_phase = Field::commander_phase; evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); - - if (fd->effect == Effect::genesis) - { - unsigned index(fd->rand(0, fd->cards.player_assaults.size() - 1)); - std::vector skills; - skills.emplace_back(summon, fd->cards.player_assaults[index]->m_id, allfactions, false, SkillMod::on_activate); - evaluate_skills(fd, &fd->tap->commander, skills); - } + if(__builtin_expect(fd->end, false)) { break; } // Evaluate structures fd->current_phase = Field::structures_phase; @@ -669,6 +673,7 @@ Results play(Field* fd) } // Evaluate skills evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); + if(__builtin_expect(fd->end, false)) { break; } // Special case: check for temporary split (clone battlefield effects) if(current_status.m_temporary_split) @@ -677,18 +682,17 @@ Results play(Field* fd) skills.emplace_back(split, 0, allfactions, false, SkillMod::on_activate); evaluate_skills(fd, ¤t_status, skills); } + if(__builtin_expect(fd->end, false)) { break; } + // Attack - if(!fd->end && can_attack(¤t_status)) + if(can_attack(¤t_status)) { current_status.m_step = CardStep::attacking; attack_phase(fd); } current_status.m_step = CardStep::attacked; } - if(fd->end) - { - break; - } + if(__builtin_expect(fd->end, false)) { break; } _DEBUG_MSG(1, "TURN %u ends for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); std::swap(fd->tapi, fd->tipi); std::swap(fd->tap, fd->tip); @@ -1201,11 +1205,7 @@ struct PerformAttack { immobilize(); attack_damage(); - if(fd->end) - { - // Commander dies? - return; - } + if(__builtin_expect(fd->end, false)) { return; } siphon_poison_disease(); on_kill(); } @@ -1487,8 +1487,7 @@ void attack_phase(Field* fd) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(); } - if(fd->end) - { return; } + if(__builtin_expect(fd->end, false)) { return; } // stille alive? attack the card in front if(can_attack(att_status) && alive_assault(def_assaults, fd->current_ci)) { @@ -1498,10 +1497,9 @@ void attack_phase(Field* fd) { attack_commander(fd, att_status); } - if(fd->end) - { return; } + if(__builtin_expect(fd->end, false)) { return; } // still alive? attack the card on the right - if(!fd->end && can_attack(att_status) && alive_assault(def_assaults, fd->current_ci + 1)) + if(can_attack(att_status) && alive_assault(def_assaults, fd->current_ci + 1)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(); } @@ -2132,7 +2130,7 @@ template void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) { unsigned player = src_status->m_player; - const Card* summoned = fd->cards.by_id(std::get<1>(s)); + const Card* summoned = std::get<1>(s) != 0 ? fd->cards.by_id(std::get<1>(s)) : fd->random_in_vector(fd->cards.player_assaults); assert(summoned->m_type == CardType::assault || summoned->m_type == CardType::structure); Hand* hand{fd->players[player]}; if(hand->assaults.size() + hand->structures.size() < 100) @@ -2213,7 +2211,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) _DEBUG_MSG(2, "Evaluating %s mimiced skill %s\n", status_description(c).c_str(), skill_description(fd->cards, skill).c_str()); fd->skill_queue.emplace_back(src_status, mimic_s); resolve_skill(fd); - if(fd->end) { break; } + if(__builtin_expect(fd->end, false)) { break; } check_regeneration(fd); } } @@ -2330,7 +2328,7 @@ void modify_cards(Cards& cards, enum Effect effect) cards_gain_skill(cards, chaos, 0, true, true); break; case Effect::genesis: - // Do nothing; this is implemented in play + cards_gain_skill(cards, summon, 0, false, false); // No replace exising Summon, if any break; case Effect::decrepit: cards_gain_skill(cards, enfeeble, 1, true, true); diff --git a/sim.h b/sim.h index c260e02b..8cf5449c 100644 --- a/sim.h +++ b/sim.h @@ -239,7 +239,7 @@ class Field } template - inline T& random_in_vector(std::vector& v) + inline T random_in_vector(const std::vector& v) { assert(v.size() > 0); return(v[this->rand(0, v.size() - 1)]); From de4d29fdf5b60f09ca5de30a4cb2274ca4d833f6 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 11 Apr 2013 13:13:23 +0800 Subject: [PATCH 145/406] Simulate bizarre in-game behaviors for Weaken/Jam/Chaos/Rally/Augment skills. --- sim.cpp | 74 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/sim.cpp b/sim.cpp index ae4976e8..3340c9c2 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1539,13 +1539,17 @@ struct if_ }; template -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { assert(false); return(false); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { - if(can_act(c) && (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))) + const auto& mod = std::get<4>(s); + if(can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))) + (src->m_player != c->m_player || mod == SkillMod::on_death ? (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)) : + mod == SkillMod::on_attacked ? is_active_next_turn(c) : + is_active(c) && !is_attacking_or_has_attacked(c))) { for(auto& s: c->m_card->m_skills) { @@ -1557,17 +1561,17 @@ inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { const auto& mod = std::get<4>(s); - return(!c->m_chaosed && can_act(c) && + return(!c->m_chaosed && can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : - mod == SkillMod::on_death ? is_active(c) && !has_attacked(c) : + mod == SkillMod::on_death ? (fd->tapi != src->m_player ? is_active(c) && !has_attacked(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0 && ( c->m_chaosed || @@ -1582,72 +1586,76 @@ inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(can_be_healed(c)); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(c->m_faction != bloodthirsty); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { const auto& mod = std::get<4>(s); - return(can_act(c) && + return(can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : - mod == SkillMod::on_death ? is_active(c) && !has_attacked(c) : + mod == SkillMod::on_death ? (fd->tapi != src->m_player ? is_active(c) && !has_attacked(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { - return(can_attack(c) && (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); + const auto& mod = std::get<4>(s); + return(can_attack(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); + (src->m_player != c->m_player || mod == SkillMod::on_death ? (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)) : + mod == SkillMod::on_attacked ? is_active_next_turn(c) : + is_active(c) && !is_attacking_or_has_attacked(c))); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(can_be_healed(c)); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(c->m_delay > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(can_be_healed(c)); } template<> -inline bool skill_predicate(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { const auto& mod = std::get<4>(s); - return(can_attack(c) && attack_power(c) > 0 && + return(can_attack(c) && attack_power(c) > 0 && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : - mod == SkillMod::on_death ? is_active(c) && !has_attacked(c) : + mod == SkillMod::on_death ? (fd->tapi != src->m_player ? is_active(c) && !has_attacked(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } @@ -1782,11 +1790,11 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector { if(std::get<2>(s) == allfactions) { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, s](CardStatus* c){return(skill_predicate(fd, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); } else { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, s](CardStatus* c){return(c->m_faction == std::get<2>(s) && skill_predicate(fd, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return(c->m_faction == std::get<2>(s) && skill_predicate(fd, src_status, c, s));})); } } @@ -1797,13 +1805,13 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std if(!(src_status->m_card->m_type == CardType::assault)) { return(0); } const unsigned min_index(src_status->m_index - (src_status->m_index == 0 ? 0 : 1)); const unsigned max_index(src_status->m_index + (src_status->m_index == cards.size() - 1 ? 0 : 1)); - return(fd->make_selection_array(cards.begin() + min_index, cards.begin() + max_index + 1, [fd, s](CardStatus* c){return(skill_predicate(fd, c, s));})); + return(fd->make_selection_array(cards.begin() + min_index, cards.begin() + max_index + 1, [fd, src_status, s](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); } inline unsigned select_infuse(Field* fd, const SkillSpec& s) { const auto &cards = boost::join(fd->tap->assaults.m_indirect, fd->tip->assaults.m_indirect); - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, s](CardStatus* c){return(skill_predicate(fd, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, s](CardStatus* c){return(skill_predicate(fd, &fd->tap->commander, c, s));})); } inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src_status) @@ -2022,7 +2030,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski // Count at most once even targeting "All" is_count_achievement = false; // Payback - if(c->m_card->m_payback && skill_predicate(fd, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) + if(c->m_card->m_payback && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) { count_achievement(fd, c); _DEBUG_MSG(1, "%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); @@ -2067,7 +2075,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil // Count at most once even targeting "All" is_count_achievement = false; // Tribute - if(c->m_card->m_tribute && skill_predicate(fd, src_status, s) && fd->flip() && skill_check(fd, c, src_status)) + if(c->m_card->m_tribute && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status)) { count_achievement(fd, c); _DEBUG_MSG(1, "Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); @@ -2078,7 +2086,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil if(opp->assaults.size() > c->m_index) { CardStatus& emulator = opp->assaults[c->m_index]; - if(emulator.m_card->m_emulate && skill_predicate(fd, &emulator, s) && skill_check(fd, &emulator, nullptr)) + if(emulator.m_card->m_emulate && skill_predicate(fd, src_status, &emulator, s) && skill_check(fd, &emulator, nullptr)) { count_achievement(fd, &emulator); _DEBUG_MSG(1, "Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); From 3bcda00f7ffc8f5cfaa2fb9540092c2eae01a40b Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 13 Apr 2013 22:57:57 +0800 Subject: [PATCH 146/406] Fix bug: Infuse should be Interceptable when targeting an enemy assault. --- sim.cpp | 73 ++++++++++++++++++++++++++------------------------------ tyrant.h | 2 +- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/sim.cpp b/sim.cpp index 3340c9c2..f28a52c5 100644 --- a/sim.cpp +++ b/sim.cpp @@ -381,11 +381,12 @@ void prepend_on_death(Field* fd) { if(status->m_jammed) { + _DEBUG_MSG(2, "%s is jammed and cannot activate its on Death skill.\n", status_description(status).c_str()); continue; } for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_on_death)) { - _DEBUG_MSG(2, "On death skill pushed in front: %s\n", skill_description(fd->cards, skill).c_str()); + _DEBUG_MSG(2, "%s pushes its on Death skill in front of the queue: %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); fd->skill_queue.emplace_front(status, skill); } } @@ -403,6 +404,7 @@ void resolve_skill(Field* fd) fd->skill_queue.pop_front(); if(!status) { + // trigger_regen skill_table[std::get<0>(skill)](fd, status, skill); } else if(!status->m_jammed) @@ -1808,12 +1810,6 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std return(fd->make_selection_array(cards.begin() + min_index, cards.begin() + max_index + 1, [fd, src_status, s](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); } -inline unsigned select_infuse(Field* fd, const SkillSpec& s) -{ - const auto &cards = boost::join(fd->tap->assaults.m_indirect, fd->tip->assaults.m_indirect); - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, s](CardStatus* c){return(skill_predicate(fd, &fd->tap->commander, c, s));})); -} - inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src_status) { return(fd->players[src_status->m_chaosed ? src_status->m_player : opponent(src_status->m_player)]->assaults.m_indirect); @@ -1905,35 +1901,35 @@ void maybeTriggerRegen(Field* fd) fd->skill_queue.emplace_front(nullptr, std::make_tuple(trigger_regen, 0, allfactions, false, SkillMod::on_activate)); } -unsigned get_target_hostile_index(Field* fd, CardStatus* src_status) +CardStatus* select_interceptable(Field* fd, CardStatus* src_status, unsigned index) { - unsigned rand_index(fd->rand(0, fd->selection_array.size() - 1)); - // intercept - if(!src_status->m_chaosed) + CardStatus* status(fd->selection_array[index]); + // do not intercept skills from allied units (Chaosed / Infuse) + if(src_status->m_player == status->m_player) + { + return(fd->selection_array[index]); + } + if(index > 0) { - CardStatus* status(fd->selection_array[rand_index]); - if(rand_index > 0) + CardStatus* left_status(fd->selection_array[index - 1]); + if(left_status->m_card->m_intercept && left_status->m_index == status->m_index - 1 && left_status->m_player == status->m_player && skill_check(fd, left_status, status)) { - CardStatus* left_status(fd->selection_array[rand_index - 1]); - if(left_status->m_card->m_intercept && left_status->m_index == status->m_index - 1 && skill_check(fd, left_status, status)) - { - count_achievement(fd, left_status); - _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(left_status).c_str(), status_description(status).c_str()); - return(rand_index - 1); - } + count_achievement(fd, left_status); + _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(left_status).c_str(), status_description(status).c_str()); + return(fd->selection_array[index - 1]); } - if(rand_index + 1 < fd->selection_array.size()) + } + if(index + 1 < fd->selection_array.size()) + { + CardStatus* right_status(fd->selection_array[index + 1]); + if(right_status->m_card->m_intercept && right_status->m_index == status->m_index + 1 && right_status->m_player == status->m_player && skill_check(fd, right_status, status)) { - CardStatus* right_status(fd->selection_array[rand_index + 1]); - if(right_status->m_card->m_intercept && right_status->m_index == status->m_index + 1 && skill_check(fd, right_status, status)) - { - count_achievement(fd, right_status); - _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(right_status).c_str(), status_description(status).c_str()); - return(rand_index + 1); - } + count_achievement(fd, right_status); + _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(right_status).c_str(), status_description(status).c_str()); + return(fd->selection_array[index + 1]); } } - return(rand_index); + return(fd->selection_array[index]); } template @@ -2011,20 +2007,17 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski } else { - if(!skill_roll(fd)) - { - return; - } - index_start = index_end = get_target_hostile_index(fd, src_status); + index_start = index_end = fd->rand(0, fd->selection_array.size() - 1); } bool is_count_achievement(true); for(unsigned s_index(index_start); s_index <= index_end; ++s_index) { - if(std::get<3>(s) && !skill_roll(fd)) + if(!skill_roll(fd)) { + _DEBUG_MSG(2, "%s misses the 50%% chance to activate %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(fd->selection_array[s_index]).c_str()); continue; } - CardStatus* c(fd->selection_array[s_index]); + CardStatus* c(std::get<3>(s) ? fd->selection_array[s_index] : select_interceptable(fd, src_status, s_index)); if(check_and_perform_skill(fd, src_status, c, s, true, is_count_achievement)) { // Count at most once even targeting "All" @@ -2067,6 +2060,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil // So far no friendly activation skill needs to roll 50% but check it for completeness. if(!skill_roll(fd)) { + _DEBUG_MSG(2, "%s misses the 50%% chance to %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(fd->selection_array[s_index]).c_str()); continue; } CardStatus* c(fd->selection_array[s_index]); @@ -2104,10 +2098,11 @@ void perform_backfire(Field* fd, CardStatus* src_status, const SkillSpec& s) void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) { - if(select_infuse(fd, s) > 0) + const auto &cards = boost::join(fd->tap->assaults.m_indirect, fd->tip->assaults.m_indirect); + if(fd->make_selection_array(cards.begin(), cards.end(), [fd, s](CardStatus* c){return(skill_predicate(fd, &fd->tap->commander, c, s));}) > 0) { _DEBUG_SELECTION("%s", skill_names[infuse].c_str()); - CardStatus* c(fd->selection_array[fd->rand(0, fd->selection_array.size() - 1)]); + CardStatus* c(select_interceptable(fd, src_status, fd->rand(0, fd->selection_array.size() - 1))); check_and_perform_skill(fd, src_status, c, s, true, true); } } @@ -2196,7 +2191,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) return; } _DEBUG_SELECTION("%s", skill_names[mimic].c_str()); - CardStatus* c(fd->selection_array[fd->effect == Effect::copycat ? fd->rand(0, fd->selection_array.size() - 1) : get_target_hostile_index(fd, src_status)]); + CardStatus* c(select_interceptable(fd, src_status, fd->rand(0, fd->selection_array.size() - 1))); // evade check for mimic // individual skills are subject to evade checks too, // but resolve_skill will handle those. diff --git a/tyrant.h b/tyrant.h index 2072682b..c3728559 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.11" +#define TYRANT_OPTIMIZER_VERSION "1.0.12" #include #include From 7b1a4119c9901c2218ddc8d0553b19ffc77e4e52 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 14 Apr 2013 20:26:57 +0800 Subject: [PATCH 147/406] Fix bug: Tributed skills should be Emulatable. --- sim.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/sim.cpp b/sim.cpp index f28a52c5..b8b94173 100644 --- a/sim.cpp +++ b/sim.cpp @@ -768,6 +768,7 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { + unsigned opponent_player = opponent(c->m_player); return(fd->players[opponent_player]->assaults.size() > c->m_index && fd->players[opponent_player]->assaults[c->m_index].m_hp > 0 && @@ -2035,6 +2036,22 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski prepend_on_death(fd); } +template +inline void check_and_perform_emulate(Field* fd, CardStatus* src_status, CardStatus* opposite_status, const SkillSpec& s) +{ + Hand* hand = fd->players[opponent(opposite_status->m_player)]; + if(hand->assaults.size() > opposite_status->m_index) + { + CardStatus& emulator = hand->assaults[opposite_status->m_index]; + if(emulator.m_card->m_emulate && skill_predicate(fd, src_status, &emulator, s) && skill_check(fd, &emulator, nullptr)) + { + count_achievement(fd, &emulator); + _DEBUG_MSG(1, "Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); + perform_skill(fd, &emulator, std::get<1>(s)); + } + } +} + template void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { @@ -2074,19 +2091,9 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil count_achievement(fd, c); _DEBUG_MSG(1, "Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); perform_skill(fd, src_status, std::get<1>(s)); + check_and_perform_emulate(fd, src_status, src_status, s); } - // Emulate - Hand* opp = fd->players[opponent(c->m_player)]; - if(opp->assaults.size() > c->m_index) - { - CardStatus& emulator = opp->assaults[c->m_index]; - if(emulator.m_card->m_emulate && skill_predicate(fd, src_status, &emulator, s) && skill_check(fd, &emulator, nullptr)) - { - count_achievement(fd, &emulator); - _DEBUG_MSG(1, "Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); - perform_skill(fd, &emulator, std::get<1>(s)); - } - } + check_and_perform_emulate(fd, src_status, c, s); } } } From f8dff9d295f433ffb89afbfd28a67aef5864c415 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 14 Apr 2013 20:28:26 +0800 Subject: [PATCH 148/406] Fix bug: Blitz should not be activated on units summoned during assault activation phase. --- sim.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index b8b94173..2b3aec9e 100644 --- a/sim.cpp +++ b/sim.cpp @@ -768,9 +768,9 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - unsigned opponent_player = opponent(c->m_player); - return(fd->players[opponent_player]->assaults.size() > c->m_index && + return(fd->current_phase != Field::assaults_phase && + fd->players[opponent_player]->assaults.size() > c->m_index && fd->players[opponent_player]->assaults[c->m_index].m_hp > 0 && fd->players[opponent_player]->assaults[c->m_index].m_delay == 0); } From e68776fc24c4ac6ecdd7a0514e9890678538a6df Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 15 Apr 2013 18:36:18 +0800 Subject: [PATCH 149/406] Use long double instead of double. --- read.cpp | 6 ++--- read.h | 2 +- tyrant_optimize.cpp | 66 ++++++++++++++++++++++----------------------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/read.cpp b/read.cpp index 92490efc..de0f63ac 100644 --- a/read.cpp +++ b/read.cpp @@ -21,9 +21,9 @@ void load_decks(Decks& decks, Cards& cards) } } -std::vector > parse_deck_list(std::string list_string) +std::vector > parse_deck_list(std::string list_string) { - std::vector > res; + std::vector > res; boost::tokenizer > list_tokens{list_string, boost::char_delimiters_separator{false, ";", ""}}; for(auto list_token = list_tokens.begin(); list_token != list_tokens.end(); ++list_token) { @@ -33,7 +33,7 @@ std::vector > parse_deck_list(std::string list_st ++deck_token; if(deck_token != deck_tokens.end()) { - res.back().second = boost::lexical_cast(*deck_token); + res.back().second = boost::lexical_cast(*deck_token); } } return(res); diff --git a/read.h b/read.h index 6e6cdaef..c0dddbde 100644 --- a/read.h +++ b/read.h @@ -11,7 +11,7 @@ class Deck; void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign); void load_decks(Decks& decks, Cards& cards); -std::vector > parse_deck_list(std::string list_string); +std::vector > parse_deck_list(std::string list_string); unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename); void read_owned_cards(Cards& cards, std::map& owned_cards, const char *filename); unsigned read_card_abbrs(Cards& cards, const std::string& filename); diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 45869638..b3a1acfa 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -215,9 +215,9 @@ bool suitable_commander(const Card* card) return(true); } //------------------------------------------------------------------------------ -double compute_score(const std::pair> , unsigned>& results, std::vector& factors) +long double compute_score(const std::pair> , unsigned>& results, std::vector& factors) { - double sum{0.}; + long double sum{0.}; for(unsigned index(0); index < results.first.size(); ++index) { if(use_anp) @@ -233,13 +233,13 @@ double compute_score(const std::pair> , unsigned>& sum += results.first[index].wins * factors[index]; } } - return(sum / std::accumulate(factors.begin(), factors.end(), 0.) / (double)results.second); + return(sum / std::accumulate(factors.begin(), factors.end(), 0.) / (long double)results.second); } //------------------------------------------------------------------------------ volatile unsigned thread_num_iterations{0}; // written by threads std::vector> thread_results; // written by threads volatile unsigned thread_total{0}; // written by threads -volatile double thread_prev_score{0.0}; +volatile long double thread_prev_score{0.0}; volatile bool thread_compare{false}; volatile bool thread_compare_stop{false}; // written by threads volatile bool destroy_threads; @@ -256,12 +256,12 @@ struct SimulationData Hand att_hand; std::vector > def_decks; std::vector def_hands; - std::vector factors; + std::vector factors; gamemode_t gamemode; enum Effect effect; const Achievement& achievement; - SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_def_decks_, std::vector factors_, gamemode_t gamemode_, enum Effect effect_, const Achievement& achievement_) : + SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_def_decks_, std::vector factors_, gamemode_t gamemode_, enum Effect effect_, const Achievement& achievement_) : re(seed), cards(cards_), decks(decks_), @@ -329,12 +329,12 @@ class Process const Decks& decks; Deck* att_deck; const std::vector def_decks; - std::vector factors; + std::vector factors; gamemode_t gamemode; enum Effect effect; Achievement achievement; - Process(unsigned _num_threads, const Cards& cards_, const Decks& decks_, Deck* att_deck_, std::vector _def_decks, std::vector _factors, gamemode_t _gamemode, enum Effect _effect, const Achievement& achievement_) : + Process(unsigned _num_threads, const Cards& cards_, const Decks& decks_, Deck* att_deck_, std::vector _def_decks, std::vector _factors, gamemode_t _gamemode, enum Effect _effect, const Achievement& achievement_) : num_threads(_num_threads), main_barrier(num_threads+1), cards(cards_), @@ -376,7 +376,7 @@ class Process return(std::make_pair(thread_results, thread_total)); } - std::pair> , unsigned> compare(unsigned num_iterations, double prev_score) + std::pair> , unsigned> compare(unsigned num_iterations, long double prev_score) { thread_num_iterations = num_iterations; thread_results = std::vector>(def_decks.size()); @@ -446,7 +446,7 @@ void thread_evaluate(boost::barrier& main_barrier, // Multiple defense decks case: scaling by factors and approximation of a "discrete" number of events. if(result.size() > 1) { - double score_accum_d = 0.0; + long double score_accum_d = 0.0; for(unsigned i = 0; i < thread_score_local.size(); ++i) { score_accum_d += thread_score_local[i] * sim.factors[i]; @@ -463,7 +463,7 @@ void thread_evaluate(boost::barrier& main_barrier, { // TODO: Fix this solution for ANP // Get a loose, rather than no, upper bound. - double best_possible = gamemode_local == surge ? 45 : 25; + long double best_possible = gamemode_local == surge ? 45 : 25; compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum / best_possible, 0.01) * best_possible < thread_prev_score); } else @@ -482,7 +482,7 @@ void thread_evaluate(boost::barrier& main_barrier, } } //------------------------------------------------------------------------------ -void print_score_info(const std::pair> , unsigned>& results, std::vector& factors) +void print_score_info(const std::pair> , unsigned>& results, std::vector& factors) { if(use_anp) { @@ -492,7 +492,7 @@ void print_score_info(const std::pair> , unsigned> std::cout << " ("; for(auto val: results.first) { - std::cout << static_cast(val.points) / results.second << " "; + std::cout << static_cast(val.points) / results.second << " "; } std::cout << "reps.)"; } @@ -509,9 +509,9 @@ void print_score_info(const std::pair> , unsigned> } } //------------------------------------------------------------------------------ -void print_results(const std::pair> , unsigned>& results, std::vector& factors) +void print_results(const std::pair> , unsigned>& results, std::vector& factors) { - Results final{0, 0, 0, 0}; + Results final{0, 0, 0, 0}; for(unsigned index(0); index < results.first.size(); ++index) { final.wins += results.first[index].wins * factors[index]; @@ -519,10 +519,10 @@ void print_results(const std::pair> , unsigned>& r final.losses += results.first[index].losses * factors[index]; final.points += results.first[index].points * factors[index]; } - final.wins /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; - final.draws /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; - final.losses /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; - final.points /= std::accumulate(factors.begin(), factors.end(), 0.) * (double)results.second; + final.wins /= std::accumulate(factors.begin(), factors.end(), 0.) * (long double)results.second; + final.draws /= std::accumulate(factors.begin(), factors.end(), 0.) * (long double)results.second; + final.losses /= std::accumulate(factors.begin(), factors.end(), 0.) * (long double)results.second; + final.points /= std::accumulate(factors.begin(), factors.end(), 0.) * (long double)results.second; std::cout << "win%: " << final.wins * 100.0 << " ("; for(auto val: results.first) @@ -551,14 +551,14 @@ void print_results(const std::pair> , unsigned>& r std::cout << " ("; for(auto val: results.first) { - std::cout << static_cast(val.points) / results.second << " "; + std::cout << static_cast(val.points) / results.second << " "; } std::cout << "reps.)"; } std::cout << std::endl; } //------------------------------------------------------------------------------ -void print_deck_inline(const double score, const Card *commander, std::vector cards, bool is_ordered) +void print_deck_inline(const long double score, const Card *commander, std::vector cards, bool is_ordered) { if(use_anp) { @@ -603,8 +603,8 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) { auto results = proc.evaluate(num_iterations); print_score_info(results, proc.factors); - double current_score = compute_score(results, proc.factors); - double best_score = current_score; + long double current_score = compute_score(results, proc.factors); + long double best_score = current_score; // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); @@ -616,7 +616,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; bool eval_commander = true; - double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; + long double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(10, d1->cards.size() + (fixed_len ? 0 : 1))) { if(deck_has_been_improved) @@ -708,8 +708,8 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) { auto results = proc.evaluate(num_iterations); print_score_info(results, proc.factors); - double current_score = compute_score(results, proc.factors); - double best_score = current_score; + long double current_score = compute_score(results, proc.factors); + long double best_score = current_score; // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); @@ -721,7 +721,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; bool eval_commander = true; - double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; + long double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(10, d1->cards.size() + (fixed_len ? 0 : 1))) { if(deck_has_been_improved) @@ -872,7 +872,7 @@ class Combination }; //------------------------------------------------------------------------------ static unsigned total_num_combinations_test(0); -inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsigned num_iterations, const std::vector& card_indices, std::vector& cards, const Card* commander, Process& proc, double& best_score, boost::optional& best_deck) +inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsigned num_iterations, const std::vector& card_indices, std::vector& cards, const Card* commander, Process& proc, long double& best_score, boost::optional& best_deck) { assert(card_indices.size() > 0); assert(card_indices.size() <= deck_size); @@ -909,7 +909,7 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig deck.set(commander, deck_cards); (*dynamic_cast(proc.att_deck)) = deck; auto new_results = proc.compare(num_iterations, best_score); - double new_score = compute_score(new_results, proc.factors); + long double new_score = compute_score(new_results, proc.factors); if(new_score > best_score) { best_score = new_score; @@ -949,7 +949,7 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig deck.set(commander, deck_cards); *proc.att_deck = deck; auto new_results = proc.compare(num_iterations, best_score); - double new_score = compute_score(new_results, proc.factors); + long double new_score = compute_score(new_results, proc.factors); if(new_score > best_score) { best_score = new_score; @@ -982,7 +982,7 @@ void exhaustive_k(unsigned num_iterations, unsigned var_k, Process& proc) Combination cardIndices(var_n, var_k); const std::vector& indices = cardIndices.getIndices(); bool finished(false); - double best_score{0}; + long double best_score{0}; boost::optional best_deck; unsigned num_cards = ((Deck*)proc.att_deck)->cards.size(); while(!finished) @@ -1264,7 +1264,7 @@ int main(int argc, char** argv) att_deck->strategy = att_strategy; std::vector def_decks; - std::vector def_decks_factors; + std::vector def_decks_factors; for(auto deck_parsed: deck_list_parsed) { Deck* def_deck{nullptr}; @@ -1359,7 +1359,7 @@ int main(int argc, char** argv) { debug_str.clear(); auto results = p.evaluate(1); - double score = compute_score(results, p.factors); + long double score = compute_score(results, p.factors); if(score >= std::get<0>(op) && score <= std::get<1>(op)) { std::cout << debug_str << std::flush; print_results(results, p.factors); From 692de80c30ac8ad4648ec67e8f675e81db1d28f7 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 19 Apr 2013 12:57:02 +0800 Subject: [PATCH 150/406] Fix bug: If an assault swipes and gets stuned, it wrongly attacks the commander. --- sim.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sim.cpp b/sim.cpp index 2b3aec9e..d40e41c9 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1490,9 +1490,9 @@ void attack_phase(Field* fd) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(); } - if(__builtin_expect(fd->end, false)) { return; } - // stille alive? attack the card in front - if(can_attack(att_status) && alive_assault(def_assaults, fd->current_ci)) + if(fd->end || !can_attack(att_status)) { return; } + // attack the card in front (or attacks the commander if the card in front is just died) + if(alive_assault(def_assaults, fd->current_ci)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); } @@ -1500,9 +1500,9 @@ void attack_phase(Field* fd) { attack_commander(fd, att_status); } - if(__builtin_expect(fd->end, false)) { return; } - // still alive? attack the card on the right - if(can_attack(att_status) && alive_assault(def_assaults, fd->current_ci + 1)) + if(fd->end || !can_attack(att_status)) { return; } + // attack the card on the right + if(alive_assault(def_assaults, fd->current_ci + 1)) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(); } From cf7685fa2efc2a3e7616fc37df09760e95f4035f Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 21 Apr 2013 22:35:03 +0800 Subject: [PATCH 151/406] Cards summoned by Genesis effect do not Blitz. --- sim.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index d40e41c9..bd6c80b3 100644 --- a/sim.cpp +++ b/sim.cpp @@ -2154,7 +2154,8 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) card_status.m_is_summoned = true; _DEBUG_MSG(1, "%s %s %s %u [%s]\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); prepend_skills(fd, &card_status); - if(card_status.m_card->m_blitz) + // Summon X (Genesis effect) does not activate Blitz for X + if(std::get<1>(s) != 0 && card_status.m_card->m_blitz) { check_and_perform_blitz(fd, &card_status); } From 1499ba00132d18c6f03b9f51882c4e0c92ac798f Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 21 Apr 2013 23:59:43 +0800 Subject: [PATCH 152/406] Cleanse should not activate under Decay battleground effect. --- sim.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sim.cpp b/sim.cpp index bd6c80b3..5750c85c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1576,7 +1576,8 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, co template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { - return(c->m_hp > 0 && ( + return(fd->effect != Effect::decay && + c->m_hp > 0 && ( c->m_chaosed || c->m_diseased || c->m_enfeebled > 0 || @@ -1688,10 +1689,6 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - if(fd->effect == Effect::decay) - { - return; - } c->m_chaosed = false; c->m_diseased = false; c->m_enfeebled = 0; From 97a1fae28a03e7e58cc9bbf5db3197c87ff12566 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 4 May 2013 23:00:06 +0800 Subject: [PATCH 153/406] Speed up hill climbing: skip evaluated decks. --- deck.h | 12 ++++ sim.cpp | 2 + tyrant_optimize.cpp | 160 +++++++++++++++++++++++++++----------------- 3 files changed, 114 insertions(+), 60 deletions(-) diff --git a/deck.h b/deck.h index f53cf7c9..ef0aea25 100644 --- a/deck.h +++ b/deck.h @@ -78,6 +78,18 @@ struct Deck void set(const Cards& all_cards, const std::string& deck_string_); void resolve(const Cards& all_cards); + template + Container card_ids() const + { + Container results; + results.insert(results.end(), commander->m_id); + for(auto card: cards) + { + results.insert(results.end(), card->m_id); + } + return(results); + } + Deck* clone() const; std::string short_description() const; std::string long_description(const Cards& all_cards) const; diff --git a/sim.cpp b/sim.cpp index 5750c85c..311b86ee 100644 --- a/sim.cpp +++ b/sim.cpp @@ -216,12 +216,14 @@ std::string card_description(const Cards& cards, const Card* c) if(c->m_immobilize) { desc += ", immobilize"; } if(c->m_intercept) { desc += ", intercept"; } if(c->m_leech > 0) { desc += ", leech " + to_string(c->m_leech); } + if(c->m_legion > 0) { desc += ", legion " + to_string(c->m_legion); } if(c->m_payback) { desc += ", payback"; } if(c->m_pierce > 0) { desc += ", pierce " + to_string(c->m_pierce); } if(c->m_poison > 0) { desc += ", poison " + to_string(c->m_poison); } if(c->m_refresh) { desc += ", refresh"; } if(c->m_regenerate > 0) { desc += ", regenerate " + to_string(c->m_regenerate); } if(c->m_siphon > 0) { desc += ", siphon " + to_string(c->m_siphon); } + if(c->m_stun) { desc += ", stun"; } if(c->m_swipe) { desc += ", swipe"; } if(c->m_tribute) { desc += ", tribute"; } if(c->m_valor > 0) { desc += ", valor " + to_string(c->m_valor); } diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index b3a1acfa..4b804dc6 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -615,7 +615,8 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) print_deck_inline(best_score, best_commander, best_cards, false); std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; - bool eval_commander = true; + std::map, unsigned> evaluated_decks; + unsigned long skipped_simulations = 0; long double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(10, d1->cards.size() + (fixed_len ? 0 : 1))) { @@ -624,7 +625,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) dead_slot = slot_i; deck_has_been_improved = false; } - if(eval_commander && !keep_commander) + if(!keep_commander) { for(const Card* commander_candidate: proc.cards.player_commanders) { @@ -634,24 +635,32 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) if(!suitable_commander(commander_candidate)) { continue; } // Place it in the deck d1->commander = commander_candidate; - // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score); - current_score = compute_score(compare_results, proc.factors); - // Is it better ? - if(current_score > best_score) + auto &&cur_deck = d1->card_ids>(); + if(evaluated_decks.count(cur_deck) == 0) { - // Then update best score/commander, print stuff - best_score = current_score; - best_commander = commander_candidate; - deck_has_been_improved = true; - std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards, false) << " commander -> " << card_id_name(commander_candidate) << ": "; - print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards, false); + // Evaluate new deck + auto compare_results = proc.compare(num_iterations, best_score); + current_score = compute_score(compare_results, proc.factors); + evaluated_decks[cur_deck] = compare_results.second; + // Is it better ? + if(current_score > best_score) + { + // Then update best score/commander, print stuff + best_score = current_score; + best_commander = commander_candidate; + deck_has_been_improved = true; + std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards, false) << " commander -> " << card_id_name(commander_candidate) << ": "; + print_score_info(compare_results, proc.factors); + print_deck_inline(best_score, best_commander, best_cards, false); + } + } + else + { + skipped_simulations += evaluated_decks[cur_deck]; } } // Now that all commanders are evaluated, take the best one d1->commander = best_commander; - eval_commander = false; } std::shuffle(non_commander_cards.begin(), non_commander_cards.end(), re); for(const Card* card_candidate: non_commander_cards) @@ -678,28 +687,38 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) // Remove it from the deck d1->cards.erase(d1->cards.begin() + slot_i); } - // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score); - current_score = compute_score(compare_results, proc.factors); - // Is it better ? - if(current_score > best_score) + auto &&cur_deck = d1->card_ids>(); + if(evaluated_decks.count(cur_deck) == 0) + { + // Evaluate new deck + auto compare_results = proc.compare(num_iterations, best_score); + current_score = compute_score(compare_results, proc.factors); + evaluated_decks[cur_deck] = compare_results.second; + // Is it better ? + if(current_score > best_score) + { + std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards, false) << " " << card_id_name(slot_i < best_cards.size() ? best_cards[slot_i] : NULL) << + " -> " << card_id_name(card_candidate) << ": "; + // Then update best score/slot, print stuff + best_score = current_score; + best_cards = d1->cards; + deck_has_been_improved = true; + print_score_info(compare_results, proc.factors); + print_deck_inline(best_score, best_commander, best_cards, false); + } + } + else { - std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards, false) << " " << card_id_name(slot_i < best_cards.size() ? best_cards[slot_i] : NULL) << - " -> " << card_id_name(card_candidate) << ": "; - // Then update best score/slot, print stuff - best_score = current_score; - best_cards = d1->cards; - eval_commander = true; - deck_has_been_improved = true; - print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards, false); + skipped_simulations += evaluated_decks[cur_deck]; } d1->cards = best_cards; if(best_score == best_possible) { break; } } - // Now that all cards are evaluated, take the best one - d1->cards = best_cards; } + unsigned simulations = 0; + for(auto evaluation: evaluated_decks) + { simulations += evaluation.second; } + std::cout << "Evaluated " << evaluated_decks.size() << " decks (" << simulations << " + " << skipped_simulations << " simulations)." << std::endl; std::cout << "Optimized Deck: "; print_deck_inline(best_score, best_commander, best_cards, false); } @@ -720,7 +739,8 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) print_deck_inline(best_score, best_commander, best_cards, true); std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; - bool eval_commander = true; + std::map, unsigned> evaluated_decks; + unsigned long skipped_simulations = 0; long double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(10, d1->cards.size() + (fixed_len ? 0 : 1))) { @@ -729,7 +749,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) dead_slot = from_slot; deck_has_been_improved = false; } - if(eval_commander && !keep_commander) + if(!keep_commander) { for(const Card* commander_candidate: proc.cards.player_commanders) { @@ -740,24 +760,32 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) if(!suitable_commander(commander_candidate)) { continue; } // Place it in the deck d1->commander = commander_candidate; - // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score); - current_score = compute_score(compare_results, proc.factors); - // Is it better ? - if(current_score > best_score) + auto &&cur_deck = d1->card_ids>(); + if(evaluated_decks.count(cur_deck) == 0) { - // Then update best score/commander, print stuff - best_score = current_score; - best_commander = commander_candidate; - deck_has_been_improved = true; - std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards, true) << " commander -> " << card_id_name(commander_candidate) << ": "; - print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards, true); + // Evaluate new deck + auto compare_results = proc.compare(num_iterations, best_score); + current_score = compute_score(compare_results, proc.factors); + evaluated_decks[cur_deck] = compare_results.second; + // Is it better ? + if(current_score > best_score) + { + // Then update best score/commander, print stuff + best_score = current_score; + best_commander = commander_candidate; + deck_has_been_improved = true; + std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards, true) << " commander -> " << card_id_name(commander_candidate) << ": "; + print_score_info(compare_results, proc.factors); + print_deck_inline(best_score, best_commander, best_cards, true); + } + } + else + { + skipped_simulations += evaluated_decks[cur_deck]; } } // Now that all commanders are evaluated, take the best one d1->commander = best_commander; - eval_commander = false; } std::shuffle(non_commander_cards.begin(), non_commander_cards.end(), re); for(const Card* card_candidate: non_commander_cards) @@ -784,27 +812,39 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) // Remove it from the deck d1->cards.erase(d1->cards.begin() + from_slot); } - // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score); - current_score = compute_score(compare_results, proc.factors); - // Is it better ? - if(current_score > best_score) + auto &&cur_deck = d1->card_ids>(); + if(evaluated_decks.count(cur_deck) == 0) { - // Then update best score/slot, print stuff - std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards, true) << " " << from_slot << " " << card_id_name(from_slot < best_cards.size() ? best_cards[from_slot] : NULL) << - " -> " << to_slot << " " << card_id_name(card_candidate) << ": "; - best_score = current_score; - best_cards = d1->cards; - eval_commander = true; - deck_has_been_improved = true; - print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards, true); + // Evaluate new deck + auto compare_results = proc.compare(num_iterations, best_score); + current_score = compute_score(compare_results, proc.factors); + evaluated_decks[cur_deck] = compare_results.second; + // Is it better ? + if(current_score > best_score) + { + // Then update best score/slot, print stuff + std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards, true) << " " << from_slot << " " << card_id_name(from_slot < best_cards.size() ? best_cards[from_slot] : NULL) << + " -> " << to_slot << " " << card_id_name(card_candidate) << ": "; + best_score = current_score; + best_cards = d1->cards; + deck_has_been_improved = true; + print_score_info(compare_results, proc.factors); + print_deck_inline(best_score, best_commander, best_cards, true); + } + } + else + { + skipped_simulations += evaluated_decks[cur_deck]; } d1->cards = best_cards; } if(best_score == best_possible) { break; } } } + unsigned simulations = 0; + for(auto evaluation: evaluated_decks) + { simulations += evaluation.second; } + std::cout << "Evaluated " << evaluated_decks.size() << " decks (" << simulations << " + " << skipped_simulations << " simulations)." << std::endl; std::cout << "Optimized Deck: "; print_deck_inline(best_score, best_commander, best_cards, true); } From 5915e2a02f694eb55568a0b6426c2adad1daa084 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 16 May 2013 12:56:58 +0800 Subject: [PATCH 154/406] Add Sunder skill (in advance). --- card.h | 4 ++++ deck.h | 2 +- sim.cpp | 47 ++++++++++++++++++++++++++++++++++++++++------- sim.h | 1 + tyrant.cpp | 2 +- tyrant.h | 4 ++-- xml.cpp | 6 ++++++ 7 files changed, 55 insertions(+), 11 deletions(-) diff --git a/card.h b/card.h index 9481ad15..fa2002c0 100644 --- a/card.h +++ b/card.h @@ -47,6 +47,8 @@ class Card m_siphon(0), m_split(false), m_stun(false), + m_sunder(false), + m_sunder_oa(false), m_swipe(false), m_tribute(false), m_unique(false), @@ -106,6 +108,8 @@ class Card unsigned m_siphon; bool m_split; bool m_stun; + bool m_sunder; + bool m_sunder_oa; bool m_swipe; bool m_tribute; bool m_unique; diff --git a/deck.h b/deck.h index ef0aea25..4d7683b4 100644 --- a/deck.h +++ b/deck.h @@ -7,8 +7,8 @@ #include #include #include "tyrant.h" +#include "card.h" -class Card; class Cards; std::string deck_hash(const Card* commander, std::vector cards, bool is_ordered); diff --git a/sim.cpp b/sim.cpp index 311b86ee..8e35bf24 100644 --- a/sim.cpp +++ b/sim.cpp @@ -100,6 +100,7 @@ CardStatus::CardStatus(const Card* card) : m_protected(0), m_rallied(0), m_stunned(0), + m_sundered(false), m_weakened(0), m_temporary_split(false), m_is_summoned(false), @@ -136,6 +137,7 @@ inline void CardStatus::set(const Card& card) m_rallied = 0; m_weakened = 0; m_stunned = 0; + m_sundered = false; m_temporary_split = false; m_is_summoned = false; m_step = CardStep::none; @@ -224,6 +226,7 @@ std::string card_description(const Cards& cards, const Card* c) if(c->m_regenerate > 0) { desc += ", regenerate " + to_string(c->m_regenerate); } if(c->m_siphon > 0) { desc += ", siphon " + to_string(c->m_siphon); } if(c->m_stun) { desc += ", stun"; } + if(c->m_sunder) { desc += ", sunder"; } if(c->m_swipe) { desc += ", swipe"; } if(c->m_tribute) { desc += ", tribute"; } if(c->m_valor > 0) { desc += ", valor " + to_string(c->m_valor); } @@ -234,6 +237,7 @@ std::string card_description(const Cards& cards, const Card* c) if(c->m_berserk_oa > 0) { desc += ", berserk " + to_string(c->m_berserk_oa); } if(c->m_disease_oa) { desc += ", disease on Attacked"; } if(c->m_poison_oa > 0) { desc += ", poison " + to_string(c->m_poison_oa) + " on Attacked"; } + if(c->m_sunder_oa) { desc += ", sunder on Attacked"; } for(auto& skill: c->m_skills_on_attacked) { desc += ", " + skill_description(cards, skill); } for(auto& skill: c->m_skills_on_death) { desc += ", " + skill_description(cards, skill); } return(desc); @@ -282,6 +286,7 @@ std::string CardStatus::description() if(m_immobilized) { desc += ", immobilized"; } if(m_infused) { desc += ", infused"; } if(m_jammed) { desc += ", jammed"; } + if(m_sundered) { desc += ", sundered"; } if(m_temporary_split) { desc += ", cloning"; } if(m_augmented > 0) { desc += ", augmented " + to_string(m_augmented); } if(m_enfeebled > 0) { desc += ", enfeebled " + to_string(m_enfeebled); } @@ -767,6 +772,12 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(ref->m_card->m_flying); } +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(!c->m_sundered); +} + template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { @@ -843,6 +854,12 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(ref->m_card->m_type == CardType::assault); } +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(!ref->m_sundered); +} + template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { @@ -934,11 +951,11 @@ inline void remove_dead(Storage& storage) { storage.remove(is_it_dead); } -inline void add_hp(Field* field, CardStatus* target, unsigned v) +inline void add_hp(Field* fd, CardStatus* target, unsigned v) { unsigned old_hp = target->m_hp; target->m_hp = std::min(target->m_hp + v, target->m_card->m_health); - if(field->effect == Effect::invigorate && target->m_card->m_type == CardType::assault) + if(fd->effect == Effect::invigorate && target->m_card->m_type == CardType::assault && skill_check(fd, target, nullptr)) { unsigned healed = target->m_hp - old_hp; target->m_berserk += healed; @@ -1089,7 +1106,7 @@ void evaluate_legion(Field* fd) } // skill_check bool do_heal = can_be_healed(status); - bool do_rally = status->m_hp > 0 && (fd->tapi == status->m_player ? status->m_delay == 0 || status->m_blitzing : status->m_delay <= 1); + bool do_rally = status->m_hp > 0 && !status->m_sundered && (fd->tapi == status->m_player ? status->m_delay == 0 || status->m_blitzing : status->m_delay <= 1); if(!do_heal && !do_rally) { continue; @@ -1181,8 +1198,8 @@ struct PerformAttack // modify damage // assaults only: immobilize // deal damage - // assaults only: (siphon, poison, disease, on_kill) - // on_attacked: poison, disease, assaults only: berserk, skills + // assaults only: (siphon, poison, disease, sunder, on_kill) + // on_attacked: poison, disease, sunder, assaults only: berserk, skills // counter, berserk // assaults only: (crush, leech if still alive) // check regeneration @@ -1353,6 +1370,13 @@ struct PerformAttack count_achievement(fd, def_status); def_status->m_berserk += def_status->m_card->m_berserk_oa; } + if(def_status->m_card->m_sunder_oa && skill_check(fd, def_status, att_status)) + { + count_achievement(fd, def_status); + // perform_skill_sunder + _DEBUG_MSG(1, "%s (on attacked) sunders %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); + att_status->m_sundered = true; + } evaluate_skills(fd, def_status, def_status->m_card->m_skills_on_attacked); } @@ -1403,6 +1427,13 @@ void PerformAttack::siphon_poison_disease() _DEBUG_MSG(1, "%s diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); def_status->m_diseased = true; } + if(att_status->m_card->m_sunder && skill_check(fd, att_status, def_status)) + { + count_achievement(fd, att_status); + // perform_skill_sunder + _DEBUG_MSG(1, "%s sunders %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); + def_status->m_sundered = true; + } } template<> @@ -1587,7 +1618,8 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, c->m_immobilized || c->m_jammed || c->m_poisoned || - c->m_stunned + c->m_stunned || + c->m_sundered )); } @@ -1629,7 +1661,7 @@ template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { const auto& mod = std::get<4>(s); - return(can_attack(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); + return(can_attack(c) && !c->m_sundered && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (src->m_player != c->m_player || mod == SkillMod::on_death ? (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)) : mod == SkillMod::on_attacked ? is_active_next_turn(c) : is_active(c) && !is_attacking_or_has_attacked(c))); @@ -1699,6 +1731,7 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) c->m_jammed = false; c->m_poisoned = 0; c->m_stunned = 0; + c->m_sundered = false; } template<> diff --git a/sim.h b/sim.h index 8cf5449c..8d26c159 100644 --- a/sim.h +++ b/sim.h @@ -138,6 +138,7 @@ struct CardStatus unsigned m_protected; unsigned m_rallied; unsigned m_stunned; + bool m_sundered; unsigned m_weakened; bool m_temporary_split; bool m_is_summoned; // is this card summoned (or split)? diff --git a/tyrant.cpp b/tyrant.cpp index 7a929b76..2e908efb 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -21,7 +21,7 @@ std::string skill_names[Skill::num_skills] = // Damage-Dependant: "Berserk", "Crush", "Disease", "Immobilize", "Leech", "Poison", "Siphon", // Defensive: - "Armored", "Counter", "Emulate", "Evade", "Flying", "Intercept", "Payback", "Refresh", "Regenerate", "Stun", "Tribute", "Wall", + "Armored", "Counter", "Emulate", "Evade", "Flying", "Intercept", "Payback", "Refresh", "Regenerate", "Stun", "Sunder", "Tribute", "Wall", // Triggered: "Blitz", "Legion", // Static (Ignored): diff --git a/tyrant.h b/tyrant.h index c3728559..a3a4e241 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.12" +#define TYRANT_OPTIMIZER_VERSION "1.0.13" #include #include @@ -33,7 +33,7 @@ enum Skill // Damage-Dependant: berserk, crush, disease, immobilize, leech, poison, siphon, // Defensive: - armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, tribute, wall, + armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, sunder, tribute, wall, // Triggered: blitz, legion, // Static, ignored: diff --git a/xml.cpp b/xml.cpp index 8f097256..2551d94c 100644 --- a/xml.cpp +++ b/xml.cpp @@ -268,6 +268,12 @@ void read_cards(Cards& cards) { c->m_siphon = atoi(skill->first_attribute("x")->value()); } if(strcmp(skill->first_attribute("id")->value(), "stun") == 0) { c->m_stun = true; } + if(strcmp(skill->first_attribute("id")->value(), "sunder") == 0) + { + bool attacked(skill->first_attribute("attacked")); + if(attacked) { c->m_sunder_oa = true; } + else {c->m_sunder = true; } + } if(strcmp(skill->first_attribute("id")->value(), "swipe") == 0) { c->m_swipe = true; } if(strcmp(skill->first_attribute("id")->value(), "tribute") == 0) From 2cf44ad1a51e2f34cbf8bb19f16a7f56afec6692 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 23 May 2013 15:15:52 +0800 Subject: [PATCH 155/406] Add United Front effect. --- sim.cpp | 23 ++++++++++++++++++----- tyrant.cpp | 1 + tyrant.h | 1 + 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/sim.cpp b/sim.cpp index 8e35bf24..bd2656c7 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1093,7 +1093,8 @@ void evaluate_legion(Field* fd) for(fd->current_ci = 0; fd->current_ci < assaults.size(); ++fd->current_ci) { CardStatus* status(&assaults[fd->current_ci]); - if(status->m_card->m_legion == 0) + unsigned legion_base = fd->effect == Effect::united_front && status->m_player == 1 ? status->m_card->m_delay : status->m_card->m_legion; + if(legion_base == 0) { continue; } @@ -1112,8 +1113,8 @@ void evaluate_legion(Field* fd) continue; } count_achievement(fd, status); - unsigned legion_value = status->m_card->m_legion * legion_size; - _DEBUG_MSG(1, "%s activates Legion %u, %s%s%s by %u\n", status_description(status).c_str(), status->m_card->m_legion, + unsigned legion_value = legion_base * legion_size; + _DEBUG_MSG(1, "%s activates Legion %u, %s%s%s by %u\n", status_description(status).c_str(), legion_base, do_heal ? "healed" : "", do_heal && do_rally ? " and " : "", do_rally ? "rallied" : "", legion_value); if(do_heal) { @@ -2363,8 +2364,7 @@ void modify_cards(Cards& cards, enum Effect effect) // Do nothing; this is implemented in add_hp break; case Effect::clone_project: - case Effect::clone_experiment: - // Do nothing; these are implemented in play + // Do nothing; this is implemented in play break; case Effect::friendly_fire: cards_gain_skill(cards, strike, 1, false, false); @@ -2373,6 +2373,10 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::genesis: cards_gain_skill(cards, summon, 0, false, false); // No replace exising Summon, if any break; + case Effect::artillery_fire: + case Effect::photon_shield: + throw std::runtime_error("Unused effect"); + break; case Effect::decrepit: cards_gain_skill(cards, enfeeble, 1, true, true); break; @@ -2382,10 +2386,19 @@ void modify_cards(Cards& cards, enum Effect effect) case Effect::chilling_touch: cards_gain_skill(cards, freeze, 0, false, false); break; + case Effect::clone_experiment: + // Do nothing; this is implemented in play + break; case Effect::toxic: // Do nothing; this is implemented in PlayCard::fieldEffects // and summon_card break; + case Effect::haunt: + throw std::runtime_error("Unused effect"); + break; + case Effect::united_front: + // Do nothing: this is implemented in evaluate_legion + break; default: // TODO: throw something more useful throw effect; diff --git a/tyrant.cpp b/tyrant.cpp index 2e908efb..034ead97 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -60,6 +60,7 @@ std::string effect_names[Effect::num_effects] = { "Clone Experiment", "Toxic", "Haunt", + "United Front", }; std::string achievement_misc_req_names[AchievementMiscReq::num_achievement_misc_reqs] = { diff --git a/tyrant.h b/tyrant.h index a3a4e241..05be8489 100644 --- a/tyrant.h +++ b/tyrant.h @@ -103,6 +103,7 @@ enum Effect { clone_experiment, toxic, haunt, + united_front, num_effects }; From 5caee3c812a9a5531913f5f35775f1abf6ded456 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 23 May 2013 15:39:03 +0800 Subject: [PATCH 156/406] Add options minlen and maxlen. --- tyrant_optimize.cpp | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 4b804dc6..328925d7 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -80,7 +80,8 @@ Deck* find_deck(Decks& decks, const Cards& cards, std::string deck_name) //------------------------------------------------------------------------------ std::map owned_cards; bool use_owned_cards{false}; -bool fixed_len{false}; +unsigned min_deck_len{1}; +unsigned max_deck_len{10}; // No raid rewards from 500 and 1k honor for ancient raids // No very hard to get rewards (level >= 150, faction >= 13) @@ -609,7 +610,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_actions.begin(), proc.cards.player_actions.end()); - if(!fixed_len) { non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); } + non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; print_deck_inline(best_score, best_commander, best_cards, false); @@ -618,7 +619,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) std::map, unsigned> evaluated_decks; unsigned long skipped_simulations = 0; long double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; - for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(10, d1->cards.size() + (fixed_len ? 0 : 1))) + for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(deck_has_been_improved) { @@ -683,7 +684,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) } else { - if(fixed_len || slot_i == best_cards.size() || best_cards.size() == 1) { continue; } + if(best_cards.size() <= min_deck_len || slot_i == best_cards.size()) { continue; } // Remove it from the deck d1->cards.erase(d1->cards.begin() + slot_i); } @@ -733,7 +734,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_actions.begin(), proc.cards.player_actions.end()); - if(!fixed_len) { non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); } + non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; print_deck_inline(best_score, best_commander, best_cards, true); @@ -742,7 +743,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) std::map, unsigned> evaluated_decks; unsigned long skipped_simulations = 0; long double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; - for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(10, d1->cards.size() + (fixed_len ? 0 : 1))) + for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(deck_has_been_improved) { @@ -808,7 +809,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) } else { - if(fixed_len || from_slot == best_cards.size() || best_cards.size() == 1) { continue; } + if(best_cards.size() <= min_deck_len || from_slot == best_cards.size()) { continue; } // Remove it from the deck d1->cards.erase(d1->cards.begin() + from_slot); } @@ -1093,7 +1094,8 @@ void usage(int argc, char** argv) " -A : optimize for the achievement specified by either id or name.\n" " -c: don't try to optimize the commander.\n" " -e : set the battleground effect. effect is automatically set for quests.\n" - " -fixedlen: prevent hill climbing from changing the number of cards.\n" + " minlen : prevent hill climbing from shortening decks than cards.\n" + " maxlen : prevent hill climbing from lengthening decks than cards.\n" " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n" " -o=: restrict hill climbing to the owned cards listed in .\n" " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" @@ -1151,6 +1153,7 @@ int main(int argc, char** argv) effect_map[ss.str()] = i; } + bool fixed_len{false}; std::vector > todo; for(int argIndex(3); argIndex < argc; ++argIndex) { @@ -1175,10 +1178,6 @@ int main(int argc, char** argv) { keep_commander = true; } - else if(strcmp(argv[argIndex], "tournament") == 0) - { - gamemode = tournament; - } else if(strcmp(argv[argIndex], "-e") == 0) { std::string arg_effect(argv[argIndex + 1]); @@ -1196,6 +1195,16 @@ int main(int argc, char** argv) { fixed_len = true; } + else if(strcmp(argv[argIndex], "maxlen") == 0) + { + max_deck_len = atoi(argv[argIndex + 1]); + argIndex += 1; + } + else if(strcmp(argv[argIndex], "minlen") == 0) + { + min_deck_len = atoi(argv[argIndex + 1]); + argIndex += 1; + } else if(strcmp(argv[argIndex], "-o") == 0) { read_owned_cards(cards, owned_cards, "ownedcards.txt"); @@ -1226,6 +1235,10 @@ int main(int argc, char** argv) { gamemode = surge; } + else if(strcmp(argv[argIndex], "tournament") == 0) + { + gamemode = tournament; + } else if(strcmp(argv[argIndex], "-t") == 0) { num_threads = atoi(argv[argIndex+1]); @@ -1302,6 +1315,10 @@ int main(int argc, char** argv) return(5); } att_deck->strategy = att_strategy; + if(fixed_len) + { + min_deck_len = max_deck_len = att_deck->cards.size(); + } std::vector def_decks; std::vector def_decks_factors; From 125a22defce1bd9076efb120690c5acbc6d303c6 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 24 May 2013 11:28:03 +0800 Subject: [PATCH 157/406] Support 'locking' cards during hill climbing by adding '!' prefix to cards. --- deck.cpp | 33 +++++---------------------------- deck.h | 1 + read.cpp | 12 ++++++++++-- read.h | 2 +- tyrant.h | 2 +- tyrant_optimize.cpp | 45 ++++++++++++++++++++++++++++++++++----------- 6 files changed, 52 insertions(+), 43 deletions(-) diff --git a/deck.cpp b/deck.cpp index 48a6fe95..db9d983b 100644 --- a/deck.cpp +++ b/deck.cpp @@ -125,33 +125,6 @@ void hash_to_ids(const char* hash, std::vector& ids) } } // end of namespace -void namelist_to_ids(const Cards& all_cards, const std::string& deck_string, std::vector& ids) -{ - boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; - auto token_iter = deck_tokens.begin(); - for(; token_iter != deck_tokens.end(); ++token_iter) - { - std::string card_spec(*token_iter); - unsigned card_id{0}; - unsigned card_num{1}; - signed num_sign{0}; - try - { - parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign); - assert(num_sign == 0); - for(unsigned i(0); i < card_num; ++i) - { - ids.push_back(card_id); - } - } - catch(std::exception& e) - { - std::cerr << "Ignore card: " << e.what() << std::endl; - continue; - } - } -} - namespace range = boost::range; void Deck::set(const Cards& all_cards, const std::vector& ids) @@ -209,19 +182,23 @@ void Deck::resolve(const Cards& all_cards) { boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; auto token_iter = deck_tokens.begin(); + signed p = -1; for(; token_iter != deck_tokens.end(); ++token_iter) { std::string card_spec(*token_iter); unsigned card_id{0}; unsigned card_num{1}; signed num_sign{0}; + char mark{0}; try { - parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign); + parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign, mark); assert(num_sign == 0); for(unsigned i(0); i < card_num; ++i) { ids.push_back(card_id); + if(mark) { card_marks[p] = mark; } + ++ p; } } catch(std::exception& e) diff --git a/deck.h b/deck.h index 4d7683b4..af0fd4a9 100644 --- a/deck.h +++ b/deck.h @@ -40,6 +40,7 @@ struct Deck const Card* commander; std::vector cards; + std::map card_marks; // : -1 indicating the commander. E.g, used as a mark to be kept in attacking deck when optimizing. std::deque shuffled_cards; // card id -> card order std::map > order; diff --git a/read.cpp b/read.cpp index de0f63ac..c8463d4f 100644 --- a/read.cpp +++ b/read.cpp @@ -81,15 +81,21 @@ template Iterator read_toke } // num_sign = 0 if card_num is "N"; = +1 if "+N"; = -1 if "-N" -void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign) +void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign, char& mark) { static std::set recognized_abbr; auto card_spec_iter = card_spec.begin(); card_id = 0; card_num = 1; num_sign = 0; + mark = 0; std::string card_name; card_spec_iter = read_token(card_spec_iter, card_spec.end(), [](char c){return(c=='#' || c=='(' || c=='\r');}, card_name); + if(card_name[0] == '!') + { + mark = card_name[0]; + card_name.erase(0, 1); + } if(card_name.empty()) { throw std::runtime_error("no card name"); @@ -280,7 +286,9 @@ void read_owned_cards(Cards& cards, std::map& owned_cards, c unsigned card_id{0}; unsigned card_num{1}; signed num_sign{0}; - parse_card_spec(cards, card_spec, card_id, card_num, num_sign); + char mark{0}; + parse_card_spec(cards, card_spec, card_id, card_num, num_sign, mark); + assert(mark == 0); if(num_sign == 0) { owned_cards[card_id] = card_num; diff --git a/read.h b/read.h index c0dddbde..7036f747 100644 --- a/read.h +++ b/read.h @@ -9,7 +9,7 @@ class Cards; class Decks; class Deck; -void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign); +void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign, char& mark); void load_decks(Decks& decks, Cards& cards); std::vector > parse_deck_list(std::string list_string); unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename); diff --git a/tyrant.h b/tyrant.h index 05be8489..f350eaaf 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.13" +#define TYRANT_OPTIMIZER_VERSION "1.0.14" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 328925d7..d0c173e7 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -198,11 +198,9 @@ bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) return(true); } -bool keep_commander{false}; bool suitable_commander(const Card* card) { assert(card->m_type == CardType::commander); - if(keep_commander) { return(false); } if(use_owned_cards) { auto owned_iter = owned_cards.find(card->m_id); @@ -600,7 +598,7 @@ void print_deck_inline(const long double score, const Card *commander, std::vect std::cout << std::endl; } //------------------------------------------------------------------------------ -void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) +void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) { auto results = proc.evaluate(num_iterations); print_score_info(results, proc.factors); @@ -621,12 +619,13 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) long double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { + if(card_marks.count(slot_i)) { continue; } if(deck_has_been_improved) { dead_slot = slot_i; deck_has_been_improved = false; } - if(!keep_commander) + if(!card_marks.count(-1)) { for(const Card* commander_candidate: proc.cards.player_commanders) { @@ -724,7 +723,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc) print_deck_inline(best_score, best_commander, best_cards, false); } //------------------------------------------------------------------------------ -void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) +void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) { auto results = proc.evaluate(num_iterations); print_score_info(results, proc.factors); @@ -750,7 +749,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) dead_slot = from_slot; deck_has_been_improved = false; } - if(!keep_commander) + if(!card_marks.count(-1)) { for(const Card* commander_candidate: proc.cards.player_commanders) { @@ -795,6 +794,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) assert(!card_candidate || card_candidate->m_type != CardType::commander); for(unsigned to_slot(card_candidate ? 0 : d1->cards.size() - 1); to_slot < d1->cards.size() + (from_slot < d1->cards.size() ? 0 : 1); ++to_slot) { + if(card_marks.count(from_slot) && card_candidate != best_cards[from_slot]) { break; } if(card_candidate) { // Various checks to check if the card is accepted @@ -831,6 +831,24 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc) deck_has_been_improved = true; print_score_info(compare_results, proc.factors); print_deck_inline(best_score, best_commander, best_cards, true); + std::map new_card_marks; + for(auto it: card_marks) + { + signed pos = it.first; + char mark = it.second; + if(pos < 0) {} + else if(static_cast(pos) == from_slot) + { + pos = to_slot; + } + else + { + if(static_cast(pos) > from_slot) { -- pos; } + if(static_cast(pos) >= to_slot) { ++ pos; } + } + new_card_marks[pos] = mark; + } + card_marks = new_card_marks; } } else @@ -1004,7 +1022,7 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig } } //------------------------------------------------------------------------------ -void exhaustive_k(unsigned num_iterations, unsigned var_k, Process& proc) +void exhaustive_k(unsigned num_iterations, unsigned var_k, Process& proc, std::map card_marks) { std::vector ass_structs; for(const Card* card: proc.cards.player_assaults) @@ -1028,7 +1046,7 @@ void exhaustive_k(unsigned num_iterations, unsigned var_k, Process& proc) unsigned num_cards = ((Deck*)proc.att_deck)->cards.size(); while(!finished) { - if(keep_commander) + if(card_marks.count(-1)) { try_all_ratio_combinations(num_cards, var_k, num_iterations, indices, ass_structs, ((Deck*)proc.att_deck)->commander, proc, best_score, best_deck); } @@ -1153,6 +1171,7 @@ int main(int argc, char** argv) effect_map[ss.str()] = i; } + bool keep_commander{false}; bool fixed_len{false}; std::vector > todo; for(int argIndex(3); argIndex < argc; ++argIndex) @@ -1315,6 +1334,10 @@ int main(int argc, char** argv) return(5); } att_deck->strategy = att_strategy; + if(keep_commander) + { + att_deck->card_marks[-1] = '!'; + } if(fixed_len) { min_deck_len = max_deck_len = att_deck->cards.size(); @@ -1388,17 +1411,17 @@ int main(int argc, char** argv) switch(std::get<2>(op)) { case bruteforce: { - exhaustive_k(std::get<1>(op), std::get<0>(op), p); + exhaustive_k(std::get<1>(op), std::get<0>(op), p, att_deck->card_marks); break; } case climb: { if(att_strategy == DeckStrategy::random) { - hill_climbing(std::get<0>(op), att_deck, p); + hill_climbing(std::get<0>(op), att_deck, p, att_deck->card_marks); } else { - hill_climbing_ordered(std::get<0>(op), att_deck, p); + hill_climbing_ordered(std::get<0>(op), att_deck, p, att_deck->card_marks); } break; } From c4bb9fb653b82416f3f62bc8c406758ec57ba241 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 25 May 2013 18:22:49 +0800 Subject: [PATCH 158/406] Fix bug: the original deck should be counted as evaluated during hill climbing. --- tyrant_optimize.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index d0c173e7..b30f9f03 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -604,6 +604,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map, unsigned> evaluated_decks{{d1->card_ids>(), num_iterations}}; // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); @@ -614,7 +615,6 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map, unsigned> evaluated_decks; unsigned long skipped_simulations = 0; long double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score < best_possible; slot_i = (slot_i + 1) % std::min(max_deck_len, d1->cards.size() + 1)) @@ -729,6 +729,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std print_score_info(results, proc.factors); long double current_score = compute_score(results, proc.factors); long double best_score = current_score; + std::map, unsigned> evaluated_decks{{d1->card_ids>(), num_iterations}}; // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); @@ -739,7 +740,6 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std print_deck_inline(best_score, best_commander, best_cards, true); std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; - std::map, unsigned> evaluated_decks; unsigned long skipped_simulations = 0; long double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) From 75bce0395ff192718970d594e338f382cee2252d Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 27 Jun 2013 12:33:56 +0800 Subject: [PATCH 159/406] Update United Front battleground effect. --- sim.cpp | 2 +- tyrant.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index bd2656c7..12cc7473 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1093,7 +1093,7 @@ void evaluate_legion(Field* fd) for(fd->current_ci = 0; fd->current_ci < assaults.size(); ++fd->current_ci) { CardStatus* status(&assaults[fd->current_ci]); - unsigned legion_base = fd->effect == Effect::united_front && status->m_player == 1 ? status->m_card->m_delay : status->m_card->m_legion; + unsigned legion_base = fd->effect == Effect::united_front ? status->m_card->m_delay : status->m_card->m_legion; if(legion_base == 0) { continue; diff --git a/tyrant.h b/tyrant.h index f350eaaf..b2b660b2 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.14" +#define TYRANT_OPTIMIZER_VERSION "1.0.15" #include #include From efe5dda4337d5a48408711536532591243d4bfbc Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 30 Jun 2013 17:09:31 +0800 Subject: [PATCH 160/406] Add option -L. Remove options minlen and maxlen. --- tyrant.h | 2 +- tyrant_optimize.cpp | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/tyrant.h b/tyrant.h index b2b660b2..670984c7 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.15" +#define TYRANT_OPTIMIZER_VERSION "1.0.16" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index b30f9f03..fb9577d6 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1112,8 +1112,7 @@ void usage(int argc, char** argv) " -A : optimize for the achievement specified by either id or name.\n" " -c: don't try to optimize the commander.\n" " -e : set the battleground effect. effect is automatically set for quests.\n" - " minlen : prevent hill climbing from shortening decks than cards.\n" - " maxlen : prevent hill climbing from lengthening decks than cards.\n" + " -L : restrict deck size between and during hill climbing.\n" " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n" " -o=: restrict hill climbing to the owned cards listed in .\n" " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" @@ -1214,15 +1213,11 @@ int main(int argc, char** argv) { fixed_len = true; } - else if(strcmp(argv[argIndex], "maxlen") == 0) - { - max_deck_len = atoi(argv[argIndex + 1]); - argIndex += 1; - } - else if(strcmp(argv[argIndex], "minlen") == 0) + else if(strcmp(argv[argIndex], "-L") == 0) { min_deck_len = atoi(argv[argIndex + 1]); - argIndex += 1; + max_deck_len = atoi(argv[argIndex + 2]); + argIndex += 2; } else if(strcmp(argv[argIndex], "-o") == 0) { From fef1d52cb126cd3660922a7db7d1119d852fdbbe Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 5 Jul 2013 12:14:30 +0800 Subject: [PATCH 161/406] Simulate bizarre behavior: On death uses relative-position targeting. --- sim.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index 12cc7473..65f1459f 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1603,7 +1603,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, co const auto& mod = std::get<4>(s); return(!c->m_chaosed && can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : - mod == SkillMod::on_death ? (fd->tapi != src->m_player ? is_active(c) && !has_attacked(c) : is_active_next_turn(c)) : + mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != c->m_player ? is_active(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } @@ -1646,7 +1646,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, cons const auto& mod = std::get<4>(s); return(can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : - mod == SkillMod::on_death ? (fd->tapi != src->m_player ? is_active(c) && !has_attacked(c) : is_active_next_turn(c)) : + mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != c->m_player ? is_active(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } @@ -1694,7 +1694,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, c const auto& mod = std::get<4>(s); return(can_attack(c) && attack_power(c) > 0 && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : - mod == SkillMod::on_death ? (fd->tapi != src->m_player ? is_active(c) && !has_attacked(c) : is_active_next_turn(c)) : + mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != c->m_player ? is_active(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } From ef524da5c62c8ad286b268c69f89b27699db8c92 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 5 Jul 2013 18:34:07 +0800 Subject: [PATCH 162/406] Fix typo. --- sim.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index 65f1459f..1a4af1f1 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1603,7 +1603,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, co const auto& mod = std::get<4>(s); return(!c->m_chaosed && can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : - mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != c->m_player ? is_active(c) : is_active_next_turn(c)) : + mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } @@ -1646,7 +1646,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, cons const auto& mod = std::get<4>(s); return(can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : - mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != c->m_player ? is_active(c) : is_active_next_turn(c)) : + mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } @@ -1694,7 +1694,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, c const auto& mod = std::get<4>(s); return(can_attack(c) && attack_power(c) > 0 && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : - mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != c->m_player ? is_active(c) : is_active_next_turn(c)) : + mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } From b857960c6613552f6962feda07e85577c993b376 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 14 Jul 2013 18:06:55 +0800 Subject: [PATCH 163/406] Simulate bizarre behavior: On attacked uses relative-position targeting. --- sim.cpp | 6 +++--- tyrant.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sim.cpp b/sim.cpp index 1a4af1f1..851ddf12 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1602,7 +1602,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, co { const auto& mod = std::get<4>(s); return(!c->m_chaosed && can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); - (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : + (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } @@ -1645,7 +1645,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, cons { const auto& mod = std::get<4>(s); return(can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); - (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : + (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } @@ -1693,7 +1693,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, c { const auto& mod = std::get<4>(s); return(can_attack(c) && attack_power(c) > 0 && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); - (mod == SkillMod::on_attacked ? is_active(c) && !is_attacking_or_has_attacked(c) : + (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } diff --git a/tyrant.h b/tyrant.h index 670984c7..1b737a9e 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.16" +#define TYRANT_OPTIMIZER_VERSION "1.0.17" #include #include From dc4d0daee1fba7339736671470a0d71d91eaf0e1 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 26 Jul 2013 15:08:51 +0800 Subject: [PATCH 164/406] Add option target. --- tyrant_optimize.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index fb9577d6..c134e081 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -42,6 +42,7 @@ namespace { bool use_anp{false}; bool win_tie{false}; gamemode_t gamemode{fight}; + long double target_score{-1}; } using namespace std::placeholders; @@ -616,8 +617,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map(max_deck_len, d1->cards.size() + 1)) + for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score - target_score < 1e-9; slot_i = (slot_i + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(card_marks.count(slot_i)) { continue; } if(deck_has_been_improved) @@ -712,7 +712,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcards = best_cards; - if(best_score == best_possible) { break; } + if(best_score - target_score > -1e-9) { break; } } } unsigned simulations = 0; @@ -741,8 +741,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; - long double best_possible = use_anp ? (gamemode == surge ? 45 : 25) : 1; - for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score < best_possible; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) + for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score - target_score < 1e-9; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(deck_has_been_improved) { @@ -753,7 +752,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std { for(const Card* commander_candidate: proc.cards.player_commanders) { - if(best_score == best_possible) { break; } + if(best_score - target_score > -1e-9) { break; } // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); if(commander_candidate->m_name == best_commander->m_name) { continue; } @@ -857,7 +856,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std } d1->cards = best_cards; } - if(best_score == best_possible) { break; } + if(best_score - target_score > -1e-9) { break; } } } unsigned simulations = 0; @@ -1118,6 +1117,7 @@ void usage(int argc, char** argv) " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" " -s: use surge (default is fight).\n" " -t : set the number of threads, default is 4.\n" + " target : set the target score for hill climbing.\n" " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n" " -wintie: attacker wins if turns run out (default is defeated; can be used for def deck simulation).\n" "\n" @@ -1249,6 +1249,11 @@ int main(int argc, char** argv) { gamemode = surge; } + else if(strcmp(argv[argIndex], "target") == 0) + { + target_score = atof(argv[argIndex+1]); + argIndex += 1; + } else if(strcmp(argv[argIndex], "tournament") == 0) { gamemode = tournament; @@ -1303,6 +1308,10 @@ int main(int argc, char** argv) return(1); } } + if (target_score < 0) + { + target_score = use_anp ? (gamemode == surge ? 45 : 25) : 1; + } Deck* att_deck{nullptr}; try From 9a5873607a81f4a67dbaa53a07bb75c0bddb4829 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 1 Aug 2013 10:21:30 +0800 Subject: [PATCH 165/406] Support revamped raids. --- sim.cpp | 38 ++++++--- sim.h | 9 ++- tyrant.h | 9 ++- tyrant_optimize.cpp | 192 ++++++++++++++++++-------------------------- 4 files changed, 117 insertions(+), 131 deletions(-) diff --git a/sim.cpp b/sim.cpp index 851ddf12..06acca8f 100644 --- a/sim.cpp +++ b/sim.cpp @@ -585,8 +585,11 @@ Results play(Field* fd) fd->achievement_counter.clear(); fd->achievement_counter.resize(fd->achievement.req_counter.size()); +#if 0 // ANP: Last decision point is second-to-last card played. fd->points_since_last_decision = 0; +#endif + fd->all_damage_to_commander = 0; unsigned p0_size = fd->players[0]->deck->cards.size(); fd->last_decision_turn = p0_size == 1 ? 0 : p0_size * 2 - (fd->gamemode == surge ? 2 : 3); @@ -600,12 +603,14 @@ Results play(Field* fd) // Initialize stuff, remove dead cards _DEBUG_MSG(1, "------------------------------------------------------------------------\n" "TURN %u begins for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); +#if 0 // ANP: If it's the player's turn and he's making a decision, // reset his points to 0. if(fd->tapi == 0 && fd->turn <= fd->last_decision_turn) { fd->points_since_last_decision = 0; } +#endif turn_start_phase(fd); // Special case: refresh on commander if(fd->tip->commander.m_card->m_refresh) @@ -709,6 +714,7 @@ Results play(Field* fd) fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::turns); } bool made_achievement = true; + fd->set_counter(fd->achievement.misc_req, AchievementMiscReq::com_total, fd->all_damage_to_commander); for(unsigned i(0); made_achievement && i < fd->achievement.req_counter.size(); ++i) { made_achievement = made_achievement && fd->achievement.req_counter[i].check(fd->achievement_counter[i]); @@ -717,34 +723,42 @@ Results play(Field* fd) { print_achievement_results(fd); } - // defender wins + // you lose if(fd->players[0]->commander.m_hp == 0) { - _DEBUG_MSG(1, "Defender wins.\n"); + _DEBUG_MSG(1, "You lose.\n"); return {0, 0, 1, 0}; } - // achievement: assuming winner='1' - if (!made_achievement) + // you win in raid + if(fd->optimization_mode == OptimizationMode::raid) { - _DEBUG_MSG(1, "Achievement fails.\n"); - return {0, 0, 1, 0}; + _DEBUG_MSG(1, "You win.\n"); + return {1, 0, 0, std::min(fd->all_damage_to_commander, 200u) + (fd->players[1]->commander.m_hp == 0 ? 50 : 0)}; } - // attacker wins + // you win if(fd->players[1]->commander.m_hp == 0) { + if (!made_achievement) + { + _DEBUG_MSG(1, "You win but no achievement.\n"); + return {1, 0, 0, 0}; + } + _DEBUG_MSG(1, "You win.\n"); +#if 0 // ANP: Speedy if turn < last_decision + 10. bool speedy = fd->turn < fd->last_decision_turn + 10; if(fd->points_since_last_decision > 10) { fd->points_since_last_decision = 10; } - _DEBUG_MSG(1, "Attacker wins.\n"); - return {1, 0, 0, 10 + (speedy ? 5 : 0) + (fd->gamemode == surge ? 20 : 0) + fd->points_since_last_decision}; + return {1, 0, 0, 10 + (speedy ? 5 : 0) + (fd->gamemode == surge ? 20 : 0) + fd->points_since_last_decision, 0}; +#endif + return {1, 0, 0, 1}; } if (fd->turn > turn_limit) { _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); - return {0, 1, 0, 0}; + return {0, 1, 0, fd->optimization_mode == OptimizationMode::defense}; } // Huh? How did we get here? @@ -1164,8 +1178,10 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg, bool count // Points are awarded for overkill, so it is correct to simply add dmg. if(count_points && status.m_player == 1) { +#if 0 fd->points_since_last_decision += dmg; - fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::com_total, dmg); +#endif + fd->all_damage_to_commander += dmg; } if(status.m_hp == 0) { diff --git a/sim.h b/sim.h index 8d26c159..48bed538 100644 --- a/sim.h +++ b/sim.h @@ -190,7 +190,8 @@ class Field std::vector selection_array; unsigned turn; gamemode_t gamemode; - const enum Effect effect; + OptimizationMode optimization_mode; + const Effect effect; const Achievement& achievement; // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. @@ -212,18 +213,20 @@ class Field // otherwise is the index of the current card in players->structures or players->assaults unsigned current_ci; unsigned last_decision_turn; - unsigned points_since_last_decision; +// unsigned points_since_last_decision; + unsigned all_damage_to_commander; unsigned fusion_count; std::vector achievement_counter; - Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, enum Effect effect_, const Achievement& achievement_) : + Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, OptimizationMode optimization_mode_, Effect effect_, const Achievement& achievement_) : end{false}, re(re_), cards(cards_), players{{&hand1, &hand2}}, turn(1), gamemode(gamemode_), + optimization_mode(optimization_mode_), effect(effect_), achievement(achievement_) { diff --git a/tyrant.h b/tyrant.h index 1b737a9e..7b7f2c75 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.0.17" +#define TYRANT_OPTIMIZER_VERSION "1.1.0" #include #include @@ -128,6 +128,13 @@ enum gamemode_t tournament }; +enum class OptimizationMode +{ + win, + raid, + defense +}; + struct true_ {}; struct false_ {}; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index c134e081..6083f5b8 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -39,10 +39,9 @@ //#include "timer.hpp" namespace { - bool use_anp{false}; - bool win_tie{false}; gamemode_t gamemode{fight}; - long double target_score{-1}; + OptimizationMode optimization_mode{OptimizationMode::win}; + long double target_score{-1e9}; } using namespace std::placeholders; @@ -220,18 +219,7 @@ long double compute_score(const std::pair> , unsig long double sum{0.}; for(unsigned index(0); index < results.first.size(); ++index) { - if(use_anp) - { - sum += results.first[index].points * factors[index]; - } - else if(win_tie) - { - sum += (results.first[index].wins + results.first[index].draws) * factors[index]; - } - else - { - sum += results.first[index].wins * factors[index]; - } + sum += results.first[index].points * factors[index]; } return(sum / std::accumulate(factors.begin(), factors.end(), 0.) / (long double)results.second); } @@ -302,7 +290,7 @@ struct SimulationData { att_hand.reset(re); def_hand->reset(re); - Field fd(re, cards, att_hand, *def_hand, gamemode, effect, achievement); + Field fd(re, cards, att_hand, *def_hand, gamemode, optimization_mode, effect, achievement); Results result(play(&fd)); res.emplace_back(result); } @@ -398,8 +386,6 @@ void thread_evaluate(boost::barrier& main_barrier, const Process& p, unsigned thread_id) { - bool use_anp_local{use_anp}; - gamemode_t gamemode_local{gamemode}; while(true) { main_barrier.wait(); @@ -424,18 +410,7 @@ void thread_evaluate(boost::barrier& main_barrier, for(unsigned index(0); index < result.size(); ++index) { thread_results[index] += result[index]; //! - if(use_anp_local) - { - thread_score_local[index] = thread_results[index].points; //! - } - else if(win_tie) - { - thread_score_local[index] = thread_results[index].wins + thread_results[index].draws; //! - } - else - { - thread_score_local[index] = thread_results[index].wins; //! - } + thread_score_local[index] = thread_results[index].points; //! } ++thread_total; //! unsigned thread_total_local{thread_total}; //! @@ -459,17 +434,9 @@ void thread_evaluate(boost::barrier& main_barrier, score_accum = thread_score_local[0]; } bool compare_stop(false); - if(use_anp_local) - { - // TODO: Fix this solution for ANP - // Get a loose, rather than no, upper bound. - long double best_possible = gamemode_local == surge ? 45 : 25; - compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum / best_possible, 0.01) * best_possible < thread_prev_score); - } - else - { - compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum, 0.01) < thread_prev_score); - } + long double best_possible = (optimization_mode == OptimizationMode::raid ? 250 : 1); + // Get a loose (better than no) upper bound. TODO: Improve it. + compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum / best_possible, 0.01) * best_possible < thread_prev_score); if(compare_stop) { shared_mutex.lock(); //<<<< //std::cout << thread_total_local << "\n"; @@ -484,29 +451,12 @@ void thread_evaluate(boost::barrier& main_barrier, //------------------------------------------------------------------------------ void print_score_info(const std::pair> , unsigned>& results, std::vector& factors) { - if(use_anp) + std::cout << compute_score(results, factors) << " ("; + for(auto val: results.first) { - std::cout << "ANP: " << compute_score(results, factors); - if(results.first.size() > 1) - { - std::cout << " ("; - for(auto val: results.first) - { - std::cout << static_cast(val.points) / results.second << " "; - } - std::cout << "reps.)"; - } - std::cout << std::endl; - } - else - { - std::cout << "win%: " << compute_score(results, factors) * 100.0 << " ("; - for(auto val: results.first) - { - std::cout << val.wins << " "; - } - std::cout << "out of " << results.second << ")" << std::endl; + std::cout << val.points << " "; } + std::cout << "/ " << results.second << ")" << std::endl; } //------------------------------------------------------------------------------ void print_results(const std::pair> , unsigned>& results, std::vector& factors) @@ -529,46 +479,39 @@ void print_results(const std::pair> , unsigned>& r { std::cout << val.wins << " "; } - std::cout << "out of " << results.second << ")" << std::endl; + std::cout << "/ " << results.second << ")" << std::endl; - std::cout << "draw%: " << final.draws * 100.0 << " ("; - for(auto val: results.first) + if(optimization_mode != OptimizationMode::raid) { - std::cout << val.draws << " "; + std::cout << "draw%: " << final.draws * 100.0 << " ("; + for(auto val: results.first) + { + std::cout << val.draws << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; } - std::cout << "out of " << results.second << ")" << std::endl; std::cout << "loss%: " << final.losses * 100.0 << " ("; for(auto val: results.first) { std::cout << val.losses << " "; } - std::cout << "out of " << results.second << ")" << std::endl; + std::cout << "/ " << results.second << ")" << std::endl; - std::cout << "ANP: " << final.points; - if(results.first.size() > 1) + if(optimization_mode == OptimizationMode::raid) { - std::cout << " ("; + std::cout << "ard: " << final.points << " ("; for(auto val: results.first) { - std::cout << static_cast(val.points) / results.second << " "; + std::cout << val.points << " "; } - std::cout << "reps.)"; + std::cout << "/ " << results.second << ")" << std::endl; } - std::cout << std::endl; } //------------------------------------------------------------------------------ void print_deck_inline(const long double score, const Card *commander, std::vector cards, bool is_ordered) { - if(use_anp) - { - std::cout << score << ": "; - } - else - { - std::cout << score * 100.0 << "%: "; - } - std::cout << commander->m_name; + std::cout << score << ": " << commander->m_name; if(!is_ordered) { std::sort(cards.begin(), cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); @@ -1068,7 +1011,8 @@ enum Operation { bruteforce, climb, simulate, - fightuntil + debug, + debuguntil }; //------------------------------------------------------------------------------ void print_available_decks(const Decks& decks, bool allow_card_pool) @@ -1095,21 +1039,21 @@ void print_available_effects() void usage(int argc, char** argv) { - std::cout << "usage: " << argv[0] << " Attacker Defender [Flags] [Operations]\n" + std::cout << "usage: " << argv[0] << " Your_Deck Enemy_Deck [Flags] [Operations]\n" "\n" - "Attacker:\n" + "Your_Deck:\n" " the name/hash/cards of a custom deck.\n" "\n" - "Defender:\n" + "Enemy_Deck:\n" " semicolon separated list of defense decks, syntax:\n" " deck1[:factor1];deck2[:factor2];...\n" " where deck is the name/hash/cards of a mission, raid, quest or custom deck, and factor is optional. The default factor is 1.\n" " example: \'fear:0.2;slowroll:0.8\' means fear is the defense deck 20% of the time, while slowroll is the defense deck 80% of the time.\n" "\n" "Flags:\n" - " -a: optimize for ANP instead of win rate.\n" " -A : optimize for the achievement specified by either id or name.\n" " -c: don't try to optimize the commander.\n" + " defense: score even if turns run out. can be used for defending deck simulation.\n" " -e : set the battleground effect. effect is automatically set for quests.\n" " -L : restrict deck size between and during hill climbing.\n" " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n" @@ -1117,9 +1061,8 @@ void usage(int argc, char** argv) " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" " -s: use surge (default is fight).\n" " -t : set the number of threads, default is 4.\n" - " target : set the target score for hill climbing.\n" + " target : stop hill climbing as soon as the score reaches .\n" " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n" - " -wintie: attacker wins if turns run out (default is defeated; can be used for def deck simulation).\n" "\n" "Operations:\n" " brute : find the best combination of different cards, using up to battles to evaluate a deck.\n" @@ -1175,11 +1118,7 @@ int main(int argc, char** argv) std::vector > todo; for(int argIndex(3); argIndex < argc; ++argIndex) { - if(strcmp(argv[argIndex], "-a") == 0) - { - use_anp = true; - } - else if(strcmp(argv[argIndex], "-A") == 0) + if(strcmp(argv[argIndex], "-A") == 0) { try { @@ -1196,6 +1135,10 @@ int main(int argc, char** argv) { keep_commander = true; } + else if(strcmp(argv[argIndex], "defense") == 0) + { + optimization_mode = OptimizationMode::defense; + } else if(strcmp(argv[argIndex], "-e") == 0) { std::string arg_effect(argv[argIndex + 1]); @@ -1233,6 +1176,10 @@ int main(int argc, char** argv) { att_strategy = DeckStrategy::ordered; } + else if(strcmp(argv[argIndex], "raid") == 0) // for test + { + optimization_mode = OptimizationMode::raid; + } else if(strcmp(argv[argIndex], "exact-ordered") == 0) { att_strategy = DeckStrategy::exact_ordered; @@ -1249,11 +1196,6 @@ int main(int argc, char** argv) { gamemode = surge; } - else if(strcmp(argv[argIndex], "target") == 0) - { - target_score = atof(argv[argIndex+1]); - argIndex += 1; - } else if(strcmp(argv[argIndex], "tournament") == 0) { gamemode = tournament; @@ -1263,6 +1205,11 @@ int main(int argc, char** argv) num_threads = atoi(argv[argIndex+1]); argIndex += 1; } + else if(strcmp(argv[argIndex], "target") == 0) + { + target_score = atof(argv[argIndex+1]); + argIndex += 1; + } else if(strcmp(argv[argIndex], "-turnlimit") == 0) { turn_limit = atoi(argv[argIndex+1]); @@ -1272,10 +1219,6 @@ int main(int argc, char** argv) { ++ debug_print; } - else if(strcmp(argv[argIndex], "-wintie") == 0) - { - win_tie = true; - } else if(strcmp(argv[argIndex], "brute") == 0) { todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), bruteforce)); @@ -1293,13 +1236,13 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "debug") == 0) { - todo.push_back(std::make_tuple(0u, 45u, fightuntil)); + todo.push_back(std::make_tuple(0u, 0u, debug)); } else if(strcmp(argv[argIndex], "debuguntil") == 0) { - // fight until the last battle results in range [min_score, max_score]. - // E.g., 0 0: until lose; 1 1: until win (non-ANP); 10 25: until win (ANP). - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), fightuntil)); + // output the debug info for the first battle that min_score <= score <= max_score. + // E.g., 0 0: lose; 1 1: win (non-raid); 100 999: at least 100 damage (raid). + todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), debuguntil)); argIndex += 2; } else @@ -1308,10 +1251,6 @@ int main(int argc, char** argv) return(1); } } - if (target_score < 0) - { - target_score = use_anp ? (gamemode == surge ? 45 : 25) : 1; - } Deck* att_deck{nullptr}; try @@ -1371,7 +1310,7 @@ int main(int argc, char** argv) { if(def_deck->decktype != DeckType::mission) { - std::cerr << "Error: Defender must be mission for achievement." << std::endl; + std::cerr << "Error: Enemy's deck must be mission for achievement." << std::endl; return(1); } if(!achievement.mission_condition.check(def_deck->id)) @@ -1380,6 +1319,11 @@ int main(int argc, char** argv) return(1); } } + if(def_deck->decktype == DeckType::raid) + { + optimization_mode = OptimizationMode::raid; + turn_limit = 30; + } // Set quest effect: Effect this_effect = def_deck->effect; if(this_effect != Effect::none) @@ -1397,16 +1341,21 @@ int main(int argc, char** argv) } modify_cards(cards, effect); - std::cout << "Attacker: " << (debug_print ? att_deck->long_description(cards) : att_deck->short_description()) << std::endl; + std::cout << "Your Deck: " << (debug_print ? att_deck->long_description(cards) : att_deck->short_description()) << std::endl; for(auto def_deck: def_decks) { - std::cout << "Defender: " << (debug_print ? def_deck->long_description(cards) : def_deck->short_description()) << std::endl; + std::cout << "Enemy's Deck: " << (debug_print ? def_deck->long_description(cards) : def_deck->short_description()) << std::endl; } if(effect != Effect::none) { std::cout << "Effect: " << effect_names[effect] << std::endl; } + if (target_score < 0) + { + target_score = optimization_mode == OptimizationMode::raid ? 250 : 1; + } + Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); { //ScopeClock timer; @@ -1434,7 +1383,18 @@ int main(int argc, char** argv) print_results(results, p.factors); break; } - case fightuntil: { + case debug: { + unsigned saved_num_threads = num_threads; + num_threads = 1; + ++ debug_print; + debug_str.clear(); + auto results = p.evaluate(1); + print_results(results, p.factors); + -- debug_print; + num_threads = saved_num_threads; + break; + } + case debuguntil: { unsigned saved_num_threads = num_threads; num_threads = 1; ++ debug_print; From e3675210d1faf0de8c9457932a68bb652fd930db Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 1 Aug 2013 11:07:15 +0800 Subject: [PATCH 166/406] Fix bug: Blitz status should carry into the next attack phase if the assault is Jammed. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 06acca8f..53bc6d8b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1058,7 +1058,7 @@ void turn_start_phase(Field* fd) CardStatus& status(assaults[index]); status.m_index = index; status.m_augmented = 0; - status.m_blitzing = false; + status.m_blitzing = status.m_blitzing && status.m_jammed; status.m_chaosed = false; status.m_enfeebled = 0; status.m_frozen = false; From 2142517dd0e59e1b64b78166be47f39aa0db6a03 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 1 Aug 2013 14:17:10 +0800 Subject: [PATCH 167/406] Fix edge cases. --- sim.cpp | 2 +- tyrant_optimize.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index 53bc6d8b..0c3b1196 100644 --- a/sim.cpp +++ b/sim.cpp @@ -733,7 +733,7 @@ Results play(Field* fd) if(fd->optimization_mode == OptimizationMode::raid) { _DEBUG_MSG(1, "You win.\n"); - return {1, 0, 0, std::min(fd->all_damage_to_commander, 200u) + (fd->players[1]->commander.m_hp == 0 ? 50 : 0)}; + return {1, 0, 0, fd->players[1]->commander.m_hp == 0 ? 250 : std::min(fd->all_damage_to_commander, 200u)}; } // you win if(fd->players[1]->commander.m_hp == 0) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 6083f5b8..50ada72e 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -560,7 +560,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map(max_deck_len, d1->cards.size() + 1)) + for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score - target_score < -1e-9; slot_i = (slot_i + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(card_marks.count(slot_i)) { continue; } if(deck_has_been_improved) @@ -684,7 +684,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; - for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score - target_score < 1e-9; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) + for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score - target_score < -1e-9; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(deck_has_been_improved) { @@ -1179,6 +1179,7 @@ int main(int argc, char** argv) else if(strcmp(argv[argIndex], "raid") == 0) // for test { optimization_mode = OptimizationMode::raid; + turn_limit = 30; } else if(strcmp(argv[argIndex], "exact-ordered") == 0) { From a6e097307a48b28b40a0c30b3baa593550bf11d1 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 2 Aug 2013 11:39:20 +0800 Subject: [PATCH 168/406] Improve output message. --- sim.cpp | 33 +++++--- tyrant.h | 6 +- tyrant_optimize.cpp | 181 ++++++++++++++++++++++++++------------------ xml.cpp | 4 - 4 files changed, 133 insertions(+), 91 deletions(-) diff --git a/sim.cpp b/sim.cpp index 0c3b1196..36ffebc6 100644 --- a/sim.cpp +++ b/sim.cpp @@ -348,7 +348,7 @@ void Hand::reset(std::mt19937& re) // Everything about how a battle plays out, except the following: // the implementation of the attack by an assault card is in the next section; // the implementation of the active skills is in the section after that. -unsigned turn_limit{50}; +unsigned turn_limit{0}; //------------------------------------------------------------------------------ inline unsigned opponent(unsigned player) { @@ -714,14 +714,17 @@ Results play(Field* fd) fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::turns); } bool made_achievement = true; - fd->set_counter(fd->achievement.misc_req, AchievementMiscReq::com_total, fd->all_damage_to_commander); - for(unsigned i(0); made_achievement && i < fd->achievement.req_counter.size(); ++i) + if(fd->optimization_mode == OptimizationMode::achievement) { - made_achievement = made_achievement && fd->achievement.req_counter[i].check(fd->achievement_counter[i]); - } - if(debug_print) - { - print_achievement_results(fd); + fd->set_counter(fd->achievement.misc_req, AchievementMiscReq::com_total, fd->all_damage_to_commander); + for(unsigned i(0); made_achievement && i < fd->achievement.req_counter.size(); ++i) + { + made_achievement = made_achievement && fd->achievement.req_counter[i].check(fd->achievement_counter[i]); + } + if(debug_print) + { + print_achievement_results(fd); + } } // you lose if(fd->players[0]->commander.m_hp == 0) @@ -732,13 +735,21 @@ Results play(Field* fd) // you win in raid if(fd->optimization_mode == OptimizationMode::raid) { - _DEBUG_MSG(1, "You win.\n"); - return {1, 0, 0, fd->players[1]->commander.m_hp == 0 ? 250 : std::min(fd->all_damage_to_commander, 200u)}; + if(fd->players[1]->commander.m_hp == 0) + { + _DEBUG_MSG(1, "You win (boss killed).\n"); + return {1, 0, 0, 250}; + } + else + { + _DEBUG_MSG(1, "You win (survival).\n"); + return {0, 1, 0, std::min(fd->all_damage_to_commander, 200u)}; + } } // you win if(fd->players[1]->commander.m_hp == 0) { - if (!made_achievement) + if (fd->optimization_mode == OptimizationMode::achievement && !made_achievement) { _DEBUG_MSG(1, "You win but no achievement.\n"); return {1, 0, 0, 0}; diff --git a/tyrant.h b/tyrant.h index 7b7f2c75..e841d82b 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.1.0" +#define TYRANT_OPTIMIZER_VERSION "1.1.1" #include #include @@ -130,7 +130,9 @@ enum gamemode_t enum class OptimizationMode { - win, + none, + winrate, + achievement, raid, defense }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 50ada72e..082ebceb 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -40,7 +40,7 @@ namespace { gamemode_t gamemode{fight}; - OptimizationMode optimization_mode{OptimizationMode::win}; + OptimizationMode optimization_mode{OptimizationMode::none}; long double target_score{-1e9}; } @@ -214,14 +214,22 @@ bool suitable_commander(const Card* card) return(true); } //------------------------------------------------------------------------------ -long double compute_score(const std::pair> , unsigned>& results, std::vector& factors) +Results compute_score(const std::pair> , unsigned>& results, std::vector& factors) { - long double sum{0.}; + Results final{0, 0, 0, 0}; for(unsigned index(0); index < results.first.size(); ++index) { - sum += results.first[index].points * factors[index]; + final.wins += results.first[index].wins * factors[index]; + final.draws += results.first[index].draws * factors[index]; + final.losses += results.first[index].losses * factors[index]; + final.points += results.first[index].points * factors[index]; } - return(sum / std::accumulate(factors.begin(), factors.end(), 0.) / (long double)results.second); + auto factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); + final.wins /= factor_sum * (long double)results.second; + final.draws /= factor_sum * (long double)results.second; + final.losses /= factor_sum * (long double)results.second; + final.points /= factor_sum * (long double)results.second; + return final; } //------------------------------------------------------------------------------ volatile unsigned thread_num_iterations{0}; // written by threads @@ -451,7 +459,8 @@ void thread_evaluate(boost::barrier& main_barrier, //------------------------------------------------------------------------------ void print_score_info(const std::pair> , unsigned>& results, std::vector& factors) { - std::cout << compute_score(results, factors) << " ("; + auto final = compute_score(results, factors); + std::cout << final.points << " ("; for(auto val: results.first) { std::cout << val.points << " "; @@ -461,35 +470,21 @@ void print_score_info(const std::pair> , unsigned> //------------------------------------------------------------------------------ void print_results(const std::pair> , unsigned>& results, std::vector& factors) { - Results final{0, 0, 0, 0}; - for(unsigned index(0); index < results.first.size(); ++index) - { - final.wins += results.first[index].wins * factors[index]; - final.draws += results.first[index].draws * factors[index]; - final.losses += results.first[index].losses * factors[index]; - final.points += results.first[index].points * factors[index]; - } - final.wins /= std::accumulate(factors.begin(), factors.end(), 0.) * (long double)results.second; - final.draws /= std::accumulate(factors.begin(), factors.end(), 0.) * (long double)results.second; - final.losses /= std::accumulate(factors.begin(), factors.end(), 0.) * (long double)results.second; - final.points /= std::accumulate(factors.begin(), factors.end(), 0.) * (long double)results.second; + auto final = compute_score(results, factors); - std::cout << "win%: " << final.wins * 100.0 << " ("; + std::cout << (optimization_mode == OptimizationMode::raid ? "kill%: " : "win%: ") << final.wins * 100.0 << " ("; for(auto val: results.first) { std::cout << val.wins << " "; } std::cout << "/ " << results.second << ")" << std::endl; - if(optimization_mode != OptimizationMode::raid) + std::cout << "stall%: " << final.draws * 100.0 << " ("; + for(auto val: results.first) { - std::cout << "draw%: " << final.draws * 100.0 << " ("; - for(auto val: results.first) - { - std::cout << val.draws << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; + std::cout << val.draws << " "; } + std::cout << "/ " << results.second << ")" << std::endl; std::cout << "loss%: " << final.losses * 100.0 << " ("; for(auto val: results.first) @@ -498,20 +493,46 @@ void print_results(const std::pair> , unsigned>& r } std::cout << "/ " << results.second << ")" << std::endl; - if(optimization_mode == OptimizationMode::raid) + switch(optimization_mode) { - std::cout << "ard: " << final.points << " ("; - for(auto val: results.first) - { - std::cout << val.points << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; + case OptimizationMode::raid: + std::cout << "ard: " << final.points << " ("; + for(auto val: results.first) + { + std::cout << val.points << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; + break; + case OptimizationMode::achievement: + std::cout << "achievement%: " << final.points * 100.0 << " ("; + for(auto val: results.first) + { + std::cout << val.points << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; + break; + default: + break; } } //------------------------------------------------------------------------------ -void print_deck_inline(const long double score, const Card *commander, std::vector cards, bool is_ordered) +void print_deck_inline(const Results score, const Card *commander, std::vector cards, bool is_ordered) { - std::cout << score << ": " << commander->m_name; + switch(optimization_mode) + { + case OptimizationMode::raid: + std::cout << "(" << score.wins * 100.0 << "% kill, " << score.draws * 100.0 << "% stall) "; + break; + case OptimizationMode::achievement: + std::cout << "(" << score.wins * 100.0 << "% win) "; + break; + case OptimizationMode::defense: + std::cout << "(" << score.draws * 100.0 << "% stall) "; + break; + default: + break; + } + std::cout << score.points << ": " << commander->m_name; if(!is_ordered) { std::sort(cards.begin(), cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); @@ -546,8 +567,8 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map, unsigned> evaluated_decks{{d1->card_ids>(), num_iterations}}; // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; @@ -560,7 +581,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map(max_deck_len, d1->cards.size() + 1)) + for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score.points - target_score < -1e-9; slot_i = (slot_i + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(card_marks.count(slot_i)) { continue; } if(deck_has_been_improved) @@ -582,11 +603,11 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map best_score) + if(current_score.points > best_score.points) { // Then update best score/commander, print stuff best_score = current_score; @@ -634,11 +655,11 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map best_score) + if(current_score.points > best_score.points) { std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards, false) << " " << card_id_name(slot_i < best_cards.size() ? best_cards[slot_i] : NULL) << " -> " << card_id_name(card_candidate) << ": "; @@ -655,7 +676,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcards = best_cards; - if(best_score - target_score > -1e-9) { break; } + if(best_score.points - target_score > -1e-9) { break; } } } unsigned simulations = 0; @@ -670,8 +691,8 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std { auto results = proc.evaluate(num_iterations); print_score_info(results, proc.factors); - long double current_score = compute_score(results, proc.factors); - long double best_score = current_score; + auto current_score = compute_score(results, proc.factors); + auto best_score = current_score; std::map, unsigned> evaluated_decks{{d1->card_ids>(), num_iterations}}; // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; @@ -684,7 +705,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; - for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score - target_score < -1e-9; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) + for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score.points - target_score < -1e-9; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(deck_has_been_improved) { @@ -695,7 +716,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std { for(const Card* commander_candidate: proc.cards.player_commanders) { - if(best_score - target_score > -1e-9) { break; } + if(best_score.points - target_score > -1e-9) { break; } // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); if(commander_candidate->m_name == best_commander->m_name) { continue; } @@ -706,11 +727,11 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std if(evaluated_decks.count(cur_deck) == 0) { // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score); + auto compare_results = proc.compare(num_iterations, best_score.points); current_score = compute_score(compare_results, proc.factors); evaluated_decks[cur_deck] = compare_results.second; // Is it better ? - if(current_score > best_score) + if(current_score.points > best_score.points) { // Then update best score/commander, print stuff best_score = current_score; @@ -759,11 +780,11 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std if(evaluated_decks.count(cur_deck) == 0) { // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score); + auto compare_results = proc.compare(num_iterations, best_score.points); current_score = compute_score(compare_results, proc.factors); evaluated_decks[cur_deck] = compare_results.second; // Is it better ? - if(current_score > best_score) + if(current_score.points > best_score.points) { // Then update best score/slot, print stuff std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards, true) << " " << from_slot << " " << card_id_name(from_slot < best_cards.size() ? best_cards[from_slot] : NULL) << @@ -799,7 +820,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std } d1->cards = best_cards; } - if(best_score - target_score > -1e-9) { break; } + if(best_score.points - target_score > -1e-9) { break; } } } unsigned simulations = 0; @@ -873,7 +894,7 @@ class Combination }; //------------------------------------------------------------------------------ static unsigned total_num_combinations_test(0); -inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsigned num_iterations, const std::vector& card_indices, std::vector& cards, const Card* commander, Process& proc, long double& best_score, boost::optional& best_deck) +inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsigned num_iterations, const std::vector& card_indices, std::vector& cards, const Card* commander, Process& proc, Results& best_score, boost::optional& best_deck) { assert(card_indices.size() > 0); assert(card_indices.size() <= deck_size); @@ -909,9 +930,9 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig Deck deck{}; deck.set(commander, deck_cards); (*dynamic_cast(proc.att_deck)) = deck; - auto new_results = proc.compare(num_iterations, best_score); - long double new_score = compute_score(new_results, proc.factors); - if(new_score > best_score) + auto new_results = proc.compare(num_iterations, best_score.points); + auto new_score = compute_score(new_results, proc.factors); + if(new_score.points > best_score.points) { best_score = new_score; best_deck = deck; @@ -949,9 +970,9 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig Deck deck{}; deck.set(commander, deck_cards); *proc.att_deck = deck; - auto new_results = proc.compare(num_iterations, best_score); - long double new_score = compute_score(new_results, proc.factors); - if(new_score > best_score) + auto new_results = proc.compare(num_iterations, best_score.points); + auto new_score = compute_score(new_results, proc.factors); + if(new_score.points > best_score.points) { best_score = new_score; best_deck = deck; @@ -983,7 +1004,7 @@ void exhaustive_k(unsigned num_iterations, unsigned var_k, Process& proc, std::m Combination cardIndices(var_n, var_k); const std::vector& indices = cardIndices.getIndices(); bool finished(false); - long double best_score{0}; + Results best_score{0, 0, 0, 0}; boost::optional best_deck; unsigned num_cards = ((Deck*)proc.att_deck)->cards.size(); while(!finished) @@ -1118,11 +1139,24 @@ int main(int argc, char** argv) std::vector > todo; for(int argIndex(3); argIndex < argc; ++argIndex) { - if(strcmp(argv[argIndex], "-A") == 0) + if(strcmp(argv[argIndex], "win") == 0) // for test + { + optimization_mode = OptimizationMode::winrate; + } + else if(strcmp(argv[argIndex], "raid") == 0) // for test + { + optimization_mode = OptimizationMode::raid; + } + else if(strcmp(argv[argIndex], "defense") == 0) + { + optimization_mode = OptimizationMode::defense; + } + else if(strcmp(argv[argIndex], "-A") == 0) { try { read_achievement(decks, cards, achievement, argv[argIndex + 1]); + optimization_mode = OptimizationMode::achievement; } catch(const std::runtime_error& e) { @@ -1135,10 +1169,6 @@ int main(int argc, char** argv) { keep_commander = true; } - else if(strcmp(argv[argIndex], "defense") == 0) - { - optimization_mode = OptimizationMode::defense; - } else if(strcmp(argv[argIndex], "-e") == 0) { std::string arg_effect(argv[argIndex + 1]); @@ -1176,11 +1206,6 @@ int main(int argc, char** argv) { att_strategy = DeckStrategy::ordered; } - else if(strcmp(argv[argIndex], "raid") == 0) // for test - { - optimization_mode = OptimizationMode::raid; - turn_limit = 30; - } else if(strcmp(argv[argIndex], "exact-ordered") == 0) { att_strategy = DeckStrategy::exact_ordered; @@ -1320,10 +1345,9 @@ int main(int argc, char** argv) return(1); } } - if(def_deck->decktype == DeckType::raid) + if(def_deck->decktype == DeckType::raid && optimization_mode == OptimizationMode::none) { optimization_mode = OptimizationMode::raid; - turn_limit = 30; } // Set quest effect: Effect this_effect = def_deck->effect; @@ -1352,6 +1376,15 @@ int main(int argc, char** argv) std::cout << "Effect: " << effect_names[effect] << std::endl; } + // set "auto determined" default values + if (optimization_mode == OptimizationMode::none) + { + optimization_mode = OptimizationMode::winrate; + } + if (turn_limit == 0) + { + turn_limit = optimization_mode == OptimizationMode::raid ? 30 : 50; + } if (target_score < 0) { target_score = optimization_mode == OptimizationMode::raid ? 250 : 1; @@ -1404,8 +1437,8 @@ int main(int argc, char** argv) { debug_str.clear(); auto results = p.evaluate(1); - long double score = compute_score(results, p.factors); - if(score >= std::get<0>(op) && score <= std::get<1>(op)) { + auto score = compute_score(results, p.factors); + if(score.points >= std::get<0>(op) && score.points <= std::get<1>(op)) { std::cout << debug_str << std::flush; print_results(results, p.factors); break; diff --git a/xml.cpp b/xml.cpp index 2551d94c..420967b3 100644 --- a/xml.cpp +++ b/xml.cpp @@ -611,10 +611,6 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement } else if(num_turns) { - if(comparator == less_equal) - { - turn_limit = atoi(num_turns->value()); - } achievement.misc_req[AchievementMiscReq::turns] = achievement.req_counter.size(); achievement.req_counter.emplace_back(atoi(num_turns->value()), comparator); std::cout << " " << achievement_misc_req_names[AchievementMiscReq::turns] << achievement.req_counter.back().str() << std::endl; From 8fb2323fbfbc9240a8e8f88eb01a1b6b2d8b5cb2 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 3 Aug 2013 02:06:12 +0800 Subject: [PATCH 169/406] Fix bug: Frozen assault should carry Blitz status. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 36ffebc6..dbc5003a 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1069,7 +1069,7 @@ void turn_start_phase(Field* fd) CardStatus& status(assaults[index]); status.m_index = index; status.m_augmented = 0; - status.m_blitzing = status.m_blitzing && status.m_jammed; + status.m_blitzing = status.m_blitzing && is_jammed(&status); status.m_chaosed = false; status.m_enfeebled = 0; status.m_frozen = false; From 67d473ed316cc4ff6c450187c24639b8e7db687d Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 3 Aug 2013 23:39:11 +0800 Subject: [PATCH 170/406] Add option +stdev. --- sim.cpp | 16 ++++++++-------- sim.h | 4 +++- tyrant_optimize.cpp | 43 +++++++++++++++++++++++++++++-------------- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/sim.cpp b/sim.cpp index dbc5003a..c75753ba 100644 --- a/sim.cpp +++ b/sim.cpp @@ -572,7 +572,7 @@ void turn_start_phase(Field* fd); void evaluate_legion(Field* fd); bool check_and_perform_refresh(Field* fd, CardStatus* src_status); // return value : (raid points) -> attacker wins, 0 -> defender wins -Results play(Field* fd) +Results play(Field* fd) { fd->players[0]->commander.m_player = 0; fd->players[1]->commander.m_player = 1; @@ -730,7 +730,7 @@ Results play(Field* fd) if(fd->players[0]->commander.m_hp == 0) { _DEBUG_MSG(1, "You lose.\n"); - return {0, 0, 1, 0}; + return {0, 0, 1, 0, 0}; } // you win in raid if(fd->optimization_mode == OptimizationMode::raid) @@ -738,12 +738,12 @@ Results play(Field* fd) if(fd->players[1]->commander.m_hp == 0) { _DEBUG_MSG(1, "You win (boss killed).\n"); - return {1, 0, 0, 250}; + return {1, 0, 0, 250, 0}; } else { _DEBUG_MSG(1, "You win (survival).\n"); - return {0, 1, 0, std::min(fd->all_damage_to_commander, 200u)}; + return {0, 1, 0, std::min(fd->all_damage_to_commander, 200u), 0}; } } // you win @@ -752,7 +752,7 @@ Results play(Field* fd) if (fd->optimization_mode == OptimizationMode::achievement && !made_achievement) { _DEBUG_MSG(1, "You win but no achievement.\n"); - return {1, 0, 0, 0}; + return {1, 0, 0, 0, 0}; } _DEBUG_MSG(1, "You win.\n"); #if 0 @@ -764,17 +764,17 @@ Results play(Field* fd) } return {1, 0, 0, 10 + (speedy ? 5 : 0) + (fd->gamemode == surge ? 20 : 0) + fd->points_since_last_decision, 0}; #endif - return {1, 0, 0, 1}; + return {1, 0, 0, 1, 0}; } if (fd->turn > turn_limit) { _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); - return {0, 1, 0, fd->optimization_mode == OptimizationMode::defense}; + return {0, 1, 0, fd->optimization_mode == OptimizationMode::defense, 0}; } // Huh? How did we get here? assert(false); - return {0, 0, 0, 0}; + return {0, 0, 0, 0, 0}; } // Roll a coin in case an Activation skill has 50% chance to proc. diff --git a/sim.h b/sim.h index 48bed538..c9fe6f6a 100644 --- a/sim.h +++ b/sim.h @@ -30,6 +30,7 @@ struct Results result_type draws; result_type losses; result_type points; + result_type sq_points; template Results& operator+=(const Results& other) { @@ -37,12 +38,13 @@ struct Results draws += other.draws; losses += other.losses; points += other.points; + sq_points += other.points * other.points; return *this; } }; void fill_skill_table(); -Results play(Field* fd); +Results play(Field* fd); void modify_cards(Cards& cards, enum Effect effect); // Pool-based indexed storage. //---------------------- Pool-based indexed storage ---------------------------- diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 082ebceb..3e3e5964 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -42,6 +42,7 @@ namespace { gamemode_t gamemode{fight}; OptimizationMode optimization_mode{OptimizationMode::none}; long double target_score{-1e9}; + bool show_stdev{false}; } using namespace std::placeholders; @@ -214,26 +215,28 @@ bool suitable_commander(const Card* card) return(true); } //------------------------------------------------------------------------------ -Results compute_score(const std::pair> , unsigned>& results, std::vector& factors) +Results compute_score(const std::pair> , unsigned>& results, std::vector& factors) { - Results final{0, 0, 0, 0}; + Results final{0, 0, 0, 0, 0}; for(unsigned index(0); index < results.first.size(); ++index) { final.wins += results.first[index].wins * factors[index]; final.draws += results.first[index].draws * factors[index]; final.losses += results.first[index].losses * factors[index]; final.points += results.first[index].points * factors[index]; + final.sq_points += results.first[index].sq_points * factors[index] * factors[index]; } auto factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); final.wins /= factor_sum * (long double)results.second; final.draws /= factor_sum * (long double)results.second; final.losses /= factor_sum * (long double)results.second; final.points /= factor_sum * (long double)results.second; + final.sq_points /= factor_sum * factor_sum * (long double)results.second; return final; } //------------------------------------------------------------------------------ volatile unsigned thread_num_iterations{0}; // written by threads -std::vector> thread_results; // written by threads +std::vector> thread_results; // written by threads volatile unsigned thread_total{0}; // written by threads volatile long double thread_prev_score{0.0}; volatile bool thread_compare{false}; @@ -291,15 +294,15 @@ struct SimulationData } } - inline std::vector> evaluate() + inline std::vector> evaluate() { - std::vector> res; + std::vector> res; for(Hand* def_hand: def_hands) { att_hand.reset(re); def_hand->reset(re); Field fd(re, cards, att_hand, *def_hand, gamemode, optimization_mode, effect, achievement); - Results result(play(&fd)); + Results result(play(&fd)); res.emplace_back(result); } return(res); @@ -359,10 +362,10 @@ class Process for(auto data: threads_data) { delete(data); } } - std::pair> , unsigned> evaluate(unsigned num_iterations) + std::pair> , unsigned> evaluate(unsigned num_iterations) { thread_num_iterations = num_iterations; - thread_results = std::vector>(def_decks.size()); + thread_results = std::vector>(def_decks.size()); thread_total = 0; thread_compare = false; // unlock all the threads @@ -372,10 +375,10 @@ class Process return(std::make_pair(thread_results, thread_total)); } - std::pair> , unsigned> compare(unsigned num_iterations, long double prev_score) + std::pair> , unsigned> compare(unsigned num_iterations, long double prev_score) { thread_num_iterations = num_iterations; - thread_results = std::vector>(def_decks.size()); + thread_results = std::vector>(def_decks.size()); thread_total = 0; thread_prev_score = prev_score; thread_compare = true; @@ -412,7 +415,7 @@ void thread_evaluate(boost::barrier& main_barrier, { --thread_num_iterations; //! shared_mutex.unlock(); //>>>> - std::vector> result{sim.evaluate()}; + std::vector> result{sim.evaluate()}; shared_mutex.lock(); //<<<< std::vector thread_score_local(thread_results.size(), 0u); //! for(unsigned index(0); index < result.size(); ++index) @@ -457,7 +460,7 @@ void thread_evaluate(boost::barrier& main_barrier, } } //------------------------------------------------------------------------------ -void print_score_info(const std::pair> , unsigned>& results, std::vector& factors) +void print_score_info(const std::pair> , unsigned>& results, std::vector& factors) { auto final = compute_score(results, factors); std::cout << final.points << " ("; @@ -468,7 +471,7 @@ void print_score_info(const std::pair> , unsigned> std::cout << "/ " << results.second << ")" << std::endl; } //------------------------------------------------------------------------------ -void print_results(const std::pair> , unsigned>& results, std::vector& factors) +void print_results(const std::pair> , unsigned>& results, std::vector& factors) { auto final = compute_score(results, factors); @@ -502,6 +505,10 @@ void print_results(const std::pair> , unsigned>& r std::cout << val.points << " "; } std::cout << "/ " << results.second << ")" << std::endl; + if (show_stdev) + { + std::cout << "stdev: " << sqrt(final.sq_points - final.points * final.points) << std::endl; + } break; case OptimizationMode::achievement: std::cout << "achievement%: " << final.points * 100.0 << " ("; @@ -521,7 +528,11 @@ void print_deck_inline(const Results score, const Card *commander, switch(optimization_mode) { case OptimizationMode::raid: - std::cout << "(" << score.wins * 100.0 << "% kill, " << score.draws * 100.0 << "% stall) "; + std::cout << "(" << score.wins * 100.0 << "% kill, " << score.draws * 100.0 << "% stall"; + if (show_stdev) { + std::cout << ", " << sqrt(score.sq_points - score.points * score.points) << " stdev"; + } + std::cout << ") "; break; case OptimizationMode::achievement: std::cout << "(" << score.wins * 100.0 << "% win) "; @@ -1241,6 +1252,10 @@ int main(int argc, char** argv) turn_limit = atoi(argv[argIndex+1]); argIndex += 1; } + else if(strcmp(argv[argIndex], "+stdev") == 0) + { + show_stdev = true; + } else if(strcmp(argv[argIndex], "+v") == 0) { ++ debug_print; From 584f8ad2e94a667623dcde688d5427e24f0e047d Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 4 Aug 2013 07:09:19 +0800 Subject: [PATCH 171/406] Display win rate in percentage instead of fraction. --- sim.cpp | 4 ++-- tyrant_optimize.cpp | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/sim.cpp b/sim.cpp index c75753ba..b8213694 100644 --- a/sim.cpp +++ b/sim.cpp @@ -764,12 +764,12 @@ Results play(Field* fd) } return {1, 0, 0, 10 + (speedy ? 5 : 0) + (fd->gamemode == surge ? 20 : 0) + fd->points_since_last_decision, 0}; #endif - return {1, 0, 0, 1, 0}; + return {1, 0, 0, 100, 0}; } if (fd->turn > turn_limit) { _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); - return {0, 1, 0, fd->optimization_mode == OptimizationMode::defense, 0}; + return {0, 1, 0, fd->optimization_mode == OptimizationMode::defense ? 100ul : 0ul, 0}; } // Huh? How did we get here? diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 3e3e5964..03306429 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -445,7 +445,7 @@ void thread_evaluate(boost::barrier& main_barrier, score_accum = thread_score_local[0]; } bool compare_stop(false); - long double best_possible = (optimization_mode == OptimizationMode::raid ? 250 : 1); + long double best_possible = (optimization_mode == OptimizationMode::raid ? 250 : 100); // Get a loose (better than no) upper bound. TODO: Improve it. compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum / best_possible, 0.01) * best_possible < thread_prev_score); if(compare_stop) { @@ -466,7 +466,15 @@ void print_score_info(const std::pair> , unsigned> std::cout << final.points << " ("; for(auto val: results.first) { - std::cout << val.points << " "; + switch(optimization_mode) + { + case OptimizationMode::raid: + std::cout << val.points << " "; + break; + default: + std::cout << val.points / 100 << " "; + break; + } } std::cout << "/ " << results.second << ")" << std::endl; } @@ -511,10 +519,10 @@ void print_results(const std::pair> , unsigned>& r } break; case OptimizationMode::achievement: - std::cout << "achievement%: " << final.points * 100.0 << " ("; + std::cout << "achievement%: " << final.points << " ("; for(auto val: results.first) { - std::cout << val.points << " "; + std::cout << val.points / 100 << " "; } std::cout << "/ " << results.second << ")" << std::endl; break; @@ -1282,7 +1290,7 @@ int main(int argc, char** argv) else if(strcmp(argv[argIndex], "debuguntil") == 0) { // output the debug info for the first battle that min_score <= score <= max_score. - // E.g., 0 0: lose; 1 1: win (non-raid); 100 999: at least 100 damage (raid). + // E.g., 0 0: lose; 100 100: win (non-raid); 150 250: at least 150 damage (raid). todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), debuguntil)); argIndex += 2; } @@ -1402,7 +1410,7 @@ int main(int argc, char** argv) } if (target_score < 0) { - target_score = optimization_mode == OptimizationMode::raid ? 250 : 1; + target_score = optimization_mode == OptimizationMode::raid ? 250 : 100; } Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); From eaf749f3a48533464c31e7424f11deff35fcb469 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 4 Aug 2013 12:06:01 +0800 Subject: [PATCH 172/406] Automatically upgrade owned cards during hill climbing. --- card.h | 11 +++++-- cards.cpp | 71 +++++++++++++++++++++++--------------------- cards.h | 2 +- read.cpp | 4 +-- sim.cpp | 4 --- sim.h | 5 ++++ tyrant.h | 2 +- tyrant_optimize.cpp | 72 ++++++++++++++++++++++++++------------------- xml.cpp | 22 +++++++++----- 9 files changed, 112 insertions(+), 81 deletions(-) diff --git a/card.h b/card.h index fa2002c0..6c85d272 100644 --- a/card.h +++ b/card.h @@ -30,6 +30,7 @@ class Card m_flying(false), m_fusion(false), m_health(0), + m_hidden(0), m_id(0), m_immobilize(false), m_intercept(false), @@ -40,9 +41,11 @@ class Card m_pierce(0), m_poison(0), m_poison_oa(0), + m_proto_id(0), m_rarity(1), m_refresh(false), m_regenerate(0), + m_replace(0), m_set(0), m_siphon(0), m_split(false), @@ -73,7 +76,7 @@ class Card unsigned m_antiair; unsigned m_armored; unsigned m_attack; - unsigned m_base_id; // Cards sharing the same name share a unique base id. (for "unique" check) + unsigned m_base_id; // The id of the original card if a card is unique and alt/upgraded. The own id of the card otherwise. unsigned m_berserk; unsigned m_berserk_oa; bool m_blitz; @@ -91,6 +94,7 @@ class Card bool m_flying; bool m_fusion; unsigned m_health; + unsigned m_hidden; unsigned m_id; bool m_immobilize; bool m_intercept; @@ -101,10 +105,12 @@ class Card unsigned m_pierce; unsigned m_poison; unsigned m_poison_oa; + unsigned m_proto_id; // The id of the prototype card (before upgraded) for an upgraded card. 0 otherwise. unsigned m_rarity; bool m_refresh; unsigned m_regenerate; - int m_set; + unsigned m_replace; + unsigned m_set; unsigned m_siphon; bool m_split; bool m_stun; @@ -113,6 +119,7 @@ class Card bool m_swipe; bool m_tribute; bool m_unique; + unsigned m_upgraded_id; // The id of the upgraded card for an upgradable card. 0 otherwise. unsigned m_valor; bool m_wall; std::vector m_skills; diff --git a/cards.cpp b/cards.cpp index 0c787787..ccb0d888 100644 --- a/cards.cpp +++ b/cards.cpp @@ -88,59 +88,62 @@ void Cards::organize() } cards_by_id[card->m_id] = card; // Card available to players - if(card->m_set != -1) + if(card->m_set != 0) { player_cards.push_back(card); switch(card->m_type) { - case CardType::commander: { - player_commanders.push_back(card); - break; - } - case CardType::assault: { - player_assaults.push_back(card); - break; - } - case CardType::structure: { - player_structures.push_back(card); - break; - } - case CardType::action: { - player_actions.push_back(card); - break; - } - case CardType::num_cardtypes: { - throw card->m_type; - break; - } + case CardType::commander: { + player_commanders.push_back(card); + break; + } + case CardType::assault: { + player_assaults.push_back(card); + break; + } + case CardType::structure: { + player_structures.push_back(card); + break; + } + case CardType::action: { + player_actions.push_back(card); + break; + } + case CardType::num_cardtypes: { + throw card->m_type; + break; + } } std::string simple_name{simplify_name(card->m_name)}; - if(player_cards_by_name.find(simple_name) == player_cards_by_name.end()) + auto card_itr = player_cards_by_name.find({simple_name, card->m_hidden}); + if(card_itr == player_cards_by_name.end() || card_itr->second->m_id == card->m_replace) { - player_cards_by_name[simple_name] = card; + player_cards_by_name[{simple_name, card->m_hidden}] = card; } } } for(Card* card: cards) { // generate abbreviations - for(auto&& abbr_name : get_abbreviations(card->m_name)) + if(card->m_hidden == 0) { - if(abbr_name.length() > 1 && player_cards_by_name.find(abbr_name) == player_cards_by_name.end()) + for(auto&& abbr_name : get_abbreviations(card->m_name)) { - player_cards_abbr[abbr_name] = card->m_name; + if(abbr_name.length() > 1 && player_cards_by_name.find({abbr_name, 0}) == player_cards_by_name.end()) + { + player_cards_abbr[abbr_name] = card->m_name; + } } } - // update base_id - std::string base_name{simplify_name(card->m_name)}; + + // update proto_id and upgraded_id if(card->m_set == 5002) { - base_name.erase(base_name.size() - 1); - } - auto card_itr = player_cards_by_name.find(base_name); - if(card_itr != player_cards_by_name.end() && card_itr->second != card) - { - card->m_base_id = card_itr->second->m_id; + std::string proto_name{simplify_name(card->m_name)}; + proto_name.erase(proto_name.size() - 1); // remove suffix "*" + Card * proto_card = player_cards_by_name[{proto_name, card->m_hidden}]; + card->m_proto_id = proto_card->m_id; + proto_card->m_upgraded_id = card->m_id; } } } diff --git a/cards.h b/cards.h index 3a02d766..bf7976a6 100644 --- a/cards.h +++ b/cards.h @@ -14,7 +14,7 @@ struct Cards std::vector cards; std::map cards_by_id; std::vector player_cards; - std::map player_cards_by_name; + std::map, Card*> player_cards_by_name; std::vector player_commanders; std::vector player_assaults; std::vector player_structures; diff --git a/read.cpp b/read.cpp index c8463d4f..a6e4768a 100644 --- a/read.cpp +++ b/read.cpp @@ -112,7 +112,7 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ } simple_name = simplify_name(abbr_it->second); } - auto card_it = cards.player_cards_by_name.find(simple_name); + auto card_it = cards.player_cards_by_name.find({simple_name, 0}); auto card_id_iter = advance_until(simple_name.begin(), simple_name.end(), [](char c){return(c=='[');}); if(card_it != cards.player_cards_by_name.end()) { @@ -180,7 +180,7 @@ unsigned read_card_abbrs(Cards& cards, const std::string& filename) continue; } abbr_string_iter = advance_until(abbr_string_iter + 1, abbr_string.end(), [](const char& c){return(c != ' ');}); - if(cards.player_cards_by_name.find(abbr_name) != cards.player_cards_by_name.end()) + if(cards.player_cards_by_name.find({abbr_name, 0}) != cards.player_cards_by_name.end()) { std::cerr << "Warning in card abbreviation file " << filename << " at line " << num_line << ": ignored because the name has been used by an existing card." << std::endl; } diff --git a/sim.cpp b/sim.cpp index b8213694..1b59d293 100644 --- a/sim.cpp +++ b/sim.cpp @@ -143,10 +143,6 @@ inline void CardStatus::set(const Card& card) m_step = CardStep::none; } //------------------------------------------------------------------------------ -inline unsigned safe_minus(unsigned x, unsigned y) -{ - return(x - std::min(x, y)); -} inline int attack_power(CardStatus* att) { return(safe_minus(att->m_card->m_attack + att->m_berserk + att->m_rallied, att->m_weakened)); diff --git a/sim.h b/sim.h index c9fe6f6a..cd7aab2c 100644 --- a/sim.h +++ b/sim.h @@ -22,6 +22,11 @@ extern bool debug_line; extern std::string debug_str; extern unsigned turn_limit; +inline unsigned safe_minus(unsigned x, unsigned y) +{ + return(x - std::min(x, y)); +} + //---------------------- Represent Simulation Results ---------------------------- template struct Results diff --git a/tyrant.h b/tyrant.h index e841d82b..a3470546 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.1.1" +#define TYRANT_OPTIMIZER_VERSION "1.1.2" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 03306429..cfc6ec09 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -41,6 +41,7 @@ namespace { gamemode_t gamemode{fight}; OptimizationMode optimization_mode{OptimizationMode::none}; + bool auto_upgrade_cards{true}; long double target_score{-1e9}; bool show_stdev{false}; } @@ -157,25 +158,9 @@ std::set top_commanders{ 1225, // Yuletta }; //------------------------------------------------------------------------------ -bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) +bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card, const Cards & cards) { assert(card->m_type != CardType::commander); - if(use_owned_cards) - { - if(owned_cards.find(card->m_id) == owned_cards.end()) { return(false); } - else - { - unsigned num_in_deck{0}; - for(unsigned i(0); i < deck.cards.size(); ++i) - { - if(i != slot && deck.cards[i]->m_id == card->m_id) - { - ++num_in_deck; - } - } - if(owned_cards.find(card->m_id)->second <= num_in_deck) { return(false); } - } - } if(card->m_rarity == 4) // legendary - 1 per deck { for(unsigned i(0); i < deck.cards.size(); ++i) @@ -196,23 +181,45 @@ bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) } } } - return(true); + if(!use_owned_cards) + { + return(true); + } + + unsigned num_in_deck{0}, num_proto_in_deck{0}, num_upgraded_in_deck{0}; + for(unsigned i(0); i < deck.cards.size(); ++i) + { + if(i == slot) { continue; } + num_in_deck += (deck.cards[i]->m_id == card->m_id); + num_proto_in_deck += (deck.cards[i]->m_id == card->m_proto_id); + num_upgraded_in_deck += (deck.cards[i]->m_id == card->m_upgraded_id); + } + unsigned num_owned{owned_cards.find(card->m_id)->second}; + auto owned_upgraded_iter = owned_cards.find(card->m_upgraded_id); + if(num_owned >= num_in_deck + 2 * safe_minus(num_upgraded_in_deck, owned_upgraded_iter == owned_cards.end() ? 0 : owned_upgraded_iter->second) + 1) { return(true); } + // try upgrade + if(auto_upgrade_cards && card->m_proto_id > 0) + { + auto owned_proto_iter = owned_cards.find(card->m_proto_id); + if(owned_proto_iter != owned_cards.end() && num_owned * 2 + owned_proto_iter->second >= num_in_deck * 2 + num_proto_in_deck + 2) { return(true); } + } + return(false); } -bool suitable_commander(const Card* card) +bool suitable_commander(const Card* card, const Cards & cards) { assert(card->m_type == CardType::commander); if(use_owned_cards) { - auto owned_iter = owned_cards.find(card->m_id); - if(owned_iter == owned_cards.end()) { return(false); } - else + if(owned_cards.find(card->m_id)->second >= 1) { return(true); } + // try upgrade + if(auto_upgrade_cards && card->m_proto_id > 0) { - if(owned_iter->second <= 0) { return(false); } + auto owned_iter = owned_cards.find(card->m_proto_id); + if(owned_iter != owned_cards.end() && owned_iter->second >= 2) { return(true); } } } -// if(top_commanders.find(card->m_id) == top_commanders.end()) { return(false); } // XXX - return(true); + return(false); } //------------------------------------------------------------------------------ Results compute_score(const std::pair> , unsigned>& results, std::vector& factors) @@ -615,7 +622,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapm_type == CardType::commander); if(commander_candidate->m_name == best_commander->m_name) { continue; } - if(!suitable_commander(commander_candidate)) { continue; } + if(!suitable_commander(commander_candidate, proc.cards)) { continue; } // Place it in the deck d1->commander = commander_candidate; auto &&cur_deck = d1->card_ids>(); @@ -653,7 +660,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapm_type != CardType::commander); if(slot_i < best_cards.size() && card_candidate->m_name == best_cards[slot_i]->m_name) { continue; } - if(!suitable_non_commander(*d1, slot_i, card_candidate)) { continue; } + if(!suitable_non_commander(*d1, slot_i, card_candidate, proc.cards)) { continue; } // Place it in the deck if(slot_i == d1->cards.size()) { @@ -739,7 +746,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); if(commander_candidate->m_name == best_commander->m_name) { continue; } - if(!suitable_commander(commander_candidate)) { continue; } + if(!suitable_commander(commander_candidate, proc.cards)) { continue; } // Place it in the deck d1->commander = commander_candidate; auto &&cur_deck = d1->card_ids>(); @@ -781,7 +788,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std { // Various checks to check if the card is accepted if(to_slot < best_cards.size() && card_candidate->m_name == best_cards[to_slot]->m_name) { continue; } - if(!suitable_non_commander(*d1, from_slot, card_candidate)) { continue; } + if(!suitable_non_commander(*d1, from_slot, card_candidate, proc.cards)) { continue; } // Place it in the deck if(from_slot < d1->cards.size()) { @@ -1038,7 +1045,7 @@ void exhaustive_k(unsigned num_iterations, unsigned var_k, Process& proc, std::m for(unsigned commanderIndex(0); commanderIndex < proc.cards.player_commanders.size() && !finished; ++commanderIndex) { const Card* commander(proc.cards.player_commanders[commanderIndex]); - if(!suitable_commander(commander)) { continue; } + if(!suitable_commander(commander, proc.cards)) { continue; } try_all_ratio_combinations(num_cards, var_k, num_iterations, indices, ass_structs, commander, proc, best_score, best_deck); } } @@ -1101,6 +1108,7 @@ void usage(int argc, char** argv) " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" " -s: use surge (default is fight).\n" " -t : set the number of threads, default is 4.\n" + " -u: do not upgrade owned cards during hill climbing. (by default, upgrade owned cards when needed)\n" " target : stop hill climbing as soon as the score reaches .\n" " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n" "\n" @@ -1260,6 +1268,10 @@ int main(int argc, char** argv) turn_limit = atoi(argv[argIndex+1]); argIndex += 1; } + else if(strcmp(argv[argIndex], "-u") == 0) + { + auto_upgrade_cards = false; + } else if(strcmp(argv[argIndex], "+stdev") == 0) { show_stdev = true; diff --git a/xml.cpp b/xml.cpp index 420967b3..6717ed10 100644 --- a/xml.cpp +++ b/xml.cpp @@ -150,7 +150,7 @@ void read_cards(Cards& cards) return; } - bool mission_only(false); + bool ai_only(false); unsigned nb_cards(0); for(xml_node<>* card = root->first_node(); card; @@ -162,25 +162,30 @@ void read_cards(Cards& cards) assert(id_node); unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(card->first_node("name")); + xml_node<>* hidden_node(card->first_node("hidden")); + unsigned hidden(hidden_node ? atoi(hidden_node->value()) : 0); + xml_node<>* replace_node(card->first_node("replace")); + unsigned replace_id(replace_node ? atoi(replace_node->value()) : 0); xml_node<>* attack_node(card->first_node("attack")); xml_node<>* health_node(card->first_node("health")); xml_node<>* cost_node(card->first_node("cost")); xml_node<>* unique_node(card->first_node("unique")); + xml_node<>* base_card_node(card->first_node("base_card")); + unsigned base_card_id(base_card_node ? atoi(base_card_node->value()) : id); xml_node<>* rarity_node(card->first_node("rarity")); xml_node<>* type_node(card->first_node("type")); xml_node<>* set_node(card->first_node("set")); - int set(set_node ? atoi(set_node->value()) : -1); - mission_only = set == -1; - if((mission_only || set >= 0) && name_node && rarity_node) + int set(set_node ? atoi(set_node->value()) : 0); + ai_only = set == 0; + if((ai_only || set >= 0) && name_node && rarity_node) { - if(!mission_only) + if(!ai_only) { nb_cards++; sets_counts[set]++; } Card* c(new Card()); c->m_id = id; - c->m_base_id = id; c->m_name = name_node->value(); // So far, commanders have attack_node (value == 0) if(id < 1000) @@ -195,10 +200,13 @@ void read_cards(Cards& cards) { c->m_type = CardType::assault; } else { c->m_type = cost_node ? (attack_node ? CardType::assault : CardType::structure) : (health_node ? CardType::commander : CardType::action); } + c->m_hidden = hidden; + c->m_replace = replace_id; if(attack_node) { c->m_attack = atoi(attack_node->value()); } if(health_node) { c->m_health = atoi(health_node->value()); } if(cost_node) { c->m_delay = atoi(cost_node->value()); } if(unique_node) { c->m_unique = true; } + c->m_base_id = base_card_id; c->m_rarity = atoi(rarity_node->value()); unsigned type(type_node ? atoi(type_node->value()) : 0); c->m_faction = map_to_faction(type); @@ -463,7 +471,7 @@ void read_quests(Decks& decks, const Cards& cards, std::string filename) unsigned id(id_node ? atoi(id_node->value()) : 0); std::string deck_name{"Step " + std::string{id_node->value()}}; xml_node<>* battleground_id_node(quest_node->first_node("battleground_id")); - int battleground_id(battleground_id_node ? atoi(battleground_id_node->value()) : -1); + int battleground_id(battleground_id_node ? atoi(battleground_id_node->value()) : 0); Deck* deck = read_deck(decks, cards, quest_node, DeckType::quest, id, deck_name); deck->effect = static_cast(battleground_id); } From fddcd288a6b112c2e91a99d85b9e54bbcf678899 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 4 Aug 2013 18:01:10 +0800 Subject: [PATCH 173/406] Claim mission reward cards as owned. Claim your initial deck cards as owned. --- cards.cpp | 2 +- deck.cpp | 4 +- deck.h | 20 ++- read.cpp | 34 +++-- read.h | 6 +- sim.cpp | 2 +- sim.h | 2 +- tyrant.h | 1 - tyrant_optimize.cpp | 322 +++++++++++++++++++------------------------- xml.cpp | 36 +++-- 10 files changed, 200 insertions(+), 229 deletions(-) diff --git a/cards.cpp b/cards.cpp index ccb0d888..072b2cf3 100644 --- a/cards.cpp +++ b/cards.cpp @@ -34,7 +34,7 @@ std::string simplify_name(const std::string& card_name) std::list get_abbreviations(const std::string& name) { std::list abbr_list; - boost::tokenizer > word_token{name, boost::char_delimiters_separator{false, " ", ""}}; + boost::tokenizer> word_token{name, boost::char_delimiters_separator{false, " ", ""}}; std::string initial; auto token_iter = word_token.begin(); for(; token_iter != word_token.end(); ++token_iter) diff --git a/deck.cpp b/deck.cpp index db9d983b..7443dd40 100644 --- a/deck.cpp +++ b/deck.cpp @@ -180,7 +180,7 @@ void Deck::resolve(const Cards& all_cards) } else { - boost::tokenizer > deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; + boost::tokenizer> deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; auto token_iter = deck_tokens.begin(); signed p = -1; for(; token_iter != deck_tokens.end(); ++token_iter) @@ -188,7 +188,7 @@ void Deck::resolve(const Cards& all_cards) std::string card_spec(*token_iter); unsigned card_id{0}; unsigned card_num{1}; - signed num_sign{0}; + char num_sign{0}; char mark{0}; try { diff --git a/deck.h b/deck.h index af0fd4a9..8767d741 100644 --- a/deck.h +++ b/deck.h @@ -43,8 +43,10 @@ struct Deck std::map card_marks; // : -1 indicating the commander. E.g, used as a mark to be kept in attacking deck when optimizing. std::deque shuffled_cards; // card id -> card order - std::map > order; - std::vector > > raid_cards; + std::map> order; + std::vector>> raid_cards; + std::vector reward_cards; + unsigned mission_req; std::string deck_string; @@ -58,7 +60,8 @@ struct Deck name(name_), strategy(strategy_), effect(Effect::none), - commander(nullptr) + commander(nullptr), + mission_req(0) { } @@ -67,12 +70,15 @@ struct Deck void set( const Card* commander_, const std::vector& cards_, - std::vector > > raid_cards_ = - std::vector > >()) + std::vector>> raid_cards_ = {}, + std::vector reward_cards_ = {}, + unsigned mission_req_ = 0) { commander = commander_; cards = std::vector(std::begin(cards_), std::end(cards_)); - raid_cards = std::vector > >(raid_cards_); + raid_cards = std::vector>>(raid_cards_); + reward_cards = std::vector(reward_cards_); + mission_req = mission_req_; } void set(const Cards& all_cards, const std::vector& ids); @@ -104,8 +110,8 @@ struct Deck struct Decks { std::list decks; + std::map, Deck*> by_type_id; std::map by_name; - std::map mission_names_by_id; std::map custom_decks; }; diff --git a/read.cpp b/read.cpp index a6e4768a..3dbf4426 100644 --- a/read.cpp +++ b/read.cpp @@ -21,13 +21,13 @@ void load_decks(Decks& decks, Cards& cards) } } -std::vector > parse_deck_list(std::string list_string) +std::vector> parse_deck_list(std::string list_string) { - std::vector > res; - boost::tokenizer > list_tokens{list_string, boost::char_delimiters_separator{false, ";", ""}}; + std::vector> res; + boost::tokenizer> list_tokens{list_string, boost::char_delimiters_separator{false, ";", ""}}; for(auto list_token = list_tokens.begin(); list_token != list_tokens.end(); ++list_token) { - boost::tokenizer > deck_tokens{*list_token, boost::char_delimiters_separator{false, ":", ""}}; + boost::tokenizer> deck_tokens{*list_token, boost::char_delimiters_separator{false, ":", ""}}; auto deck_token = deck_tokens.begin(); res.push_back(std::make_pair(*deck_token, 1.0d)); ++deck_token; @@ -80,8 +80,7 @@ template Iterator read_toke return(token_end_after_spaces); } -// num_sign = 0 if card_num is "N"; = +1 if "+N"; = -1 if "-N" -void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign, char& mark) +void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark) { static std::set recognized_abbr; auto card_spec_iter = card_spec.begin(); @@ -128,14 +127,9 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ ++card_spec_iter; if(card_spec_iter != card_spec.end()) { - if(*card_spec_iter == '+') + if(strchr("+-$", *card_spec_iter)) { - num_sign = +1; - ++card_spec_iter; - } - else if(*card_spec_iter == '-') - { - num_sign = -1; + num_sign = *card_spec_iter; ++card_spec_iter; } } @@ -173,7 +167,7 @@ unsigned read_card_abbrs(Cards& cards, const std::string& filename) continue; } std::string abbr_name; - auto abbr_string_iter = read_token(abbr_string.begin(), abbr_string.end(), [](char c){return(strchr(":", c));}, abbr_name); + auto abbr_string_iter = read_token(abbr_string.begin(), abbr_string.end(), [](char c){return(c == ':');}, abbr_name); if(abbr_string_iter == abbr_string.end() || abbr_name.empty()) { std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ", could not read the deck name.\n"; @@ -263,7 +257,7 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) return(0); } -void read_owned_cards(Cards& cards, std::map& owned_cards, const char *filename) +void read_owned_cards(Cards& cards, std::map& owned_cards, std::map& buyable_cards, const char *filename) { std::ifstream owned_file{filename}; if(!owned_file.good()) @@ -285,7 +279,7 @@ void read_owned_cards(Cards& cards, std::map& owned_cards, c { unsigned card_id{0}; unsigned card_num{1}; - signed num_sign{0}; + char num_sign{0}; char mark{0}; parse_card_spec(cards, card_spec, card_id, card_num, num_sign, mark); assert(mark == 0); @@ -293,14 +287,18 @@ void read_owned_cards(Cards& cards, std::map& owned_cards, c { owned_cards[card_id] = card_num; } - else if(num_sign > 0) + else if(num_sign == '+') { owned_cards[card_id] += card_num; } - else if(num_sign < 0) + else if(num_sign == '-') { owned_cards[card_id] = owned_cards[card_id] > card_num ? owned_cards[card_id] - card_num : 0; } + else if(num_sign == '$') + { + buyable_cards[card_id] = card_num; + } } catch(std::exception& e) { diff --git a/read.h b/read.h index 7036f747..f9845a9f 100644 --- a/read.h +++ b/read.h @@ -9,11 +9,11 @@ class Cards; class Decks; class Deck; -void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, signed& num_sign, char& mark); +void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark); void load_decks(Decks& decks, Cards& cards); -std::vector > parse_deck_list(std::string list_string); +std::vector> parse_deck_list(std::string list_string); unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename); -void read_owned_cards(Cards& cards, std::map& owned_cards, const char *filename); +void read_owned_cards(Cards& cards, std::map& owned_cards, std::map& buyable_cards, const char *filename); unsigned read_card_abbrs(Cards& cards, const std::string& filename); #endif diff --git a/sim.cpp b/sim.cpp index 1b59d293..5e5d5caf 100644 --- a/sim.cpp +++ b/sim.cpp @@ -344,7 +344,7 @@ void Hand::reset(std::mt19937& re) // Everything about how a battle plays out, except the following: // the implementation of the attack by an assault card is in the next section; // the implementation of the active skills is in the section after that. -unsigned turn_limit{0}; +unsigned turn_limit{50}; //------------------------------------------------------------------------------ inline unsigned opponent(unsigned player) { diff --git a/sim.h b/sim.h index cd7aab2c..5eb10007 100644 --- a/sim.h +++ b/sim.h @@ -202,7 +202,7 @@ class Field const Achievement& achievement; // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. - std::deque > skill_queue; + std::deque> skill_queue; std::vector killed_with_on_death; std::vector killed_with_regen; enum phase diff --git a/tyrant.h b/tyrant.h index a3470546..39588188 100644 --- a/tyrant.h +++ b/tyrant.h @@ -130,7 +130,6 @@ enum gamemode_t enum class OptimizationMode { - none, winrate, achievement, raid, diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index cfc6ec09..20f9a598 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -40,9 +40,9 @@ namespace { gamemode_t gamemode{fight}; - OptimizationMode optimization_mode{OptimizationMode::none}; + OptimizationMode optimization_mode{OptimizationMode::winrate}; bool auto_upgrade_cards{true}; - long double target_score{-1e9}; + long double target_score{100}; bool show_stdev{false}; } @@ -81,82 +81,24 @@ Deck* find_deck(Decks& decks, const Cards& cards, std::string deck_name) // Owned cards //------------------------------------------------------------------------------ std::map owned_cards; +std::map buyable_cards; bool use_owned_cards{false}; unsigned min_deck_len{1}; unsigned max_deck_len{10}; -// No raid rewards from 500 and 1k honor for ancient raids -// No very hard to get rewards (level >= 150, faction >= 13) -// No WB -// No UB -bool cheap_1(const Card* card) +void cliam_cards(const std::vector & cards) { - if(card->m_set == 2 || card->m_set == 3 || card->m_set == 4 || card->m_set == 6 || card->m_set == 5001 || card->m_set == 9000) { return(false); } - // Ancient raids rewards - // pantheon - if(card->m_id == 567 || card->m_id == 566) { return(false); } - // sentinel - if(card->m_id == 572 || card->m_id == 564) { return(false); } - // lithid - if(card->m_id == 570 || card->m_id == 571) { return(false); } - // hydra - if(card->m_id == 565 || card->m_id == 568) { return(false); } - // Arachnous - if(card->m_id == 432) { return(false); } - // Shrouded defiler - if(card->m_id == 434) { return(false); } - // Emergency fire - if(card->m_id == 3021) { return(false); } - // Turbo commando - if(card->m_id == 428) { return(false); } - // Solar powerhouse - if(card->m_id == 530) { return(false); } - // Utopia Beacon - if(card->m_id == 469) { return(false); } - return(true); + std::map num_cards; + for(auto card: cards) + { + num_cards[card->m_id] += 1; + } + for(auto & it: num_cards) + { + owned_cards[it.first] = std::max(owned_cards[it.first], it.second); + } } -// Top commanders -std::set top_commanders{ - // common commanders: - 1105, // Opak - 1121, // Daizon - // uncommon commanders: - 1031, // Dalia - 1017, // Duncan - 1120, // Emanuel - 1102, // Korvald - // rare commanders: - 1021, // Atlas - 1153, // Daedalus - 1045, // Dracorex - 1099, // Empress - 1116, // Gaia - 1182, // Gialdrea - 1050, // IC - 1184, // Kleave - 1004, // LoT - 1123, // LtW - 1171, // Nexor - 1104, // Stavros - 1152, // Svetlana - 1141, // Tabitha - 1172, // Teiffa - // halcyon + terminus: - 1203, // Halcyon the Corrupt shitty artwork - 1204, // Halcyon the Corrupt LE - 1200, // Corra - 1049, // Lord Halcyon - 1198, // Virulentus - 1199, // Lord Silus - 1207, // Typhon Vex - // occupation - 1220, // Anzix - 1223, // Balthazar - 1226, // Gnorlock - 1221, // Nikolas - 1225, // Yuletta - }; //------------------------------------------------------------------------------ bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card, const Cards & cards) { @@ -185,7 +127,6 @@ bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card, c { return(true); } - unsigned num_in_deck{0}, num_proto_in_deck{0}, num_upgraded_in_deck{0}; for(unsigned i(0); i < deck.cards.size(); ++i) { @@ -196,29 +137,35 @@ bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card, c } unsigned num_owned{owned_cards.find(card->m_id)->second}; auto owned_upgraded_iter = owned_cards.find(card->m_upgraded_id); - if(num_owned >= num_in_deck + 2 * safe_minus(num_upgraded_in_deck, owned_upgraded_iter == owned_cards.end() ? 0 : owned_upgraded_iter->second) + 1) { return(true); } + unsigned num_to_buy = safe_minus(num_in_deck + 2 * safe_minus(num_upgraded_in_deck, owned_upgraded_iter == owned_cards.end() ? 0 : owned_upgraded_iter->second) + 1, num_owned); + if(num_to_buy == 0) { return(true); } // try upgrade if(auto_upgrade_cards && card->m_proto_id > 0) { auto owned_proto_iter = owned_cards.find(card->m_proto_id); - if(owned_proto_iter != owned_cards.end() && num_owned * 2 + owned_proto_iter->second >= num_in_deck * 2 + num_proto_in_deck + 2) { return(true); } + num_to_buy = safe_minus(2 * num_in_deck + num_proto_in_deck + 2, 2 * num_owned + (owned_proto_iter == owned_cards.end() ? 0 : owned_proto_iter->second)); + if(num_to_buy == 0) { return(true); } + // buy proto } + // buy return(false); } bool suitable_commander(const Card* card, const Cards & cards) { assert(card->m_type == CardType::commander); - if(use_owned_cards) + if(!use_owned_cards) { + return(true); + } + if(owned_cards.find(card->m_id)->second >= 1) { return(true); } + // try upgrade + if(auto_upgrade_cards && card->m_proto_id > 0) { - if(owned_cards.find(card->m_id)->second >= 1) { return(true); } - // try upgrade - if(auto_upgrade_cards && card->m_proto_id > 0) - { - auto owned_iter = owned_cards.find(card->m_proto_id); - if(owned_iter != owned_cards.end() && owned_iter->second >= 2) { return(true); } - } + auto owned_iter = owned_cards.find(card->m_proto_id); + if(owned_iter != owned_cards.end() && owned_iter->second >= 2) { return(true); } + // buy proto } + // buy return(false); } //------------------------------------------------------------------------------ @@ -260,7 +207,7 @@ struct SimulationData const Decks& decks; std::shared_ptr att_deck; Hand att_hand; - std::vector > def_decks; + std::vector> def_decks; std::vector def_hands; std::vector factors; gamemode_t gamemode; @@ -701,9 +648,9 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcards = best_cards; if(best_score.points - target_score > -1e-9) { break; } } + d1->cards = best_cards; } unsigned simulations = 0; for(auto evaluation: evaluated_decks) @@ -1106,9 +1053,10 @@ void usage(int argc, char** argv) " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n" " -o=: restrict hill climbing to the owned cards listed in .\n" " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" + " reorder: enable -r and restrict hill climbing to your initial deck.\n" " -s: use surge (default is fight).\n" " -t : set the number of threads, default is 4.\n" - " -u: do not upgrade owned cards during hill climbing. (by default, upgrade owned cards when needed)\n" + " -u: don't upgrade owned cards during hill climbing. (by default, upgrade owned cards when needed)\n" " target : stop hill climbing as soon as the score reaches .\n" " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n" "\n" @@ -1151,7 +1099,85 @@ int main(int argc, char** argv) std::string att_deck_name{argv[1]}; auto deck_list_parsed = parse_deck_list(argv[2]); - enum Effect effect = Effect::none; + Deck* att_deck{nullptr}; + std::vector def_decks; + std::vector def_decks_factors; + enum Effect effect(Effect::none); + bool keep_commander{false}; + bool fixed_len{false}; + std::vector> todo; + + try + { + att_deck = find_deck(decks, cards, att_deck_name); + } + catch(const std::runtime_error& e) + { + std::cerr << "Error: Deck " << att_deck_name << ": " << e.what() << std::endl; + return(5); + } + if(att_deck == nullptr) + { + std::cerr << "Error: Invalid attack deck name/hash " << att_deck_name << ".\n"; + } + else if(!att_deck->raid_cards.empty()) + { + std::cerr << "Error: Invalid attack deck " << att_deck_name << ": has optional cards.\n"; + att_deck = nullptr; + } + if(att_deck == nullptr) + { + print_available_decks(decks, false); + return(5); + } + + for(auto deck_parsed: deck_list_parsed) + { + Deck* def_deck{nullptr}; + try + { + def_deck = find_deck(decks, cards, deck_parsed.first); + } + catch(const std::runtime_error& e) + { + std::cerr << "Error: Deck " << deck_parsed.first << ": " << e.what() << std::endl; + return(5); + } + if(def_deck == nullptr) + { + std::cerr << "Error: Invalid defense deck name/hash " << deck_parsed.first << ".\n"; + print_available_decks(decks, true); + return(5); + } + if(def_deck->decktype == DeckType::raid) + { + optimization_mode = OptimizationMode::raid; + turn_limit = 30; + target_score = 250; + } + // Set quest effect: + Effect this_effect = def_deck->effect; + if(this_effect != Effect::none) + { + if(effect != Effect::none && effect != this_effect) + { + std::cerr << "Error: Inconsistent effects: " << effect_names[effect] << " and " << effect_names[this_effect] << ".\n"; + return(7); + } + effect = this_effect; + } + // claim reward cards as owned; use filter file if you want to exclude + unsigned prev_mission_id = def_deck->mission_req; + while(prev_mission_id != 0) + { + auto prev_deck = decks.by_type_id[{DeckType::mission, prev_mission_id}]; + prev_mission_id = prev_deck->mission_req; + cliam_cards(prev_deck->reward_cards); + } + def_decks.push_back(def_deck); + def_decks_factors.push_back(deck_parsed.second); + } + std::map effect_map; for(unsigned i(0); i < Effect::num_effects; ++i) { @@ -1161,9 +1187,6 @@ int main(int argc, char** argv) effect_map[ss.str()] = i; } - bool keep_commander{false}; - bool fixed_len{false}; - std::vector > todo; for(int argIndex(3); argIndex < argc; ++argIndex) { if(strcmp(argv[argIndex], "win") == 0) // for test @@ -1173,6 +1196,8 @@ int main(int argc, char** argv) else if(strcmp(argv[argIndex], "raid") == 0) // for test { optimization_mode = OptimizationMode::raid; + turn_limit = 30; + target_score = 250; } else if(strcmp(argv[argIndex], "defense") == 0) { @@ -1190,6 +1215,19 @@ int main(int argc, char** argv) std::cerr << "Error: Achievement " << argv[argIndex + 1] << ": " << e.what() << std::endl; return(1); } + for(auto def_deck: def_decks) + { + if(def_deck->decktype != DeckType::mission) + { + std::cerr << "Error: Enemy's deck must be mission for achievement." << std::endl; + return(1); + } + if(!achievement.mission_condition.check(def_deck->id)) + { + std::cerr << "Error: Wrong mission [" << def_deck->name << "] for achievement." << std::endl; + return(1); + } + } argIndex += 1; } else if(strcmp(argv[argIndex], "-c") == 0) @@ -1221,14 +1259,19 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "-o") == 0) { - read_owned_cards(cards, owned_cards, "ownedcards.txt"); + read_owned_cards(cards, owned_cards, buyable_cards, "ownedcards.txt"); use_owned_cards = true; } else if(strncmp(argv[argIndex], "-o=", 3) == 0) { - read_owned_cards(cards, owned_cards, argv[argIndex] + 3); + read_owned_cards(cards, owned_cards, buyable_cards, argv[argIndex] + 3); use_owned_cards = true; } + else if(strcmp(argv[argIndex], "reorder") == 0) + { + att_strategy = DeckStrategy::ordered; + owned_cards.clear(); + } else if(strcmp(argv[argIndex], "-r") == 0 || strcmp(argv[argIndex], "ordered") == 0) { att_strategy = DeckStrategy::ordered; @@ -1313,31 +1356,15 @@ int main(int argc, char** argv) } } - Deck* att_deck{nullptr}; - try - { - att_deck = find_deck(decks, cards, att_deck_name); - } - catch(const std::runtime_error& e) - { - std::cerr << "Error: Deck " << att_deck_name << ": " << e.what() << std::endl; - return(5); - } - if(att_deck == nullptr) - { - std::cerr << "Error: Invalid attack deck name/hash " << att_deck_name << ".\n"; - } - else if(!att_deck->raid_cards.empty()) - { - std::cerr << "Error: Invalid attack deck " << att_deck_name << ": has optional cards.\n"; - att_deck = nullptr; - } - if(att_deck == nullptr) + // Force to own all cards in your initial deck + cliam_cards(att_deck->cards); + + att_deck->strategy = att_strategy; + for(auto def_deck: def_decks) { - print_available_decks(decks, false); - return(5); + def_deck->strategy = def_strategy; } - att_deck->strategy = att_strategy; + if(keep_commander) { att_deck->card_marks[-1] = '!'; @@ -1347,59 +1374,6 @@ int main(int argc, char** argv) min_deck_len = max_deck_len = att_deck->cards.size(); } - std::vector def_decks; - std::vector def_decks_factors; - for(auto deck_parsed: deck_list_parsed) - { - Deck* def_deck{nullptr}; - try - { - def_deck = find_deck(decks, cards, deck_parsed.first); - } - catch(const std::runtime_error& e) - { - std::cerr << "Error: Deck " << deck_parsed.first << ": " << e.what() << std::endl; - return(5); - } - if(def_deck == nullptr) - { - std::cerr << "Error: Invalid defense deck name/hash " << deck_parsed.first << ".\n"; - print_available_decks(decks, true); - return(5); - } - if(achievement.id > 0) - { - if(def_deck->decktype != DeckType::mission) - { - std::cerr << "Error: Enemy's deck must be mission for achievement." << std::endl; - return(1); - } - if(!achievement.mission_condition.check(def_deck->id)) - { - std::cerr << "Error: Wrong mission [" << deck_parsed.first << "] for achievement." << std::endl; - return(1); - } - } - if(def_deck->decktype == DeckType::raid && optimization_mode == OptimizationMode::none) - { - optimization_mode = OptimizationMode::raid; - } - // Set quest effect: - Effect this_effect = def_deck->effect; - if(this_effect != Effect::none) - { - if(effect != Effect::none && effect != this_effect) - { - std::cerr << "Error: Inconsistent effects: " << effect_names[effect] << " and " << effect_names[this_effect] << ".\n"; - return(7); - } - effect = this_effect; - } - def_deck->strategy = def_strategy; - def_decks.push_back(def_deck); - def_decks_factors.push_back(deck_parsed.second); - } - modify_cards(cards, effect); std::cout << "Your Deck: " << (debug_print ? att_deck->long_description(cards) : att_deck->short_description()) << std::endl; for(auto def_deck: def_decks) @@ -1411,20 +1385,6 @@ int main(int argc, char** argv) std::cout << "Effect: " << effect_names[effect] << std::endl; } - // set "auto determined" default values - if (optimization_mode == OptimizationMode::none) - { - optimization_mode = OptimizationMode::winrate; - } - if (turn_limit == 0) - { - turn_limit = optimization_mode == OptimizationMode::raid ? 30 : 50; - } - if (target_score < 0) - { - target_score = optimization_mode == OptimizationMode::raid ? 250 : 100; - } - Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); { //ScopeClock timer; diff --git a/xml.cpp b/xml.cpp index 6717ed10..141a3666 100644 --- a/xml.cpp +++ b/xml.cpp @@ -352,42 +352,51 @@ Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::De { xml_node<>* commander_node(node->first_node("commander")); const Card* commander_card{cards.by_id(atoi(commander_node->value()))}; + std::vector always_cards; + std::vector>> some_cards; + std::vector reward_cards; xml_node<>* deck_node(node->first_node("deck")); xml_node<>* always_node{deck_node->first_node("always_include")}; - std::vector always_cards; for(xml_node<>* card_node = (always_node ? always_node : deck_node)->first_node("card"); card_node; card_node = card_node->next_sibling("card")) { - unsigned card_id{static_cast(atoi(card_node->value()))}; + unsigned card_id(atoi(card_node->value())); always_cards.push_back(cards.by_id(card_id)); } - std::vector > > some_cards; for(xml_node<>* pool_node = deck_node->first_node("card_pool"); pool_node; pool_node = pool_node->next_sibling("card_pool")) { - unsigned num_cards_from_pool{static_cast(atoi(pool_node->first_attribute("amount")->value()))}; + unsigned num_cards_from_pool(atoi(pool_node->first_attribute("amount")->value())); std::vector cards_from_pool; for(xml_node<>* card_node = pool_node->first_node("card"); card_node; card_node = card_node->next_sibling("card")) { - unsigned card_id{static_cast(atoi(card_node->value()))}; - // Special case Arctis Vanguard id 0 because of stray ` character. - // Don't continue on other raids because I want to be notified of other errors. - if(card_id == 0 && decktype == DeckType::raid && id == 1) - { - continue; - } + unsigned card_id(atoi(card_node->value())); cards_from_pool.push_back(cards.by_id(card_id)); } some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); } + xml_node<>* rewards_node(node->first_node("rewards")); + if(decktype == DeckType::mission && rewards_node) + { + for(xml_node<>* card_node = rewards_node->first_node("card"); + card_node; + card_node = card_node->next_sibling("card")) + { + unsigned card_id(atoi(card_node->value())); + reward_cards.push_back(cards.by_id(card_id)); + } + } + xml_node<>* mission_req_node(node->first_node(decktype == DeckType::mission ? "req" : "mission_req")); + unsigned mission_req(mission_req_node ? atoi(mission_req_node->value()) : 0); decks.decks.push_back(Deck{decktype, id, deck_name}); Deck* deck = &decks.decks.back(); - deck->set(commander_card, always_cards, some_cards); + deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); + decks.by_type_id[{decktype, id}] = deck; decks.by_name[deck_name] = deck; std::stringstream alt_name; alt_name << decktype_names[decktype] << " #" << id; @@ -418,7 +427,6 @@ void read_missions(Decks& decks, const Cards& cards, std::string filename) xml_node<>* name_node(mission_node->first_node("name")); std::string deck_name{name_node->value()}; read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name); - decks.mission_names_by_id[id] = deck_name; } } //------------------------------------------------------------------------------ @@ -529,7 +537,7 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement if(strcmp(mission_id->value(), "*") != 0) { achievement.mission_condition.init(atoi(mission_id->value()), get_comparator(type_node, equal)); - std::cout << " Mission" << achievement.mission_condition.str() << " (" << decks.mission_names_by_id[atoi(mission_id->value())] << ") and win" << std::endl; + std::cout << " Mission" << achievement.mission_condition.str() << " (" << decks.by_type_id[{DeckType::mission, atoi(mission_id->value())}]->name << ") and win" << std::endl; } for (xml_node<>* req_node = achievement_node->first_node("req"); req_node; From fb23cb8751a45ea90f1164623443a11459126194 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 5 Aug 2013 12:15:23 +0800 Subject: [PATCH 174/406] Support buying cards during hill climbing. --- tyrant_optimize.cpp | 160 ++++++++++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 66 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 20f9a598..45191b4f 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -41,6 +41,7 @@ namespace { gamemode_t gamemode{fight}; OptimizationMode optimization_mode{OptimizationMode::winrate}; + unsigned budget{0}; bool auto_upgrade_cards{true}; long double target_score{100}; bool show_stdev{false}; @@ -86,7 +87,8 @@ bool use_owned_cards{false}; unsigned min_deck_len{1}; unsigned max_deck_len{10}; -void cliam_cards(const std::vector & cards) +// @claim_all: true = claim all cards; false = claim only non-buyable cards. +void claim_cards(const std::vector & cards, bool claim_all) { std::map num_cards; for(auto card: cards) @@ -95,12 +97,15 @@ void cliam_cards(const std::vector & cards) } for(auto & it: num_cards) { - owned_cards[it.first] = std::max(owned_cards[it.first], it.second); + if(claim_all || buyable_cards.find(it.first) == buyable_cards.end()) + { + owned_cards[it.first] = std::max(owned_cards[it.first], it.second); + } } } //------------------------------------------------------------------------------ -bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card, const Cards & cards) +bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) { assert(card->m_type != CardType::commander); if(card->m_rarity == 4) // legendary - 1 per deck @@ -109,7 +114,7 @@ bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card, c { if(i != slot && deck.cards[i]->m_rarity == 4) { - return(false); + return false; } } } @@ -119,55 +124,55 @@ bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card, c { if(i != slot && deck.cards[i]->m_base_id == card->m_base_id) { - return(false); + return false; } } } + return true; +} + +unsigned get_deck_cost(const Deck * deck) +{ if(!use_owned_cards) { - return(true); + return 0; } - unsigned num_in_deck{0}, num_proto_in_deck{0}, num_upgraded_in_deck{0}; - for(unsigned i(0); i < deck.cards.size(); ++i) + std::map num_in_deck; + auto card = deck->commander; + if(card->m_proto_id > 0 && owned_cards[card->m_id] == 0) { - if(i == slot) { continue; } - num_in_deck += (deck.cards[i]->m_id == card->m_id); - num_proto_in_deck += (deck.cards[i]->m_id == card->m_proto_id); - num_upgraded_in_deck += (deck.cards[i]->m_id == card->m_upgraded_id); + num_in_deck[card->m_proto_id] += 2; } - unsigned num_owned{owned_cards.find(card->m_id)->second}; - auto owned_upgraded_iter = owned_cards.find(card->m_upgraded_id); - unsigned num_to_buy = safe_minus(num_in_deck + 2 * safe_minus(num_upgraded_in_deck, owned_upgraded_iter == owned_cards.end() ? 0 : owned_upgraded_iter->second) + 1, num_owned); - if(num_to_buy == 0) { return(true); } - // try upgrade - if(auto_upgrade_cards && card->m_proto_id > 0) + else { - auto owned_proto_iter = owned_cards.find(card->m_proto_id); - num_to_buy = safe_minus(2 * num_in_deck + num_proto_in_deck + 2, 2 * num_owned + (owned_proto_iter == owned_cards.end() ? 0 : owned_proto_iter->second)); - if(num_to_buy == 0) { return(true); } - // buy proto + num_in_deck[card->m_id] += 1; } - // buy - return(false); -} - -bool suitable_commander(const Card* card, const Cards & cards) -{ - assert(card->m_type == CardType::commander); - if(!use_owned_cards) { - return(true); + for(auto card: deck->cards) + { + if(card->m_proto_id > 0 && owned_cards[card->m_id] <= num_in_deck[card->m_id]) + { + num_in_deck[card->m_proto_id] += 2; + } + else + { + num_in_deck[card->m_id] += 1; + } } - if(owned_cards.find(card->m_id)->second >= 1) { return(true); } - // try upgrade - if(auto_upgrade_cards && card->m_proto_id > 0) + unsigned deck_cost = 0; + for(auto it: num_in_deck) { - auto owned_iter = owned_cards.find(card->m_proto_id); - if(owned_iter != owned_cards.end() && owned_iter->second >= 2) { return(true); } - // buy proto + unsigned card_id = it.first; + unsigned num_to_buy = safe_minus(it.second, owned_cards[card_id]); + if(num_to_buy > 0) + { + auto buyable_iter = buyable_cards.find(card_id); + if(buyable_iter == buyable_cards.end()) { return UINT_MAX; } + deck_cost += num_to_buy * buyable_iter->second; + } } - // buy - return(false); + return deck_cost; } + //------------------------------------------------------------------------------ Results compute_score(const std::pair> , unsigned>& results, std::vector& factors) { @@ -485,8 +490,12 @@ void print_results(const std::pair> , unsigned>& r } } //------------------------------------------------------------------------------ -void print_deck_inline(const Results score, const Card *commander, std::vector cards, bool is_ordered) +void print_deck_inline(const unsigned deck_cost, const Results score, const Card *commander, std::vector cards, bool is_ordered) { + if(budget > 0) + { + std::cout << "$" << deck_cost << " "; + } switch(optimization_mode) { case OptimizationMode::raid: @@ -550,7 +559,9 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; - print_deck_inline(best_score, best_commander, best_cards, false); + unsigned deck_cost = get_deck_cost(d1); + budget = std::max(budget, deck_cost); + print_deck_inline(deck_cost, best_score, best_commander, best_cards, false); std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; @@ -569,12 +580,13 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapm_type == CardType::commander); if(commander_candidate->m_name == best_commander->m_name) { continue; } - if(!suitable_commander(commander_candidate, proc.cards)) { continue; } // Place it in the deck d1->commander = commander_candidate; auto &&cur_deck = d1->card_ids>(); if(evaluated_decks.count(cur_deck) == 0) { + deck_cost = get_deck_cost(d1); + if(deck_cost > budget) { continue; } // Evaluate new deck auto compare_results = proc.compare(num_iterations, best_score.points); current_score = compute_score(compare_results, proc.factors); @@ -588,7 +600,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map " << card_id_name(commander_candidate) << ": "; print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards, false); + print_deck_inline(deck_cost, best_score, best_commander, best_cards, false); } } else @@ -607,7 +619,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapm_type != CardType::commander); if(slot_i < best_cards.size() && card_candidate->m_name == best_cards[slot_i]->m_name) { continue; } - if(!suitable_non_commander(*d1, slot_i, card_candidate, proc.cards)) { continue; } + if(!suitable_non_commander(*d1, slot_i, card_candidate)) { continue; } // Place it in the deck if(slot_i == d1->cards.size()) { @@ -627,6 +639,8 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcard_ids>(); if(evaluated_decks.count(cur_deck) == 0) { + deck_cost = get_deck_cost(d1); + if(deck_cost > budget) { continue; } // Evaluate new deck auto compare_results = proc.compare(num_iterations, best_score.points); current_score = compute_score(compare_results, proc.factors); @@ -641,7 +655,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcards; deck_has_been_improved = true; print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards, false); + print_deck_inline(deck_cost, best_score, best_commander, best_cards, false); } } else @@ -657,7 +671,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) @@ -674,7 +688,9 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; - print_deck_inline(best_score, best_commander, best_cards, true); + unsigned deck_cost = get_deck_cost(d1); + budget = std::max(budget, deck_cost); + print_deck_inline(deck_cost, best_score, best_commander, best_cards, true); std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; @@ -693,12 +709,13 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); if(commander_candidate->m_name == best_commander->m_name) { continue; } - if(!suitable_commander(commander_candidate, proc.cards)) { continue; } // Place it in the deck d1->commander = commander_candidate; auto &&cur_deck = d1->card_ids>(); if(evaluated_decks.count(cur_deck) == 0) { + deck_cost = get_deck_cost(d1); + if(deck_cost > budget) { continue; } // Evaluate new deck auto compare_results = proc.compare(num_iterations, best_score.points); current_score = compute_score(compare_results, proc.factors); @@ -712,7 +729,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std deck_has_been_improved = true; std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards, true) << " commander -> " << card_id_name(commander_candidate) << ": "; print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards, true); + print_deck_inline(deck_cost, best_score, best_commander, best_cards, true); } } else @@ -731,11 +748,12 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std for(unsigned to_slot(card_candidate ? 0 : d1->cards.size() - 1); to_slot < d1->cards.size() + (from_slot < d1->cards.size() ? 0 : 1); ++to_slot) { if(card_marks.count(from_slot) && card_candidate != best_cards[from_slot]) { break; } + d1->cards = best_cards; if(card_candidate) { // Various checks to check if the card is accepted if(to_slot < best_cards.size() && card_candidate->m_name == best_cards[to_slot]->m_name) { continue; } - if(!suitable_non_commander(*d1, from_slot, card_candidate, proc.cards)) { continue; } + if(!suitable_non_commander(*d1, from_slot, card_candidate)) { continue; } // Place it in the deck if(from_slot < d1->cards.size()) { @@ -752,6 +770,8 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std auto &&cur_deck = d1->card_ids>(); if(evaluated_decks.count(cur_deck) == 0) { + deck_cost = get_deck_cost(d1); + if(deck_cost > budget) { continue; } // Evaluate new deck auto compare_results = proc.compare(num_iterations, best_score.points); current_score = compute_score(compare_results, proc.factors); @@ -766,7 +786,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std best_cards = d1->cards; deck_has_been_improved = true; print_score_info(compare_results, proc.factors); - print_deck_inline(best_score, best_commander, best_cards, true); + print_deck_inline(deck_cost, best_score, best_commander, best_cards, true); std::map new_card_marks; for(auto it: card_marks) { @@ -791,17 +811,17 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std { skipped_simulations += evaluated_decks[cur_deck]; } - d1->cards = best_cards; } if(best_score.points - target_score > -1e-9) { break; } } + d1->cards = best_cards; } unsigned simulations = 0; for(auto evaluation: evaluated_decks) { simulations += evaluation.second; } std::cout << "Evaluated " << evaluated_decks.size() << " decks (" << simulations << " + " << skipped_simulations << " simulations)." << std::endl; std::cout << "Optimized Deck: "; - print_deck_inline(best_score, best_commander, best_cards, true); + print_deck_inline(get_deck_cost(d1), best_score, best_commander, best_cards, true); } //------------------------------------------------------------------------------ // Implements iteration over all combination of k elements from n elements. @@ -910,7 +930,7 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig best_score = new_score; best_deck = deck; print_score_info(new_results, proc.factors); - print_deck_inline(best_score, commander, deck_cards, false); + print_deck_inline(0, best_score, commander, deck_cards, false); } //++num; // num_cards = num_cards_to_combine ... @@ -950,7 +970,7 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig best_score = new_score; best_deck = deck; print_score_info(new_results, proc.factors); - print_deck_inline(best_score, commander, deck_cards, false); + print_deck_inline(0, best_score, commander, deck_cards, false); } ++total_num_combinations_test; finished = cardAmounts.next(); @@ -992,7 +1012,6 @@ void exhaustive_k(unsigned num_iterations, unsigned var_k, Process& proc, std::m for(unsigned commanderIndex(0); commanderIndex < proc.cards.player_commanders.size() && !finished; ++commanderIndex) { const Card* commander(proc.cards.player_commanders[commanderIndex]); - if(!suitable_commander(commander, proc.cards)) { continue; } try_all_ratio_combinations(num_cards, var_k, num_iterations, indices, ass_structs, commander, proc, best_score, best_deck); } } @@ -1046,19 +1065,21 @@ void usage(int argc, char** argv) "\n" "Flags:\n" " -A : optimize for the achievement specified by either id or name.\n" - " -c: don't try to optimize the commander.\n" " defense: score even if turns run out. can be used for defending deck simulation.\n" " -e : set the battleground effect. effect is automatically set for quests.\n" + " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" + " -s: use surge (default is fight).\n" + " -t : set the number of threads, default is 4.\n" + " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n" + "Flags for climb:\n" + " -c: don't try to optimize the commander.\n" " -L : restrict deck size between and during hill climbing.\n" " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n" " -o=: restrict hill climbing to the owned cards listed in .\n" - " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" + " pay : pay at most gold to buy extra cards. prices are specified in ownedcards file.\n" " reorder: enable -r and restrict hill climbing to your initial deck.\n" - " -s: use surge (default is fight).\n" - " -t : set the number of threads, default is 4.\n" - " -u: don't upgrade owned cards during hill climbing. (by default, upgrade owned cards when needed)\n" " target : stop hill climbing as soon as the score reaches .\n" - " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n" + " -u: don't upgrade owned cards during hill climbing. (by default, upgrade owned cards when needed)\n" "\n" "Operations:\n" " brute : find the best combination of different cards, using up to battles to evaluate a deck.\n" @@ -1172,7 +1193,7 @@ int main(int argc, char** argv) { auto prev_deck = decks.by_type_id[{DeckType::mission, prev_mission_id}]; prev_mission_id = prev_deck->mission_req; - cliam_cards(prev_deck->reward_cards); + claim_cards(prev_deck->reward_cards, true); } def_decks.push_back(def_deck); def_decks_factors.push_back(deck_parsed.second); @@ -1267,10 +1288,16 @@ int main(int argc, char** argv) read_owned_cards(cards, owned_cards, buyable_cards, argv[argIndex] + 3); use_owned_cards = true; } + else if(strcmp(argv[argIndex], "pay") == 0) + { + budget = atoi(argv[argIndex+1]); + argIndex += 1; + } else if(strcmp(argv[argIndex], "reorder") == 0) { att_strategy = DeckStrategy::ordered; owned_cards.clear(); + claim_cards(att_deck->cards, true); } else if(strcmp(argv[argIndex], "-r") == 0 || strcmp(argv[argIndex], "ordered") == 0) { @@ -1356,9 +1383,10 @@ int main(int argc, char** argv) } } - // Force to own all cards in your initial deck - cliam_cards(att_deck->cards); - + // Force to claim non-buyable cards in your initial deck. + claim_cards({att_deck->commander}, budget == 0); + claim_cards(att_deck->cards, budget == 0); + att_deck->strategy = att_strategy; for(auto def_deck: def_decks) { From 64486a52176037658e7185da033a80d29d58b049 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 6 Aug 2013 10:17:30 +0800 Subject: [PATCH 175/406] Backfire damage should count in raid damage. --- sim.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index 5e5d5caf..383112aa 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1734,8 +1734,8 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - // backfire damage not count in ANP. - remove_commander_hp(fd, *c, v, false); + // backfire damage counts in ARD. + remove_commander_hp(fd, *c, v, true); } template<> @@ -1816,7 +1816,7 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) template<> inline void perform_skill(Field* fd, CardStatus* c, unsigned v) { - // shock damage counts in ANP. (if attacker ever has the skill) + // shock damage counts in ARD. (if attacker ever has the skill) remove_commander_hp(fd, *c, v, true); } From f78346a252e6d906f1fcd94d07933e71c8366e30 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 6 Aug 2013 23:45:23 +0800 Subject: [PATCH 176/406] Blitz status should carry into the next attack phase if the assault does not take action. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 383112aa..00c9424b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -681,6 +681,7 @@ Results play(Field* fd) current_status.m_step = CardStep::attacked; continue; } + current_status.m_blitzing = false; // Evaluate skills evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); if(__builtin_expect(fd->end, false)) { break; } @@ -1065,7 +1066,6 @@ void turn_start_phase(Field* fd) CardStatus& status(assaults[index]); status.m_index = index; status.m_augmented = 0; - status.m_blitzing = status.m_blitzing && is_jammed(&status); status.m_chaosed = false; status.m_enfeebled = 0; status.m_frozen = false; From 9b80d5861ff8925c55b219d8bd6b18e6cbbb6635 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 7 Aug 2013 19:32:05 +0800 Subject: [PATCH 177/406] No automatically claim mission reward cards. Fix card claiming, buying and upgrading functions. --- card.h | 3 + tyrant_optimize.cpp | 141 +++++++++++++++++++++++++++----------------- xml.cpp | 7 +++ 3 files changed, 98 insertions(+), 53 deletions(-) diff --git a/card.h b/card.h index 6c85d272..e484f375 100644 --- a/card.h +++ b/card.h @@ -110,6 +110,7 @@ class Card bool m_refresh; unsigned m_regenerate; unsigned m_replace; + unsigned m_reserve; unsigned m_set; unsigned m_siphon; bool m_split; @@ -119,6 +120,8 @@ class Card bool m_swipe; bool m_tribute; bool m_unique; + unsigned m_upgrade_consumables; + unsigned m_upgrade_gold_cost; unsigned m_upgraded_id; // The id of the upgraded card for an upgradable card. 0 otherwise. unsigned m_valor; bool m_wall; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 45191b4f..bd2c7660 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -41,7 +41,7 @@ namespace { gamemode_t gamemode{fight}; OptimizationMode optimization_mode{OptimizationMode::winrate}; - unsigned budget{0}; + unsigned fund{0}; bool auto_upgrade_cards{true}; long double target_score{100}; bool show_stdev{false}; @@ -88,18 +88,33 @@ unsigned min_deck_len{1}; unsigned max_deck_len{10}; // @claim_all: true = claim all cards; false = claim only non-buyable cards. -void claim_cards(const std::vector & cards, bool claim_all) +// @is_reward: not claim a card if there is upgraded version (assuming the reward card has been upgraded). +void claim_cards(const std::vector & card_list, const Cards & cards, bool claim_all, bool is_reward) { - std::map num_cards; - for(auto card: cards) + std::map num_cards; + for(const Card * card: card_list) { - num_cards[card->m_id] += 1; + if(card->m_proto_id > 0 && auto_upgrade_cards && owned_cards[card->m_id] <= num_cards[card]) + { + const Card * proto_card = cards.by_id(card->m_proto_id); + num_cards[proto_card] += proto_card->m_upgrade_consumables; + } + else + { + num_cards[card] += 1; + } } - for(auto & it: num_cards) + for(auto it: num_cards) { - if(claim_all || buyable_cards.find(it.first) == buyable_cards.end()) + const Card * card = it.first; + if(claim_all || buyable_cards.find(card->m_id) == buyable_cards.end()) { - owned_cards[it.first] = std::max(owned_cards[it.first], it.second); + unsigned num_to_claim = safe_minus(it.second, owned_cards[card->m_id] + (is_reward ? card->m_upgrade_consumables * owned_cards[card->m_upgraded_id] : 0)); + if(num_to_claim > 0) + { + owned_cards[card->m_id] += num_to_claim; +// std::cout << "Claim " << card->m_name << " (" << num_to_claim << ")" << std::endl; + } } } } @@ -131,34 +146,38 @@ bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) return true; } -unsigned get_deck_cost(const Deck * deck) +unsigned get_deck_cost(const Deck * deck, const Cards & cards) { if(!use_owned_cards) { return 0; } std::map num_in_deck; - auto card = deck->commander; - if(card->m_proto_id > 0 && owned_cards[card->m_id] == 0) + unsigned deck_cost = 0; + const Card * card = deck->commander; + if(card->m_proto_id > 0 && auto_upgrade_cards && owned_cards[card->m_id] == 0) { - num_in_deck[card->m_proto_id] += 2; + const Card * proto_card = cards.by_id(card->m_proto_id); + num_in_deck[proto_card->m_id] += proto_card->m_upgrade_consumables; + deck_cost += proto_card->m_upgrade_gold_cost; } else { num_in_deck[card->m_id] += 1; } - for(auto card: deck->cards) + for(const Card * card: deck->cards) { - if(card->m_proto_id > 0 && owned_cards[card->m_id] <= num_in_deck[card->m_id]) + if(card->m_proto_id > 0 && auto_upgrade_cards && owned_cards[card->m_id] <= num_in_deck[card->m_id]) { - num_in_deck[card->m_proto_id] += 2; + const Card * proto_card = cards.by_id(card->m_proto_id); + num_in_deck[proto_card->m_id] += proto_card->m_upgrade_consumables; + deck_cost += proto_card->m_upgrade_gold_cost; } else { num_in_deck[card->m_id] += 1; } } - unsigned deck_cost = 0; for(auto it: num_in_deck) { unsigned card_id = it.first; @@ -185,7 +204,7 @@ Results compute_score(const std::pair final.points += results.first[index].points * factors[index]; final.sq_points += results.first[index].sq_points * factors[index] * factors[index]; } - auto factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); + long double factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); final.wins /= factor_sum * (long double)results.second; final.draws /= factor_sum * (long double)results.second; final.losses /= factor_sum * (long double)results.second; @@ -492,7 +511,7 @@ void print_results(const std::pair> , unsigned>& r //------------------------------------------------------------------------------ void print_deck_inline(const unsigned deck_cost, const Results score, const Card *commander, std::vector cards, bool is_ordered) { - if(budget > 0) + if(fund > 0) { std::cout << "$" << deck_cost << " "; } @@ -559,8 +578,8 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; - unsigned deck_cost = get_deck_cost(d1); - budget = std::max(budget, deck_cost); + unsigned deck_cost = get_deck_cost(d1, proc.cards); + fund = std::max(fund, deck_cost); print_deck_inline(deck_cost, best_score, best_commander, best_cards, false); std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; @@ -585,8 +604,8 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcard_ids>(); if(evaluated_decks.count(cur_deck) == 0) { - deck_cost = get_deck_cost(d1); - if(deck_cost > budget) { continue; } + deck_cost = get_deck_cost(d1, proc.cards); + if(deck_cost > fund) { continue; } // Evaluate new deck auto compare_results = proc.compare(num_iterations, best_score.points); current_score = compute_score(compare_results, proc.factors); @@ -639,8 +658,8 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcard_ids>(); if(evaluated_decks.count(cur_deck) == 0) { - deck_cost = get_deck_cost(d1); - if(deck_cost > budget) { continue; } + deck_cost = get_deck_cost(d1, proc.cards); + if(deck_cost > fund) { continue; } // Evaluate new deck auto compare_results = proc.compare(num_iterations, best_score.points); current_score = compute_score(compare_results, proc.factors); @@ -671,7 +690,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) @@ -688,8 +707,8 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; - unsigned deck_cost = get_deck_cost(d1); - budget = std::max(budget, deck_cost); + unsigned deck_cost = get_deck_cost(d1, proc.cards); + fund = std::max(fund, deck_cost); print_deck_inline(deck_cost, best_score, best_commander, best_cards, true); std::mt19937 re(time(NULL)); bool deck_has_been_improved = true; @@ -714,8 +733,8 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std auto &&cur_deck = d1->card_ids>(); if(evaluated_decks.count(cur_deck) == 0) { - deck_cost = get_deck_cost(d1); - if(deck_cost > budget) { continue; } + deck_cost = get_deck_cost(d1, proc.cards); + if(deck_cost > fund) { continue; } // Evaluate new deck auto compare_results = proc.compare(num_iterations, best_score.points); current_score = compute_score(compare_results, proc.factors); @@ -770,8 +789,8 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std auto &&cur_deck = d1->card_ids>(); if(evaluated_decks.count(cur_deck) == 0) { - deck_cost = get_deck_cost(d1); - if(deck_cost > budget) { continue; } + deck_cost = get_deck_cost(d1, proc.cards); + if(deck_cost > fund) { continue; } // Evaluate new deck auto compare_results = proc.compare(num_iterations, best_score.points); current_score = compute_score(compare_results, proc.factors); @@ -821,7 +840,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std { simulations += evaluation.second; } std::cout << "Evaluated " << evaluated_decks.size() << " decks (" << simulations << " + " << skipped_simulations << " simulations)." << std::endl; std::cout << "Optimized Deck: "; - print_deck_inline(get_deck_cost(d1), best_score, best_commander, best_cards, true); + print_deck_inline(get_deck_cost(d1, proc.cards), best_score, best_commander, best_cards, true); } //------------------------------------------------------------------------------ // Implements iteration over all combination of k elements from n elements. @@ -1065,21 +1084,23 @@ void usage(int argc, char** argv) "\n" "Flags:\n" " -A : optimize for the achievement specified by either id or name.\n" - " defense: score even if turns run out. can be used for defending deck simulation.\n" " -e : set the battleground effect. effect is automatically set for quests.\n" " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" " -s: use surge (default is fight).\n" " -t : set the number of threads, default is 4.\n" " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n" + " win: simulate/optimize for win rate. default for non-raids.\n" + " defense: simulate/optimize for win rate + stall rate. can be used for defending deck or win rate oriented raid simulations.\n" + " raid: simulate/optimize for average raid damage (ARD). default for raids.\n" "Flags for climb:\n" " -c: don't try to optimize the commander.\n" - " -L : restrict deck size between and during hill climbing.\n" - " -o: restrict hill climbing to the owned cards listed in \"ownedcards.txt\".\n" - " -o=: restrict hill climbing to the owned cards listed in .\n" - " pay : pay at most gold to buy extra cards. prices are specified in ownedcards file.\n" - " reorder: enable -r and restrict hill climbing to your initial deck.\n" - " target : stop hill climbing as soon as the score reaches .\n" - " -u: don't upgrade owned cards during hill climbing. (by default, upgrade owned cards when needed)\n" + " -L : restrict deck size between and .\n" + " -o: restrict to the owned cards listed in \"ownedcards.txt\".\n" + " -o=: restrict to the owned cards listed in .\n" + " fund : fund gold to buy/upgrade cards. prices are specified in ownedcards file.\n" + " reorder: enable -r and restrict to the cards in your initial deck.\n" + " target : stop as soon as the score reaches .\n" + " -u: don't upgrade owned cards. (by default, upgrade owned cards when needed)\n" "\n" "Operations:\n" " brute : find the best combination of different cards, using up to battles to evaluate a deck.\n" @@ -1187,14 +1208,6 @@ int main(int argc, char** argv) } effect = this_effect; } - // claim reward cards as owned; use filter file if you want to exclude - unsigned prev_mission_id = def_deck->mission_req; - while(prev_mission_id != 0) - { - auto prev_deck = decks.by_type_id[{DeckType::mission, prev_mission_id}]; - prev_mission_id = prev_deck->mission_req; - claim_cards(prev_deck->reward_cards, true); - } def_decks.push_back(def_deck); def_decks_factors.push_back(deck_parsed.second); } @@ -1288,16 +1301,35 @@ int main(int argc, char** argv) read_owned_cards(cards, owned_cards, buyable_cards, argv[argIndex] + 3); use_owned_cards = true; } - else if(strcmp(argv[argIndex], "pay") == 0) + else if(strncmp(argv[argIndex], "-o+", 3) == 0) { - budget = atoi(argv[argIndex+1]); + unsigned completed_mission_id = atoi(argv[argIndex] + 3); + // claim reward cards as owned; use filter file if you want to exclude + for(auto def_deck: def_decks) + { + unsigned prev_mission_id = def_deck->mission_req; + while(prev_mission_id > completed_mission_id) + { + auto prev_deck = decks.by_type_id[{DeckType::mission, prev_mission_id}]; + prev_mission_id = prev_deck->mission_req; + claim_cards(prev_deck->reward_cards, cards, true, true); + } + } + } + else if(strcmp(argv[argIndex], "fund") == 0) + { + fund = atoi(argv[argIndex+1]); argIndex += 1; } else if(strcmp(argv[argIndex], "reorder") == 0) { att_strategy = DeckStrategy::ordered; + fixed_len = true; + use_owned_cards = true; + auto_upgrade_cards = false; owned_cards.clear(); - claim_cards(att_deck->cards, true); + claim_cards({att_deck->commander}, cards, true, false); + claim_cards(att_deck->cards, cards, true, false); } else if(strcmp(argv[argIndex], "-r") == 0 || strcmp(argv[argIndex], "ordered") == 0) { @@ -1384,8 +1416,11 @@ int main(int argc, char** argv) } // Force to claim non-buyable cards in your initial deck. - claim_cards({att_deck->commander}, budget == 0); - claim_cards(att_deck->cards, budget == 0); + if(use_owned_cards) + { + claim_cards({att_deck->commander}, cards, fund == 0, false); + claim_cards(att_deck->cards, cards, fund == 0, false); + } att_deck->strategy = att_strategy; for(auto def_deck: def_decks) diff --git a/xml.cpp b/xml.cpp index 141a3666..ca65c65e 100644 --- a/xml.cpp +++ b/xml.cpp @@ -170,6 +170,8 @@ void read_cards(Cards& cards) xml_node<>* health_node(card->first_node("health")); xml_node<>* cost_node(card->first_node("cost")); xml_node<>* unique_node(card->first_node("unique")); + xml_node<>* reserve_node(card->first_node("reserve")); + unsigned reserve(reserve_node ? atoi(reserve_node->value()) : 0); xml_node<>* base_card_node(card->first_node("base_card")); unsigned base_card_id(base_card_node ? atoi(base_card_node->value()) : id); xml_node<>* rarity_node(card->first_node("rarity")); @@ -206,11 +208,16 @@ void read_cards(Cards& cards) if(health_node) { c->m_health = atoi(health_node->value()); } if(cost_node) { c->m_delay = atoi(cost_node->value()); } if(unique_node) { c->m_unique = true; } + c->m_reserve = reserve; c->m_base_id = base_card_id; c->m_rarity = atoi(rarity_node->value()); unsigned type(type_node ? atoi(type_node->value()) : 0); c->m_faction = map_to_faction(type); c->m_set = set; + // Promo and Unpurchasable Reward cards will only require 1 copy + c->m_upgrade_consumables = set == 5001 || (set == 5000 and reserve) ? 1 : 2; + // Reward cards will still have a gold cost + c->m_upgrade_gold_cost = set == 5000 ? (c->m_rarity == 4 ? 100000 : 20000) : 0; for(xml_node<>* skill = card->first_node("skill"); skill; skill = skill->next_sibling("skill")) { From 6520e2f86b5616cfc2fe3455d2f218094c1cd912 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 8 Aug 2013 11:34:47 +0800 Subject: [PATCH 178/406] Remove operation brute. Add operation reorder. --- tyrant_optimize.cpp | 90 +++++++++++---------------------------------- 1 file changed, 22 insertions(+), 68 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index bd2c7660..74a15618 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -997,52 +997,10 @@ inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsig } } //------------------------------------------------------------------------------ -void exhaustive_k(unsigned num_iterations, unsigned var_k, Process& proc, std::map card_marks) -{ - std::vector ass_structs; - for(const Card* card: proc.cards.player_assaults) - { - if(card->m_rarity >= 3) { ass_structs.push_back(card); } - } - for(const Card* card: proc.cards.player_structures) - { - if(card->m_rarity >= 3) { ass_structs.push_back(card); } - } - //std::vector ass_structs; = cards.player_assaults; - //ass_structs.insert(ass_structs.end(), cards.player_structures.begin(), cards.player_structures.end()); - unsigned var_n = ass_structs.size(); - assert(var_k <= var_n); - unsigned num(0); - Combination cardIndices(var_n, var_k); - const std::vector& indices = cardIndices.getIndices(); - bool finished(false); - Results best_score{0, 0, 0, 0}; - boost::optional best_deck; - unsigned num_cards = ((Deck*)proc.att_deck)->cards.size(); - while(!finished) - { - if(card_marks.count(-1)) - { - try_all_ratio_combinations(num_cards, var_k, num_iterations, indices, ass_structs, ((Deck*)proc.att_deck)->commander, proc, best_score, best_deck); - } - else - { - // Iterate over all commanders - for(unsigned commanderIndex(0); commanderIndex < proc.cards.player_commanders.size() && !finished; ++commanderIndex) - { - const Card* commander(proc.cards.player_commanders[commanderIndex]); - try_all_ratio_combinations(num_cards, var_k, num_iterations, indices, ass_structs, commander, proc, best_score, best_deck); - } - } - finished = cardIndices.next(); - } - std::cout << "done " << num << "\n"; -} -//------------------------------------------------------------------------------ enum Operation { - bruteforce, - climb, simulate, + climb, + reorder, debug, debuguntil }; @@ -1098,14 +1056,13 @@ void usage(int argc, char** argv) " -o: restrict to the owned cards listed in \"ownedcards.txt\".\n" " -o=: restrict to the owned cards listed in .\n" " fund : fund gold to buy/upgrade cards. prices are specified in ownedcards file.\n" - " reorder: enable -r and restrict to the cards in your initial deck.\n" " target : stop as soon as the score reaches .\n" " -u: don't upgrade owned cards. (by default, upgrade owned cards when needed)\n" "\n" "Operations:\n" - " brute : find the best combination of different cards, using up to battles to evaluate a deck.\n" - " climb : perform hill-climbing starting from the given attack deck, using up to battles to evaluate a deck.\n" " sim : simulate battles to evaluate a deck.\n" + " climb : perform hill-climbing starting from the given attack deck, using up to battles to evaluate a deck.\n" + " reorder : optimize the order for given attack deck, using up to battles to evaluate an order.\n" #ifndef NDEBUG " debug: testing purpose only. very verbose output. only one battle.\n" " debuguntil : testing purpose only. fight until the last fight results in range [, ]. recommend to redirect output.\n" @@ -1321,16 +1278,6 @@ int main(int argc, char** argv) fund = atoi(argv[argIndex+1]); argIndex += 1; } - else if(strcmp(argv[argIndex], "reorder") == 0) - { - att_strategy = DeckStrategy::ordered; - fixed_len = true; - use_owned_cards = true; - auto_upgrade_cards = false; - owned_cards.clear(); - claim_cards({att_deck->commander}, cards, true, false); - claim_cards(att_deck->cards, cards, true, false); - } else if(strcmp(argv[argIndex], "-r") == 0 || strcmp(argv[argIndex], "ordered") == 0) { att_strategy = DeckStrategy::ordered; @@ -1382,20 +1329,20 @@ int main(int argc, char** argv) { ++ debug_print; } - else if(strcmp(argv[argIndex], "brute") == 0) + else if(strcmp(argv[argIndex], "sim") == 0) { - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), bruteforce)); - argIndex += 2; + todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); + argIndex += 1; } else if(strcmp(argv[argIndex], "climb") == 0) { todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, climb)); argIndex += 1; } - else if(strcmp(argv[argIndex], "sim") == 0) + else if(strcmp(argv[argIndex], "reorder") == 0) { - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); - argIndex += 1; + todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, reorder)); + argIndex += 1; } else if(strcmp(argv[argIndex], "debug") == 0) { @@ -1455,8 +1402,9 @@ int main(int argc, char** argv) { switch(std::get<2>(op)) { - case bruteforce: { - exhaustive_k(std::get<1>(op), std::get<0>(op), p, att_deck->card_marks); + case simulate: { + auto results = p.evaluate(std::get<0>(op)); + print_results(results, p.factors); break; } case climb: { @@ -1470,9 +1418,15 @@ int main(int argc, char** argv) } break; } - case simulate: { - auto results = p.evaluate(std::get<0>(op)); - print_results(results, p.factors); + case reorder: { + att_strategy = DeckStrategy::ordered; + fixed_len = true; + use_owned_cards = true; + auto_upgrade_cards = false; + owned_cards.clear(); + claim_cards({att_deck->commander}, cards, true, false); + claim_cards(att_deck->cards, cards, true, false); + hill_climbing_ordered(std::get<0>(op), att_deck, p, att_deck->card_marks); break; } case debug: { From b91075b37d9c1aea7aaefeecfdb95e44093b464e Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 9 Aug 2013 23:30:56 +0800 Subject: [PATCH 179/406] Stunned assault can be targeted by Weaken. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 00c9424b..ee2455d4 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1715,7 +1715,7 @@ template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { const auto& mod = std::get<4>(s); - return(can_attack(c) && attack_power(c) > 0 && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); + return(can_act(c) && !c->m_immobilized && attack_power(c) > 0 && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); From 3739b5efda41f53e09452f6043e846d7e44a6ee7 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 11 Aug 2013 16:37:02 +0800 Subject: [PATCH 180/406] Fix hill climbing. --- tyrant_optimize.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 74a15618..be15c00e 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -584,7 +584,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map(max_deck_len, d1->cards.size() + 1)) + for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score.points - target_score < -1e-9; slot_i = (slot_i + 1) % std::min(max_deck_len, best_cards.size() + 1)) { if(card_marks.count(slot_i)) { continue; } if(deck_has_been_improved) @@ -633,6 +633,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcards = best_cards; if(card_candidate) { // Various checks to check if the card is accepted @@ -764,18 +765,18 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std { // Various checks to check if the card is accepted assert(!card_candidate || card_candidate->m_type != CardType::commander); - for(unsigned to_slot(card_candidate ? 0 : d1->cards.size() - 1); to_slot < d1->cards.size() + (from_slot < d1->cards.size() ? 0 : 1); ++to_slot) + for(unsigned to_slot(card_candidate ? 0 : best_cards.size() - 1); to_slot < best_cards.size() + (from_slot < best_cards.size() ? 0 : 1); ++to_slot) { if(card_marks.count(from_slot) && card_candidate != best_cards[from_slot]) { break; } d1->cards = best_cards; if(card_candidate) { // Various checks to check if the card is accepted - if(to_slot < best_cards.size() && card_candidate->m_name == best_cards[to_slot]->m_name) { continue; } if(!suitable_non_commander(*d1, from_slot, card_candidate)) { continue; } // Place it in the deck - if(from_slot < d1->cards.size()) + if(from_slot < best_cards.size()) { + if(from_slot == to_slot && card_candidate->m_name == best_cards[to_slot]->m_name) { continue; } d1->cards.erase(d1->cards.begin() + from_slot); } d1->cards.insert(d1->cards.begin() + to_slot, card_candidate); From 7318ac77d89e2d091da6e04a01451687631ccc62 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 11 Aug 2013 23:53:10 +0800 Subject: [PATCH 181/406] Reformat output message for raids. --- tyrant_optimize.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index be15c00e..de810d21 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -461,19 +461,32 @@ void print_results(const std::pair> , unsigned>& r { auto final = compute_score(results, factors); - std::cout << (optimization_mode == OptimizationMode::raid ? "kill%: " : "win%: ") << final.wins * 100.0 << " ("; + if(optimization_mode == OptimizationMode::raid) + { + std::cout << "win%: " << (final.wins + final.draws) * 100.0 << " ("; + for(auto val: results.first) + { + std::cout << val.wins + val.draws << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; + } + + std::cout << (optimization_mode == OptimizationMode::raid ? "slay%: " : "win%: ") << final.wins * 100.0 << " ("; for(auto val: results.first) { std::cout << val.wins << " "; } std::cout << "/ " << results.second << ")" << std::endl; - std::cout << "stall%: " << final.draws * 100.0 << " ("; - for(auto val: results.first) + if(optimization_mode != OptimizationMode::raid) { - std::cout << val.draws << " "; + std::cout << "stall%: " << final.draws * 100.0 << " ("; + for(auto val: results.first) + { + std::cout << val.draws << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; } - std::cout << "/ " << results.second << ")" << std::endl; std::cout << "loss%: " << final.losses * 100.0 << " ("; for(auto val: results.first) @@ -518,7 +531,7 @@ void print_deck_inline(const unsigned deck_cost, const Results scor switch(optimization_mode) { case OptimizationMode::raid: - std::cout << "(" << score.wins * 100.0 << "% kill, " << score.draws * 100.0 << "% stall"; + std::cout << "(" << (score.wins + score.draws) * 100 << "% win, " << score.wins * 100.0 << "% slay"; if (show_stdev) { std::cout << ", " << sqrt(score.sq_points - score.points * score.points) << " stdev"; } From 540dde6cf4ccb06d491e71825bd1d65e9a5f2e7b Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 12 Aug 2013 14:00:47 +0800 Subject: [PATCH 182/406] Fix operation reorder. Add option hand for debugging. --- deck.cpp | 134 ++++++++++++++++++++++++++++---------------- deck.h | 4 +- tyrant_optimize.cpp | 30 +++++----- 3 files changed, 104 insertions(+), 64 deletions(-) diff --git a/deck.cpp b/deck.cpp index 7443dd40..e41c1765 100644 --- a/deck.cpp +++ b/deck.cpp @@ -83,7 +83,7 @@ const char* base64_chars = // Converts cards in `hash' to a deck. // Stores resulting card IDs in `ids'. -void hash_to_ids(const char* hash, std::vector& ids) +void hash_to_ids(const char* hash, std::vector& ids) { unsigned int last_id = 0; const char* pc = hash; @@ -123,11 +123,61 @@ void hash_to_ids(const char* hash, std::vector& ids) } } } + +const std::pair, std::map> string_to_ids(const Cards& all_cards, const std::string& deck_string, const std::string & description) +{ + std::vector card_ids; + std::map card_marks; + if(deck_string.find_first_of(":,") == std::string::npos) + { + try + { + hash_to_ids(deck_string.c_str(), card_ids); + } + catch(std::exception& e) + { + std::cerr << "Error while resolving " << description << ": " << e.what() << std::endl; + throw; + } + } + else + { + boost::tokenizer> deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; + auto token_iter = deck_tokens.begin(); + signed p = -1; + for(; token_iter != deck_tokens.end(); ++token_iter) + { + std::string card_spec(*token_iter); + unsigned card_id{0}; + unsigned card_num{1}; + char num_sign{0}; + char mark{0}; + try + { + parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign, mark); + assert(num_sign == 0); + for(unsigned i(0); i < card_num; ++i) + { + card_ids.push_back(card_id); + if(mark) { card_marks[p] = mark; } + ++ p; + } + } + catch(std::exception& e) + { + std::cerr << "Warning while resolving " << description << ": " << e.what() << std::endl; + continue; + } + } + } + return {card_ids, card_marks}; +} + } // end of namespace namespace range = boost::range; -void Deck::set(const Cards& all_cards, const std::vector& ids) +void Deck::set(const Cards& all_cards, const std::vector& ids, const std::map marks) { commander = nullptr; strategy = DeckStrategy::random; @@ -154,6 +204,7 @@ void Deck::set(const Cards& all_cards, const std::vector& ids) { throw std::runtime_error("While constructing a deck: no commander found"); } + card_marks = marks; } void Deck::set(const Cards& all_cards, const std::string& deck_string_) @@ -163,54 +214,19 @@ void Deck::set(const Cards& all_cards, const std::string& deck_string_) void Deck::resolve(const Cards& all_cards) { - if(commander == nullptr) + if(commander != nullptr) { - std::vector ids; - if(deck_string.find_first_of(":,") == std::string::npos) - { - try - { - hash_to_ids(deck_string.c_str(), ids); - } - catch(std::exception& e) - { - std::cerr << "Error while resolving " << short_description() << ": " << e.what() << std::endl; - throw; - } - } - else - { - boost::tokenizer> deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; - auto token_iter = deck_tokens.begin(); - signed p = -1; - for(; token_iter != deck_tokens.end(); ++token_iter) - { - std::string card_spec(*token_iter); - unsigned card_id{0}; - unsigned card_num{1}; - char num_sign{0}; - char mark{0}; - try - { - parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign, mark); - assert(num_sign == 0); - for(unsigned i(0); i < card_num; ++i) - { - ids.push_back(card_id); - if(mark) { card_marks[p] = mark; } - ++ p; - } - } - catch(std::exception& e) - { - std::cerr << "Warning while resolving " << short_description() << ": " << e.what() << std::endl; - continue; - } - } - } - set(all_cards, ids); - deck_string.clear(); + return; } + auto && id_marks = string_to_ids(all_cards, deck_string, short_description()); + set(all_cards, id_marks.first, id_marks.second); + deck_string.clear(); +} + +void Deck::set_given_hand(const Cards& all_cards, const std::string& hand_string) +{ + auto && id_marks = string_to_ids(all_cards, hand_string, "hand"); + given_hand = id_marks.first; } std::string Deck::short_description() const @@ -353,7 +369,27 @@ void Deck::shuffle(std::mt19937& re) } if(strategy != DeckStrategy::exact_ordered) { - std::shuffle(shuffled_cards.begin(), shuffled_cards.end(), re); + auto shufflable_iter = shuffled_cards.begin(); + for(auto hand_card_id: given_hand) + { + auto it = std::find_if(shufflable_iter, shuffled_cards.end(), [hand_card_id](const Card* card) -> bool { return card->m_id == hand_card_id; }); + if(it != shuffled_cards.end()) + { + std::swap(*shufflable_iter, *it); + ++ shufflable_iter; + } + } + std::shuffle(shufflable_iter, shuffled_cards.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; + } +#endif } } diff --git a/deck.h b/deck.h index 8767d741..d1e8e4de 100644 --- a/deck.h +++ b/deck.h @@ -49,6 +49,7 @@ struct Deck unsigned mission_req; std::string deck_string; + std::vector given_hand; Deck( DeckType::DeckType decktype_ = DeckType::deck, @@ -81,9 +82,10 @@ struct Deck mission_req = mission_req_; } - void set(const Cards& all_cards, const std::vector& ids); + void set(const Cards& all_cards, const std::vector& ids, const std::map marks = {}); void set(const Cards& all_cards, const std::string& deck_string_); void resolve(const Cards& all_cards); + void set_given_hand(const Cards& all_cards, const std::string& deck_string_); template Container card_ids() const diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index de810d21..6fb981f7 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -41,6 +41,11 @@ namespace { gamemode_t gamemode{fight}; OptimizationMode optimization_mode{OptimizationMode::winrate}; + std::map owned_cards; + std::map buyable_cards; + bool use_owned_cards{false}; + unsigned min_deck_len{1}; + unsigned max_deck_len{10}; unsigned fund{0}; bool auto_upgrade_cards{true}; long double target_score{100}; @@ -78,14 +83,6 @@ Deck* find_deck(Decks& decks, const Cards& cards, std::string deck_name) return(deck); } //---------------------- $80 deck optimization --------------------------------- -//------------------------------------------------------------------------------ -// Owned cards -//------------------------------------------------------------------------------ -std::map owned_cards; -std::map buyable_cards; -bool use_owned_cards{false}; -unsigned min_deck_len{1}; -unsigned max_deck_len{10}; // @claim_all: true = claim all cards; false = claim only non-buyable cards. // @is_reward: not claim a card if there is upgraded version (assuming the reward card has been upgraded). @@ -1194,11 +1191,11 @@ int main(int argc, char** argv) for(int argIndex(3); argIndex < argc; ++argIndex) { - if(strcmp(argv[argIndex], "win") == 0) // for test + if(strcmp(argv[argIndex], "win") == 0) { optimization_mode = OptimizationMode::winrate; } - else if(strcmp(argv[argIndex], "raid") == 0) // for test + else if(strcmp(argv[argIndex], "raid") == 0) { optimization_mode = OptimizationMode::raid; turn_limit = 30; @@ -1343,10 +1340,15 @@ int main(int argc, char** argv) { ++ debug_print; } + else if(strcmp(argv[argIndex], "hand") == 0) // set initial hand for test + { + att_deck->set_given_hand(cards, argv[argIndex + 1]); + argIndex += 1; + } else if(strcmp(argv[argIndex], "sim") == 0) { - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); - argIndex += 1; + todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); + argIndex += 1; } else if(strcmp(argv[argIndex], "climb") == 0) { @@ -1433,8 +1435,8 @@ int main(int argc, char** argv) break; } case reorder: { - att_strategy = DeckStrategy::ordered; - fixed_len = true; + att_deck->strategy = DeckStrategy::ordered; + min_deck_len = max_deck_len = att_deck->cards.size(); use_owned_cards = true; auto_upgrade_cards = false; owned_cards.clear(); From 1767013361c5ae624d6b82217e34993c54856e7b Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 17 Aug 2013 00:55:35 +0800 Subject: [PATCH 183/406] Calculate raid damage based on the final HP of enemy commander. --- sim.cpp | 6 ++---- sim.h | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/sim.cpp b/sim.cpp index ee2455d4..059b0dfe 100644 --- a/sim.cpp +++ b/sim.cpp @@ -585,7 +585,6 @@ Results play(Field* fd) // ANP: Last decision point is second-to-last card played. fd->points_since_last_decision = 0; #endif - fd->all_damage_to_commander = 0; unsigned p0_size = fd->players[0]->deck->cards.size(); fd->last_decision_turn = p0_size == 1 ? 0 : p0_size * 2 - (fd->gamemode == surge ? 2 : 3); @@ -713,7 +712,6 @@ Results play(Field* fd) bool made_achievement = true; if(fd->optimization_mode == OptimizationMode::achievement) { - fd->set_counter(fd->achievement.misc_req, AchievementMiscReq::com_total, fd->all_damage_to_commander); for(unsigned i(0); made_achievement && i < fd->achievement.req_counter.size(); ++i) { made_achievement = made_achievement && fd->achievement.req_counter[i].check(fd->achievement_counter[i]); @@ -740,7 +738,7 @@ Results play(Field* fd) else { _DEBUG_MSG(1, "You win (survival).\n"); - return {0, 1, 0, std::min(fd->all_damage_to_commander, 200u), 0}; + return {0, 1, 0, fd->players[1]->commander.m_card->m_health - fd->players[1]->commander.m_hp, 0}; } } // you win @@ -1188,7 +1186,7 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg, bool count #if 0 fd->points_since_last_decision += dmg; #endif - fd->all_damage_to_commander += dmg; + fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::com_total, dmg); } if(status.m_hp == 0) { diff --git a/sim.h b/sim.h index 5eb10007..14c98db1 100644 --- a/sim.h +++ b/sim.h @@ -221,7 +221,6 @@ class Field unsigned current_ci; unsigned last_decision_turn; // unsigned points_since_last_decision; - unsigned all_damage_to_commander; unsigned fusion_count; std::vector achievement_counter; From f6dcdb9501dc57f269d2d969d9420e6a2861c6de Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 23 Aug 2013 10:46:26 +0800 Subject: [PATCH 184/406] Add Phase skill. --- card.h | 2 ++ sim.cpp | 42 ++++++++++++++++++++++++++++++------------ sim.h | 1 + tyrant.cpp | 4 ++-- tyrant.h | 6 +++--- xml.cpp | 2 ++ 6 files changed, 40 insertions(+), 17 deletions(-) diff --git a/card.h b/card.h index e484f375..24e15467 100644 --- a/card.h +++ b/card.h @@ -39,6 +39,7 @@ class Card m_name(""), m_payback(false), m_pierce(0), + m_phase(0), m_poison(0), m_poison_oa(0), m_proto_id(0), @@ -103,6 +104,7 @@ class Card std::string m_name; bool m_payback; unsigned m_pierce; + unsigned m_phase; unsigned m_poison; unsigned m_poison_oa; unsigned m_proto_id; // The id of the prototype card (before upgraded) for an upgraded card. 0 otherwise. diff --git a/sim.cpp b/sim.cpp index 059b0dfe..725e4ff9 100644 --- a/sim.cpp +++ b/sim.cpp @@ -96,6 +96,7 @@ CardStatus::CardStatus(const Card* card) : m_immobilized(false), m_infused(false), m_jammed(false), + m_phased(false), m_poisoned(0), m_protected(0), m_rallied(0), @@ -132,6 +133,7 @@ inline void CardStatus::set(const Card& card) m_immobilized = false; m_infused = false; m_jammed = false; + m_phased = false; m_poisoned = 0; m_protected = 0; m_rallied = 0; @@ -217,6 +219,7 @@ std::string card_description(const Cards& cards, const Card* c) if(c->m_legion > 0) { desc += ", legion " + to_string(c->m_legion); } if(c->m_payback) { desc += ", payback"; } if(c->m_pierce > 0) { desc += ", pierce " + to_string(c->m_pierce); } + if(c->m_phase) { desc += ", phase"; } if(c->m_poison > 0) { desc += ", poison " + to_string(c->m_poison); } if(c->m_refresh) { desc += ", refresh"; } if(c->m_regenerate > 0) { desc += ", regenerate " + to_string(c->m_regenerate); } @@ -282,6 +285,7 @@ std::string CardStatus::description() if(m_immobilized) { desc += ", immobilized"; } if(m_infused) { desc += ", infused"; } if(m_jammed) { desc += ", jammed"; } + if(m_phased) { desc += ", phased"; } if(m_sundered) { desc += ", sundered"; } if(m_temporary_split) { desc += ", cloning"; } if(m_augmented > 0) { desc += ", augmented " + to_string(m_augmented); } @@ -852,6 +856,12 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(ref->m_card->m_type == CardType::assault && c->m_player != ref->m_player && ref->m_hp > 0); } +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(!ref->m_phased); +} + template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { return(can_be_healed(c)); } @@ -1069,6 +1079,7 @@ void turn_start_phase(Field* fd) status.m_frozen = false; status.m_immobilized = false; status.m_jammed = false; + status.m_phased = false; status.m_rallied = 0; if(status.m_stunned > 0) { -- status.m_stunned; } status.m_weakened = 0; @@ -1220,7 +1231,7 @@ struct PerformAttack // modify damage // assaults only: immobilize // deal damage - // assaults only: (siphon, poison, disease, sunder, on_kill) + // assaults only: (siphon, poison, disease, sunder, phase, on_kill) // on_attacked: poison, disease, sunder, assaults only: berserk, skills // counter, berserk // assaults only: (crush, leech if still alive) @@ -1250,7 +1261,7 @@ struct PerformAttack immobilize(); attack_damage(); if(__builtin_expect(fd->end, false)) { return; } - siphon_poison_disease(); + damage_dependant_pre_oa(); on_kill(); } on_attacked(); @@ -1365,7 +1376,7 @@ struct PerformAttack } template - void siphon_poison_disease() {} + void damage_dependant_pre_oa() {} template void on_kill() {} @@ -1424,7 +1435,7 @@ void PerformAttack::attack_damage() } template<> -void PerformAttack::siphon_poison_disease() +void PerformAttack::damage_dependant_pre_oa() { if(att_status->m_card->m_siphon > 0 && skill_check(fd, att_status, def_status)) { @@ -1456,6 +1467,13 @@ void PerformAttack::siphon_poison_disease() _DEBUG_MSG(1, "%s sunders %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); def_status->m_sundered = true; } + if(att_status->m_card->m_phase && skill_check(fd, att_status, def_status)) + { + count_achievement(fd, att_status); + // perform_skill_phase + _DEBUG_MSG(1, "%s phases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); + def_status->m_phased = true; + } } template<> @@ -1843,26 +1861,26 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) } template -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) +inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s, bool is_helpful_skill) { if(std::get<2>(s) == allfactions) { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s, is_helpful_skill](CardStatus* c){return(!(is_helpful_skill && c->m_phased) && skill_predicate(fd, src_status, c, s));})); } else { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return(c->m_faction == std::get<2>(s) && skill_predicate(fd, src_status, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s, is_helpful_skill](CardStatus* c){return(c->m_faction == std::get<2>(s) && !(is_helpful_skill && c->m_phased) && skill_predicate(fd, src_status, c, s));})); } } template<> -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) +inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s, bool is_helpful_skill) { // mimiced supply by a structure, etc ? if(!(src_status->m_card->m_type == CardType::assault)) { return(0); } const unsigned min_index(src_status->m_index - (src_status->m_index == 0 ? 0 : 1)); const unsigned max_index(src_status->m_index + (src_status->m_index == cards.size() - 1 ? 0 : 1)); - return(fd->make_selection_array(cards.begin() + min_index, cards.begin() + max_index + 1, [fd, src_status, s](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); + return(fd->make_selection_array(cards.begin() + min_index, cards.begin() + max_index + 1, [fd, src_status, s, is_helpful_skill](CardStatus* c){return(!(is_helpful_skill && c->m_phased) && skill_predicate(fd, src_status, c, s));})); } inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src_status) @@ -2049,7 +2067,7 @@ template void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { std::vector& cards(skill_targets(fd, src_status)); - if(select_fast(fd, src_status, cards, s) == 0) + if(select_fast(fd, src_status, cards, s, false) == 0) { return; } @@ -2110,7 +2128,7 @@ template void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { std::vector& cards(skill_targets(fd, src_status)); - if(select_fast(fd, src_status, cards, s) == 0) + if(select_fast(fd, src_status, cards, s, true) == 0) { return; } @@ -2248,7 +2266,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // so we can probably clear it safely. This is necessary, because mimic calls resolve_skill as well (infinite loop). fd->skill_queue.clear(); std::vector& cards(skill_targets(fd, src_status)); - if(select_fast(fd, src_status, cards, s) == 0) + if(select_fast(fd, src_status, cards, s, false) == 0) { return; } diff --git a/sim.h b/sim.h index 14c98db1..d1f30d37 100644 --- a/sim.h +++ b/sim.h @@ -141,6 +141,7 @@ struct CardStatus bool m_immobilized; bool m_infused; bool m_jammed; + bool m_phased; unsigned m_poisoned; unsigned m_protected; unsigned m_rallied; diff --git a/tyrant.cpp b/tyrant.cpp index 034ead97..f700533c 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -19,9 +19,9 @@ std::string skill_names[Skill::num_skills] = // Combat-Modifier: "AntiAir", "Burst", "Fear", "Flurry", "Pierce", "Swipe", "Valor", // Damage-Dependant: - "Berserk", "Crush", "Disease", "Immobilize", "Leech", "Poison", "Siphon", + "Berserk", "Crush", "Disease", "Immobilize", "Leech", "Phase", "Poison", "Siphon", "Sunder", // Defensive: - "Armored", "Counter", "Emulate", "Evade", "Flying", "Intercept", "Payback", "Refresh", "Regenerate", "Stun", "Sunder", "Tribute", "Wall", + "Armored", "Counter", "Emulate", "Evade", "Flying", "Intercept", "Payback", "Refresh", "Regenerate", "Stun", "Tribute", "Wall", // Triggered: "Blitz", "Legion", // Static (Ignored): diff --git a/tyrant.h b/tyrant.h index 39588188..7c4702ab 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.1.2" +#define TYRANT_OPTIMIZER_VERSION "1.1.3" #include #include @@ -31,9 +31,9 @@ enum Skill // Combat-Modifier: antiair, burst, fear, flurry, pierce, swipe, valor, // Damage-Dependant: - berserk, crush, disease, immobilize, leech, poison, siphon, + berserk, crush, disease, immobilize, leech, phase, poison, siphon, sunder, // Defensive: - armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, sunder, tribute, wall, + armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, tribute, wall, // Triggered: blitz, legion, // Static, ignored: diff --git a/xml.cpp b/xml.cpp index ca65c65e..c03e9ab7 100644 --- a/xml.cpp +++ b/xml.cpp @@ -269,6 +269,8 @@ void read_cards(Cards& cards) { c->m_payback = true; } if(strcmp(skill->first_attribute("id")->value(), "pierce") == 0) { c->m_pierce = atoi(skill->first_attribute("x")->value()); } + if(strcmp(skill->first_attribute("id")->value(), "phase") == 0) + { c->m_phase = true; } if(strcmp(skill->first_attribute("id")->value(), "poison") == 0) { bool attacked(skill->first_attribute("attacked")); From 40fd849a7b1bcaee5a70462df32093f8ad91cbe6 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 29 Aug 2013 11:03:10 +0800 Subject: [PATCH 185/406] Support battleground effects in a more flexible way. --- sim.cpp | 398 ++++++++++++++++++++++++++------------------ tyrant.cpp | 4 +- tyrant.h | 4 +- tyrant_optimize.cpp | 15 +- 4 files changed, 241 insertions(+), 180 deletions(-) diff --git a/sim.cpp b/sim.cpp index 725e4ff9..565d4617 100644 --- a/sim.cpp +++ b/sim.cpp @@ -137,10 +137,10 @@ inline void CardStatus::set(const Card& card) m_poisoned = 0; m_protected = 0; m_rallied = 0; - m_weakened = 0; m_stunned = 0; m_sundered = false; m_temporary_split = false; + m_weakened = 0; m_is_summoned = false; m_step = CardStep::none; } @@ -158,7 +158,8 @@ std::string skill_description(const Cards& cards, const SkillSpec& s) if(std::get<1>(s) == 0) { // Summon X - return(skill_names[std::get<0>(s)] + " X"); + return(skill_names[std::get<0>(s)] + " X" + + skill_activation_modifier_names[std::get<4>(s)]); } else { @@ -355,7 +356,7 @@ inline unsigned opponent(unsigned player) return((player + 1) % 2); } //------------------------------------------------------------------------------ -SkillSpec augmented_skill(const CardStatus* status, const SkillSpec& s) +SkillSpec apply_augment(const CardStatus* status, const SkillSpec& s) { if (std::get<0>(s) == augment || std::get<0>(s) == summon || std::get<1>(s) == 0) { @@ -365,13 +366,13 @@ SkillSpec augmented_skill(const CardStatus* status, const SkillSpec& s) std::get<1>(augmented_s) += status->m_augmented; return(augmented_s); } -SkillSpec fusioned_skill(const SkillSpec& s) +SkillSpec apply_fusion(const SkillSpec& s) { SkillSpec fusioned_s = s; std::get<1>(fusioned_s) *= 2; return(fusioned_s); } -SkillSpec infused_skill(const SkillSpec& s) +SkillSpec apply_infuse(const SkillSpec& s) { if (std::get<2>(s) == allfactions || std::get<2>(s) == bloodthirsty || helpful_skills.find(std::get<0>(s)) == helpful_skills.end()) { @@ -382,21 +383,169 @@ SkillSpec infused_skill(const SkillSpec& s) return(infused_s); } //------------------------------------------------------------------------------ +bool may_change_skill(const Field* fd, const CardStatus* status, const SkillMod::SkillMod mod) +{ + switch (mod) + { + case SkillMod::on_activate: + switch (status->m_card->m_type) + { + case CardType::commander: + return (fd->effect == Effect::time_surge || + fd->effect == Effect::friendly_fire || + fd->effect == Effect::genesis || + (fd->effect == Effect::artillery_strike && fd->turn >= 9 && status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 1 : 0)) || + fd->effect == Effect::decrepit || + fd->effect == Effect::forcefield || + fd->effect == Effect::chilling_touch); + case CardType::assault: + return (fd->effect == Effect::friendly_fire || + ((fd->effect == Effect::clone_project || fd->effect == Effect::clone_experiment) && status->m_temporary_split)); + default: + break; + } + break; + case SkillMod::on_death: + return ((status->m_card->m_type == CardType::assault || status->m_card->m_type == CardType::structure) && + fd->effect == Effect::haunt && status->m_card->m_faction != bloodthirsty); + default: + break; + } + return false; +} +SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, const SkillSpec& ss, const SkillMod::SkillMod mod, bool& need_add_skill) +{ + const auto& skill = std::get<0>(ss); + switch (fd->effect) + { + case Effect::time_surge: + // replace other instance of the skill + if(skill == rush || skill == new_skill) + { + need_add_skill = false; + return SkillSpec(rush, 1, allfactions, false, mod); + } + break; + case Effect::clone_project: + case Effect::clone_experiment: + // no gain the skill if already have + if(skill == split) + { + need_add_skill = false; + } + else if(skill == new_skill) + { + return SkillSpec(split, 0, allfactions, false, mod); + } + break; + case Effect::friendly_fire: + switch (status->m_card->m_type) + { + case CardType::assault: + // no gain the skill if already have + if(skill == strike) + { + need_add_skill = false; + } + else if(skill == new_skill) + { + return SkillSpec(strike, 1, allfactions, false, mod); + } + break; + case CardType::commander: + // replace other instance of the skill + if(skill == chaos || skill == new_skill) + { + need_add_skill = false; + return SkillSpec(chaos, 0, allfactions, true, mod); + } + break; + default: + break; + } + break; + case Effect::genesis: + // replace other instance of the skill + if(skill == summon || skill == new_skill) + { + need_add_skill = false; + return SkillSpec(summon, 0, allfactions, false, mod); + } + break; + case Effect::artillery_strike: + // replace other instance of the skill + if(skill == strike || skill == new_skill) + { + need_add_skill = false; + return SkillSpec(strike, 3, allfactions, true, mod); + } + break; + case Effect::decrepit: + // replace other instance of the skill + if(skill == enfeeble || skill == new_skill) + { + need_add_skill = false; + return SkillSpec(enfeeble, 1, allfactions, true, mod); + } + break; + case Effect::forcefield: + // replace other instance of the skill + if(skill == protect || skill == new_skill) + { + need_add_skill = false; + return SkillSpec(protect, 1, allfactions, true, mod); + } + break; + case Effect::chilling_touch: + // replace other instance of the skill + if(skill == freeze || skill == new_skill) + { + need_add_skill = false; + return SkillSpec(freeze, 0, allfactions, false, mod); + } + break; + case Effect::haunt: + // replace other instance of the skill + if(skill == summon || skill == new_skill) + { + need_add_skill = false; + return SkillSpec(summon, 0, bloodthirsty, false, mod); + } + break; + default: + break; + } + return ss; +} +//------------------------------------------------------------------------------ void prepend_on_death(Field* fd) { - for(auto status: boost::adaptors::reverse(fd->killed_with_on_death)) + std::vector> od_skills; + auto mod = SkillMod::on_death; + for(auto status: fd->killed_with_on_death) { if(status->m_jammed) { _DEBUG_MSG(2, "%s is jammed and cannot activate its on Death skill.\n", status_description(status).c_str()); continue; } - for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_on_death)) + bool need_add_skill = may_change_skill(fd, status, mod); + for(auto& ss: status->m_card->m_skills_on_death) + { + auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, status, ss, mod, need_add_skill) : ss; + _DEBUG_MSG(2, "Preparing %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); + od_skills.emplace_back(status, battleground_s); + //if(__builtin_expect(fd->end, false)) { return; } // so far no "on Death" skill may end the battle + } + if(need_add_skill) { - _DEBUG_MSG(2, "%s pushes its on Death skill in front of the queue: %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); - fd->skill_queue.emplace_front(status, skill); + auto battleground_s = apply_battleground_effect(fd, status, SkillSpec(new_skill, 0, allfactions, false, mod), mod, need_add_skill); + assert(std::get<0>(battleground_s) != new_skill); + _DEBUG_MSG(2, "Preparing %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); + od_skills.emplace_back(status, battleground_s); } } + fd->skill_queue.insert(fd->skill_queue.begin(), od_skills.begin(), od_skills.end()); fd->killed_with_on_death.clear(); } //------------------------------------------------------------------------------ @@ -417,26 +566,36 @@ void resolve_skill(Field* fd) else if(!status->m_jammed) { bool fusion_active = status->m_card->m_fusion && status->m_player == fd->tapi && fd->fusion_count >= 3; - auto& augmented_s = status->m_augmented > 0 ? augmented_skill(status, skill) : skill; - auto& fusioned_s = fusion_active ? fusioned_skill(augmented_s) : augmented_s; - auto& infused_s = status->m_infused ? infused_skill(fusioned_s) : fusioned_s; + auto& augmented_s = status->m_augmented > 0 ? apply_augment(status, skill) : skill; + auto& fusioned_s = fusion_active ? apply_fusion(augmented_s) : augmented_s; + auto& infused_s = status->m_infused ? apply_infuse(fusioned_s) : fusioned_s; skill_table[std::get<0>(skill)](fd, status, infused_s); } } } //------------------------------------------------------------------------------ void attack_phase(Field* fd); -void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills) +void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills, const SkillMod::SkillMod mod) { assert(status); assert(fd->skill_queue.size() == 0); - for(auto& skill: skills) + bool need_add_skill = may_change_skill(fd, status, mod); + for(auto& ss: skills) { - _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, skill).c_str()); - fd->skill_queue.emplace_back(status, skill); + auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, status, ss, mod, need_add_skill) : ss; + _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); + fd->skill_queue.emplace_back(status, battleground_s); resolve_skill(fd); if(__builtin_expect(fd->end, false)) { break; } } + if(need_add_skill) + { + auto battleground_s = apply_battleground_effect(fd, status, SkillSpec(new_skill, 0, allfactions, false, mod), mod, need_add_skill); + assert(std::get<0>(battleground_s) != new_skill); + _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); + fd->skill_queue.emplace_back(status, battleground_s); + resolve_skill(fd); + } } bool check_and_perform_blitz(Field* fd, CardStatus* src_status); struct PlayCard @@ -506,7 +665,7 @@ struct PlayCard template void onPlaySkills() { - evaluate_skills(fd, status, card->m_skills_on_play); + evaluate_skills(fd, status, card->m_skills_on_play, SkillMod::on_play); } }; // assault @@ -554,7 +713,7 @@ void PlayCard::fieldEffects() template <> void PlayCard::onPlaySkills() { - evaluate_skills(fd, status, card->m_skills); + evaluate_skills(fd, status, card->m_skills, SkillMod::on_play); } //------------------------------------------------------------------------------ inline bool is_attacking_or_has_attacked(CardStatus* c) { return(c->m_step >= CardStep::attacking); } @@ -659,7 +818,7 @@ Results play(Field* fd) // Evaluate commander fd->current_phase = Field::commander_phase; - evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); + evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills, SkillMod::on_activate); if(__builtin_expect(fd->end, false)) { break; } // Evaluate structures @@ -669,7 +828,7 @@ Results play(Field* fd) CardStatus& current_status(fd->tap->structures[fd->current_ci]); if(current_status.m_delay == 0 && current_status.m_hp > 0) { - evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); + evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills, SkillMod::on_activate); } } // Evaluate assaults @@ -686,16 +845,7 @@ Results play(Field* fd) } current_status.m_blitzing = false; // Evaluate skills - evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); - if(__builtin_expect(fd->end, false)) { break; } - - // Special case: check for temporary split (clone battlefield effects) - if(current_status.m_temporary_split) - { - std::vector skills; - skills.emplace_back(split, 0, allfactions, false, SkillMod::on_activate); - evaluate_skills(fd, ¤t_status, skills); - } + evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills, SkillMod::on_activate); if(__builtin_expect(fd->end, false)) { break; } // Attack @@ -951,7 +1101,7 @@ void remove_hp(Field* fd, CardStatus& status, unsigned dmg) if(status.m_hp == 0) { _DEBUG_MSG(1, "%s dies\n", status_description(&status).c_str()); - if(status.m_card->m_skills_on_death.size() > 0) + if(status.m_card->m_skills_on_death.size() > 0 || fd->effect == Effect::haunt) { fd->killed_with_on_death.push_back(&status); } @@ -1102,7 +1252,7 @@ void turn_start_phase(Field* fd) { CardStatus& status(structures[index]); status.m_index = index; - if(status.m_card->m_refresh) + if(status.m_card->m_refresh && fd->effect != Effect::impenetrable) { check_and_perform_refresh(fd, &status); } @@ -1335,15 +1485,20 @@ struct PerformAttack // prevent damage std::string reduced_desc; unsigned reduced_dmg(0); - if(def_card.m_armored > 0) + unsigned armored_value(def_card.m_armored); + if(armored_value == 0 && fd->effect == Effect::photon_shield && def_status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 0 : 1)) + { + armored_value = 2; + } + if(armored_value > 0) { // Armored counts if not totally cancelled by Pierce. TODO how if Armored + Proteced > Pierce? - if(def_card.m_armored > att_card.m_pierce) + if(armored_value > att_card.m_pierce) { count_achievement(fd, def_status); } - if(debug_print) { reduced_desc += to_string(def_card.m_armored) + "(armored)"; } - reduced_dmg += def_card.m_armored; + if(debug_print) { reduced_desc += to_string(armored_value) + "(armored)"; } + reduced_dmg += armored_value; } if(def_status->m_protected > 0) { @@ -1400,7 +1555,7 @@ struct PerformAttack } if(def_status->m_hp > 0 && def_status->m_card->m_berserk_oa > 0 && skill_check(fd, def_status, nullptr)) { - count_achievement(fd, def_status); + count_achievement(fd, def_status); def_status->m_berserk += def_status->m_card->m_berserk_oa; } if(def_status->m_card->m_sunder_oa && skill_check(fd, def_status, att_status)) @@ -1410,7 +1565,7 @@ struct PerformAttack _DEBUG_MSG(1, "%s (on attacked) sunders %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); att_status->m_sundered = true; } - evaluate_skills(fd, def_status, def_status->m_card->m_skills_on_attacked); + evaluate_skills(fd, def_status, def_status->m_card->m_skills_on_attacked, SkillMod::on_attacked); } template @@ -1481,7 +1636,7 @@ void PerformAttack::on_kill() { if(killed_by_attack) { - evaluate_skills(fd, att_status, att_status->m_card->m_skills_on_kill); + evaluate_skills(fd, att_status, att_status->m_card->m_skills_on_kill, SkillMod::on_kill); } } @@ -1632,6 +1787,14 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, // Any quantifiable skill except augment if(std::get<1>(s) > 0 && std::get<0>(s) != augment && std::get<0>(s) != summon) { return(true); } } + bool need_add_skill = true; + auto mod = SkillMod::on_activate; + if(may_change_skill(fd, c, mod)) + { + auto s = apply_battleground_effect(fd, c, SkillSpec(new_skill, 0, allfactions, false, mod), mod, need_add_skill); + assert(std::get<0>(s) != new_skill); + if(std::get<1>(s) > 0 && std::get<0>(s) != augment && std::get<0>(s) != summon) { return(true); } + } } return(false); } @@ -2010,7 +2173,7 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ { if(skill_check(fd, src_status, dst_status)) { - if(is_evadable && dst_status->m_card->m_evade && fd->flip() && skill_check(fd, dst_status, src_status)) + if(is_evadable && (dst_status->m_card->m_evade || (fd->effect == Effect::quicksilver && dst_status->m_card->m_type == CardType::assault)) && fd->flip() && skill_check(fd, dst_status, src_status)) { count_achievement(fd, dst_status); _DEBUG_MSG(1, "%s %s (%u) on %s but it evades\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); @@ -2212,7 +2375,19 @@ template void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) { unsigned player = src_status->m_player; - const Card* summoned = std::get<1>(s) != 0 ? fd->cards.by_id(std::get<1>(s)) : fd->random_in_vector(fd->cards.player_assaults); + unsigned summoned_id = std::get<1>(s); + const Card* summoned = 0; + if(summoned_id != 0) + { + summoned = fd->cards.by_id(summoned_id); + } + else + { + Faction summond_faction = std::get<2>(s); + do { + summoned = fd->random_in_vector(fd->cards.player_assaults); + } while(summond_faction != allfactions && summond_faction != summoned->m_faction); + } assert(summoned->m_type == CardType::assault || summoned->m_type == CardType::structure); Hand* hand{fd->players[player]}; if(hand->assaults.size() + hand->structures.size() < 100) @@ -2268,14 +2443,14 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) std::vector& cards(skill_targets(fd, src_status)); if(select_fast(fd, src_status, cards, s, false) == 0) { - return; + return; } _DEBUG_SELECTION("%s", skill_names[mimic].c_str()); CardStatus* c(select_interceptable(fd, src_status, fd->rand(0, fd->selection_array.size() - 1))); // evade check for mimic // individual skills are subject to evade checks too, // but resolve_skill will handle those. - if(c->m_card->m_evade && fd->flip() && skill_check(fd, c, src_status)) + if((c->m_card->m_evade || (fd->effect == Effect::quicksilver && c->m_card->m_type == CardType::assault)) && fd->flip() && skill_check(fd, c, src_status)) { count_achievement(fd, c); _DEBUG_MSG(1, "%s %s on %s but it evades\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); @@ -2283,20 +2458,32 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) } count_achievement(fd, src_status); _DEBUG_MSG(1, "%s %s on %s\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); - for(auto skill: c->m_card->m_skills) + auto mod = SkillMod::on_activate; + bool need_add_skill = may_change_skill(fd, c, mod); + for(auto& skill: c->m_card->m_skills) { if(src_status->m_card->m_type != CardType::action && src_status->m_hp == 0) { break; } if(std::get<0>(skill) == mimic || std::get<0>(skill) == split || (std::get<0>(skill) == supply && src_status->m_card->m_type != CardType::assault)) { continue; } - SkillSpec mimic_s(std::get<0>(skill), std::get<1>(skill), allfactions, std::get<3>(skill), SkillMod::on_activate); - _DEBUG_MSG(2, "Evaluating %s mimiced skill %s\n", status_description(c).c_str(), skill_description(fd->cards, skill).c_str()); + auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, c, skill, mod, need_add_skill) : skill; + SkillSpec mimic_s(std::get<0>(battleground_s), std::get<1>(battleground_s), allfactions, std::get<3>(battleground_s), mod); + _DEBUG_MSG(2, "Evaluating %s mimiced skill %s\n", status_description(c).c_str(), skill_description(fd->cards, mimic_s).c_str()); fd->skill_queue.emplace_back(src_status, mimic_s); resolve_skill(fd); if(__builtin_expect(fd->end, false)) { break; } check_regeneration(fd); } + if(need_add_skill) + { + auto battleground_s = apply_battleground_effect(fd, c, SkillSpec(new_skill, 0, allfactions, false, mod), mod, need_add_skill); + assert(std::get<0>(battleground_s) != new_skill); + SkillSpec mimic_s(std::get<0>(battleground_s), std::get<1>(battleground_s), allfactions, std::get<3>(battleground_s), mod); + _DEBUG_MSG(2, "Evaluating %s mimiced skill %s\n", status_description(c).c_str(), skill_description(fd->cards, mimic_s).c_str()); + fd->skill_queue.emplace_back(src_status, mimic_s); + resolve_skill(fd); + } } //------------------------------------------------------------------------------ void fill_skill_table() @@ -2325,122 +2512,3 @@ void fill_skill_table() skill_table[trigger_regen] = perform_trigger_regen; skill_table[weaken] = perform_targetted_hostile_fast; } -//------------------------------------------------------------------------------ -// Utility functions for modify_cards. - -// Adds the skill " " to all cards of cardtype. -// If the card has an instance of in its skill list and replace_instance==true, the new skill replaces it. -// If the card has no instance of in its skill list, the new skill is added on to the end. -template -inline void cards_gain_skill(Cards& cards, Skill new_skill, unsigned magnitude, bool all, bool replace_instance) -{ - for(Card* card: cards.cards) - { - if(card->m_type != cardtype) - { - continue; - } - - bool do_add_skill(true); - for(auto& skill: card->m_skills) - { - if(std::get<0>(skill) == new_skill) - { - if(replace_instance) - { - skill = std::make_tuple(new_skill, magnitude, allfactions, all, SkillMod::on_activate); - } - do_add_skill = false; - } - } - - if(do_add_skill) - { - card->add_skill(new_skill, magnitude, allfactions, all); - } - } -} -//------------------------------------------------------------------------------ -void modify_cards(Cards& cards, enum Effect effect) -{ - switch (effect) - { - case Effect::none: - break; - case Effect::time_surge: - cards_gain_skill(cards, rush, 1, false, false); - break; - case Effect::copycat: - // Do nothing; this is implemented in perform_mimic - break; - case Effect::quicksilver: - for(Card* card: cards.cards) - { - if(card->m_type == CardType::assault) - { - card->m_evade = true; - } - } - break; - case Effect::decay: - // Do nothing; this is implemented in PlayCard::fieldEffects, - // summon_card, and perform_skill - break; - case Effect::high_skies: - // Do nothing; this is implemented in PerformAttack - break; - case Effect::impenetrable: - // Also implemented in PerformAttack - for(Card* card: cards.cards) - { - if(card->m_type == CardType::structure) - { - card->m_refresh = false; - } - } - break; - case Effect::invigorate: - // Do nothing; this is implemented in add_hp - break; - case Effect::clone_project: - // Do nothing; this is implemented in play - break; - case Effect::friendly_fire: - cards_gain_skill(cards, strike, 1, false, false); - cards_gain_skill(cards, chaos, 0, true, true); - break; - case Effect::genesis: - cards_gain_skill(cards, summon, 0, false, false); // No replace exising Summon, if any - break; - case Effect::artillery_fire: - case Effect::photon_shield: - throw std::runtime_error("Unused effect"); - break; - case Effect::decrepit: - cards_gain_skill(cards, enfeeble, 1, true, true); - break; - case Effect::forcefield: - cards_gain_skill(cards, protect, 1, true, true); - break; - case Effect::chilling_touch: - cards_gain_skill(cards, freeze, 0, false, false); - break; - case Effect::clone_experiment: - // Do nothing; this is implemented in play - break; - case Effect::toxic: - // Do nothing; this is implemented in PlayCard::fieldEffects - // and summon_card - break; - case Effect::haunt: - throw std::runtime_error("Unused effect"); - break; - case Effect::united_front: - // Do nothing: this is implemented in evaluate_legion - break; - default: - // TODO: throw something more useful - throw effect; - break; - } -} diff --git a/tyrant.cpp b/tyrant.cpp index f700533c..dfbc6974 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -26,6 +26,8 @@ std::string skill_names[Skill::num_skills] = "Blitz", "Legion", // Static (Ignored): /* "Blizzard", "Fusion", "Mist", */ + // Placeholder for new gained skill from battleground effect: + "" }; std::set helpful_skills{ @@ -52,7 +54,7 @@ std::string effect_names[Effect::num_effects] = { "Clone Project", "Friendly Fire", "Genesis", - "Artillery Fire", + "Artillery Strike", "Photon Shield", "Decrepit", "Forcefield", diff --git a/tyrant.h b/tyrant.h index 7c4702ab..3ae87859 100644 --- a/tyrant.h +++ b/tyrant.h @@ -38,6 +38,8 @@ enum Skill blitz, legion, // Static, ignored: /* blizzard, fusion, mist, */ + // Placeholder for new gained skill from battleground effect: + new_skill, num_skills }; extern std::string skill_names[num_skills]; @@ -95,7 +97,7 @@ enum Effect { clone_project, friendly_fire, genesis, - artillery_fire, + artillery_strike, photon_shield, decrepit, forcefield, diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 6fb981f7..868c3a90 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -276,7 +276,7 @@ struct SimulationData { att_hand.reset(re); def_hand->reset(re); - Field fd(re, cards, att_hand, *def_hand, gamemode, optimization_mode, effect, achievement); + Field fd(re, cards, att_hand, *def_hand, gamemode, optimization_mode, effect != Effect::none ? effect : def_hand->deck->effect, achievement); Results result(play(&fd)); res.emplace_back(result); } @@ -1165,17 +1165,6 @@ int main(int argc, char** argv) turn_limit = 30; target_score = 250; } - // Set quest effect: - Effect this_effect = def_deck->effect; - if(this_effect != Effect::none) - { - if(effect != Effect::none && effect != this_effect) - { - std::cerr << "Error: Inconsistent effects: " << effect_names[effect] << " and " << effect_names[this_effect] << ".\n"; - return(7); - } - effect = this_effect; - } def_decks.push_back(def_deck); def_decks_factors.push_back(deck_parsed.second); } @@ -1400,7 +1389,6 @@ int main(int argc, char** argv) min_deck_len = max_deck_len = att_deck->cards.size(); } - modify_cards(cards, effect); std::cout << "Your Deck: " << (debug_print ? att_deck->long_description(cards) : att_deck->short_description()) << std::endl; for(auto def_deck: def_decks) { @@ -1412,6 +1400,7 @@ int main(int argc, char** argv) } Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); + { //ScopeClock timer; for(auto op: todo) From 981c22cb6a00bd2a51e82e13ef3a14670fc56aab Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 10 Sep 2013 23:02:17 +0800 Subject: [PATCH 186/406] Raid damage is 100 for slaying boss in Arctis Vanguard. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 565d4617..6ac4b17e 100644 --- a/sim.cpp +++ b/sim.cpp @@ -887,7 +887,7 @@ Results play(Field* fd) if(fd->players[1]->commander.m_hp == 0) { _DEBUG_MSG(1, "You win (boss killed).\n"); - return {1, 0, 0, 250, 0}; + return {1, 0, 0, fd->players[1]->commander.m_card->m_health + 50, 0}; } else { From 4f7da8db7e722887909f94b173df6db7272a474a Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 13 Sep 2013 00:47:26 +0800 Subject: [PATCH 187/406] Support harmonic mean. --- tyrant_optimize.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 868c3a90..cadb9229 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -50,6 +50,7 @@ namespace { bool auto_upgrade_cards{true}; long double target_score{100}; bool show_stdev{false}; + bool use_harmonic_mean{false}; } using namespace std::placeholders; @@ -198,14 +199,20 @@ Results compute_score(const std::pair final.wins += results.first[index].wins * factors[index]; final.draws += results.first[index].draws * factors[index]; final.losses += results.first[index].losses * factors[index]; - final.points += results.first[index].points * factors[index]; + if(use_harmonic_mean) + { final.points += factors[index] / results.first[index].points; } + else + { final.points += results.first[index].points * factors[index]; } final.sq_points += results.first[index].sq_points * factors[index] * factors[index]; } long double factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); final.wins /= factor_sum * (long double)results.second; final.draws /= factor_sum * (long double)results.second; final.losses /= factor_sum * (long double)results.second; - final.points /= factor_sum * (long double)results.second; + if(use_harmonic_mean) + { final.points = factor_sum / ((long double)results.second * final.points); } + else + { final.points /= factor_sum * (long double)results.second; } final.sq_points /= factor_sum * factor_sum * (long double)results.second; return final; } @@ -1325,6 +1332,10 @@ int main(int argc, char** argv) { show_stdev = true; } + else if(strcmp(argv[argIndex], "+hm") == 0) + { + use_harmonic_mean = true; + } else if(strcmp(argv[argIndex], "+v") == 0) { ++ debug_print; From 7f12e1c75875426e39d6ff7a663b256d94e31f41 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 22 Sep 2013 15:30:56 +0800 Subject: [PATCH 188/406] Add debug info. --- tyrant_optimize.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index cadb9229..8a533ffd 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -111,7 +111,10 @@ void claim_cards(const std::vector & card_list, const Cards & cards if(num_to_claim > 0) { owned_cards[card->m_id] += num_to_claim; -// std::cout << "Claim " << card->m_name << " (" << num_to_claim << ")" << std::endl; + if(debug_print) + { + std::cout << "Claim " << card->m_name << " (" << num_to_claim << ")" << std::endl; + } } } } From f4907c7be4c3aa2c83e7819a00edfc875db56695 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 26 Sep 2013 08:10:52 +0800 Subject: [PATCH 189/406] Support battleground effect for raids. --- xml.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/xml.cpp b/xml.cpp index c03e9ab7..76bac4ce 100644 --- a/xml.cpp +++ b/xml.cpp @@ -460,7 +460,13 @@ void read_raids(Decks& decks, const Cards& cards, std::string filename) unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(raid_node->first_node("name")); std::string deck_name{name_node->value()}; - read_deck(decks, cards, raid_node, DeckType::raid, id, deck_name); + Deck* deck = read_deck(decks, cards, raid_node, DeckType::raid, id, deck_name); + xml_node<>* effect_id_node(raid_node->first_node("effect")); + if(effect_id_node) + { + int effect_id(effect_id_node ? atoi(effect_id_node->value()) : 0); + deck->effect = static_cast(effect_id); + } } } //------------------------------------------------------------------------------ @@ -487,10 +493,10 @@ void read_quests(Decks& decks, const Cards& cards, std::string filename) assert(id_node); unsigned id(id_node ? atoi(id_node->value()) : 0); std::string deck_name{"Step " + std::string{id_node->value()}}; - xml_node<>* battleground_id_node(quest_node->first_node("battleground_id")); - int battleground_id(battleground_id_node ? atoi(battleground_id_node->value()) : 0); Deck* deck = read_deck(decks, cards, quest_node, DeckType::quest, id, deck_name); - deck->effect = static_cast(battleground_id); + xml_node<>* effect_id_node(quest_node->first_node("effect_id")); + int effect_id(effect_id_node ? atoi(effect_id_node->value()) : 0); + deck->effect = static_cast(effect_id); } } //------------------------------------------------------------------------------ From 6bfeecdd6c6ef008e7cf1a745ba0a271a6eb95f0 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 26 Sep 2013 09:20:23 +0800 Subject: [PATCH 190/406] Honor summon limit (29 + deck size). --- sim.cpp | 59 ++++++++++++++++++++++++++++++++++---------------------- sim.h | 1 + tyrant.h | 2 +- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/sim.cpp b/sim.cpp index 6ac4b17e..b834caac 100644 --- a/sim.cpp +++ b/sim.cpp @@ -749,6 +749,9 @@ Results play(Field* fd) fd->points_since_last_decision = 0; #endif unsigned p0_size = fd->players[0]->deck->cards.size(); + unsigned p1_size = fd->players[1]->deck->cards.size(); + fd->players[0]->available_summons = 29 + p0_size; + fd->players[1]->available_summons = 29 + p1_size; fd->last_decision_turn = p0_size == 1 ? 0 : p0_size * 2 - (fd->gamemode == surge ? 2 : 3); // Count commander as played for achievements (not count in type / faction / rarity requirements) @@ -2375,6 +2378,19 @@ template void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) { unsigned player = src_status->m_player; + const auto& mod = std::get<4>(s); + if(skill_id == summon && mod == SkillMod::on_activate) + { + if(fd->players[player]->available_summons == 0) + { + return; + } + -- fd->players[player]->available_summons; + if(fd->players[player]->available_summons == 0) + { + _DEBUG_MSG(1, "** Reaching summon limit, this is the last summon.\n"); + } + } unsigned summoned_id = std::get<1>(s); const Card* summoned = 0; if(summoned_id != 0) @@ -2390,33 +2406,30 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) } assert(summoned->m_type == CardType::assault || summoned->m_type == CardType::structure); Hand* hand{fd->players[player]}; - if(hand->assaults.size() + hand->structures.size() < 100) + count_achievement(fd, src_status); + Storage* storage{summoned->m_type == CardType::assault ? &hand->assaults : &hand->structures}; + CardStatus& card_status(storage->add_back()); + card_status.set(summoned); + card_status.m_index = storage->size() - 1; + card_status.m_player = player; + card_status.m_is_summoned = true; + _DEBUG_MSG(1, "%s %s %s %u [%s]\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); + prepend_skills(fd, &card_status); + // Summon X (Genesis effect) does not activate Blitz for X + if(std::get<1>(s) != 0 && card_status.m_card->m_blitz) + { + check_and_perform_blitz(fd, &card_status); + } + if(summoned->m_type == CardType::assault) { - count_achievement(fd, src_status); - Storage* storage{summoned->m_type == CardType::assault ? &hand->assaults : &hand->structures}; - CardStatus& card_status(storage->add_back()); - card_status.set(summoned); - card_status.m_index = storage->size() - 1; - card_status.m_player = player; - card_status.m_is_summoned = true; - _DEBUG_MSG(1, "%s %s %s %u [%s]\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); - prepend_skills(fd, &card_status); - // Summon X (Genesis effect) does not activate Blitz for X - if(std::get<1>(s) != 0 && card_status.m_card->m_blitz) + if(fd->effect == Effect::toxic) { - check_and_perform_blitz(fd, &card_status); + card_status.m_poisoned = 1; } - if(summoned->m_type == CardType::assault) + else if(fd->effect == Effect::decay) { - if(fd->effect == Effect::toxic) - { - card_status.m_poisoned = 1; - } - else if(fd->effect == Effect::decay) - { - card_status.m_poisoned = 1; - card_status.m_diseased = true; - } + card_status.m_poisoned = 1; + card_status.m_diseased = true; } } } diff --git a/sim.h b/sim.h index d1f30d37..40462ce2 100644 --- a/sim.h +++ b/sim.h @@ -179,6 +179,7 @@ class Hand CardStatus commander; Storage assaults; Storage structures; + unsigned available_summons; }; //------------------------------------------------------------------------------ // struct Field is the data model of a battle: diff --git a/tyrant.h b/tyrant.h index 3ae87859..9fd08b24 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.1.3" +#define TYRANT_OPTIMIZER_VERSION "1.1.4" #include #include From 8dee22612352124ce8e28ef5b352ff80589a7af0 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 29 Sep 2013 08:54:04 +0800 Subject: [PATCH 191/406] Fix typo: automatically apply battleground effect for quests. --- xml.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml.cpp b/xml.cpp index 76bac4ce..cb1413bd 100644 --- a/xml.cpp +++ b/xml.cpp @@ -494,7 +494,7 @@ void read_quests(Decks& decks, const Cards& cards, std::string filename) unsigned id(id_node ? atoi(id_node->value()) : 0); std::string deck_name{"Step " + std::string{id_node->value()}}; Deck* deck = read_deck(decks, cards, quest_node, DeckType::quest, id, deck_name); - xml_node<>* effect_id_node(quest_node->first_node("effect_id")); + xml_node<>* effect_id_node(quest_node->first_node("battleground_id")); int effect_id(effect_id_node ? atoi(effect_id_node->value()) : 0); deck->effect = static_cast(effect_id); } From 81f50d35176f98f387d1e4ee6a78652293c68e3c Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 30 Sep 2013 11:12:57 +0800 Subject: [PATCH 192/406] Add debug function: specify enemy hand. --- tyrant_optimize.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 8a533ffd..87b1ec65 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1348,6 +1348,14 @@ int main(int argc, char** argv) att_deck->set_given_hand(cards, argv[argIndex + 1]); argIndex += 1; } + else if(strcmp(argv[argIndex], "defender:hand") == 0) // set enemies' initial hand for test + { + for(auto def_deck: def_decks) + { + def_deck->set_given_hand(cards, argv[argIndex + 1]); + } + argIndex += 1; + } else if(strcmp(argv[argIndex], "sim") == 0) { todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); From a5a33cab1f958c8f540c8b636b662628bec36c24 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 30 Sep 2013 11:29:24 +0800 Subject: [PATCH 193/406] Fix debug info. --- sim.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index b834caac..9e3268fe 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1615,21 +1615,21 @@ void PerformAttack::damage_dependant_pre_oa() { count_achievement(fd, att_status); // perform_skill_disease - _DEBUG_MSG(1, "%s diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); + _DEBUG_MSG(1, "%s diseases %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_diseased = true; } if(att_status->m_card->m_sunder && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_sunder - _DEBUG_MSG(1, "%s sunders %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); + _DEBUG_MSG(1, "%s sunders %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_sundered = true; } if(att_status->m_card->m_phase && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_phase - _DEBUG_MSG(1, "%s phases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); + _DEBUG_MSG(1, "%s phases %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_phased = true; } } From 40fff5b7096ce893b37ffd4ccd63a86e26001d58 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 22 Oct 2013 14:35:12 +0800 Subject: [PATCH 194/406] Support battleground effect Harsh Conditions (-e 20). --- sim.cpp | 10 +++++++--- tyrant.cpp | 1 + tyrant.h | 3 ++- tyrant_optimize.cpp | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/sim.cpp b/sim.cpp index 9e3268fe..adeeb1ae 100644 --- a/sim.cpp +++ b/sim.cpp @@ -394,7 +394,7 @@ bool may_change_skill(const Field* fd, const CardStatus* status, const SkillMod: return (fd->effect == Effect::time_surge || fd->effect == Effect::friendly_fire || fd->effect == Effect::genesis || - (fd->effect == Effect::artillery_strike && fd->turn >= 9 && status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 1 : 0)) || + (fd->effect == Effect::artillery_strike && fd->turn >= 9 && status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 1u : 0u)) || fd->effect == Effect::decrepit || fd->effect == Effect::forcefield || fd->effect == Effect::chilling_touch); @@ -635,7 +635,7 @@ struct PlayCard status->set(card); status->m_index = storage->size() - 1; status->m_player = fd->tapi; - if(fd->turn == 1 && fd->gamemode == tournament && status->m_delay > 0) + if((fd->turn == 1 && fd->gamemode == tournament && status->m_delay > 0) || (type == CardType::assault && fd->effect == Effect::harsh_conditions)) { ++status->m_delay; } @@ -1489,7 +1489,7 @@ struct PerformAttack std::string reduced_desc; unsigned reduced_dmg(0); unsigned armored_value(def_card.m_armored); - if(armored_value == 0 && fd->effect == Effect::photon_shield && def_status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 0 : 1)) + if(armored_value == 0 && fd->effect == Effect::photon_shield && def_status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 0u : 1u)) { armored_value = 2; } @@ -2412,6 +2412,10 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) card_status.set(summoned); card_status.m_index = storage->size() - 1; card_status.m_player = player; + if(summoned->m_type == CardType::assault && fd->effect == Effect::harsh_conditions) + { + ++card_status.m_delay; + } card_status.m_is_summoned = true; _DEBUG_MSG(1, "%s %s %s %u [%s]\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); prepend_skills(fd, &card_status); diff --git a/tyrant.cpp b/tyrant.cpp index dfbc6974..adb32520 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -63,6 +63,7 @@ std::string effect_names[Effect::num_effects] = { "Toxic", "Haunt", "United Front", + "Harsh Conditions", }; std::string achievement_misc_req_names[AchievementMiscReq::num_achievement_misc_reqs] = { diff --git a/tyrant.h b/tyrant.h index 9fd08b24..e6ef1734 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.1.4" +#define TYRANT_OPTIMIZER_VERSION "1.1.5" #include #include @@ -106,6 +106,7 @@ enum Effect { toxic, haunt, united_front, + harsh_conditions, num_effects }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 87b1ec65..35800c48 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1063,7 +1063,7 @@ void usage(int argc, char** argv) "\n" "Flags:\n" " -A : optimize for the achievement specified by either id or name.\n" - " -e : set the battleground effect. effect is automatically set for quests.\n" + " -e : set the battleground effect. effect is automatically set for quests and raids.\n" " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" " -s: use surge (default is fight).\n" " -t : set the number of threads, default is 4.\n" From 8a5c3df278b2b25b72363399d916d02aaf73d55f Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 22 Oct 2013 17:52:55 +0800 Subject: [PATCH 195/406] Fix display of win rate in defense mode. --- sim.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index adeeb1ae..cbf0ab52 100644 --- a/sim.cpp +++ b/sim.cpp @@ -921,7 +921,10 @@ Results play(Field* fd) if (fd->turn > turn_limit) { _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); - return {0, 1, 0, fd->optimization_mode == OptimizationMode::defense ? 100ul : 0ul, 0}; + if (fd->optimization_mode == OptimizationMode::defense) + { return {1, 1, 0, 100, 0}; } + else + { return {0, 1, 0, 0, 0}; } } // Huh? How did we get here? From ca917440106e70e5cc3e2701ead16f2a46646289 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 24 Oct 2013 11:08:53 +0800 Subject: [PATCH 196/406] Summon on Death/Attacked/Kill now count towards the Summon Limit. --- sim.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index cbf0ab52..a4cce62b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -2382,7 +2382,8 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) { unsigned player = src_status->m_player; const auto& mod = std::get<4>(s); - if(skill_id == summon && mod == SkillMod::on_activate) + // Split and Summon on Play are not counted towards the Summon Limit. + if(skill_id == summon && mod != SkillMod::on_play) { if(fd->players[player]->available_summons == 0) { From 993070ca1346a30c03765bd298b94dc08bc8e299 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 31 Oct 2013 07:43:24 +0800 Subject: [PATCH 197/406] Support battleground effect for missions. --- tyrant.h | 2 +- tyrant_optimize.cpp | 2 +- xml.cpp | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tyrant.h b/tyrant.h index e6ef1734..3eaebcf9 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.1.5" +#define TYRANT_OPTIMIZER_VERSION "1.1.6" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 35800c48..80b2234e 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1063,7 +1063,7 @@ void usage(int argc, char** argv) "\n" "Flags:\n" " -A : optimize for the achievement specified by either id or name.\n" - " -e : set the battleground effect. effect is automatically set for quests and raids.\n" + " -e : set the battleground effect. effect is automatically set when applicable.\n" " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" " -s: use surge (default is fight).\n" " -t : set the number of threads, default is 4.\n" diff --git a/xml.cpp b/xml.cpp index cb1413bd..06b08c33 100644 --- a/xml.cpp +++ b/xml.cpp @@ -435,7 +435,13 @@ void read_missions(Decks& decks, const Cards& cards, std::string filename) unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(mission_node->first_node("name")); std::string deck_name{name_node->value()}; - read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name); + Deck* deck = read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name); + xml_node<>* effect_id_node(mission_node->first_node("effect")); + if(effect_id_node) + { + int effect_id(effect_id_node ? atoi(effect_id_node->value()) : 0); + deck->effect = static_cast(effect_id); + } } } //------------------------------------------------------------------------------ From 603944dbf78e3ab1cc786d2b75d0dd93b780e7bd Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 2 Nov 2013 12:34:30 +0800 Subject: [PATCH 198/406] Support 'hidden only' cards. --- read.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/read.cpp b/read.cpp index 3dbf4426..4ebe3951 100644 --- a/read.cpp +++ b/read.cpp @@ -112,6 +112,10 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ simple_name = simplify_name(abbr_it->second); } auto card_it = cards.player_cards_by_name.find({simple_name, 0}); + if(card_it == cards.player_cards_by_name.end()) + { + card_it = cards.player_cards_by_name.find({simple_name, 1}); + } auto card_id_iter = advance_until(simple_name.begin(), simple_name.end(), [](char c){return(c=='[');}); if(card_it != cards.player_cards_by_name.end()) { From 51754444a55b5c945e26f7ed1f5c432a5a407edd Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 29 Mar 2014 23:48:18 +0800 Subject: [PATCH 199/406] Support Tyrant Unleashed. --- Makefile | 2 +- card.h | 143 +++++++------ cards.cpp | 36 +++- deck.cpp | 60 +++++- deck.h | 4 + read.cpp | 8 +- read.h | 2 +- sim.cpp | 412 ++++++++++++++++++++++------------- sim.h | 10 + tyrant.cpp | 22 +- tyrant.h | 34 ++- tyrant_optimize.cpp | 149 ++++++++++--- xml.cpp | 509 +++++++++++++++++++++++--------------------- 13 files changed, 874 insertions(+), 517 deletions(-) diff --git a/Makefile b/Makefile index d9a57c48..3ac93bc2 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ MAIN := tyrant_optimize SRCS := $(wildcard *.cpp) OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) -CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 +CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -DTYRANT_UNLEASHED LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem all: $(MAIN) diff --git a/card.h b/card.h index 24e15467..c0e0b660 100644 --- a/card.h +++ b/card.h @@ -7,6 +7,70 @@ class Card { +public: + unsigned m_antiair; + unsigned m_armored; + unsigned m_attack; + unsigned m_base_id; // The id of the original card if a card is unique and alt/upgraded. The own id of the card otherwise. + unsigned m_berserk; + unsigned m_berserk_oa; + bool m_blitz; + unsigned m_burst; + unsigned m_counter; + unsigned m_crush; + unsigned m_delay; + bool m_disease; + bool m_disease_oa; + bool m_emulate; + unsigned m_evade; + Faction m_faction; + bool m_fear; + unsigned m_final_id; // The id of fully upgraded card + unsigned m_flurry; + bool m_flying; + bool m_fusion; + unsigned m_health; + unsigned m_hidden; + unsigned m_id; + bool m_immobilize; + unsigned m_inhibit; + bool m_intercept; + unsigned m_leech; + unsigned m_legion; + unsigned m_level; +#if defined(TYRANT_UNLEASHED) + std::map m_material_list; +#endif + std::string m_name; + bool m_payback; + unsigned m_pierce; + unsigned m_phase; + unsigned m_poison; + unsigned m_poison_oa; + unsigned m_proto_id; // The id of the prototype card (before upgraded) for an upgraded card. 0 otherwise. + unsigned m_rarity; + bool m_refresh; + unsigned m_regenerate; + unsigned m_replace; + unsigned m_reserve; + unsigned m_set; + unsigned m_siphon; + bool m_split; + bool m_stun; + bool m_sunder; + bool m_sunder_oa; + bool m_swipe; + bool m_tribute; + bool m_unique; + unsigned m_upgrade_consumables; + unsigned m_upgrade_gold_cost; + unsigned m_upgraded_id; // The id of the upgraded card for an upgradable card. 0 otherwise. + unsigned m_valor; + bool m_wall; + std::vector m_skills[SkillMod::num_skill_activation_modifiers]; + bool m_skills_set[num_skills]; + CardType::CardType m_type; + public: Card() : m_antiair(0), @@ -23,9 +87,10 @@ class Card m_disease(false), m_disease_oa(false), m_emulate(false), - m_evade(false), + m_evade(0), m_faction(imperial), m_fear(false), + m_final_id(0), m_flurry(0), m_flying(false), m_fusion(false), @@ -33,9 +98,14 @@ class Card m_hidden(0), m_id(0), m_immobilize(false), + m_inhibit(0), m_intercept(false), m_leech(0), m_legion(0), + m_level(1), +#if defined(TYRANT_UNLEASHED) + m_material_list(), +#endif m_name(""), m_payback(false), m_pierce(0), @@ -63,76 +133,7 @@ class Card { } - void add_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills.push_back(std::make_tuple(v1, v2, v3, v4, SkillMod::on_activate)); } - void add_played_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_play.push_back(std::make_tuple(v1, v2, v3, v4, SkillMod::on_play)); } - void add_died_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_death.push_back(std::make_tuple(v1, v2, v3, v4, SkillMod::on_death)); } - void add_attacked_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_attacked.push_back(std::make_tuple(v1, v2, v3, v4, SkillMod::on_attacked)); } - void add_kill_skill(Skill v1, unsigned v2, Faction v3, bool v4) - { m_skills_on_kill.push_back(std::make_tuple(v1, v2, v3, v4, SkillMod::on_kill)); } - - unsigned m_antiair; - unsigned m_armored; - unsigned m_attack; - unsigned m_base_id; // The id of the original card if a card is unique and alt/upgraded. The own id of the card otherwise. - unsigned m_berserk; - unsigned m_berserk_oa; - bool m_blitz; - unsigned m_burst; - unsigned m_counter; - unsigned m_crush; - unsigned m_delay; - bool m_disease; - bool m_disease_oa; - bool m_emulate; - bool m_evade; - Faction m_faction; - bool m_fear; - unsigned m_flurry; - bool m_flying; - bool m_fusion; - unsigned m_health; - unsigned m_hidden; - unsigned m_id; - bool m_immobilize; - bool m_intercept; - unsigned m_leech; - unsigned m_legion; - std::string m_name; - bool m_payback; - unsigned m_pierce; - unsigned m_phase; - unsigned m_poison; - unsigned m_poison_oa; - unsigned m_proto_id; // The id of the prototype card (before upgraded) for an upgraded card. 0 otherwise. - unsigned m_rarity; - bool m_refresh; - unsigned m_regenerate; - unsigned m_replace; - unsigned m_reserve; - unsigned m_set; - unsigned m_siphon; - bool m_split; - bool m_stun; - bool m_sunder; - bool m_sunder_oa; - bool m_swipe; - bool m_tribute; - bool m_unique; - unsigned m_upgrade_consumables; - unsigned m_upgrade_gold_cost; - unsigned m_upgraded_id; // The id of the upgraded card for an upgradable card. 0 otherwise. - unsigned m_valor; - bool m_wall; - std::vector m_skills; - std::vector m_skills_on_play; - std::vector m_skills_on_death; - std::vector m_skills_on_attacked; - std::vector m_skills_on_kill; - CardType::CardType m_type; + void add_skill(Skill id, unsigned x, Faction y, Skill s, bool all, SkillMod::SkillMod mod=SkillMod::on_activate); }; #endif diff --git a/cards.cpp b/cards.cpp index 072b2cf3..0b661776 100644 --- a/cards.cpp +++ b/cards.cpp @@ -76,16 +76,33 @@ void Cards::organize() player_actions.clear(); for(Card* card: cards) { +// std::cout << "C:" << card->m_id << "\n"; // Remove delimiters from card names size_t pos; while((pos = card->m_name.find_first_of(";:,")) != std::string::npos) { card->m_name.erase(pos, 1); } +#if defined(TYRANT_UNLEASHED) + if(card->m_level == 1) + { + player_cards_by_name[{simplify_name(card->m_name + " Lv.1"), card->m_hidden}] = card; + } + else + { + if (card->m_id == by_id(card->m_base_id)->m_final_id) + { + player_cards_by_name[{simplify_name(card->m_name + "*"), card->m_hidden}] = card; + } + card->m_name += " Lv."; + card->m_name += to_string(card->m_level); + } +#else if(card->m_set == 5002) { card->m_name += '*'; } +#endif cards_by_id[card->m_id] = card; // Card available to players if(card->m_set != 0) @@ -125,7 +142,7 @@ void Cards::organize() for(Card* card: cards) { // generate abbreviations - if(card->m_hidden == 0) + if(card->m_set > 0) { for(auto&& abbr_name : get_abbreviations(card->m_name)) { @@ -136,6 +153,7 @@ void Cards::organize() } } +#if not defined(TYRANT_UNLEASHED) // update proto_id and upgraded_id if(card->m_set == 5002) { @@ -145,5 +163,21 @@ void Cards::organize() card->m_proto_id = proto_card->m_id; proto_card->m_upgraded_id = card->m_id; } +#endif + } +} + +// class Card +void Card::add_skill(Skill id, unsigned x, Faction y, Skill s, bool all, SkillMod::SkillMod mod) +{ + for(auto it = m_skills[mod].begin(); it != m_skills[mod].end(); ++ it) + { + if(it->id == id) + { + m_skills[mod].erase(it); + break; + } } + m_skills[mod].push_back({id, x, y, s, all, mod}); } + diff --git a/deck.cpp b/deck.cpp index e41c1765..369e0b9a 100644 --- a/deck.cpp +++ b/deck.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -34,8 +35,14 @@ std::string deck_hash(const Card* commander, std::vector cards, boo { std::string base64= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; std::stringstream ios; - ios << base64[commander->m_id / 64]; - ios << base64[commander->m_id % 64]; + unsigned card_id = commander->m_id; + if(card_id > 4000) + { + ios << '-'; + card_id -= 4000; + } + ios << base64[card_id / 64]; + ios << base64[card_id % 64]; if(!is_ordered) { std::sort(cards.begin(), cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); @@ -44,7 +51,7 @@ std::string deck_hash(const Card* commander, std::vector cards, boo unsigned num_repeat = 0; for(const Card* card: cards) { - unsigned card_id(card->m_id); + card_id = card->m_id; if(card_id == last_id) { ++ num_repeat; @@ -274,7 +281,7 @@ std::string Deck::long_description(const Cards& all_cards) const } for(auto& pool: raid_cards) { - ios << pool.first << " from:\n"; + ios << pool.first << " of:\n"; for(auto& card: pool.second) { ios << " " << card_description(all_cards, card) << "\n"; @@ -398,3 +405,48 @@ void Deck::place_at_bottom(const Card* card) shuffled_cards.push_back(card); } +#if defined(TYRANT_UNLEASHED) +void load_fusions(Cards& cards) +{ + std::ifstream fusions_file("Fusions.txt"); + if(!fusions_file.is_open()) + { + std::cerr << "Warning: Failed to open Fusions.txt. Proceeding without reading from this file.\n"; + return; + } + + unsigned num_line(0); + fusions_file.exceptions(std::ifstream::badbit); + try + { + while(fusions_file && !fusions_file.eof()) + { + std::string recipe_string; + getline(fusions_file, recipe_string); + ++ num_line; + if(recipe_string.size() == 0 || strncmp(recipe_string.c_str(), "//", 2) == 0) + { + continue; + } + auto && card_list = string_to_ids(cards, recipe_string, "fusion recipe").first; + auto card_it = card_list.begin(); + auto & card = cards.cards_by_id.find(*card_it)->second; + for (++ card_it; card_it != card_list.end(); ++ card_it) + { + ++ card->m_material_list[cards.by_id(*card_it)]; + } + } + } + catch (std::exception& e) + { + std::cerr << "Exception while parsing the recipe file Fusions.txt"; + if(num_line > 0) + { + std::cerr << " at line " << num_line; + } + std::cerr << ": " << e.what() << ". Skip remaining lines.\n"; + return; + } +} +#endif + diff --git a/deck.h b/deck.h index d1e8e4de..1ae80952 100644 --- a/deck.h +++ b/deck.h @@ -117,4 +117,8 @@ struct Decks std::map custom_decks; }; +#if defined(TYRANT_UNLEASHED) +void load_fusions(Cards& cards); +#endif + #endif diff --git a/read.cpp b/read.cpp index 4ebe3951..a7dcfbf2 100644 --- a/read.cpp +++ b/read.cpp @@ -25,9 +25,9 @@ std::vector> parse_deck_list(std::string lis { std::vector> res; boost::tokenizer> list_tokens{list_string, boost::char_delimiters_separator{false, ";", ""}}; - for(auto list_token = list_tokens.begin(); list_token != list_tokens.end(); ++list_token) + for(const auto list_token : list_tokens) { - boost::tokenizer> deck_tokens{*list_token, boost::char_delimiters_separator{false, ":", ""}}; + boost::tokenizer> deck_tokens{list_token, boost::char_delimiters_separator{false, ":", ""}}; auto deck_token = deck_tokens.begin(); res.push_back(std::make_pair(*deck_token, 1.0d)); ++deck_token; @@ -112,10 +112,6 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ simple_name = simplify_name(abbr_it->second); } auto card_it = cards.player_cards_by_name.find({simple_name, 0}); - if(card_it == cards.player_cards_by_name.end()) - { - card_it = cards.player_cards_by_name.find({simple_name, 1}); - } auto card_id_iter = advance_until(simple_name.begin(), simple_name.end(), [](char c){return(c=='[');}); if(card_it != cards.player_cards_by_name.end()) { diff --git a/read.h b/read.h index f9845a9f..80bb51cb 100644 --- a/read.h +++ b/read.h @@ -9,9 +9,9 @@ class Cards; class Decks; class Deck; -void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark); void load_decks(Decks& decks, Cards& cards); std::vector> parse_deck_list(std::string list_string); +void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark); unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename); void read_owned_cards(Cards& cards, std::map& owned_cards, std::map& buyable_cards, const char *filename); unsigned read_card_abbrs(Cards& cards, const std::string& filename); diff --git a/sim.cpp b/sim.cpp index a4cce62b..ee18b42e 100644 --- a/sim.cpp +++ b/sim.cpp @@ -86,15 +86,20 @@ CardStatus::CardStatus(const Card* card) : m_augmented(0), m_berserk(0), m_blitzing(false), + m_cd_jam(0), m_chaosed(false), m_delay(card->m_delay), m_diseased(false), + m_enhanced_skill(no_skill), + m_enhanced_value(0), + m_evaded(0), m_enfeebled(0), m_faction(card->m_faction), m_frozen(false), m_hp(card->m_health), m_immobilized(false), m_infused(false), + m_inhibited(0), m_jammed(false), m_phased(false), m_poisoned(0), @@ -124,14 +129,19 @@ inline void CardStatus::set(const Card& card) m_berserk = 0; m_blitzing = false; m_chaosed = false; + m_cd_jam = 0; m_delay = card.m_delay; m_diseased = false; + m_enhanced_skill = no_skill; + m_enhanced_value = 0; + m_evaded = 0; m_enfeebled = 0; m_faction = card.m_faction; m_frozen = false; m_hp = card.m_health; m_immobilized = false; m_infused = false; + m_inhibited = 0; m_jammed = false; m_phased = false; m_poisoned = 0; @@ -152,27 +162,28 @@ inline int attack_power(CardStatus* att) //------------------------------------------------------------------------------ std::string skill_description(const Cards& cards, const SkillSpec& s) { - switch(std::get<0>(s)) + switch(s.id) { case summon: - if(std::get<1>(s) == 0) + if(s.x == 0) { // Summon X - return(skill_names[std::get<0>(s)] + " X" + - skill_activation_modifier_names[std::get<4>(s)]); + return(skill_names[s.id] + " X" + + skill_activation_modifier_names[s.mod]); } else { - return(skill_names[std::get<0>(s)] + - " " + cards.by_id(std::get<1>(s))->m_name.c_str() + - skill_activation_modifier_names[std::get<4>(s)]); + return(skill_names[s.id] + + " " + cards.by_id(s.x)->m_name.c_str() + + skill_activation_modifier_names[s.mod]); } default: - return(skill_names[std::get<0>(s)] + - (std::get<3>(s) ? " all" : "") + - (std::get<2>(s) == allfactions ? "" : std::string(" ") + faction_names[std::get<2>(s)]) + - (std::get<1>(s) == 0 ? "" : std::string(" ") + to_string(std::get<1>(s))) + - skill_activation_modifier_names[std::get<4>(s)]); + return(skill_names[s.id] + + (s.all ? " all" : "") + + (s.y == allfactions ? "" : std::string(" ") + faction_names[s.y]) + + (s.s == no_skill ? "" : std::string(" ") + skill_names[s.s]) + + (s.x == 0 ? "" : std::string(" ") + to_string(s.x)) + + skill_activation_modifier_names[s.mod]); } } //------------------------------------------------------------------------------ @@ -209,12 +220,17 @@ std::string card_description(const Cards& cards, const Card* c) if(c->m_crush > 0) { desc += ", crush " + to_string(c->m_crush); } if(c->m_disease) { desc += ", disease"; } if(c->m_emulate) { desc += ", emulate"; } +#if defined(TYRANT_UNLEASHED) + if(c->m_evade) { desc += ", evade " + to_string(c->m_evade); } +#else if(c->m_evade) { desc += ", evade"; } +#endif if(c->m_fear) { desc += ", fear"; } if(c->m_flurry > 0) { desc += ", flurry " + to_string(c->m_flurry); } if(c->m_flying) { desc += ", flying"; } if(c->m_fusion) { desc += ", fusion"; } if(c->m_immobilize) { desc += ", immobilize"; } + if(c->m_inhibit) { desc += ", inhibit " + to_string(c->m_inhibit); } if(c->m_intercept) { desc += ", intercept"; } if(c->m_leech > 0) { desc += ", leech " + to_string(c->m_leech); } if(c->m_legion > 0) { desc += ", legion " + to_string(c->m_legion); } @@ -231,15 +247,14 @@ std::string card_description(const Cards& cards, const Card* c) if(c->m_tribute) { desc += ", tribute"; } if(c->m_valor > 0) { desc += ", valor " + to_string(c->m_valor); } if(c->m_wall) { desc += ", wall"; } - for(auto& skill: c->m_skills) { desc += ", " + skill_description(cards, skill); } - for(auto& skill: c->m_skills_on_play) { desc += ", " + skill_description(cards, skill); } - for(auto& skill: c->m_skills_on_kill) { desc += ", " + skill_description(cards, skill); } if(c->m_berserk_oa > 0) { desc += ", berserk " + to_string(c->m_berserk_oa); } if(c->m_disease_oa) { desc += ", disease on Attacked"; } if(c->m_poison_oa > 0) { desc += ", poison " + to_string(c->m_poison_oa) + " on Attacked"; } if(c->m_sunder_oa) { desc += ", sunder on Attacked"; } - for(auto& skill: c->m_skills_on_attacked) { desc += ", " + skill_description(cards, skill); } - for(auto& skill: c->m_skills_on_death) { desc += ", " + skill_description(cards, skill); } + for(auto mod = 0; mod < SkillMod::num_skill_activation_modifiers; ++ mod) + { + for(auto& skill: c->m_skills[mod]) { desc += ", " + skill_description(cards, skill); } + } return(desc); } //------------------------------------------------------------------------------ @@ -281,7 +296,7 @@ std::string CardStatus::description() if(m_blitzing) { desc += "(blitzing)"; } } if(m_chaosed) { desc += ", chaosed"; } - if(m_diseased) { desc += ", diseaseded"; } + if(m_diseased) { desc += ", diseased"; } if(m_frozen) { desc += ", frozen"; } if(m_immobilized) { desc += ", immobilized"; } if(m_infused) { desc += ", infused"; } @@ -291,6 +306,7 @@ std::string CardStatus::description() if(m_temporary_split) { desc += ", cloning"; } if(m_augmented > 0) { desc += ", augmented " + to_string(m_augmented); } if(m_enfeebled > 0) { desc += ", enfeebled " + to_string(m_enfeebled); } + if(m_inhibited > 0) { desc += ", inhibited " + to_string(m_inhibited); } if(m_poisoned > 0) { desc += ", poisoned " + to_string(m_poisoned); } if(m_protected > 0) { desc += ", protected " + to_string(m_protected); } if(m_stunned > 0) { desc += ", stunned " + to_string(m_stunned); } @@ -358,28 +374,28 @@ inline unsigned opponent(unsigned player) //------------------------------------------------------------------------------ SkillSpec apply_augment(const CardStatus* status, const SkillSpec& s) { - if (std::get<0>(s) == augment || std::get<0>(s) == summon || std::get<1>(s) == 0) + if (s.id == augment || s.id == summon || s.x == 0) { return s; } SkillSpec augmented_s = s; - std::get<1>(augmented_s) += status->m_augmented; + augmented_s.x += status->m_augmented; return(augmented_s); } SkillSpec apply_fusion(const SkillSpec& s) { SkillSpec fusioned_s = s; - std::get<1>(fusioned_s) *= 2; + fusioned_s.x *= 2; return(fusioned_s); } SkillSpec apply_infuse(const SkillSpec& s) { - if (std::get<2>(s) == allfactions || std::get<2>(s) == bloodthirsty || helpful_skills.find(std::get<0>(s)) == helpful_skills.end()) + if (s.y == allfactions || s.y == bloodthirsty || helpful_skills.find(s.id) == helpful_skills.end()) { return s; } SkillSpec infused_s = s; - std::get<2>(infused_s) = bloodthirsty; + infused_s.y = bloodthirsty; return(infused_s); } //------------------------------------------------------------------------------ @@ -415,27 +431,26 @@ bool may_change_skill(const Field* fd, const CardStatus* status, const SkillMod: } SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, const SkillSpec& ss, const SkillMod::SkillMod mod, bool& need_add_skill) { - const auto& skill = std::get<0>(ss); switch (fd->effect) { case Effect::time_surge: // replace other instance of the skill - if(skill == rush || skill == new_skill) + if(ss.id == rush || ss.id == no_skill) { need_add_skill = false; - return SkillSpec(rush, 1, allfactions, false, mod); + return SkillSpec{rush, 1, allfactions, no_skill, false, mod}; } break; case Effect::clone_project: case Effect::clone_experiment: // no gain the skill if already have - if(skill == split) + if(ss.id == split) { need_add_skill = false; } - else if(skill == new_skill) + else if(ss.id == no_skill) { - return SkillSpec(split, 0, allfactions, false, mod); + return SkillSpec{split, 0, allfactions, no_skill, false, mod}; } break; case Effect::friendly_fire: @@ -443,21 +458,21 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c { case CardType::assault: // no gain the skill if already have - if(skill == strike) + if(ss.id == strike) { need_add_skill = false; } - else if(skill == new_skill) + else if(ss.id == no_skill) { - return SkillSpec(strike, 1, allfactions, false, mod); + return SkillSpec{strike, 1, allfactions, no_skill, false, mod}; } break; case CardType::commander: // replace other instance of the skill - if(skill == chaos || skill == new_skill) + if(ss.id == chaos || ss.id == no_skill) { need_add_skill = false; - return SkillSpec(chaos, 0, allfactions, true, mod); + return SkillSpec{chaos, 0, allfactions, no_skill, true, mod}; } break; default: @@ -466,50 +481,50 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c break; case Effect::genesis: // replace other instance of the skill - if(skill == summon || skill == new_skill) + if(ss.id == summon || ss.id == no_skill) { need_add_skill = false; - return SkillSpec(summon, 0, allfactions, false, mod); + return SkillSpec{summon, 0, allfactions, no_skill, false, mod}; } break; case Effect::artillery_strike: // replace other instance of the skill - if(skill == strike || skill == new_skill) + if(ss.id == strike || ss.id == no_skill) { need_add_skill = false; - return SkillSpec(strike, 3, allfactions, true, mod); + return SkillSpec{strike, 3, allfactions, no_skill, true, mod}; } break; case Effect::decrepit: // replace other instance of the skill - if(skill == enfeeble || skill == new_skill) + if(ss.id == enfeeble || ss.id == no_skill) { need_add_skill = false; - return SkillSpec(enfeeble, 1, allfactions, true, mod); + return SkillSpec{enfeeble, 1, allfactions, no_skill, true, mod}; } break; case Effect::forcefield: // replace other instance of the skill - if(skill == protect || skill == new_skill) + if(ss.id == protect || ss.id == no_skill) { need_add_skill = false; - return SkillSpec(protect, 1, allfactions, true, mod); + return SkillSpec{protect, 1, allfactions, no_skill, true, mod}; } break; case Effect::chilling_touch: // replace other instance of the skill - if(skill == freeze || skill == new_skill) + if(ss.id == freeze || ss.id == no_skill) { need_add_skill = false; - return SkillSpec(freeze, 0, allfactions, false, mod); + return SkillSpec{freeze, 0, allfactions, no_skill, false, mod}; } break; case Effect::haunt: // replace other instance of the skill - if(skill == summon || skill == new_skill) + if(ss.id == summon || ss.id == no_skill) { need_add_skill = false; - return SkillSpec(summon, 0, bloodthirsty, false, mod); + return SkillSpec{summon, 0, bloodthirsty, no_skill, false, mod}; } break; default: @@ -530,7 +545,7 @@ void prepend_on_death(Field* fd) continue; } bool need_add_skill = may_change_skill(fd, status, mod); - for(auto& ss: status->m_card->m_skills_on_death) + for(auto& ss: status->m_card->m_skills[SkillMod::on_death]) { auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, status, ss, mod, need_add_skill) : ss; _DEBUG_MSG(2, "Preparing %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); @@ -539,8 +554,8 @@ void prepend_on_death(Field* fd) } if(need_add_skill) { - auto battleground_s = apply_battleground_effect(fd, status, SkillSpec(new_skill, 0, allfactions, false, mod), mod, need_add_skill); - assert(std::get<0>(battleground_s) != new_skill); + auto battleground_s = apply_battleground_effect(fd, status, SkillSpec{no_skill, 0, allfactions, no_skill, false, mod}, mod, need_add_skill); + assert(battleground_s.id != no_skill); _DEBUG_MSG(2, "Preparing %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); od_skills.emplace_back(status, battleground_s); } @@ -561,7 +576,7 @@ void resolve_skill(Field* fd) if(!status) { // trigger_regen - skill_table[std::get<0>(skill)](fd, status, skill); + skill_table[skill.id](fd, status, skill); } else if(!status->m_jammed) { @@ -569,7 +584,7 @@ void resolve_skill(Field* fd) auto& augmented_s = status->m_augmented > 0 ? apply_augment(status, skill) : skill; auto& fusioned_s = fusion_active ? apply_fusion(augmented_s) : augmented_s; auto& infused_s = status->m_infused ? apply_infuse(fusioned_s) : fusioned_s; - skill_table[std::get<0>(skill)](fd, status, infused_s); + skill_table[skill.id](fd, status, infused_s); } } } @@ -590,8 +605,8 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector } if(need_add_skill) { - auto battleground_s = apply_battleground_effect(fd, status, SkillSpec(new_skill, 0, allfactions, false, mod), mod, need_add_skill); - assert(std::get<0>(battleground_s) != new_skill); + auto battleground_s = apply_battleground_effect(fd, status, SkillSpec{no_skill, 0, allfactions, no_skill, false, mod}, mod, need_add_skill); + assert(battleground_s.id != no_skill); _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); fd->skill_queue.emplace_back(status, battleground_s); resolve_skill(fd); @@ -665,7 +680,7 @@ struct PlayCard template void onPlaySkills() { - evaluate_skills(fd, status, card->m_skills_on_play, SkillMod::on_play); + evaluate_skills(fd, status, card->m_skills[SkillMod::on_play], SkillMod::on_play); } }; // assault @@ -713,7 +728,7 @@ void PlayCard::fieldEffects() template <> void PlayCard::onPlaySkills() { - evaluate_skills(fd, status, card->m_skills, SkillMod::on_play); + evaluate_skills(fd, status, card->m_skills[SkillMod::on_activate], SkillMod::on_play); } //------------------------------------------------------------------------------ inline bool is_attacking_or_has_attacked(CardStatus* c) { return(c->m_step >= CardStep::attacking); } @@ -809,6 +824,7 @@ Results play(Field* fd) break; case CardType::commander: case CardType::num_cardtypes: + _DEBUG_MSG(0, "Unknown card type: #%d %s: %d\n", played_card->m_id, card_description(fd->cards, played_card).c_str(), played_card->m_type); assert(false); break; } @@ -821,7 +837,7 @@ Results play(Field* fd) // Evaluate commander fd->current_phase = Field::commander_phase; - evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills, SkillMod::on_activate); + evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills[SkillMod::on_activate], SkillMod::on_activate); if(__builtin_expect(fd->end, false)) { break; } // Evaluate structures @@ -831,7 +847,7 @@ Results play(Field* fd) CardStatus& current_status(fd->tap->structures[fd->current_ci]); if(current_status.m_delay == 0 && current_status.m_hp > 0) { - evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills, SkillMod::on_activate); + evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills[SkillMod::on_activate], SkillMod::on_activate); } } // Evaluate assaults @@ -848,7 +864,7 @@ Results play(Field* fd) } current_status.m_blitzing = false; // Evaluate skills - evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills, SkillMod::on_activate); + evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills[SkillMod::on_activate], SkillMod::on_activate); if(__builtin_expect(fd->end, false)) { break; } // Attack @@ -937,9 +953,11 @@ template inline bool skill_roll(Field* fd) { return(true); } +#if not defined(TYRANT_UNLEASHED) template<> inline bool skill_roll(Field* fd) { return(fd->flip()); } +#endif // Check if a skill actually proc'ed. template @@ -999,6 +1017,14 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(!ref->m_immobilized && !is_jammed(ref) && is_active_next_turn(ref)); } +#if defined(TYRANT_UNLEASHED) +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(c->m_cd_jam == 0); +} +#endif + template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { @@ -1107,7 +1133,7 @@ void remove_hp(Field* fd, CardStatus& status, unsigned dmg) if(status.m_hp == 0) { _DEBUG_MSG(1, "%s dies\n", status_description(&status).c_str()); - if(status.m_card->m_skills_on_death.size() > 0 || fd->effect == Effect::haunt) + if(status.m_card->m_skills[SkillMod::on_death].size() > 0 || fd->effect == Effect::haunt) { fd->killed_with_on_death.push_back(&status); } @@ -1172,6 +1198,8 @@ void turn_start_phase(Field* fd) remove_dead(fd->tip->assaults); remove_dead(fd->tip->structures); fd->fusion_count = 0; + // Active player's commander card: + if(fd->tip->commander.m_cd_jam) { -- fd->tip->commander.m_cd_jam; } // Active player's assault cards: // update index // remove enfeeble, protect; apply poison damage, reduce delay @@ -1183,7 +1211,11 @@ void turn_start_phase(Field* fd) { CardStatus& status(assaults[index]); status.m_index = index; + if(status.m_cd_jam > 0) { -- status.m_cd_jam; } status.m_enfeebled = 0; + status.m_enhanced_skill = no_skill; + status.m_enhanced_value = 0; + status.m_evaded = 0; status.m_protected = 0; if(status.m_poisoned > 0) { @@ -1193,9 +1225,9 @@ void turn_start_phase(Field* fd) if(status.m_delay > 0 && !status.m_frozen) { _DEBUG_MSG(1, "%s reduces its timer\n", status_description(&status).c_str()); - --status.m_delay; + -- status.m_delay; } - if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } + if(status.m_card->m_fusion && status.m_delay == 0) { ++ fd->fusion_count; } } } // Active player's structure cards: @@ -1209,6 +1241,7 @@ void turn_start_phase(Field* fd) { CardStatus& status(structures[index]); status.m_index = index; + if(status.m_cd_jam > 0) { -- status.m_cd_jam; } if(status.m_delay > 0) { _DEBUG_MSG(1, "%s reduces its timer\n", status_description(&status).c_str()); @@ -1234,6 +1267,7 @@ void turn_start_phase(Field* fd) status.m_enfeebled = 0; status.m_frozen = false; status.m_immobilized = false; + status.m_inhibited = 0; status.m_jammed = false; status.m_phased = false; status.m_rallied = 0; @@ -1319,7 +1353,7 @@ inline unsigned counter_damage(CardStatus* att, CardStatus* def) { assert(att->m_card->m_type == CardType::assault); assert(def->m_card->m_type != CardType::action); - return(safe_minus(def->m_card->m_counter + att->m_enfeebled, att->m_protected)); + return(safe_minus(def->m_card->m_counter + def->enhanced(counter) + att->m_enfeebled, att->m_protected)); } inline CardStatus* select_first_enemy_wall(Field* fd) { @@ -1444,7 +1478,7 @@ struct PerformAttack { count_achievement(fd, att_status); // perform_skill_berserk - att_status->m_berserk += att_status->m_card->m_berserk; + att_status->m_berserk += att_status->m_card->m_berserk + att_status->enhanced(berserk); } } crush_leech(); @@ -1491,7 +1525,7 @@ struct PerformAttack // prevent damage std::string reduced_desc; unsigned reduced_dmg(0); - unsigned armored_value(def_card.m_armored); + unsigned armored_value(def_card.m_armored + def_status->enhanced(armored)); if(armored_value == 0 && fd->effect == Effect::photon_shield && def_status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 0u : 1u)) { armored_value = 2; @@ -1548,7 +1582,7 @@ struct PerformAttack if(def_status->m_card->m_poison_oa > att_status->m_poisoned && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); - unsigned v = def_status->m_card->m_poison_oa; + unsigned v = def_status->m_card->m_poison_oa + def_status->enhanced(poison); _DEBUG_MSG(1, "%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); att_status->m_poisoned = v; } @@ -1562,7 +1596,7 @@ struct PerformAttack if(def_status->m_hp > 0 && def_status->m_card->m_berserk_oa > 0 && skill_check(fd, def_status, nullptr)) { count_achievement(fd, def_status); - def_status->m_berserk += def_status->m_card->m_berserk_oa; + def_status->m_berserk += def_status->m_card->m_berserk_oa + def_status->enhanced(berserk); } if(def_status->m_card->m_sunder_oa && skill_check(fd, def_status, att_status)) { @@ -1571,7 +1605,7 @@ struct PerformAttack _DEBUG_MSG(1, "%s (on attacked) sunders %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); att_status->m_sundered = true; } - evaluate_skills(fd, def_status, def_status->m_card->m_skills_on_attacked, SkillMod::on_attacked); + evaluate_skills(fd, def_status, def_status->m_card->m_skills[SkillMod::on_attacked], SkillMod::on_attacked); } template @@ -1598,6 +1632,7 @@ void PerformAttack::attack_damage() template<> void PerformAttack::damage_dependant_pre_oa() { +#if not defined(TYRANT_UNLEASHED) if(att_status->m_card->m_siphon > 0 && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); @@ -1606,14 +1641,16 @@ void PerformAttack::damage_dependant_pre_oa() _DEBUG_MSG(1, "%s siphons %u health for %s\n", status_description(att_status).c_str(), v, status_description(&fd->tap->commander).c_str()); add_hp(fd, &fd->tap->commander, v); } +#endif if(att_status->m_card->m_poison > def_status->m_poisoned && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_poison - unsigned v = att_status->m_card->m_poison; + unsigned v = att_status->m_card->m_poison + att_status->enhanced(poison); _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); def_status->m_poisoned = v; } +#if not defined(TYRANT_UNLEASHED) if(att_status->m_card->m_disease && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); @@ -1635,6 +1672,18 @@ void PerformAttack::damage_dependant_pre_oa() _DEBUG_MSG(1, "%s phases %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_phased = true; } +#endif +#if defined(TYRANT_UNLEASHED) + // XXX Assume inhibit stacks, if happened in future + if(att_status->m_card->m_inhibit > 0 && skill_check(fd, att_status, def_status)) + { + count_achievement(fd, att_status); + // perform_skill_inhibit + unsigned v = att_status->m_card->m_inhibit + att_status->enhanced(inhibit); + _DEBUG_MSG(1, "%s inhibits %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); + def_status->m_inhibited += v; + } +#endif } template<> @@ -1642,7 +1691,7 @@ void PerformAttack::on_kill() { if(killed_by_attack) { - evaluate_skills(fd, att_status, att_status->m_card->m_skills_on_kill, SkillMod::on_kill); + evaluate_skills(fd, att_status, att_status->m_card->m_skills[SkillMod::on_kill], SkillMod::on_kill); } } @@ -1668,8 +1717,9 @@ void PerformAttack::crush_leech() if(att_status->m_card->m_leech > 0 && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); - _DEBUG_MSG(1, "%s leeches %u health\n", status_description(att_status).c_str(), std::min(att_dmg, att_status->m_card->m_leech)); - add_hp(fd, att_status, std::min(att_dmg, att_status->m_card->m_leech)); + auto leech_value = std::min(att_dmg, att_status->m_card->m_leech + att_status->enhanced(leech)); + _DEBUG_MSG(1, "%s leeches %u health\n", status_description(att_status).c_str(), leech_value); + add_hp(fd, att_status, leech_value); } } @@ -1782,24 +1832,24 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const Ski template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { - const auto& mod = std::get<4>(s); + const auto& mod = s.mod; if(can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))) (src->m_player != c->m_player || mod == SkillMod::on_death ? (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)) : mod == SkillMod::on_attacked ? is_active_next_turn(c) : is_active(c) && !is_attacking_or_has_attacked(c))) { - for(auto& s: c->m_card->m_skills) + for(auto& s: c->m_card->m_skills[SkillMod::on_activate]) { // Any quantifiable skill except augment - if(std::get<1>(s) > 0 && std::get<0>(s) != augment && std::get<0>(s) != summon) { return(true); } + if(s.x > 0 && s.id != augment && s.id != summon) { return(true); } } bool need_add_skill = true; auto mod = SkillMod::on_activate; if(may_change_skill(fd, c, mod)) { - auto s = apply_battleground_effect(fd, c, SkillSpec(new_skill, 0, allfactions, false, mod), mod, need_add_skill); - assert(std::get<0>(s) != new_skill); - if(std::get<1>(s) > 0 && std::get<0>(s) != augment && std::get<0>(s) != summon) { return(true); } + auto s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, no_skill, false, mod}, mod, need_add_skill); + assert(s.id != no_skill); + if(s.x > 0 && s.id != augment && s.id != summon) { return(true); } } } return(false); @@ -1808,7 +1858,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { - const auto& mod = std::get<4>(s); + const auto& mod = s.mod; return(!c->m_chaosed && can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : @@ -1832,6 +1882,20 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, )); } +template<> +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) +{ + bool need_add_skill = true; + auto mod = SkillMod::on_activate; + if(may_change_skill(fd, c, mod)) + { + auto battleground_s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, no_skill, false, mod}, mod, need_add_skill); + assert(battleground_s.id != no_skill); + if(battleground_s.id == s.s) { return(true); } + } + return(c->m_card->m_skills_set[s.s]); +} + template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { return(c->m_hp > 0); } @@ -1851,7 +1915,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, c template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { - const auto& mod = std::get<4>(s); + const auto& mod = s.mod; return(can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : @@ -1869,7 +1933,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { - const auto& mod = std::get<4>(s); + const auto& mod = s.mod; return(can_attack(c) && !c->m_sundered && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (src->m_player != c->m_player || mod == SkillMod::on_death ? (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)) : mod == SkillMod::on_attacked ? is_active_next_turn(c) : @@ -1899,38 +1963,38 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, c template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { - const auto& mod = std::get<4>(s); - return(can_act(c) && !c->m_immobilized && attack_power(c) > 0 && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); + const auto& mod = s.mod; + return(can_attack(c) && attack_power(c) > 0 && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : is_active(c) || is_active_next_turn(c))); } template -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { assert(false); } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { - c->m_augmented += v; + c->m_augmented += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { // backfire damage counts in ARD. - remove_commander_hp(fd, *c, v, true); + remove_commander_hp(fd, *c, s.x, true); } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { c->m_chaosed = true; } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { c->m_chaosed = false; c->m_diseased = false; @@ -1944,102 +2008,120 @@ inline void perform_skill(Field* fd, CardStatus* c, unsigned v) } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { - c->m_enfeebled += v; + c->m_enfeebled += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +{ + c->m_enhanced_skill = s.s; + c->m_enhanced_value += s.x; +} + +template<> +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { c->m_frozen = true; } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { - add_hp(fd, c, v); + add_hp(fd, c, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { c->m_faction = bloodthirsty; c->m_infused = true; } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { c->m_jammed = true; } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { - c->m_protected += v; + c->m_protected += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { - c->m_rallied += v; + c->m_rallied += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { - add_hp(fd, c, v); + add_hp(fd, c, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { - c->m_delay = safe_minus(c->m_delay, v); + c->m_delay = safe_minus(c->m_delay, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { // shock damage counts in ARD. (if attacker ever has the skill) - remove_commander_hp(fd, *c, v, true); + remove_commander_hp(fd, *c, s.x, true); } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { - remove_hp(fd, *c, v); + remove_hp(fd, *c, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { - remove_hp(fd, *c, strike_damage(c, v)); + remove_hp(fd, *c, strike_damage(c, s.x)); } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { - add_hp(fd, c, v); + add_hp(fd, c, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, unsigned v) +inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) { - c->m_weakened += v; + c->m_weakened += s.x; } template inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s, bool is_helpful_skill) { - if(std::get<2>(s) == allfactions) +#if defined(TYRANT_UNLEASHED) + if(s.y == allfactions) + { + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s, is_helpful_skill](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); + } + else + { + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s, is_helpful_skill](CardStatus* c){return((c->m_faction == s.y || s.y == progenitor) && skill_predicate(fd, src_status, c, s));})); + } +#else + if(s.y == allfactions) { return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s, is_helpful_skill](CardStatus* c){return(!(is_helpful_skill && c->m_phased) && skill_predicate(fd, src_status, c, s));})); } else { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s, is_helpful_skill](CardStatus* c){return(c->m_faction == std::get<2>(s) && !(is_helpful_skill && c->m_phased) && skill_predicate(fd, src_status, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s, is_helpful_skill](CardStatus* c){return(c->m_faction == s.y && !(is_helpful_skill && c->m_phased) && skill_predicate(fd, src_status, c, s));})); } +#endif } template<> @@ -2079,7 +2161,7 @@ std::vector& skill_targets(Field* fd, CardStatus* src_status) throw; } -template<> inline std::vector& skill_targets(Field* fd, CardStatus* src_status) +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) @@ -2091,6 +2173,9 @@ template<> std::vector& skill_targets(Field* fd, CardStatu template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_assault(fd, src_status)); } @@ -2140,7 +2225,7 @@ void maybeTriggerRegen(Field* fd) template<> void maybeTriggerRegen(Field* fd) { - fd->skill_queue.emplace_front(nullptr, std::make_tuple(trigger_regen, 0, allfactions, false, SkillMod::on_activate)); + fd->skill_queue.emplace_front(nullptr, SkillSpec{trigger_regen, 0, allfactions, no_skill, false, SkillMod::on_activate}); } CardStatus* select_interceptable(Field* fd, CardStatus* src_status, unsigned index) @@ -2179,18 +2264,29 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ { if(skill_check(fd, src_status, dst_status)) { - if(is_evadable && (dst_status->m_card->m_evade || (fd->effect == Effect::quicksilver && dst_status->m_card->m_type == CardType::assault)) && fd->flip() && skill_check(fd, dst_status, src_status)) + if(is_evadable && +#if defined(TYRANT_UNLEASHED) + dst_status->m_evaded < dst_status->m_card->m_evade + dst_status->enhanced(evade) && +#else + (dst_status->m_card->m_evade || (fd->effect == Effect::quicksilver && dst_status->m_card->m_type == CardType::assault)) && fd->flip() && +#endif + skill_check(fd, dst_status, src_status)) { + ++ dst_status->m_evaded; count_achievement(fd, dst_status); - _DEBUG_MSG(1, "%s %s (%u) on %s but it evades\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); + _DEBUG_MSG(1, "%s %s (%u) on %s but it evades\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), s.x, status_description(dst_status).c_str()); return(false); } if(is_count_achievement) { count_achievement(fd, src_status); } - _DEBUG_MSG(1, "%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(dst_status).c_str()); - perform_skill(fd, dst_status, std::get<1>(s)); + _DEBUG_MSG(1, "%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), s.x, status_description(dst_status).c_str()); + perform_skill(fd, dst_status, s); + if(skill_id == jam) + { + src_status->m_cd_jam = s.x; + } return(true); } return(false); @@ -2242,7 +2338,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski } _DEBUG_SELECTION("%s", skill_names[skill_id].c_str()); unsigned index_start, index_end; - if(std::get<3>(s)) // target all + if(s.all) // target all { index_start = 0; index_end = fd->selection_array.size() - 1; @@ -2256,10 +2352,10 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski { if(!skill_roll(fd)) { - _DEBUG_MSG(2, "%s misses the 50%% chance to activate %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(fd->selection_array[s_index]).c_str()); + _DEBUG_MSG(2, "%s misses the 50%% chance to activate %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), s.x, status_description(fd->selection_array[s_index]).c_str()); continue; } - CardStatus* c(std::get<3>(s) ? fd->selection_array[s_index] : select_interceptable(fd, src_status, s_index)); + CardStatus* c(s.all ? fd->selection_array[s_index] : select_interceptable(fd, src_status, s_index)); if(check_and_perform_skill(fd, src_status, c, s, true, is_count_achievement)) { // Count at most once even targeting "All" @@ -2268,8 +2364,8 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski if(c->m_card->m_payback && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) { count_achievement(fd, c); - _DEBUG_MSG(1, "%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); - perform_skill(fd, src_status, std::get<1>(s)); + _DEBUG_MSG(1, "%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), s.x, status_description(src_status).c_str()); + perform_skill(fd, src_status, s); } } } @@ -2287,8 +2383,8 @@ inline void check_and_perform_emulate(Field* fd, CardStatus* src_status, CardSta if(emulator.m_card->m_emulate && skill_predicate(fd, src_status, &emulator, s) && skill_check(fd, &emulator, nullptr)) { count_achievement(fd, &emulator); - _DEBUG_MSG(1, "Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(&emulator).c_str()); - perform_skill(fd, &emulator, std::get<1>(s)); + _DEBUG_MSG(1, "Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), s.x, status_description(&emulator).c_str()); + perform_skill(fd, &emulator, s); } } } @@ -2303,7 +2399,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil } _DEBUG_SELECTION("%s", skill_names[skill_id].c_str()); unsigned index_start, index_end; - if(std::get<3>(s) || skill_id == supply) // target all or supply + if(s.all || skill_id == supply) // target all or supply { index_start = 0; index_end = fd->selection_array.size() - 1; @@ -2315,13 +2411,21 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil bool is_count_achievement(true); for(unsigned s_index(index_start); s_index <= index_end; ++s_index) { + CardStatus* c(fd->selection_array[s_index]); // So far no friendly activation skill needs to roll 50% but check it for completeness. if(!skill_roll(fd)) { - _DEBUG_MSG(2, "%s misses the 50%% chance to %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), std::get<1>(s), status_description(fd->selection_array[s_index]).c_str()); + _DEBUG_MSG(2, "%s misses the 50%% chance to %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), s.x, status_description(c).c_str()); continue; } - CardStatus* c(fd->selection_array[s_index]); +#if defined(TYRANT_UNLEASHED) + if(c->m_inhibited > 0) + { + _DEBUG_MSG(1, "%s %s (%u) on %s but it is inhibited\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), s.x, status_description(c).c_str()); + -- c->m_inhibited; + continue; + } +#endif if(check_and_perform_skill(fd, src_status, c, s, false, is_count_achievement)) { // Count at most once even targeting "All" @@ -2330,8 +2434,8 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil if(c->m_card->m_tribute && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status)) { count_achievement(fd, c); - _DEBUG_MSG(1, "Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), std::get<1>(s), status_description(src_status).c_str()); - perform_skill(fd, src_status, std::get<1>(s)); + _DEBUG_MSG(1, "Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), s.x, status_description(src_status).c_str()); + perform_skill(fd, src_status, s); check_and_perform_emulate(fd, src_status, src_status, s); } check_and_perform_emulate(fd, src_status, c, s); @@ -2363,7 +2467,7 @@ inline void perform_recharge(Field* fd, CardStatus* src_status, const SkillSpec& // a summoned card's on play skills seem to be evaluated before any other skills on the skill queue. inline void prepend_skills(Field* fd, CardStatus* status) { - for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills_on_play)) + for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills[SkillMod::on_play])) { fd->skill_queue.emplace_front(status, skill); } @@ -2374,14 +2478,14 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s); void perform_split(Field* fd, CardStatus* src_status, const SkillSpec& s) { - perform_summon(fd, src_status, SkillSpec(summon, src_status->m_card->m_id, std::get<2>(s), std::get<3>(s), std::get<4>(s))); + perform_summon(fd, src_status, SkillSpec{summon, src_status->m_card->m_id, s.y, s.s, s.all, s.mod}); } template void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) { unsigned player = src_status->m_player; - const auto& mod = std::get<4>(s); + const auto& mod = s.mod; // Split and Summon on Play are not counted towards the Summon Limit. if(skill_id == summon && mod != SkillMod::on_play) { @@ -2395,7 +2499,7 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) _DEBUG_MSG(1, "** Reaching summon limit, this is the last summon.\n"); } } - unsigned summoned_id = std::get<1>(s); + unsigned summoned_id = s.x; const Card* summoned = 0; if(summoned_id != 0) { @@ -2403,7 +2507,7 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) } else { - Faction summond_faction = std::get<2>(s); + Faction summond_faction = s.y; do { summoned = fd->random_in_vector(fd->cards.player_assaults); } while(summond_faction != allfactions && summond_faction != summoned->m_faction); @@ -2424,7 +2528,7 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) _DEBUG_MSG(1, "%s %s %s %u [%s]\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); prepend_skills(fd, &card_status); // Summon X (Genesis effect) does not activate Blitz for X - if(std::get<1>(s) != 0 && card_status.m_card->m_blitz) + if(s.x != 0 && card_status.m_card->m_blitz) { check_and_perform_blitz(fd, &card_status); } @@ -2471,25 +2575,32 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // evade check for mimic // individual skills are subject to evade checks too, // but resolve_skill will handle those. - if((c->m_card->m_evade || (fd->effect == Effect::quicksilver && c->m_card->m_type == CardType::assault)) && fd->flip() && skill_check(fd, c, src_status)) + if( +#if defined(TYRANT_UNLEASHED) + c->m_evaded < c->m_card->m_evade + c->enhanced(evade) && +#else + (c->m_card->m_evade || (fd->effect == Effect::quicksilver && c->m_card->m_type == CardType::assault)) && fd->flip() && +#endif + skill_check(fd, c, src_status)) { + ++ c->m_evaded; count_achievement(fd, c); - _DEBUG_MSG(1, "%s %s on %s but it evades\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); + _DEBUG_MSG(1, "%s %s on %s but it evades\n", status_description(src_status).c_str(), skill_names[s.id].c_str(), status_description(c).c_str()); return; } count_achievement(fd, src_status); - _DEBUG_MSG(1, "%s %s on %s\n", status_description(src_status).c_str(), skill_names[std::get<0>(s)].c_str(), status_description(c).c_str()); + _DEBUG_MSG(1, "%s %s on %s\n", status_description(src_status).c_str(), skill_names[s.id].c_str(), status_description(c).c_str()); auto mod = SkillMod::on_activate; bool need_add_skill = may_change_skill(fd, c, mod); - for(auto& skill: c->m_card->m_skills) + for(auto& skill: c->m_card->m_skills[SkillMod::on_activate]) { if(src_status->m_card->m_type != CardType::action && src_status->m_hp == 0) { break; } - if(std::get<0>(skill) == mimic || std::get<0>(skill) == split || - (std::get<0>(skill) == supply && src_status->m_card->m_type != CardType::assault)) + if(skill.id == mimic || skill.id == split || + (skill.id == supply && src_status->m_card->m_type != CardType::assault)) { continue; } auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, c, skill, mod, need_add_skill) : skill; - SkillSpec mimic_s(std::get<0>(battleground_s), std::get<1>(battleground_s), allfactions, std::get<3>(battleground_s), mod); + SkillSpec mimic_s{battleground_s.id, battleground_s.x, allfactions, battleground_s.s, battleground_s.all, mod}; _DEBUG_MSG(2, "Evaluating %s mimiced skill %s\n", status_description(c).c_str(), skill_description(fd->cards, mimic_s).c_str()); fd->skill_queue.emplace_back(src_status, mimic_s); resolve_skill(fd); @@ -2498,9 +2609,9 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) } if(need_add_skill) { - auto battleground_s = apply_battleground_effect(fd, c, SkillSpec(new_skill, 0, allfactions, false, mod), mod, need_add_skill); - assert(std::get<0>(battleground_s) != new_skill); - SkillSpec mimic_s(std::get<0>(battleground_s), std::get<1>(battleground_s), allfactions, std::get<3>(battleground_s), mod); + auto battleground_s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, no_skill, false, mod}, mod, need_add_skill); + assert(battleground_s.id != no_skill); + SkillSpec mimic_s{battleground_s.id, battleground_s.x, allfactions, battleground_s.s, battleground_s.all, mod}; _DEBUG_MSG(2, "Evaluating %s mimiced skill %s\n", status_description(c).c_str(), skill_description(fd->cards, mimic_s).c_str()); fd->skill_queue.emplace_back(src_status, mimic_s); resolve_skill(fd); @@ -2514,6 +2625,7 @@ void fill_skill_table() skill_table[chaos] = perform_targetted_hostile_fast; skill_table[cleanse] = perform_targetted_allied_fast; skill_table[enfeeble] = perform_targetted_hostile_fast; + skill_table[enhance] = perform_targetted_allied_fast; skill_table[freeze] = perform_targetted_hostile_fast; skill_table[heal] = perform_targetted_allied_fast; skill_table[infuse] = perform_infuse; diff --git a/sim.h b/sim.h index 40462ce2..cab0abff 100644 --- a/sim.h +++ b/sim.h @@ -131,15 +131,24 @@ struct CardStatus unsigned m_augmented; unsigned m_berserk; bool m_blitzing; +// begin for TYRANT_UNLEASHED + unsigned m_cd_jam; +// end bool m_chaosed; unsigned m_delay; bool m_diseased; +// begin for TYRANT_UNLEASHED + Skill m_enhanced_skill; + unsigned m_enhanced_value; + unsigned m_evaded; +// end unsigned m_enfeebled; Faction m_faction; bool m_frozen; unsigned m_hp; bool m_immobilized; bool m_infused; + int m_inhibited; bool m_jammed; bool m_phased; unsigned m_poisoned; @@ -158,6 +167,7 @@ struct CardStatus void set(const Card* card); void set(const Card& card); std::string description(); + inline unsigned enhanced(Skill skill) { return m_enhanced_skill == skill ? m_enhanced_value : 0; } }; //------------------------------------------------------------------------------ // Represents a particular draw from a deck. diff --git a/tyrant.cpp b/tyrant.cpp index adb32520..4fd38d9c 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -3,10 +3,12 @@ #include const std::string faction_names[Faction::num_factions] = -{ "", "bloodthirsty", "imperial", "raider", "righteous", "xeno" }; +{ "", "imperial", "raider", "bloodthirsty", "xeno", "righteous", "progenitor" }; std::string skill_names[Skill::num_skills] = { + // Placeholder for new gained skill from battleground effect: + "", // Attack: "0", // Activation (Including Destroyed): @@ -19,26 +21,32 @@ std::string skill_names[Skill::num_skills] = // Combat-Modifier: "AntiAir", "Burst", "Fear", "Flurry", "Pierce", "Swipe", "Valor", // Damage-Dependant: - "Berserk", "Crush", "Disease", "Immobilize", "Leech", "Phase", "Poison", "Siphon", "Sunder", + "Berserk", "Crush", "Disease", "Immobilize", "Inhibit", "Leech", "Phase", "Poison", "Siphon", "Sunder", // Defensive: "Armored", "Counter", "Emulate", "Evade", "Flying", "Intercept", "Payback", "Refresh", "Regenerate", "Stun", "Tribute", "Wall", // Triggered: "Blitz", "Legion", + // Tyrant Unleashed: + "Enhance", // Static (Ignored): - /* "Blizzard", "Fusion", "Mist", */ - // Placeholder for new gained skill from battleground effect: - "" + "Fusion", + /* "Blizzard", "Mist", */ }; std::set helpful_skills{ - augment, cleanse, heal, protect, rally, repair, rush, supply, + augment, cleanse, enhance, heal, protect, rally, repair, rush, supply, }; std::string skill_activation_modifier_names[SkillMod::num_skill_activation_modifiers] = {"", " on Play", " on Attacked", " on Kill", " on Death", }; std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Structure", "Action", }; -std::string rarity_names[5]{"", "common", "uncommon", "rare", "legendary", }; +std::string rarity_names[6]{"", "common", "uncommon", "rare", "legendary", "vindicator", }; + +#if defined(TYRANT_UNLEASHED) +unsigned upgrade_cost[]{0, 5, 15, 30, 75, 150}; +unsigned salvaging_income[][7]{{}, {0, 1, 2, 5}, {0, 5, 10, 15, 20}, {0, 20, 25, 30, 40, 50, 65}, {0, 40, 45, 60, 75, 100, 125}, {0, 80, 85, 100, 125, 175, 250}}; +#endif std::string decktype_names[DeckType::num_decktypes]{"Deck", "Mission", "Raid", "Quest", "Custom Deck", }; diff --git a/tyrant.h b/tyrant.h index 3eaebcf9..89cb0872 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.1.6" +#define TYRANT_OPTIMIZER_VERSION "1.2.0" #include #include @@ -10,17 +10,19 @@ enum Faction { allfactions, - bloodthirsty, imperial, raider, - righteous, + bloodthirsty, xeno, + righteous, + progenitor, num_factions }; extern const std::string faction_names[num_factions]; enum Skill { + no_skill, // Attack: attack, // Activation (including Destroyed): @@ -31,15 +33,17 @@ enum Skill // Combat-Modifier: antiair, burst, fear, flurry, pierce, swipe, valor, // Damage-Dependant: - berserk, crush, disease, immobilize, leech, phase, poison, siphon, sunder, + berserk, crush, disease, immobilize, inhibit, leech, phase, poison, siphon, sunder, // Defensive: armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, tribute, wall, // Triggered: blitz, legion, + // Tyrant Unleashed: + enhance, // Static, ignored: - /* blizzard, fusion, mist, */ + fusion, + /* blizzard, mist, */ // Placeholder for new gained skill from battleground effect: - new_skill, num_skills }; extern std::string skill_names[num_skills]; @@ -70,7 +74,12 @@ enum CardType { extern std::string cardtype_names[CardType::num_cardtypes]; -extern std::string rarity_names[5]; +extern std::string rarity_names[]; + +#if defined(TYRANT_UNLEASHED) +extern unsigned upgrade_cost[]; +extern unsigned salvaging_income[][7]; +#endif namespace DeckType { enum DeckType { @@ -161,6 +170,15 @@ enum SkillSourceType source_chaos }; -typedef std::tuple SkillSpec; +//typedef std::tuple SkillSpec; +struct SkillSpec +{ + Skill id; + unsigned x; + Faction y; + Skill s; + bool all; + SkillMod::SkillMod mod; +}; #endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 80b2234e..e075b976 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -85,6 +85,89 @@ Deck* find_deck(Decks& decks, const Cards& cards, std::string deck_name) } //---------------------- $80 deck optimization --------------------------------- +#if defined(TYRANT_UNLEASHED) +unsigned get_required_cards_before_upgrade(const std::vector & card_list, const Cards & cards, std::map & num_cards) +{ + unsigned deck_cost = 0; + std::set unresolved_cards; + for(const Card * card: card_list) + { + ++ num_cards[card->m_id]; + unresolved_cards.insert(card); + } + while (!unresolved_cards.empty()) + { + auto card = *unresolved_cards.begin(); + unresolved_cards.erase(unresolved_cards.begin()); + if(auto_upgrade_cards && owned_cards[card->m_id] < num_cards[card->m_id] && !card->m_material_list.empty()) + { + unsigned num_under = num_cards[card->m_id] - owned_cards[card->m_id]; + num_cards[card->m_id] = owned_cards[card->m_id]; +// std::cout << "-" << num_under << " " << card->m_name << "\n"; // XXX + deck_cost += num_under * card->m_upgrade_gold_cost; + for (auto material_it : card->m_material_list) + { + num_cards[material_it.first->m_id] += num_under * material_it.second; +// std::cout << "+" << num_under * material_it.second << " " << material_it.first->m_name << "\n"; // XXX + unresolved_cards.insert(material_it.first); + } + } + } +// std::cout << "\n"; // XXX + return deck_cost; +} + +// @claim_all: true = claim all cards; false = claim only non-buyable cards. +// @is_reward: not claim a card if there is upgraded version (assuming the reward card has been upgraded). +void claim_cards(const std::vector & card_list, const Cards & cards, bool claim_all, bool is_reward=false) +{ + std::map num_cards; + get_required_cards_before_upgrade(card_list, cards, num_cards); + for(auto it: num_cards) + { + unsigned card_id = it.first; + if(claim_all || buyable_cards.find(card_id) == buyable_cards.end()) + { + unsigned num_to_claim = safe_minus(it.second, owned_cards[card_id]); + if(num_to_claim > 0) + { + owned_cards[card_id] += num_to_claim; + if(debug_print) + { + std::cout << "Claim " << cards.by_id(card_id)->m_name << " (" << num_to_claim << ")" << std::endl; + } + } + } + } +} + +unsigned get_deck_cost(const Deck * deck, const Cards & cards) +{ + if(!use_owned_cards) + { + return 0; + } + std::map num_in_deck; + unsigned deck_cost = get_required_cards_before_upgrade({deck->commander}, cards, num_in_deck); + deck_cost += get_required_cards_before_upgrade(deck->cards, cards, num_in_deck); + for(auto it: num_in_deck) + { + unsigned card_id = it.first; + unsigned num_to_buy = safe_minus(it.second, owned_cards[card_id]); +// std::cout << "BUY " << cards.by_id(card_id)->m_name << " +" << num_to_buy << " =" << it.second << ".\n"; // XXX + if(num_to_buy > 0) + { + auto buyable_iter = buyable_cards.find(card_id); + if(buyable_iter == buyable_cards.end()) { return UINT_MAX; } + deck_cost += num_to_buy * buyable_iter->second; + } + } +// std::cout << "\n"; // XXX + return deck_cost; +} + +#else + // @claim_all: true = claim all cards; false = claim only non-buyable cards. // @is_reward: not claim a card if there is upgraded version (assuming the reward card has been upgraded). void claim_cards(const std::vector & card_list, const Cards & cards, bool claim_all, bool is_reward) @@ -120,33 +203,6 @@ void claim_cards(const std::vector & card_list, const Cards & cards } } -//------------------------------------------------------------------------------ -bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) -{ - assert(card->m_type != CardType::commander); - if(card->m_rarity == 4) // legendary - 1 per deck - { - for(unsigned i(0); i < deck.cards.size(); ++i) - { - if(i != slot && deck.cards[i]->m_rarity == 4) - { - return false; - } - } - } - if(card->m_unique) // unique - 1 card with same id per deck - { - for(unsigned i(0); i < deck.cards.size(); ++i) - { - if(i != slot && deck.cards[i]->m_base_id == card->m_base_id) - { - return false; - } - } - } - return true; -} - unsigned get_deck_cost(const Deck * deck, const Cards & cards) { if(!use_owned_cards) @@ -168,7 +224,7 @@ unsigned get_deck_cost(const Deck * deck, const Cards & cards) } for(const Card * card: deck->cards) { - if(card->m_proto_id > 0 && auto_upgrade_cards && owned_cards[card->m_id] <= num_in_deck[card->m_id]) + if(card->m_proto_id > 0 && auto_upgrade_cards && owned_cards[card->m_id] < num_in_deck[card->m_id]) { const Card * proto_card = cards.by_id(card->m_proto_id); num_in_deck[proto_card->m_id] += proto_card->m_upgrade_consumables; @@ -192,6 +248,36 @@ unsigned get_deck_cost(const Deck * deck, const Cards & cards) } return deck_cost; } +#endif + +//------------------------------------------------------------------------------ +inline bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) +{ +#if not defined(TYRANT_UNLEASHED) + assert(card->m_type != CardType::commander); + if(card->m_rarity == 4) // legendary - 1 per deck + { + for(unsigned i(0); i < deck.cards.size(); ++i) + { + if(i != slot && deck.cards[i]->m_rarity == 4) + { + return false; + } + } + } + if(card->m_unique) // unique - 1 card with same id per deck + { + for(unsigned i(0); i < deck.cards.size(); ++i) + { + if(i != slot && deck.cards[i]->m_base_id == card->m_base_id) + { + return false; + } + } + } +#endif + return true; +} //------------------------------------------------------------------------------ Results compute_score(const std::pair> , unsigned>& results, std::vector& factors) @@ -1096,7 +1182,11 @@ int main(int argc, char** argv) if(argc == 1) { usage(argc, argv); return(0); } if(argc <= 2 && strcmp(argv[1], "-version") == 0) { +#if defined(TYRANT_UNLEASHED) + std::cout << "Tyrant Unleashed Optimizer " << TYRANT_OPTIMIZER_VERSION << std::endl; +#else std::cout << "Tyrant Optimizer " << TYRANT_OPTIMIZER_VERSION << std::endl; +#endif return(0); } unsigned num_threads = 4; @@ -1109,6 +1199,9 @@ int main(int argc, char** argv) Achievement achievement; load_decks_xml(decks, cards); load_decks(decks, cards); +#if defined(TYRANT_UNLEASHED) + load_fusions(cards); +#endif fill_skill_table(); if(argc <= 2) diff --git a/xml.cpp b/xml.cpp index 06b08c33..1c26d5b2 100644 --- a/xml.cpp +++ b/xml.cpp @@ -19,16 +19,24 @@ // mission only and test cards have no set using namespace rapidxml; -std::map sets_counts; - Faction map_to_faction(unsigned i) { +#if defined(TYRANT_UNLEASHED) return(i == 1 ? imperial : + i == 2 ? raider : i == 3 ? bloodthirsty : i == 4 ? xeno : - i == 8 ? righteous : + i == 5 ? righteous : + i == 6 ? progenitor : + allfactions); +#else + return(i == 1 ? imperial : i == 9 ? raider : + i == 3 ? bloodthirsty : + i == 4 ? xeno : + i == 8 ? righteous : allfactions); +#endif } CardType::CardType map_to_type(unsigned i) @@ -40,6 +48,22 @@ CardType::CardType map_to_type(unsigned i) CardType::num_cardtypes); } +unsigned skill_name_to_id(const char* name) +{ + static std::map skill_map; + if(skill_map.empty()) + { + for(unsigned i(0); i < Skill::num_skills; ++i) + { + std::string skill_id{skill_names[i]}; + std::transform(skill_id.begin(), skill_id.end(), skill_id.begin(), ::tolower); + skill_map[skill_id] = i; + } + } + auto x = skill_map.find(name); + return x == skill_map.end() ? no_skill : x->second; +} + Faction skill_faction(xml_node<>* skill) { unsigned unmapped_faction(0); @@ -62,21 +86,17 @@ unsigned skill_value(xml_node<>* skill) return(value); } -template -void handle_skill(xml_node<>* node, Card* card) +Skill skill_target_skill(xml_node<>* skill) { - bool all(node->first_attribute("all")); - bool played(node->first_attribute("played")); - bool attacked(node->first_attribute("attacked")); - bool kill(node->first_attribute("kill")); - bool died(node->first_attribute("died")); - bool normal(!(played || died || attacked || kill)); - if(played) { card->add_played_skill(skill, skill_value(node), skill_faction(node), all); } - if(attacked) {card->add_attacked_skill(skill, skill_value(node), skill_faction(node), all); } - if(kill) {card->add_kill_skill(skill, skill_value(node), skill_faction(node), all); } - if(died) {card->add_died_skill(skill, skill_value(node), skill_faction(node), all); } - if(normal) {card->add_skill(skill, skill_value(node), skill_faction(node), all); } + Skill s(no_skill); + xml_attribute<>* x(skill->first_attribute("s")); + if(x) + { + s = (Skill)skill_name_to_id(x->value()); + } + return(s); } + //------------------------------------------------------------------------------ void load_decks_xml(Decks& decks, const Cards& cards) { @@ -88,6 +108,7 @@ void load_decks_xml(Decks& decks, const Cards& cards) { std::cout << "\nException while loading decks from file missions.xml\n"; } +#if not defined(TYRANT_UNLEASHED) try { read_raids(decks, cards, "raids.xml"); @@ -104,6 +125,7 @@ void load_decks_xml(Decks& decks, const Cards& cards) { std::cout << "\nException while loading decks from file quests.xml\n"; } +#endif } //------------------------------------------------------------------------------ @@ -138,6 +160,202 @@ void parse_file(const char* filename, std::vector& buffer, xml_document<>& } } //------------------------------------------------------------------------------ +void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) +{ + xml_node<>* id_node(card_node->first_node("id")); + if(!id_node) + { + id_node = card_node->first_node("card_id"); + } + assert(id_node); + unsigned id(id_node ? atoi(id_node->value()) : 0); + xml_node<>* name_node(card_node->first_node("name")); + xml_node<>* hidden_node(card_node->first_node("hidden")); + xml_node<>* replace_node(card_node->first_node("replace")); + xml_node<>* attack_node(card_node->first_node("attack")); + xml_node<>* health_node(card_node->first_node("health")); + xml_node<>* cost_node(card_node->first_node("cost")); + xml_node<>* unique_node(card_node->first_node("unique")); + xml_node<>* reserve_node(card_node->first_node("reserve")); + xml_node<>* base_card_node(card_node->first_node("base_card")); + xml_node<>* rarity_node(card_node->first_node("rarity")); + xml_node<>* type_node(card_node->first_node("type")); + xml_node<>* set_node(card_node->first_node("set")); + int set(set_node ? atoi(set_node->value()) : card->m_set); + xml_node<>* level_node(card_node->first_node("level")); +#if 0 + if (set > 0) // not AI only + { + nb_cards++; + sets_counts[set]++; + } +#endif + card->m_id = id; + if(name_node) { card->m_name = name_node->value(); } + if(level_node) { card->m_level = atoi(level_node->value()); } + // So far, commanders have attack_node (value == 0) + if(id < 1000) + { card->m_type = CardType::assault; } + else if(id < 2000) + { card->m_type = CardType::commander; } + else if(id < 3000) + { card->m_type = CardType::structure; } + else if(id < 4000) + { card->m_type = CardType::action; } + else if(id < 6000) + { card->m_type = CardType::assault; } + else + { card->m_type = cost_node ? (attack_node ? CardType::assault : CardType::structure) : (health_node ? CardType::commander : CardType::action); } + if(hidden_node) { card->m_hidden = atoi(hidden_node->value()); } + if(replace_node) { card->m_replace = atoi(replace_node->value()); } + if(attack_node) { card->m_attack = atoi(attack_node->value()); } + if(health_node) { card->m_health = atoi(health_node->value()); } + if(cost_node) { card->m_delay = atoi(cost_node->value()); } + if(unique_node) { card->m_unique = true; } + if(reserve_node) { card->m_reserve = atoi(reserve_node->value()); } + if(base_card_node) { card->m_base_id = atoi(base_card_node->value()); } + else if(card->m_base_id == 0) { card->m_base_id = card->m_id; } + if(rarity_node) { card->m_rarity = atoi(rarity_node->value()); } + if(type_node) { card->m_faction = map_to_faction(atoi(type_node->value())); } + card->m_set = set; + // Promo and Unpurchasable Reward cards will only require 1 copy + card->m_upgrade_consumables = set == 5001 || (set == 5000 && card->m_reserve) ? 1 : 2; + // Reward cards will still have a gold cost + card->m_upgrade_gold_cost = set == 5000 ? (card->m_rarity == 4 ? 100000 : 20000) : 0; + for(xml_node<>* skill_node = card_node->first_node("skill"); + skill_node; + skill_node = skill_node->next_sibling("skill")) + { + Skill skill_id = (Skill)skill_name_to_id(skill_node->first_attribute("id")->value()); + if(skill_id == no_skill) { continue; } + + bool all(skill_node->first_attribute("all")); + bool played(skill_node->first_attribute("played")); + bool attacked(skill_node->first_attribute("attacked")); + bool kill(skill_node->first_attribute("kill")); + bool died(skill_node->first_attribute("died")); + bool normal(!(played || died || attacked || kill)); + + if(normal) { card->m_skills_set[skill_id] = true; } + + if(skill_id == antiair) + { card->m_antiair = skill_value(skill_node); } + else if(skill_id == armored) + { card->m_armored = skill_value(skill_node); } + else if(skill_id == berserk) + { + if(attacked) { card->m_berserk_oa = skill_value(skill_node); } + else {card->m_berserk = skill_value(skill_node); } + } + else if(skill_id == blitz) + { card->m_blitz = true; } + else if(skill_id == burst) + { card->m_burst = skill_value(skill_node); } + else if(skill_id == counter) + { card->m_counter = skill_value(skill_node); } + else if(skill_id == crush) + { card->m_crush = skill_value(skill_node); } + else if(skill_id == disease) + { + if(attacked) { card->m_disease_oa = true; } + else {card->m_disease = true; } + } + else if(skill_id == emulate) + { card->m_emulate = true; } + else if(skill_id == evade) +#if defined(TYRANT_UNLEASHED) + { card->m_evade = skill_value(skill_node); } +#else + { card->m_evade = 1; } +#endif + else if(skill_id == fear) + { card->m_fear = true; } + else if(skill_id == flurry) + { card->m_flurry = skill_value(skill_node); } + else if(skill_id == flying) + { card->m_flying = true; } + else if(skill_id == fusion) + { card->m_fusion = true; } + else if(skill_id == immobilize) + { card->m_immobilize = true; } +#if defined(TYRANT_UNLEASHED) + else if(skill_id == inhibit) + { card->m_inhibit = skill_value(skill_node); } +#endif + else if(skill_id == intercept) + { card->m_intercept = true; } + else if(skill_id == leech) + { card->m_leech = skill_value(skill_node); } + else if(skill_id == legion) + { card->m_legion = skill_value(skill_node); } + else if(skill_id == payback) + { card->m_payback = true; } + else if(skill_id == pierce) + { card->m_pierce = skill_value(skill_node); } + else if(skill_id == phase) + { card->m_phase = true; } + else if(skill_id == poison) + { + if(attacked) { card->m_poison_oa = skill_value(skill_node); } + else {card->m_poison = skill_value(skill_node); } + } + else if(skill_id == refresh) + { card->m_refresh = true; } + else if(skill_id == regenerate) + { card->m_regenerate = skill_value(skill_node); } + else if(skill_id == siphon) + { card->m_siphon = skill_value(skill_node); } + else if(skill_id == stun) + { card->m_stun = true; } + else if(skill_id == sunder) + { + if(attacked) { card->m_sunder_oa = true; } + else {card->m_sunder = true; } + } + else if(skill_id == swipe) + { card->m_swipe = true; } + else if(skill_id == tribute) + { card->m_tribute = true; } + else if(skill_id == valor) + { card->m_valor = skill_value(skill_node); } + else if(skill_id == wall) + { card->m_wall = true; } + else + { + if(played) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_target_skill(skill_node), all, SkillMod::on_play); } + if(attacked) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_target_skill(skill_node), all, SkillMod::on_attacked); } + if(kill) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_target_skill(skill_node), all, SkillMod::on_kill); } + if(died) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_target_skill(skill_node), all, SkillMod::on_death); } + if(normal) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_target_skill(skill_node), all); } + } + } + cards.cards.push_back(card); +#if defined(TYRANT_UNLEASHED) + Card * top_card = card; + for(xml_node<>* upgrade_node = card_node->first_node("upgrade"); + upgrade_node; + upgrade_node = upgrade_node->next_sibling("upgrade")) + { + Card * pre_upgraded_card = top_card; + top_card = new Card(*top_card); + parse_card_node(cards, top_card, upgrade_node); + top_card->m_material_list.clear(); + top_card->m_material_list[pre_upgraded_card] = 1; + if (top_card->m_type == CardType::commander) + { + // Commanders cost twice and cannot be salvaged. + top_card->m_upgrade_gold_cost = 2 * upgrade_cost[pre_upgraded_card->m_level]; + } + else + { + // Salvaging income counts? + top_card->m_upgrade_gold_cost = upgrade_cost[pre_upgraded_card->m_level]; // + salvaging_income[top_card->m_rarity][pre_upgraded_card->m_level] - salvaging_income[top_card->m_rarity][top_card->m_level]; + } + } + card->m_final_id = top_card->m_id; +#endif +} + void read_cards(Cards& cards) { std::vector buffer; @@ -149,218 +367,35 @@ void read_cards(Cards& cards) { return; } - - bool ai_only(false); +#if 0 unsigned nb_cards(0); - for(xml_node<>* card = root->first_node(); - card; - card = card->next_sibling()) + std::map sets_counts; +#endif + for(xml_node<>* card_node = root->first_node("unit"); + card_node; + card_node = card_node->next_sibling("unit")) { - if(strcmp(card->name(), "unit") == 0) - { - xml_node<>* id_node(card->first_node("id")); - assert(id_node); - unsigned id(id_node ? atoi(id_node->value()) : 0); - xml_node<>* name_node(card->first_node("name")); - xml_node<>* hidden_node(card->first_node("hidden")); - unsigned hidden(hidden_node ? atoi(hidden_node->value()) : 0); - xml_node<>* replace_node(card->first_node("replace")); - unsigned replace_id(replace_node ? atoi(replace_node->value()) : 0); - xml_node<>* attack_node(card->first_node("attack")); - xml_node<>* health_node(card->first_node("health")); - xml_node<>* cost_node(card->first_node("cost")); - xml_node<>* unique_node(card->first_node("unique")); - xml_node<>* reserve_node(card->first_node("reserve")); - unsigned reserve(reserve_node ? atoi(reserve_node->value()) : 0); - xml_node<>* base_card_node(card->first_node("base_card")); - unsigned base_card_id(base_card_node ? atoi(base_card_node->value()) : id); - xml_node<>* rarity_node(card->first_node("rarity")); - xml_node<>* type_node(card->first_node("type")); - xml_node<>* set_node(card->first_node("set")); - int set(set_node ? atoi(set_node->value()) : 0); - ai_only = set == 0; - if((ai_only || set >= 0) && name_node && rarity_node) - { - if(!ai_only) - { - nb_cards++; - sets_counts[set]++; - } - Card* c(new Card()); - c->m_id = id; - c->m_name = name_node->value(); - // So far, commanders have attack_node (value == 0) - if(id < 1000) - { c->m_type = CardType::assault; } - else if(id < 2000) - { c->m_type = CardType::commander; } - else if(id < 3000) - { c->m_type = CardType::structure; } - else if(id < 4000) - { c->m_type = CardType::action; } - else if(id < 5000) - { c->m_type = CardType::assault; } - else - { c->m_type = cost_node ? (attack_node ? CardType::assault : CardType::structure) : (health_node ? CardType::commander : CardType::action); } - c->m_hidden = hidden; - c->m_replace = replace_id; - if(attack_node) { c->m_attack = atoi(attack_node->value()); } - if(health_node) { c->m_health = atoi(health_node->value()); } - if(cost_node) { c->m_delay = atoi(cost_node->value()); } - if(unique_node) { c->m_unique = true; } - c->m_reserve = reserve; - c->m_base_id = base_card_id; - c->m_rarity = atoi(rarity_node->value()); - unsigned type(type_node ? atoi(type_node->value()) : 0); - c->m_faction = map_to_faction(type); - c->m_set = set; - // Promo and Unpurchasable Reward cards will only require 1 copy - c->m_upgrade_consumables = set == 5001 || (set == 5000 and reserve) ? 1 : 2; - // Reward cards will still have a gold cost - c->m_upgrade_gold_cost = set == 5000 ? (c->m_rarity == 4 ? 100000 : 20000) : 0; - for(xml_node<>* skill = card->first_node("skill"); skill; - skill = skill->next_sibling("skill")) - { - if(strcmp(skill->first_attribute("id")->value(), "antiair") == 0) - { c->m_antiair = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "armored") == 0) - { c->m_armored = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "berserk") == 0) - { - bool attacked(skill->first_attribute("attacked")); - if(attacked) { c->m_berserk_oa = atoi(skill->first_attribute("x")->value()); } - else {c->m_berserk = atoi(skill->first_attribute("x")->value()); } - } - if(strcmp(skill->first_attribute("id")->value(), "blitz") == 0) - { c->m_blitz = true; } - if(strcmp(skill->first_attribute("id")->value(), "burst") == 0) - { c->m_burst = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "counter") == 0) - { c->m_counter = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "crush") == 0) - { c->m_crush = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "disease") == 0) - { - bool attacked(skill->first_attribute("attacked")); - if(attacked) { c->m_disease_oa = true; } - else {c->m_disease = true; } - } - if(strcmp(skill->first_attribute("id")->value(), "emulate") == 0) - { c->m_emulate = true; } - if(strcmp(skill->first_attribute("id")->value(), "evade") == 0) - { c->m_evade = true; } - if(strcmp(skill->first_attribute("id")->value(), "fear") == 0) - { c->m_fear = true; } - if(strcmp(skill->first_attribute("id")->value(), "flurry") == 0) - { c->m_flurry = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "flying") == 0) - { c->m_flying = true; } - if(strcmp(skill->first_attribute("id")->value(), "fusion") == 0) - { c->m_fusion = true; } - if(strcmp(skill->first_attribute("id")->value(), "immobilize") == 0) - { c->m_immobilize = true; } - if(strcmp(skill->first_attribute("id")->value(), "intercept") == 0) - { c->m_intercept = true; } - if(strcmp(skill->first_attribute("id")->value(), "leech") == 0) - { c->m_leech = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "legion") == 0) - { c->m_legion = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "payback") == 0) - { c->m_payback = true; } - if(strcmp(skill->first_attribute("id")->value(), "pierce") == 0) - { c->m_pierce = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "phase") == 0) - { c->m_phase = true; } - if(strcmp(skill->first_attribute("id")->value(), "poison") == 0) - { - bool attacked(skill->first_attribute("attacked")); - if(attacked) { c->m_poison_oa = atoi(skill->first_attribute("x")->value()); } - else {c->m_poison = atoi(skill->first_attribute("x")->value()); } - } - if(strcmp(skill->first_attribute("id")->value(), "refresh") == 0) - { c->m_refresh = true; } - if(strcmp(skill->first_attribute("id")->value(), "regenerate") == 0) - { c->m_regenerate = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "siphon") == 0) - { c->m_siphon = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "stun") == 0) - { c->m_stun = true; } - if(strcmp(skill->first_attribute("id")->value(), "sunder") == 0) - { - bool attacked(skill->first_attribute("attacked")); - if(attacked) { c->m_sunder_oa = true; } - else {c->m_sunder = true; } - } - if(strcmp(skill->first_attribute("id")->value(), "swipe") == 0) - { c->m_swipe = true; } - if(strcmp(skill->first_attribute("id")->value(), "tribute") == 0) - { c->m_tribute = true; } - if(strcmp(skill->first_attribute("id")->value(), "valor") == 0) - { c->m_valor = atoi(skill->first_attribute("x")->value()); } - if(strcmp(skill->first_attribute("id")->value(), "wall") == 0) - { c->m_wall = true; } - if(strcmp(skill->first_attribute("id")->value(), "augment") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "backfire") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "chaos") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "cleanse") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "enfeeble") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "freeze") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "heal") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "infuse") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "jam") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "mimic") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "protect") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "rally") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "recharge") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "repair") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "rush") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "shock") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "siege") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "split") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "strike") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "summon") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "supply") == 0) - { handle_skill(skill, c); } - if(strcmp(skill->first_attribute("id")->value(), "weaken") == 0) - { handle_skill(skill, c); } - } - cards.cards.push_back(c); - } - } + auto card = new Card(); + parse_card_node(cards, card, card_node); } cards.organize(); - // std::cout << "nb cards: " << nb_cards << "\n"; - // for(auto counts: sets_counts) - // { - // std::cout << "set " << counts.first << " (" << sets[counts.first] << ")" << ": " << counts.second << "\n"; - // } - // std::cout << "nb mission cards: " << cards.mission_cards.size() << "\n"; +#if 0 + std::cout << "nb cards: " << nb_cards << "\n"; + for(auto counts: sets_counts) + { + std::cout << "set " << counts.first << ": " << counts.second << "\n"; + } +#endif } //------------------------------------------------------------------------------ Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::DeckType decktype, unsigned id, std::string& deck_name) { xml_node<>* commander_node(node->first_node("commander")); - const Card* commander_card{cards.by_id(atoi(commander_node->value()))}; + unsigned card_id = atoi(commander_node->value()); +#if defined(TYRANT_UNLEASHED) + card_id = cards.by_id(cards.by_id(card_id)->m_base_id)->m_final_id; +#endif + const Card* commander_card{cards.by_id(card_id)}; std::vector always_cards; std::vector>> some_cards; std::vector reward_cards; @@ -370,7 +405,10 @@ Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::De card_node; card_node = card_node->next_sibling("card")) { - unsigned card_id(atoi(card_node->value())); + card_id = atoi(card_node->value()); +#if defined(TYRANT_UNLEASHED) + card_id = cards.by_id(cards.by_id(card_id)->m_base_id)->m_final_id; +#endif always_cards.push_back(cards.by_id(card_id)); } for(xml_node<>* pool_node = deck_node->first_node("card_pool"); @@ -529,19 +567,10 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement throw std::runtime_error("Failed to parse " + filename); } - std::map skill_map; - for(unsigned i(0); i < Skill::num_skills; ++i) - { - std::string skill_id{skill_names[i]}; - std::transform(skill_id.begin(), skill_id.end(), skill_id.begin(), ::tolower); - skill_map[skill_id] = i; - } - - for(xml_node<>* achievement_node = root->first_node(); + for(xml_node<>* achievement_node = root->first_node("achievement"); achievement_node; - achievement_node = achievement_node->next_sibling()) + achievement_node = achievement_node->next_sibling("achievement")) { - if(strcmp(achievement_node->name(), "achievement") != 0) { continue; } xml_node<>* id_node(achievement_node->first_node("id")); xml_node<>* name_node(achievement_node->first_node("name")); if(!id_node || !name_node || (strcmp(id_node->value(), achievement_id_name) != 0 && strcmp(name_node->value(), achievement_id_name) != 0)) { continue; } @@ -565,7 +594,7 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement req_node = req_node->next_sibling("req")) { Comparator comparator = get_comparator(req_node, great_equal); - xml_attribute<>* skill_id(req_node->first_attribute("skill_id")); + xml_attribute<>* skill_id_node(req_node->first_attribute("skill_id")); xml_attribute<>* unit_id(req_node->first_attribute("unit_id")); xml_attribute<>* unit_type(req_node->first_attribute("unit_type")); xml_attribute<>* unit_race(req_node->first_attribute("unit_race")); @@ -578,16 +607,16 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement xml_attribute<>* damage(req_node->first_attribute("damage")); xml_attribute<>* com_total(req_node->first_attribute("com_total")); xml_attribute<>* only(req_node->first_attribute("only")); - if(skill_id && num_used) + if(skill_id_node && num_used) { - auto x = skill_map.find(skill_id->value()); - if(x == skill_map.end()) + auto skill_id = skill_name_to_id(skill_id_node->value()); + if(skill_id == no_skill) { - throw std::runtime_error(std::string("Unknown skill ") + skill_id->value()); + throw std::runtime_error(std::string("Unknown skill ") + skill_id_node->value()); } - achievement.skill_used[x->second] = achievement.req_counter.size(); + achievement.skill_used[skill_id] = achievement.req_counter.size(); achievement.req_counter.emplace_back(atoi(num_used->value()), comparator); - std::cout << " Use skills: " << skill_id->value() << achievement.req_counter.back().str() << std::endl; + std::cout << " Use skills: " << skill_id_node->value() << achievement.req_counter.back().str() << std::endl; } else if(unit_id && num_played) { @@ -634,13 +663,13 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement achievement.req_counter.emplace_back(atoi(num_killed->value()), comparator); std::cout << " Kill units of type: " << cardtype_names[i] << achievement.req_counter.back().str() << std::endl; } - else if(num_killed_with && skill_id && strcmp(skill_id->value(), "flying") == 0) + else if(num_killed_with && skill_id_node && strcmp(skill_id_node->value(), "flying") == 0) { achievement.misc_req[AchievementMiscReq::unit_with_flying_killed] = achievement.req_counter.size(); achievement.req_counter.emplace_back(atoi(num_killed_with->value()), comparator); std::cout << " " << achievement_misc_req_names[AchievementMiscReq::unit_with_flying_killed] << achievement.req_counter.back().str() << std::endl; } - else if(only && skill_id && strcmp(skill_id->value(), "0") == 0) + else if(only && skill_id_node && strcmp(skill_id_node->value(), "0") == 0) { achievement.misc_req[AchievementMiscReq::skill_activated] = achievement.req_counter.size(); achievement.req_counter.emplace_back(0, equal); From 6ac739ffd3086dd195c25d49a6d7061884ce33d6 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 7 Apr 2014 12:29:55 +0800 Subject: [PATCH 200/406] Support Fortresses. --- deck.cpp | 14 ++++++++++++-- deck.h | 2 ++ sim.cpp | 13 +++++++++++++ tyrant_optimize.cpp | 13 +++++++++++++ xml.cpp | 8 ++++---- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/deck.cpp b/deck.cpp index 369e0b9a..e8222f0a 100644 --- a/deck.cpp +++ b/deck.cpp @@ -230,12 +230,22 @@ void Deck::resolve(const Cards& all_cards) deck_string.clear(); } -void Deck::set_given_hand(const Cards& all_cards, const std::string& hand_string) +void Deck::set_given_hand(const Cards& all_cards, const std::string& deck_string) { - auto && id_marks = string_to_ids(all_cards, hand_string, "hand"); + auto && id_marks = string_to_ids(all_cards, deck_string, "hand"); given_hand = id_marks.first; } +void Deck::set_forts(const Cards& all_cards, const std::string& deck_string) +{ + auto && id_marks = string_to_ids(all_cards, deck_string, "fort_cards"); + fort_cards.clear(); + for (auto id: id_marks.first) + { + fort_cards.push_back(all_cards.by_id(id)); + } +} + std::string Deck::short_description() const { std::stringstream ios; diff --git a/deck.h b/deck.h index 1ae80952..9afe5e5a 100644 --- a/deck.h +++ b/deck.h @@ -50,6 +50,7 @@ struct Deck std::string deck_string; std::vector given_hand; + std::vector fort_cards; Deck( DeckType::DeckType decktype_ = DeckType::deck, @@ -86,6 +87,7 @@ struct Deck void set(const Cards& all_cards, const std::string& deck_string_); void resolve(const Cards& all_cards); void set_given_hand(const Cards& all_cards, const std::string& deck_string_); + void set_forts(const Cards& all_cards, const std::string& deck_string_); template Container card_ids() const diff --git a/sim.cpp b/sim.cpp index ee18b42e..99d5c0c0 100644 --- a/sim.cpp +++ b/sim.cpp @@ -759,6 +759,19 @@ Results play(Field* fd) fd->achievement_counter.clear(); fd->achievement_counter.resize(fd->achievement.req_counter.size()); +#if defined(TYRANT_UNLEASHED) + // Play fortresses + for (unsigned _ = 0; _ < 2; ++ _) + { + for (const Card* played_card: fd->tap->deck->fort_cards) + { + PlayCard(played_card, fd).op(); + } + std::swap(fd->tapi, fd->tipi); + std::swap(fd->tap, fd->tip); + } +#endif + #if 0 // ANP: Last decision point is second-to-last card played. fd->points_since_last_decision = 0; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index e075b976..cb74546a 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1449,6 +1449,19 @@ int main(int argc, char** argv) } argIndex += 1; } + else if(strcmp(argv[argIndex], "forts") == 0) // set forts + { + att_deck->set_forts(cards, argv[argIndex + 1]); + argIndex += 1; + } + else if(strcmp(argv[argIndex], "defender:forts") == 0) // set enemies' forts + { + for(auto def_deck: def_decks) + { + def_deck->set_forts(cards, argv[argIndex + 1]); + } + argIndex += 1; + } else if(strcmp(argv[argIndex], "sim") == 0) { todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); diff --git a/xml.cpp b/xml.cpp index 1c26d5b2..8b54de69 100644 --- a/xml.cpp +++ b/xml.cpp @@ -388,12 +388,12 @@ void read_cards(Cards& cards) #endif } //------------------------------------------------------------------------------ -Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::DeckType decktype, unsigned id, std::string& deck_name) +Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::DeckType decktype, unsigned id, std::string& deck_name, unsigned level=1) { xml_node<>* commander_node(node->first_node("commander")); unsigned card_id = atoi(commander_node->value()); #if defined(TYRANT_UNLEASHED) - card_id = cards.by_id(cards.by_id(card_id)->m_base_id)->m_final_id; + if(level == 10) { card_id = cards.by_id(cards.by_id(card_id)->m_base_id)->m_final_id; } #endif const Card* commander_card{cards.by_id(card_id)}; std::vector always_cards; @@ -407,7 +407,7 @@ Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::De { card_id = atoi(card_node->value()); #if defined(TYRANT_UNLEASHED) - card_id = cards.by_id(cards.by_id(card_id)->m_base_id)->m_final_id; + if(level == 10) { card_id = cards.by_id(cards.by_id(card_id)->m_base_id)->m_final_id; } #endif always_cards.push_back(cards.by_id(card_id)); } @@ -473,7 +473,7 @@ void read_missions(Decks& decks, const Cards& cards, std::string filename) unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(mission_node->first_node("name")); std::string deck_name{name_node->value()}; - Deck* deck = read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name); + Deck* deck = read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name, 10); xml_node<>* effect_id_node(mission_node->first_node("effect")); if(effect_id_node) { From b16b082f1580292f76bbd534501361b8dc458839 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 7 Apr 2014 14:08:54 +0800 Subject: [PATCH 201/406] Support unleashed battleground. --- sim.cpp | 48 +++++++++++++++++++++++++++++++------------ sim.h | 9 ++++++-- tyrant.h | 2 +- tyrant_optimize.cpp | 50 +++++++++++++++++++++++++++++++++------------ xml.cpp | 8 ++++---- xml.h | 2 ++ 6 files changed, 86 insertions(+), 33 deletions(-) diff --git a/sim.cpp b/sim.cpp index 99d5c0c0..0ba17cba 100644 --- a/sim.cpp +++ b/sim.cpp @@ -114,6 +114,11 @@ CardStatus::CardStatus(const Card* card) : { } +//------------------------------------------------------------------------------ +inline unsigned CardStatus::enhanced(Field* fd, Skill skill) +{ + return (fd->bg_enhanced_skill == skill ? fd->bg_enhanced_value : 0) + (m_enhanced_skill == skill ? m_enhanced_value : 0); +} //------------------------------------------------------------------------------ inline void CardStatus::set(const Card* card) { @@ -398,9 +403,16 @@ SkillSpec apply_infuse(const SkillSpec& s) infused_s.y = bloodthirsty; return(infused_s); } +SkillSpec apply_enhance(const SkillSpec& s, unsigned enhanced_value) +{ + SkillSpec enahnced_s = s; + enahnced_s.x += enhanced_value; + return(enahnced_s); +} //------------------------------------------------------------------------------ bool may_change_skill(const Field* fd, const CardStatus* status, const SkillMod::SkillMod mod) { +#if not defined(TYRANT_UNLEASHED) switch (mod) { case SkillMod::on_activate: @@ -427,10 +439,12 @@ bool may_change_skill(const Field* fd, const CardStatus* status, const SkillMod: default: break; } +#endif return false; } SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, const SkillSpec& ss, const SkillMod::SkillMod mod, bool& need_add_skill) { +#if not defined(TYRANT_UNLEASHED) switch (fd->effect) { case Effect::time_surge: @@ -530,6 +544,7 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c default: break; } +#endif return ss; } //------------------------------------------------------------------------------ @@ -580,11 +595,18 @@ void resolve_skill(Field* fd) } else if(!status->m_jammed) { +#if defined(TYRANT_UNLEASHED) + unsigned enhanced_value = status->enhanced(fd, skill.id); + auto& enhanced_s = enhanced_value > 0 ? apply_enhance(skill, enhanced_value) : skill; + auto& modified_s = enhanced_s; +#else bool fusion_active = status->m_card->m_fusion && status->m_player == fd->tapi && fd->fusion_count >= 3; auto& augmented_s = status->m_augmented > 0 ? apply_augment(status, skill) : skill; auto& fusioned_s = fusion_active ? apply_fusion(augmented_s) : augmented_s; auto& infused_s = status->m_infused ? apply_infuse(fusioned_s) : fusioned_s; - skill_table[skill.id](fd, status, infused_s); + auto& modified_s = modified_s; +#endif + skill_table[skill.id](fd, status, modified_s); } } } @@ -1362,11 +1384,11 @@ void evaluate_legion(Field* fd) //---------------------- $50 attack by assault card implementation ------------- // Counter damage dealt to the attacker (att) by defender (def) // pre-condition: only valid if m_card->m_counter > 0 -inline unsigned counter_damage(CardStatus* att, CardStatus* def) +inline unsigned counter_damage(Field* fd, CardStatus* att, CardStatus* def) { assert(att->m_card->m_type == CardType::assault); assert(def->m_card->m_type != CardType::action); - return(safe_minus(def->m_card->m_counter + def->enhanced(counter) + att->m_enfeebled, att->m_protected)); + return(safe_minus(def->m_card->m_counter + def->enhanced(fd, counter) + att->m_enfeebled, att->m_protected)); } inline CardStatus* select_first_enemy_wall(Field* fd) { @@ -1483,7 +1505,7 @@ struct PerformAttack { count_achievement(fd, def_status); // perform_skill_counter - unsigned counter_dmg(counter_damage(att_status, def_status)); + unsigned counter_dmg(counter_damage(fd, att_status, def_status)); _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, *att_status, counter_dmg); } @@ -1491,7 +1513,7 @@ struct PerformAttack { count_achievement(fd, att_status); // perform_skill_berserk - att_status->m_berserk += att_status->m_card->m_berserk + att_status->enhanced(berserk); + att_status->m_berserk += att_status->m_card->m_berserk + att_status->enhanced(fd, berserk); } } crush_leech(); @@ -1538,7 +1560,7 @@ struct PerformAttack // prevent damage std::string reduced_desc; unsigned reduced_dmg(0); - unsigned armored_value(def_card.m_armored + def_status->enhanced(armored)); + unsigned armored_value(def_card.m_armored + def_status->enhanced(fd, armored)); if(armored_value == 0 && fd->effect == Effect::photon_shield && def_status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 0u : 1u)) { armored_value = 2; @@ -1595,7 +1617,7 @@ struct PerformAttack if(def_status->m_card->m_poison_oa > att_status->m_poisoned && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); - unsigned v = def_status->m_card->m_poison_oa + def_status->enhanced(poison); + unsigned v = def_status->m_card->m_poison_oa + def_status->enhanced(fd, poison); _DEBUG_MSG(1, "%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); att_status->m_poisoned = v; } @@ -1609,7 +1631,7 @@ struct PerformAttack if(def_status->m_hp > 0 && def_status->m_card->m_berserk_oa > 0 && skill_check(fd, def_status, nullptr)) { count_achievement(fd, def_status); - def_status->m_berserk += def_status->m_card->m_berserk_oa + def_status->enhanced(berserk); + def_status->m_berserk += def_status->m_card->m_berserk_oa + def_status->enhanced(fd, berserk); } if(def_status->m_card->m_sunder_oa && skill_check(fd, def_status, att_status)) { @@ -1659,7 +1681,7 @@ void PerformAttack::damage_dependant_pre_oa() { count_achievement(fd, att_status); // perform_skill_poison - unsigned v = att_status->m_card->m_poison + att_status->enhanced(poison); + unsigned v = att_status->m_card->m_poison + att_status->enhanced(fd, poison); _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); def_status->m_poisoned = v; } @@ -1692,7 +1714,7 @@ void PerformAttack::damage_dependant_pre_oa() { count_achievement(fd, att_status); // perform_skill_inhibit - unsigned v = att_status->m_card->m_inhibit + att_status->enhanced(inhibit); + unsigned v = att_status->m_card->m_inhibit + att_status->enhanced(fd, inhibit); _DEBUG_MSG(1, "%s inhibits %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); def_status->m_inhibited += v; } @@ -1730,7 +1752,7 @@ void PerformAttack::crush_leech() if(att_status->m_card->m_leech > 0 && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); - auto leech_value = std::min(att_dmg, att_status->m_card->m_leech + att_status->enhanced(leech)); + auto leech_value = std::min(att_dmg, att_status->m_card->m_leech + att_status->enhanced(fd, leech)); _DEBUG_MSG(1, "%s leeches %u health\n", status_description(att_status).c_str(), leech_value); add_hp(fd, att_status, leech_value); } @@ -2279,7 +2301,7 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ { if(is_evadable && #if defined(TYRANT_UNLEASHED) - dst_status->m_evaded < dst_status->m_card->m_evade + dst_status->enhanced(evade) && + dst_status->m_evaded < dst_status->m_card->m_evade + dst_status->enhanced(fd, evade) && #else (dst_status->m_card->m_evade || (fd->effect == Effect::quicksilver && dst_status->m_card->m_type == CardType::assault)) && fd->flip() && #endif @@ -2590,7 +2612,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // but resolve_skill will handle those. if( #if defined(TYRANT_UNLEASHED) - c->m_evaded < c->m_card->m_evade + c->enhanced(evade) && + c->m_evaded < c->m_card->m_evade + c->enhanced(fd, evade) && #else (c->m_card->m_evade || (fd->effect == Effect::quicksilver && c->m_card->m_type == CardType::assault)) && fd->flip() && #endif diff --git a/sim.h b/sim.h index cab0abff..ee7f7777 100644 --- a/sim.h +++ b/sim.h @@ -167,7 +167,7 @@ struct CardStatus void set(const Card* card); void set(const Card& card); std::string description(); - inline unsigned enhanced(Skill skill) { return m_enhanced_skill == skill ? m_enhanced_value : 0; } + unsigned enhanced(Field* fd, Skill skill); }; //------------------------------------------------------------------------------ // Represents a particular draw from a deck. @@ -211,6 +211,8 @@ class Field gamemode_t gamemode; OptimizationMode optimization_mode; const Effect effect; + Skill bg_enhanced_skill; + unsigned bg_enhanced_value; const Achievement& achievement; // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. @@ -237,7 +239,8 @@ class Field unsigned fusion_count; std::vector achievement_counter; - Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, OptimizationMode optimization_mode_, Effect effect_, const Achievement& achievement_) : + Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, OptimizationMode optimization_mode_, + Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_, const Achievement& achievement_) : end{false}, re(re_), cards(cards_), @@ -246,6 +249,8 @@ class Field gamemode(gamemode_), optimization_mode(optimization_mode_), effect(effect_), + bg_enhanced_skill(bg_enhanced_skill_), + bg_enhanced_value(bg_enhanced_value_), achievement(achievement_) { } diff --git a/tyrant.h b/tyrant.h index 89cb0872..7df64d37 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.2.0" +#define TYRANT_OPTIMIZER_VERSION "1.2.1" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index cb74546a..385da535 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -329,9 +329,12 @@ struct SimulationData std::vector factors; gamemode_t gamemode; enum Effect effect; + Skill bg_enhanced_skill; + unsigned bg_enhanced_value; const Achievement& achievement; - SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_def_decks_, std::vector factors_, gamemode_t gamemode_, enum Effect effect_, const Achievement& achievement_) : + SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_def_decks_, std::vector factors_, gamemode_t gamemode_, + enum Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_, const Achievement& achievement_) : re(seed), cards(cards_), decks(decks_), @@ -341,6 +344,8 @@ struct SimulationData factors(factors_), gamemode(gamemode_), effect(effect_), + bg_enhanced_skill(bg_enhanced_skill_), + bg_enhanced_value(bg_enhanced_value_), achievement(achievement_) { for(auto def_deck: def_decks) @@ -372,7 +377,7 @@ struct SimulationData { att_hand.reset(re); def_hand->reset(re); - Field fd(re, cards, att_hand, *def_hand, gamemode, optimization_mode, effect != Effect::none ? effect : def_hand->deck->effect, achievement); + Field fd(re, cards, att_hand, *def_hand, gamemode, optimization_mode, effect != Effect::none ? effect : def_hand->deck->effect, bg_enhanced_skill, bg_enhanced_value, achievement); Results result(play(&fd)); res.emplace_back(result); } @@ -402,25 +407,30 @@ class Process std::vector factors; gamemode_t gamemode; enum Effect effect; + Skill bg_enhanced_skill; + unsigned bg_enhanced_value; Achievement achievement; - Process(unsigned _num_threads, const Cards& cards_, const Decks& decks_, Deck* att_deck_, std::vector _def_decks, std::vector _factors, gamemode_t _gamemode, enum Effect _effect, const Achievement& achievement_) : - num_threads(_num_threads), + Process(unsigned num_threads_, const Cards& cards_, const Decks& decks_, Deck* att_deck_, std::vector def_decks_, std::vector factors_, gamemode_t gamemode_, + enum Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_, const Achievement& achievement_) : + num_threads(num_threads_), main_barrier(num_threads+1), cards(cards_), decks(decks_), att_deck(att_deck_), - def_decks(_def_decks), - factors(_factors), - gamemode(_gamemode), - effect(_effect), + def_decks(def_decks_), + factors(factors_), + gamemode(gamemode_), + effect(effect_), + bg_enhanced_skill(bg_enhanced_skill_), + bg_enhanced_value(bg_enhanced_value_), achievement(achievement_) { destroy_threads = false; unsigned seed(time(0)); for(unsigned i(0); i < num_threads; ++i) { - threads_data.push_back(new SimulationData(seed + i, cards, decks, def_decks.size(), factors, gamemode, effect, achievement)); + threads_data.push_back(new SimulationData(seed + i, cards, decks, def_decks.size(), factors, gamemode, effect, bg_enhanced_skill, bg_enhanced_value, achievement)); threads.push_back(new boost::thread(thread_evaluate, std::ref(main_barrier), std::ref(shared_mutex), std::ref(*threads_data.back()), std::ref(*this), i)); } } @@ -1216,6 +1226,8 @@ int main(int argc, char** argv) std::vector def_decks; std::vector def_decks_factors; enum Effect effect(Effect::none); + Skill bg_enhanced_skill(no_skill); + unsigned bg_enhanced_value(0); bool keep_commander{false}; bool fixed_len{false}; std::vector> todo; @@ -1275,10 +1287,10 @@ int main(int argc, char** argv) std::map effect_map; for(unsigned i(0); i < Effect::num_effects; ++i) { - effect_map[effect_names[i]] = i; + effect_map[effect_names[i]] = static_cast(i); std::stringstream ss; ss << i; - effect_map[ss.str()] = i; + effect_map[ss.str()] = static_cast(i); } for(int argIndex(3); argIndex < argc; ++argIndex) @@ -1330,8 +1342,19 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "-e") == 0) { +#if defined(TYRANT_UNLEASHED) + std::string arg_skill(argv[argIndex + 1]); + bg_enhanced_skill = skill_name_to_id(arg_skill.c_str()); + if(bg_enhanced_skill == no_skill) + { + std::cout << "The skill '" << arg_skill << "' was not found. "; + return(6); + } + bg_enhanced_value = atoi(argv[argIndex + 2]); + argIndex += 2; +#else std::string arg_effect(argv[argIndex + 1]); - auto x = effect_map.find(arg_effect); + const auto & x = effect_map.find(arg_effect); if(x == effect_map.end()) { std::cout << "The effect '" << arg_effect << "' was not found. "; @@ -1340,6 +1363,7 @@ int main(int argc, char** argv) } effect = static_cast(x->second); argIndex += 1; +#endif } else if(strcmp(argv[argIndex], "-fixedlen") == 0) { @@ -1527,7 +1551,7 @@ int main(int argc, char** argv) std::cout << "Effect: " << effect_names[effect] << std::endl; } - Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, achievement); + Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, bg_enhanced_skill, bg_enhanced_value, achievement); { //ScopeClock timer; diff --git a/xml.cpp b/xml.cpp index 8b54de69..bb61493c 100644 --- a/xml.cpp +++ b/xml.cpp @@ -48,7 +48,7 @@ CardType::CardType map_to_type(unsigned i) CardType::num_cardtypes); } -unsigned skill_name_to_id(const char* name) +Skill skill_name_to_id(const char* name) { static std::map skill_map; if(skill_map.empty()) @@ -61,7 +61,7 @@ unsigned skill_name_to_id(const char* name) } } auto x = skill_map.find(name); - return x == skill_map.end() ? no_skill : x->second; + return x == skill_map.end() ? no_skill : (Skill)x->second; } Faction skill_faction(xml_node<>* skill) @@ -92,7 +92,7 @@ Skill skill_target_skill(xml_node<>* skill) xml_attribute<>* x(skill->first_attribute("s")); if(x) { - s = (Skill)skill_name_to_id(x->value()); + s = skill_name_to_id(x->value()); } return(s); } @@ -226,7 +226,7 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) skill_node; skill_node = skill_node->next_sibling("skill")) { - Skill skill_id = (Skill)skill_name_to_id(skill_node->first_attribute("id")->value()); + Skill skill_id = skill_name_to_id(skill_node->first_attribute("id")->value()); if(skill_id == no_skill) { continue; } bool all(skill_node->first_attribute("all")); diff --git a/xml.h b/xml.h index aef08580..afd3bf3c 100644 --- a/xml.h +++ b/xml.h @@ -2,11 +2,13 @@ #define XML_H_INCLUDED #include +#include "tyrant.h" class Cards; class Decks; class Achievement; +Skill skill_name_to_id(const char* name); void load_decks_xml(Decks& decks, const Cards& cards); void read_cards(Cards& cards); void read_missions(Decks& decks, const Cards& cards, std::string filename); From 5c432b978e74c6c5ffe6ae5ea6d71cf1ba74a191 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 1 May 2014 08:39:44 +0800 Subject: [PATCH 202/406] Support skill cooldown. --- card.h | 6 ++- cards.cpp | 4 +- sim.cpp | 145 ++++++++++++++++++++++++++++++------------------------ sim.h | 7 +-- tyrant.h | 4 +- xml.cpp | 25 +++++++--- 6 files changed, 112 insertions(+), 79 deletions(-) diff --git a/card.h b/card.h index c0e0b660..5c44f9f5 100644 --- a/card.h +++ b/card.h @@ -3,6 +3,7 @@ #include #include +#include #include "tyrant.h" class Card @@ -68,8 +69,8 @@ class Card unsigned m_valor; bool m_wall; std::vector m_skills[SkillMod::num_skill_activation_modifiers]; - bool m_skills_set[num_skills]; CardType::CardType m_type; + unsigned m_skill_pos[num_skills]; public: Card() : @@ -131,9 +132,10 @@ class Card m_skills(), m_type(CardType::assault) { + std::memset(m_skill_pos, 0, sizeof m_skill_pos); } - void add_skill(Skill id, unsigned x, Faction y, Skill s, bool all, SkillMod::SkillMod mod=SkillMod::on_activate); + void add_skill(Skill id, unsigned x, Faction y, unsigned c, Skill s, bool all, SkillMod::SkillMod mod=SkillMod::on_activate); }; #endif diff --git a/cards.cpp b/cards.cpp index 0b661776..2fcdfd0b 100644 --- a/cards.cpp +++ b/cards.cpp @@ -168,7 +168,7 @@ void Cards::organize() } // class Card -void Card::add_skill(Skill id, unsigned x, Faction y, Skill s, bool all, SkillMod::SkillMod mod) +void Card::add_skill(Skill id, unsigned x, Faction y, unsigned c, Skill s, bool all, SkillMod::SkillMod mod) { for(auto it = m_skills[mod].begin(); it != m_skills[mod].end(); ++ it) { @@ -178,6 +178,6 @@ void Card::add_skill(Skill id, unsigned x, Faction y, Skill s, bool all, SkillMo break; } } - m_skills[mod].push_back({id, x, y, s, all, mod}); + m_skills[mod].push_back({id, x, y, c, s, all, mod}); } diff --git a/sim.cpp b/sim.cpp index 0ba17cba..4912907f 100644 --- a/sim.cpp +++ b/sim.cpp @@ -86,7 +86,6 @@ CardStatus::CardStatus(const Card* card) : m_augmented(0), m_berserk(0), m_blitzing(false), - m_cd_jam(0), m_chaosed(false), m_delay(card->m_delay), m_diseased(false), @@ -112,6 +111,7 @@ CardStatus::CardStatus(const Card* card) : m_is_summoned(false), m_step(CardStep::none) { + std::memset(m_skill_cd, 0, sizeof m_skill_cd); } //------------------------------------------------------------------------------ @@ -134,7 +134,6 @@ inline void CardStatus::set(const Card& card) m_berserk = 0; m_blitzing = false; m_chaosed = false; - m_cd_jam = 0; m_delay = card.m_delay; m_diseased = false; m_enhanced_skill = no_skill; @@ -158,6 +157,7 @@ inline void CardStatus::set(const Card& card) m_weakened = 0; m_is_summoned = false; m_step = CardStep::none; + std::memset(m_skill_cd, 0, sizeof m_skill_cd); } //------------------------------------------------------------------------------ inline int attack_power(CardStatus* att) @@ -188,9 +188,17 @@ std::string skill_description(const Cards& cards, const SkillSpec& s) (s.y == allfactions ? "" : std::string(" ") + faction_names[s.y]) + (s.s == no_skill ? "" : std::string(" ") + skill_names[s.s]) + (s.x == 0 ? "" : std::string(" ") + to_string(s.x)) + + (s.c == 0 ? "" : std::string(" every ") + to_string(s.c)) + skill_activation_modifier_names[s.mod]); } } +std::string skill_short_description(const SkillSpec& s) +{ + // NOTE: not support summon + return skill_names[s.id] + + (s.s == no_skill ? "" : std::string(" ") + skill_names[s.s]) + + (s.x == 0 ? "" : std::string(" ") + to_string(s.x)); +} //------------------------------------------------------------------------------ std::string card_description(const Cards& cards, const Card* c) { @@ -452,7 +460,7 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c if(ss.id == rush || ss.id == no_skill) { need_add_skill = false; - return SkillSpec{rush, 1, allfactions, no_skill, false, mod}; + return SkillSpec{rush, 1, allfactions, 0, no_skill, false, mod}; } break; case Effect::clone_project: @@ -464,7 +472,7 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c } else if(ss.id == no_skill) { - return SkillSpec{split, 0, allfactions, no_skill, false, mod}; + return SkillSpec{split, 0, allfactions, 0, no_skill, false, mod}; } break; case Effect::friendly_fire: @@ -478,7 +486,7 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c } else if(ss.id == no_skill) { - return SkillSpec{strike, 1, allfactions, no_skill, false, mod}; + return SkillSpec{strike, 1, allfactions, 0, no_skill, false, mod}; } break; case CardType::commander: @@ -486,7 +494,7 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c if(ss.id == chaos || ss.id == no_skill) { need_add_skill = false; - return SkillSpec{chaos, 0, allfactions, no_skill, true, mod}; + return SkillSpec{chaos, 0, allfactions, 0, no_skill, true, mod}; } break; default: @@ -498,7 +506,7 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c if(ss.id == summon || ss.id == no_skill) { need_add_skill = false; - return SkillSpec{summon, 0, allfactions, no_skill, false, mod}; + return SkillSpec{summon, 0, allfactions, 0, no_skill, false, mod}; } break; case Effect::artillery_strike: @@ -506,7 +514,7 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c if(ss.id == strike || ss.id == no_skill) { need_add_skill = false; - return SkillSpec{strike, 3, allfactions, no_skill, true, mod}; + return SkillSpec{strike, 3, allfactions, 0, no_skill, true, mod}; } break; case Effect::decrepit: @@ -514,7 +522,7 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c if(ss.id == enfeeble || ss.id == no_skill) { need_add_skill = false; - return SkillSpec{enfeeble, 1, allfactions, no_skill, true, mod}; + return SkillSpec{enfeeble, 1, allfactions, 0, no_skill, true, mod}; } break; case Effect::forcefield: @@ -522,7 +530,7 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c if(ss.id == protect || ss.id == no_skill) { need_add_skill = false; - return SkillSpec{protect, 1, allfactions, no_skill, true, mod}; + return SkillSpec{protect, 1, allfactions, 0, no_skill, true, mod}; } break; case Effect::chilling_touch: @@ -530,7 +538,7 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c if(ss.id == freeze || ss.id == no_skill) { need_add_skill = false; - return SkillSpec{freeze, 0, allfactions, no_skill, false, mod}; + return SkillSpec{freeze, 0, allfactions, 0, no_skill, false, mod}; } break; case Effect::haunt: @@ -538,7 +546,7 @@ SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, c if(ss.id == summon || ss.id == no_skill) { need_add_skill = false; - return SkillSpec{summon, 0, bloodthirsty, no_skill, false, mod}; + return SkillSpec{summon, 0, bloodthirsty, 0, no_skill, false, mod}; } break; default: @@ -569,7 +577,7 @@ void prepend_on_death(Field* fd) } if(need_add_skill) { - auto battleground_s = apply_battleground_effect(fd, status, SkillSpec{no_skill, 0, allfactions, no_skill, false, mod}, mod, need_add_skill); + auto battleground_s = apply_battleground_effect(fd, status, SkillSpec{no_skill, 0, allfactions, 0, no_skill, false, mod}, mod, need_add_skill); assert(battleground_s.id != no_skill); _DEBUG_MSG(2, "Preparing %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); od_skills.emplace_back(status, battleground_s); @@ -620,6 +628,12 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector for(auto& ss: skills) { auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, status, ss, mod, need_add_skill) : ss; + auto skill_pos = status->m_card->m_skill_pos[ss.id]; + if (skill_pos > 0 && status->m_skill_cd[skill_pos - 1] > 0) + { + _DEBUG_MSG(2, "Cooling down (%u) %s skill %s\n", status->m_skill_cd[skill_pos - 1], status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); + continue; + } _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); fd->skill_queue.emplace_back(status, battleground_s); resolve_skill(fd); @@ -627,7 +641,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector } if(need_add_skill) { - auto battleground_s = apply_battleground_effect(fd, status, SkillSpec{no_skill, 0, allfactions, no_skill, false, mod}, mod, need_add_skill); + auto battleground_s = apply_battleground_effect(fd, status, SkillSpec{no_skill, 0, allfactions, 0, no_skill, false, mod}, mod, need_add_skill); assert(battleground_s.id != no_skill); _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); fd->skill_queue.emplace_back(status, battleground_s); @@ -859,7 +873,7 @@ Results play(Field* fd) break; case CardType::commander: case CardType::num_cardtypes: - _DEBUG_MSG(0, "Unknown card type: #%d %s: %d\n", played_card->m_id, card_description(fd->cards, played_card).c_str(), played_card->m_type); + _DEBUG_MSG(0, "Unknown card type: #%u %s: %u\n", played_card->m_id, card_description(fd->cards, played_card).c_str(), played_card->m_type); assert(false); break; } @@ -1052,14 +1066,6 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(!ref->m_immobilized && !is_jammed(ref) && is_active_next_turn(ref)); } -#if defined(TYRANT_UNLEASHED) -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return(c->m_cd_jam == 0); -} -#endif - template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { @@ -1234,7 +1240,10 @@ void turn_start_phase(Field* fd) remove_dead(fd->tip->structures); fd->fusion_count = 0; // Active player's commander card: - if(fd->tip->commander.m_cd_jam) { -- fd->tip->commander.m_cd_jam; } + for (auto & skill_cd : fd->tip->commander.m_skill_cd) + { + if (skill_cd > 0) { -- skill_cd; } + } // Active player's assault cards: // update index // remove enfeeble, protect; apply poison damage, reduce delay @@ -1246,7 +1255,6 @@ void turn_start_phase(Field* fd) { CardStatus& status(assaults[index]); status.m_index = index; - if(status.m_cd_jam > 0) { -- status.m_cd_jam; } status.m_enfeebled = 0; status.m_enhanced_skill = no_skill; status.m_enhanced_value = 0; @@ -1263,6 +1271,10 @@ void turn_start_phase(Field* fd) -- status.m_delay; } if(status.m_card->m_fusion && status.m_delay == 0) { ++ fd->fusion_count; } + for (auto & skill_cd : status.m_skill_cd) + { + if (skill_cd > 0) { -- skill_cd; } + } } } // Active player's structure cards: @@ -1276,13 +1288,16 @@ void turn_start_phase(Field* fd) { CardStatus& status(structures[index]); status.m_index = index; - if(status.m_cd_jam > 0) { -- status.m_cd_jam; } if(status.m_delay > 0) { _DEBUG_MSG(1, "%s reduces its timer\n", status_description(&status).c_str()); --status.m_delay; } if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } + for (auto & skill_cd : status.m_skill_cd) + { + if (skill_cd > 0) { -- skill_cd; } + } } } // Defending player's assault cards: @@ -1882,7 +1897,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, auto mod = SkillMod::on_activate; if(may_change_skill(fd, c, mod)) { - auto s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, no_skill, false, mod}, mod, need_add_skill); + auto s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, 0, no_skill, false, mod}, mod, need_add_skill); assert(s.id != no_skill); if(s.x > 0 && s.id != augment && s.id != summon) { return(true); } } @@ -1924,11 +1939,11 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, auto mod = SkillMod::on_activate; if(may_change_skill(fd, c, mod)) { - auto battleground_s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, no_skill, false, mod}, mod, need_add_skill); + auto battleground_s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, 0, no_skill, false, mod}, mod, need_add_skill); assert(battleground_s.id != no_skill); if(battleground_s.id == s.s) { return(true); } } - return(c->m_card->m_skills_set[s.s]); + return(c->m_card->m_skill_pos[s.s] > 0); } template<> @@ -2006,30 +2021,30 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, c } template -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { assert(false); } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_augmented += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { // backfire damage counts in ARD. remove_commander_hp(fd, *c, s.x, true); } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_chaosed = true; } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_chaosed = false; c->m_diseased = false; @@ -2043,94 +2058,94 @@ inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_enfeebled += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_enhanced_skill = s.s; c->m_enhanced_value += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_frozen = true; } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { add_hp(fd, c, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_faction = bloodthirsty; c->m_infused = true; } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_jammed = true; } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_protected += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_rallied += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { add_hp(fd, c, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_delay = safe_minus(c->m_delay, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { // shock damage counts in ARD. (if attacker ever has the skill) remove_commander_hp(fd, *c, s.x, true); } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { remove_hp(fd, *c, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { remove_hp(fd, *c, strike_damage(c, s.x)); } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { add_hp(fd, c, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, SkillSpec s) +inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { c->m_weakened += s.x; } @@ -2260,7 +2275,7 @@ void maybeTriggerRegen(Field* fd) template<> void maybeTriggerRegen(Field* fd) { - fd->skill_queue.emplace_front(nullptr, SkillSpec{trigger_regen, 0, allfactions, no_skill, false, SkillMod::on_activate}); + fd->skill_queue.emplace_front(nullptr, SkillSpec{trigger_regen, 0, allfactions, 0, no_skill, false, SkillMod::on_activate}); } CardStatus* select_interceptable(Field* fd, CardStatus* src_status, unsigned index) @@ -2309,21 +2324,23 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ { ++ dst_status->m_evaded; count_achievement(fd, dst_status); - _DEBUG_MSG(1, "%s %s (%u) on %s but it evades\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), s.x, status_description(dst_status).c_str()); + _DEBUG_MSG(1, "%s %s on %s but it evades\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst_status).c_str()); return(false); } if(is_count_achievement) { count_achievement(fd, src_status); } - _DEBUG_MSG(1, "%s %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), s.x, status_description(dst_status).c_str()); + _DEBUG_MSG(1, "%s %s on %s\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst_status).c_str()); perform_skill(fd, dst_status, s); - if(skill_id == jam) + auto skill_pos = src_status->m_card->m_skill_pos[skill_id]; + if (skill_pos > 0 && s.c > 0) { - src_status->m_cd_jam = s.x; + src_status->m_skill_cd[skill_pos - 1] = s.c; } return(true); } + _DEBUG_MSG(1, "(CANCELLED) %s %s on %s\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst_status).c_str()); return(false); } @@ -2387,7 +2404,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski { if(!skill_roll(fd)) { - _DEBUG_MSG(2, "%s misses the 50%% chance to activate %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), s.x, status_description(fd->selection_array[s_index]).c_str()); + _DEBUG_MSG(2, "%s misses the 50%% chance to activate %s on %s\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(fd->selection_array[s_index]).c_str()); continue; } CardStatus* c(s.all ? fd->selection_array[s_index] : select_interceptable(fd, src_status, s_index)); @@ -2399,7 +2416,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski if(c->m_card->m_payback && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) { count_achievement(fd, c); - _DEBUG_MSG(1, "%s paybacks (%s %u) on %s\n", status_description(c).c_str(), skill_names[skill_id].c_str(), s.x, status_description(src_status).c_str()); + _DEBUG_MSG(1, "%s paybacks (%s) on %s\n", status_description(c).c_str(), skill_short_description(s).c_str(), status_description(src_status).c_str()); perform_skill(fd, src_status, s); } } @@ -2418,7 +2435,7 @@ inline void check_and_perform_emulate(Field* fd, CardStatus* src_status, CardSta if(emulator.m_card->m_emulate && skill_predicate(fd, src_status, &emulator, s) && skill_check(fd, &emulator, nullptr)) { count_achievement(fd, &emulator); - _DEBUG_MSG(1, "Emulate (%s %u) on %s\n", skill_names[skill_id].c_str(), s.x, status_description(&emulator).c_str()); + _DEBUG_MSG(1, "Emulate (%s) on %s\n", skill_short_description(s).c_str(), status_description(&emulator).c_str()); perform_skill(fd, &emulator, s); } } @@ -2450,13 +2467,13 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil // So far no friendly activation skill needs to roll 50% but check it for completeness. if(!skill_roll(fd)) { - _DEBUG_MSG(2, "%s misses the 50%% chance to %s (%u) on %s\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), s.x, status_description(c).c_str()); + _DEBUG_MSG(2, "%s misses the 50%% chance to activate %s on %s\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(c).c_str()); continue; } #if defined(TYRANT_UNLEASHED) if(c->m_inhibited > 0) { - _DEBUG_MSG(1, "%s %s (%u) on %s but it is inhibited\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), s.x, status_description(c).c_str()); + _DEBUG_MSG(1, "%s %s on %s but it is inhibited\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(c).c_str()); -- c->m_inhibited; continue; } @@ -2469,7 +2486,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil if(c->m_card->m_tribute && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status)) { count_achievement(fd, c); - _DEBUG_MSG(1, "Tribute (%s %u) on %s\n", skill_names[skill_id].c_str(), s.x, status_description(src_status).c_str()); + _DEBUG_MSG(1, "Tribute (%s) on %s\n", skill_short_description(s).c_str(), status_description(src_status).c_str()); perform_skill(fd, src_status, s); check_and_perform_emulate(fd, src_status, src_status, s); } @@ -2513,7 +2530,7 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s); void perform_split(Field* fd, CardStatus* src_status, const SkillSpec& s) { - perform_summon(fd, src_status, SkillSpec{summon, src_status->m_card->m_id, s.y, s.s, s.all, s.mod}); + perform_summon(fd, src_status, SkillSpec{summon, src_status->m_card->m_id, s.y, s.c, s.s, s.all, s.mod}); } template @@ -2635,7 +2652,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) (skill.id == supply && src_status->m_card->m_type != CardType::assault)) { continue; } auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, c, skill, mod, need_add_skill) : skill; - SkillSpec mimic_s{battleground_s.id, battleground_s.x, allfactions, battleground_s.s, battleground_s.all, mod}; + SkillSpec mimic_s{battleground_s.id, battleground_s.x, allfactions, battleground_s.c, battleground_s.s, battleground_s.all, mod}; _DEBUG_MSG(2, "Evaluating %s mimiced skill %s\n", status_description(c).c_str(), skill_description(fd->cards, mimic_s).c_str()); fd->skill_queue.emplace_back(src_status, mimic_s); resolve_skill(fd); @@ -2644,9 +2661,9 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) } if(need_add_skill) { - auto battleground_s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, no_skill, false, mod}, mod, need_add_skill); + auto battleground_s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, 0, no_skill, false, mod}, mod, need_add_skill); assert(battleground_s.id != no_skill); - SkillSpec mimic_s{battleground_s.id, battleground_s.x, allfactions, battleground_s.s, battleground_s.all, mod}; + SkillSpec mimic_s{battleground_s.id, battleground_s.x, allfactions, battleground_s.c, battleground_s.s, battleground_s.all, mod}; _DEBUG_MSG(2, "Evaluating %s mimiced skill %s\n", status_description(c).c_str(), skill_description(fd->cards, mimic_s).c_str()); fd->skill_queue.emplace_back(src_status, mimic_s); resolve_skill(fd); diff --git a/sim.h b/sim.h index ee7f7777..af8c43d7 100644 --- a/sim.h +++ b/sim.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "tyrant.h" @@ -131,9 +132,6 @@ struct CardStatus unsigned m_augmented; unsigned m_berserk; bool m_blitzing; -// begin for TYRANT_UNLEASHED - unsigned m_cd_jam; -// end bool m_chaosed; unsigned m_delay; bool m_diseased; @@ -160,6 +158,9 @@ struct CardStatus bool m_temporary_split; bool m_is_summoned; // is this card summoned (or split)? CardStep m_step; +// begin for TYRANT_UNLEASHED + unsigned m_skill_cd[4]; // XXX +// end CardStatus() {} CardStatus(const Card* card); diff --git a/tyrant.h b/tyrant.h index 7df64d37..39fca010 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.2.1" +#define TYRANT_OPTIMIZER_VERSION "1.2.2" #include #include @@ -170,12 +170,12 @@ enum SkillSourceType source_chaos }; -//typedef std::tuple SkillSpec; struct SkillSpec { Skill id; unsigned x; Faction y; + unsigned c; Skill s; bool all; SkillMod::SkillMod mod; diff --git a/xml.cpp b/xml.cpp index bb61493c..3b463b81 100644 --- a/xml.cpp +++ b/xml.cpp @@ -86,6 +86,17 @@ unsigned skill_value(xml_node<>* skill) return(value); } +unsigned skill_cd(xml_node<>* skill) +{ + unsigned value(0); + xml_attribute<>* c(skill->first_attribute("c")); + if(c) + { + value = atoi(c->value()); + } + return(value); +} + Skill skill_target_skill(xml_node<>* skill) { Skill s(no_skill); @@ -222,6 +233,7 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) card->m_upgrade_consumables = set == 5001 || (set == 5000 && card->m_reserve) ? 1 : 2; // Reward cards will still have a gold cost card->m_upgrade_gold_cost = set == 5000 ? (card->m_rarity == 4 ? 100000 : 20000) : 0; + unsigned skill_pos = 1; for(xml_node<>* skill_node = card_node->first_node("skill"); skill_node; skill_node = skill_node->next_sibling("skill")) @@ -236,7 +248,7 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) bool died(skill_node->first_attribute("died")); bool normal(!(played || died || attacked || kill)); - if(normal) { card->m_skills_set[skill_id] = true; } + if(normal) { card->m_skill_pos[skill_id] = skill_pos; } if(skill_id == antiair) { card->m_antiair = skill_value(skill_node); } @@ -322,12 +334,13 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) { card->m_wall = true; } else { - if(played) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_target_skill(skill_node), all, SkillMod::on_play); } - if(attacked) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_target_skill(skill_node), all, SkillMod::on_attacked); } - if(kill) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_target_skill(skill_node), all, SkillMod::on_kill); } - if(died) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_target_skill(skill_node), all, SkillMod::on_death); } - if(normal) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_target_skill(skill_node), all); } + if(played) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_cd(skill_node), skill_target_skill(skill_node), all, SkillMod::on_play); } + if(attacked) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_cd(skill_node), skill_target_skill(skill_node), all, SkillMod::on_attacked); } + if(kill) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_cd(skill_node), skill_target_skill(skill_node), all, SkillMod::on_kill); } + if(died) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_cd(skill_node), skill_target_skill(skill_node), all, SkillMod::on_death); } + if(normal) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_cd(skill_node), skill_target_skill(skill_node), all); } } + ++ skill_pos; } cards.cards.push_back(card); #if defined(TYRANT_UNLEASHED) From 0437f3e9053e197df42ff22cfba5ffc13c73cf30 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 1 May 2014 21:12:46 +0800 Subject: [PATCH 203/406] Improve unleashed battleground (interactive w/ inhibit). --- Makefile | 3 ++- sim.cpp | 73 +++++++++++++++++++++++++++++++++++------------------- sim.h | 10 +++----- tyrant.cpp | 4 +++ tyrant.h | 1 + 5 files changed, 59 insertions(+), 32 deletions(-) diff --git a/Makefile b/Makefile index 3ac93bc2..07bb9478 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,14 @@ MAIN := tyrant_optimize SRCS := $(wildcard *.cpp) OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) +INCS := $(wildcard *.h) CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -DTYRANT_UNLEASHED LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem all: $(MAIN) -obj/%.o: %.cpp +obj/%.o: %.cpp $(INCS) $(CXX) $(CPPFLAGS) -o $@ -c $< $(MAIN): $(OBJS) diff --git a/sim.cpp b/sim.cpp index 4912907f..18264549 100644 --- a/sim.cpp +++ b/sim.cpp @@ -89,8 +89,6 @@ CardStatus::CardStatus(const Card* card) : m_chaosed(false), m_delay(card->m_delay), m_diseased(false), - m_enhanced_skill(no_skill), - m_enhanced_value(0), m_evaded(0), m_enfeebled(0), m_faction(card->m_faction), @@ -111,13 +109,14 @@ CardStatus::CardStatus(const Card* card) : m_is_summoned(false), m_step(CardStep::none) { + std::memset(m_enhanced_value, 0, sizeof m_enhanced_value); std::memset(m_skill_cd, 0, sizeof m_skill_cd); } //------------------------------------------------------------------------------ -inline unsigned CardStatus::enhanced(Field* fd, Skill skill) +inline unsigned CardStatus::enhanced(Skill skill) { - return (fd->bg_enhanced_skill == skill ? fd->bg_enhanced_value : 0) + (m_enhanced_skill == skill ? m_enhanced_value : 0); + return m_enhanced_value[m_card->m_skill_pos[skill]]; } //------------------------------------------------------------------------------ inline void CardStatus::set(const Card* card) @@ -136,8 +135,6 @@ inline void CardStatus::set(const Card& card) m_chaosed = false; m_delay = card.m_delay; m_diseased = false; - m_enhanced_skill = no_skill; - m_enhanced_value = 0; m_evaded = 0; m_enfeebled = 0; m_faction = card.m_faction; @@ -157,6 +154,7 @@ inline void CardStatus::set(const Card& card) m_weakened = 0; m_is_summoned = false; m_step = CardStep::none; + std::memset(m_enhanced_value, 0, sizeof m_enhanced_value); std::memset(m_skill_cd, 0, sizeof m_skill_cd); } //------------------------------------------------------------------------------ @@ -604,7 +602,7 @@ void resolve_skill(Field* fd) else if(!status->m_jammed) { #if defined(TYRANT_UNLEASHED) - unsigned enhanced_value = status->enhanced(fd, skill.id); + unsigned enhanced_value = status->enhanced(skill.id); auto& enhanced_s = enhanced_value > 0 ? apply_enhance(skill, enhanced_value) : skill; auto& modified_s = enhanced_s; #else @@ -629,9 +627,9 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector { auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, status, ss, mod, need_add_skill) : ss; auto skill_pos = status->m_card->m_skill_pos[ss.id]; - if (skill_pos > 0 && status->m_skill_cd[skill_pos - 1] > 0) + if (status->m_skill_cd[skill_pos] > 0) { - _DEBUG_MSG(2, "Cooling down (%u) %s skill %s\n", status->m_skill_cd[skill_pos - 1], status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); + _DEBUG_MSG(2, "Cooling down (%u) %s skill %s\n", status->m_skill_cd[skill_pos], status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); continue; } _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); @@ -803,6 +801,14 @@ Results play(Field* fd) { PlayCard(played_card, fd).op(); } + auto& structures(fd->tap->structures); + for(unsigned index(0), end(structures.size()); + index < end; + ++index) + { + CardStatus& status(structures[index]); + --status.m_delay; + } std::swap(fd->tapi, fd->tipi); std::swap(fd->tap, fd->tip); } @@ -880,9 +886,20 @@ Results play(Field* fd) } if(__builtin_expect(fd->end, false)) { break; } +#if defined(TYRANT_UNLEASHED) + // Evaluate Unleashed Battleground effect (Enhance all) + if (fd->bg_enhanced_skill != no_skill) + { + SkillSpec battleground_s{enhance, fd->bg_enhanced_value, allfactions, 0, fd->bg_enhanced_skill, true, SkillMod::on_activate}; + _DEBUG_MSG(2, "Evaluating Battleground skill %s\n", skill_description(fd->cards, battleground_s).c_str()); + fd->skill_queue.emplace_back(&fd->tap->commander, battleground_s); + resolve_skill(fd); + } +#else // Evaluate Legion skill fd->current_phase = Field::legion_phase; evaluate_legion(fd); +#endif // Evaluate commander fd->current_phase = Field::commander_phase; @@ -1240,10 +1257,12 @@ void turn_start_phase(Field* fd) remove_dead(fd->tip->structures); fd->fusion_count = 0; // Active player's commander card: +#if defined(TYRANT_UNLEASHED) for (auto & skill_cd : fd->tip->commander.m_skill_cd) { if (skill_cd > 0) { -- skill_cd; } } +#endif // Active player's assault cards: // update index // remove enfeeble, protect; apply poison damage, reduce delay @@ -1256,8 +1275,6 @@ void turn_start_phase(Field* fd) CardStatus& status(assaults[index]); status.m_index = index; status.m_enfeebled = 0; - status.m_enhanced_skill = no_skill; - status.m_enhanced_value = 0; status.m_evaded = 0; status.m_protected = 0; if(status.m_poisoned > 0) @@ -1271,10 +1288,13 @@ void turn_start_phase(Field* fd) -- status.m_delay; } if(status.m_card->m_fusion && status.m_delay == 0) { ++ fd->fusion_count; } +#if defined(TYRANT_UNLEASHED) + std::memset(status.m_enhanced_value, 0, sizeof status.m_enhanced_value); for (auto & skill_cd : status.m_skill_cd) { if (skill_cd > 0) { -- skill_cd; } } +#endif } } // Active player's structure cards: @@ -1294,10 +1314,12 @@ void turn_start_phase(Field* fd) --status.m_delay; } if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } +#if defined(TYRANT_UNLEASHED) for (auto & skill_cd : status.m_skill_cd) { if (skill_cd > 0) { -- skill_cd; } } +#endif } } // Defending player's assault cards: @@ -1403,7 +1425,7 @@ inline unsigned counter_damage(Field* fd, CardStatus* att, CardStatus* def) { assert(att->m_card->m_type == CardType::assault); assert(def->m_card->m_type != CardType::action); - return(safe_minus(def->m_card->m_counter + def->enhanced(fd, counter) + att->m_enfeebled, att->m_protected)); + return(safe_minus(def->m_card->m_counter + def->enhanced(counter) + att->m_enfeebled, att->m_protected)); } inline CardStatus* select_first_enemy_wall(Field* fd) { @@ -1528,7 +1550,7 @@ struct PerformAttack { count_achievement(fd, att_status); // perform_skill_berserk - att_status->m_berserk += att_status->m_card->m_berserk + att_status->enhanced(fd, berserk); + att_status->m_berserk += att_status->m_card->m_berserk + att_status->enhanced(berserk); } } crush_leech(); @@ -1575,7 +1597,7 @@ struct PerformAttack // prevent damage std::string reduced_desc; unsigned reduced_dmg(0); - unsigned armored_value(def_card.m_armored + def_status->enhanced(fd, armored)); + unsigned armored_value(def_card.m_armored + def_status->enhanced(armored)); if(armored_value == 0 && fd->effect == Effect::photon_shield && def_status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 0u : 1u)) { armored_value = 2; @@ -1632,7 +1654,7 @@ struct PerformAttack if(def_status->m_card->m_poison_oa > att_status->m_poisoned && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); - unsigned v = def_status->m_card->m_poison_oa + def_status->enhanced(fd, poison); + unsigned v = def_status->m_card->m_poison_oa + def_status->enhanced(poison); _DEBUG_MSG(1, "%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); att_status->m_poisoned = v; } @@ -1646,7 +1668,7 @@ struct PerformAttack if(def_status->m_hp > 0 && def_status->m_card->m_berserk_oa > 0 && skill_check(fd, def_status, nullptr)) { count_achievement(fd, def_status); - def_status->m_berserk += def_status->m_card->m_berserk_oa + def_status->enhanced(fd, berserk); + def_status->m_berserk += def_status->m_card->m_berserk_oa + def_status->enhanced(berserk); } if(def_status->m_card->m_sunder_oa && skill_check(fd, def_status, att_status)) { @@ -1696,7 +1718,7 @@ void PerformAttack::damage_dependant_pre_oa() { count_achievement(fd, att_status); // perform_skill_poison - unsigned v = att_status->m_card->m_poison + att_status->enhanced(fd, poison); + unsigned v = att_status->m_card->m_poison + att_status->enhanced(poison); _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); def_status->m_poisoned = v; } @@ -1729,7 +1751,7 @@ void PerformAttack::damage_dependant_pre_oa() { count_achievement(fd, att_status); // perform_skill_inhibit - unsigned v = att_status->m_card->m_inhibit + att_status->enhanced(fd, inhibit); + unsigned v = att_status->m_card->m_inhibit + att_status->enhanced(inhibit); _DEBUG_MSG(1, "%s inhibits %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); def_status->m_inhibited += v; } @@ -1767,7 +1789,7 @@ void PerformAttack::crush_leech() if(att_status->m_card->m_leech > 0 && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); - auto leech_value = std::min(att_dmg, att_status->m_card->m_leech + att_status->enhanced(fd, leech)); + auto leech_value = std::min(att_dmg, att_status->m_card->m_leech + att_status->enhanced(leech)); _DEBUG_MSG(1, "%s leeches %u health\n", status_description(att_status).c_str(), leech_value); add_hp(fd, att_status, leech_value); } @@ -1943,7 +1965,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, assert(battleground_s.id != no_skill); if(battleground_s.id == s.s) { return(true); } } - return(c->m_card->m_skill_pos[s.s] > 0); + return (c->m_card->m_skill_pos[s.s] > 0) && (defensive_skills.find(s.s) != defensive_skills.end() || is_active(c)); } template<> @@ -2066,8 +2088,9 @@ inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s template<> inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { - c->m_enhanced_skill = s.s; - c->m_enhanced_value += s.x; + auto skill_pos = c->m_card->m_skill_pos[s.s]; + assert(skill_pos > 0); + c->m_enhanced_value[skill_pos] += s.x; } template<> @@ -2316,7 +2339,7 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ { if(is_evadable && #if defined(TYRANT_UNLEASHED) - dst_status->m_evaded < dst_status->m_card->m_evade + dst_status->enhanced(fd, evade) && + dst_status->m_evaded < dst_status->m_card->m_evade + dst_status->enhanced(evade) && #else (dst_status->m_card->m_evade || (fd->effect == Effect::quicksilver && dst_status->m_card->m_type == CardType::assault)) && fd->flip() && #endif @@ -2336,7 +2359,7 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ auto skill_pos = src_status->m_card->m_skill_pos[skill_id]; if (skill_pos > 0 && s.c > 0) { - src_status->m_skill_cd[skill_pos - 1] = s.c; + src_status->m_skill_cd[skill_pos] = s.c; } return(true); } @@ -2629,7 +2652,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // but resolve_skill will handle those. if( #if defined(TYRANT_UNLEASHED) - c->m_evaded < c->m_card->m_evade + c->enhanced(fd, evade) && + c->m_evaded < c->m_card->m_evade + c->enhanced(evade) && #else (c->m_card->m_evade || (fd->effect == Effect::quicksilver && c->m_card->m_type == CardType::assault)) && fd->flip() && #endif diff --git a/sim.h b/sim.h index af8c43d7..8f8b613a 100644 --- a/sim.h +++ b/sim.h @@ -135,11 +135,7 @@ struct CardStatus bool m_chaosed; unsigned m_delay; bool m_diseased; -// begin for TYRANT_UNLEASHED - Skill m_enhanced_skill; - unsigned m_enhanced_value; unsigned m_evaded; -// end unsigned m_enfeebled; Faction m_faction; bool m_frozen; @@ -159,7 +155,9 @@ struct CardStatus bool m_is_summoned; // is this card summoned (or split)? CardStep m_step; // begin for TYRANT_UNLEASHED - unsigned m_skill_cd[4]; // XXX + // indexed by m_card->m_skill_pos[skill_id], [0] as fallback + unsigned m_enhanced_value[5]; // XXX + unsigned m_skill_cd[5]; // XXX // end CardStatus() {} @@ -168,7 +166,7 @@ struct CardStatus void set(const Card* card); void set(const Card& card); std::string description(); - unsigned enhanced(Field* fd, Skill skill); + unsigned enhanced(Skill skill); }; //------------------------------------------------------------------------------ // Represents a particular draw from a deck. diff --git a/tyrant.cpp b/tyrant.cpp index 4fd38d9c..751bd58c 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -37,6 +37,10 @@ std::set helpful_skills{ augment, cleanse, enhance, heal, protect, rally, repair, rush, supply, }; +std::set defensive_skills{ + armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, tribute, wall, +}; + std::string skill_activation_modifier_names[SkillMod::num_skill_activation_modifiers] = {"", " on Play", " on Attacked", " on Kill", " on Death", }; std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Structure", "Action", }; diff --git a/tyrant.h b/tyrant.h index 39fca010..46655131 100644 --- a/tyrant.h +++ b/tyrant.h @@ -48,6 +48,7 @@ enum Skill }; extern std::string skill_names[num_skills]; extern std::set helpful_skills; +extern std::set defensive_skills; namespace SkillMod { enum SkillMod From 346413cf0f428f9acd15244e6fc88e2ba20bf615 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 1 May 2014 23:22:24 +0800 Subject: [PATCH 204/406] Use recipes xml. --- deck.cpp | 45 ------------------- tyrant_optimize.cpp | 2 +- xml.cpp | 102 ++++++++++++++++++++++++++------------------ xml.h | 1 + 4 files changed, 62 insertions(+), 88 deletions(-) diff --git a/deck.cpp b/deck.cpp index e8222f0a..b9e9cb82 100644 --- a/deck.cpp +++ b/deck.cpp @@ -415,48 +415,3 @@ void Deck::place_at_bottom(const Card* card) shuffled_cards.push_back(card); } -#if defined(TYRANT_UNLEASHED) -void load_fusions(Cards& cards) -{ - std::ifstream fusions_file("Fusions.txt"); - if(!fusions_file.is_open()) - { - std::cerr << "Warning: Failed to open Fusions.txt. Proceeding without reading from this file.\n"; - return; - } - - unsigned num_line(0); - fusions_file.exceptions(std::ifstream::badbit); - try - { - while(fusions_file && !fusions_file.eof()) - { - std::string recipe_string; - getline(fusions_file, recipe_string); - ++ num_line; - if(recipe_string.size() == 0 || strncmp(recipe_string.c_str(), "//", 2) == 0) - { - continue; - } - auto && card_list = string_to_ids(cards, recipe_string, "fusion recipe").first; - auto card_it = card_list.begin(); - auto & card = cards.cards_by_id.find(*card_it)->second; - for (++ card_it; card_it != card_list.end(); ++ card_it) - { - ++ card->m_material_list[cards.by_id(*card_it)]; - } - } - } - catch (std::exception& e) - { - std::cerr << "Exception while parsing the recipe file Fusions.txt"; - if(num_line > 0) - { - std::cerr << " at line " << num_line; - } - std::cerr << ": " << e.what() << ". Skip remaining lines.\n"; - return; - } -} -#endif - diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 385da535..e272e2a5 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1210,7 +1210,7 @@ int main(int argc, char** argv) load_decks_xml(decks, cards); load_decks(decks, cards); #if defined(TYRANT_UNLEASHED) - load_fusions(cards); + load_recipes_xml(cards); #endif fill_skill_table(); diff --git a/xml.cpp b/xml.cpp index 3b463b81..5675abcf 100644 --- a/xml.cpp +++ b/xml.cpp @@ -75,26 +75,10 @@ Faction skill_faction(xml_node<>* skill) return(unmapped_faction == 0 ? allfactions : map_to_faction(unmapped_faction)); } -unsigned skill_value(xml_node<>* skill) +unsigned node_value(xml_node<>* skill, const char* attribute) { - unsigned value(0); - xml_attribute<>* x(skill->first_attribute("x")); - if(x) - { - value = atoi(x->value()); - } - return(value); -} - -unsigned skill_cd(xml_node<>* skill) -{ - unsigned value(0); - xml_attribute<>* c(skill->first_attribute("c")); - if(c) - { - value = atoi(c->value()); - } - return(value); + xml_attribute<>* value_node(skill->first_attribute(attribute)); + return value_node ? atoi(value_node->value()) : 0; } Skill skill_target_skill(xml_node<>* skill) @@ -251,22 +235,22 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) if(normal) { card->m_skill_pos[skill_id] = skill_pos; } if(skill_id == antiair) - { card->m_antiair = skill_value(skill_node); } + { card->m_antiair = node_value(skill_node, "x"); } else if(skill_id == armored) - { card->m_armored = skill_value(skill_node); } + { card->m_armored = node_value(skill_node, "x"); } else if(skill_id == berserk) { - if(attacked) { card->m_berserk_oa = skill_value(skill_node); } - else {card->m_berserk = skill_value(skill_node); } + if(attacked) { card->m_berserk_oa = node_value(skill_node, "x"); } + else {card->m_berserk = node_value(skill_node, "x"); } } else if(skill_id == blitz) { card->m_blitz = true; } else if(skill_id == burst) - { card->m_burst = skill_value(skill_node); } + { card->m_burst = node_value(skill_node, "x"); } else if(skill_id == counter) - { card->m_counter = skill_value(skill_node); } + { card->m_counter = node_value(skill_node, "x"); } else if(skill_id == crush) - { card->m_crush = skill_value(skill_node); } + { card->m_crush = node_value(skill_node, "x"); } else if(skill_id == disease) { if(attacked) { card->m_disease_oa = true; } @@ -276,14 +260,14 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) { card->m_emulate = true; } else if(skill_id == evade) #if defined(TYRANT_UNLEASHED) - { card->m_evade = skill_value(skill_node); } + { card->m_evade = node_value(skill_node, "x"); } #else { card->m_evade = 1; } #endif else if(skill_id == fear) { card->m_fear = true; } else if(skill_id == flurry) - { card->m_flurry = skill_value(skill_node); } + { card->m_flurry = node_value(skill_node, "x"); } else if(skill_id == flying) { card->m_flying = true; } else if(skill_id == fusion) @@ -292,31 +276,31 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) { card->m_immobilize = true; } #if defined(TYRANT_UNLEASHED) else if(skill_id == inhibit) - { card->m_inhibit = skill_value(skill_node); } + { card->m_inhibit = node_value(skill_node, "x"); } #endif else if(skill_id == intercept) { card->m_intercept = true; } else if(skill_id == leech) - { card->m_leech = skill_value(skill_node); } + { card->m_leech = node_value(skill_node, "x"); } else if(skill_id == legion) - { card->m_legion = skill_value(skill_node); } + { card->m_legion = node_value(skill_node, "x"); } else if(skill_id == payback) { card->m_payback = true; } else if(skill_id == pierce) - { card->m_pierce = skill_value(skill_node); } + { card->m_pierce = node_value(skill_node, "x"); } else if(skill_id == phase) { card->m_phase = true; } else if(skill_id == poison) { - if(attacked) { card->m_poison_oa = skill_value(skill_node); } - else {card->m_poison = skill_value(skill_node); } + if(attacked) { card->m_poison_oa = node_value(skill_node, "x"); } + else {card->m_poison = node_value(skill_node, "x"); } } else if(skill_id == refresh) { card->m_refresh = true; } else if(skill_id == regenerate) - { card->m_regenerate = skill_value(skill_node); } + { card->m_regenerate = node_value(skill_node, "x"); } else if(skill_id == siphon) - { card->m_siphon = skill_value(skill_node); } + { card->m_siphon = node_value(skill_node, "x"); } else if(skill_id == stun) { card->m_stun = true; } else if(skill_id == sunder) @@ -329,16 +313,16 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) else if(skill_id == tribute) { card->m_tribute = true; } else if(skill_id == valor) - { card->m_valor = skill_value(skill_node); } + { card->m_valor = node_value(skill_node, "x"); } else if(skill_id == wall) { card->m_wall = true; } else { - if(played) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_cd(skill_node), skill_target_skill(skill_node), all, SkillMod::on_play); } - if(attacked) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_cd(skill_node), skill_target_skill(skill_node), all, SkillMod::on_attacked); } - if(kill) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_cd(skill_node), skill_target_skill(skill_node), all, SkillMod::on_kill); } - if(died) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_cd(skill_node), skill_target_skill(skill_node), all, SkillMod::on_death); } - if(normal) { card->add_skill(skill_id, skill_value(skill_node), skill_faction(skill_node), skill_cd(skill_node), skill_target_skill(skill_node), all); } + if(played) { card->add_skill(skill_id, node_value(skill_node, "x"), skill_faction(skill_node), node_value(skill_node, "c"), skill_target_skill(skill_node), all, SkillMod::on_play); } + if(attacked) { card->add_skill(skill_id, node_value(skill_node, "x"), skill_faction(skill_node), node_value(skill_node, "c"), skill_target_skill(skill_node), all, SkillMod::on_attacked); } + if(kill) { card->add_skill(skill_id, node_value(skill_node, "x"), skill_faction(skill_node), node_value(skill_node, "c"), skill_target_skill(skill_node), all, SkillMod::on_kill); } + if(died) { card->add_skill(skill_id, node_value(skill_node, "x"), skill_faction(skill_node), node_value(skill_node, "c"), skill_target_skill(skill_node), all, SkillMod::on_death); } + if(normal) { card->add_skill(skill_id, node_value(skill_node, "x"), skill_faction(skill_node), node_value(skill_node, "c"), skill_target_skill(skill_node), all); } } ++ skill_pos; } @@ -556,6 +540,40 @@ void read_quests(Decks& decks, const Cards& cards, std::string filename) deck->effect = static_cast(effect_id); } } + +//------------------------------------------------------------------------------ +void load_recipes_xml(Cards& cards) +{ + std::vector buffer; + xml_document<> doc; + parse_file("fusion_recipes_cj2.xml", buffer, doc); + xml_node<>* root = doc.first_node(); + + if(!root) + { + return; + } + + for(xml_node<>* recipe_node = root->first_node("fusion_recipe"); + recipe_node; + recipe_node = recipe_node->next_sibling("fusion_recipe")) + { + xml_node<>* card_id_node(recipe_node->first_node("card_id")); + if (!card_id_node) { continue; } + unsigned card_id(atoi(card_id_node->value())); + auto & card = cards.cards_by_id[card_id]; + for(xml_node<>* resource_node = recipe_node->first_node("resource"); + resource_node; + resource_node = resource_node->next_sibling("resource")) + { + unsigned card_id(node_value(resource_node, "card_id")); + unsigned number(node_value(resource_node, "number")); + if (card_id == 0 || number == 0) { continue; } + card->m_material_list[cards.by_id(card_id)] += number; + } + } +} + //------------------------------------------------------------------------------ extern unsigned turn_limit; Comparator get_comparator(xml_node<>* node, Comparator default_comparator) diff --git a/xml.h b/xml.h index afd3bf3c..d955436d 100644 --- a/xml.h +++ b/xml.h @@ -10,6 +10,7 @@ class Achievement; Skill skill_name_to_id(const char* name); void load_decks_xml(Decks& decks, const Cards& cards); +void load_recipes_xml(Cards& cards); void read_cards(Cards& cards); void read_missions(Decks& decks, const Cards& cards, std::string filename); void read_raids(Decks& decks, const Cards& cards, std::string filename); From f7868ce20b4593bbbb0c3ab7948c513c0b20f2d9 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 3 May 2014 19:31:04 +0800 Subject: [PATCH 205/406] Use tu_optimize style card name. --- cards.cpp | 11 +++-------- sim.cpp | 8 -------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/cards.cpp b/cards.cpp index 2fcdfd0b..2d4bcde5 100644 --- a/cards.cpp +++ b/cards.cpp @@ -84,18 +84,13 @@ void Cards::organize() card->m_name.erase(pos, 1); } #if defined(TYRANT_UNLEASHED) - if(card->m_level == 1) + if (card->m_level > 1 && card->m_id == by_id(card->m_base_id)->m_final_id) { - player_cards_by_name[{simplify_name(card->m_name + " Lv.1"), card->m_hidden}] = card; + player_cards_by_name[{simplify_name(card->m_name + "-" + to_string(card->m_level)), card->m_hidden}] = card; } else { - if (card->m_id == by_id(card->m_base_id)->m_final_id) - { - player_cards_by_name[{simplify_name(card->m_name + "*"), card->m_hidden}] = card; - } - card->m_name += " Lv."; - card->m_name += to_string(card->m_level); + card->m_name += "-" + to_string(card->m_level); } #else if(card->m_set == 5002) diff --git a/sim.cpp b/sim.cpp index 18264549..b03e61e0 100644 --- a/sim.cpp +++ b/sim.cpp @@ -801,14 +801,6 @@ Results play(Field* fd) { PlayCard(played_card, fd).op(); } - auto& structures(fd->tap->structures); - for(unsigned index(0), end(structures.size()); - index < end; - ++index) - { - CardStatus& status(structures[index]); - --status.m_delay; - } std::swap(fd->tapi, fd->tipi); std::swap(fd->tap, fd->tip); } From 026cd1c847c5bc9d77e67c42f1fc532bc7fb363f Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 4 May 2014 16:19:35 +0800 Subject: [PATCH 206/406] Add end phase and fix Poison skill. --- sim.cpp | 159 ++++++++++++++++++++++++++++++++++++++++++------------- sim.h | 3 +- tyrant.h | 2 +- 3 files changed, 126 insertions(+), 38 deletions(-) diff --git a/sim.cpp b/sim.cpp index b03e61e0..1939bde9 100644 --- a/sim.cpp +++ b/sim.cpp @@ -777,6 +777,7 @@ inline bool can_attack(CardStatus* c) { return(can_act(c) && !c->m_immobilized & inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } //------------------------------------------------------------------------------ void turn_start_phase(Field* fd); +void turn_end_phase(Field* fd); void evaluate_legion(Field* fd); bool check_and_perform_refresh(Field* fd, CardStatus* src_status); // return value : (raid points) -> attacker wins, 0 -> defender wins @@ -879,7 +880,7 @@ Results play(Field* fd) if(__builtin_expect(fd->end, false)) { break; } #if defined(TYRANT_UNLEASHED) - // Evaluate Unleashed Battleground effect (Enhance all) + // Evaluate TU Battleground effect (Enhance all) if (fd->bg_enhanced_skill != no_skill) { SkillSpec battleground_s{enhance, fd->bg_enhanced_value, allfactions, 0, fd->bg_enhanced_skill, true, SkillMod::on_activate}; @@ -933,6 +934,8 @@ Results play(Field* fd) } current_status.m_step = CardStep::attacked; } + fd->current_phase = Field::end_phase; + turn_end_phase(fd); if(__builtin_expect(fd->end, false)) { break; } _DEBUG_MSG(1, "TURN %u ends for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); std::swap(fd->tapi, fd->tipi); @@ -1243,21 +1246,18 @@ void check_regeneration(Field* fd) } void turn_start_phase(Field* fd) { - remove_dead(fd->tap->assaults); - remove_dead(fd->tap->structures); - remove_dead(fd->tip->assaults); - remove_dead(fd->tip->structures); fd->fusion_count = 0; // Active player's commander card: #if defined(TYRANT_UNLEASHED) - for (auto & skill_cd : fd->tip->commander.m_skill_cd) + for (auto & skill_cd : fd->tap->commander.m_skill_cd) { if (skill_cd > 0) { -- skill_cd; } } #endif // Active player's assault cards: // update index - // remove enfeeble, protect; apply poison damage, reduce delay + // reduce delay; [TU] reduce skill cooldown + // [WM:T] apply poison damage { auto& assaults(fd->tap->assaults); for(unsigned index(0), end(assaults.size()); @@ -1266,32 +1266,29 @@ void turn_start_phase(Field* fd) { CardStatus& status(assaults[index]); status.m_index = index; - status.m_enfeebled = 0; - status.m_evaded = 0; - status.m_protected = 0; - if(status.m_poisoned > 0) - { - _DEBUG_MSG(1, "%s takes poison damage\n", status_description(&status).c_str()); - remove_hp(fd, status, status.m_poisoned); - } if(status.m_delay > 0 && !status.m_frozen) { _DEBUG_MSG(1, "%s reduces its timer\n", status_description(&status).c_str()); -- status.m_delay; } - if(status.m_card->m_fusion && status.m_delay == 0) { ++ fd->fusion_count; } #if defined(TYRANT_UNLEASHED) - std::memset(status.m_enhanced_value, 0, sizeof status.m_enhanced_value); for (auto & skill_cd : status.m_skill_cd) { if (skill_cd > 0) { -- skill_cd; } } +#else + if(status.m_poisoned > 0) + { + _DEBUG_MSG(1, "%s takes poison damage\n", status_description(&status).c_str()); + remove_hp(fd, status, status.m_poisoned); + } + if(status.m_card->m_fusion && status.m_delay == 0) { ++ fd->fusion_count; } #endif } } // Active player's structure cards: // update index - // reduce delay + // reduce delay; [TU] reduce skill cooldown { auto& structures(fd->tap->structures); for(unsigned index(0), end(structures.size()); @@ -1305,19 +1302,18 @@ void turn_start_phase(Field* fd) _DEBUG_MSG(1, "%s reduces its timer\n", status_description(&status).c_str()); --status.m_delay; } - if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } #if defined(TYRANT_UNLEASHED) for (auto & skill_cd : status.m_skill_cd) { if (skill_cd > 0) { -- skill_cd; } } +#else + if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } #endif } } // Defending player's assault cards: // update index - // remove augment, chaos, freeze, immobilize, jam, rally, weaken, apply refresh - // remove temp split { auto& assaults(fd->tip->assaults); for(unsigned index(0), end(assaults.size()); @@ -1326,47 +1322,138 @@ void turn_start_phase(Field* fd) { CardStatus& status(assaults[index]); status.m_index = index; + } + } + // Defending player's structure cards: + // update index + { + auto& structures(fd->tip->structures); + for(unsigned index(0), end(structures.size()); + index < end; + ++index) + { + CardStatus& status(structures[index]); + status.m_index = index; + } + } +#if not defined(TYRANT_UNLEASHED) + // Perform on death skills (from cards killed by poison damage) + prepend_on_death(fd); + resolve_skill(fd); + // Regen from poison + check_regeneration(fd); +#endif +} +void turn_end_phase(Field* fd) +{ + // Active player's assault cards: + // remove augment, chaos, freeze, immobilize, jam, rally, weaken; apply refresh + // remove temp split + // [TU] apply poison damage + { + auto& assaults(fd->tap->assaults); + for(unsigned index(0), end(assaults.size()); + index < end; + ++index) + { + CardStatus& status(assaults[index]); + if (status.m_hp <= 0) + { + continue; + } + status.m_jammed = false; + status.m_rallied = 0; + status.m_weakened = 0; + status.m_step = CardStep::none; +#if defined(TYRANT_UNLEASHED) + status.m_inhibited = 0; + if(status.m_poisoned > 0) + { + _DEBUG_MSG(1, "%s takes poison damage\n", status_description(&status).c_str()); + remove_hp(fd, status, status.m_poisoned); + } +#else + // not need to fade out in own turn in TU + status.m_enfeebled = 0; + // not in TU status.m_augmented = 0; status.m_chaosed = false; - status.m_enfeebled = 0; status.m_frozen = false; status.m_immobilized = false; - status.m_inhibited = 0; - status.m_jammed = false; status.m_phased = false; - status.m_rallied = 0; if(status.m_stunned > 0) { -- status.m_stunned; } - status.m_weakened = 0; status.m_temporary_split = false; - status.m_step = CardStep::none; if(status.m_card->m_refresh) { check_and_perform_refresh(fd, &status); } +#endif } } - // Defending player's structure cards: - // update index + // Active player's structure cards: // apply refresh +#if not defined(TYRANT_UNLEASHED) { - auto& structures(fd->tip->structures); + auto& structures(fd->tap->structures); for(unsigned index(0), end(structures.size()); index < end; ++index) { CardStatus& status(structures[index]); - status.m_index = index; + if (status.m_hp <= 0) + { + continue; + } if(status.m_card->m_refresh && fd->effect != Effect::impenetrable) { check_and_perform_refresh(fd, &status); } } } - // Perform on death skills (from cards killed by poison damage) - prepend_on_death(fd); - resolve_skill(fd); - // Regen from poison - check_regeneration(fd); +#endif + // Defending player's assault cards: + // remove enfeeble, protect + { + auto& assaults(fd->tip->assaults); + for(unsigned index(0), end(assaults.size()); + index < end; + ++index) + { + CardStatus& status(assaults[index]); + if (status.m_hp <= 0) + { + continue; + } + status.m_enfeebled = 0; + status.m_protected = 0; +#if defined(TYRANT_UNLEASHED) + // so far only useful in Defending turn + status.m_evaded = 0; + std::memset(status.m_enhanced_value, 0, sizeof status.m_enhanced_value); +#endif + } + } + // Defending player's structure cards: + // nothing so far +#if 0 + { + auto& structures(fd->tip->structures); + for(unsigned index(0), end(structures.size()); + index < end; + ++index) + { + CardStatus& status(structures[index]); + if (status.m_hp <= 0) + { + continue; + } + } + } +#endif + remove_dead(fd->tap->assaults); + remove_dead(fd->tap->structures); + remove_dead(fd->tip->assaults); + remove_dead(fd->tip->structures); } void evaluate_legion(Field* fd) { diff --git a/sim.h b/sim.h index 8f8b613a..623ad9f0 100644 --- a/sim.h +++ b/sim.h @@ -224,7 +224,8 @@ class Field legion_phase, commander_phase, structures_phase, - assaults_phase + assaults_phase, + end_phase, }; // the current phase of the turn: starts with playcard_phase, then commander_phase, structures_phase, and assaults_phase phase current_phase; diff --git a/tyrant.h b/tyrant.h index 46655131..04346e3d 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.2.2" +#define TYRANT_OPTIMIZER_VERSION "1.2.3" #include #include From 73f9b72fcd294371c10bacff901c44e0e5aa3c45 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 11 May 2014 00:48:10 +0800 Subject: [PATCH 207/406] Support nested group & regex in custom.txt. --- Makefile | 6 +- Makefile.win | 17 ------ cards.cpp | 10 +--- deck.cpp | 34 +++++++++-- deck.h | 4 +- read.cpp | 137 +++++++++++++++++++++++++++++++++++--------- read.h | 6 +- sim.cpp | 19 +++--- tyrant.h | 13 ++++- tyrant_optimize.cpp | 8 +-- xml.cpp | 2 + xml.h | 2 + 12 files changed, 175 insertions(+), 83 deletions(-) delete mode 100644 Makefile.win diff --git a/Makefile b/Makefile index 07bb9478..26f5ac54 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ SRCS := $(wildcard *.cpp) OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) INCS := $(wildcard *.h) -CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -DTYRANT_UNLEASHED -LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem +CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -DTYRANT_UNLEASHED -DNDEBUG +LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_regex all: $(MAIN) @@ -15,4 +15,4 @@ $(MAIN): $(OBJS) $(CXX) -o $@ $(OBJS) $(LDFLAGS) clean: - rm -f $(MAIN) obj/*.o + del /q $(MAIN).exe obj\*.o diff --git a/Makefile.win b/Makefile.win deleted file mode 100644 index f993f22c..00000000 --- a/Makefile.win +++ /dev/null @@ -1,17 +0,0 @@ -MAIN := tyrant_optimize -SRCS := $(wildcard *.cpp) -OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) - -CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_chrono - -all: $(MAIN) - -obj/%.o: %.cpp - $(CXX) $(CPPFLAGS) -o $@ -c $< - -$(MAIN): $(OBJS) - $(CXX) -o $@ $(OBJS) $(LDFLAGS) - -clean: - del /q $(MAIN).exe obj\*.o diff --git a/cards.cpp b/cards.cpp index 2d4bcde5..ff7db684 100644 --- a/cards.cpp +++ b/cards.cpp @@ -7,16 +7,8 @@ #include #include -#include "card.h" #include "tyrant.h" - -template -std::string to_string(T val) -{ - std::stringstream s; - s << val; - return s.str(); -} +#include "card.h" std::string simplify_name(const std::string& card_name) { diff --git a/deck.cpp b/deck.cpp index b9e9cb82..d8d56d46 100644 --- a/deck.cpp +++ b/deck.cpp @@ -263,12 +263,40 @@ std::string Deck::short_description() const 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"; + } + for(const Card * card: cards) + { + ios << ", " << card->m_name; + } + unsigned num_pool_cards = 0; + for(auto& pool: raid_cards) + { + num_pool_cards += pool.first; + } + if(num_pool_cards > 0) + { + ios << ", and " << num_pool_cards << " cards from pool"; + } + return ios.str(); +} + extern std::string card_description(const Cards& cards, const Card* c); std::string Deck::long_description(const Cards& all_cards) const { std::stringstream ios; - ios << short_description() << std::endl; + ios << medium_description() << "\n"; if(effect != Effect::none) { ios << "Effect: " << effect_names[effect] << "\n"; @@ -281,10 +309,6 @@ std::string Deck::long_description(const Cards& all_cards) const { ios << "No commander\n"; } - if(!cards.empty() && !raid_cards.empty()) - { - ios << "Always include:\n"; - } for(const Card* card: cards) { ios << " " << card_description(all_cards, card) << "\n"; diff --git a/deck.h b/deck.h index 9afe5e5a..ac56a59f 100644 --- a/deck.h +++ b/deck.h @@ -103,6 +103,7 @@ struct Deck Deck* clone() const; std::string short_description() const; + std::string medium_description() const; std::string long_description(const Cards& all_cards) const; const Card* get_commander(); const Card* next(); @@ -110,13 +111,12 @@ struct Deck void place_at_bottom(const Card* card); }; -// + also the custom decks +typedef std::map DeckList; struct Decks { std::list decks; std::map, Deck*> by_type_id; std::map by_name; - std::map custom_decks; }; #if defined(TYRANT_UNLEASHED) diff --git a/read.cpp b/read.cpp index a7dcfbf2..e6427a8e 100644 --- a/read.cpp +++ b/read.cpp @@ -3,12 +3,15 @@ #include #include #include +#include #include -#include +#include +#include #include #include #include +#include "tyrant.h" #include "card.h" #include "cards.h" #include "deck.h" @@ -21,24 +24,6 @@ void load_decks(Decks& decks, Cards& cards) } } -std::vector> parse_deck_list(std::string list_string) -{ - std::vector> res; - boost::tokenizer> list_tokens{list_string, boost::char_delimiters_separator{false, ";", ""}}; - for(const auto list_token : list_tokens) - { - boost::tokenizer> deck_tokens{list_token, boost::char_delimiters_separator{false, ":", ""}}; - auto deck_token = deck_tokens.begin(); - res.push_back(std::make_pair(*deck_token, 1.0d)); - ++deck_token; - if(deck_token != deck_tokens.end()) - { - res.back().second = boost::lexical_cast(*deck_token); - } - } - return(res); -} - template Iterator advance_until(Iterator it, Iterator it_end, Functor f) { while(it != it_end) @@ -80,9 +65,105 @@ template Iterator read_toke return(token_end_after_spaces); } +DeckList & normalize(DeckList & decklist) +{ + long double factor_sum = 0; + for (const auto & it : decklist) + { + factor_sum += it.second; + } + if (factor_sum > 0) + { + for (auto & it : decklist) + { + it.second /= factor_sum; + } + } + return decklist; +} + +DeckList expand_deck_to_list(std::string deck_name, const Decks& decks) +{ + static std::unordered_set expanding_decks; + if (expanding_decks.count(deck_name)) + { + std::cerr << "Warning: circular referred deck: " << deck_name << std::endl; + return {}; + } + auto deck_string = deck_name; + const auto & deck = decks.by_name.find(deck_name); + if (deck != decks.by_name.end()) + { + deck_string = deck->second->deck_string; + if (deck_string.find_first_of(";:") != std::string::npos || decks.by_name.find(deck_string) != decks.by_name.end()) + { + // deck_name refers to a deck list + expanding_decks.insert(deck_name); + auto && decklist = parse_deck_list(deck_string, decks); + expanding_decks.erase(deck_name); + return normalize(decklist); + } + } + + if (deck_string.length() >= 3 && deck_string.front() == '/' && deck_string.back() == '/') + { + // deck_name is, or refers to, a regex + DeckList res; + std::string regex_string(deck_string, 1, deck_string.length() - 2); + boost::regex regex(regex_string); + boost::smatch smatch; + expanding_decks.insert(deck_name); + for (const auto & deck: decks.by_name) + { + if (boost::regex_search(deck.first, smatch, regex)) + { + auto && decklist = expand_deck_to_list(deck.first, decks); + for (const auto & it : decklist) + { + res[it.first] += it.second; + } + } + } + expanding_decks.erase(deck_name); + if (res.size() == 0) + { + std::cerr << "Warning: regular expression matches nothing: /" << regex_string << "/." << std::endl; + } + return normalize(res); + } + else + { + return {{deck_name, 1}}; + } +} + +DeckList parse_deck_list(std::string list_string, const Decks& decks) +{ + DeckList res; + boost::tokenizer> list_tokens{list_string, boost::char_delimiters_separator{false, ";", ""}}; + for(const auto & list_token : list_tokens) + { + boost::tokenizer> deck_tokens{list_token, boost::char_delimiters_separator{false, ":", ""}}; + auto deck_token = deck_tokens.begin(); + auto deck_name = *deck_token; + double factor = 1.0d; + ++ deck_token; + if(deck_token != deck_tokens.end()) + { + factor = boost::lexical_cast(*deck_token); + } + auto && decklist = expand_deck_to_list(deck_name, decks); + for (const auto & it : decklist) + { + res[it.first] += it.second * factor; + } + } + return res; +} + void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark) { - static std::set recognized_abbr; +// static std::set recognized_abbr; auto card_spec_iter = card_spec.begin(); card_id = 0; card_num = 1; @@ -101,14 +182,14 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ } // If card name is not found, try find card id quoted in '[]' in name, ignoring other characters. std::string simple_name{simplify_name(card_name)}; - auto abbr_it = cards.player_cards_abbr.find(simple_name); + const auto && abbr_it = cards.player_cards_abbr.find(simple_name); if(abbr_it != cards.player_cards_abbr.end()) { - if(recognized_abbr.count(card_name) == 0) - { - std::cout << "Recognize abbreviation " << card_name << ": " << abbr_it->second << std::endl; - recognized_abbr.insert(card_name); - } +// if(recognized_abbr.count(card_name) == 0) +// { +// std::cout << "Recognize abbreviation " << card_name << ": " << abbr_it->second << std::endl; +// recognized_abbr.insert(card_name); +// } simple_name = simplify_name(abbr_it->second); } auto card_it = cards.player_cards_by_name.find({simple_name, 0}); @@ -170,7 +251,7 @@ unsigned read_card_abbrs(Cards& cards, const std::string& filename) auto abbr_string_iter = read_token(abbr_string.begin(), abbr_string.end(), [](char c){return(c == ':');}, abbr_name); if(abbr_string_iter == abbr_string.end() || abbr_name.empty()) { - std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ", could not read the deck name.\n"; + std::cerr << "Error in card abbreviation file " << filename << " at line " << num_line << ", could not read the name.\n"; continue; } abbr_string_iter = advance_until(abbr_string_iter + 1, abbr_string.end(), [](const char& c){return(c != ' ');}); @@ -223,7 +304,7 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) continue; } std::string deck_name; - auto deck_string_iter = read_token(deck_string.begin(), deck_string.end(), [](char c){return(strchr(":,", c));}, deck_name); + auto deck_string_iter = read_token(deck_string.begin(), deck_string.end(), [](char c){return(strchr(":", c));}, deck_name); if(deck_string_iter == deck_string.end() || deck_name.empty()) { std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ", could not read the deck name.\n"; diff --git a/read.h b/read.h index 80bb51cb..03f1604f 100644 --- a/read.h +++ b/read.h @@ -3,14 +3,16 @@ #include #include -#include +#include + +#include "deck.h" class Cards; class Decks; class Deck; void load_decks(Decks& decks, Cards& cards); -std::vector> parse_deck_list(std::string list_string); +DeckList parse_deck_list(std::string list_string, const Decks& decks); void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark); unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename); void read_owned_cards(Cards& cards, std::map& owned_cards, std::map& buyable_cards, const char *filename); diff --git a/sim.cpp b/sim.cpp index 1939bde9..12011646 100644 --- a/sim.cpp +++ b/sim.cpp @@ -7,19 +7,12 @@ #include #include +#include "tyrant.h" #include "card.h" #include "cards.h" #include "deck.h" #include "achievement.h" -//---------------------- $00 general stuff ------------------------------------- -template -std::string to_string(T val) -{ - std::stringstream s; - s << val; - return s.str(); -} //---------------------- Debugging stuff --------------------------------------- unsigned debug_print(0); unsigned debug_cached(0); @@ -73,10 +66,12 @@ inline unsigned Field::make_selection_array(CardsIter first, CardsIter last, Fun } inline void Field::print_selection_array() { +#ifndef NDEBUG for(auto c: this->selection_array) { _DEBUG_MSG(2, "+ %s\n", status_description(c).c_str()); } +#endif } //------------------------------------------------------------------------------ CardStatus::CardStatus(const Card* card) : @@ -207,13 +202,13 @@ std::string card_description(const Cards& cards, const Card* c) case CardType::action: break; case CardType::assault: - desc += " " + to_string(c->m_attack) + "/" + to_string(c->m_health) + "/" + to_string(c->m_delay); + desc += ": " + to_string(c->m_attack) + "/" + to_string(c->m_health) + "/" + to_string(c->m_delay); break; case CardType::structure: - desc += " " + to_string(c->m_health) + "/" + to_string(c->m_delay); + desc += ": " + to_string(c->m_health) + "/" + to_string(c->m_delay); break; case CardType::commander: - desc += " hp:" + to_string(c->m_health); + desc += ": hp:" + to_string(c->m_health); break; case CardType::num_cardtypes: assert(false); @@ -610,7 +605,7 @@ void resolve_skill(Field* fd) auto& augmented_s = status->m_augmented > 0 ? apply_augment(status, skill) : skill; auto& fusioned_s = fusion_active ? apply_fusion(augmented_s) : augmented_s; auto& infused_s = status->m_infused ? apply_infuse(fusioned_s) : fusioned_s; - auto& modified_s = modified_s; + auto& modified_s = infused_s; #endif skill_table[skill.id](fd, status, modified_s); } diff --git a/tyrant.h b/tyrant.h index 04346e3d..7f355741 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,9 +1,10 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.2.3" +#define TYRANT_OPTIMIZER_VERSION "1.2.4" #include +#include #include #include @@ -182,4 +183,14 @@ struct SkillSpec SkillMod::SkillMod mod; }; +// -------------------------------------------------------------------------------- +// Common functions +template +std::string to_string(const T val) +{ + std::stringstream s; + s << val; + return s.str(); +} + #endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index e272e2a5..bc1f9d35 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1220,7 +1220,7 @@ int main(int argc, char** argv) return(4); } std::string att_deck_name{argv[1]}; - auto deck_list_parsed = parse_deck_list(argv[2]); + auto && deck_list_parsed = parse_deck_list(argv[2], decks); Deck* att_deck{nullptr}; std::vector def_decks; @@ -1541,10 +1541,10 @@ int main(int argc, char** argv) min_deck_len = max_deck_len = att_deck->cards.size(); } - std::cout << "Your Deck: " << (debug_print ? att_deck->long_description(cards) : att_deck->short_description()) << std::endl; - for(auto def_deck: def_decks) + std::cout << "Your Deck: " << (debug_print ? att_deck->long_description(cards) : att_deck->medium_description()) << std::endl; + for (unsigned i(0); i < def_decks.size(); ++i) { - std::cout << "Enemy's Deck: " << (debug_print ? def_deck->long_description(cards) : def_deck->short_description()) << std::endl; + std::cout << "Enemy's Deck:" << def_decks_factors[i] << ": " << (debug_print ? def_decks[i]->long_description(cards) : def_decks[i]->medium_description()) << std::endl; } if(effect != Effect::none) { diff --git a/xml.cpp b/xml.cpp index 5675abcf..7d515372 100644 --- a/xml.cpp +++ b/xml.cpp @@ -542,6 +542,7 @@ void read_quests(Decks& decks, const Cards& cards, std::string filename) } //------------------------------------------------------------------------------ +#if defined(TYRANT_UNLEASHED) void load_recipes_xml(Cards& cards) { std::vector buffer; @@ -573,6 +574,7 @@ void load_recipes_xml(Cards& cards) } } } +#endif //------------------------------------------------------------------------------ extern unsigned turn_limit; diff --git a/xml.h b/xml.h index d955436d..3e1a5e44 100644 --- a/xml.h +++ b/xml.h @@ -10,7 +10,9 @@ class Achievement; Skill skill_name_to_id(const char* name); void load_decks_xml(Decks& decks, const Cards& cards); +#if defined(TYRANT_UNLEASHED) void load_recipes_xml(Cards& cards); +#endif void read_cards(Cards& cards); void read_missions(Decks& decks, const Cards& cards, std::string filename); void read_raids(Decks& decks, const Cards& cards, std::string filename); From 3ba8aba6e05bd80321534a97f83d23f21fcfe7ac Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 16 May 2014 03:18:25 +0800 Subject: [PATCH 208/406] Add Corrosive skill. --- card.h | 2 + sim.cpp | 124 ++++++++++++++++++++++++++++++++++++++++------------- sim.h | 2 + tyrant.cpp | 2 +- tyrant.h | 4 +- xml.cpp | 6 +-- 6 files changed, 104 insertions(+), 36 deletions(-) diff --git a/card.h b/card.h index 5c44f9f5..3f617937 100644 --- a/card.h +++ b/card.h @@ -17,6 +17,7 @@ class Card unsigned m_berserk_oa; bool m_blitz; unsigned m_burst; + unsigned m_corrosive; unsigned m_counter; unsigned m_crush; unsigned m_delay; @@ -82,6 +83,7 @@ class Card m_berserk_oa(0), m_blitz(false), m_burst(0), + m_corrosive(0), m_counter(0), m_crush(0), m_delay(0), diff --git a/sim.cpp b/sim.cpp index 12011646..d5d8caa1 100644 --- a/sim.cpp +++ b/sim.cpp @@ -78,10 +78,12 @@ CardStatus::CardStatus(const Card* card) : m_card(card), m_index(0), m_player(0), + m_attack(card->m_attack), m_augmented(0), m_berserk(0), m_blitzing(false), m_chaosed(false), + m_corroded(0), m_delay(card->m_delay), m_diseased(false), m_evaded(0), @@ -124,10 +126,12 @@ inline void CardStatus::set(const Card& card) m_card = &card; m_index = 0; m_player = 0; + m_attack = card.m_attack; m_augmented = 0; m_berserk = 0; m_blitzing = false; m_chaosed = false; + m_corroded = 0; m_delay = card.m_delay; m_diseased = false; m_evaded = 0; @@ -155,7 +159,7 @@ inline void CardStatus::set(const Card& card) //------------------------------------------------------------------------------ inline int attack_power(CardStatus* att) { - return(safe_minus(att->m_card->m_attack + att->m_berserk + att->m_rallied, att->m_weakened)); + return(safe_minus(att->m_attack + att->m_rallied, att->m_weakened)); } //------------------------------------------------------------------------------ std::string skill_description(const Cards& cards, const SkillSpec& s) @@ -223,6 +227,7 @@ std::string card_description(const Cards& cards, const Card* c) if(c->m_blitz) { desc += ", blitz"; } if(c->m_burst > 0) { desc += ", burst " + to_string(c->m_burst); } if(c->m_counter > 0) { desc += ", counter " + to_string(c->m_counter); } + if(c->m_corrosive > 0) { desc += ", corrosive " + to_string(c->m_corrosive); } if(c->m_crush > 0) { desc += ", crush " + to_string(c->m_crush); } if(c->m_disease) { desc += ", disease"; } if(c->m_emulate) { desc += ", emulate"; } @@ -281,10 +286,9 @@ std::string CardStatus::description() case CardType::action: break; case CardType::assault: - desc += " att:" + to_string(m_card->m_attack); + desc += " att:" + to_string(m_attack); { std::string att_desc; - if(m_berserk > 0) { att_desc += "+" + to_string(m_berserk) + "(berserk)"; } if(m_rallied > 0) { att_desc += "+" + to_string(m_rallied) + "(rallied)"; } if(m_weakened > 0) { att_desc += "-" + to_string(m_weakened) + "(weakened)"; } if(!att_desc.empty()) { desc += att_desc + "=" + to_string(attack_power(this)); } @@ -311,6 +315,8 @@ std::string CardStatus::description() if(m_sundered) { desc += ", sundered"; } if(m_temporary_split) { desc += ", cloning"; } if(m_augmented > 0) { desc += ", augmented " + to_string(m_augmented); } + if(m_berserk > 0) { desc += ", berserk " + to_string(m_berserk); } + if(m_corroded > 0) { desc += ", corroded " + to_string(m_corroded); } if(m_enfeebled > 0) { desc += ", enfeebled " + to_string(m_enfeebled); } if(m_inhibited > 0) { desc += ", inhibited " + to_string(m_inhibited); } if(m_poisoned > 0) { desc += ", poisoned " + to_string(m_poisoned); } @@ -612,7 +618,7 @@ void resolve_skill(Field* fd) } } //------------------------------------------------------------------------------ -void attack_phase(Field* fd); +bool attack_phase(Field* fd); void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills, const SkillMod::SkillMod mod) { assert(status); @@ -910,22 +916,36 @@ Results play(Field* fd) { // ca: current assault CardStatus& current_status(fd->tap->assaults[fd->current_ci]); + bool attacked = false; if(!is_active(¤t_status) || !can_act(¤t_status)) { _DEBUG_MSG(2, "Assault %s cannot take action.\n", status_description(¤t_status).c_str()); - current_status.m_step = CardStep::attacked; - continue; } - current_status.m_blitzing = false; - // Evaluate skills - evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills[SkillMod::on_activate], SkillMod::on_activate); - if(__builtin_expect(fd->end, false)) { break; } + else + { + current_status.m_blitzing = false; + // Evaluate skills + evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills[SkillMod::on_activate], SkillMod::on_activate); + if(__builtin_expect(fd->end, false)) { break; } - // Attack - if(can_attack(¤t_status)) + // Attack + if(can_attack(¤t_status)) + { + current_status.m_step = CardStep::attacking; + attacked = attack_phase(fd); + } + } + if (!attacked) { - current_status.m_step = CardStep::attacking; - attack_phase(fd); +#if defined(TYRANT_UNLEASHED) + CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); + if (att_status->m_corroded > 0) + { + att_status->m_corroded = 0; + att_status->m_attack = att_status->m_card->m_attack + att_status->m_berserk; + _DEBUG_MSG(1, "%s lost corroded.\n", status_description(att_status).c_str()); + } +#endif } current_status.m_step = CardStep::attacked; } @@ -1219,6 +1239,7 @@ inline void add_hp(Field* fd, CardStatus* target, unsigned v) { unsigned healed = target->m_hp - old_hp; target->m_berserk += healed; + target->m_attack += healed; } } void check_regeneration(Field* fd) @@ -1572,12 +1593,14 @@ struct PerformAttack // counter, berserk // assaults only: (crush, leech if still alive) // check regeneration +#if not defined(TYRANT_UNLEASHED) if(def_status->m_card->m_flying && (fd->effect == Effect::high_skies || fd->flip()) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); _DEBUG_MSG(1, "%s attacks %s but it dodges with Flying\n", status_description(att_status).c_str(), status_description(def_status).c_str()); return; } +#endif modify_attack_damage(pre_modifier_dmg); if(att_status->m_player == 0) @@ -1585,6 +1608,7 @@ struct PerformAttack fd->update_max_counter(fd->achievement.misc_req, AchievementMiscReq::damage, att_dmg); } +#if not defined(TYRANT_UNLEASHED) // If Impenetrable, prevent attack damage against walls, // but still activate Counter! if(att_dmg > 0 && fd->effect == Effect::impenetrable && def_status->m_card->m_wall) @@ -1592,6 +1616,7 @@ struct PerformAttack _DEBUG_MSG(1, "%s is impenetrable\n", status_description(def_status).c_str()); att_dmg = 0; } +#endif if(att_dmg > 0) { immobilize(); @@ -1605,13 +1630,15 @@ struct PerformAttack { if(att_status->m_hp > 0) { - if(def_status->m_card->m_stun && skill_check(fd, def_status, att_status)) +#if not defined(TYRANT_UNLEASHED) + if(def_status->m_card->m_stun && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); // perform_skill_stun _DEBUG_MSG(1, "%s stuns %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); att_status->m_stunned = 2; } +#endif if(def_status->m_card->m_counter > 0 && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); @@ -1624,15 +1651,37 @@ struct PerformAttack { count_achievement(fd, att_status); // perform_skill_berserk - att_status->m_berserk += att_status->m_card->m_berserk + att_status->enhanced(berserk); + unsigned v = att_status->m_card->m_berserk + att_status->enhanced(berserk); + att_status->m_berserk += v; + att_status->m_attack += v; } +#if defined(TYRANT_UNLEASHED) + if (def_status->m_card->m_corrosive > 0 && skill_check(fd, def_status, att_status)) + { + // perform_skill_corrosive + unsigned v = def_status->m_card->m_corrosive + def_status->enhanced(corrosive); + if (v > att_status->m_corroded) + { + count_achievement(fd, def_status); + _DEBUG_MSG(1, "%s corrodes %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); + att_status->m_corroded = v; + } + } +#endif } crush_leech(); } - +#if defined(TYRANT_UNLEASHED) + if (att_status->m_corroded > 0) + { + _DEBUG_MSG(1, "%s loses Attack %u.\n", status_description(att_status).c_str(), att_status->m_corroded); + att_status->m_attack = safe_minus(att_status->m_attack, att_status->m_corroded); + } +#else prepend_on_death(fd); resolve_skill(fd); check_regeneration(fd); +#endif } template @@ -1645,6 +1694,7 @@ struct PerformAttack att_dmg = pre_modifier_dmg; // enhance damage std::string desc; +#if not defined(TYRANT_UNLEASHED) if(att_card.m_valor > 0 && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); @@ -1663,6 +1713,7 @@ struct PerformAttack if(debug_print) { desc += "+" + to_string(att_card.m_burst) + "(burst)"; } att_dmg += att_card.m_burst; } +#endif if(def_status->m_enfeebled > 0) { if(debug_print) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } @@ -1725,12 +1776,15 @@ struct PerformAttack template void on_attacked() { - if(def_status->m_card->m_poison_oa > att_status->m_poisoned && skill_check(fd, def_status, att_status)) + if(def_status->m_card->m_poison_oa > 0 && skill_check(fd, def_status, att_status)) { - count_achievement(fd, def_status); unsigned v = def_status->m_card->m_poison_oa + def_status->enhanced(poison); - _DEBUG_MSG(1, "%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); - att_status->m_poisoned = v; + if (v > att_status->m_poisoned) + { + count_achievement(fd, def_status); + _DEBUG_MSG(1, "%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); + att_status->m_poisoned = v; + } } if(def_status->m_card->m_disease_oa && skill_check(fd, def_status, att_status)) { @@ -1742,7 +1796,9 @@ struct PerformAttack if(def_status->m_hp > 0 && def_status->m_card->m_berserk_oa > 0 && skill_check(fd, def_status, nullptr)) { count_achievement(fd, def_status); - def_status->m_berserk += def_status->m_card->m_berserk_oa + def_status->enhanced(berserk); + unsigned v = def_status->m_card->m_berserk_oa + def_status->enhanced(berserk); + def_status->m_berserk += v; + def_status->m_attack += v; } if(def_status->m_card->m_sunder_oa && skill_check(fd, def_status, att_status)) { @@ -1788,13 +1844,16 @@ void PerformAttack::damage_dependant_pre_oa() add_hp(fd, &fd->tap->commander, v); } #endif - if(att_status->m_card->m_poison > def_status->m_poisoned && skill_check(fd, att_status, def_status)) + if(att_status->m_card->m_poison > 0 && skill_check(fd, att_status, def_status)) { - count_achievement(fd, att_status); // perform_skill_poison unsigned v = att_status->m_card->m_poison + att_status->enhanced(poison); - _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); - def_status->m_poisoned = v; + if (v > def_status->m_poisoned) + { + count_achievement(fd, att_status); + _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); + def_status->m_poisoned = v; + } } #if not defined(TYRANT_UNLEASHED) if(att_status->m_card->m_disease && skill_check(fd, att_status, def_status)) @@ -1882,11 +1941,15 @@ void attack_commander(Field* fd, CardStatus* att_status) PerformAttack{fd, att_status, &fd->tip->commander}.op(); } } -void attack_phase(Field* fd) +// Return true if actually attacks +bool attack_phase(Field* fd) { CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); - if(attack_power(att_status) == 0) { return; } + if(attack_power(att_status) == 0) + { + return false; + } unsigned num_attacks(1); if(att_status->m_card->m_flurry > 0 && fd->flip() && skill_check(fd, att_status, nullptr)) { @@ -1920,7 +1983,7 @@ void attack_phase(Field* fd) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(); } - if(fd->end || !can_attack(att_status)) { return; } + if(fd->end || !can_attack(att_status)) { return true; } // attack the card in front (or attacks the commander if the card in front is just died) if(alive_assault(def_assaults, fd->current_ci)) { @@ -1930,7 +1993,7 @@ void attack_phase(Field* fd) { attack_commander(fd, att_status); } - if(fd->end || !can_attack(att_status)) { return; } + if(fd->end || !can_attack(att_status)) { return true; } // attack the card on the right if(alive_assault(def_assaults, fd->current_ci + 1)) { @@ -1944,6 +2007,7 @@ void attack_phase(Field* fd) attack_commander(fd, att_status); } } + return true; } //---------------------- $65 active skills implementation ---------------------- diff --git a/sim.h b/sim.h index 623ad9f0..388ddd82 100644 --- a/sim.h +++ b/sim.h @@ -129,10 +129,12 @@ struct CardStatus const Card* m_card; unsigned m_index; unsigned m_player; + unsigned m_attack; unsigned m_augmented; unsigned m_berserk; bool m_blitzing; bool m_chaosed; + unsigned m_corroded; unsigned m_delay; bool m_diseased; unsigned m_evaded; diff --git a/tyrant.cpp b/tyrant.cpp index 751bd58c..170f0e61 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -23,7 +23,7 @@ std::string skill_names[Skill::num_skills] = // Damage-Dependant: "Berserk", "Crush", "Disease", "Immobilize", "Inhibit", "Leech", "Phase", "Poison", "Siphon", "Sunder", // Defensive: - "Armored", "Counter", "Emulate", "Evade", "Flying", "Intercept", "Payback", "Refresh", "Regenerate", "Stun", "Tribute", "Wall", + "Armored", "Corrosive", "Counter", "Emulate", "Evade", "Flying", "Intercept", "Payback", "Refresh", "Regenerate", "Stun", "Tribute", "Wall", // Triggered: "Blitz", "Legion", // Tyrant Unleashed: diff --git a/tyrant.h b/tyrant.h index 7f355741..af548cbb 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.2.4" +#define TYRANT_OPTIMIZER_VERSION "1.2.5" #include #include @@ -36,7 +36,7 @@ enum Skill // Damage-Dependant: berserk, crush, disease, immobilize, inhibit, leech, phase, poison, siphon, sunder, // Defensive: - armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, tribute, wall, + armored, corrosive, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, tribute, wall, // Triggered: blitz, legion, // Tyrant Unleashed: diff --git a/xml.cpp b/xml.cpp index 7d515372..afac4388 100644 --- a/xml.cpp +++ b/xml.cpp @@ -197,10 +197,8 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) { card->m_type = CardType::structure; } else if(id < 4000) { card->m_type = CardType::action; } - else if(id < 6000) - { card->m_type = CardType::assault; } else - { card->m_type = cost_node ? (attack_node ? CardType::assault : CardType::structure) : (health_node ? CardType::commander : CardType::action); } + { card->m_type = CardType::assault; } if(hidden_node) { card->m_hidden = atoi(hidden_node->value()); } if(replace_node) { card->m_replace = atoi(replace_node->value()); } if(attack_node) { card->m_attack = atoi(attack_node->value()); } @@ -247,6 +245,8 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) { card->m_blitz = true; } else if(skill_id == burst) { card->m_burst = node_value(skill_node, "x"); } + else if(skill_id == corrosive) + { card->m_corrosive = node_value(skill_node, "x"); } else if(skill_id == counter) { card->m_counter = node_value(skill_node, "x"); } else if(skill_id == crush) From c32558d12d685065e2d45da8a4be709105e52eb8 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 16 May 2014 11:06:34 +0800 Subject: [PATCH 209/406] Fix bug: Protect should take effect on Poison. --- sim.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index d5d8caa1..7f4b8a2f 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1383,10 +1383,11 @@ void turn_end_phase(Field* fd) status.m_step = CardStep::none; #if defined(TYRANT_UNLEASHED) status.m_inhibited = 0; - if(status.m_poisoned > 0) + unsigned poison_dmg = safe_minus(status.m_poisoned, status.m_protected); + if(poison_dmg > 0) { - _DEBUG_MSG(1, "%s takes poison damage\n", status_description(&status).c_str()); - remove_hp(fd, status, status.m_poisoned); + _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg); + remove_hp(fd, status, poison_dmg); } #else // not need to fade out in own turn in TU From 67637d28759b1f914da0b5aed10f9dc0cc672e7d Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 17 May 2014 12:36:01 +0800 Subject: [PATCH 210/406] Merge recipe logic for WMT and TU --- card.h | 19 +++---- cards.cpp | 16 +++--- deck.h | 4 -- read.cpp | 1 + tyrant.cpp | 4 +- tyrant.h | 4 +- tyrant_optimize.cpp | 125 +++++++++----------------------------------- xml.cpp | 23 ++++---- xml.h | 2 - 9 files changed, 55 insertions(+), 143 deletions(-) diff --git a/card.h b/card.h index 3f617937..f742de9b 100644 --- a/card.h +++ b/card.h @@ -40,16 +40,12 @@ class Card unsigned m_leech; unsigned m_legion; unsigned m_level; -#if defined(TYRANT_UNLEASHED) - std::map m_material_list; -#endif std::string m_name; bool m_payback; unsigned m_pierce; unsigned m_phase; unsigned m_poison; unsigned m_poison_oa; - unsigned m_proto_id; // The id of the prototype card (before upgraded) for an upgraded card. 0 otherwise. unsigned m_rarity; bool m_refresh; unsigned m_regenerate; @@ -64,13 +60,13 @@ class Card bool m_swipe; bool m_tribute; bool m_unique; - unsigned m_upgrade_consumables; - unsigned m_upgrade_gold_cost; - unsigned m_upgraded_id; // The id of the upgraded card for an upgradable card. 0 otherwise. unsigned m_valor; bool m_wall; std::vector m_skills[SkillMod::num_skill_activation_modifiers]; CardType::CardType m_type; + unsigned m_recipe_cost; + std::map m_recipe_cards; + std::map m_used_for_cards; unsigned m_skill_pos[num_skills]; public: @@ -106,16 +102,12 @@ class Card m_leech(0), m_legion(0), m_level(1), -#if defined(TYRANT_UNLEASHED) - m_material_list(), -#endif m_name(""), m_payback(false), m_pierce(0), m_phase(0), m_poison(0), m_poison_oa(0), - m_proto_id(0), m_rarity(1), m_refresh(false), m_regenerate(0), @@ -132,7 +124,10 @@ class Card m_valor(0), m_wall(false), m_skills(), - m_type(CardType::assault) + m_type(CardType::assault), + m_recipe_cost(0), + m_recipe_cards(), + m_used_for_cards() { std::memset(m_skill_pos, 0, sizeof m_skill_pos); } diff --git a/cards.cpp b/cards.cpp index ff7db684..4d60a182 100644 --- a/cards.cpp +++ b/cards.cpp @@ -141,14 +141,18 @@ void Cards::organize() } #if not defined(TYRANT_UNLEASHED) - // update proto_id and upgraded_id + // update recipes if(card->m_set == 5002) { - std::string proto_name{simplify_name(card->m_name)}; - proto_name.erase(proto_name.size() - 1); // remove suffix "*" - Card * proto_card = player_cards_by_name[{proto_name, card->m_hidden}]; - card->m_proto_id = proto_card->m_id; - proto_card->m_upgraded_id = card->m_id; + std::string material_name{simplify_name(card->m_name)}; + material_name.erase(material_name.size() - 1); // remove suffix "*" + Card * material_card = player_cards_by_name[{material_name, card->m_hidden}]; + // Promo and Unpurchasable Reward cards only require 1 copy + unsigned number = material_card->m_set == 5001 || (material_card->m_set == 5000 && material_card->m_reserve) ? 1 : 2; + // Reward cards still have gold cost + card->m_recipe_cost = material_card->m_set == 5000 ? (card->m_rarity == 4 ? 100000 : 20000) : 0; + card->m_recipe_cards[material_card] = number; + material_card->m_used_for_cards[card] = number; } #endif } diff --git a/deck.h b/deck.h index ac56a59f..e2abab62 100644 --- a/deck.h +++ b/deck.h @@ -119,8 +119,4 @@ struct Decks std::map by_name; }; -#if defined(TYRANT_UNLEASHED) -void load_fusions(Cards& cards); -#endif - #endif diff --git a/read.cpp b/read.cpp index e6427a8e..882295e5 100644 --- a/read.cpp +++ b/read.cpp @@ -193,6 +193,7 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ simple_name = simplify_name(abbr_it->second); } auto card_it = cards.player_cards_by_name.find({simple_name, 0}); + if (card_it == cards.player_cards_by_name.end()) { card_it = cards.player_cards_by_name.find({simple_name, 1}); } auto card_id_iter = advance_until(simple_name.begin(), simple_name.end(), [](char c){return(c=='[');}); if(card_it != cards.player_cards_by_name.end()) { diff --git a/tyrant.cpp b/tyrant.cpp index 170f0e61..0f775ee2 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -47,10 +47,10 @@ std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Str std::string rarity_names[6]{"", "common", "uncommon", "rare", "legendary", "vindicator", }; -#if defined(TYRANT_UNLEASHED) +// begin for TYRANT_UNLEASHED unsigned upgrade_cost[]{0, 5, 15, 30, 75, 150}; unsigned salvaging_income[][7]{{}, {0, 1, 2, 5}, {0, 5, 10, 15, 20}, {0, 20, 25, 30, 40, 50, 65}, {0, 40, 45, 60, 75, 100, 125}, {0, 80, 85, 100, 125, 175, 250}}; -#endif +// end std::string decktype_names[DeckType::num_decktypes]{"Deck", "Mission", "Raid", "Quest", "Custom Deck", }; diff --git a/tyrant.h b/tyrant.h index af548cbb..95f31714 100644 --- a/tyrant.h +++ b/tyrant.h @@ -78,10 +78,10 @@ extern std::string cardtype_names[CardType::num_cardtypes]; extern std::string rarity_names[]; -#if defined(TYRANT_UNLEASHED) +// begin for TYRANT_UNLEASHED extern unsigned upgrade_cost[]; extern unsigned salvaging_income[][7]; -#endif +// end namespace DeckType { enum DeckType { diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index bc1f9d35..f9c551f1 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -85,29 +85,29 @@ Deck* find_deck(Decks& decks, const Cards& cards, std::string deck_name) } //---------------------- $80 deck optimization --------------------------------- -#if defined(TYRANT_UNLEASHED) -unsigned get_required_cards_before_upgrade(const std::vector & card_list, const Cards & cards, std::map & num_cards) +unsigned get_required_cards_before_upgrade(const std::vector & card_list, const Cards & cards, std::map & num_cards) { unsigned deck_cost = 0; std::set unresolved_cards; for(const Card * card: card_list) { - ++ num_cards[card->m_id]; + ++ num_cards[card]; unresolved_cards.insert(card); } while (!unresolved_cards.empty()) { - auto card = *unresolved_cards.begin(); - unresolved_cards.erase(unresolved_cards.begin()); - if(auto_upgrade_cards && owned_cards[card->m_id] < num_cards[card->m_id] && !card->m_material_list.empty()) + auto card_it = unresolved_cards.end(); + auto card = *(-- card_it); + unresolved_cards.erase(card_it); + if(auto_upgrade_cards && owned_cards[card->m_id] < num_cards[card] && !card->m_recipe_cards.empty()) { - unsigned num_under = num_cards[card->m_id] - owned_cards[card->m_id]; - num_cards[card->m_id] = owned_cards[card->m_id]; + unsigned num_under = num_cards[card] - owned_cards[card->m_id]; + num_cards[card] = owned_cards[card->m_id]; // std::cout << "-" << num_under << " " << card->m_name << "\n"; // XXX - deck_cost += num_under * card->m_upgrade_gold_cost; - for (auto material_it : card->m_material_list) + deck_cost += num_under * card->m_recipe_cost; + for (auto material_it : card->m_recipe_cards) { - num_cards[material_it.first->m_id] += num_under * material_it.second; + num_cards[material_it.first] += num_under * material_it.second; // std::cout << "+" << num_under * material_it.second << " " << material_it.first->m_name << "\n"; // XXX unresolved_cards.insert(material_it.first); } @@ -121,76 +121,21 @@ unsigned get_required_cards_before_upgrade(const std::vector & card // @is_reward: not claim a card if there is upgraded version (assuming the reward card has been upgraded). void claim_cards(const std::vector & card_list, const Cards & cards, bool claim_all, bool is_reward=false) { - std::map num_cards; + std::map num_cards; get_required_cards_before_upgrade(card_list, cards, num_cards); for(auto it: num_cards) { - unsigned card_id = it.first; - if(claim_all || buyable_cards.find(card_id) == buyable_cards.end()) + const Card * card = it.first; + if(claim_all || buyable_cards.find(card->m_id) == buyable_cards.end()) { - unsigned num_to_claim = safe_minus(it.second, owned_cards[card_id]); - if(num_to_claim > 0) + unsigned num_to_claim = safe_minus(it.second, owned_cards[card->m_id]); + if (is_reward) { - owned_cards[card_id] += num_to_claim; - if(debug_print) + for (const auto & it : card->m_used_for_cards) { - std::cout << "Claim " << cards.by_id(card_id)->m_name << " (" << num_to_claim << ")" << std::endl; + num_to_claim = safe_minus(num_to_claim, owned_cards[it.first->m_id] * it.second); } } - } - } -} - -unsigned get_deck_cost(const Deck * deck, const Cards & cards) -{ - if(!use_owned_cards) - { - return 0; - } - std::map num_in_deck; - unsigned deck_cost = get_required_cards_before_upgrade({deck->commander}, cards, num_in_deck); - deck_cost += get_required_cards_before_upgrade(deck->cards, cards, num_in_deck); - for(auto it: num_in_deck) - { - unsigned card_id = it.first; - unsigned num_to_buy = safe_minus(it.second, owned_cards[card_id]); -// std::cout << "BUY " << cards.by_id(card_id)->m_name << " +" << num_to_buy << " =" << it.second << ".\n"; // XXX - if(num_to_buy > 0) - { - auto buyable_iter = buyable_cards.find(card_id); - if(buyable_iter == buyable_cards.end()) { return UINT_MAX; } - deck_cost += num_to_buy * buyable_iter->second; - } - } -// std::cout << "\n"; // XXX - return deck_cost; -} - -#else - -// @claim_all: true = claim all cards; false = claim only non-buyable cards. -// @is_reward: not claim a card if there is upgraded version (assuming the reward card has been upgraded). -void claim_cards(const std::vector & card_list, const Cards & cards, bool claim_all, bool is_reward) -{ - std::map num_cards; - for(const Card * card: card_list) - { - if(card->m_proto_id > 0 && auto_upgrade_cards && owned_cards[card->m_id] <= num_cards[card]) - { - const Card * proto_card = cards.by_id(card->m_proto_id); - num_cards[proto_card] += proto_card->m_upgrade_consumables; - } - else - { - num_cards[card] += 1; - } - } - for(auto it: num_cards) - { - const Card * card = it.first; - if(claim_all || buyable_cards.find(card->m_id) == buyable_cards.end()) - { - unsigned num_to_claim = safe_minus(it.second, owned_cards[card->m_id] + (is_reward ? card->m_upgrade_consumables * owned_cards[card->m_upgraded_id] : 0)); if(num_to_claim > 0) { owned_cards[card->m_id] += num_to_claim; @@ -209,36 +154,14 @@ unsigned get_deck_cost(const Deck * deck, const Cards & cards) { return 0; } - std::map num_in_deck; - unsigned deck_cost = 0; - const Card * card = deck->commander; - if(card->m_proto_id > 0 && auto_upgrade_cards && owned_cards[card->m_id] == 0) - { - const Card * proto_card = cards.by_id(card->m_proto_id); - num_in_deck[proto_card->m_id] += proto_card->m_upgrade_consumables; - deck_cost += proto_card->m_upgrade_gold_cost; - } - else - { - num_in_deck[card->m_id] += 1; - } - for(const Card * card: deck->cards) - { - if(card->m_proto_id > 0 && auto_upgrade_cards && owned_cards[card->m_id] < num_in_deck[card->m_id]) - { - const Card * proto_card = cards.by_id(card->m_proto_id); - num_in_deck[proto_card->m_id] += proto_card->m_upgrade_consumables; - deck_cost += proto_card->m_upgrade_gold_cost; - } - else - { - num_in_deck[card->m_id] += 1; - } - } + std::map num_in_deck; + unsigned deck_cost = get_required_cards_before_upgrade({deck->commander}, cards, num_in_deck); + deck_cost += get_required_cards_before_upgrade(deck->cards, cards, num_in_deck); for(auto it: num_in_deck) { - unsigned card_id = it.first; + unsigned card_id = it.first->m_id; unsigned num_to_buy = safe_minus(it.second, owned_cards[card_id]); +// std::cout << "BUY " << cards.by_id(card_id)->m_name << " +" << num_to_buy << " =" << it.second << ".\n"; // XXX if(num_to_buy > 0) { auto buyable_iter = buyable_cards.find(card_id); @@ -246,9 +169,9 @@ unsigned get_deck_cost(const Deck * deck, const Cards & cards) deck_cost += num_to_buy * buyable_iter->second; } } +// std::cout << "\n"; // XXX return deck_cost; } -#endif //------------------------------------------------------------------------------ inline bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) diff --git a/xml.cpp b/xml.cpp index afac4388..39e58a70 100644 --- a/xml.cpp +++ b/xml.cpp @@ -211,10 +211,6 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) if(rarity_node) { card->m_rarity = atoi(rarity_node->value()); } if(type_node) { card->m_faction = map_to_faction(atoi(type_node->value())); } card->m_set = set; - // Promo and Unpurchasable Reward cards will only require 1 copy - card->m_upgrade_consumables = set == 5001 || (set == 5000 && card->m_reserve) ? 1 : 2; - // Reward cards will still have a gold cost - card->m_upgrade_gold_cost = set == 5000 ? (card->m_rarity == 4 ? 100000 : 20000) : 0; unsigned skill_pos = 1; for(xml_node<>* skill_node = card_node->first_node("skill"); skill_node; @@ -274,10 +270,8 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) { card->m_fusion = true; } else if(skill_id == immobilize) { card->m_immobilize = true; } -#if defined(TYRANT_UNLEASHED) else if(skill_id == inhibit) { card->m_inhibit = node_value(skill_node, "x"); } -#endif else if(skill_id == intercept) { card->m_intercept = true; } else if(skill_id == leech) @@ -336,18 +330,19 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) Card * pre_upgraded_card = top_card; top_card = new Card(*top_card); parse_card_node(cards, top_card, upgrade_node); - top_card->m_material_list.clear(); - top_card->m_material_list[pre_upgraded_card] = 1; if (top_card->m_type == CardType::commander) { // Commanders cost twice and cannot be salvaged. - top_card->m_upgrade_gold_cost = 2 * upgrade_cost[pre_upgraded_card->m_level]; + top_card->m_recipe_cost = 2 * upgrade_cost[pre_upgraded_card->m_level]; } else { // Salvaging income counts? - top_card->m_upgrade_gold_cost = upgrade_cost[pre_upgraded_card->m_level]; // + salvaging_income[top_card->m_rarity][pre_upgraded_card->m_level] - salvaging_income[top_card->m_rarity][top_card->m_level]; + top_card->m_recipe_cost = upgrade_cost[pre_upgraded_card->m_level]; // + salvaging_income[top_card->m_rarity][pre_upgraded_card->m_level] - salvaging_income[top_card->m_rarity][top_card->m_level]; } + top_card->m_recipe_cards.clear(); + top_card->m_recipe_cards[pre_upgraded_card] = 1; + pre_upgraded_card->m_used_for_cards[top_card] = 1; } card->m_final_id = top_card->m_id; #endif @@ -542,7 +537,6 @@ void read_quests(Decks& decks, const Cards& cards, std::string filename) } //------------------------------------------------------------------------------ -#if defined(TYRANT_UNLEASHED) void load_recipes_xml(Cards& cards) { std::vector buffer; @@ -562,7 +556,7 @@ void load_recipes_xml(Cards& cards) xml_node<>* card_id_node(recipe_node->first_node("card_id")); if (!card_id_node) { continue; } unsigned card_id(atoi(card_id_node->value())); - auto & card = cards.cards_by_id[card_id]; + Card * card = cards.cards_by_id[card_id]; for(xml_node<>* resource_node = recipe_node->first_node("resource"); resource_node; resource_node = resource_node->next_sibling("resource")) @@ -570,11 +564,12 @@ void load_recipes_xml(Cards& cards) unsigned card_id(node_value(resource_node, "card_id")); unsigned number(node_value(resource_node, "number")); if (card_id == 0 || number == 0) { continue; } - card->m_material_list[cards.by_id(card_id)] += number; + Card * material_card = cards.cards_by_id[card_id]; + card->m_recipe_cards[material_card] += number; + material_card->m_used_for_cards[card] += number; } } } -#endif //------------------------------------------------------------------------------ extern unsigned turn_limit; diff --git a/xml.h b/xml.h index 3e1a5e44..d955436d 100644 --- a/xml.h +++ b/xml.h @@ -10,9 +10,7 @@ class Achievement; Skill skill_name_to_id(const char* name); void load_decks_xml(Decks& decks, const Cards& cards); -#if defined(TYRANT_UNLEASHED) void load_recipes_xml(Cards& cards); -#endif void read_cards(Cards& cards); void read_missions(Decks& decks, const Cards& cards, std::string filename); void read_raids(Decks& decks, const Cards& cards, std::string filename); From 81883974314cfa09d0a19c268156b00b503eee49 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 18 May 2014 00:39:54 +0800 Subject: [PATCH 211/406] Improve hill climbing to better support fusion. --- deck.cpp | 110 ++++----- deck.h | 9 +- tyrant.h | 2 +- tyrant_optimize.cpp | 534 ++++++++++++++++++++------------------------ 4 files changed, 303 insertions(+), 352 deletions(-) diff --git a/deck.cpp b/deck.cpp index d8d56d46..09cfd287 100644 --- a/deck.cpp +++ b/deck.cpp @@ -30,57 +30,6 @@ void partial_shuffle(RandomAccessIterator first, RandomAccessIterator middle, } } -//------------------------------------------------------------------------------ -std::string deck_hash(const Card* commander, std::vector cards, bool is_ordered) -{ - std::string base64= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - std::stringstream ios; - unsigned card_id = commander->m_id; - if(card_id > 4000) - { - ios << '-'; - card_id -= 4000; - } - ios << base64[card_id / 64]; - ios << base64[card_id % 64]; - if(!is_ordered) - { - std::sort(cards.begin(), cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); - } - unsigned last_id = 0; - unsigned num_repeat = 0; - for(const Card* card: cards) - { - card_id = card->m_id; - if(card_id == last_id) - { - ++ num_repeat; - } - else - { - if(num_repeat > 1) - { - ios << base64[(num_repeat + 4000) / 64]; - ios << base64[(num_repeat + 4000) % 64]; - } - last_id = card_id; - num_repeat = 1; - if(card_id > 4000) - { - ios << '-'; - card_id -= 4000; - } - ios << base64[card_id / 64]; - ios << base64[card_id % 64]; - } - } - if(num_repeat > 1) - { - ios << base64[(num_repeat + 4000) / 64]; - ios << base64[(num_repeat + 4000) % 64]; - } - return ios.str(); -} //------------------------------------------------------------------------------ namespace { const char* base64_chars = @@ -246,7 +195,58 @@ void Deck::set_forts(const Cards& all_cards, const std::string& deck_string) } } -std::string Deck::short_description() const +std::string Deck::hash() +{ + std::string base64= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::stringstream ios; + unsigned card_id = commander->m_id; + if(card_id > 4000) + { + ios << '-'; + card_id -= 4000; + } + ios << base64[card_id / 64]; + ios << base64[card_id % 64]; + if (strategy == DeckStrategy::random) + { + std::sort(cards.begin(), cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); + } + unsigned last_id = 0; + unsigned num_repeat = 0; + for(const Card* card: cards) + { + card_id = card->m_id; + if(card_id == last_id) + { + ++ num_repeat; + } + else + { + if(num_repeat > 1) + { + ios << base64[(num_repeat + 4000) / 64]; + ios << base64[(num_repeat + 4000) % 64]; + } + last_id = card_id; + num_repeat = 1; + if(card_id > 4000) + { + ios << '-'; + card_id -= 4000; + } + ios << base64[card_id / 64]; + ios << base64[card_id % 64]; + } + } + if(num_repeat > 1) + { + ios << base64[(num_repeat + 4000) / 64]; + ios << base64[(num_repeat + 4000) % 64]; + } + return ios.str(); +} + +std::string Deck::short_description() { std::stringstream ios; ios << decktype_names[decktype]; @@ -254,7 +254,7 @@ std::string Deck::short_description() const if(!name.empty()) { ios << " \"" << name << "\""; } if(deck_string.empty()) { - if(raid_cards.empty()) { ios << ": " << deck_hash(commander, cards, strategy == DeckStrategy::ordered || strategy == DeckStrategy::exact_ordered); } + if(raid_cards.empty()) { ios << ": " << hash(); } } else { @@ -263,7 +263,7 @@ std::string Deck::short_description() const return ios.str(); } -std::string Deck::medium_description() const +std::string Deck::medium_description() { std::stringstream ios; ios << short_description() << std::endl; @@ -293,7 +293,7 @@ std::string Deck::medium_description() const extern std::string card_description(const Cards& cards, const Card* c); -std::string Deck::long_description(const Cards& all_cards) const +std::string Deck::long_description(const Cards& all_cards) { std::stringstream ios; ios << medium_description() << "\n"; diff --git a/deck.h b/deck.h index e2abab62..45cf2734 100644 --- a/deck.h +++ b/deck.h @@ -11,8 +11,6 @@ class Cards; -std::string deck_hash(const Card* commander, std::vector cards, bool is_ordered); - //---------------------- $30 Deck: a commander + a sequence of cards ----------- // Can be shuffled. // Implementations: random player and raid decks, ordered player decks. @@ -102,9 +100,10 @@ struct Deck } Deck* clone() const; - std::string short_description() const; - std::string medium_description() const; - std::string long_description(const Cards& all_cards) const; + std::string hash(); + std::string short_description(); + std::string medium_description(); + std::string long_description(const Cards& all_cards); const Card* get_commander(); const Card* next(); void shuffle(std::mt19937& re); diff --git a/tyrant.h b/tyrant.h index 95f31714..fe8d77d7 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.2.5" +#define TYRANT_OPTIMIZER_VERSION "1.2.6" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index f9c551f1..cbe6975f 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,38 @@ std::string card_id_name(const Card* card) } return ios.str(); } +std::string card_id_names(const std::vector> card_list) +{ + if (card_list.empty()) + { + return "-void-"; + } + std::stringstream ios; + for (const auto & card_it : card_list) + { + if (card_it != *card_list.cbegin()) + { ios << ", "; } + ios << "[" << card_it.second->m_id << "] " << card_it.second->m_name; + } + return ios.str(); +} +std::string card_slot_id_names(const std::vector> card_list) +{ + if (card_list.empty()) + { + return "-void-"; + } + std::stringstream ios; + for (const auto & card_it : card_list) + { + if (card_it != *card_list.cbegin()) + { ios << ", "; } + if (card_it.first >= 0) + { ios << card_it.first << " "; } + ios << "[" << card_it.second->m_id << "] " << card_it.second->m_name; + } + return ios.str(); +} //------------------------------------------------------------------------------ Deck* find_deck(Decks& decks, const Cards& cards, std::string deck_name) { @@ -84,12 +117,11 @@ Deck* find_deck(Decks& decks, const Cards& cards, std::string deck_name) return(deck); } //---------------------- $80 deck optimization --------------------------------- - -unsigned get_required_cards_before_upgrade(const std::vector & card_list, const Cards & cards, std::map & num_cards) +unsigned get_required_cards_before_upgrade(const std::vector & card_list, std::map & num_cards) { unsigned deck_cost = 0; std::set unresolved_cards; - for(const Card * card: card_list) + for (const Card * card : card_list) { ++ num_cards[card]; unresolved_cards.insert(card); @@ -117,12 +149,69 @@ unsigned get_required_cards_before_upgrade(const std::vector & card return deck_cost; } +unsigned get_deck_cost(const Deck * deck) +{ + if (!use_owned_cards) + { return 0; } + std::map num_in_deck; + unsigned deck_cost = get_required_cards_before_upgrade({deck->commander}, num_in_deck); + deck_cost += get_required_cards_before_upgrade(deck->cards, num_in_deck); + for(auto it: num_in_deck) + { + unsigned card_id = it.first->m_id; + unsigned num_to_buy = safe_minus(it.second, owned_cards[card_id]); +// std::cout << "BUY " << it.first->m_name << " +" << num_to_buy << " =" << it.second << ".\n"; // XXX + if(num_to_buy > 0) + { + auto buyable_iter = buyable_cards.find(card_id); + if(buyable_iter == buyable_cards.end()) + { return UINT_MAX; } + deck_cost += num_to_buy * buyable_iter->second; + } + } +// std::cout << "\n"; // XXX + return deck_cost; +} + +// insert card at to_slot into deck limited by fund; store deck_cost +// return true if affordable +bool adjust_deck(Deck * deck, const unsigned to_slot, const Card * card, unsigned fund, std::mt19937 & re, unsigned & deck_cost, std::vector> & costed_cards) +{ + std::vector cards = deck->cards; + deck->cards.clear(); + if (card) + { deck->cards.push_back(card); } + deck_cost = get_deck_cost(deck); + if (deck_cost > fund) + { return false; } + if (deck->strategy == DeckStrategy::random) + { std::shuffle(cards.begin(), cards.end(), re); } + for (unsigned i = 0; i < cards.size(); ++ i) + { + auto saved_cards = deck->cards; + if (i < to_slot && card) + { deck->cards.insert(deck->cards.end() - 1, cards[i]); } + else + { deck->cards.push_back(cards[i]); } + deck_cost = get_deck_cost(deck); + if (deck_cost > fund) + { + if (card == nullptr || cards[i] == card) + { return false; } + deck->cards = saved_cards; + costed_cards.push_back({i, cards[i]}); + } + } + deck_cost = get_deck_cost(deck); + return true; +} + // @claim_all: true = claim all cards; false = claim only non-buyable cards. // @is_reward: not claim a card if there is upgraded version (assuming the reward card has been upgraded). -void claim_cards(const std::vector & card_list, const Cards & cards, bool claim_all, bool is_reward=false) +void claim_cards(const std::vector & card_list, bool claim_all, bool is_reward=false) { std::map num_cards; - get_required_cards_before_upgrade(card_list, cards, num_cards); + get_required_cards_before_upgrade(card_list, num_cards); for(auto it: num_cards) { const Card * card = it.first; @@ -148,31 +237,6 @@ void claim_cards(const std::vector & card_list, const Cards & cards } } -unsigned get_deck_cost(const Deck * deck, const Cards & cards) -{ - if(!use_owned_cards) - { - return 0; - } - std::map num_in_deck; - unsigned deck_cost = get_required_cards_before_upgrade({deck->commander}, cards, num_in_deck); - deck_cost += get_required_cards_before_upgrade(deck->cards, cards, num_in_deck); - for(auto it: num_in_deck) - { - unsigned card_id = it.first->m_id; - unsigned num_to_buy = safe_minus(it.second, owned_cards[card_id]); -// std::cout << "BUY " << cards.by_id(card_id)->m_name << " +" << num_to_buy << " =" << it.second << ".\n"; // XXX - if(num_to_buy > 0) - { - auto buyable_iter = buyable_cards.find(card_id); - if(buyable_iter == buyable_cards.end()) { return UINT_MAX; } - deck_cost += num_to_buy * buyable_iter->second; - } - } -// std::cout << "\n"; // XXX - return deck_cost; -} - //------------------------------------------------------------------------------ inline bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) { @@ -405,7 +469,8 @@ void thread_evaluate(boost::barrier& main_barrier, { main_barrier.wait(); sim.set_decks(p.att_deck, p.def_decks); - if(destroy_threads) { return; } + if(destroy_threads) + { return; } while(true) { shared_mutex.lock(); //<<<< @@ -452,7 +517,8 @@ void thread_evaluate(boost::barrier& main_barrier, long double best_possible = (optimization_mode == OptimizationMode::raid ? 250 : 100); // Get a loose (better than no) upper bound. TODO: Improve it. compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum / best_possible, 0.01) * best_possible < thread_prev_score); - if(compare_stop) { + if(compare_stop) + { shared_mutex.lock(); //<<<< //std::cout << thread_total_local << "\n"; thread_compare_stop = true; //! @@ -548,7 +614,7 @@ void print_results(const std::pair> , unsigned>& r } } //------------------------------------------------------------------------------ -void print_deck_inline(const unsigned deck_cost, const Results score, const Card *commander, std::vector cards, bool is_ordered) +void print_deck_inline(const unsigned deck_cost, const Results score, Deck * deck) { if(fund > 0) { @@ -558,7 +624,8 @@ void print_deck_inline(const unsigned deck_cost, const Results scor { case OptimizationMode::raid: std::cout << "(" << (score.wins + score.draws) * 100 << "% win, " << score.wins * 100.0 << "% slay"; - if (show_stdev) { + if (show_stdev) + { std::cout << ", " << sqrt(score.sq_points - score.points * score.points) << " stdev"; } std::cout << ") "; @@ -572,14 +639,14 @@ void print_deck_inline(const unsigned deck_cost, const Results scor default: break; } - std::cout << score.points << ": " << commander->m_name; - if(!is_ordered) + std::cout << score.points << ": " << deck->commander->m_name; + if (deck->strategy == DeckStrategy::random) { - std::sort(cards.begin(), cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); + std::sort(deck->cards.begin(), deck->cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); } std::string last_name; unsigned num_repeat(0); - for(const Card* card: cards) + for(const Card* card: deck->cards) { if(card->m_name == last_name) { @@ -617,15 +684,15 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; - unsigned deck_cost = get_deck_cost(d1, proc.cards); + unsigned deck_cost = get_deck_cost(d1); fund = std::max(fund, deck_cost); - print_deck_inline(deck_cost, best_score, best_commander, best_cards, false); - std::mt19937 re(time(NULL)); + print_deck_inline(deck_cost, best_score, d1); + std::mt19937 re(std::chrono::system_clock::now().time_since_epoch().count()); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; + std::vector> costed_cards; for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score.points - target_score < -1e-9; slot_i = (slot_i + 1) % std::min(max_deck_len, best_cards.size() + 1)) { - if(card_marks.count(slot_i)) { continue; } if(deck_has_been_improved) { dead_slot = slot_i; @@ -637,91 +704,84 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapm_type == CardType::commander); - if(commander_candidate->m_name == best_commander->m_name) { continue; } - // Place it in the deck + if(commander_candidate->m_name == best_commander->m_name) + { continue; } + d1->cards = best_cards; + // Place it in the deck and restore other cards + costed_cards.clear(); + costed_cards.push_back({-1, best_commander}); d1->commander = commander_candidate; + if (! adjust_deck(d1, 0, nullptr, fund, re, deck_cost, costed_cards)) + { continue; } auto &&cur_deck = d1->card_ids>(); - if(evaluated_decks.count(cur_deck) == 0) + if (evaluated_decks.count(cur_deck) > 0) { - deck_cost = get_deck_cost(d1, proc.cards); - if(deck_cost > fund) { continue; } - // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score.points); - current_score = compute_score(compare_results, proc.factors); - evaluated_decks[cur_deck] = compare_results.second; - // Is it better ? - if(current_score.points > best_score.points) - { - // Then update best score/commander, print stuff - best_score = current_score; - best_commander = commander_candidate; - deck_has_been_improved = true; - std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards, false) << " commander -> " << card_id_name(commander_candidate) << ": "; - print_score_info(compare_results, proc.factors); - print_deck_inline(deck_cost, best_score, best_commander, best_cards, false); - } + skipped_simulations += evaluated_decks[cur_deck]; + continue; } - else + // Evaluate new deck + auto compare_results = proc.compare(num_iterations, best_score.points); + current_score = compute_score(compare_results, proc.factors); + evaluated_decks[cur_deck] = compare_results.second; + // Is it better ? + if(current_score.points > best_score.points) { - skipped_simulations += evaluated_decks[cur_deck]; + std::cout << "Deck improved: " << d1->hash() << ": " << card_id_names(costed_cards) << " -> " << card_id_name(commander_candidate) << ": "; + // Then update best score/commander, print stuff + best_score = current_score; + best_commander = d1->commander; + best_cards = d1->cards; + deck_has_been_improved = true; + print_score_info(compare_results, proc.factors); + print_deck_inline(deck_cost, best_score, d1); } } // Now that all commanders are evaluated, take the best one d1->commander = best_commander; + d1->cards = best_cards; } std::shuffle(non_commander_cards.begin(), non_commander_cards.end(), re); for(const Card* card_candidate: non_commander_cards) { d1->cards = best_cards; - if(card_candidate) + if (card_candidate ? + ((slot_i < best_cards.size() && card_candidate->m_name == best_cards[slot_i]->m_name) || // Omega -> Omega + !suitable_non_commander(*d1, slot_i, card_candidate)) + : + (best_cards.size() <= min_deck_len || + slot_i == best_cards.size())) // void -> void + { continue; } + costed_cards.clear(); + if (slot_i < d1->cards.size()) { - // Various checks to check if the card is accepted - assert(card_candidate->m_type != CardType::commander); - if(slot_i < best_cards.size() && card_candidate->m_name == best_cards[slot_i]->m_name) { continue; } - if(!suitable_non_commander(*d1, slot_i, card_candidate)) { continue; } - // Place it in the deck - if(slot_i == d1->cards.size()) - { - d1->cards.emplace_back(card_candidate); - } - else - { - d1->cards[slot_i] = card_candidate; - } - } - else - { - if(best_cards.size() <= min_deck_len || slot_i == best_cards.size()) { continue; } - // Remove it from the deck + costed_cards.push_back({slot_i, d1->cards[slot_i]}); d1->cards.erase(d1->cards.begin() + slot_i); } + if (! adjust_deck(d1, 0, card_candidate, fund, re, deck_cost, costed_cards)) + { continue; } auto &&cur_deck = d1->card_ids>(); - if(evaluated_decks.count(cur_deck) == 0) + if (evaluated_decks.count(cur_deck) > 0) { - deck_cost = get_deck_cost(d1, proc.cards); - if(deck_cost > fund) { continue; } - // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score.points); - current_score = compute_score(compare_results, proc.factors); - evaluated_decks[cur_deck] = compare_results.second; - // Is it better ? - if(current_score.points > best_score.points) - { - std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards, false) << " " << card_id_name(slot_i < best_cards.size() ? best_cards[slot_i] : NULL) << - " -> " << card_id_name(card_candidate) << ": "; - // Then update best score/slot, print stuff - best_score = current_score; - best_cards = d1->cards; - deck_has_been_improved = true; - print_score_info(compare_results, proc.factors); - print_deck_inline(deck_cost, best_score, best_commander, best_cards, false); - } + skipped_simulations += evaluated_decks[cur_deck]; + continue; } - else + // Evaluate new deck + auto compare_results = proc.compare(num_iterations, best_score.points); + current_score = compute_score(compare_results, proc.factors); + evaluated_decks[cur_deck] = compare_results.second; + // Is it better ? + if(current_score.points > best_score.points) { - skipped_simulations += evaluated_decks[cur_deck]; + std::cout << "Deck improved: " << d1->hash() << ": " << card_id_names(costed_cards) << " -> " << card_id_name(card_candidate) << ": "; + // Then update best score/slot, print stuff + best_score = current_score; + best_cards = d1->cards; + deck_has_been_improved = true; + print_score_info(compare_results, proc.factors); + print_deck_inline(deck_cost, best_score, d1); } - if(best_score.points - target_score > -1e-9) { break; } + if(best_score.points - target_score > -1e-9) + { break; } } d1->cards = best_cards; } @@ -730,7 +790,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) @@ -747,12 +807,13 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; - unsigned deck_cost = get_deck_cost(d1, proc.cards); + unsigned deck_cost = get_deck_cost(d1); fund = std::max(fund, deck_cost); - print_deck_inline(deck_cost, best_score, best_commander, best_cards, true); - std::mt19937 re(time(NULL)); + print_deck_inline(deck_cost, best_score, d1); + std::mt19937 re(std::chrono::system_clock::now().time_since_epoch().count()); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; + std::vector> costed_cards; for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score.points - target_score < -1e-9; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(deck_has_been_improved) @@ -764,40 +825,45 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std { for(const Card* commander_candidate: proc.cards.player_commanders) { - if(best_score.points - target_score > -1e-9) { break; } + if(best_score.points - target_score > -1e-9) + { break; } // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); - if(commander_candidate->m_name == best_commander->m_name) { continue; } + if(commander_candidate->m_name == best_commander->m_name) + { continue; } + d1->cards = best_cards; // Place it in the deck + costed_cards.clear(); + costed_cards.push_back({-1, best_commander}); d1->commander = commander_candidate; + if (! adjust_deck(d1, 0, nullptr, fund, re, deck_cost, costed_cards)) + { continue; } auto &&cur_deck = d1->card_ids>(); - if(evaluated_decks.count(cur_deck) == 0) + if (evaluated_decks.count(cur_deck) > 0) { - deck_cost = get_deck_cost(d1, proc.cards); - if(deck_cost > fund) { continue; } - // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score.points); - current_score = compute_score(compare_results, proc.factors); - evaluated_decks[cur_deck] = compare_results.second; - // Is it better ? - if(current_score.points > best_score.points) - { - // Then update best score/commander, print stuff - best_score = current_score; - best_commander = commander_candidate; - deck_has_been_improved = true; - std::cout << "Deck improved: " << deck_hash(commander_candidate, best_cards, true) << " commander -> " << card_id_name(commander_candidate) << ": "; - print_score_info(compare_results, proc.factors); - print_deck_inline(deck_cost, best_score, best_commander, best_cards, true); - } + skipped_simulations += evaluated_decks[cur_deck]; + continue; } - else + // Evaluate new deck + auto compare_results = proc.compare(num_iterations, best_score.points); + current_score = compute_score(compare_results, proc.factors); + evaluated_decks[cur_deck] = compare_results.second; + // Is it better ? + if(current_score.points > best_score.points) { - skipped_simulations += evaluated_decks[cur_deck]; + std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(costed_cards) << " -> " << card_id_name(commander_candidate) << ": "; + // Then update best score/commander, print stuff + best_score = current_score; + best_commander = commander_candidate; + best_cards = d1->cards; + deck_has_been_improved = true; + print_score_info(compare_results, proc.factors); + print_deck_inline(deck_cost, best_score, d1); } } // Now that all commanders are evaluated, take the best one d1->commander = best_commander; + d1->cards = best_cards; } std::shuffle(non_commander_cards.begin(), non_commander_cards.end(), re); for(const Card* card_candidate: non_commander_cards) @@ -806,72 +872,46 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std assert(!card_candidate || card_candidate->m_type != CardType::commander); for(unsigned to_slot(card_candidate ? 0 : best_cards.size() - 1); to_slot < best_cards.size() + (from_slot < best_cards.size() ? 0 : 1); ++to_slot) { - if(card_marks.count(from_slot) && card_candidate != best_cards[from_slot]) { break; } d1->cards = best_cards; - if(card_candidate) - { - // Various checks to check if the card is accepted - if(!suitable_non_commander(*d1, from_slot, card_candidate)) { continue; } - // Place it in the deck - if(from_slot < best_cards.size()) - { - if(from_slot == to_slot && card_candidate->m_name == best_cards[to_slot]->m_name) { continue; } - d1->cards.erase(d1->cards.begin() + from_slot); - } - d1->cards.insert(d1->cards.begin() + to_slot, card_candidate); - } - else + if (card_candidate ? + ((from_slot < best_cards.size() && (from_slot == to_slot && card_candidate->m_name == best_cards[to_slot]->m_name)) || // 2 Omega -> 2 Omega + !suitable_non_commander(*d1, from_slot, card_candidate)) + : + (best_cards.size() <= min_deck_len || + from_slot == best_cards.size())) // void -> void + { continue; } + costed_cards.clear(); + if (from_slot < d1->cards.size()) { - if(best_cards.size() <= min_deck_len || from_slot == best_cards.size()) { continue; } - // Remove it from the deck + costed_cards.push_back({from_slot, d1->cards[from_slot]}); d1->cards.erase(d1->cards.begin() + from_slot); } + if (! adjust_deck(d1, to_slot, card_candidate, fund, re, deck_cost, costed_cards)) + { continue; } auto &&cur_deck = d1->card_ids>(); - if(evaluated_decks.count(cur_deck) == 0) + if (evaluated_decks.count(cur_deck) > 0) { - deck_cost = get_deck_cost(d1, proc.cards); - if(deck_cost > fund) { continue; } - // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score.points); - current_score = compute_score(compare_results, proc.factors); - evaluated_decks[cur_deck] = compare_results.second; - // Is it better ? - if(current_score.points > best_score.points) - { - // Then update best score/slot, print stuff - std::cout << "Deck improved: " << deck_hash(best_commander, d1->cards, true) << " " << from_slot << " " << card_id_name(from_slot < best_cards.size() ? best_cards[from_slot] : NULL) << - " -> " << to_slot << " " << card_id_name(card_candidate) << ": "; - best_score = current_score; - best_cards = d1->cards; - deck_has_been_improved = true; - print_score_info(compare_results, proc.factors); - print_deck_inline(deck_cost, best_score, best_commander, best_cards, true); - std::map new_card_marks; - for(auto it: card_marks) - { - signed pos = it.first; - char mark = it.second; - if(pos < 0) {} - else if(static_cast(pos) == from_slot) - { - pos = to_slot; - } - else - { - if(static_cast(pos) > from_slot) { -- pos; } - if(static_cast(pos) >= to_slot) { ++ pos; } - } - new_card_marks[pos] = mark; - } - card_marks = new_card_marks; - } + skipped_simulations += evaluated_decks[cur_deck]; + continue; } - else + // Evaluate new deck + auto compare_results = proc.compare(num_iterations, best_score.points); + current_score = compute_score(compare_results, proc.factors); + evaluated_decks[cur_deck] = compare_results.second; + // Is it better ? + if(current_score.points > best_score.points) { - skipped_simulations += evaluated_decks[cur_deck]; + // Then update best score/slot, print stuff + std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(costed_cards) << " -> " << to_slot << " " << card_id_name(card_candidate) << ": "; + best_score = current_score; + best_cards = d1->cards; + deck_has_been_improved = true; + print_score_info(compare_results, proc.factors); + print_deck_inline(deck_cost, best_score, d1); } } - if(best_score.points - target_score > -1e-9) { break; } + if(best_score.points - target_score > -1e-9) + { break; } } d1->cards = best_cards; } @@ -880,7 +920,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std { simulations += evaluation.second; } std::cout << "Evaluated " << evaluated_decks.size() << " decks (" << simulations << " + " << skipped_simulations << " simulations)." << std::endl; std::cout << "Optimized Deck: "; - print_deck_inline(get_deck_cost(d1, proc.cards), best_score, best_commander, best_cards, true); + print_deck_inline(get_deck_cost(d1), best_score, d1); } //------------------------------------------------------------------------------ // Implements iteration over all combination of k elements from n elements. @@ -945,98 +985,6 @@ class Combination unsigned nextIndex; }; //------------------------------------------------------------------------------ -static unsigned total_num_combinations_test(0); -inline void try_all_ratio_combinations(unsigned deck_size, unsigned var_k, unsigned num_iterations, const std::vector& card_indices, std::vector& cards, const Card* commander, Process& proc, Results& best_score, boost::optional& best_deck) -{ - assert(card_indices.size() > 0); - assert(card_indices.size() <= deck_size); - unsigned num_cards_to_combine(deck_size); - std::vector unique_cards; - std::vector cards_to_combine; - bool legendary_found(false); - for(unsigned card_index: card_indices) - { - const Card* card(cards[card_index]); - if(card->m_unique || card->m_rarity == 4) - { - if(card->m_rarity == 4) - { - if(legendary_found) { return; } - legendary_found = true; - } - --num_cards_to_combine; - unique_cards.push_back(card); - } - else - { - cards_to_combine.push_back(card); - } - } - // all unique or legendaries, quit - if(cards_to_combine.size() == 0) { return; } - if(cards_to_combine.size() == 1) - { - std::vector deck_cards = unique_cards; - std::vector combined_cards(num_cards_to_combine, cards_to_combine[0]); - deck_cards.insert(deck_cards.end(), combined_cards.begin(), combined_cards.end()); - Deck deck{}; - deck.set(commander, deck_cards); - (*dynamic_cast(proc.att_deck)) = deck; - auto new_results = proc.compare(num_iterations, best_score.points); - auto new_score = compute_score(new_results, proc.factors); - if(new_score.points > best_score.points) - { - best_score = new_score; - best_deck = deck; - print_score_info(new_results, proc.factors); - print_deck_inline(0, best_score, commander, deck_cards, false); - } - //++num; - // num_cards = num_cards_to_combine ... - } - else - { - var_k = cards_to_combine.size() - 1; - Combination cardAmounts(num_cards_to_combine-1, var_k); - bool finished(false); - while(!finished) - { - const std::vector& indices = cardAmounts.getIndices(); - std::vector num_cards(var_k+1, 0); - num_cards[0] = indices[0] + 1; - for(unsigned i(1); i < var_k; ++i) - { - num_cards[i] = indices[i] - indices[i-1]; - } - num_cards[var_k] = num_cards_to_combine - (indices[var_k-1] + 1); - std::vector deck_cards = unique_cards; - //std::cout << "num cards: "; - for(unsigned num_index(0); num_index < num_cards.size(); ++num_index) - { - //std::cout << num_cards[num_index] << " "; - for(unsigned i(0); i < num_cards[num_index]; ++i) { deck_cards.push_back(cards[card_indices[num_index]]); } - } - //std::cout << "\n" << std::flush; - //std::cout << std::flush; - assert(deck_cards.size() == deck_size); - Deck deck{}; - deck.set(commander, deck_cards); - *proc.att_deck = deck; - auto new_results = proc.compare(num_iterations, best_score.points); - auto new_score = compute_score(new_results, proc.factors); - if(new_score.points > best_score.points) - { - best_score = new_score; - best_deck = deck; - print_score_info(new_results, proc.factors); - print_deck_inline(0, best_score, commander, deck_cards, false); - } - ++total_num_combinations_test; - finished = cardAmounts.next(); - } - } -} -//------------------------------------------------------------------------------ enum Operation { simulate, climb, @@ -1045,7 +993,7 @@ enum Operation { debuguntil }; //------------------------------------------------------------------------------ -void print_available_decks(const Decks& decks, bool allow_card_pool) +void print_available_decks(Decks & decks, bool allow_card_pool) { std::cout << "Available decks: (use double-quoted name)" << std::endl; std::cout << "(All missions, omitted because the list is too long.)" << std::endl; @@ -1319,7 +1267,7 @@ int main(int argc, char** argv) { auto prev_deck = decks.by_type_id[{DeckType::mission, prev_mission_id}]; prev_mission_id = prev_deck->mission_req; - claim_cards(prev_deck->reward_cards, cards, true, true); + claim_cards(prev_deck->reward_cards, true, true); } } } @@ -1445,8 +1393,8 @@ int main(int argc, char** argv) // Force to claim non-buyable cards in your initial deck. if(use_owned_cards) { - claim_cards({att_deck->commander}, cards, fund == 0, false); - claim_cards(att_deck->cards, cards, fund == 0, false); + claim_cards({att_deck->commander}, fund == 0, false); + claim_cards(att_deck->cards, fund == 0, false); } att_deck->strategy = att_strategy; @@ -1488,13 +1436,16 @@ int main(int argc, char** argv) break; } case climb: { - if(att_strategy == DeckStrategy::random) + switch (att_strategy) { + case DeckStrategy::random: hill_climbing(std::get<0>(op), att_deck, p, att_deck->card_marks); - } - else - { + break; +// case DeckStrategy::ordered: +// case DeckStrategy::exact_ordered: + default: hill_climbing_ordered(std::get<0>(op), att_deck, p, att_deck->card_marks); + break; } break; } @@ -1504,8 +1455,8 @@ int main(int argc, char** argv) use_owned_cards = true; auto_upgrade_cards = false; owned_cards.clear(); - claim_cards({att_deck->commander}, cards, true, false); - claim_cards(att_deck->cards, cards, true, false); + claim_cards({att_deck->commander}, true, false); + claim_cards(att_deck->cards, true, false); hill_climbing_ordered(std::get<0>(op), att_deck, p, att_deck->card_marks); break; } @@ -1530,7 +1481,8 @@ int main(int argc, char** argv) debug_str.clear(); auto results = p.evaluate(1); auto score = compute_score(results, p.factors); - if(score.points >= std::get<0>(op) && score.points <= std::get<1>(op)) { + if(score.points >= std::get<0>(op) && score.points <= std::get<1>(op)) + { std::cout << debug_str << std::flush; print_results(results, p.factors); break; From 3271993c1ef3208076a5244988aa646d510a4565 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 18 May 2014 16:36:54 +0800 Subject: [PATCH 212/406] Improve hill climbing to support de-fusion. --- tyrant_optimize.cpp | 125 +++++++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 47 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index cbe6975f..0832a375 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -69,21 +70,6 @@ std::string card_id_name(const Card* card) } return ios.str(); } -std::string card_id_names(const std::vector> card_list) -{ - if (card_list.empty()) - { - return "-void-"; - } - std::stringstream ios; - for (const auto & card_it : card_list) - { - if (card_it != *card_list.cbegin()) - { ios << ", "; } - ios << "[" << card_it.second->m_id << "] " << card_it.second->m_name; - } - return ios.str(); -} std::string card_slot_id_names(const std::vector> card_list) { if (card_list.empty()) @@ -91,10 +77,11 @@ std::string card_slot_id_names(const std::vector return "-void-"; } std::stringstream ios; + std::string separator = ""; for (const auto & card_it : card_list) { - if (card_it != *card_list.cbegin()) - { ios << ", "; } + ios << separator; + separator = ", "; if (card_it.first >= 0) { ios << card_it.first << " "; } ios << "[" << card_it.second->m_id << "] " << card_it.second->m_name; @@ -173,33 +160,76 @@ unsigned get_deck_cost(const Deck * deck) return deck_cost; } +// remove val from oppo if found, otherwise append val to self +template +void append_unless_remove(C & self, C & oppo, typename C::const_reference val) +{ + for (auto it = oppo.begin(); it != oppo.end(); ++ it) + { + if (*it == val) + { + oppo.erase(it); + return; + } + } + self.push_back(val); +} + // insert card at to_slot into deck limited by fund; store deck_cost // return true if affordable -bool adjust_deck(Deck * deck, const unsigned to_slot, const Card * card, unsigned fund, std::mt19937 & re, unsigned & deck_cost, std::vector> & costed_cards) +bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, const Card * card, unsigned fund, std::mt19937 & re, unsigned & deck_cost, + std::vector> & cards_out, std::vector> & cards_in) { + if (card == nullptr) + { + deck_cost = get_deck_cost(deck); + return (deck_cost <= fund); + } + bool is_random = deck->strategy == DeckStrategy::random; std::vector cards = deck->cards; deck->cards.clear(); + cards_in.clear(); if (card) - { deck->cards.push_back(card); } + { + deck->cards.emplace_back(card); + cards_in.emplace_back(is_random ? -1 : to_slot, card); + } + else if (to_slot < 0) + { // commander + cards_in.emplace_back(-1, deck->commander); + } deck_cost = get_deck_cost(deck); if (deck_cost > fund) { return false; } - if (deck->strategy == DeckStrategy::random) + if (is_random) { std::shuffle(cards.begin(), cards.end(), re); } - for (unsigned i = 0; i < cards.size(); ++ i) + for (signed i = 0; i < (signed)cards.size(); ++ i) { auto saved_cards = deck->cards; - if (i < to_slot && card) - { deck->cards.insert(deck->cards.end() - 1, cards[i]); } - else - { deck->cards.push_back(cards[i]); } - deck_cost = get_deck_cost(deck); + auto in_it = deck->cards.end() - (i < to_slot); + in_it = deck->cards.insert(in_it, nullptr); + std::stack candidate_cards; + candidate_cards.emplace(cards[i]); + while (! candidate_cards.empty()) + { + const Card* card_in = candidate_cards.top(); + candidate_cards.pop(); + *in_it = card_in; + deck_cost = get_deck_cost(deck); + if (deck_cost <= fund) + { break; } + for (auto recipe_it : card_in->m_recipe_cards) + { candidate_cards.emplace(recipe_it.first); } + } if (deck_cost > fund) { - if (card == nullptr || cards[i] == card) - { return false; } + append_unless_remove(cards_out, cards_in, {is_random ? -1 : i + (i >= to_slot), cards[i]}); deck->cards = saved_cards; - costed_cards.push_back({i, cards[i]}); + } + else if (*in_it != cards[i]) + { + append_unless_remove(cards_out, cards_in, {is_random ? -1 : i + (i >= from_slot), cards[i]}); + append_unless_remove(cards_in, cards_out, {is_random ? -1 : i + (i >= to_slot), *in_it}); } } deck_cost = get_deck_cost(deck); @@ -690,7 +720,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map> costed_cards; + std::vector> cards_out, cards_in; for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score.points - target_score < -1e-9; slot_i = (slot_i + 1) % std::min(max_deck_len, best_cards.size() + 1)) { if(deck_has_been_improved) @@ -708,10 +738,11 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcards = best_cards; // Place it in the deck and restore other cards - costed_cards.clear(); - costed_cards.push_back({-1, best_commander}); + cards_out.clear(); + cards_out.emplace_back(-1, best_commander); + cards_out = {{-1, best_commander}}; d1->commander = commander_candidate; - if (! adjust_deck(d1, 0, nullptr, fund, re, deck_cost, costed_cards)) + if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in)) { continue; } auto &&cur_deck = d1->card_ids>(); if (evaluated_decks.count(cur_deck) > 0) @@ -726,7 +757,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map best_score.points) { - std::cout << "Deck improved: " << d1->hash() << ": " << card_id_names(costed_cards) << " -> " << card_id_name(commander_candidate) << ": "; + std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; // Then update best score/commander, print stuff best_score = current_score; best_commander = d1->commander; @@ -751,13 +782,13 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map void { continue; } - costed_cards.clear(); + cards_out.clear(); if (slot_i < d1->cards.size()) { - costed_cards.push_back({slot_i, d1->cards[slot_i]}); + cards_out.emplace_back(-1, d1->cards[slot_i]); d1->cards.erase(d1->cards.begin() + slot_i); } - if (! adjust_deck(d1, 0, card_candidate, fund, re, deck_cost, costed_cards)) + if (! adjust_deck(d1, slot_i, slot_i, card_candidate, fund, re, deck_cost, cards_out, cards_in)) { continue; } auto &&cur_deck = d1->card_ids>(); if (evaluated_decks.count(cur_deck) > 0) @@ -772,7 +803,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map best_score.points) { - std::cout << "Deck improved: " << d1->hash() << ": " << card_id_names(costed_cards) << " -> " << card_id_name(card_candidate) << ": "; + std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; // Then update best score/slot, print stuff best_score = current_score; best_cards = d1->cards; @@ -813,7 +844,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std std::mt19937 re(std::chrono::system_clock::now().time_since_epoch().count()); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; - std::vector> costed_cards; + std::vector> cards_out, cards_in; for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score.points - target_score < -1e-9; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(deck_has_been_improved) @@ -833,10 +864,10 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std { continue; } d1->cards = best_cards; // Place it in the deck - costed_cards.clear(); - costed_cards.push_back({-1, best_commander}); + cards_out.clear(); + cards_out.emplace_back(-1, best_commander); d1->commander = commander_candidate; - if (! adjust_deck(d1, 0, nullptr, fund, re, deck_cost, costed_cards)) + if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in)) { continue; } auto &&cur_deck = d1->card_ids>(); if (evaluated_decks.count(cur_deck) > 0) @@ -851,7 +882,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std // Is it better ? if(current_score.points > best_score.points) { - std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(costed_cards) << " -> " << card_id_name(commander_candidate) << ": "; + std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; // Then update best score/commander, print stuff best_score = current_score; best_commander = commander_candidate; @@ -880,13 +911,13 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std (best_cards.size() <= min_deck_len || from_slot == best_cards.size())) // void -> void { continue; } - costed_cards.clear(); + cards_out.clear(); if (from_slot < d1->cards.size()) { - costed_cards.push_back({from_slot, d1->cards[from_slot]}); + cards_out.emplace_back(from_slot, d1->cards[from_slot]); d1->cards.erase(d1->cards.begin() + from_slot); } - if (! adjust_deck(d1, to_slot, card_candidate, fund, re, deck_cost, costed_cards)) + if (! adjust_deck(d1, from_slot, to_slot, card_candidate, fund, re, deck_cost, cards_out, cards_in)) { continue; } auto &&cur_deck = d1->card_ids>(); if (evaluated_decks.count(cur_deck) > 0) @@ -902,7 +933,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std if(current_score.points > best_score.points) { // Then update best score/slot, print stuff - std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(costed_cards) << " -> " << to_slot << " " << card_id_name(card_candidate) << ": "; + std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; best_score = current_score; best_cards = d1->cards; deck_has_been_improved = true; From ff31a9d986603c31297993f96a2745e639b46073 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 18 May 2014 18:08:04 +0800 Subject: [PATCH 213/406] Fix rarity names. --- tyrant.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tyrant.cpp b/tyrant.cpp index 0f775ee2..b47891cc 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -45,7 +45,11 @@ std::string skill_activation_modifier_names[SkillMod::num_skill_activation_modif std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Structure", "Action", }; -std::string rarity_names[6]{"", "common", "uncommon", "rare", "legendary", "vindicator", }; +#if defined(TYRANT_UNLEASHED) +std::string rarity_names[6]{"", "common", "rare", "epic", "legendary", "vindicator", }; +#else +std::string rarity_names[5]{"", "common", "uncommon", "rare", "legendary", }; +#endif // begin for TYRANT_UNLEASHED unsigned upgrade_cost[]{0, 5, 15, 30, 75, 150}; From 49621ed578101b1ff28fba3faea40f0c70e681a9 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 19 May 2014 01:07:38 +0800 Subject: [PATCH 214/406] Fix display. --- tyrant_optimize.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 0832a375..451803e7 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -180,24 +180,21 @@ void append_unless_remove(C & self, C & oppo, typename C::const_reference val) bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, const Card * card, unsigned fund, std::mt19937 & re, unsigned & deck_cost, std::vector> & cards_out, std::vector> & cards_in) { + cards_in.clear(); if (card == nullptr) { + if (to_slot < 0) + { // commander + cards_in.emplace_back(-1, deck->commander); + } deck_cost = get_deck_cost(deck); return (deck_cost <= fund); } bool is_random = deck->strategy == DeckStrategy::random; std::vector cards = deck->cards; deck->cards.clear(); - cards_in.clear(); - if (card) - { - deck->cards.emplace_back(card); - cards_in.emplace_back(is_random ? -1 : to_slot, card); - } - else if (to_slot < 0) - { // commander - cards_in.emplace_back(-1, deck->commander); - } + deck->cards.emplace_back(card); + cards_in.emplace_back(is_random ? -1 : to_slot, card); deck_cost = get_deck_cost(deck); if (deck_cost > fund) { return false; } From 23133d39012eb7cfc857408f183470834ff0c1bf Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 19 May 2014 02:10:07 +0800 Subject: [PATCH 215/406] Add back level-1 missions. --- xml.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/xml.cpp b/xml.cpp index 39e58a70..36538974 100644 --- a/xml.cpp +++ b/xml.cpp @@ -380,7 +380,7 @@ void read_cards(Cards& cards) #endif } //------------------------------------------------------------------------------ -Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::DeckType decktype, unsigned id, std::string& deck_name, unsigned level=1) +Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::DeckType decktype, unsigned id, std::string deck_name, unsigned level=1) { xml_node<>* commander_node(node->first_node("commander")); unsigned card_id = atoi(commander_node->value()); @@ -465,7 +465,13 @@ void read_missions(Decks& decks, const Cards& cards, std::string filename) unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(mission_node->first_node("name")); std::string deck_name{name_node->value()}; - Deck* deck = read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name, 10); + Deck* deck; +#if defined(TYRANT_UNLEASHED) + { + deck = read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name + "-1", 1); + } +#endif + deck = read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name, 10); xml_node<>* effect_id_node(mission_node->first_node("effect")); if(effect_id_node) { From 280b673016e2b2eb5a712b3b5c488ca430a46015 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 23 May 2014 12:02:44 +0800 Subject: [PATCH 216/406] Fix reorder. --- tyrant_optimize.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 451803e7..d5e27040 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -776,8 +776,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapm_name == best_cards[slot_i]->m_name) || // Omega -> Omega !suitable_non_commander(*d1, slot_i, card_candidate)) : - (best_cards.size() <= min_deck_len || - slot_i == best_cards.size())) // void -> void + (slot_i == best_cards.size())) // void -> void { continue; } cards_out.clear(); if (slot_i < d1->cards.size()) @@ -785,7 +784,8 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcards[slot_i]); d1->cards.erase(d1->cards.begin() + slot_i); } - if (! adjust_deck(d1, slot_i, slot_i, card_candidate, fund, re, deck_cost, cards_out, cards_in)) + if (! adjust_deck(d1, slot_i, slot_i, card_candidate, fund, re, deck_cost, cards_out, cards_in) || + d1->cards.size() < min_deck_len) { continue; } auto &&cur_deck = d1->card_ids>(); if (evaluated_decks.count(cur_deck) > 0) @@ -905,8 +905,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std ((from_slot < best_cards.size() && (from_slot == to_slot && card_candidate->m_name == best_cards[to_slot]->m_name)) || // 2 Omega -> 2 Omega !suitable_non_commander(*d1, from_slot, card_candidate)) : - (best_cards.size() <= min_deck_len || - from_slot == best_cards.size())) // void -> void + (from_slot == best_cards.size())) // void -> void { continue; } cards_out.clear(); if (from_slot < d1->cards.size()) @@ -914,7 +913,8 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std cards_out.emplace_back(from_slot, d1->cards[from_slot]); d1->cards.erase(d1->cards.begin() + from_slot); } - if (! adjust_deck(d1, from_slot, to_slot, card_candidate, fund, re, deck_cost, cards_out, cards_in)) + if (! adjust_deck(d1, from_slot, to_slot, card_candidate, fund, re, deck_cost, cards_out, cards_in) || + d1->cards.size() < min_deck_len) { continue; } auto &&cur_deck = d1->card_ids>(); if (evaluated_decks.count(cur_deck) > 0) @@ -1397,6 +1397,7 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "reorder") == 0) { + att_strategy = DeckStrategy::ordered; todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, reorder)); argIndex += 1; } @@ -1478,7 +1479,6 @@ int main(int argc, char** argv) break; } case reorder: { - att_deck->strategy = DeckStrategy::ordered; min_deck_len = max_deck_len = att_deck->cards.size(); use_owned_cards = true; auto_upgrade_cards = false; From d53e0bda5837bfb02e2f10e654fbb4efa8b42585 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 24 May 2014 18:32:12 +0800 Subject: [PATCH 217/406] Improve debug info for Corrosive. --- sim.cpp | 39 +++++++++++++++++++-------------------- sim.h | 4 ++-- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/sim.cpp b/sim.cpp index 7f4b8a2f..8d2ff81a 100644 --- a/sim.cpp +++ b/sim.cpp @@ -78,12 +78,12 @@ CardStatus::CardStatus(const Card* card) : m_card(card), m_index(0), m_player(0), - m_attack(card->m_attack), m_augmented(0), m_berserk(0), m_blitzing(false), m_chaosed(false), - m_corroded(0), + m_corroded_rate(0), + m_corroded_weakened(0), m_delay(card->m_delay), m_diseased(false), m_evaded(0), @@ -126,12 +126,12 @@ inline void CardStatus::set(const Card& card) m_card = &card; m_index = 0; m_player = 0; - m_attack = card.m_attack; m_augmented = 0; m_berserk = 0; m_blitzing = false; m_chaosed = false; - m_corroded = 0; + m_corroded_rate = 0; + m_corroded_weakened = 0; m_delay = card.m_delay; m_diseased = false; m_evaded = 0; @@ -159,7 +159,7 @@ inline void CardStatus::set(const Card& card) //------------------------------------------------------------------------------ inline int attack_power(CardStatus* att) { - return(safe_minus(att->m_attack + att->m_rallied, att->m_weakened)); + return(safe_minus(att->m_card->m_attack + att->m_berserk + att->m_rallied, att->m_weakened + att->m_corroded_weakened)); } //------------------------------------------------------------------------------ std::string skill_description(const Cards& cards, const SkillSpec& s) @@ -286,11 +286,13 @@ std::string CardStatus::description() case CardType::action: break; case CardType::assault: - desc += " att:" + to_string(m_attack); + desc += " att:" + to_string(m_card->m_attack); { std::string att_desc; + if(m_berserk > 0) { att_desc += "+" + to_string(m_berserk) + "(berserk)"; } if(m_rallied > 0) { att_desc += "+" + to_string(m_rallied) + "(rallied)"; } if(m_weakened > 0) { att_desc += "-" + to_string(m_weakened) + "(weakened)"; } + if(m_corroded_weakened > 0) { att_desc += "-" + to_string(m_corroded_weakened) + "(corroded)"; } if(!att_desc.empty()) { desc += att_desc + "=" + to_string(attack_power(this)); } } case CardType::structure: @@ -315,8 +317,7 @@ std::string CardStatus::description() if(m_sundered) { desc += ", sundered"; } if(m_temporary_split) { desc += ", cloning"; } if(m_augmented > 0) { desc += ", augmented " + to_string(m_augmented); } - if(m_berserk > 0) { desc += ", berserk " + to_string(m_berserk); } - if(m_corroded > 0) { desc += ", corroded " + to_string(m_corroded); } + if(m_corroded_rate > 0) { desc += ", corroded " + to_string(m_corroded_rate); } if(m_enfeebled > 0) { desc += ", enfeebled " + to_string(m_enfeebled); } if(m_inhibited > 0) { desc += ", inhibited " + to_string(m_inhibited); } if(m_poisoned > 0) { desc += ", poisoned " + to_string(m_poisoned); } @@ -939,11 +940,11 @@ Results play(Field* fd) { #if defined(TYRANT_UNLEASHED) CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); - if (att_status->m_corroded > 0) + if (att_status->m_corroded_rate > 0) { - att_status->m_corroded = 0; - att_status->m_attack = att_status->m_card->m_attack + att_status->m_berserk; - _DEBUG_MSG(1, "%s lost corroded.\n", status_description(att_status).c_str()); + _DEBUG_MSG(1, "%s loses Status corroded.\n", status_description(att_status).c_str()); + att_status->m_corroded_rate = 0; + att_status->m_corroded_weakened = 0; } #endif } @@ -1239,7 +1240,6 @@ inline void add_hp(Field* fd, CardStatus* target, unsigned v) { unsigned healed = target->m_hp - old_hp; target->m_berserk += healed; - target->m_attack += healed; } } void check_regeneration(Field* fd) @@ -1654,18 +1654,17 @@ struct PerformAttack // perform_skill_berserk unsigned v = att_status->m_card->m_berserk + att_status->enhanced(berserk); att_status->m_berserk += v; - att_status->m_attack += v; } #if defined(TYRANT_UNLEASHED) if (def_status->m_card->m_corrosive > 0 && skill_check(fd, def_status, att_status)) { // perform_skill_corrosive unsigned v = def_status->m_card->m_corrosive + def_status->enhanced(corrosive); - if (v > att_status->m_corroded) + if (v > att_status->m_corroded_rate) { count_achievement(fd, def_status); _DEBUG_MSG(1, "%s corrodes %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); - att_status->m_corroded = v; + att_status->m_corroded_rate = v; } } #endif @@ -1673,10 +1672,11 @@ struct PerformAttack crush_leech(); } #if defined(TYRANT_UNLEASHED) - if (att_status->m_corroded > 0) + if (att_status->m_corroded_rate > 0) { - _DEBUG_MSG(1, "%s loses Attack %u.\n", status_description(att_status).c_str(), att_status->m_corroded); - att_status->m_attack = safe_minus(att_status->m_attack, att_status->m_corroded); + unsigned v = std::min(att_status->m_corroded_rate, safe_minus(att_status->m_card->m_attack + att_status->m_berserk, att_status->m_corroded_weakened)); + _DEBUG_MSG(1, "%s loses Attack by %u.\n", status_description(att_status).c_str(), v); + att_status->m_corroded_weakened += v; } #else prepend_on_death(fd); @@ -1799,7 +1799,6 @@ struct PerformAttack count_achievement(fd, def_status); unsigned v = def_status->m_card->m_berserk_oa + def_status->enhanced(berserk); def_status->m_berserk += v; - def_status->m_attack += v; } if(def_status->m_card->m_sunder_oa && skill_check(fd, def_status, att_status)) { diff --git a/sim.h b/sim.h index 388ddd82..e4793f26 100644 --- a/sim.h +++ b/sim.h @@ -129,12 +129,12 @@ struct CardStatus const Card* m_card; unsigned m_index; unsigned m_player; - unsigned m_attack; unsigned m_augmented; unsigned m_berserk; bool m_blitzing; bool m_chaosed; - unsigned m_corroded; + unsigned m_corroded_rate; + unsigned m_corroded_weakened; unsigned m_delay; bool m_diseased; unsigned m_evaded; From cd477f7c39487a5b124bf3d150f06220181c35f7 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 24 May 2014 22:34:00 +0800 Subject: [PATCH 218/406] Improve level-1 mission names. --- xml.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/xml.cpp b/xml.cpp index 36538974..30264acb 100644 --- a/xml.cpp +++ b/xml.cpp @@ -432,6 +432,9 @@ Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::De } xml_node<>* mission_req_node(node->first_node(decktype == DeckType::mission ? "req" : "mission_req")); unsigned mission_req(mission_req_node ? atoi(mission_req_node->value()) : 0); +#if defined(TYRANT_UNLEASHED) + if (level < 10) { deck_name += "-" + to_string(level); } +#endif decks.decks.push_back(Deck{decktype, id, deck_name}); Deck* deck = &decks.decks.back(); deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); @@ -439,6 +442,9 @@ Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::De decks.by_name[deck_name] = deck; std::stringstream alt_name; alt_name << decktype_names[decktype] << " #" << id; +#if defined(TYRANT_UNLEASHED) + if (level < 10) { alt_name << "-" << level; } +#endif decks.by_name[alt_name.str()] = deck; return deck; } @@ -468,7 +474,7 @@ void read_missions(Decks& decks, const Cards& cards, std::string filename) Deck* deck; #if defined(TYRANT_UNLEASHED) { - deck = read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name + "-1", 1); + deck = read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name, 1); } #endif deck = read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name, 10); From 4a3d20266876b059c160f69ee5a1643359098121 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 25 May 2014 15:29:30 +0800 Subject: [PATCH 219/406] Improve debug info. --- deck.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/deck.cpp b/deck.cpp index 09cfd287..1aaa0113 100644 --- a/deck.cpp +++ b/deck.cpp @@ -321,6 +321,15 @@ std::string Deck::long_description(const Cards& all_cards) ios << " " << card_description(all_cards, card) << "\n"; } } + if (! reward_cards.empty()) + { + ios << "Reward Cards: "; + for (const auto & card : reward_cards) + { + ios << card->m_name << ", "; + } + ios << "\n"; + } return ios.str(); } From 3802c935eb3e1895bbb5d5ce641afb812bec707f Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 4 Jun 2014 17:51:49 +0800 Subject: [PATCH 220/406] Improve hill climbing to support commander downgrading. --- tyrant_optimize.cpp | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index d5e27040..29be733c 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -182,9 +182,9 @@ bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, cons { cards_in.clear(); if (card == nullptr) - { + { // change commander or remove card if (to_slot < 0) - { // commander + { // change commander cards_in.emplace_back(-1, deck->commander); } deck_cost = get_deck_cost(deck); @@ -195,13 +195,38 @@ bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, cons deck->cards.clear(); deck->cards.emplace_back(card); cards_in.emplace_back(is_random ? -1 : to_slot, card); - deck_cost = get_deck_cost(deck); - if (deck_cost > fund) - { return false; } + { + // try to add commander into the deck, defuse/downgrade it if necessary + std::stack candidate_cards; + const Card * old_commander = deck->commander; + candidate_cards.emplace(deck->commander); + while (! candidate_cards.empty()) + { + const Card* card_in = candidate_cards.top(); + candidate_cards.pop(); + deck->commander = card_in; + deck_cost = get_deck_cost(deck); + if (deck_cost <= fund) + { break; } + for (auto recipe_it : card_in->m_recipe_cards) + { candidate_cards.emplace(recipe_it.first); } + } + if (deck_cost > fund) + { + deck->commander = old_commander; + return false; + } + else if (deck->commander != old_commander) + { + append_unless_remove(cards_out, cards_in, {-1, old_commander}); + append_unless_remove(cards_in, cards_out, {-1, deck->commander}); + } + } if (is_random) { std::shuffle(cards.begin(), cards.end(), re); } for (signed i = 0; i < (signed)cards.size(); ++ i) { + // try to add cards[i] into the deck, defuse/downgrade it if necessary auto saved_cards = deck->cards; auto in_it = deck->cards.end() - (i < to_slot); in_it = deck->cards.insert(in_it, nullptr); @@ -771,6 +796,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcommander = best_commander; d1->cards = best_cards; if (card_candidate ? ((slot_i < best_cards.size() && card_candidate->m_name == best_cards[slot_i]->m_name) || // Omega -> Omega @@ -803,6 +829,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::maphash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; // Then update best score/slot, print stuff best_score = current_score; + best_commander = d1->commander; best_cards = d1->cards; deck_has_been_improved = true; print_score_info(compare_results, proc.factors); @@ -811,6 +838,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map -1e-9) { break; } } + d1->commander = best_commander; d1->cards = best_cards; } unsigned simulations = 0; @@ -900,6 +928,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std assert(!card_candidate || card_candidate->m_type != CardType::commander); for(unsigned to_slot(card_candidate ? 0 : best_cards.size() - 1); to_slot < best_cards.size() + (from_slot < best_cards.size() ? 0 : 1); ++to_slot) { + d1->commander = best_commander; d1->cards = best_cards; if (card_candidate ? ((from_slot < best_cards.size() && (from_slot == to_slot && card_candidate->m_name == best_cards[to_slot]->m_name)) || // 2 Omega -> 2 Omega @@ -932,6 +961,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std // Then update best score/slot, print stuff std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; best_score = current_score; + best_commander = d1->commander; best_cards = d1->cards; deck_has_been_improved = true; print_score_info(compare_results, proc.factors); @@ -941,6 +971,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std if(best_score.points - target_score > -1e-9) { break; } } + d1->commander = best_commander; d1->cards = best_cards; } unsigned simulations = 0; From 3f171eba3d81e681c77d56a986a24b9f3aafbf4d Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 5 Jun 2014 00:04:41 +0800 Subject: [PATCH 221/406] Restructure skills. --- card.h | 76 +----------- cards.cpp | 1 + sim.cpp | 348 +++++++++++++++++++++-------------------------------- sim.h | 10 +- tyrant.cpp | 4 +- tyrant.h | 8 +- xml.cpp | 114 +++--------------- 7 files changed, 171 insertions(+), 390 deletions(-) diff --git a/card.h b/card.h index f742de9b..21cc4275 100644 --- a/card.h +++ b/card.h @@ -9,127 +9,55 @@ class Card { public: - unsigned m_antiair; - unsigned m_armored; unsigned m_attack; unsigned m_base_id; // The id of the original card if a card is unique and alt/upgraded. The own id of the card otherwise. - unsigned m_berserk; - unsigned m_berserk_oa; - bool m_blitz; - unsigned m_burst; - unsigned m_corrosive; - unsigned m_counter; - unsigned m_crush; unsigned m_delay; - bool m_disease; bool m_disease_oa; - bool m_emulate; - unsigned m_evade; Faction m_faction; - bool m_fear; unsigned m_final_id; // The id of fully upgraded card - unsigned m_flurry; - bool m_flying; - bool m_fusion; unsigned m_health; unsigned m_hidden; unsigned m_id; - bool m_immobilize; - unsigned m_inhibit; - bool m_intercept; - unsigned m_leech; - unsigned m_legion; unsigned m_level; std::string m_name; - bool m_payback; - unsigned m_pierce; unsigned m_phase; - unsigned m_poison; - unsigned m_poison_oa; unsigned m_rarity; - bool m_refresh; - unsigned m_regenerate; unsigned m_replace; unsigned m_reserve; unsigned m_set; - unsigned m_siphon; - bool m_split; - bool m_stun; - bool m_sunder; - bool m_sunder_oa; - bool m_swipe; - bool m_tribute; bool m_unique; - unsigned m_valor; - bool m_wall; std::vector m_skills[SkillMod::num_skill_activation_modifiers]; + unsigned m_x[SkillMod::num_skill_activation_modifiers][num_skills]; CardType::CardType m_type; unsigned m_recipe_cost; std::map m_recipe_cards; std::map m_used_for_cards; - unsigned m_skill_pos[num_skills]; public: Card() : - m_antiair(0), - m_armored(0), m_attack(0), m_base_id(0), - m_berserk(0), - m_berserk_oa(0), - m_blitz(false), - m_burst(0), - m_corrosive(0), - m_counter(0), - m_crush(0), m_delay(0), - m_disease(false), m_disease_oa(false), - m_emulate(false), - m_evade(0), m_faction(imperial), - m_fear(false), m_final_id(0), - m_flurry(0), - m_flying(false), - m_fusion(false), m_health(0), m_hidden(0), m_id(0), - m_immobilize(false), - m_inhibit(0), - m_intercept(false), - m_leech(0), - m_legion(0), m_level(1), m_name(""), - m_payback(false), - m_pierce(0), m_phase(0), - m_poison(0), - m_poison_oa(0), m_rarity(1), - m_refresh(false), - m_regenerate(0), m_replace(0), m_set(0), - m_siphon(0), - m_split(false), - m_stun(false), - m_sunder(false), - m_sunder_oa(false), - m_swipe(false), - m_tribute(false), m_unique(false), - m_valor(0), - m_wall(false), m_skills(), m_type(CardType::assault), m_recipe_cost(0), m_recipe_cards(), m_used_for_cards() { - std::memset(m_skill_pos, 0, sizeof m_skill_pos); + std::memset(m_x, 0, sizeof m_x); } void add_skill(Skill id, unsigned x, Faction y, unsigned c, Skill s, bool all, SkillMod::SkillMod mod=SkillMod::on_activate); diff --git a/cards.cpp b/cards.cpp index 4d60a182..2da67b4a 100644 --- a/cards.cpp +++ b/cards.cpp @@ -170,5 +170,6 @@ void Card::add_skill(Skill id, unsigned x, Faction y, unsigned c, Skill s, bool } } m_skills[mod].push_back({id, x, y, c, s, all, mod}); + m_x[mod][id] = std::max(1u, x); } diff --git a/sim.cpp b/sim.cpp index 8d2ff81a..fcceaba4 100644 --- a/sim.cpp +++ b/sim.cpp @@ -74,46 +74,23 @@ inline void Field::print_selection_array() #endif } //------------------------------------------------------------------------------ -CardStatus::CardStatus(const Card* card) : - m_card(card), - m_index(0), - m_player(0), - m_augmented(0), - m_berserk(0), - m_blitzing(false), - m_chaosed(false), - m_corroded_rate(0), - m_corroded_weakened(0), - m_delay(card->m_delay), - m_diseased(false), - m_evaded(0), - m_enfeebled(0), - m_faction(card->m_faction), - m_frozen(false), - m_hp(card->m_health), - m_immobilized(false), - m_infused(false), - m_inhibited(0), - m_jammed(false), - m_phased(false), - m_poisoned(0), - m_protected(0), - m_rallied(0), - m_stunned(0), - m_sundered(false), - m_weakened(0), - m_temporary_split(false), - m_is_summoned(false), - m_step(CardStep::none) +inline bool CardStatus::has(Skill skill, SkillMod::SkillMod mod) const { - std::memset(m_enhanced_value, 0, sizeof m_enhanced_value); - std::memset(m_skill_cd, 0, sizeof m_skill_cd); + return m_card->m_x[mod][skill]; } - //------------------------------------------------------------------------------ -inline unsigned CardStatus::enhanced(Skill skill) +inline unsigned CardStatus::x(Skill skill, SkillMod::SkillMod mod) const { - return m_enhanced_value[m_card->m_skill_pos[skill]]; + return m_card->m_x[mod][skill] + enhanced(skill); +} +//------------------------------------------------------------------------------ +inline unsigned CardStatus::enhanced(Skill skill) const +{ +#if defined(TYRANT_UNLEASHED) + return m_enhanced_value[skill]; +#else + return 0; +#endif } //------------------------------------------------------------------------------ inline void CardStatus::set(const Card* card) @@ -221,47 +198,6 @@ std::string card_description(const Cards& cards, const Card* c) if(c->m_unique) { desc += " unique"; } if(c->m_rarity == 4) { desc += " legendary"; } if(c->m_faction != allfactions) { desc += " " + faction_names[c->m_faction]; } - if(c->m_antiair > 0) { desc += ", antiair " + to_string(c->m_antiair); } - if(c->m_armored > 0) { desc += ", armored " + to_string(c->m_armored); } - if(c->m_berserk > 0) { desc += ", berserk " + to_string(c->m_berserk); } - if(c->m_blitz) { desc += ", blitz"; } - if(c->m_burst > 0) { desc += ", burst " + to_string(c->m_burst); } - if(c->m_counter > 0) { desc += ", counter " + to_string(c->m_counter); } - if(c->m_corrosive > 0) { desc += ", corrosive " + to_string(c->m_corrosive); } - if(c->m_crush > 0) { desc += ", crush " + to_string(c->m_crush); } - if(c->m_disease) { desc += ", disease"; } - if(c->m_emulate) { desc += ", emulate"; } -#if defined(TYRANT_UNLEASHED) - if(c->m_evade) { desc += ", evade " + to_string(c->m_evade); } -#else - if(c->m_evade) { desc += ", evade"; } -#endif - if(c->m_fear) { desc += ", fear"; } - if(c->m_flurry > 0) { desc += ", flurry " + to_string(c->m_flurry); } - if(c->m_flying) { desc += ", flying"; } - if(c->m_fusion) { desc += ", fusion"; } - if(c->m_immobilize) { desc += ", immobilize"; } - if(c->m_inhibit) { desc += ", inhibit " + to_string(c->m_inhibit); } - if(c->m_intercept) { desc += ", intercept"; } - if(c->m_leech > 0) { desc += ", leech " + to_string(c->m_leech); } - if(c->m_legion > 0) { desc += ", legion " + to_string(c->m_legion); } - if(c->m_payback) { desc += ", payback"; } - if(c->m_pierce > 0) { desc += ", pierce " + to_string(c->m_pierce); } - if(c->m_phase) { desc += ", phase"; } - if(c->m_poison > 0) { desc += ", poison " + to_string(c->m_poison); } - if(c->m_refresh) { desc += ", refresh"; } - if(c->m_regenerate > 0) { desc += ", regenerate " + to_string(c->m_regenerate); } - if(c->m_siphon > 0) { desc += ", siphon " + to_string(c->m_siphon); } - if(c->m_stun) { desc += ", stun"; } - if(c->m_sunder) { desc += ", sunder"; } - if(c->m_swipe) { desc += ", swipe"; } - if(c->m_tribute) { desc += ", tribute"; } - if(c->m_valor > 0) { desc += ", valor " + to_string(c->m_valor); } - if(c->m_wall) { desc += ", wall"; } - if(c->m_berserk_oa > 0) { desc += ", berserk " + to_string(c->m_berserk_oa); } - if(c->m_disease_oa) { desc += ", disease on Attacked"; } - if(c->m_poison_oa > 0) { desc += ", poison " + to_string(c->m_poison_oa) + " on Attacked"; } - if(c->m_sunder_oa) { desc += ", sunder on Attacked"; } for(auto mod = 0; mod < SkillMod::num_skill_activation_modifiers; ++ mod) { for(auto& skill: c->m_skills[mod]) { desc += ", " + skill_description(cards, skill); } @@ -371,7 +307,7 @@ void Hand::reset(std::mt19937& re) { assaults.reset(); structures.reset(); - commander = CardStatus(deck->get_commander()); + commander.set(deck->get_commander()); deck->shuffle(re); } //---------------------- $40 Game rules implementation ------------------------- @@ -608,7 +544,7 @@ void resolve_skill(Field* fd) auto& enhanced_s = enhanced_value > 0 ? apply_enhance(skill, enhanced_value) : skill; auto& modified_s = enhanced_s; #else - bool fusion_active = status->m_card->m_fusion && status->m_player == fd->tapi && fd->fusion_count >= 3; + bool fusion_active = status->has(fusion) && status->m_player == fd->tapi && fd->fusion_count >= 3; auto& augmented_s = status->m_augmented > 0 ? apply_augment(status, skill) : skill; auto& fusioned_s = fusion_active ? apply_fusion(augmented_s) : augmented_s; auto& infused_s = status->m_infused ? apply_infuse(fusioned_s) : fusioned_s; @@ -627,11 +563,12 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector bool need_add_skill = may_change_skill(fd, status, mod); for(auto& ss: skills) { + if (skill_table[ss.id] == nullptr) + { continue; } auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, status, ss, mod, need_add_skill) : ss; - auto skill_pos = status->m_card->m_skill_pos[ss.id]; - if (status->m_skill_cd[skill_pos] > 0) + if (status->m_skill_cd[ss.id] > 0) { - _DEBUG_MSG(2, "Cooling down (%u) %s skill %s\n", status->m_skill_cd[skill_pos], status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); + _DEBUG_MSG(2, "Cooling down (%u) %s skill %s\n", status->m_skill_cd[ss.id], status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); continue; } _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); @@ -668,7 +605,7 @@ struct PlayCard { setStorage(); placeCard(); - blitz(); + performBlitz(); onPlaySkills(); fieldEffects(); return(true); @@ -702,7 +639,7 @@ struct PlayCard // all except assault: noop template - void blitz() + void performBlitz() { } @@ -739,9 +676,9 @@ void PlayCard::setStorage() } // assault template <> -void PlayCard::blitz() +void PlayCard::performBlitz() { - if(status->m_card->m_blitz) + if(status->has(blitz)) { check_and_perform_blitz(fd, status); } @@ -839,7 +776,7 @@ Results play(Field* fd) #endif turn_start_phase(fd); // Special case: refresh on commander - if(fd->tip->commander.m_card->m_refresh) + if(fd->tip->commander.has(refresh)) { check_and_perform_refresh(fd, &fd->tip->commander); } @@ -1044,7 +981,7 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return(ref->m_card->m_flying); + return ref->has(flying); } template<> @@ -1084,7 +1021,7 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return(!ref->m_card->m_flying && !ref->m_card->m_antiair > 0); + return !ref->has(flying) && ref->x(antiair) == 0; } // Not yet support on Attacked/on Death. @@ -1188,7 +1125,7 @@ inline void count_killed_achievements(Field* fd, const CardStatus* status) { fd->inc_counter(fd->achievement.unit_type_killed, status->m_card->m_type); } - if(status->m_card->m_flying) + if(status->has(flying)) { fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::unit_with_flying_killed); } @@ -1206,7 +1143,7 @@ void remove_hp(Field* fd, CardStatus& status, unsigned dmg) { fd->killed_with_on_death.push_back(&status); } - if(status.m_card->m_regenerate > 0) + if(status.has(regenerate)) { fd->killed_with_regen.push_back(&status); } @@ -1251,7 +1188,7 @@ void check_regeneration(Field* fd) { count_achievement(fd, status); _DEBUG_MSG(1, "%s regenerates with %u health\n", status_description(status).c_str(), status->m_card->m_health); - add_hp(fd, status, status->m_card->m_regenerate); + add_hp(fd, status, status->x(regenerate)); } else { @@ -1260,15 +1197,19 @@ void check_regeneration(Field* fd) } fd->killed_with_regen.clear(); } +void cooldown_skills(CardStatus & status) +{ + for (const auto & ss : status.m_card->m_skills[SkillMod::on_activate]) + { + if (status.m_skill_cd[ss.id] > 0) { -- status.m_skill_cd[ss.id]; } + } +} void turn_start_phase(Field* fd) { fd->fusion_count = 0; // Active player's commander card: #if defined(TYRANT_UNLEASHED) - for (auto & skill_cd : fd->tap->commander.m_skill_cd) - { - if (skill_cd > 0) { -- skill_cd; } - } + cooldown_skills(fd->tap->commander); #endif // Active player's assault cards: // update index @@ -1288,17 +1229,14 @@ void turn_start_phase(Field* fd) -- status.m_delay; } #if defined(TYRANT_UNLEASHED) - for (auto & skill_cd : status.m_skill_cd) - { - if (skill_cd > 0) { -- skill_cd; } - } + cooldown_skills(status); #else if(status.m_poisoned > 0) { _DEBUG_MSG(1, "%s takes poison damage\n", status_description(&status).c_str()); remove_hp(fd, status, status.m_poisoned); } - if(status.m_card->m_fusion && status.m_delay == 0) { ++ fd->fusion_count; } + if(status.has(fusion) && status.m_delay == 0) { ++ fd->fusion_count; } #endif } } @@ -1319,12 +1257,9 @@ void turn_start_phase(Field* fd) --status.m_delay; } #if defined(TYRANT_UNLEASHED) - for (auto & skill_cd : status.m_skill_cd) - { - if (skill_cd > 0) { -- skill_cd; } - } + cooldown_skills(status); #else - if(status.m_card->m_fusion && status.m_delay == 0) { ++fd->fusion_count; } + if(status.has(fusion) && status.m_delay == 0) { ++fd->fusion_count; } #endif } } @@ -1400,7 +1335,7 @@ void turn_end_phase(Field* fd) status.m_phased = false; if(status.m_stunned > 0) { -- status.m_stunned; } status.m_temporary_split = false; - if(status.m_card->m_refresh) + if(status.has(refresh)) { check_and_perform_refresh(fd, &status); } @@ -1421,7 +1356,7 @@ void turn_end_phase(Field* fd) { continue; } - if(status.m_card->m_refresh && fd->effect != Effect::impenetrable) + if(status.has(refresh) && fd->effect != Effect::impenetrable) { check_and_perform_refresh(fd, &status); } @@ -1481,7 +1416,7 @@ void evaluate_legion(Field* fd) for(fd->current_ci = 0; fd->current_ci < assaults.size(); ++fd->current_ci) { CardStatus* status(&assaults[fd->current_ci]); - unsigned legion_base = fd->effect == Effect::united_front ? status->m_card->m_delay : status->m_card->m_legion; + unsigned legion_base = fd->effect == Effect::united_front ? status->m_card->m_delay : status->x(legion); if(legion_base == 0) { continue; @@ -1521,14 +1456,14 @@ inline unsigned counter_damage(Field* fd, CardStatus* att, CardStatus* def) { assert(att->m_card->m_type == CardType::assault); assert(def->m_card->m_type != CardType::action); - return(safe_minus(def->m_card->m_counter + def->enhanced(counter) + att->m_enfeebled, att->m_protected)); + return(safe_minus(def->x(counter) + att->m_enfeebled, att->m_protected)); } inline CardStatus* select_first_enemy_wall(Field* fd) { for(unsigned i(0); i < fd->tip->structures.size(); ++i) { CardStatus& c(fd->tip->structures[i]); - if(c.m_card->m_wall && c.m_hp > 0 && skill_check(fd, &c, nullptr)) + if(c.has(wall) && c.m_hp > 0 && skill_check(fd, &c, nullptr)) { count_achievement(fd, &c); return(&c); @@ -1595,7 +1530,7 @@ struct PerformAttack // assaults only: (crush, leech if still alive) // check regeneration #if not defined(TYRANT_UNLEASHED) - if(def_status->m_card->m_flying && (fd->effect == Effect::high_skies || fd->flip()) && skill_check(fd, def_status, att_status)) + if(def_status->has(flying) && (fd->effect == Effect::high_skies || fd->flip()) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); _DEBUG_MSG(1, "%s attacks %s but it dodges with Flying\n", status_description(att_status).c_str(), status_description(def_status).c_str()); @@ -1612,7 +1547,7 @@ struct PerformAttack #if not defined(TYRANT_UNLEASHED) // If Impenetrable, prevent attack damage against walls, // but still activate Counter! - if(att_dmg > 0 && fd->effect == Effect::impenetrable && def_status->m_card->m_wall) + if(att_dmg > 0 && fd->effect == Effect::impenetrable && def_status->has(wall)) { _DEBUG_MSG(1, "%s is impenetrable\n", status_description(def_status).c_str()); att_dmg = 0; @@ -1620,7 +1555,7 @@ struct PerformAttack #endif if(att_dmg > 0) { - immobilize(); + performImmobilize(); attack_damage(); if(__builtin_expect(fd->end, false)) { return; } damage_dependant_pre_oa(); @@ -1632,7 +1567,7 @@ struct PerformAttack if(att_status->m_hp > 0) { #if not defined(TYRANT_UNLEASHED) - if(def_status->m_card->m_stun && skill_check(fd, def_status, att_status)) + if(def_status->has(stun) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); // perform_skill_stun @@ -1640,7 +1575,7 @@ struct PerformAttack att_status->m_stunned = 2; } #endif - if(def_status->m_card->m_counter > 0 && skill_check(fd, def_status, att_status)) + if(def_status->has(counter) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); // perform_skill_counter @@ -1648,24 +1583,21 @@ struct PerformAttack _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, *att_status, counter_dmg); } - if(att_status->m_card->m_berserk > 0 && skill_check(fd, att_status, nullptr)) + unsigned berserk_value = att_status->x(berserk); + if(berserk_value > 0 && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); // perform_skill_berserk - unsigned v = att_status->m_card->m_berserk + att_status->enhanced(berserk); - att_status->m_berserk += v; + att_status->m_berserk += berserk_value; } #if defined(TYRANT_UNLEASHED) - if (def_status->m_card->m_corrosive > 0 && skill_check(fd, def_status, att_status)) + unsigned corrosive_value = def_status->x(corrosive); + if (corrosive_value > att_status->m_corroded_rate && skill_check(fd, def_status, att_status)) { + count_achievement(fd, def_status); // perform_skill_corrosive - unsigned v = def_status->m_card->m_corrosive + def_status->enhanced(corrosive); - if (v > att_status->m_corroded_rate) - { - count_achievement(fd, def_status); - _DEBUG_MSG(1, "%s corrodes %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); - att_status->m_corroded_rate = v; - } + _DEBUG_MSG(1, "%s corrodes %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), corrosive_value); + att_status->m_corroded_rate = corrosive_value; } #endif } @@ -1688,31 +1620,32 @@ struct PerformAttack template void modify_attack_damage(unsigned pre_modifier_dmg) { - const Card& att_card(*att_status->m_card); - const Card& def_card(*def_status->m_card); - assert(att_card.m_type == CardType::assault); + assert(att_status->m_card->m_type == CardType::assault); assert(pre_modifier_dmg > 0); att_dmg = pre_modifier_dmg; // enhance damage std::string desc; #if not defined(TYRANT_UNLEASHED) - if(att_card.m_valor > 0 && skill_check(fd, att_status, nullptr)) + unsigned valor_value = att_status->x(valor); + if(valor_value > 0 && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); - if(debug_print) { desc += "+" + to_string(att_card.m_valor) + "(valor)"; } - att_dmg += att_card.m_valor; + if(debug_print) { desc += "+" + to_string(valor_value) + "(valor)"; } + att_dmg += valor_value; } - if(att_card.m_antiair > 0 && skill_check(fd, att_status, def_status)) + unsigned antiair_value = att_status->x(antiair); + if(antiair_value > 0 && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); - if(debug_print) { desc += "+" + to_string(att_card.m_antiair) + "(antiair)"; } - att_dmg += att_card.m_antiair; + if(debug_print) { desc += "+" + to_string(antiair_value) + "(antiair)"; } + att_dmg += antiair_value; } - if(att_card.m_burst > 0 && skill_check(fd, att_status, def_status)) + unsigned burst_value = att_status->x(burst); + if(burst_value > 0 && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); - if(debug_print) { desc += "+" + to_string(att_card.m_burst) + "(burst)"; } - att_dmg += att_card.m_burst; + if(debug_print) { desc += "+" + to_string(burst_value) + "(burst)"; } + att_dmg += burst_value; } #endif if(def_status->m_enfeebled > 0) @@ -1723,7 +1656,7 @@ struct PerformAttack // prevent damage std::string reduced_desc; unsigned reduced_dmg(0); - unsigned armored_value(def_card.m_armored + def_status->enhanced(armored)); + unsigned armored_value = def_status->x(armored); if(armored_value == 0 && fd->effect == Effect::photon_shield && def_status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 0u : 1u)) { armored_value = 2; @@ -1731,7 +1664,7 @@ struct PerformAttack if(armored_value > 0) { // Armored counts if not totally cancelled by Pierce. TODO how if Armored + Proteced > Pierce? - if(armored_value > att_card.m_pierce) + if(armored_value > att_status->x(pierce)) { count_achievement(fd, def_status); } @@ -1743,11 +1676,11 @@ struct PerformAttack if(debug_print) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->m_protected) + "(protected)"; } reduced_dmg += def_status->m_protected; } - if(reduced_dmg > 0 && att_card.m_pierce > 0) + if(reduced_dmg > 0 && att_status->x(pierce) > 0) { // TODO No Pierce achievement yet, so no count_achievement(fd, att_status) - if(debug_print) { reduced_desc += "-" + to_string(att_card.m_pierce) + "(pierce)"; } - reduced_dmg = safe_minus(reduced_dmg, att_card.m_pierce); + if(debug_print) { reduced_desc += "-" + to_string(att_status->x(pierce)) + "(pierce)"; } + reduced_dmg = safe_minus(reduced_dmg, att_status->x(pierce)); } att_dmg = safe_minus(att_dmg, reduced_dmg); if(debug_print) @@ -1759,7 +1692,7 @@ struct PerformAttack } template - void immobilize() {} + void performImmobilize() {} template void attack_damage() @@ -1777,30 +1710,28 @@ struct PerformAttack template void on_attacked() { - if(def_status->m_card->m_poison_oa > 0 && skill_check(fd, def_status, att_status)) + unsigned poison_value = def_status->x(poison, SkillMod::on_attacked); + if(poison_value > att_status->m_poisoned && skill_check(fd, def_status, att_status)) { - unsigned v = def_status->m_card->m_poison_oa + def_status->enhanced(poison); - if (v > att_status->m_poisoned) - { - count_achievement(fd, def_status); - _DEBUG_MSG(1, "%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), v); - att_status->m_poisoned = v; - } + count_achievement(fd, def_status); + // perform_skill_poison + _DEBUG_MSG(1, "%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), poison_value); + att_status->m_poisoned = poison_value; } - if(def_status->m_card->m_disease_oa && skill_check(fd, def_status, att_status)) + if(def_status->has(disease, SkillMod::on_attacked) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); // perform_skill_disease _DEBUG_MSG(1, "%s (on attacked) diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); att_status->m_diseased = true; } - if(def_status->m_hp > 0 && def_status->m_card->m_berserk_oa > 0 && skill_check(fd, def_status, nullptr)) + unsigned berserk_value = def_status->x(berserk, SkillMod::on_attacked); + if(def_status->m_hp > 0 && berserk_value > 0 && skill_check(fd, def_status, nullptr)) { count_achievement(fd, def_status); - unsigned v = def_status->m_card->m_berserk_oa + def_status->enhanced(berserk); - def_status->m_berserk += v; + def_status->m_berserk += berserk_value; } - if(def_status->m_card->m_sunder_oa && skill_check(fd, def_status, att_status)) + if(def_status->has(sunder, SkillMod::on_attacked) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); // perform_skill_sunder @@ -1815,11 +1746,11 @@ struct PerformAttack }; template<> -void PerformAttack::immobilize() +void PerformAttack::performImmobilize() { - if(att_status->m_card->m_immobilize && fd->flip() && skill_check(fd, att_status, def_status)) + if(att_status->has(immobilize) && fd->flip() && skill_check(fd, att_status, def_status)) { - count_achievement(fd, att_status); + count_achievement(fd, att_status); _DEBUG_MSG(1, "%s immobilizes %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_immobilized = true; } @@ -1835,42 +1766,39 @@ template<> void PerformAttack::damage_dependant_pre_oa() { #if not defined(TYRANT_UNLEASHED) - if(att_status->m_card->m_siphon > 0 && skill_check(fd, att_status, def_status)) + unsigned siphon_value = std::min(att_dmg, att_status->x(siphon)); + if(siphon_value > 0 && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_siphon - unsigned v = std::min(att_dmg, att_status->m_card->m_siphon); - _DEBUG_MSG(1, "%s siphons %u health for %s\n", status_description(att_status).c_str(), v, status_description(&fd->tap->commander).c_str()); - add_hp(fd, &fd->tap->commander, v); + _DEBUG_MSG(1, "%s siphons %u health for %s\n", status_description(att_status).c_str(), siphon_value, status_description(&fd->tap->commander).c_str()); + add_hp(fd, &fd->tap->commander, siphon_value); } #endif - if(att_status->m_card->m_poison > 0 && skill_check(fd, att_status, def_status)) + unsigned poison_value = att_status->x(poison); + if(poison_value > def_status->m_poisoned && skill_check(fd, att_status, def_status)) { + count_achievement(fd, att_status); // perform_skill_poison - unsigned v = att_status->m_card->m_poison + att_status->enhanced(poison); - if (v > def_status->m_poisoned) - { - count_achievement(fd, att_status); - _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); - def_status->m_poisoned = v; - } + _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), poison_value); + def_status->m_poisoned = poison_value; } #if not defined(TYRANT_UNLEASHED) - if(att_status->m_card->m_disease && skill_check(fd, att_status, def_status)) + if(att_status->has(disease) && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_disease _DEBUG_MSG(1, "%s diseases %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_diseased = true; } - if(att_status->m_card->m_sunder && skill_check(fd, att_status, def_status)) + if(att_status->has(sunder) && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_sunder _DEBUG_MSG(1, "%s sunders %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_sundered = true; } - if(att_status->m_card->m_phase && skill_check(fd, att_status, def_status)) + if(att_status->has(phase) && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_phase @@ -1880,13 +1808,13 @@ void PerformAttack::damage_dependant_pre_oa() #endif #if defined(TYRANT_UNLEASHED) // XXX Assume inhibit stacks, if happened in future - if(att_status->m_card->m_inhibit > 0 && skill_check(fd, att_status, def_status)) + unsigned inhibit_value = att_status->x(inhibit); + if (inhibit_value > 0 && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_inhibit - unsigned v = att_status->m_card->m_inhibit + att_status->enhanced(inhibit); - _DEBUG_MSG(1, "%s inhibits %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), v); - def_status->m_inhibited += v; + _DEBUG_MSG(1, "%s inhibits %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), inhibit_value); + def_status->m_inhibited += inhibit_value; } #endif } @@ -1903,26 +1831,27 @@ void PerformAttack::on_kill() template<> void PerformAttack::crush_leech() { - if(att_status->m_card->m_crush > 0 && killed_by_attack && skill_check(fd, att_status, nullptr)) + unsigned crush_value = att_status->x(crush); + if(crush_value > 0 && killed_by_attack && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); // perform_skill_crush CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall if (def_status != nullptr) { - _DEBUG_MSG(1, "%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), att_status->m_card->m_crush); - remove_hp(fd, *def_status, att_status->m_card->m_crush); + _DEBUG_MSG(1, "%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), crush_value); + remove_hp(fd, *def_status, crush_value); } else { - _DEBUG_MSG(1, "%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(&fd->tip->commander).c_str(), att_status->m_card->m_crush); - remove_commander_hp(fd, fd->tip->commander, att_status->m_card->m_crush, true); + _DEBUG_MSG(1, "%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(&fd->tip->commander).c_str(), crush_value); + remove_commander_hp(fd, fd->tip->commander, crush_value, true); } } - if(att_status->m_card->m_leech > 0 && skill_check(fd, att_status, nullptr)) + unsigned leech_value = std::min(att_dmg, att_status->x(leech)); + if(leech_value > 0 && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); - auto leech_value = std::min(att_dmg, att_status->m_card->m_leech + att_status->enhanced(leech)); _DEBUG_MSG(1, "%s leeches %u health\n", status_description(att_status).c_str(), leech_value); add_hp(fd, att_status, leech_value); } @@ -1951,11 +1880,12 @@ bool attack_phase(Field* fd) return false; } unsigned num_attacks(1); - if(att_status->m_card->m_flurry > 0 && fd->flip() && skill_check(fd, att_status, nullptr)) + unsigned flurry_value = att_status->x(flurry); + if(flurry_value > 0 && fd->flip() && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); - _DEBUG_MSG(1, "%s activates Flurry\n", status_description(att_status).c_str()); - num_attacks += att_status->m_card->m_flurry; + _DEBUG_MSG(1, "%s activates Flurry %u\n", status_description(att_status).c_str(), flurry_value); + num_attacks += flurry_value; } for(unsigned attack_index(0); attack_index < num_attacks && can_attack(att_status) && fd->tip->commander.m_hp > 0; ++attack_index) { @@ -1966,10 +1896,10 @@ bool attack_phase(Field* fd) // See http://www.kongregate.com/forums/65-tyrant/topics/289416?page=22#posts-6861970 // - 3. attack against the commander or walls (if there is no assault or if the attacker has the fear attribute) // Check if attack mode is 1. or 2. (there is a living assault card in front, and no fear) - if(alive_assault(def_assaults, fd->current_ci) && !(att_status->m_card->m_fear && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) + if(alive_assault(def_assaults, fd->current_ci) && !(att_status->has(fear) && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) { // attack mode 1. - if(!(att_status->m_card->m_swipe && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) + if(!(att_status->has(swipe) && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); } @@ -2103,7 +2033,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, assert(battleground_s.id != no_skill); if(battleground_s.id == s.s) { return(true); } } - return (c->m_card->m_skill_pos[s.s] > 0) && (defensive_skills.find(s.s) != defensive_skills.end() || is_active(c)); + return c->has(s.s) && (defensive_skills.find(s.s) != defensive_skills.end() || is_active(c)); } template<> @@ -2226,9 +2156,7 @@ inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s template<> inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) { - auto skill_pos = c->m_card->m_skill_pos[s.s]; - assert(skill_pos > 0); - c->m_enhanced_value[skill_pos] += s.x; + c->m_enhanced_value[s.s] += s.x; } template<> @@ -2450,7 +2378,7 @@ CardStatus* select_interceptable(Field* fd, CardStatus* src_status, unsigned ind if(index > 0) { CardStatus* left_status(fd->selection_array[index - 1]); - if(left_status->m_card->m_intercept && left_status->m_index == status->m_index - 1 && left_status->m_player == status->m_player && skill_check(fd, left_status, status)) + if(left_status->has(intercept) && left_status->m_index == status->m_index - 1 && left_status->m_player == status->m_player && skill_check(fd, left_status, status)) { count_achievement(fd, left_status); _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(left_status).c_str(), status_description(status).c_str()); @@ -2460,7 +2388,7 @@ CardStatus* select_interceptable(Field* fd, CardStatus* src_status, unsigned ind if(index + 1 < fd->selection_array.size()) { CardStatus* right_status(fd->selection_array[index + 1]); - if(right_status->m_card->m_intercept && right_status->m_index == status->m_index + 1 && right_status->m_player == status->m_player && skill_check(fd, right_status, status)) + if(right_status->has(intercept) && right_status->m_index == status->m_index + 1 && right_status->m_player == status->m_player && skill_check(fd, right_status, status)) { count_achievement(fd, right_status); _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(right_status).c_str(), status_description(status).c_str()); @@ -2477,9 +2405,9 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ { if(is_evadable && #if defined(TYRANT_UNLEASHED) - dst_status->m_evaded < dst_status->m_card->m_evade + dst_status->enhanced(evade) && + dst_status->m_evaded < dst_status->x(evade) && #else - (dst_status->m_card->m_evade || (fd->effect == Effect::quicksilver && dst_status->m_card->m_type == CardType::assault)) && fd->flip() && + (dst_status->has(evade) || (fd->effect == Effect::quicksilver && dst_status->m_card->m_type == CardType::assault)) && fd->flip() && #endif skill_check(fd, dst_status, src_status)) { @@ -2494,10 +2422,9 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ } _DEBUG_MSG(1, "%s %s on %s\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst_status).c_str()); perform_skill(fd, dst_status, s); - auto skill_pos = src_status->m_card->m_skill_pos[skill_id]; - if (skill_pos > 0 && s.c > 0) + if (s.c > 0) { - src_status->m_skill_cd[skill_pos] = s.c; + src_status->m_skill_cd[skill_id] = s.c; } return(true); } @@ -2574,7 +2501,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski // Count at most once even targeting "All" is_count_achievement = false; // Payback - if(c->m_card->m_payback && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) + if(c->has(payback) && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) { count_achievement(fd, c); _DEBUG_MSG(1, "%s paybacks (%s) on %s\n", status_description(c).c_str(), skill_short_description(s).c_str(), status_description(src_status).c_str()); @@ -2593,7 +2520,7 @@ inline void check_and_perform_emulate(Field* fd, CardStatus* src_status, CardSta if(hand->assaults.size() > opposite_status->m_index) { CardStatus& emulator = hand->assaults[opposite_status->m_index]; - if(emulator.m_card->m_emulate && skill_predicate(fd, src_status, &emulator, s) && skill_check(fd, &emulator, nullptr)) + if(emulator.has(emulate) && skill_predicate(fd, src_status, &emulator, s) && skill_check(fd, &emulator, nullptr)) { count_achievement(fd, &emulator); _DEBUG_MSG(1, "Emulate (%s) on %s\n", skill_short_description(s).c_str(), status_description(&emulator).c_str()); @@ -2644,7 +2571,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil // Count at most once even targeting "All" is_count_achievement = false; // Tribute - if(c->m_card->m_tribute && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status)) + if(c->has(tribute) && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status)) { count_achievement(fd, c); _DEBUG_MSG(1, "Tribute (%s) on %s\n", skill_short_description(s).c_str(), status_description(src_status).c_str()); @@ -2741,7 +2668,7 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) _DEBUG_MSG(1, "%s %s %s %u [%s]\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); prepend_skills(fd, &card_status); // Summon X (Genesis effect) does not activate Blitz for X - if(s.x != 0 && card_status.m_card->m_blitz) + if(s.x != 0 && card_status.has(blitz)) { check_and_perform_blitz(fd, &card_status); } @@ -2790,9 +2717,9 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // but resolve_skill will handle those. if( #if defined(TYRANT_UNLEASHED) - c->m_evaded < c->m_card->m_evade + c->enhanced(evade) && + c->m_evaded < c->x(evade) && #else - (c->m_card->m_evade || (fd->effect == Effect::quicksilver && c->m_card->m_type == CardType::assault)) && fd->flip() && + (c->has(evade) || (fd->effect == Effect::quicksilver && c->m_card->m_type == CardType::assault)) && fd->flip() && #endif skill_check(fd, c, src_status)) { @@ -2809,7 +2736,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) { if(src_status->m_card->m_type != CardType::action && src_status->m_hp == 0) { break; } - if(skill.id == mimic || skill.id == split || + if(skill.id == mimic || skill.id == split || skill_table[skill.id] == nullptr || (skill.id == supply && src_status->m_card->m_type != CardType::assault)) { continue; } auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, c, skill, mod, need_add_skill) : skill; @@ -2833,6 +2760,7 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) //------------------------------------------------------------------------------ void fill_skill_table() { + memset(skill_table, 0, sizeof skill_table); skill_table[augment] = perform_targetted_allied_fast; skill_table[backfire] = perform_backfire; skill_table[chaos] = perform_targetted_hostile_fast; diff --git a/sim.h b/sim.h index e4793f26..6dac4efe 100644 --- a/sim.h +++ b/sim.h @@ -157,18 +157,18 @@ struct CardStatus bool m_is_summoned; // is this card summoned (or split)? CardStep m_step; // begin for TYRANT_UNLEASHED - // indexed by m_card->m_skill_pos[skill_id], [0] as fallback - unsigned m_enhanced_value[5]; // XXX - unsigned m_skill_cd[5]; // XXX + unsigned m_enhanced_value[num_skills]; + unsigned m_skill_cd[num_skills]; // end CardStatus() {} - CardStatus(const Card* card); void set(const Card* card); void set(const Card& card); std::string description(); - unsigned enhanced(Skill skill); + bool has(Skill skill, SkillMod::SkillMod mod=SkillMod::on_activate) const; + unsigned x(Skill skill, SkillMod::SkillMod mod=SkillMod::on_activate) const; + unsigned enhanced(Skill skill) const; }; //------------------------------------------------------------------------------ // Represents a particular draw from a deck. diff --git a/tyrant.cpp b/tyrant.cpp index b47891cc..bc5cce17 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -33,11 +33,11 @@ std::string skill_names[Skill::num_skills] = /* "Blizzard", "Mist", */ }; -std::set helpful_skills{ +std::unordered_set> helpful_skills{ augment, cleanse, enhance, heal, protect, rally, repair, rush, supply, }; -std::set defensive_skills{ +std::unordered_set> defensive_skills{ armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, tribute, wall, }; diff --git a/tyrant.h b/tyrant.h index fe8d77d7..0872da7b 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,11 +1,11 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.2.6" +#define TYRANT_OPTIMIZER_VERSION "1.2.7" #include #include -#include +#include #include enum Faction @@ -48,8 +48,8 @@ enum Skill num_skills }; extern std::string skill_names[num_skills]; -extern std::set helpful_skills; -extern std::set defensive_skills; +extern std::unordered_set> helpful_skills; +extern std::unordered_set> defensive_skills; namespace SkillMod { enum SkillMod diff --git a/xml.cpp b/xml.cpp index 30264acb..861f7d58 100644 --- a/xml.cpp +++ b/xml.cpp @@ -75,10 +75,10 @@ Faction skill_faction(xml_node<>* skill) return(unmapped_faction == 0 ? allfactions : map_to_faction(unmapped_faction)); } -unsigned node_value(xml_node<>* skill, const char* attribute) +unsigned node_value(xml_node<>* skill, const char* attribute, unsigned default_value = 0) { xml_attribute<>* value_node(skill->first_attribute(attribute)); - return value_node ? atoi(value_node->value()) : 0; + return value_node ? atoi(value_node->value()) : default_value; } Skill skill_target_skill(xml_node<>* skill) @@ -211,7 +211,14 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) if(rarity_node) { card->m_rarity = atoi(rarity_node->value()); } if(type_node) { card->m_faction = map_to_faction(atoi(type_node->value())); } card->m_set = set; - unsigned skill_pos = 1; + + if (card_node->first_node("skill")) + { // inherit no skill if there is skill node + for (unsigned mod = 0; mod < SkillMod::num_skill_activation_modifiers; ++ mod) + { + card->m_skills[mod].clear(); + } + } for(xml_node<>* skill_node = card_node->first_node("skill"); skill_node; skill_node = skill_node->next_sibling("skill")) @@ -226,99 +233,16 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) bool died(skill_node->first_attribute("died")); bool normal(!(played || died || attacked || kill)); - if(normal) { card->m_skill_pos[skill_id] = skill_pos; } + auto x = node_value(skill_node, "x", 0); + auto y = skill_faction(skill_node); + auto c = node_value(skill_node, "c", 0); + auto s = skill_target_skill(skill_node); - if(skill_id == antiair) - { card->m_antiair = node_value(skill_node, "x"); } - else if(skill_id == armored) - { card->m_armored = node_value(skill_node, "x"); } - else if(skill_id == berserk) - { - if(attacked) { card->m_berserk_oa = node_value(skill_node, "x"); } - else {card->m_berserk = node_value(skill_node, "x"); } - } - else if(skill_id == blitz) - { card->m_blitz = true; } - else if(skill_id == burst) - { card->m_burst = node_value(skill_node, "x"); } - else if(skill_id == corrosive) - { card->m_corrosive = node_value(skill_node, "x"); } - else if(skill_id == counter) - { card->m_counter = node_value(skill_node, "x"); } - else if(skill_id == crush) - { card->m_crush = node_value(skill_node, "x"); } - else if(skill_id == disease) - { - if(attacked) { card->m_disease_oa = true; } - else {card->m_disease = true; } - } - else if(skill_id == emulate) - { card->m_emulate = true; } - else if(skill_id == evade) -#if defined(TYRANT_UNLEASHED) - { card->m_evade = node_value(skill_node, "x"); } -#else - { card->m_evade = 1; } -#endif - else if(skill_id == fear) - { card->m_fear = true; } - else if(skill_id == flurry) - { card->m_flurry = node_value(skill_node, "x"); } - else if(skill_id == flying) - { card->m_flying = true; } - else if(skill_id == fusion) - { card->m_fusion = true; } - else if(skill_id == immobilize) - { card->m_immobilize = true; } - else if(skill_id == inhibit) - { card->m_inhibit = node_value(skill_node, "x"); } - else if(skill_id == intercept) - { card->m_intercept = true; } - else if(skill_id == leech) - { card->m_leech = node_value(skill_node, "x"); } - else if(skill_id == legion) - { card->m_legion = node_value(skill_node, "x"); } - else if(skill_id == payback) - { card->m_payback = true; } - else if(skill_id == pierce) - { card->m_pierce = node_value(skill_node, "x"); } - else if(skill_id == phase) - { card->m_phase = true; } - else if(skill_id == poison) - { - if(attacked) { card->m_poison_oa = node_value(skill_node, "x"); } - else {card->m_poison = node_value(skill_node, "x"); } - } - else if(skill_id == refresh) - { card->m_refresh = true; } - else if(skill_id == regenerate) - { card->m_regenerate = node_value(skill_node, "x"); } - else if(skill_id == siphon) - { card->m_siphon = node_value(skill_node, "x"); } - else if(skill_id == stun) - { card->m_stun = true; } - else if(skill_id == sunder) - { - if(attacked) { card->m_sunder_oa = true; } - else {card->m_sunder = true; } - } - else if(skill_id == swipe) - { card->m_swipe = true; } - else if(skill_id == tribute) - { card->m_tribute = true; } - else if(skill_id == valor) - { card->m_valor = node_value(skill_node, "x"); } - else if(skill_id == wall) - { card->m_wall = true; } - else - { - if(played) { card->add_skill(skill_id, node_value(skill_node, "x"), skill_faction(skill_node), node_value(skill_node, "c"), skill_target_skill(skill_node), all, SkillMod::on_play); } - if(attacked) { card->add_skill(skill_id, node_value(skill_node, "x"), skill_faction(skill_node), node_value(skill_node, "c"), skill_target_skill(skill_node), all, SkillMod::on_attacked); } - if(kill) { card->add_skill(skill_id, node_value(skill_node, "x"), skill_faction(skill_node), node_value(skill_node, "c"), skill_target_skill(skill_node), all, SkillMod::on_kill); } - if(died) { card->add_skill(skill_id, node_value(skill_node, "x"), skill_faction(skill_node), node_value(skill_node, "c"), skill_target_skill(skill_node), all, SkillMod::on_death); } - if(normal) { card->add_skill(skill_id, node_value(skill_node, "x"), skill_faction(skill_node), node_value(skill_node, "c"), skill_target_skill(skill_node), all); } - } - ++ skill_pos; + if (played) { card->add_skill(skill_id, x, y, c, s, all, SkillMod::on_play); } + if (attacked) { card->add_skill(skill_id, x, y, c, s, all, SkillMod::on_attacked); } + if (kill) { card->add_skill(skill_id, x, y, c, s, all, SkillMod::on_kill); } + if (died) { card->add_skill(skill_id, x, y, c, s, all, SkillMod::on_death); } + if (normal) { card->add_skill(skill_id, x, y, c, s, all); } } cards.cards.push_back(card); #if defined(TYRANT_UNLEASHED) From 67b56d5525289490511dbfdb7e3112ad378890dc Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 5 Jun 2014 08:53:21 +0800 Subject: [PATCH 222/406] Fix bug: passive skills of Mutants. --- card.h | 4 +- cards.cpp | 2 +- sim.cpp | 120 +++++++++++++++++++++++++++--------------------------- sim.h | 4 +- xml.cpp | 1 + 5 files changed, 66 insertions(+), 65 deletions(-) diff --git a/card.h b/card.h index 21cc4275..dbcdfdb5 100644 --- a/card.h +++ b/card.h @@ -27,7 +27,7 @@ class Card unsigned m_set; bool m_unique; std::vector m_skills[SkillMod::num_skill_activation_modifiers]; - unsigned m_x[SkillMod::num_skill_activation_modifiers][num_skills]; + unsigned m_skill_value[SkillMod::num_skill_activation_modifiers][num_skills]; CardType::CardType m_type; unsigned m_recipe_cost; std::map m_recipe_cards; @@ -57,7 +57,7 @@ class Card m_recipe_cards(), m_used_for_cards() { - std::memset(m_x, 0, sizeof m_x); + std::memset(m_skill_value, 0, sizeof m_skill_value); } void add_skill(Skill id, unsigned x, Faction y, unsigned c, Skill s, bool all, SkillMod::SkillMod mod=SkillMod::on_activate); diff --git a/cards.cpp b/cards.cpp index 2da67b4a..1b166295 100644 --- a/cards.cpp +++ b/cards.cpp @@ -170,6 +170,6 @@ void Card::add_skill(Skill id, unsigned x, Faction y, unsigned c, Skill s, bool } } m_skills[mod].push_back({id, x, y, c, s, all, mod}); - m_x[mod][id] = std::max(1u, x); + m_skill_value[mod][id] = std::max(1u, x); } diff --git a/sim.cpp b/sim.cpp index fcceaba4..11c8c23a 100644 --- a/sim.cpp +++ b/sim.cpp @@ -74,14 +74,14 @@ inline void Field::print_selection_array() #endif } //------------------------------------------------------------------------------ -inline bool CardStatus::has(Skill skill, SkillMod::SkillMod mod) const +inline bool CardStatus::has_skill(Skill skill, SkillMod::SkillMod mod) const { - return m_card->m_x[mod][skill]; + return m_card->m_skill_value[mod][skill]; } //------------------------------------------------------------------------------ -inline unsigned CardStatus::x(Skill skill, SkillMod::SkillMod mod) const +inline unsigned CardStatus::skill(Skill skill, SkillMod::SkillMod mod) const { - return m_card->m_x[mod][skill] + enhanced(skill); + return m_card->m_skill_value[mod][skill] + enhanced(skill); } //------------------------------------------------------------------------------ inline unsigned CardStatus::enhanced(Skill skill) const @@ -544,7 +544,7 @@ void resolve_skill(Field* fd) auto& enhanced_s = enhanced_value > 0 ? apply_enhance(skill, enhanced_value) : skill; auto& modified_s = enhanced_s; #else - bool fusion_active = status->has(fusion) && status->m_player == fd->tapi && fd->fusion_count >= 3; + bool fusion_active = status->has_skill(fusion) && status->m_player == fd->tapi && fd->fusion_count >= 3; auto& augmented_s = status->m_augmented > 0 ? apply_augment(status, skill) : skill; auto& fusioned_s = fusion_active ? apply_fusion(augmented_s) : augmented_s; auto& infused_s = status->m_infused ? apply_infuse(fusioned_s) : fusioned_s; @@ -678,7 +678,7 @@ void PlayCard::setStorage() template <> void PlayCard::performBlitz() { - if(status->has(blitz)) + if(status->has_skill(blitz)) { check_and_perform_blitz(fd, status); } @@ -776,7 +776,7 @@ Results play(Field* fd) #endif turn_start_phase(fd); // Special case: refresh on commander - if(fd->tip->commander.has(refresh)) + if(fd->tip->commander.has_skill(refresh)) { check_and_perform_refresh(fd, &fd->tip->commander); } @@ -981,7 +981,7 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return ref->has(flying); + return ref->has_skill(flying); } template<> @@ -1021,7 +1021,7 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return !ref->has(flying) && ref->x(antiair) == 0; + return !ref->has_skill(flying) && ref->skill(antiair) == 0; } // Not yet support on Attacked/on Death. @@ -1125,7 +1125,7 @@ inline void count_killed_achievements(Field* fd, const CardStatus* status) { fd->inc_counter(fd->achievement.unit_type_killed, status->m_card->m_type); } - if(status->has(flying)) + if(status->has_skill(flying)) { fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::unit_with_flying_killed); } @@ -1143,7 +1143,7 @@ void remove_hp(Field* fd, CardStatus& status, unsigned dmg) { fd->killed_with_on_death.push_back(&status); } - if(status.has(regenerate)) + if(status.has_skill(regenerate)) { fd->killed_with_regen.push_back(&status); } @@ -1188,7 +1188,7 @@ void check_regeneration(Field* fd) { count_achievement(fd, status); _DEBUG_MSG(1, "%s regenerates with %u health\n", status_description(status).c_str(), status->m_card->m_health); - add_hp(fd, status, status->x(regenerate)); + add_hp(fd, status, status->skill(regenerate)); } else { @@ -1236,7 +1236,7 @@ void turn_start_phase(Field* fd) _DEBUG_MSG(1, "%s takes poison damage\n", status_description(&status).c_str()); remove_hp(fd, status, status.m_poisoned); } - if(status.has(fusion) && status.m_delay == 0) { ++ fd->fusion_count; } + if(status.has_skill(fusion) && status.m_delay == 0) { ++ fd->fusion_count; } #endif } } @@ -1259,7 +1259,7 @@ void turn_start_phase(Field* fd) #if defined(TYRANT_UNLEASHED) cooldown_skills(status); #else - if(status.has(fusion) && status.m_delay == 0) { ++fd->fusion_count; } + if(status.has_skill(fusion) && status.m_delay == 0) { ++fd->fusion_count; } #endif } } @@ -1335,7 +1335,7 @@ void turn_end_phase(Field* fd) status.m_phased = false; if(status.m_stunned > 0) { -- status.m_stunned; } status.m_temporary_split = false; - if(status.has(refresh)) + if(status.has_skill(refresh)) { check_and_perform_refresh(fd, &status); } @@ -1356,7 +1356,7 @@ void turn_end_phase(Field* fd) { continue; } - if(status.has(refresh) && fd->effect != Effect::impenetrable) + if(status.has_skill(refresh) && fd->effect != Effect::impenetrable) { check_and_perform_refresh(fd, &status); } @@ -1416,7 +1416,7 @@ void evaluate_legion(Field* fd) for(fd->current_ci = 0; fd->current_ci < assaults.size(); ++fd->current_ci) { CardStatus* status(&assaults[fd->current_ci]); - unsigned legion_base = fd->effect == Effect::united_front ? status->m_card->m_delay : status->x(legion); + unsigned legion_base = fd->effect == Effect::united_front ? status->m_card->m_delay : status->skill(legion); if(legion_base == 0) { continue; @@ -1456,14 +1456,14 @@ inline unsigned counter_damage(Field* fd, CardStatus* att, CardStatus* def) { assert(att->m_card->m_type == CardType::assault); assert(def->m_card->m_type != CardType::action); - return(safe_minus(def->x(counter) + att->m_enfeebled, att->m_protected)); + return(safe_minus(def->skill(counter) + att->m_enfeebled, att->m_protected)); } inline CardStatus* select_first_enemy_wall(Field* fd) { for(unsigned i(0); i < fd->tip->structures.size(); ++i) { CardStatus& c(fd->tip->structures[i]); - if(c.has(wall) && c.m_hp > 0 && skill_check(fd, &c, nullptr)) + if(c.has_skill(wall) && c.m_hp > 0 && skill_check(fd, &c, nullptr)) { count_achievement(fd, &c); return(&c); @@ -1530,7 +1530,7 @@ struct PerformAttack // assaults only: (crush, leech if still alive) // check regeneration #if not defined(TYRANT_UNLEASHED) - if(def_status->has(flying) && (fd->effect == Effect::high_skies || fd->flip()) && skill_check(fd, def_status, att_status)) + if(def_status->has_skill(flying) && (fd->effect == Effect::high_skies || fd->flip()) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); _DEBUG_MSG(1, "%s attacks %s but it dodges with Flying\n", status_description(att_status).c_str(), status_description(def_status).c_str()); @@ -1547,7 +1547,7 @@ struct PerformAttack #if not defined(TYRANT_UNLEASHED) // If Impenetrable, prevent attack damage against walls, // but still activate Counter! - if(att_dmg > 0 && fd->effect == Effect::impenetrable && def_status->has(wall)) + if(att_dmg > 0 && fd->effect == Effect::impenetrable && def_status->has_skill(wall)) { _DEBUG_MSG(1, "%s is impenetrable\n", status_description(def_status).c_str()); att_dmg = 0; @@ -1567,7 +1567,7 @@ struct PerformAttack if(att_status->m_hp > 0) { #if not defined(TYRANT_UNLEASHED) - if(def_status->has(stun) && skill_check(fd, def_status, att_status)) + if(def_status->has_skill(stun) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); // perform_skill_stun @@ -1575,7 +1575,7 @@ struct PerformAttack att_status->m_stunned = 2; } #endif - if(def_status->has(counter) && skill_check(fd, def_status, att_status)) + if(def_status->has_skill(counter) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); // perform_skill_counter @@ -1583,7 +1583,7 @@ struct PerformAttack _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, *att_status, counter_dmg); } - unsigned berserk_value = att_status->x(berserk); + unsigned berserk_value = att_status->skill(berserk); if(berserk_value > 0 && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); @@ -1591,7 +1591,7 @@ struct PerformAttack att_status->m_berserk += berserk_value; } #if defined(TYRANT_UNLEASHED) - unsigned corrosive_value = def_status->x(corrosive); + unsigned corrosive_value = def_status->skill(corrosive); if (corrosive_value > att_status->m_corroded_rate && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); @@ -1626,21 +1626,21 @@ struct PerformAttack // enhance damage std::string desc; #if not defined(TYRANT_UNLEASHED) - unsigned valor_value = att_status->x(valor); + unsigned valor_value = att_status->skill(valor); if(valor_value > 0 && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); if(debug_print) { desc += "+" + to_string(valor_value) + "(valor)"; } att_dmg += valor_value; } - unsigned antiair_value = att_status->x(antiair); + unsigned antiair_value = att_status->skill(antiair); if(antiair_value > 0 && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); if(debug_print) { desc += "+" + to_string(antiair_value) + "(antiair)"; } att_dmg += antiair_value; } - unsigned burst_value = att_status->x(burst); + unsigned burst_value = att_status->skill(burst); if(burst_value > 0 && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); @@ -1656,7 +1656,7 @@ struct PerformAttack // prevent damage std::string reduced_desc; unsigned reduced_dmg(0); - unsigned armored_value = def_status->x(armored); + unsigned armored_value = def_status->skill(armored); if(armored_value == 0 && fd->effect == Effect::photon_shield && def_status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 0u : 1u)) { armored_value = 2; @@ -1664,7 +1664,7 @@ struct PerformAttack if(armored_value > 0) { // Armored counts if not totally cancelled by Pierce. TODO how if Armored + Proteced > Pierce? - if(armored_value > att_status->x(pierce)) + if(armored_value > att_status->skill(pierce)) { count_achievement(fd, def_status); } @@ -1676,11 +1676,11 @@ struct PerformAttack if(debug_print) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->m_protected) + "(protected)"; } reduced_dmg += def_status->m_protected; } - if(reduced_dmg > 0 && att_status->x(pierce) > 0) + if(reduced_dmg > 0 && att_status->skill(pierce) > 0) { // TODO No Pierce achievement yet, so no count_achievement(fd, att_status) - if(debug_print) { reduced_desc += "-" + to_string(att_status->x(pierce)) + "(pierce)"; } - reduced_dmg = safe_minus(reduced_dmg, att_status->x(pierce)); + if(debug_print) { reduced_desc += "-" + to_string(att_status->skill(pierce)) + "(pierce)"; } + reduced_dmg = safe_minus(reduced_dmg, att_status->skill(pierce)); } att_dmg = safe_minus(att_dmg, reduced_dmg); if(debug_print) @@ -1710,7 +1710,7 @@ struct PerformAttack template void on_attacked() { - unsigned poison_value = def_status->x(poison, SkillMod::on_attacked); + unsigned poison_value = def_status->skill(poison, SkillMod::on_attacked); if(poison_value > att_status->m_poisoned && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); @@ -1718,20 +1718,20 @@ struct PerformAttack _DEBUG_MSG(1, "%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), poison_value); att_status->m_poisoned = poison_value; } - if(def_status->has(disease, SkillMod::on_attacked) && skill_check(fd, def_status, att_status)) + if(def_status->has_skill(disease, SkillMod::on_attacked) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); // perform_skill_disease _DEBUG_MSG(1, "%s (on attacked) diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); att_status->m_diseased = true; } - unsigned berserk_value = def_status->x(berserk, SkillMod::on_attacked); + unsigned berserk_value = def_status->skill(berserk, SkillMod::on_attacked); if(def_status->m_hp > 0 && berserk_value > 0 && skill_check(fd, def_status, nullptr)) { count_achievement(fd, def_status); def_status->m_berserk += berserk_value; } - if(def_status->has(sunder, SkillMod::on_attacked) && skill_check(fd, def_status, att_status)) + if(def_status->has_skill(sunder, SkillMod::on_attacked) && skill_check(fd, def_status, att_status)) { count_achievement(fd, def_status); // perform_skill_sunder @@ -1748,7 +1748,7 @@ struct PerformAttack template<> void PerformAttack::performImmobilize() { - if(att_status->has(immobilize) && fd->flip() && skill_check(fd, att_status, def_status)) + if(att_status->has_skill(immobilize) && fd->flip() && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); _DEBUG_MSG(1, "%s immobilizes %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); @@ -1766,7 +1766,7 @@ template<> void PerformAttack::damage_dependant_pre_oa() { #if not defined(TYRANT_UNLEASHED) - unsigned siphon_value = std::min(att_dmg, att_status->x(siphon)); + unsigned siphon_value = std::min(att_dmg, att_status->skill(siphon)); if(siphon_value > 0 && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); @@ -1775,7 +1775,7 @@ void PerformAttack::damage_dependant_pre_oa() add_hp(fd, &fd->tap->commander, siphon_value); } #endif - unsigned poison_value = att_status->x(poison); + unsigned poison_value = att_status->skill(poison); if(poison_value > def_status->m_poisoned && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); @@ -1784,21 +1784,21 @@ void PerformAttack::damage_dependant_pre_oa() def_status->m_poisoned = poison_value; } #if not defined(TYRANT_UNLEASHED) - if(att_status->has(disease) && skill_check(fd, att_status, def_status)) + if(att_status->has_skill(disease) && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_disease _DEBUG_MSG(1, "%s diseases %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_diseased = true; } - if(att_status->has(sunder) && skill_check(fd, att_status, def_status)) + if(att_status->has_skill(sunder) && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_sunder _DEBUG_MSG(1, "%s sunders %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); def_status->m_sundered = true; } - if(att_status->has(phase) && skill_check(fd, att_status, def_status)) + if(att_status->has_skill(phase) && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); // perform_skill_phase @@ -1808,7 +1808,7 @@ void PerformAttack::damage_dependant_pre_oa() #endif #if defined(TYRANT_UNLEASHED) // XXX Assume inhibit stacks, if happened in future - unsigned inhibit_value = att_status->x(inhibit); + unsigned inhibit_value = att_status->skill(inhibit); if (inhibit_value > 0 && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); @@ -1831,7 +1831,7 @@ void PerformAttack::on_kill() template<> void PerformAttack::crush_leech() { - unsigned crush_value = att_status->x(crush); + unsigned crush_value = att_status->skill(crush); if(crush_value > 0 && killed_by_attack && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); @@ -1848,7 +1848,7 @@ void PerformAttack::crush_leech() remove_commander_hp(fd, fd->tip->commander, crush_value, true); } } - unsigned leech_value = std::min(att_dmg, att_status->x(leech)); + unsigned leech_value = std::min(att_dmg, att_status->skill(leech)); if(leech_value > 0 && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); @@ -1880,7 +1880,7 @@ bool attack_phase(Field* fd) return false; } unsigned num_attacks(1); - unsigned flurry_value = att_status->x(flurry); + unsigned flurry_value = att_status->skill(flurry); if(flurry_value > 0 && fd->flip() && skill_check(fd, att_status, nullptr)) { count_achievement(fd, att_status); @@ -1896,10 +1896,10 @@ bool attack_phase(Field* fd) // See http://www.kongregate.com/forums/65-tyrant/topics/289416?page=22#posts-6861970 // - 3. attack against the commander or walls (if there is no assault or if the attacker has the fear attribute) // Check if attack mode is 1. or 2. (there is a living assault card in front, and no fear) - if(alive_assault(def_assaults, fd->current_ci) && !(att_status->has(fear) && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) + if(alive_assault(def_assaults, fd->current_ci) && !(att_status->has_skill(fear) && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) { // attack mode 1. - if(!(att_status->has(swipe) && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) + if(!(att_status->has_skill(swipe) && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) { PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); } @@ -2033,7 +2033,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, assert(battleground_s.id != no_skill); if(battleground_s.id == s.s) { return(true); } } - return c->has(s.s) && (defensive_skills.find(s.s) != defensive_skills.end() || is_active(c)); + return c->has_skill(s.s) && (defensive_skills.find(s.s) != defensive_skills.end() || is_active(c)); } template<> @@ -2378,7 +2378,7 @@ CardStatus* select_interceptable(Field* fd, CardStatus* src_status, unsigned ind if(index > 0) { CardStatus* left_status(fd->selection_array[index - 1]); - if(left_status->has(intercept) && left_status->m_index == status->m_index - 1 && left_status->m_player == status->m_player && skill_check(fd, left_status, status)) + if(left_status->has_skill(intercept) && left_status->m_index == status->m_index - 1 && left_status->m_player == status->m_player && skill_check(fd, left_status, status)) { count_achievement(fd, left_status); _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(left_status).c_str(), status_description(status).c_str()); @@ -2388,7 +2388,7 @@ CardStatus* select_interceptable(Field* fd, CardStatus* src_status, unsigned ind if(index + 1 < fd->selection_array.size()) { CardStatus* right_status(fd->selection_array[index + 1]); - if(right_status->has(intercept) && right_status->m_index == status->m_index + 1 && right_status->m_player == status->m_player && skill_check(fd, right_status, status)) + if(right_status->has_skill(intercept) && right_status->m_index == status->m_index + 1 && right_status->m_player == status->m_player && skill_check(fd, right_status, status)) { count_achievement(fd, right_status); _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(right_status).c_str(), status_description(status).c_str()); @@ -2405,9 +2405,9 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ { if(is_evadable && #if defined(TYRANT_UNLEASHED) - dst_status->m_evaded < dst_status->x(evade) && + dst_status->m_evaded < dst_status->skill(evade) && #else - (dst_status->has(evade) || (fd->effect == Effect::quicksilver && dst_status->m_card->m_type == CardType::assault)) && fd->flip() && + (dst_status->has_skill(evade) || (fd->effect == Effect::quicksilver && dst_status->m_card->m_type == CardType::assault)) && fd->flip() && #endif skill_check(fd, dst_status, src_status)) { @@ -2501,7 +2501,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski // Count at most once even targeting "All" is_count_achievement = false; // Payback - if(c->has(payback) && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) + if(c->has_skill(payback) && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) { count_achievement(fd, c); _DEBUG_MSG(1, "%s paybacks (%s) on %s\n", status_description(c).c_str(), skill_short_description(s).c_str(), status_description(src_status).c_str()); @@ -2520,7 +2520,7 @@ inline void check_and_perform_emulate(Field* fd, CardStatus* src_status, CardSta if(hand->assaults.size() > opposite_status->m_index) { CardStatus& emulator = hand->assaults[opposite_status->m_index]; - if(emulator.has(emulate) && skill_predicate(fd, src_status, &emulator, s) && skill_check(fd, &emulator, nullptr)) + if(emulator.has_skill(emulate) && skill_predicate(fd, src_status, &emulator, s) && skill_check(fd, &emulator, nullptr)) { count_achievement(fd, &emulator); _DEBUG_MSG(1, "Emulate (%s) on %s\n", skill_short_description(s).c_str(), status_description(&emulator).c_str()); @@ -2571,7 +2571,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil // Count at most once even targeting "All" is_count_achievement = false; // Tribute - if(c->has(tribute) && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status)) + if(c->has_skill(tribute) && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status)) { count_achievement(fd, c); _DEBUG_MSG(1, "Tribute (%s) on %s\n", skill_short_description(s).c_str(), status_description(src_status).c_str()); @@ -2668,7 +2668,7 @@ void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) _DEBUG_MSG(1, "%s %s %s %u [%s]\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); prepend_skills(fd, &card_status); // Summon X (Genesis effect) does not activate Blitz for X - if(s.x != 0 && card_status.has(blitz)) + if(s.x != 0 && card_status.has_skill(blitz)) { check_and_perform_blitz(fd, &card_status); } @@ -2717,9 +2717,9 @@ void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) // but resolve_skill will handle those. if( #if defined(TYRANT_UNLEASHED) - c->m_evaded < c->x(evade) && + c->m_evaded < c->skill(evade) && #else - (c->has(evade) || (fd->effect == Effect::quicksilver && c->m_card->m_type == CardType::assault)) && fd->flip() && + (c->has_skill(evade) || (fd->effect == Effect::quicksilver && c->m_card->m_type == CardType::assault)) && fd->flip() && #endif skill_check(fd, c, src_status)) { diff --git a/sim.h b/sim.h index 6dac4efe..568c8e38 100644 --- a/sim.h +++ b/sim.h @@ -166,8 +166,8 @@ struct CardStatus void set(const Card* card); void set(const Card& card); std::string description(); - bool has(Skill skill, SkillMod::SkillMod mod=SkillMod::on_activate) const; - unsigned x(Skill skill, SkillMod::SkillMod mod=SkillMod::on_activate) const; + bool has_skill(Skill skill, SkillMod::SkillMod mod=SkillMod::on_activate) const; + unsigned skill(Skill skill, SkillMod::SkillMod mod=SkillMod::on_activate) const; unsigned enhanced(Skill skill) const; }; //------------------------------------------------------------------------------ diff --git a/xml.cpp b/xml.cpp index 861f7d58..35e5195c 100644 --- a/xml.cpp +++ b/xml.cpp @@ -218,6 +218,7 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) { card->m_skills[mod].clear(); } + memset(card->m_skill_value, 0, sizeof card->m_skill_value); } for(xml_node<>* skill_node = card_node->first_node("skill"); skill_node; From 54a365159bed8d5fcfe6283e7c3b217d646a4beb Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 22 Jun 2014 16:37:42 +0800 Subject: [PATCH 223/406] Extend hash encoding. --- deck.cpp | 209 +++++++++++++++++++++++++++++++++----------- deck.h | 19 +++- tyrant.h | 2 +- tyrant_optimize.cpp | 28 +++++- xml.cpp | 40 ++++----- 5 files changed, 219 insertions(+), 79 deletions(-) diff --git a/deck.cpp b/deck.cpp index 1aaa0113..bc2a6452 100644 --- a/deck.cpp +++ b/deck.cpp @@ -31,15 +31,15 @@ void partial_shuffle(RandomAccessIterator first, RandomAccessIterator middle, } //------------------------------------------------------------------------------ -namespace { const char* base64_chars = "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(const char* hash, std::vector& ids) +void hash_to_ids_wmt_b64(const char* hash, std::vector& ids) { unsigned int last_id = 0; const char* pc = hash; @@ -47,10 +47,11 @@ void hash_to_ids(const char* hash, std::vector& ids) while(*pc) { unsigned id_plus = 0; - if(*pc == '-') + const char* pmagic = strchr(wmt_b64_magic_chars, *pc); + if(pmagic) { ++ pc; - id_plus = 4000; + id_plus = 4000 * (pmagic - wmt_b64_magic_chars + 1); } if(!*pc || !*(pc + 1)) { @@ -80,6 +81,151 @@ void hash_to_ids(const char* hash, std::vector& ids) } } +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]; +} + +void encode_deck_wmt_b64(std::stringstream &ios, const Card* commander, std::vector cards) +{ + if (commander) + { + encode_id_wmt_b64(ios, commander->m_id); + } + 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); + 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]; +} + +void encode_deck_ext_b64(std::stringstream &ios, const Card* commander, std::vector cards) +{ + if (commander) + { + encode_id_ext_b64(ios, commander->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); + } +} + +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]; +} + +void encode_deck_ddd_b64(std::stringstream &ios, const Card* commander, std::vector cards) +{ + if (commander) + { + encode_id_ddd_b64(ios, commander->m_id); + } + for (const Card* card: cards) + { + encode_id_ddd_b64(ios, card->m_id); + } +} + +#if defined(TYRANT_UNLEASHED) +DeckDecoder hash_to_ids = hash_to_ids_wmt_b64; +DeckEncoder encode_deck = encode_deck_wmt_b64; +#else +DeckDecoder hash_to_ids = hash_to_ids_ext_b64; +DeckEncoder encode_deck = encode_deck_ext_b64; +#endif + const std::pair, std::map> string_to_ids(const Cards& all_cards, const std::string& deck_string, const std::string & description) { std::vector card_ids; @@ -129,8 +275,6 @@ const std::pair, std::map> string_to_ids(con return {card_ids, card_marks}; } -} // end of namespace - namespace range = boost::range; void Deck::set(const Cards& all_cards, const std::vector& ids, const std::map marks) @@ -195,58 +339,23 @@ void Deck::set_forts(const Cards& all_cards, const std::string& deck_string) } } -std::string Deck::hash() +std::string Deck::hash() const { - std::string base64= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; std::stringstream ios; - unsigned card_id = commander->m_id; - if(card_id > 4000) - { - ios << '-'; - card_id -= 4000; - } - ios << base64[card_id / 64]; - ios << base64[card_id % 64]; if (strategy == DeckStrategy::random) { - std::sort(cards.begin(), cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); - } - unsigned last_id = 0; - unsigned num_repeat = 0; - for(const Card* card: cards) - { - card_id = card->m_id; - if(card_id == last_id) - { - ++ num_repeat; - } - else - { - if(num_repeat > 1) - { - ios << base64[(num_repeat + 4000) / 64]; - ios << base64[(num_repeat + 4000) % 64]; - } - last_id = card_id; - num_repeat = 1; - if(card_id > 4000) - { - ios << '-'; - card_id -= 4000; - } - ios << base64[card_id / 64]; - ios << base64[card_id % 64]; - } + auto sorted_cards = cards; + std::sort(sorted_cards.begin(), sorted_cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); + encode_deck(ios, commander, sorted_cards); } - if(num_repeat > 1) + else { - ios << base64[(num_repeat + 4000) / 64]; - ios << base64[(num_repeat + 4000) % 64]; + encode_deck(ios, commander, cards); } return ios.str(); } -std::string Deck::short_description() +std::string Deck::short_description() const { std::stringstream ios; ios << decktype_names[decktype]; @@ -263,7 +372,7 @@ std::string Deck::short_description() return ios.str(); } -std::string Deck::medium_description() +std::string Deck::medium_description() const { std::stringstream ios; ios << short_description() << std::endl; @@ -293,7 +402,7 @@ std::string Deck::medium_description() extern std::string card_description(const Cards& cards, const Card* c); -std::string Deck::long_description(const Cards& all_cards) +std::string Deck::long_description(const Cards& all_cards) const { std::stringstream ios; ios << medium_description() << "\n"; diff --git a/deck.h b/deck.h index 45cf2734..2568b8fc 100644 --- a/deck.h +++ b/deck.h @@ -25,6 +25,17 @@ enum DeckStrategy num_deckstrategies }; } +typedef void (*DeckDecoder)(const char* hash, std::vector& ids); +typedef void (*DeckEncoder)(std::stringstream &ios, const Card* commander, std::vector cards); +void hash_to_ids_wmt_b64(const char* hash, std::vector& ids); +void encode_deck_wmt_b64(std::stringstream &ios, const Card* commander, std::vector cards); +void hash_to_ids_ext_b64(const char* hash, std::vector& ids); +void encode_deck_ext_b64(std::stringstream &ios, const Card* commander, std::vector cards); +void hash_to_ids_ddd_b64(const char* hash, std::vector& ids); +void encode_deck_ddd_b64(std::stringstream &ios, const Card* commander, std::vector cards); +extern DeckDecoder hash_to_ids; +extern DeckEncoder encode_deck; + //------------------------------------------------------------------------------ // No support for ordered raid decks struct Deck @@ -100,10 +111,10 @@ struct Deck } Deck* clone() const; - std::string hash(); - std::string short_description(); - std::string medium_description(); - std::string long_description(const Cards& all_cards); + std::string hash() const; + std::string short_description() const; + std::string medium_description() const; + std::string long_description(const Cards& all_cards) const; const Card* get_commander(); const Card* next(); void shuffle(std::mt19937& re); diff --git a/tyrant.h b/tyrant.h index 0872da7b..e024c4ad 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.2.7" +#define TYRANT_OPTIMIZER_VERSION "1.3.0" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 29be733c..64f38790 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1149,8 +1149,30 @@ int main(int argc, char** argv) print_available_decks(decks, true); return(4); } - std::string att_deck_name{argv[1]}; - auto && deck_list_parsed = parse_deck_list(argv[2], decks); + + int argIndex(1); + if (strcmp(argv[argIndex], "ext_b64") == 0) + { + hash_to_ids = hash_to_ids_ext_b64; + encode_deck = encode_deck_ext_b64; + argIndex += 1; + } + else if (strcmp(argv[argIndex], "wmt_b64") == 0) + { + hash_to_ids = hash_to_ids_wmt_b64; + encode_deck = encode_deck_wmt_b64; + argIndex += 1; + } + else if (strcmp(argv[argIndex], "ddd_b64") == 0) + { + hash_to_ids = hash_to_ids_ddd_b64; + encode_deck = encode_deck_ddd_b64; + argIndex += 1; + } + + std::string att_deck_name{argv[argIndex]}; + auto && deck_list_parsed = parse_deck_list(argv[argIndex + 1], decks); + argIndex += 2; Deck* att_deck{nullptr}; std::vector def_decks; @@ -1223,7 +1245,7 @@ int main(int argc, char** argv) effect_map[ss.str()] = static_cast(i); } - for(int argIndex(3); argIndex < argc; ++argIndex) + for(; argIndex < argc; ++argIndex) { if(strcmp(argv[argIndex], "win") == 0) { diff --git a/xml.cpp b/xml.cpp index 35e5195c..bcd3c352 100644 --- a/xml.cpp +++ b/xml.cpp @@ -158,12 +158,8 @@ void parse_file(const char* filename, std::vector& buffer, xml_document<>& void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) { xml_node<>* id_node(card_node->first_node("id")); - if(!id_node) - { - id_node = card_node->first_node("card_id"); - } - assert(id_node); - unsigned id(id_node ? atoi(id_node->value()) : 0); + xml_node<>* card_id_node = card_node->first_node("card_id"); + assert(id_node || card_id_node); xml_node<>* name_node(card_node->first_node("name")); xml_node<>* hidden_node(card_node->first_node("hidden")); xml_node<>* replace_node(card_node->first_node("replace")); @@ -185,20 +181,23 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) sets_counts[set]++; } #endif - card->m_id = id; - if(name_node) { card->m_name = name_node->value(); } - if(level_node) { card->m_level = atoi(level_node->value()); } - // So far, commanders have attack_node (value == 0) - if(id < 1000) - { card->m_type = CardType::assault; } - else if(id < 2000) - { card->m_type = CardType::commander; } - else if(id < 3000) - { card->m_type = CardType::structure; } - else if(id < 4000) - { card->m_type = CardType::action; } - else - { card->m_type = CardType::assault; } + if (id_node) { card->m_id = atoi(id_node->value()); } + else if (card_id_node) { card->m_id = atoi(card_id_node->value()); } + if (name_node) { card->m_name = name_node->value(); } + if (level_node) { card->m_level = atoi(level_node->value()); } + if (id_node) + { + if (card->m_id < 1000) + { card->m_type = CardType::assault; } + else if (card->m_id < 2000) + { card->m_type = CardType::commander; } + else if (card->m_id < 3000) + { card->m_type = CardType::structure; } + else if (card->m_id < 4000) + { card->m_type = CardType::action; } + else + { card->m_type = cost_node ? (attack_node ? CardType::assault : CardType::structure) : (health_node ? CardType::commander : CardType::action); } + } if(hidden_node) { card->m_hidden = atoi(hidden_node->value()); } if(replace_node) { card->m_replace = atoi(replace_node->value()); } if(attack_node) { card->m_attack = atoi(attack_node->value()); } @@ -509,7 +508,6 @@ void load_recipes_xml(Cards& cards) } //------------------------------------------------------------------------------ -extern unsigned turn_limit; Comparator get_comparator(xml_node<>* node, Comparator default_comparator) { xml_attribute<>* compare(node->first_attribute("compare")); From 0d98f366177fee6111d2b075823ad34ce5a8a5f7 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 25 Jun 2014 01:31:10 +0800 Subject: [PATCH 224/406] Support mission levels. --- card.h | 5 +- cards.cpp | 17 ++++-- deck.cpp | 83 ++++++++++++++++++++++++------ deck.h | 26 ++++++---- read.cpp | 32 ++++++------ tyrant.cpp | 5 ++ tyrant.h | 33 ++++++++++++ tyrant_optimize.cpp | 56 ++++++++++---------- xml.cpp | 123 +++++++++++++++++++++----------------------- 9 files changed, 239 insertions(+), 141 deletions(-) diff --git a/card.h b/card.h index dbcdfdb5..110a3528 100644 --- a/card.h +++ b/card.h @@ -14,7 +14,6 @@ class Card unsigned m_delay; bool m_disease_oa; Faction m_faction; - unsigned m_final_id; // The id of fully upgraded card unsigned m_health; unsigned m_hidden; unsigned m_id; @@ -29,6 +28,7 @@ class Card std::vector m_skills[SkillMod::num_skill_activation_modifiers]; unsigned m_skill_value[SkillMod::num_skill_activation_modifiers][num_skills]; CardType::CardType m_type; + const Card* m_top_level_card; // [TU] corresponding full-level card unsigned m_recipe_cost; std::map m_recipe_cards; std::map m_used_for_cards; @@ -40,7 +40,6 @@ class Card m_delay(0), m_disease_oa(false), m_faction(imperial), - m_final_id(0), m_health(0), m_hidden(0), m_id(0), @@ -53,6 +52,7 @@ class Card m_unique(false), m_skills(), m_type(CardType::assault), + m_top_level_card(this), m_recipe_cost(0), m_recipe_cards(), m_used_for_cards() @@ -61,6 +61,7 @@ class Card } void add_skill(Skill id, unsigned x, Faction y, unsigned c, Skill s, bool all, SkillMod::SkillMod mod=SkillMod::on_activate); + const Card* upgraded() const { return this == m_top_level_card ? this : m_used_for_cards.begin()->first; } }; #endif diff --git a/cards.cpp b/cards.cpp index 1b166295..68f4a512 100644 --- a/cards.cpp +++ b/cards.cpp @@ -26,7 +26,7 @@ std::string simplify_name(const std::string& card_name) std::list get_abbreviations(const std::string& name) { std::list abbr_list; - boost::tokenizer> word_token{name, boost::char_delimiters_separator{false, " ", ""}}; + boost::tokenizer> word_token{name, boost::char_delimiters_separator{false, " -", ""}}; std::string initial; auto token_iter = word_token.begin(); for(; token_iter != word_token.end(); ++token_iter) @@ -66,9 +66,14 @@ void Cards::organize() player_assaults.clear(); player_structures.clear(); player_actions.clear(); + // Round 1: set cards_by_id + for(Card* card: cards) + { + cards_by_id[card->m_id] = card; + } + // Round 2: depend on cards_by_id / by_id(); update m_name, [TU] m_top_level_card etc.; set player_cards_by_name; for(Card* card: cards) { -// std::cout << "C:" << card->m_id << "\n"; // Remove delimiters from card names size_t pos; while((pos = card->m_name.find_first_of(";:,")) != std::string::npos) @@ -76,7 +81,10 @@ void Cards::organize() card->m_name.erase(pos, 1); } #if defined(TYRANT_UNLEASHED) - if (card->m_level > 1 && card->m_id == by_id(card->m_base_id)->m_final_id) + // set m_top_level_card for non base cards + card->m_top_level_card = by_id(card->m_base_id)->m_top_level_card; + // add a suffix of level to the name of cards; register as alias for the full-level cards (the formal name is without suffix) + if (card == card->m_top_level_card) { player_cards_by_name[{simplify_name(card->m_name + "-" + to_string(card->m_level)), card->m_hidden}] = card; } @@ -90,7 +98,6 @@ void Cards::organize() card->m_name += '*'; } #endif - cards_by_id[card->m_id] = card; // Card available to players if(card->m_set != 0) { @@ -126,6 +133,7 @@ void Cards::organize() } } } + // Round 3: depend on player_cards_by_name; set abbreviations and [WMT] recipes for(Card* card: cards) { // generate abbreviations @@ -139,7 +147,6 @@ void Cards::organize() } } } - #if not defined(TYRANT_UNLEASHED) // update recipes if(card->m_set == 5002) diff --git a/deck.cpp b/deck.cpp index bc2a6452..b478086f 100644 --- a/deck.cpp +++ b/deck.cpp @@ -219,11 +219,11 @@ void encode_deck_ddd_b64(std::stringstream &ios, const Card* commander, std::vec } #if defined(TYRANT_UNLEASHED) -DeckDecoder hash_to_ids = hash_to_ids_wmt_b64; -DeckEncoder encode_deck = encode_deck_wmt_b64; -#else DeckDecoder hash_to_ids = hash_to_ids_ext_b64; DeckEncoder encode_deck = encode_deck_ext_b64; +#else +DeckDecoder hash_to_ids = hash_to_ids_wmt_b64; +DeckEncoder encode_deck = encode_deck_wmt_b64; #endif const std::pair, std::map> string_to_ids(const Cards& all_cards, const std::string& deck_string, const std::string & description) @@ -277,7 +277,7 @@ const std::pair, std::map> string_to_ids(con namespace range = boost::range; -void Deck::set(const Cards& all_cards, const std::vector& ids, const std::map marks) +void Deck::set(const std::vector& ids, const std::map marks) { commander = nullptr; strategy = DeckStrategy::random; @@ -307,29 +307,29 @@ void Deck::set(const Cards& all_cards, const std::vector& ids, const s card_marks = marks; } -void Deck::set(const Cards& all_cards, const std::string& deck_string_) +void Deck::set(const std::string& deck_string_) { deck_string = deck_string_; } -void Deck::resolve(const Cards& all_cards) +void Deck::resolve() { if(commander != nullptr) { return; } auto && id_marks = string_to_ids(all_cards, deck_string, short_description()); - set(all_cards, id_marks.first, id_marks.second); + set(id_marks.first, id_marks.second); deck_string.clear(); } -void Deck::set_given_hand(const Cards& all_cards, const std::string& deck_string) +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; } -void Deck::set_forts(const Cards& all_cards, const std::string& deck_string) +void Deck::set_forts(const std::string& deck_string) { auto && id_marks = string_to_ids(all_cards, deck_string, "fort_cards"); fort_cards.clear(); @@ -379,6 +379,10 @@ std::string Deck::medium_description() const if(commander) { ios << commander->m_name; + if (upgrade_chance > 0 && commander != commander->m_top_level_card) + { + ios << "+"; + } } else { @@ -387,6 +391,10 @@ std::string Deck::medium_description() const for(const Card * card: cards) { ios << ", " << card->m_name; + if (upgrade_chance > 0 && card != card->m_top_level_card) + { + ios << "+"; + } } unsigned num_pool_cards = 0; for(auto& pool: raid_cards) @@ -400,9 +408,9 @@ std::string Deck::medium_description() const return ios.str(); } -extern std::string card_description(const Cards& cards, const Card* c); +extern std::string card_description(const Cards& all_cards, const Card* c); -std::string Deck::long_description(const Cards& all_cards) const +std::string Deck::long_description() const { std::stringstream ios; ios << medium_description() << "\n"; @@ -413,6 +421,10 @@ std::string Deck::long_description(const Cards& all_cards) const if(commander) { ios << card_description(all_cards, commander) << "\n"; + if (upgrade_chance > 0 && commander != commander->m_top_level_card) + { + ios << "->" << card_description(all_cards, commander->m_top_level_card) << "\n"; + } } else { @@ -421,6 +433,10 @@ std::string Deck::long_description(const Cards& all_cards) const for(const Card* card: cards) { ios << " " << card_description(all_cards, card) << "\n"; + if (upgrade_chance > 0 && card != card->m_top_level_card) + { + ios << " ->" << card_description(all_cards, card->m_top_level_card) << "\n"; + } } for(auto& pool: raid_cards) { @@ -428,6 +444,10 @@ std::string Deck::long_description(const Cards& all_cards) const for(auto& card: pool.second) { ios << " " << card_description(all_cards, card) << "\n"; + if (upgrade_chance > 0 && card != card->m_top_level_card) + { + ios << " ->" << card_description(all_cards, card->m_top_level_card) << "\n"; + } } } if (! reward_cards.empty()) @@ -447,12 +467,6 @@ Deck* Deck::clone() const return(new Deck(*this)); } - -const Card* Deck::get_commander() -{ - return(commander); -} - const Card* Deck::next() { if(shuffled_cards.empty()) @@ -499,10 +513,45 @@ const Card* Deck::next() throw std::runtime_error("Unknown strategy for deck."); } +const Card* Deck::upgrade_card(const Card* card, std::mt19937& re) +{ + unsigned ups = card->m_top_level_card->m_level - card->m_level; + if (upgrade_chance > 0 && ups > 0) + { + for (std::mt19937::result_type rnd = re(); ups > 0; -- ups, rnd /= 9) + { + if (rnd % 9 < upgrade_chance) + { + card = card->upgraded(); + } + } + } + return card; +} + +const Card* Deck::get_commander(std::mt19937& re) +{ + if (upgrade_chance == 0) + { + return(commander); + } + else + { + return(upgrade_card(commander, re)); + } +} + void Deck::shuffle(std::mt19937& re) { shuffled_cards.clear(); boost::insert(shuffled_cards, shuffled_cards.end(), cards); + if (upgrade_chance > 0) + { + for (auto && card: shuffled_cards) + { + card = upgrade_card(card, re); + } + } if(!raid_cards.empty()) { if(strategy != DeckStrategy::random) diff --git a/deck.h b/deck.h index 2568b8fc..3eb75578 100644 --- a/deck.h +++ b/deck.h @@ -40,11 +40,13 @@ extern DeckEncoder encode_deck; // No support for ordered raid decks struct Deck { + const Cards& all_cards; DeckType::DeckType decktype; unsigned id; std::string name; - DeckStrategy::DeckStrategy strategy; Effect effect; // for quests + unsigned upgrade_chance; // n/9 chance to upgrade; = level - 1 for level 1 to 9 and 0 for level 10 + DeckStrategy::DeckStrategy strategy; const Card* commander; std::vector cards; @@ -62,15 +64,20 @@ struct Deck std::vector fort_cards; Deck( + const Cards& all_cards_, DeckType::DeckType decktype_ = DeckType::deck, unsigned id_ = 0, std::string name_ = "", + Effect effect_ = Effect::none, + unsigned upgrade_chance_ = 0, DeckStrategy::DeckStrategy strategy_ = DeckStrategy::random) : + all_cards(all_cards_), decktype(decktype_), id(id_), name(name_), - strategy(strategy_), effect(Effect::none), + upgrade_chance(upgrade_chance_), + strategy(strategy_), commander(nullptr), mission_req(0) { @@ -92,11 +99,11 @@ struct Deck mission_req = mission_req_; } - void set(const Cards& all_cards, const std::vector& ids, const std::map marks = {}); - void set(const Cards& all_cards, const std::string& deck_string_); - void resolve(const Cards& all_cards); - void set_given_hand(const Cards& all_cards, const std::string& deck_string_); - void set_forts(const Cards& all_cards, const std::string& deck_string_); + void set(const std::vector& ids, const std::map marks = {}); + void set(const std::string& deck_string_); + void resolve(); + void set_given_hand(const std::string& deck_string_); + void set_forts(const std::string& deck_string_); template Container card_ids() const @@ -114,9 +121,10 @@ struct Deck std::string hash() const; std::string short_description() const; std::string medium_description() const; - std::string long_description(const Cards& all_cards) const; - const Card* get_commander(); + std::string long_description() const; const Card* next(); + const Card* upgrade_card(const Card* card, std::mt19937& re); + const Card* get_commander(std::mt19937& re); void shuffle(std::mt19937& re); void place_at_bottom(const Card* card); }; diff --git a/read.cpp b/read.cpp index 882295e5..5d3ac207 100644 --- a/read.cpp +++ b/read.cpp @@ -16,11 +16,11 @@ #include "cards.h" #include "deck.h" -void load_decks(Decks& decks, Cards& cards) +void load_decks(Decks& decks, Cards& all_cards) { if(boost::filesystem::exists("Custom.txt")) { - read_custom_decks(decks, cards, "Custom.txt"); + read_custom_decks(decks, all_cards, "Custom.txt"); } } @@ -161,7 +161,7 @@ DeckList parse_deck_list(std::string list_string, const Decks& decks) return res; } -void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark) +void parse_card_spec(const Cards& all_cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark) { // static std::set recognized_abbr; auto card_spec_iter = card_spec.begin(); @@ -182,8 +182,8 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ } // If card name is not found, try find card id quoted in '[]' in name, ignoring other characters. std::string simple_name{simplify_name(card_name)}; - const auto && abbr_it = cards.player_cards_abbr.find(simple_name); - if(abbr_it != cards.player_cards_abbr.end()) + const auto && abbr_it = all_cards.player_cards_abbr.find(simple_name); + if(abbr_it != all_cards.player_cards_abbr.end()) { // if(recognized_abbr.count(card_name) == 0) // { @@ -192,10 +192,10 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ // } simple_name = simplify_name(abbr_it->second); } - auto card_it = cards.player_cards_by_name.find({simple_name, 0}); - if (card_it == cards.player_cards_by_name.end()) { card_it = cards.player_cards_by_name.find({simple_name, 1}); } + auto card_it = all_cards.player_cards_by_name.find({simple_name, 0}); + if (card_it == all_cards.player_cards_by_name.end()) { card_it = all_cards.player_cards_by_name.find({simple_name, 1}); } auto card_id_iter = advance_until(simple_name.begin(), simple_name.end(), [](char c){return(c=='[');}); - if(card_it != cards.player_cards_by_name.end()) + if(card_it != all_cards.player_cards_by_name.end()) { card_id = card_it->second->m_id; } @@ -223,7 +223,7 @@ void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_ } } -unsigned read_card_abbrs(Cards& cards, const std::string& filename) +unsigned read_card_abbrs(Cards& all_cards, const std::string& filename) { if(!boost::filesystem::exists(filename)) { @@ -256,13 +256,13 @@ unsigned read_card_abbrs(Cards& cards, const std::string& filename) continue; } abbr_string_iter = advance_until(abbr_string_iter + 1, abbr_string.end(), [](const char& c){return(c != ' ');}); - if(cards.player_cards_by_name.find({abbr_name, 0}) != cards.player_cards_by_name.end()) + if(all_cards.player_cards_by_name.find({abbr_name, 0}) != all_cards.player_cards_by_name.end()) { std::cerr << "Warning in card abbreviation file " << filename << " at line " << num_line << ": ignored because the name has been used by an existing card." << std::endl; } else { - cards.player_cards_abbr[abbr_name] = std::string{abbr_string_iter, abbr_string.end()}; + all_cards.player_cards_abbr[abbr_name] = std::string{abbr_string_iter, abbr_string.end()}; } } } @@ -283,7 +283,7 @@ unsigned read_card_abbrs(Cards& cards, const std::string& filename) // Error codes: // 2 -> file not readable // 3 -> error while parsing file -unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) +unsigned read_custom_decks(Decks& decks, Cards& all_cards, std::string filename) { std::ifstream decks_file(filename); if(!decks_file.is_open()) @@ -317,9 +317,9 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) { std::cerr << "Warning in custom deck file " << filename << " at line " << num_line << ", name conflicts, overrides " << deck_iter->second->short_description() << std::endl; } - decks.decks.push_back(Deck{DeckType::custom_deck, num_line, deck_name}); + decks.decks.push_back(Deck{all_cards, DeckType::custom_deck, num_line, deck_name}); Deck* deck = &decks.decks.back(); - deck->set(cards, std::string{deck_string_iter, deck_string.end()}); + deck->set(std::string{deck_string_iter, deck_string.end()}); decks.by_name[deck_name] = deck; std::stringstream alt_name; alt_name << decktype_names[deck->decktype] << " #" << deck->id; @@ -339,7 +339,7 @@ unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename) return(0); } -void read_owned_cards(Cards& cards, std::map& owned_cards, std::map& buyable_cards, const char *filename) +void read_owned_cards(Cards& all_cards, std::map& owned_cards, std::map& buyable_cards, const char *filename) { std::ifstream owned_file{filename}; if(!owned_file.good()) @@ -363,7 +363,7 @@ void read_owned_cards(Cards& cards, std::map& owned_cards, s unsigned card_num{1}; char num_sign{0}; char mark{0}; - parse_card_spec(cards, card_spec, card_id, card_num, num_sign, mark); + parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign, mark); assert(mark == 0); if(num_sign == 0) { diff --git a/tyrant.cpp b/tyrant.cpp index bc5cce17..6e3f0ead 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -89,3 +89,8 @@ std::string achievement_misc_req_names[AchievementMiscReq::num_achievement_misc_ "Damage", "Total damage to the enemy Commander" }; + +unsigned debug_print(0); +unsigned debug_cached(0); +bool debug_line(false); +std::string debug_str(""); diff --git a/tyrant.h b/tyrant.h index e024c4ad..a7797863 100644 --- a/tyrant.h +++ b/tyrant.h @@ -193,4 +193,37 @@ std::string to_string(const T val) return s.str(); } +//---------------------- Debugging stuff --------------------------------------- +extern unsigned debug_print; +extern unsigned debug_cached; +extern bool debug_line; +extern std::string debug_str; +#ifndef NDEBUG +#define _DEBUG_MSG(v, format, args...) \ + { \ + if(__builtin_expect(debug_print >= v, false)) \ + { \ + if(debug_line) { printf("%i - " format, __LINE__ , ##args); } \ + else if(debug_cached) { \ + char buf[4096]; \ + snprintf(buf, sizeof(buf), format, ##args); \ + debug_str += buf; \ + } \ + else { printf(format, ##args); } \ + std::cout << std::flush; \ + } \ + } +#define _DEBUG_SELECTION(format, args...) \ + { \ + if(__builtin_expect(debug_print >= 2, 0)) \ + { \ + _DEBUG_MSG(2, "Possible targets of " format ":\n", ##args); \ + fd->print_selection_array(); \ + } \ + } +#else +#define _DEBUG_MSG(v, format, args...) +#define _DEBUG_SELECTION(format, args...) +#endif + #endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 64f38790..d1c1152e 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -89,18 +89,18 @@ std::string card_slot_id_names(const std::vector return ios.str(); } //------------------------------------------------------------------------------ -Deck* find_deck(Decks& decks, const Cards& cards, std::string deck_name) +Deck* find_deck(Decks& decks, const Cards& all_cards, std::string deck_name) { auto it = decks.by_name.find(deck_name); if(it != decks.by_name.end()) { - it->second->resolve(cards); + it->second->resolve(); return(it->second); } - decks.decks.push_back(Deck{}); + decks.decks.push_back(Deck{all_cards}); Deck* deck = &decks.decks.back(); - deck->set(cards, deck_name); - deck->resolve(cards); + deck->set(deck_name); + deck->resolve(); return(deck); } //---------------------- $80 deck optimization --------------------------------- @@ -124,11 +124,11 @@ unsigned get_required_cards_before_upgrade(const std::vector & car num_cards[card] = owned_cards[card->m_id]; // std::cout << "-" << num_under << " " << card->m_name << "\n"; // XXX deck_cost += num_under * card->m_recipe_cost; - for (auto material_it : card->m_recipe_cards) + for (auto recipe_it : card->m_recipe_cards) { - num_cards[material_it.first] += num_under * material_it.second; -// std::cout << "+" << num_under * material_it.second << " " << material_it.first->m_name << "\n"; // XXX - unresolved_cards.insert(material_it.first); + num_cards[recipe_it.first] += num_under * recipe_it.second; +// std::cout << "+" << num_under * recipe_it.second << " " << recipe_it.first->m_name << "\n"; // XXX + unresolved_cards.insert(recipe_it.first); } } } @@ -272,7 +272,7 @@ void claim_cards(const std::vector & card_list, bool claim_all, boo unsigned num_to_claim = safe_minus(it.second, owned_cards[card->m_id]); if (is_reward) { - for (const auto & it : card->m_used_for_cards) + for (const auto & it : card->m_used_for_cards) // low-priority FIXME: upgraded several times { num_to_claim = safe_minus(num_to_claim, owned_cards[it.first->m_id] * it.second); } @@ -1132,15 +1132,15 @@ int main(int argc, char** argv) unsigned num_threads = 4; DeckStrategy::DeckStrategy att_strategy(DeckStrategy::random); DeckStrategy::DeckStrategy def_strategy(DeckStrategy::random); - Cards cards; - read_cards(cards); - read_card_abbrs(cards, "cardabbrs.txt"); + Cards all_cards; + read_cards(all_cards); + read_card_abbrs(all_cards, "cardabbrs.txt"); Decks decks; Achievement achievement; - load_decks_xml(decks, cards); - load_decks(decks, cards); + load_decks_xml(decks, all_cards); + load_decks(decks, all_cards); #if defined(TYRANT_UNLEASHED) - load_recipes_xml(cards); + load_recipes_xml(all_cards); #endif fill_skill_table(); @@ -1186,7 +1186,7 @@ int main(int argc, char** argv) try { - att_deck = find_deck(decks, cards, att_deck_name); + att_deck = find_deck(decks, all_cards, att_deck_name); } catch(const std::runtime_error& e) { @@ -1213,7 +1213,7 @@ int main(int argc, char** argv) Deck* def_deck{nullptr}; try { - def_deck = find_deck(decks, cards, deck_parsed.first); + def_deck = find_deck(decks, all_cards, deck_parsed.first); } catch(const std::runtime_error& e) { @@ -1265,7 +1265,7 @@ int main(int argc, char** argv) { try { - read_achievement(decks, cards, achievement, argv[argIndex + 1]); + read_achievement(decks, all_cards, achievement, argv[argIndex + 1]); optimization_mode = OptimizationMode::achievement; } catch(const std::runtime_error& e) @@ -1329,12 +1329,12 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "-o") == 0) { - read_owned_cards(cards, owned_cards, buyable_cards, "ownedcards.txt"); + read_owned_cards(all_cards, owned_cards, buyable_cards, "ownedcards.txt"); use_owned_cards = true; } else if(strncmp(argv[argIndex], "-o=", 3) == 0) { - read_owned_cards(cards, owned_cards, buyable_cards, argv[argIndex] + 3); + read_owned_cards(all_cards, owned_cards, buyable_cards, argv[argIndex] + 3); use_owned_cards = true; } else if(strncmp(argv[argIndex], "-o+", 3) == 0) @@ -1414,27 +1414,27 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "hand") == 0) // set initial hand for test { - att_deck->set_given_hand(cards, argv[argIndex + 1]); + att_deck->set_given_hand(argv[argIndex + 1]); argIndex += 1; } else if(strcmp(argv[argIndex], "defender:hand") == 0) // set enemies' initial hand for test { for(auto def_deck: def_decks) { - def_deck->set_given_hand(cards, argv[argIndex + 1]); + def_deck->set_given_hand(argv[argIndex + 1]); } argIndex += 1; } else if(strcmp(argv[argIndex], "forts") == 0) // set forts { - att_deck->set_forts(cards, argv[argIndex + 1]); + att_deck->set_forts(argv[argIndex + 1]); argIndex += 1; } else if(strcmp(argv[argIndex], "defender:forts") == 0) // set enemies' forts { for(auto def_deck: def_decks) { - def_deck->set_forts(cards, argv[argIndex + 1]); + def_deck->set_forts(argv[argIndex + 1]); } argIndex += 1; } @@ -1494,17 +1494,17 @@ int main(int argc, char** argv) min_deck_len = max_deck_len = att_deck->cards.size(); } - std::cout << "Your Deck: " << (debug_print ? att_deck->long_description(cards) : att_deck->medium_description()) << std::endl; + std::cout << "Your Deck: " << (debug_print ? att_deck->long_description() : att_deck->medium_description()) << std::endl; for (unsigned i(0); i < def_decks.size(); ++i) { - std::cout << "Enemy's Deck:" << def_decks_factors[i] << ": " << (debug_print ? def_decks[i]->long_description(cards) : def_decks[i]->medium_description()) << std::endl; + std::cout << "Enemy's Deck:" << def_decks_factors[i] << ": " << (debug_print ? def_decks[i]->long_description() : def_decks[i]->medium_description()) << std::endl; } if(effect != Effect::none) { std::cout << "Effect: " << effect_names[effect] << std::endl; } - Process p(num_threads, cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, bg_enhanced_skill, bg_enhanced_value, achievement); + Process p(num_threads, all_cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, bg_enhanced_skill, bg_enhanced_value, achievement); { //ScopeClock timer; diff --git a/xml.cpp b/xml.cpp index bcd3c352..4ecc8591 100644 --- a/xml.cpp +++ b/xml.cpp @@ -93,11 +93,11 @@ Skill skill_target_skill(xml_node<>* skill) } //------------------------------------------------------------------------------ -void load_decks_xml(Decks& decks, const Cards& cards) +void load_decks_xml(Decks& decks, const Cards& all_cards) { try { - read_missions(decks, cards, "missions.xml"); + read_missions(decks, all_cards, "missions.xml"); } catch(const rapidxml::parse_error& e) { @@ -106,7 +106,7 @@ void load_decks_xml(Decks& decks, const Cards& cards) #if not defined(TYRANT_UNLEASHED) try { - read_raids(decks, cards, "raids.xml"); + read_raids(decks, all_cards, "raids.xml"); } catch(const rapidxml::parse_error& e) { @@ -114,7 +114,7 @@ void load_decks_xml(Decks& decks, const Cards& cards) } try { - read_quests(decks, cards, "quests.xml"); + read_quests(decks, all_cards, "quests.xml"); } catch(const rapidxml::parse_error& e) { @@ -155,7 +155,7 @@ void parse_file(const char* filename, std::vector& buffer, xml_document<>& } } //------------------------------------------------------------------------------ -void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) +void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) { xml_node<>* id_node(card_node->first_node("id")); xml_node<>* card_id_node = card_node->first_node("card_id"); @@ -181,7 +181,7 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) sets_counts[set]++; } #endif - if (id_node) { card->m_id = atoi(id_node->value()); } + if (id_node) { card->m_base_id = card->m_id = atoi(id_node->value()); } else if (card_id_node) { card->m_id = atoi(card_id_node->value()); } if (name_node) { card->m_name = name_node->value(); } if (level_node) { card->m_level = atoi(level_node->value()); } @@ -206,7 +206,6 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) if(unique_node) { card->m_unique = true; } if(reserve_node) { card->m_reserve = atoi(reserve_node->value()); } if(base_card_node) { card->m_base_id = atoi(base_card_node->value()); } - else if(card->m_base_id == 0) { card->m_base_id = card->m_id; } if(rarity_node) { card->m_rarity = atoi(rarity_node->value()); } if(type_node) { card->m_faction = map_to_faction(atoi(type_node->value())); } card->m_set = set; @@ -244,7 +243,7 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) if (died) { card->add_skill(skill_id, x, y, c, s, all, SkillMod::on_death); } if (normal) { card->add_skill(skill_id, x, y, c, s, all); } } - cards.cards.push_back(card); + all_cards.cards.push_back(card); #if defined(TYRANT_UNLEASHED) Card * top_card = card; for(xml_node<>* upgrade_node = card_node->first_node("upgrade"); @@ -253,7 +252,7 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) { Card * pre_upgraded_card = top_card; top_card = new Card(*top_card); - parse_card_node(cards, top_card, upgrade_node); + parse_card_node(all_cards, top_card, upgrade_node); if (top_card->m_type == CardType::commander) { // Commanders cost twice and cannot be salvaged. @@ -268,11 +267,11 @@ void parse_card_node(Cards& cards, Card* card, xml_node<>* card_node) top_card->m_recipe_cards[pre_upgraded_card] = 1; pre_upgraded_card->m_used_for_cards[top_card] = 1; } - card->m_final_id = top_card->m_id; + card->m_top_level_card = top_card; #endif } -void read_cards(Cards& cards) +void read_cards(Cards& all_cards) { std::vector buffer; xml_document<> doc; @@ -292,9 +291,9 @@ void read_cards(Cards& cards) card_node = card_node->next_sibling("unit")) { auto card = new Card(); - parse_card_node(cards, card, card_node); + parse_card_node(all_cards, card, card_node); } - cards.organize(); + all_cards.organize(); #if 0 std::cout << "nb cards: " << nb_cards << "\n"; for(auto counts: sets_counts) @@ -304,14 +303,11 @@ void read_cards(Cards& cards) #endif } //------------------------------------------------------------------------------ -Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::DeckType decktype, unsigned id, std::string deck_name, unsigned level=1) +Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const char* effect_node_name, DeckType::DeckType decktype, unsigned id, std::string base_deck_name, bool has_levels=false) { xml_node<>* commander_node(node->first_node("commander")); unsigned card_id = atoi(commander_node->value()); -#if defined(TYRANT_UNLEASHED) - if(level == 10) { card_id = cards.by_id(cards.by_id(card_id)->m_base_id)->m_final_id; } -#endif - const Card* commander_card{cards.by_id(card_id)}; + const Card* commander_card{all_cards.by_id(card_id)}; std::vector always_cards; std::vector>> some_cards; std::vector reward_cards; @@ -322,10 +318,7 @@ Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::De card_node = card_node->next_sibling("card")) { card_id = atoi(card_node->value()); -#if defined(TYRANT_UNLEASHED) - if(level == 10) { card_id = cards.by_id(cards.by_id(card_id)->m_base_id)->m_final_id; } -#endif - always_cards.push_back(cards.by_id(card_id)); + always_cards.push_back(all_cards.by_id(card_id)); } for(xml_node<>* pool_node = deck_node->first_node("card_pool"); pool_node; @@ -339,7 +332,7 @@ Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::De card_node = card_node->next_sibling("card")) { unsigned card_id(atoi(card_node->value())); - cards_from_pool.push_back(cards.by_id(card_id)); + cards_from_pool.push_back(all_cards.by_id(card_id)); } some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); } @@ -351,29 +344,52 @@ Deck* read_deck(Decks& decks, const Cards& cards, xml_node<>* node, DeckType::De card_node = card_node->next_sibling("card")) { unsigned card_id(atoi(card_node->value())); - reward_cards.push_back(cards.by_id(card_id)); + reward_cards.push_back(all_cards.by_id(card_id)); } } xml_node<>* mission_req_node(node->first_node(decktype == DeckType::mission ? "req" : "mission_req")); unsigned mission_req(mission_req_node ? atoi(mission_req_node->value()) : 0); + xml_node<>* effect_id_node(node->first_node(effect_node_name)); + Effect effect = effect_id_node ? static_cast(atoi(effect_id_node->value())) : Effect::none; #if defined(TYRANT_UNLEASHED) - if (level < 10) { deck_name += "-" + to_string(level); } + if (has_levels) + { + for (unsigned level = 1; level <= 9; ++ level) + { + std::string deck_name = base_deck_name + "-" + to_string(level); + decks.decks.push_back(Deck{all_cards, decktype, id, deck_name, effect, level - 1}); + Deck* deck = &decks.decks.back(); + deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); + std::string alt_name = decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(level); + decks.by_name[deck_name] = deck; + decks.by_name[alt_name] = deck; + } + } #endif - decks.decks.push_back(Deck{decktype, id, deck_name}); + decks.decks.push_back(Deck{all_cards, decktype, id, base_deck_name, effect}); Deck* deck = &decks.decks.back(); deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); - decks.by_type_id[{decktype, id}] = deck; - decks.by_name[deck_name] = deck; - std::stringstream alt_name; - alt_name << decktype_names[decktype] << " #" << id; #if defined(TYRANT_UNLEASHED) - if (level < 10) { alt_name << "-" << level; } + if (has_levels) + { // upgrade cards in deck + deck->commander = deck->commander->m_top_level_card; + for (auto && card: deck->cards) + { card = card->m_top_level_card; } + for (auto && pool: deck->raid_cards) + { + for (auto && card: pool.second) + { card = card->m_top_level_card; } + } + } #endif - decks.by_name[alt_name.str()] = deck; + std::string alt_name = decktype_names[decktype] + " #" + to_string(id); + decks.by_name[base_deck_name] = deck; + decks.by_name[alt_name] = deck; + decks.by_type_id[{decktype, id}] = deck; return deck; } //------------------------------------------------------------------------------ -void read_missions(Decks& decks, const Cards& cards, std::string filename) +void read_missions(Decks& decks, const Cards& all_cards, std::string filename) { std::vector buffer; xml_document<> doc; @@ -395,23 +411,11 @@ void read_missions(Decks& decks, const Cards& cards, std::string filename) unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(mission_node->first_node("name")); std::string deck_name{name_node->value()}; - Deck* deck; -#if defined(TYRANT_UNLEASHED) - { - deck = read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name, 1); - } -#endif - deck = read_deck(decks, cards, mission_node, DeckType::mission, id, deck_name, 10); - xml_node<>* effect_id_node(mission_node->first_node("effect")); - if(effect_id_node) - { - int effect_id(effect_id_node ? atoi(effect_id_node->value()) : 0); - deck->effect = static_cast(effect_id); - } + read_deck(decks, all_cards, mission_node, "effect", DeckType::mission, id, deck_name, true); } } //------------------------------------------------------------------------------ -void read_raids(Decks& decks, const Cards& cards, std::string filename) +void read_raids(Decks& decks, const Cards& all_cards, std::string filename) { std::vector buffer; xml_document<> doc; @@ -432,17 +436,11 @@ void read_raids(Decks& decks, const Cards& cards, std::string filename) unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(raid_node->first_node("name")); std::string deck_name{name_node->value()}; - Deck* deck = read_deck(decks, cards, raid_node, DeckType::raid, id, deck_name); - xml_node<>* effect_id_node(raid_node->first_node("effect")); - if(effect_id_node) - { - int effect_id(effect_id_node ? atoi(effect_id_node->value()) : 0); - deck->effect = static_cast(effect_id); - } + read_deck(decks, all_cards, raid_node, "effect", DeckType::raid, id, deck_name); } } //------------------------------------------------------------------------------ -void read_quests(Decks& decks, const Cards& cards, std::string filename) +void read_quests(Decks& decks, const Cards& all_cards, std::string filename) { std::vector buffer; xml_document<> doc; @@ -465,15 +463,12 @@ void read_quests(Decks& decks, const Cards& cards, std::string filename) assert(id_node); unsigned id(id_node ? atoi(id_node->value()) : 0); std::string deck_name{"Step " + std::string{id_node->value()}}; - Deck* deck = read_deck(decks, cards, quest_node, DeckType::quest, id, deck_name); - xml_node<>* effect_id_node(quest_node->first_node("battleground_id")); - int effect_id(effect_id_node ? atoi(effect_id_node->value()) : 0); - deck->effect = static_cast(effect_id); + read_deck(decks, all_cards, quest_node, "battleground_id", DeckType::quest, id, deck_name); } } //------------------------------------------------------------------------------ -void load_recipes_xml(Cards& cards) +void load_recipes_xml(Cards& all_cards) { std::vector buffer; xml_document<> doc; @@ -492,7 +487,7 @@ void load_recipes_xml(Cards& cards) xml_node<>* card_id_node(recipe_node->first_node("card_id")); if (!card_id_node) { continue; } unsigned card_id(atoi(card_id_node->value())); - Card * card = cards.cards_by_id[card_id]; + Card * card = all_cards.cards_by_id[card_id]; for(xml_node<>* resource_node = recipe_node->first_node("resource"); resource_node; resource_node = resource_node->next_sibling("resource")) @@ -500,7 +495,7 @@ void load_recipes_xml(Cards& cards) unsigned card_id(node_value(resource_node, "card_id")); unsigned number(node_value(resource_node, "number")); if (card_id == 0 || number == 0) { continue; } - Card * material_card = cards.cards_by_id[card_id]; + Card * material_card = all_cards.cards_by_id[card_id]; card->m_recipe_cards[material_card] += number; material_card->m_used_for_cards[card] += number; } @@ -518,7 +513,7 @@ Comparator get_comparator(xml_node<>* node, Comparator default_comparator) else { throw std::runtime_error(std::string("Not implemented: compare=\"") + compare->value() + "\""); } } -void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement, const char* achievement_id_name, std::string filename/* = "achievements.xml"*/) +void read_achievement(Decks& decks, const Cards& all_cards, Achievement& achievement, const char* achievement_id_name, std::string filename/* = "achievements.xml"*/) { std::vector buffer; xml_document<> doc; @@ -585,7 +580,7 @@ void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement { achievement.unit_played[atoi(unit_id->value())] = achievement.req_counter.size(); achievement.req_counter.emplace_back(atoi(num_played->value()), comparator); - std::cout << " Play units: " << cards.by_id(atoi(unit_id->value()))->m_name << achievement.req_counter.back().str() << std::endl; + std::cout << " Play units: " << all_cards.by_id(atoi(unit_id->value()))->m_name << achievement.req_counter.back().str() << std::endl; } else if(unit_type && num_played) { From a73158beb7155634b5c6b61db001eea8ba0c4d71 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 25 Jun 2014 01:31:34 +0800 Subject: [PATCH 225/406] Add Flurry skill. --- sim.cpp | 148 +++++++++++++++++++++++++++----------------------------- sim.h | 6 +-- 2 files changed, 73 insertions(+), 81 deletions(-) diff --git a/sim.cpp b/sim.cpp index 11c8c23a..5e99f846 100644 --- a/sim.cpp +++ b/sim.cpp @@ -13,38 +13,6 @@ #include "deck.h" #include "achievement.h" -//---------------------- Debugging stuff --------------------------------------- -unsigned debug_print(0); -unsigned debug_cached(0); -bool debug_line(false); -std::string debug_str; -#ifndef NDEBUG -#define _DEBUG_MSG(v, format, args...) \ - { \ - if(__builtin_expect(debug_print >= v, false)) \ - { \ - if(debug_line) { printf("%i - " format, __LINE__ , ##args); } \ - else if(debug_cached) { \ - char buf[4096]; \ - snprintf(buf, sizeof(buf), format, ##args); \ - debug_str += buf; \ - } \ - else { printf(format, ##args); } \ - std::cout << std::flush; \ - } \ - } -#define _DEBUG_SELECTION(format, args...) \ - { \ - if(__builtin_expect(debug_print >= 2, 0)) \ - { \ - _DEBUG_MSG(2, "Possible targets of " format ":\n", ##args); \ - fd->print_selection_array(); \ - } \ - } -#else -#define _DEBUG_MSG(v, format, args...) -#define _DEBUG_SELECTION(format, args...) -#endif //------------------------------------------------------------------------------ inline std::string status_description(CardStatus* status) { @@ -307,7 +275,7 @@ void Hand::reset(std::mt19937& re) { assaults.reset(); structures.reset(); - commander.set(deck->get_commander()); + commander.set(deck->get_commander(re)); deck->shuffle(re); } //---------------------- $40 Game rules implementation ------------------------- @@ -568,7 +536,6 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, status, ss, mod, need_add_skill) : ss; if (status->m_skill_cd[ss.id] > 0) { - _DEBUG_MSG(2, "Cooling down (%u) %s skill %s\n", status->m_skill_cd[ss.id], status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); continue; } _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); @@ -719,6 +686,8 @@ void turn_start_phase(Field* fd); void turn_end_phase(Field* fd); void evaluate_legion(Field* fd); bool check_and_perform_refresh(Field* fd, CardStatus* src_status); +template +inline bool count_achievement(Field* fd, const CardStatus* c); // return value : (raid points) -> attacker wins, 0 -> defender wins Results play(Field* fd) { @@ -853,39 +822,67 @@ Results play(Field* fd) for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->assaults.size(); ++fd->current_ci) { // ca: current assault - CardStatus& current_status(fd->tap->assaults[fd->current_ci]); + CardStatus* current_status(&fd->tap->assaults[fd->current_ci]); bool attacked = false; - if(!is_active(¤t_status) || !can_act(¤t_status)) + if(!is_active(current_status) || !can_act(current_status)) { - _DEBUG_MSG(2, "Assault %s cannot take action.\n", status_description(¤t_status).c_str()); + _DEBUG_MSG(2, "Assault %s cannot take action.\n", status_description(current_status).c_str()); } else { - current_status.m_blitzing = false; - // Evaluate skills - evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills[SkillMod::on_activate], SkillMod::on_activate); - if(__builtin_expect(fd->end, false)) { break; } - - // Attack - if(can_attack(¤t_status)) + current_status->m_blitzing = false; // XXX: assaults cannot be buffed in its blitzing turn? + unsigned num_actions(1); + for(unsigned action_index(0); action_index < num_actions; ++action_index) { - current_status.m_step = CardStep::attacking; - attacked = attack_phase(fd); + // Evaluate skills + evaluate_skills(fd, current_status, current_status->m_card->m_skills[SkillMod::on_activate], SkillMod::on_activate); + if(__builtin_expect(fd->end, false)) { break; } + // Attack + if(can_attack(current_status)) + { + current_status->m_step = CardStep::attacking; + attacked = attack_phase(fd) || attacked; + } + else + { + _DEBUG_MSG(2, "Assault %s cannot take attack.\n", status_description(current_status).c_str()); + } +#if defined(TYRANT_UNLEASHED) + // [TU] Flurry + if (can_act(current_status) && fd->tip->commander.m_hp > 0 && current_status->has_skill(flurry) && current_status->m_skill_cd[flurry] == 0) + { + count_achievement(fd, current_status); + _DEBUG_MSG(1, "%s activates Flurry\n", status_description(current_status).c_str()); + num_actions = 2; + for (const auto & ss : current_status->m_card->m_skills[SkillMod::on_activate]) + { + if (ss.id == flurry) + { + current_status->m_skill_cd[flurry] = ss.c; + } + } + } +#endif } } - if (!attacked) - { #if defined(TYRANT_UNLEASHED) - CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); - if (att_status->m_corroded_rate > 0) + if (current_status->m_corroded_rate > 0) + { + if (attacked) { - _DEBUG_MSG(1, "%s loses Status corroded.\n", status_description(att_status).c_str()); - att_status->m_corroded_rate = 0; - att_status->m_corroded_weakened = 0; + unsigned v = std::min(current_status->m_corroded_rate, safe_minus(current_status->m_card->m_attack + current_status->m_berserk, current_status->m_corroded_weakened)); + _DEBUG_MSG(1, "%s loses Attack by %u.\n", status_description(current_status).c_str(), v); + current_status->m_corroded_weakened += v; + } + else + { + _DEBUG_MSG(1, "%s loses Status corroded.\n", status_description(current_status).c_str()); + current_status->m_corroded_rate = 0; + current_status->m_corroded_weakened = 0; } -#endif } - current_status.m_step = CardStep::attacked; +#endif + current_status->m_step = CardStep::attacked; } fd->current_phase = Field::end_phase; turn_end_phase(fd); @@ -1103,6 +1100,7 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template inline bool count_achievement(Field* fd, const CardStatus* c) { +#if not defined(TYRANT_UNLEASHED) if(c->m_player == 0) { fd->inc_counter(fd->achievement.skill_used, skill_id); @@ -1111,6 +1109,7 @@ inline bool count_achievement(Field* fd, const CardStatus* c) fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::skill_activated); } } +#endif return(true); } @@ -1201,7 +1200,11 @@ void cooldown_skills(CardStatus & status) { for (const auto & ss : status.m_card->m_skills[SkillMod::on_activate]) { - if (status.m_skill_cd[ss.id] > 0) { -- status.m_skill_cd[ss.id]; } + if (status.m_skill_cd[ss.id] > 0) + { + _DEBUG_MSG(2, "%s reduces timer (%u) of skill %s\n", status_description(&status).c_str(), status.m_skill_cd[ss.id], skill_names[ss.id].c_str()); + -- status.m_skill_cd[ss.id]; + } } } void turn_start_phase(Field* fd) @@ -1603,14 +1606,7 @@ struct PerformAttack } crush_leech(); } -#if defined(TYRANT_UNLEASHED) - if (att_status->m_corroded_rate > 0) - { - unsigned v = std::min(att_status->m_corroded_rate, safe_minus(att_status->m_card->m_attack + att_status->m_berserk, att_status->m_corroded_weakened)); - _DEBUG_MSG(1, "%s loses Attack by %u.\n", status_description(att_status).c_str(), v); - att_status->m_corroded_weakened += v; - } -#else +#if not defined(TYRANT_UNLEASHED) prepend_on_death(fd); resolve_skill(fd); check_regeneration(fd); @@ -1783,7 +1779,16 @@ void PerformAttack::damage_dependant_pre_oa() _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), poison_value); def_status->m_poisoned = poison_value; } -#if not defined(TYRANT_UNLEASHED) +#if defined(TYRANT_UNLEASHED) + unsigned inhibit_value = att_status->skill(inhibit); + if (inhibit_value > def_status->m_inhibited && skill_check(fd, att_status, def_status)) + { + count_achievement(fd, att_status); + // perform_skill_inhibit + _DEBUG_MSG(1, "%s inhibits %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), inhibit_value); + def_status->m_inhibited = inhibit_value; + } +#else if(att_status->has_skill(disease) && skill_check(fd, att_status, def_status)) { count_achievement(fd, att_status); @@ -1806,17 +1811,6 @@ void PerformAttack::damage_dependant_pre_oa() def_status->m_phased = true; } #endif -#if defined(TYRANT_UNLEASHED) - // XXX Assume inhibit stacks, if happened in future - unsigned inhibit_value = att_status->skill(inhibit); - if (inhibit_value > 0 && skill_check(fd, att_status, def_status)) - { - count_achievement(fd, att_status); - // perform_skill_inhibit - _DEBUG_MSG(1, "%s inhibits %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), inhibit_value); - def_status->m_inhibited += inhibit_value; - } -#endif } template<> @@ -1880,6 +1874,7 @@ bool attack_phase(Field* fd) return false; } unsigned num_attacks(1); +#if not defined(TYRANT_UNLEASHED) unsigned flurry_value = att_status->skill(flurry); if(flurry_value > 0 && fd->flip() && skill_check(fd, att_status, nullptr)) { @@ -1887,6 +1882,7 @@ bool attack_phase(Field* fd) _DEBUG_MSG(1, "%s activates Flurry %u\n", status_description(att_status).c_str(), flurry_value); num_attacks += flurry_value; } +#endif for(unsigned attack_index(0); attack_index < num_attacks && can_attack(att_status) && fd->tip->commander.m_hp > 0; ++attack_index) { // 3 possibilities: diff --git a/sim.h b/sim.h index 568c8e38..77db3c4a 100644 --- a/sim.h +++ b/sim.h @@ -17,10 +17,6 @@ class Deck; class Field; class Achievement; -extern unsigned debug_print; -extern unsigned debug_cached; -extern bool debug_line; -extern std::string debug_str; extern unsigned turn_limit; inline unsigned safe_minus(unsigned x, unsigned y) @@ -144,7 +140,7 @@ struct CardStatus unsigned m_hp; bool m_immobilized; bool m_infused; - int m_inhibited; + unsigned m_inhibited; bool m_jammed; bool m_phased; unsigned m_poisoned; From e5b0aa94aca700b8f2240bc3b6e7699071e6484b Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 1 Jul 2014 02:24:23 +0800 Subject: [PATCH 226/406] Fine tune for TU. --- Makefile => Makefile-tu | 6 +++--- Makefile-tu-debug | 18 ++++++++++++++++++ Makefile-wmt | 18 ++++++++++++++++++ Makefile-wmt-debug | 18 ++++++++++++++++++ sim.cpp | 25 +++++++++++++++++++------ 5 files changed, 76 insertions(+), 9 deletions(-) rename Makefile => Makefile-tu (76%) create mode 100644 Makefile-tu-debug create mode 100644 Makefile-wmt create mode 100644 Makefile-wmt-debug diff --git a/Makefile b/Makefile-tu similarity index 76% rename from Makefile rename to Makefile-tu index 26f5ac54..d03599af 100644 --- a/Makefile +++ b/Makefile-tu @@ -1,6 +1,6 @@ MAIN := tyrant_optimize SRCS := $(wildcard *.cpp) -OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) +OBJS := $(patsubst %.cpp,obj-tu/%.o,$(SRCS)) INCS := $(wildcard *.h) CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -DTYRANT_UNLEASHED -DNDEBUG @@ -8,11 +8,11 @@ LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_regex all: $(MAIN) -obj/%.o: %.cpp $(INCS) +obj-tu/%.o: %.cpp $(INCS) $(CXX) $(CPPFLAGS) -o $@ -c $< $(MAIN): $(OBJS) $(CXX) -o $@ $(OBJS) $(LDFLAGS) clean: - del /q $(MAIN).exe obj\*.o + del /q $(MAIN).exe obj-tu\*.o diff --git a/Makefile-tu-debug b/Makefile-tu-debug new file mode 100644 index 00000000..83a192b3 --- /dev/null +++ b/Makefile-tu-debug @@ -0,0 +1,18 @@ +MAIN := tyrant_optimize_debug +SRCS := $(wildcard *.cpp) +OBJS := $(patsubst %.cpp,obj-tu-debug/%.o,$(SRCS)) +INCS := $(wildcard *.h) + +CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -DTYRANT_UNLEASHED +LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_regex + +all: $(MAIN) + +obj-tu-debug/%.o: %.cpp $(INCS) + $(CXX) $(CPPFLAGS) -o $@ -c $< + +$(MAIN): $(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +clean: + del /q $(MAIN).exe obj-tu-debug\*.o diff --git a/Makefile-wmt b/Makefile-wmt new file mode 100644 index 00000000..c0ee57c3 --- /dev/null +++ b/Makefile-wmt @@ -0,0 +1,18 @@ +MAIN := tyrant_optimize +SRCS := $(wildcard *.cpp) +OBJS := $(patsubst %.cpp,obj-wmt/%.o,$(SRCS)) +INCS := $(wildcard *.h) + +CPPFLAGS := -Wall -Werror -Wno-unused-but-set-variable -std=gnu++11 -O3 -DNDEBUG +LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_regex + +all: $(MAIN) + +obj-wmt/%.o: %.cpp $(INCS) + $(CXX) $(CPPFLAGS) -o $@ -c $< + +$(MAIN): $(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +clean: + del /q $(MAIN).exe obj-wmt\*.o diff --git a/Makefile-wmt-debug b/Makefile-wmt-debug new file mode 100644 index 00000000..bf20f0ab --- /dev/null +++ b/Makefile-wmt-debug @@ -0,0 +1,18 @@ +MAIN := tyrant_optimize_debug +SRCS := $(wildcard *.cpp) +OBJS := $(patsubst %.cpp,obj-wmt-debug/%.o,$(SRCS)) +INCS := $(wildcard *.h) + +CPPFLAGS := -Wall -Werror -Wno-unused-but-set-variable -std=gnu++11 -O3 +LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_regex + +all: $(MAIN) + +obj-wmt-debug/%.o: %.cpp $(INCS) + $(CXX) $(CPPFLAGS) -o $@ -c $< + +$(MAIN): $(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +clean: + del /q $(MAIN).exe obj-wmt-debug\*.o diff --git a/sim.cpp b/sim.cpp index 5e99f846..9515e5d3 100644 --- a/sim.cpp +++ b/sim.cpp @@ -835,6 +835,7 @@ Results play(Field* fd) for(unsigned action_index(0); action_index < num_actions; ++action_index) { // Evaluate skills + current_status->m_step = CardStep::none; evaluate_skills(fd, current_status, current_status->m_card->m_skills[SkillMod::on_activate], SkillMod::on_activate); if(__builtin_expect(fd->end, false)) { break; } // Attack @@ -2051,11 +2052,15 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, c template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { +#if defined(TYRANT_UNLEASHED) + return can_act(c) && is_active_next_turn(c); +#else const auto& mod = s.mod; - return(can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); + return can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)); (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : - is_active(c) || is_active_next_turn(c))); + is_active(c) || is_active_next_turn(c)); +#endif } template<> @@ -2069,11 +2074,15 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { +#if defined(TYRANT_UNLEASHED) + return can_attack(c) && is_active(c) && !is_attacking_or_has_attacked(c); +#else const auto& mod = s.mod; - return(can_attack(c) && !c->m_sundered && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); + return can_attack(c) && !c->m_sundered && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)); (src->m_player != c->m_player || mod == SkillMod::on_death ? (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)) : mod == SkillMod::on_attacked ? is_active_next_turn(c) : - is_active(c) && !is_attacking_or_has_attacked(c))); + is_active(c) && !is_attacking_or_has_attacked(c)); +#endif } template<> @@ -2099,11 +2108,15 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, c template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) { +#if defined(TYRANT_UNLEASHED) + return can_attack(c) && attack_power(c) > 0 && is_active_next_turn(c); +#else const auto& mod = s.mod; - return(can_attack(c) && attack_power(c) > 0 && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); + return can_attack(c) && attack_power(c) > 0 && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)); (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : - is_active(c) || is_active_next_turn(c))); + is_active(c) || is_active_next_turn(c)); +#endif } template From 944db1dae928d9174a77d5d018fb2b127cd4382f Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 18 Aug 2014 22:46:00 +0800 Subject: [PATCH 227/406] Add Overload skill. Remove WMT stuff. --- Makefile-tu => Makefile | 11 +- Makefile-wmt => Makefile-debug | 10 +- Makefile-tu-debug | 18 - Makefile-wmt-debug | 18 - achievement.h | 80 -- card.h | 17 +- cards.cpp | 56 +- cards.h | 2 +- deck.cpp | 72 +- read.cpp | 16 +- read.h | 4 +- sim.cpp | 1922 ++++---------------------------- sim.h | 74 +- tyrant.cpp | 82 +- tyrant.h | 90 +- tyrant_optimize.cpp | 674 +++++------ xml.cpp | 296 +---- xml.h | 14 +- 18 files changed, 639 insertions(+), 2817 deletions(-) rename Makefile-tu => Makefile (53%) rename Makefile-wmt => Makefile-debug (52%) delete mode 100644 Makefile-tu-debug delete mode 100644 Makefile-wmt-debug delete mode 100644 achievement.h diff --git a/Makefile-tu b/Makefile similarity index 53% rename from Makefile-tu rename to Makefile index d03599af..a955b230 100644 --- a/Makefile-tu +++ b/Makefile @@ -1,18 +1,19 @@ -MAIN := tyrant_optimize +MAIN := tuo.exe SRCS := $(wildcard *.cpp) -OBJS := $(patsubst %.cpp,obj-tu/%.o,$(SRCS)) +OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) INCS := $(wildcard *.h) -CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -DTYRANT_UNLEASHED -DNDEBUG +CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -DNDEBUG LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_regex all: $(MAIN) -obj-tu/%.o: %.cpp $(INCS) +obj/%.o: %.cpp $(INCS) $(CXX) $(CPPFLAGS) -o $@ -c $< $(MAIN): $(OBJS) $(CXX) -o $@ $(OBJS) $(LDFLAGS) clean: - del /q $(MAIN).exe obj-tu\*.o + del /q $(MAIN).exe obj\*.o + diff --git a/Makefile-wmt b/Makefile-debug similarity index 52% rename from Makefile-wmt rename to Makefile-debug index c0ee57c3..fda029e4 100644 --- a/Makefile-wmt +++ b/Makefile-debug @@ -1,18 +1,18 @@ -MAIN := tyrant_optimize +MAIN := tuodebug.exe SRCS := $(wildcard *.cpp) -OBJS := $(patsubst %.cpp,obj-wmt/%.o,$(SRCS)) +OBJS := $(patsubst %.cpp,obj-debug/%.o,$(SRCS)) INCS := $(wildcard *.h) -CPPFLAGS := -Wall -Werror -Wno-unused-but-set-variable -std=gnu++11 -O3 -DNDEBUG +CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_regex all: $(MAIN) -obj-wmt/%.o: %.cpp $(INCS) +obj-debug/%.o: %.cpp $(INCS) $(CXX) $(CPPFLAGS) -o $@ -c $< $(MAIN): $(OBJS) $(CXX) -o $@ $(OBJS) $(LDFLAGS) clean: - del /q $(MAIN).exe obj-wmt\*.o + del /q $(MAIN).exe obj-debug\*.o diff --git a/Makefile-tu-debug b/Makefile-tu-debug deleted file mode 100644 index 83a192b3..00000000 --- a/Makefile-tu-debug +++ /dev/null @@ -1,18 +0,0 @@ -MAIN := tyrant_optimize_debug -SRCS := $(wildcard *.cpp) -OBJS := $(patsubst %.cpp,obj-tu-debug/%.o,$(SRCS)) -INCS := $(wildcard *.h) - -CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -DTYRANT_UNLEASHED -LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_regex - -all: $(MAIN) - -obj-tu-debug/%.o: %.cpp $(INCS) - $(CXX) $(CPPFLAGS) -o $@ -c $< - -$(MAIN): $(OBJS) - $(CXX) -o $@ $(OBJS) $(LDFLAGS) - -clean: - del /q $(MAIN).exe obj-tu-debug\*.o diff --git a/Makefile-wmt-debug b/Makefile-wmt-debug deleted file mode 100644 index bf20f0ab..00000000 --- a/Makefile-wmt-debug +++ /dev/null @@ -1,18 +0,0 @@ -MAIN := tyrant_optimize_debug -SRCS := $(wildcard *.cpp) -OBJS := $(patsubst %.cpp,obj-wmt-debug/%.o,$(SRCS)) -INCS := $(wildcard *.h) - -CPPFLAGS := -Wall -Werror -Wno-unused-but-set-variable -std=gnu++11 -O3 -LDFLAGS := -lboost_system -lboost_thread -lboost_filesystem -lboost_regex - -all: $(MAIN) - -obj-wmt-debug/%.o: %.cpp $(INCS) - $(CXX) $(CPPFLAGS) -o $@ -c $< - -$(MAIN): $(OBJS) - $(CXX) -o $@ $(OBJS) $(LDFLAGS) - -clean: - del /q $(MAIN).exe obj-wmt-debug\*.o diff --git a/achievement.h b/achievement.h deleted file mode 100644 index 3b6157bd..00000000 --- a/achievement.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef ACHIEVEMENT_H_INCLUDED -#define ACHIEVEMENT_H_INCLUDED - -#include - -enum Comparator -{ - equal, - great_equal, - less_equal -}; - -class Counter -{ -public: - unsigned m_target; - Comparator m_comparator; -public: - Counter(unsigned target=1, Comparator comparator=great_equal): m_target(target), m_comparator(comparator) {} - void init(unsigned target, Comparator comparator) - { - m_target = target; - m_comparator = comparator; - } - bool check(unsigned value) const - { - switch(m_comparator) - { - case equal: return value == m_target; - case great_equal: return value >= m_target; - case less_equal: return value <= m_target; - default: throw; - } - } - // predict whether the monotonic increasing counter will be met: +1: true; 0: unknown; -1: false. - int predict_monoinc(unsigned value) const - { - switch(m_comparator) - { - case equal: - case less_equal: return value <= m_target ? 0 : -1; - case great_equal: return value < m_target ? 0 : +1; - default: throw; - } - } - std::string str() const - { - std::stringstream ios; - switch(m_comparator) - { - case equal: ios << " = "; break; - case great_equal: ios << " >= "; break; - case less_equal: ios << " <= "; break; - default: throw; - } - ios << m_target; - return ios.str(); - } -}; - -struct Achievement -{ - unsigned id; - std::string name; - Counter mission_condition; - std::vector req_counter; - // Following are indexes to the req_counter - std::map skill_used; - std::map unit_played; - std::map unit_type_played; - std::map unit_faction_played; - std::map unit_rarity_played; - std::map unit_type_killed; - std::map unit_with_skill_killed; - std::map misc_req; - - Achievement() : id(0) {} -}; - -#endif diff --git a/card.h b/card.h index 110a3528..75fc33e1 100644 --- a/card.h +++ b/card.h @@ -12,21 +12,15 @@ class Card unsigned m_attack; unsigned m_base_id; // The id of the original card if a card is unique and alt/upgraded. The own id of the card otherwise. unsigned m_delay; - bool m_disease_oa; Faction m_faction; unsigned m_health; - unsigned m_hidden; unsigned m_id; unsigned m_level; std::string m_name; - unsigned m_phase; unsigned m_rarity; - unsigned m_replace; - unsigned m_reserve; unsigned m_set; - bool m_unique; - std::vector m_skills[SkillMod::num_skill_activation_modifiers]; - unsigned m_skill_value[SkillMod::num_skill_activation_modifiers][num_skills]; + std::vector m_skills; + unsigned m_skill_value[num_skills]; CardType::CardType m_type; const Card* m_top_level_card; // [TU] corresponding full-level card unsigned m_recipe_cost; @@ -38,18 +32,13 @@ class Card m_attack(0), m_base_id(0), m_delay(0), - m_disease_oa(false), m_faction(imperial), m_health(0), - m_hidden(0), m_id(0), m_level(1), m_name(""), - m_phase(0), m_rarity(1), - m_replace(0), m_set(0), - m_unique(false), m_skills(), m_type(CardType::assault), m_top_level_card(this), @@ -60,7 +49,7 @@ class Card std::memset(m_skill_value, 0, sizeof m_skill_value); } - void add_skill(Skill id, unsigned x, Faction y, unsigned c, Skill s, bool all, SkillMod::SkillMod mod=SkillMod::on_activate); + void add_skill(Skill id, unsigned x, Faction y, unsigned n, unsigned c, Skill s, bool all); const Card* upgraded() const { return this == m_top_level_card ? this : m_used_for_cards.begin()->first; } }; diff --git a/cards.cpp b/cards.cpp index 68f4a512..7334add2 100644 --- a/cards.cpp +++ b/cards.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "tyrant.h" #include "card.h" @@ -80,24 +81,17 @@ void Cards::organize() { card->m_name.erase(pos, 1); } -#if defined(TYRANT_UNLEASHED) // set m_top_level_card for non base cards card->m_top_level_card = by_id(card->m_base_id)->m_top_level_card; // add a suffix of level to the name of cards; register as alias for the full-level cards (the formal name is without suffix) if (card == card->m_top_level_card) { - player_cards_by_name[{simplify_name(card->m_name + "-" + to_string(card->m_level)), card->m_hidden}] = card; + player_cards_by_name[simplify_name(card->m_name + "-" + to_string(card->m_level))] = card; } else { card->m_name += "-" + to_string(card->m_level); } -#else - if(card->m_set == 5002) - { - card->m_name += '*'; - } -#endif // Card available to players if(card->m_set != 0) { @@ -116,24 +110,26 @@ void Cards::organize() player_structures.push_back(card); break; } - case CardType::action: { - player_actions.push_back(card); - break; - } case CardType::num_cardtypes: { throw card->m_type; break; } } std::string simple_name{simplify_name(card->m_name)}; - auto card_itr = player_cards_by_name.find({simple_name, card->m_hidden}); - if(card_itr == player_cards_by_name.end() || card_itr->second->m_id == card->m_replace) + auto card_itr = player_cards_by_name.find(simple_name); + if (card_itr == player_cards_by_name.end()) { - player_cards_by_name[{simple_name, card->m_hidden}] = card; + player_cards_by_name[simple_name] = card; + } + else + { + // TODO check set visible +// std::cerr << "Duplicated card name [" << card->m_name << "] " << card_itr->second->m_set << ":" << card->m_set << "\n"; // XXX } } } - // Round 3: depend on player_cards_by_name; set abbreviations and [WMT] recipes +#if 0 // TODO refactor precedence + // Round 3: depend on player_cards_by_name; set abbreviations for(Card* card: cards) { // generate abbreviations @@ -141,42 +137,28 @@ void Cards::organize() { for(auto&& abbr_name : get_abbreviations(card->m_name)) { - if(abbr_name.length() > 1 && player_cards_by_name.find({abbr_name, 0}) == player_cards_by_name.end()) + if(abbr_name.length() > 1 && player_cards_by_name.find(abbr_name) == player_cards_by_name.end()) { player_cards_abbr[abbr_name] = card->m_name; } } } -#if not defined(TYRANT_UNLEASHED) - // update recipes - if(card->m_set == 5002) - { - std::string material_name{simplify_name(card->m_name)}; - material_name.erase(material_name.size() - 1); // remove suffix "*" - Card * material_card = player_cards_by_name[{material_name, card->m_hidden}]; - // Promo and Unpurchasable Reward cards only require 1 copy - unsigned number = material_card->m_set == 5001 || (material_card->m_set == 5000 && material_card->m_reserve) ? 1 : 2; - // Reward cards still have gold cost - card->m_recipe_cost = material_card->m_set == 5000 ? (card->m_rarity == 4 ? 100000 : 20000) : 0; - card->m_recipe_cards[material_card] = number; - material_card->m_used_for_cards[card] = number; - } -#endif } +#endif } // class Card -void Card::add_skill(Skill id, unsigned x, Faction y, unsigned c, Skill s, bool all, SkillMod::SkillMod mod) +void Card::add_skill(Skill id, unsigned x, Faction y, unsigned n, unsigned c, Skill s, bool all) { - for(auto it = m_skills[mod].begin(); it != m_skills[mod].end(); ++ it) + for(auto it = m_skills.begin(); it != m_skills.end(); ++ it) { if(it->id == id) { - m_skills[mod].erase(it); + m_skills.erase(it); break; } } - m_skills[mod].push_back({id, x, y, c, s, all, mod}); - m_skill_value[mod][id] = std::max(1u, x); + m_skills.push_back({id, x, y, n, c, s, all}); + m_skill_value[id] = x ? x : n ? n : 1; } diff --git a/cards.h b/cards.h index bf7976a6..3a02d766 100644 --- a/cards.h +++ b/cards.h @@ -14,7 +14,7 @@ struct Cards std::vector cards; std::map cards_by_id; std::vector player_cards; - std::map, Card*> player_cards_by_name; + std::map player_cards_by_name; std::vector player_commanders; std::vector player_assaults; std::vector player_structures; diff --git a/deck.cpp b/deck.cpp index b478086f..8e170bfb 100644 --- a/deck.cpp +++ b/deck.cpp @@ -148,6 +148,7 @@ void hash_to_ids_ext_b64(const char* hash, std::vector& ids) d = p - base64_chars; } id += factor * (d - 32); + ++ pc; ids.push_back(id); } } @@ -218,59 +219,62 @@ void encode_deck_ddd_b64(std::stringstream &ios, const Card* commander, std::vec } } -#if defined(TYRANT_UNLEASHED) DeckDecoder hash_to_ids = hash_to_ids_ext_b64; DeckEncoder encode_deck = encode_deck_ext_b64; -#else -DeckDecoder hash_to_ids = hash_to_ids_wmt_b64; -DeckEncoder encode_deck = encode_deck_wmt_b64; -#endif const std::pair, std::map> string_to_ids(const Cards& all_cards, const std::string& deck_string, const std::string & description) { std::vector card_ids; std::map card_marks; - if(deck_string.find_first_of(":,") == std::string::npos) - { + std::vector error_list; + boost::tokenizer> deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; + auto token_iter = deck_tokens.begin(); + signed p = -1; + for(; token_iter != deck_tokens.end(); ++token_iter) + { + std::string card_spec(*token_iter); + unsigned card_id{0}; + unsigned card_num{1}; + char num_sign{0}; + char mark{0}; try { - hash_to_ids(deck_string.c_str(), card_ids); + parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign, mark); + assert(num_sign == 0); + for(unsigned i(0); i < card_num; ++i) + { + card_ids.push_back(card_id); + if(mark) { card_marks[p] = mark; } + ++ p; + } } catch(std::exception& e) { - std::cerr << "Error while resolving " << description << ": " << e.what() << std::endl; - throw; + error_list.push_back(e.what()); + continue; } } - else + if (! card_ids.empty()) { - boost::tokenizer> deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; - auto token_iter = deck_tokens.begin(); - signed p = -1; - for(; token_iter != deck_tokens.end(); ++token_iter) + if (! error_list.empty()) { - std::string card_spec(*token_iter); - unsigned card_id{0}; - unsigned card_num{1}; - char num_sign{0}; - char mark{0}; - try + std::cerr << "Warning while resolving " << description << ": "; + for (auto error: error_list) { - parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign, mark); - assert(num_sign == 0); - for(unsigned i(0); i < card_num; ++i) - { - card_ids.push_back(card_id); - if(mark) { card_marks[p] = mark; } - ++ p; - } - } - catch(std::exception& e) - { - std::cerr << "Warning while resolving " << description << ": " << e.what() << std::endl; - continue; + std::cerr << '[' << error << ']'; } + std::cerr << std::endl; } + return {card_ids, card_marks}; + } + try + { + hash_to_ids(deck_string.c_str(), card_ids); + } + catch(std::exception& e) + { + std::cerr << "Error while resolving " << description << ": " << e.what() << std::endl; + throw; } return {card_ids, card_marks}; } diff --git a/read.cpp b/read.cpp index 5d3ac207..daad48db 100644 --- a/read.cpp +++ b/read.cpp @@ -16,11 +16,11 @@ #include "cards.h" #include "deck.h" -void load_decks(Decks& decks, Cards& all_cards) +void load_custom_decks(Decks& decks, Cards& all_cards, const char * filename) { - if(boost::filesystem::exists("Custom.txt")) + if(boost::filesystem::exists(filename)) { - read_custom_decks(decks, all_cards, "Custom.txt"); + read_custom_decks(decks, all_cards, filename); } } @@ -176,10 +176,6 @@ void parse_card_spec(const Cards& all_cards, std::string& card_spec, unsigned& c mark = card_name[0]; card_name.erase(0, 1); } - if(card_name.empty()) - { - throw std::runtime_error("no card name"); - } // If card name is not found, try find card id quoted in '[]' in name, ignoring other characters. std::string simple_name{simplify_name(card_name)}; const auto && abbr_it = all_cards.player_cards_abbr.find(simple_name); @@ -339,7 +335,7 @@ unsigned read_custom_decks(Decks& decks, Cards& all_cards, std::string filename) return(0); } -void read_owned_cards(Cards& all_cards, std::map& owned_cards, std::map& buyable_cards, const char *filename) +void read_owned_cards(Cards& all_cards, std::map& owned_cards, std::string filename) { std::ifstream owned_file{filename}; if(!owned_file.good()) @@ -377,10 +373,6 @@ void read_owned_cards(Cards& all_cards, std::map& owned_card { owned_cards[card_id] = owned_cards[card_id] > card_num ? owned_cards[card_id] - card_num : 0; } - else if(num_sign == '$') - { - buyable_cards[card_id] = card_num; - } } catch(std::exception& e) { diff --git a/read.h b/read.h index 03f1604f..74cc1f7f 100644 --- a/read.h +++ b/read.h @@ -11,11 +11,11 @@ class Cards; class Decks; class Deck; -void load_decks(Decks& decks, Cards& cards); +void load_custom_decks(Decks& decks, Cards& cards, const char * filename); DeckList parse_deck_list(std::string list_string, const Decks& decks); void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark); unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename); -void read_owned_cards(Cards& cards, std::map& owned_cards, std::map& buyable_cards, const char *filename); +void read_owned_cards(Cards& cards, std::map& owned_cards, std::string filename); unsigned read_card_abbrs(Cards& cards, const std::string& filename); #endif diff --git a/sim.cpp b/sim.cpp index 9515e5d3..ebfcecb3 100644 --- a/sim.cpp +++ b/sim.cpp @@ -11,7 +11,6 @@ #include "card.h" #include "cards.h" #include "deck.h" -#include "achievement.h" //------------------------------------------------------------------------------ inline std::string status_description(CardStatus* status) @@ -42,23 +41,31 @@ inline void Field::print_selection_array() #endif } //------------------------------------------------------------------------------ -inline bool CardStatus::has_skill(Skill skill, SkillMod::SkillMod mod) const +inline bool CardStatus::has_skill(Skill skill_id) const { - return m_card->m_skill_value[mod][skill]; + return m_card->m_skill_value[skill_id]; } //------------------------------------------------------------------------------ -inline unsigned CardStatus::skill(Skill skill, SkillMod::SkillMod mod) const +template +inline bool CardStatus::has_skill() const +{ + return m_card->m_skill_value[skill_id]; +} +//------------------------------------------------------------------------------ +template +inline unsigned CardStatus::skill() const { - return m_card->m_skill_value[mod][skill] + enhanced(skill); + return m_card->m_skill_value[skill_id] + enhanced(skill_id); } //------------------------------------------------------------------------------ inline unsigned CardStatus::enhanced(Skill skill) const { -#if defined(TYRANT_UNLEASHED) return m_enhanced_value[skill]; -#else - return 0; -#endif +} +//------------------------------------------------------------------------------ +inline unsigned CardStatus::protected_value() const +{ + return m_protected; } //------------------------------------------------------------------------------ inline void CardStatus::set(const Card* card) @@ -71,32 +78,21 @@ inline void CardStatus::set(const Card& card) m_card = &card; m_index = 0; m_player = 0; - m_augmented = 0; m_berserk = 0; - m_blitzing = false; - m_chaosed = false; m_corroded_rate = 0; m_corroded_weakened = 0; m_delay = card.m_delay; - m_diseased = false; m_evaded = 0; m_enfeebled = 0; m_faction = card.m_faction; - m_frozen = false; m_hp = card.m_health; - m_immobilized = false; - m_infused = false; m_inhibited = 0; m_jammed = false; - m_phased = false; + m_overloaded = false; m_poisoned = 0; m_protected = 0; m_rallied = 0; - m_stunned = 0; - m_sundered = false; - m_temporary_split = false; m_weakened = 0; - m_is_summoned = false; m_step = CardStep::none; std::memset(m_enhanced_value, 0, sizeof m_enhanced_value); std::memset(m_skill_cd, 0, sizeof m_skill_cd); @@ -109,30 +105,12 @@ inline int attack_power(CardStatus* att) //------------------------------------------------------------------------------ std::string skill_description(const Cards& cards, const SkillSpec& s) { - switch(s.id) - { - case summon: - if(s.x == 0) - { - // Summon X - return(skill_names[s.id] + " X" + - skill_activation_modifier_names[s.mod]); - } - else - { - return(skill_names[s.id] + - " " + cards.by_id(s.x)->m_name.c_str() + - skill_activation_modifier_names[s.mod]); - } - default: - return(skill_names[s.id] + - (s.all ? " all" : "") + - (s.y == allfactions ? "" : std::string(" ") + faction_names[s.y]) + - (s.s == no_skill ? "" : std::string(" ") + skill_names[s.s]) + - (s.x == 0 ? "" : std::string(" ") + to_string(s.x)) + - (s.c == 0 ? "" : std::string(" every ") + to_string(s.c)) + - skill_activation_modifier_names[s.mod]); - } + return skill_names[s.id] + + (s.all ? " all" : s.n == 0 ? "" : std::string(" ") + to_string(s.n)) + + (s.y == allfactions ? "" : std::string(" ") + faction_names[s.y]) + + (s.s == no_skill ? "" : std::string(" ") + skill_names[s.s]) + + (s.x == 0 ? "" : std::string(" ") + to_string(s.x)) + + (s.c == 0 ? "" : std::string(" every ") + to_string(s.c)); } std::string skill_short_description(const SkillSpec& s) { @@ -148,8 +126,6 @@ std::string card_description(const Cards& cards, const Card* c) desc = c->m_name; switch(c->m_type) { - case CardType::action: - break; case CardType::assault: desc += ": " + to_string(c->m_attack) + "/" + to_string(c->m_health) + "/" + to_string(c->m_delay); break; @@ -163,13 +139,9 @@ std::string card_description(const Cards& cards, const Card* c) assert(false); break; } - if(c->m_unique) { desc += " unique"; } if(c->m_rarity == 4) { desc += " legendary"; } if(c->m_faction != allfactions) { desc += " " + faction_names[c->m_faction]; } - for(auto mod = 0; mod < SkillMod::num_skill_activation_modifiers; ++ mod) - { - for(auto& skill: c->m_skills[mod]) { desc += ", " + skill_description(cards, skill); } - } + for(auto& skill: c->m_skills) { desc += ", " + skill_description(cards, skill); } return(desc); } //------------------------------------------------------------------------------ @@ -179,7 +151,6 @@ std::string CardStatus::description() switch(m_card->m_type) { case CardType::commander: desc = "Commander "; break; - case CardType::action: desc = "Action "; break; case CardType::assault: desc = "Assault " + to_string(m_index) + " "; break; case CardType::structure: desc = "Structure " + to_string(m_index) + " "; break; case CardType::num_cardtypes: assert(false); break; @@ -187,8 +158,6 @@ std::string CardStatus::description() desc += "[" + m_card->m_name; switch(m_card->m_type) { - case CardType::action: - break; case CardType::assault: desc += " att:" + to_string(m_card->m_attack); { @@ -209,68 +178,19 @@ std::string CardStatus::description() } if(m_delay > 0) { desc += " cd:" + to_string(m_delay); - if(m_blitzing) { desc += "(blitzing)"; } } - if(m_chaosed) { desc += ", chaosed"; } - if(m_diseased) { desc += ", diseased"; } - if(m_frozen) { desc += ", frozen"; } - if(m_immobilized) { desc += ", immobilized"; } - if(m_infused) { desc += ", infused"; } if(m_jammed) { desc += ", jammed"; } - if(m_phased) { desc += ", phased"; } - if(m_sundered) { desc += ", sundered"; } - if(m_temporary_split) { desc += ", cloning"; } - if(m_augmented > 0) { desc += ", augmented " + to_string(m_augmented); } + if(m_overloaded) { desc += ", overloaded"; } if(m_corroded_rate > 0) { desc += ", corroded " + to_string(m_corroded_rate); } if(m_enfeebled > 0) { desc += ", enfeebled " + to_string(m_enfeebled); } if(m_inhibited > 0) { desc += ", inhibited " + to_string(m_inhibited); } if(m_poisoned > 0) { desc += ", poisoned " + to_string(m_poisoned); } if(m_protected > 0) { desc += ", protected " + to_string(m_protected); } - if(m_stunned > 0) { desc += ", stunned " + to_string(m_stunned); } // if(m_step != CardStep::none) { desc += ", Step " + to_string(static_cast(m_step)); } desc += "]"; return(desc); } //------------------------------------------------------------------------------ -inline void print_achievement_results(Field* fd) -{ -#ifndef NDEBUG - if(fd->achievement.req_counter.size() == 0) - { - return; - } - _DEBUG_MSG(1, "Achievement:\n"); - for(auto i : fd->achievement.skill_used) - { - _DEBUG_MSG(1, " Use skills: %s %u%s? %s\n", skill_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); - } - for(auto i : fd->achievement.unit_played) - { - _DEBUG_MSG(1, " Play units: %s %u%s? %s\n", fd->cards.by_id(i.first)->m_name.c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); - } - for(auto i : fd->achievement.unit_type_played) - { - _DEBUG_MSG(1, " Play units of type: %s %u%s? %s\n", cardtype_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); - } - for(auto i : fd->achievement.unit_faction_played) - { - _DEBUG_MSG(1, " Play units of faction: %s %u%s? %s\n", faction_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); - } - for(auto i : fd->achievement.unit_rarity_played) - { - _DEBUG_MSG(1, " Play units of rarity: %s %u%s? %s\n", rarity_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); - } - for(auto i : fd->achievement.unit_type_killed) - { - _DEBUG_MSG(1, " Kill units of type: %s %u%s? %s\n", cardtype_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); - } - for(auto i : fd->achievement.misc_req) - { - _DEBUG_MSG(1, " %s %u%s? %s\n", achievement_misc_req_names[i.first].c_str(), fd->achievement_counter[i.second], fd->achievement.req_counter[i.second].str().c_str(), fd->achievement.req_counter[i.second].check(fd->achievement_counter[i.second]) ? "Yes" : "No"); - } -#endif -} -//------------------------------------------------------------------------------ void Hand::reset(std::mt19937& re) { assaults.reset(); @@ -289,32 +209,6 @@ inline unsigned opponent(unsigned player) return((player + 1) % 2); } //------------------------------------------------------------------------------ -SkillSpec apply_augment(const CardStatus* status, const SkillSpec& s) -{ - if (s.id == augment || s.id == summon || s.x == 0) - { - return s; - } - SkillSpec augmented_s = s; - augmented_s.x += status->m_augmented; - return(augmented_s); -} -SkillSpec apply_fusion(const SkillSpec& s) -{ - SkillSpec fusioned_s = s; - fusioned_s.x *= 2; - return(fusioned_s); -} -SkillSpec apply_infuse(const SkillSpec& s) -{ - if (s.y == allfactions || s.y == bloodthirsty || helpful_skills.find(s.id) == helpful_skills.end()) - { - return s; - } - SkillSpec infused_s = s; - infused_s.y = bloodthirsty; - return(infused_s); -} SkillSpec apply_enhance(const SkillSpec& s, unsigned enhanced_value) { SkillSpec enahnced_s = s; @@ -322,175 +216,6 @@ SkillSpec apply_enhance(const SkillSpec& s, unsigned enhanced_value) return(enahnced_s); } //------------------------------------------------------------------------------ -bool may_change_skill(const Field* fd, const CardStatus* status, const SkillMod::SkillMod mod) -{ -#if not defined(TYRANT_UNLEASHED) - switch (mod) - { - case SkillMod::on_activate: - switch (status->m_card->m_type) - { - case CardType::commander: - return (fd->effect == Effect::time_surge || - fd->effect == Effect::friendly_fire || - fd->effect == Effect::genesis || - (fd->effect == Effect::artillery_strike && fd->turn >= 9 && status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 1u : 0u)) || - fd->effect == Effect::decrepit || - fd->effect == Effect::forcefield || - fd->effect == Effect::chilling_touch); - case CardType::assault: - return (fd->effect == Effect::friendly_fire || - ((fd->effect == Effect::clone_project || fd->effect == Effect::clone_experiment) && status->m_temporary_split)); - default: - break; - } - break; - case SkillMod::on_death: - return ((status->m_card->m_type == CardType::assault || status->m_card->m_type == CardType::structure) && - fd->effect == Effect::haunt && status->m_card->m_faction != bloodthirsty); - default: - break; - } -#endif - return false; -} -SkillSpec apply_battleground_effect(const Field* fd, const CardStatus* status, const SkillSpec& ss, const SkillMod::SkillMod mod, bool& need_add_skill) -{ -#if not defined(TYRANT_UNLEASHED) - switch (fd->effect) - { - case Effect::time_surge: - // replace other instance of the skill - if(ss.id == rush || ss.id == no_skill) - { - need_add_skill = false; - return SkillSpec{rush, 1, allfactions, 0, no_skill, false, mod}; - } - break; - case Effect::clone_project: - case Effect::clone_experiment: - // no gain the skill if already have - if(ss.id == split) - { - need_add_skill = false; - } - else if(ss.id == no_skill) - { - return SkillSpec{split, 0, allfactions, 0, no_skill, false, mod}; - } - break; - case Effect::friendly_fire: - switch (status->m_card->m_type) - { - case CardType::assault: - // no gain the skill if already have - if(ss.id == strike) - { - need_add_skill = false; - } - else if(ss.id == no_skill) - { - return SkillSpec{strike, 1, allfactions, 0, no_skill, false, mod}; - } - break; - case CardType::commander: - // replace other instance of the skill - if(ss.id == chaos || ss.id == no_skill) - { - need_add_skill = false; - return SkillSpec{chaos, 0, allfactions, 0, no_skill, true, mod}; - } - break; - default: - break; - } - break; - case Effect::genesis: - // replace other instance of the skill - if(ss.id == summon || ss.id == no_skill) - { - need_add_skill = false; - return SkillSpec{summon, 0, allfactions, 0, no_skill, false, mod}; - } - break; - case Effect::artillery_strike: - // replace other instance of the skill - if(ss.id == strike || ss.id == no_skill) - { - need_add_skill = false; - return SkillSpec{strike, 3, allfactions, 0, no_skill, true, mod}; - } - break; - case Effect::decrepit: - // replace other instance of the skill - if(ss.id == enfeeble || ss.id == no_skill) - { - need_add_skill = false; - return SkillSpec{enfeeble, 1, allfactions, 0, no_skill, true, mod}; - } - break; - case Effect::forcefield: - // replace other instance of the skill - if(ss.id == protect || ss.id == no_skill) - { - need_add_skill = false; - return SkillSpec{protect, 1, allfactions, 0, no_skill, true, mod}; - } - break; - case Effect::chilling_touch: - // replace other instance of the skill - if(ss.id == freeze || ss.id == no_skill) - { - need_add_skill = false; - return SkillSpec{freeze, 0, allfactions, 0, no_skill, false, mod}; - } - break; - case Effect::haunt: - // replace other instance of the skill - if(ss.id == summon || ss.id == no_skill) - { - need_add_skill = false; - return SkillSpec{summon, 0, bloodthirsty, 0, no_skill, false, mod}; - } - break; - default: - break; - } -#endif - return ss; -} -//------------------------------------------------------------------------------ -void prepend_on_death(Field* fd) -{ - std::vector> od_skills; - auto mod = SkillMod::on_death; - for(auto status: fd->killed_with_on_death) - { - if(status->m_jammed) - { - _DEBUG_MSG(2, "%s is jammed and cannot activate its on Death skill.\n", status_description(status).c_str()); - continue; - } - bool need_add_skill = may_change_skill(fd, status, mod); - for(auto& ss: status->m_card->m_skills[SkillMod::on_death]) - { - auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, status, ss, mod, need_add_skill) : ss; - _DEBUG_MSG(2, "Preparing %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); - od_skills.emplace_back(status, battleground_s); - //if(__builtin_expect(fd->end, false)) { return; } // so far no "on Death" skill may end the battle - } - if(need_add_skill) - { - auto battleground_s = apply_battleground_effect(fd, status, SkillSpec{no_skill, 0, allfactions, 0, no_skill, false, mod}, mod, need_add_skill); - assert(battleground_s.id != no_skill); - _DEBUG_MSG(2, "Preparing %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); - od_skills.emplace_back(status, battleground_s); - } - } - fd->skill_queue.insert(fd->skill_queue.begin(), od_skills.begin(), od_skills.end()); - fd->killed_with_on_death.clear(); -} -//------------------------------------------------------------------------------ void(*skill_table[num_skills])(Field*, CardStatus* src_status, const SkillSpec&); void resolve_skill(Field* fd) { @@ -507,52 +232,36 @@ void resolve_skill(Field* fd) } else if(!status->m_jammed) { -#if defined(TYRANT_UNLEASHED) unsigned enhanced_value = status->enhanced(skill.id); auto& enhanced_s = enhanced_value > 0 ? apply_enhance(skill, enhanced_value) : skill; auto& modified_s = enhanced_s; -#else - bool fusion_active = status->has_skill(fusion) && status->m_player == fd->tapi && fd->fusion_count >= 3; - auto& augmented_s = status->m_augmented > 0 ? apply_augment(status, skill) : skill; - auto& fusioned_s = fusion_active ? apply_fusion(augmented_s) : augmented_s; - auto& infused_s = status->m_infused ? apply_infuse(fusioned_s) : fusioned_s; - auto& modified_s = infused_s; -#endif skill_table[skill.id](fd, status, modified_s); } } } //------------------------------------------------------------------------------ bool attack_phase(Field* fd); -void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills, const SkillMod::SkillMod mod) +void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills) { assert(status); assert(fd->skill_queue.size() == 0); - bool need_add_skill = may_change_skill(fd, status, mod); for(auto& ss: skills) { if (skill_table[ss.id] == nullptr) - { continue; } - auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, status, ss, mod, need_add_skill) : ss; + { + continue; + } if (status->m_skill_cd[ss.id] > 0) { continue; } - _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); - fd->skill_queue.emplace_back(status, battleground_s); + _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, ss).c_str()); + fd->skill_queue.emplace_back(status, ss); resolve_skill(fd); if(__builtin_expect(fd->end, false)) { break; } } - if(need_add_skill) - { - auto battleground_s = apply_battleground_effect(fd, status, SkillSpec{no_skill, 0, allfactions, 0, no_skill, false, mod}, mod, need_add_skill); - assert(battleground_s.id != no_skill); - _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, battleground_s).c_str()); - fd->skill_queue.emplace_back(status, battleground_s); - resolve_skill(fd); - } } -bool check_and_perform_blitz(Field* fd, CardStatus* src_status); + struct PlayCard { const Card* card; @@ -572,9 +281,6 @@ struct PlayCard { setStorage(); placeCard(); - performBlitz(); - onPlaySkills(); - fieldEffects(); return(true); } @@ -590,38 +296,8 @@ struct PlayCard status->set(card); status->m_index = storage->size() - 1; status->m_player = fd->tapi; - if((fd->turn == 1 && fd->gamemode == tournament && status->m_delay > 0) || (type == CardType::assault && fd->effect == Effect::harsh_conditions)) - { - ++status->m_delay; - } - if(status->m_player == 0) - { - fd->inc_counter(fd->achievement.unit_played, card->m_id); - fd->inc_counter(fd->achievement.unit_type_played, card->m_type); - fd->inc_counter(fd->achievement.unit_faction_played, card->m_faction); - fd->inc_counter(fd->achievement.unit_rarity_played, card->m_rarity); - } _DEBUG_MSG(1, "%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), static_cast(storage->size() - 1), card_description(fd->cards, card).c_str()); } - - // all except assault: noop - template - void performBlitz() - { - } - - // all except assault: noop - template - void fieldEffects() - { - } - - // assault + structure - template - void onPlaySkills() - { - evaluate_skills(fd, status, card->m_skills[SkillMod::on_play], SkillMod::on_play); - } }; // assault template <> @@ -635,59 +311,20 @@ void PlayCard::setStorage() { storage = &fd->tap->structures; } -// action: played on structure slots -template <> -void PlayCard::setStorage() -{ - storage = &fd->tap->structures; -} -// assault -template <> -void PlayCard::performBlitz() -{ - if(status->has_skill(blitz)) - { - check_and_perform_blitz(fd, status); - } -} -// assault -template <> -void PlayCard::fieldEffects() -{ - if(fd->effect == Effect::toxic) - { - status->m_poisoned = 1; - } - else if(fd->effect == Effect::decay) - { - status->m_poisoned = 1; - status->m_diseased = true; - } -} -// action -template <> -void PlayCard::onPlaySkills() -{ - evaluate_skills(fd, status, card->m_skills[SkillMod::on_activate], SkillMod::on_play); -} //------------------------------------------------------------------------------ inline bool is_attacking_or_has_attacked(CardStatus* c) { return(c->m_step >= CardStep::attacking); } inline bool is_attacking(CardStatus* c) { return(c->m_step == CardStep::attacking); } inline bool has_attacked(CardStatus* c) { return(c->m_step == CardStep::attacked); } -inline bool is_jammed(CardStatus* c) { return(c->m_jammed || c->m_frozen); } -inline bool is_active(CardStatus* c) { return(c->m_delay == 0 || c->m_blitzing); } +inline bool is_jammed(CardStatus* c) { return(c->m_jammed); } +inline bool is_active(CardStatus* c) { return(c->m_delay == 0); } inline bool is_active_next_turn(CardStatus* c) { return(c->m_delay <= 1); } inline bool can_act(CardStatus* c) { return(c->m_hp > 0 && !is_jammed(c)); } -inline bool can_attack(CardStatus* c) { return(can_act(c) && !c->m_immobilized && !c->m_stunned); } +inline bool can_attack(CardStatus* c) { return(can_act(c)); } // Can be healed / repaired -inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health && !c->m_diseased); } +inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health); } //------------------------------------------------------------------------------ void turn_start_phase(Field* fd); void turn_end_phase(Field* fd); -void evaluate_legion(Field* fd); -bool check_and_perform_refresh(Field* fd, CardStatus* src_status); -template -inline bool count_achievement(Field* fd, const CardStatus* c); // return value : (raid points) -> attacker wins, 0 -> defender wins Results play(Field* fd) { @@ -697,12 +334,8 @@ Results play(Field* fd) fd->tipi = opponent(fd->tapi); fd->tap = fd->players[fd->tapi]; fd->tip = fd->players[fd->tipi]; - fd->fusion_count = 0; fd->end = false; - fd->achievement_counter.clear(); - fd->achievement_counter.resize(fd->achievement.req_counter.size()); -#if defined(TYRANT_UNLEASHED) // Play fortresses for (unsigned _ = 0; _ < 2; ++ _) { @@ -713,7 +346,6 @@ Results play(Field* fd) std::swap(fd->tapi, fd->tipi); std::swap(fd->tap, fd->tip); } -#endif #if 0 // ANP: Last decision point is second-to-last card played. @@ -723,44 +355,14 @@ Results play(Field* fd) unsigned p1_size = fd->players[1]->deck->cards.size(); fd->players[0]->available_summons = 29 + p0_size; fd->players[1]->available_summons = 29 + p1_size; - fd->last_decision_turn = p0_size == 1 ? 0 : p0_size * 2 - (fd->gamemode == surge ? 2 : 3); - - // Count commander as played for achievements (not count in type / faction / rarity requirements) - fd->inc_counter(fd->achievement.unit_played, fd->players[0]->commander.m_card->m_id); - fd->set_counter(fd->achievement.misc_req, AchievementMiscReq::turns, 1); while(__builtin_expect(fd->turn <= turn_limit && !fd->end, true)) { fd->current_phase = Field::playcard_phase; // Initialize stuff, remove dead cards _DEBUG_MSG(1, "------------------------------------------------------------------------\n" "TURN %u begins for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); -#if 0 - // ANP: If it's the player's turn and he's making a decision, - // reset his points to 0. - if(fd->tapi == 0 && fd->turn <= fd->last_decision_turn) - { - fd->points_since_last_decision = 0; - } -#endif turn_start_phase(fd); - // Special case: refresh on commander - if(fd->tip->commander.has_skill(refresh)) - { - check_and_perform_refresh(fd, &fd->tip->commander); - } - - if(fd->effect == Effect::clone_project || - (fd->effect == Effect::clone_experiment && (fd->turn == 9 || fd->turn == 10))) - { - if(fd->make_selection_array(fd->tap->assaults.m_indirect.begin(), fd->tap->assaults.m_indirect.end(), [](CardStatus* c){return(c->m_delay == 0 && c->m_hp > 0);}) > 0) - { - _DEBUG_SELECTION("Clone effect"); - CardStatus* c(fd->selection_array[fd->rand(0, fd->selection_array.size() - 1)]); - _DEBUG_MSG(1, "%s gains skill Split until end of turn.\n", status_description(c).c_str()); - c->m_temporary_split = true; - } - } // Play a card const Card* played_card(fd->tap->deck->next()); @@ -768,10 +370,6 @@ Results play(Field* fd) { switch(played_card->m_type) { - case CardType::action: - // end: handles commander death by shock - PlayCard(played_card, fd).op(); - break; case CardType::assault: PlayCard(played_card, fd).op(); break; @@ -787,24 +385,18 @@ Results play(Field* fd) } if(__builtin_expect(fd->end, false)) { break; } -#if defined(TYRANT_UNLEASHED) - // Evaluate TU Battleground effect (Enhance all) if (fd->bg_enhanced_skill != no_skill) { - SkillSpec battleground_s{enhance, fd->bg_enhanced_value, allfactions, 0, fd->bg_enhanced_skill, true, SkillMod::on_activate}; + // Evaluate TU Battleground effect (Enhance all) + SkillSpec battleground_s = {enhance, fd->bg_enhanced_value, allfactions, 0, 0, fd->bg_enhanced_skill, true}; _DEBUG_MSG(2, "Evaluating Battleground skill %s\n", skill_description(fd->cards, battleground_s).c_str()); fd->skill_queue.emplace_back(&fd->tap->commander, battleground_s); resolve_skill(fd); } -#else - // Evaluate Legion skill - fd->current_phase = Field::legion_phase; - evaluate_legion(fd); -#endif // Evaluate commander fd->current_phase = Field::commander_phase; - evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills[SkillMod::on_activate], SkillMod::on_activate); + evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); if(__builtin_expect(fd->end, false)) { break; } // Evaluate structures @@ -814,7 +406,7 @@ Results play(Field* fd) CardStatus& current_status(fd->tap->structures[fd->current_ci]); if(current_status.m_delay == 0 && current_status.m_hp > 0) { - evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills[SkillMod::on_activate], SkillMod::on_activate); + evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); } } // Evaluate assaults @@ -830,32 +422,30 @@ Results play(Field* fd) } else { - current_status->m_blitzing = false; // XXX: assaults cannot be buffed in its blitzing turn? unsigned num_actions(1); for(unsigned action_index(0); action_index < num_actions; ++action_index) { // Evaluate skills current_status->m_step = CardStep::none; - evaluate_skills(fd, current_status, current_status->m_card->m_skills[SkillMod::on_activate], SkillMod::on_activate); - if(__builtin_expect(fd->end, false)) { break; } + evaluate_skills(fd, current_status, current_status->m_card->m_skills); + // no commander-killing skill yet // if(__builtin_expect(fd->end, false)) { break; } // Attack if(can_attack(current_status)) { current_status->m_step = CardStep::attacking; attacked = attack_phase(fd) || attacked; + if(__builtin_expect(fd->end, false)) { break; } } else { _DEBUG_MSG(2, "Assault %s cannot take attack.\n", status_description(current_status).c_str()); } -#if defined(TYRANT_UNLEASHED) - // [TU] Flurry - if (can_act(current_status) && fd->tip->commander.m_hp > 0 && current_status->has_skill(flurry) && current_status->m_skill_cd[flurry] == 0) + // Flurry + if (can_act(current_status) && fd->tip->commander.m_hp > 0 && current_status->has_skill() && current_status->m_skill_cd[flurry] == 0) { - count_achievement(fd, current_status); _DEBUG_MSG(1, "%s activates Flurry\n", status_description(current_status).c_str()); num_actions = 2; - for (const auto & ss : current_status->m_card->m_skills[SkillMod::on_activate]) + for (const auto & ss : current_status->m_card->m_skills) { if (ss.id == flurry) { @@ -863,10 +453,8 @@ Results play(Field* fd) } } } -#endif } } -#if defined(TYRANT_UNLEASHED) if (current_status->m_corroded_rate > 0) { if (attacked) @@ -882,7 +470,6 @@ Results play(Field* fd) current_status->m_corroded_weakened = 0; } } -#endif current_status->m_step = CardStep::attacked; } fd->current_phase = Field::end_phase; @@ -892,19 +479,6 @@ Results play(Field* fd) std::swap(fd->tapi, fd->tipi); std::swap(fd->tap, fd->tip); ++fd->turn; - fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::turns); - } - bool made_achievement = true; - if(fd->optimization_mode == OptimizationMode::achievement) - { - for(unsigned i(0); made_achievement && i < fd->achievement.req_counter.size(); ++i) - { - made_achievement = made_achievement && fd->achievement.req_counter[i].check(fd->achievement_counter[i]); - } - if(debug_print) - { - print_achievement_results(fd); - } } // you lose if(fd->players[0]->commander.m_hp == 0) @@ -929,21 +503,7 @@ Results play(Field* fd) // you win if(fd->players[1]->commander.m_hp == 0) { - if (fd->optimization_mode == OptimizationMode::achievement && !made_achievement) - { - _DEBUG_MSG(1, "You win but no achievement.\n"); - return {1, 0, 0, 0, 0}; - } _DEBUG_MSG(1, "You win.\n"); -#if 0 - // ANP: Speedy if turn < last_decision + 10. - bool speedy = fd->turn < fd->last_decision_turn + 10; - if(fd->points_since_last_decision > 10) - { - fd->points_since_last_decision = 10; - } - return {1, 0, 0, 10 + (speedy ? 5 : 0) + (fd->gamemode == surge ? 20 : 0) + fd->points_since_last_decision, 0}; -#endif return {1, 0, 0, 100, 0}; } if (fd->turn > turn_limit) @@ -960,246 +520,53 @@ Results play(Field* fd) return {0, 0, 0, 0, 0}; } -// Roll a coin in case an Activation skill has 50% chance to proc. -template -inline bool skill_roll(Field* fd) -{ return(true); } - -#if not defined(TYRANT_UNLEASHED) -template<> -inline bool skill_roll(Field* fd) -{ return(fd->flip()); } -#endif - // Check if a skill actually proc'ed. template inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { return(true); } -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return ref->has_skill(flying); -} - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return(!c->m_sundered); -} - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - unsigned opponent_player = opponent(c->m_player); - return(fd->current_phase != Field::assaults_phase && - fd->players[opponent_player]->assaults.size() > c->m_index && - fd->players[opponent_player]->assaults[c->m_index].m_hp > 0 && - fd->players[opponent_player]->assaults[c->m_index].m_delay == 0); -} - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return(ref->m_card->m_type == CardType::assault && ref->m_hp == ref->m_card->m_health); -} - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return(!ref->m_diseased); -} - template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { return(c->m_player != ref->m_player); } -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return !ref->has_skill(flying) && ref->skill(antiair) == 0; -} - -// Not yet support on Attacked/on Death. -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return(!ref->m_immobilized && !is_jammed(ref) && is_active_next_turn(ref)); -} - template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { return(can_be_healed(c)); } -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +void remove_hp(Field* fd, CardStatus& status, unsigned dmg) { - // Never payback allied units (chaosed). - return(ref->m_card->m_type == CardType::assault && c->m_player != ref->m_player && ref->m_hp > 0); + assert(status.m_hp > 0); + _DEBUG_MSG(2, "%s takes %u damage\n", status_description(&status).c_str(), dmg); + status.m_hp = safe_minus(status.m_hp, dmg); + if(status.m_hp == 0) + { + _DEBUG_MSG(1, "%s dies\n", status_description(&status).c_str()); + } } - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +inline bool is_it_dead(CardStatus& c) { - return(!ref->m_phased); + if(c.m_hp == 0) // yes it is + { + _DEBUG_MSG(1, "Dead and removed: %s\n", status_description(&c).c_str()); + return(true); + } + else { return(false); } // nope still kickin' } - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ return(can_be_healed(c)); } - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +inline void remove_dead(Storage& storage) { - return(c->m_hp == 0 && !c->m_diseased); + storage.remove(is_it_dead); } - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +inline void add_hp(Field* fd, CardStatus* target, unsigned v) { - return(can_be_healed(&fd->players[c->m_player]->commander)); + target->m_hp = std::min(target->m_hp + v, target->m_card->m_health); } - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +void cooldown_skills(CardStatus & status) { - return(ref->m_card->m_type == CardType::assault); -} - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return(!ref->m_sundered); -} - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return(ref->m_card->m_type == CardType::assault && ref != c && ref->m_hp > 0); -} - -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - unsigned count_ta(0); - unsigned count_td(0); - for(unsigned i(0); i < fd->tap->assaults.size(); ++i) - { - if(fd->tap->assaults[i].m_hp > 0) { ++count_ta; } - } - for(unsigned i(0); i < fd->tip->assaults.size(); ++i) - { - if(fd->tip->assaults[i].m_hp > 0) { ++count_td; } - } - return(count_ta < count_td); -} - -template -inline bool count_achievement(Field* fd, const CardStatus* c) -{ -#if not defined(TYRANT_UNLEASHED) - if(c->m_player == 0) - { - fd->inc_counter(fd->achievement.skill_used, skill_id); - if(skill_id != Skill::attack) - { - fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::skill_activated); - } - } -#endif - return(true); -} - -//------------------------------------------------------------------------------ -// All the stuff that happens at the beginning of a turn, before a card is played -// returns true iff the card died. -inline void count_killed_achievements(Field* fd, const CardStatus* status) -{ - if(status->m_player == 1) - { - if(!status->m_is_summoned) - { - fd->inc_counter(fd->achievement.unit_type_killed, status->m_card->m_type); - } - if(status->has_skill(flying)) - { - fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::unit_with_flying_killed); - } - } -} -void remove_hp(Field* fd, CardStatus& status, unsigned dmg) -{ - assert(status.m_hp > 0); - _DEBUG_MSG(2, "%s takes %u damage\n", status_description(&status).c_str(), dmg); - status.m_hp = safe_minus(status.m_hp, dmg); - if(status.m_hp == 0) - { - _DEBUG_MSG(1, "%s dies\n", status_description(&status).c_str()); - if(status.m_card->m_skills[SkillMod::on_death].size() > 0 || fd->effect == Effect::haunt) - { - fd->killed_with_on_death.push_back(&status); - } - if(status.has_skill(regenerate)) - { - fd->killed_with_regen.push_back(&status); - } - else - { - count_killed_achievements(fd, &status); - } - } -} -inline bool is_it_dead(CardStatus& c) -{ - if(c.m_hp == 0) // yes it is - { - if(c.m_card->m_type != CardType::action) - { - _DEBUG_MSG(1, "Dead and removed: %s\n", status_description(&c).c_str()); - } - return(true); - } - else { return(false); } // nope still kickin' -} -inline void remove_dead(Storage& storage) -{ - storage.remove(is_it_dead); -} -inline void add_hp(Field* fd, CardStatus* target, unsigned v) -{ - unsigned old_hp = target->m_hp; - target->m_hp = std::min(target->m_hp + v, target->m_card->m_health); - if(fd->effect == Effect::invigorate && target->m_card->m_type == CardType::assault && skill_check(fd, target, nullptr)) - { - unsigned healed = target->m_hp - old_hp; - target->m_berserk += healed; - } -} -void check_regeneration(Field* fd) -{ - for(unsigned i(0); i < fd->killed_with_regen.size(); ++i) - { - CardStatus* status = fd->killed_with_regen[i]; - if(fd->flip() && skill_check(fd, status, nullptr)) - { - count_achievement(fd, status); - _DEBUG_MSG(1, "%s regenerates with %u health\n", status_description(status).c_str(), status->m_card->m_health); - add_hp(fd, status, status->skill(regenerate)); - } - else - { - count_killed_achievements(fd, status); - } - } - fd->killed_with_regen.clear(); -} -void cooldown_skills(CardStatus & status) -{ - for (const auto & ss : status.m_card->m_skills[SkillMod::on_activate]) + for (const auto & ss : status.m_card->m_skills) { if (status.m_skill_cd[ss.id] > 0) { @@ -1210,11 +577,8 @@ void cooldown_skills(CardStatus & status) } void turn_start_phase(Field* fd) { - fd->fusion_count = 0; // Active player's commander card: -#if defined(TYRANT_UNLEASHED) cooldown_skills(fd->tap->commander); -#endif // Active player's assault cards: // update index // reduce delay; [TU] reduce skill cooldown @@ -1227,21 +591,12 @@ void turn_start_phase(Field* fd) { CardStatus& status(assaults[index]); status.m_index = index; - if(status.m_delay > 0 && !status.m_frozen) + if(status.m_delay > 0) { _DEBUG_MSG(1, "%s reduces its timer\n", status_description(&status).c_str()); -- status.m_delay; } -#if defined(TYRANT_UNLEASHED) cooldown_skills(status); -#else - if(status.m_poisoned > 0) - { - _DEBUG_MSG(1, "%s takes poison damage\n", status_description(&status).c_str()); - remove_hp(fd, status, status.m_poisoned); - } - if(status.has_skill(fusion) && status.m_delay == 0) { ++ fd->fusion_count; } -#endif } } // Active player's structure cards: @@ -1260,11 +615,7 @@ void turn_start_phase(Field* fd) _DEBUG_MSG(1, "%s reduces its timer\n", status_description(&status).c_str()); --status.m_delay; } -#if defined(TYRANT_UNLEASHED) cooldown_skills(status); -#else - if(status.has_skill(fusion) && status.m_delay == 0) { ++fd->fusion_count; } -#endif } } // Defending player's assault cards: @@ -1291,20 +642,12 @@ void turn_start_phase(Field* fd) status.m_index = index; } } -#if not defined(TYRANT_UNLEASHED) - // Perform on death skills (from cards killed by poison damage) - prepend_on_death(fd); - resolve_skill(fd); - // Regen from poison - check_regeneration(fd); -#endif } void turn_end_phase(Field* fd) { // Active player's assault cards: - // remove augment, chaos, freeze, immobilize, jam, rally, weaken; apply refresh - // remove temp split - // [TU] apply poison damage + // remove jam, rally, weaken + // apply poison damage { auto& assaults(fd->tap->assaults); for(unsigned index(0), end(assaults.size()); @@ -1317,56 +660,26 @@ void turn_end_phase(Field* fd) continue; } status.m_jammed = false; + status.m_overloaded = false; status.m_rallied = 0; status.m_weakened = 0; status.m_step = CardStep::none; -#if defined(TYRANT_UNLEASHED) status.m_inhibited = 0; - unsigned poison_dmg = safe_minus(status.m_poisoned, status.m_protected); + unsigned poison_dmg = safe_minus(status.m_poisoned, status.protected_value()); if(poison_dmg > 0) { _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg); remove_hp(fd, status, poison_dmg); } -#else +#if 0 // not need to fade out in own turn in TU status.m_enfeebled = 0; - // not in TU - status.m_augmented = 0; - status.m_chaosed = false; - status.m_frozen = false; - status.m_immobilized = false; - status.m_phased = false; - if(status.m_stunned > 0) { -- status.m_stunned; } - status.m_temporary_split = false; - if(status.has_skill(refresh)) - { - check_and_perform_refresh(fd, &status); - } #endif } } // Active player's structure cards: - // apply refresh -#if not defined(TYRANT_UNLEASHED) - { - auto& structures(fd->tap->structures); - for(unsigned index(0), end(structures.size()); - index < end; - ++index) - { - CardStatus& status(structures[index]); - if (status.m_hp <= 0) - { - continue; - } - if(status.has_skill(refresh) && fd->effect != Effect::impenetrable) - { - check_and_perform_refresh(fd, &status); - } - } - } -#endif + // nothing so far + // Defending player's assault cards: // remove enfeeble, protect { @@ -1382,11 +695,9 @@ void turn_end_phase(Field* fd) } status.m_enfeebled = 0; status.m_protected = 0; -#if defined(TYRANT_UNLEASHED) // so far only useful in Defending turn status.m_evaded = 0; std::memset(status.m_enhanced_value, 0, sizeof status.m_enhanced_value); -#endif } } // Defending player's structure cards: @@ -1411,65 +722,21 @@ void turn_end_phase(Field* fd) remove_dead(fd->tip->assaults); remove_dead(fd->tip->structures); } -void evaluate_legion(Field* fd) -{ - // Not subject to Mimic / Emulate / Augment - // Not prevented by Jam / Freeze / Immobilize - // Honor Infused faction - auto& assaults = fd->tap->assaults; - for(fd->current_ci = 0; fd->current_ci < assaults.size(); ++fd->current_ci) - { - CardStatus* status(&assaults[fd->current_ci]); - unsigned legion_base = fd->effect == Effect::united_front ? status->m_card->m_delay : status->skill(legion); - if(legion_base == 0) - { - continue; - } - unsigned legion_size(0); - legion_size += status->m_index > 0 && assaults[status->m_index - 1].m_hp > 0 && assaults[status->m_index - 1].m_faction == status->m_faction; - legion_size += status->m_index + 1 < assaults.size() && assaults[status->m_index + 1].m_hp > 0 && assaults[status->m_index + 1].m_faction == status->m_faction; - if(legion_size == 0) - { - continue; - } - // skill_check - bool do_heal = can_be_healed(status); - bool do_rally = status->m_hp > 0 && !status->m_sundered && (fd->tapi == status->m_player ? status->m_delay == 0 || status->m_blitzing : status->m_delay <= 1); - if(!do_heal && !do_rally) - { - continue; - } - count_achievement(fd, status); - unsigned legion_value = legion_base * legion_size; - _DEBUG_MSG(1, "%s activates Legion %u, %s%s%s by %u\n", status_description(status).c_str(), legion_base, - do_heal ? "healed" : "", do_heal && do_rally ? " and " : "", do_rally ? "rallied" : "", legion_value); - if(do_heal) - { - add_hp(fd, status, legion_value); - } - if(do_rally) - { - status->m_rallied += legion_value; - } - } -} //---------------------- $50 attack by assault card implementation ------------- // Counter damage dealt to the attacker (att) by defender (def) // pre-condition: only valid if m_card->m_counter > 0 inline unsigned counter_damage(Field* fd, CardStatus* att, CardStatus* def) { assert(att->m_card->m_type == CardType::assault); - assert(def->m_card->m_type != CardType::action); - return(safe_minus(def->skill(counter) + att->m_enfeebled, att->m_protected)); + return(safe_minus(def->skill() + att->m_enfeebled, att->protected_value())); } inline CardStatus* select_first_enemy_wall(Field* fd) { for(unsigned i(0); i < fd->tip->structures.size(); ++i) { CardStatus& c(fd->tip->structures[i]); - if(c.has_skill(wall) && c.m_hp > 0 && skill_check(fd, &c, nullptr)) + if(c.has_skill() && c.m_hp > 0 && skill_check(fd, &c, nullptr)) { - count_achievement(fd, &c); return(&c); } } @@ -1494,7 +761,6 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg, bool count #if 0 fd->points_since_last_decision += dmg; #endif - fd->inc_counter(fd->achievement.misc_req, AchievementMiscReq::com_total, dmg); } if(status.m_hp == 0) { @@ -1522,96 +788,48 @@ struct PerformAttack { unsigned pre_modifier_dmg = attack_power(att_status); if(pre_modifier_dmg == 0) { return; } - count_achievement(fd, att_status); // Evaluation order: - // assaults only: fly check // modify damage - // assaults only: immobilize // deal damage - // assaults only: (siphon, poison, disease, sunder, phase, on_kill) - // on_attacked: poison, disease, sunder, assaults only: berserk, skills + // assaults only: (poison) // counter, berserk - // assaults only: (crush, leech if still alive) - // check regeneration -#if not defined(TYRANT_UNLEASHED) - if(def_status->has_skill(flying) && (fd->effect == Effect::high_skies || fd->flip()) && skill_check(fd, def_status, att_status)) - { - count_achievement(fd, def_status); - _DEBUG_MSG(1, "%s attacks %s but it dodges with Flying\n", status_description(att_status).c_str(), status_description(def_status).c_str()); - return; - } -#endif + // assaults only: (leech if still alive) modify_attack_damage(pre_modifier_dmg); - if(att_status->m_player == 0) - { - fd->update_max_counter(fd->achievement.misc_req, AchievementMiscReq::damage, att_dmg); - } -#if not defined(TYRANT_UNLEASHED) - // If Impenetrable, prevent attack damage against walls, - // but still activate Counter! - if(att_dmg > 0 && fd->effect == Effect::impenetrable && def_status->has_skill(wall)) - { - _DEBUG_MSG(1, "%s is impenetrable\n", status_description(def_status).c_str()); - att_dmg = 0; - } -#endif if(att_dmg > 0) { - performImmobilize(); attack_damage(); if(__builtin_expect(fd->end, false)) { return; } damage_dependant_pre_oa(); - on_kill(); } - on_attacked(); if(att_dmg > 0) { if(att_status->m_hp > 0) { -#if not defined(TYRANT_UNLEASHED) - if(def_status->has_skill(stun) && skill_check(fd, def_status, att_status)) - { - count_achievement(fd, def_status); - // perform_skill_stun - _DEBUG_MSG(1, "%s stuns %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); - att_status->m_stunned = 2; - } -#endif - if(def_status->has_skill(counter) && skill_check(fd, def_status, att_status)) + if(def_status->has_skill() && skill_check(fd, def_status, att_status)) { - count_achievement(fd, def_status); // perform_skill_counter unsigned counter_dmg(counter_damage(fd, att_status, def_status)); _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, *att_status, counter_dmg); } - unsigned berserk_value = att_status->skill(berserk); + unsigned berserk_value = att_status->skill(); if(berserk_value > 0 && skill_check(fd, att_status, nullptr)) { - count_achievement(fd, att_status); // perform_skill_berserk att_status->m_berserk += berserk_value; } -#if defined(TYRANT_UNLEASHED) - unsigned corrosive_value = def_status->skill(corrosive); + unsigned corrosive_value = def_status->skill(); if (corrosive_value > att_status->m_corroded_rate && skill_check(fd, def_status, att_status)) { - count_achievement(fd, def_status); // perform_skill_corrosive _DEBUG_MSG(1, "%s corrodes %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), corrosive_value); att_status->m_corroded_rate = corrosive_value; } -#endif } - crush_leech(); + do_leech(); } -#if not defined(TYRANT_UNLEASHED) - prepend_on_death(fd); - resolve_skill(fd); - check_regeneration(fd); -#endif } template @@ -1622,29 +840,6 @@ struct PerformAttack att_dmg = pre_modifier_dmg; // enhance damage std::string desc; -#if not defined(TYRANT_UNLEASHED) - unsigned valor_value = att_status->skill(valor); - if(valor_value > 0 && skill_check(fd, att_status, nullptr)) - { - count_achievement(fd, att_status); - if(debug_print) { desc += "+" + to_string(valor_value) + "(valor)"; } - att_dmg += valor_value; - } - unsigned antiair_value = att_status->skill(antiair); - if(antiair_value > 0 && skill_check(fd, att_status, def_status)) - { - count_achievement(fd, att_status); - if(debug_print) { desc += "+" + to_string(antiair_value) + "(antiair)"; } - att_dmg += antiair_value; - } - unsigned burst_value = att_status->skill(burst); - if(burst_value > 0 && skill_check(fd, att_status, def_status)) - { - count_achievement(fd, att_status); - if(debug_print) { desc += "+" + to_string(burst_value) + "(burst)"; } - att_dmg += burst_value; - } -#endif if(def_status->m_enfeebled > 0) { if(debug_print) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } @@ -1653,31 +848,21 @@ struct PerformAttack // prevent damage std::string reduced_desc; unsigned reduced_dmg(0); - unsigned armored_value = def_status->skill(armored); - if(armored_value == 0 && fd->effect == Effect::photon_shield && def_status->m_player == (fd->optimization_mode == OptimizationMode::defense ? 0u : 1u)) - { - armored_value = 2; - } + unsigned armored_value = def_status->skill(); if(armored_value > 0) { - // Armored counts if not totally cancelled by Pierce. TODO how if Armored + Proteced > Pierce? - if(armored_value > att_status->skill(pierce)) - { - count_achievement(fd, def_status); - } if(debug_print) { reduced_desc += to_string(armored_value) + "(armored)"; } reduced_dmg += armored_value; } - if(def_status->m_protected > 0) + if(def_status->protected_value() > 0) { - if(debug_print) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->m_protected) + "(protected)"; } - reduced_dmg += def_status->m_protected; + if(debug_print) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->protected_value()) + "(protected)"; } + reduced_dmg += def_status->protected_value(); } - if(reduced_dmg > 0 && att_status->skill(pierce) > 0) + if(reduced_dmg > 0 && att_status->skill() > 0) { - // TODO No Pierce achievement yet, so no count_achievement(fd, att_status) - if(debug_print) { reduced_desc += "-" + to_string(att_status->skill(pierce)) + "(pierce)"; } - reduced_dmg = safe_minus(reduced_dmg, att_status->skill(pierce)); + if(debug_print) { reduced_desc += "-" + to_string(att_status->skill()) + "(pierce)"; } + reduced_dmg = safe_minus(reduced_dmg, att_status->skill()); } att_dmg = safe_minus(att_dmg, reduced_dmg); if(debug_print) @@ -1688,9 +873,6 @@ struct PerformAttack } } - template - void performImmobilize() {} - template void attack_damage() { @@ -1702,57 +884,9 @@ struct PerformAttack void damage_dependant_pre_oa() {} template - void on_kill() {} - - template - void on_attacked() - { - unsigned poison_value = def_status->skill(poison, SkillMod::on_attacked); - if(poison_value > att_status->m_poisoned && skill_check(fd, def_status, att_status)) - { - count_achievement(fd, def_status); - // perform_skill_poison - _DEBUG_MSG(1, "%s (on attacked) poisons %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), poison_value); - att_status->m_poisoned = poison_value; - } - if(def_status->has_skill(disease, SkillMod::on_attacked) && skill_check(fd, def_status, att_status)) - { - count_achievement(fd, def_status); - // perform_skill_disease - _DEBUG_MSG(1, "%s (on attacked) diseases %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); - att_status->m_diseased = true; - } - unsigned berserk_value = def_status->skill(berserk, SkillMod::on_attacked); - if(def_status->m_hp > 0 && berserk_value > 0 && skill_check(fd, def_status, nullptr)) - { - count_achievement(fd, def_status); - def_status->m_berserk += berserk_value; - } - if(def_status->has_skill(sunder, SkillMod::on_attacked) && skill_check(fd, def_status, att_status)) - { - count_achievement(fd, def_status); - // perform_skill_sunder - _DEBUG_MSG(1, "%s (on attacked) sunders %s\n", status_description(def_status).c_str(), status_description(att_status).c_str()); - att_status->m_sundered = true; - } - evaluate_skills(fd, def_status, def_status->m_card->m_skills[SkillMod::on_attacked], SkillMod::on_attacked); - } - - template - void crush_leech() {} + void do_leech() {} }; -template<> -void PerformAttack::performImmobilize() -{ - if(att_status->has_skill(immobilize) && fd->flip() && skill_check(fd, att_status, def_status)) - { - count_achievement(fd, att_status); - _DEBUG_MSG(1, "%s immobilizes %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); - def_status->m_immobilized = true; - } -} - template<> void PerformAttack::attack_damage() { @@ -1762,91 +896,28 @@ void PerformAttack::attack_damage() template<> void PerformAttack::damage_dependant_pre_oa() { -#if not defined(TYRANT_UNLEASHED) - unsigned siphon_value = std::min(att_dmg, att_status->skill(siphon)); - if(siphon_value > 0 && skill_check(fd, att_status, def_status)) - { - count_achievement(fd, att_status); - // perform_skill_siphon - _DEBUG_MSG(1, "%s siphons %u health for %s\n", status_description(att_status).c_str(), siphon_value, status_description(&fd->tap->commander).c_str()); - add_hp(fd, &fd->tap->commander, siphon_value); - } -#endif - unsigned poison_value = att_status->skill(poison); + unsigned poison_value = att_status->skill(); if(poison_value > def_status->m_poisoned && skill_check(fd, att_status, def_status)) { - count_achievement(fd, att_status); // perform_skill_poison _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), poison_value); def_status->m_poisoned = poison_value; } -#if defined(TYRANT_UNLEASHED) - unsigned inhibit_value = att_status->skill(inhibit); + unsigned inhibit_value = att_status->skill(); if (inhibit_value > def_status->m_inhibited && skill_check(fd, att_status, def_status)) { - count_achievement(fd, att_status); // perform_skill_inhibit _DEBUG_MSG(1, "%s inhibits %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), inhibit_value); def_status->m_inhibited = inhibit_value; } -#else - if(att_status->has_skill(disease) && skill_check(fd, att_status, def_status)) - { - count_achievement(fd, att_status); - // perform_skill_disease - _DEBUG_MSG(1, "%s diseases %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); - def_status->m_diseased = true; - } - if(att_status->has_skill(sunder) && skill_check(fd, att_status, def_status)) - { - count_achievement(fd, att_status); - // perform_skill_sunder - _DEBUG_MSG(1, "%s sunders %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); - def_status->m_sundered = true; - } - if(att_status->has_skill(phase) && skill_check(fd, att_status, def_status)) - { - count_achievement(fd, att_status); - // perform_skill_phase - _DEBUG_MSG(1, "%s phases %s\n", status_description(att_status).c_str(), status_description(def_status).c_str()); - def_status->m_phased = true; - } -#endif } template<> -void PerformAttack::on_kill() +void PerformAttack::do_leech() { - if(killed_by_attack) - { - evaluate_skills(fd, att_status, att_status->m_card->m_skills[SkillMod::on_kill], SkillMod::on_kill); - } -} - -template<> -void PerformAttack::crush_leech() -{ - unsigned crush_value = att_status->skill(crush); - if(crush_value > 0 && killed_by_attack && skill_check(fd, att_status, nullptr)) - { - count_achievement(fd, att_status); - // perform_skill_crush - CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall - if (def_status != nullptr) - { - _DEBUG_MSG(1, "%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), crush_value); - remove_hp(fd, *def_status, crush_value); - } - else - { - _DEBUG_MSG(1, "%s crushes %s for %u damage\n", status_description(att_status).c_str(), status_description(&fd->tip->commander).c_str(), crush_value); - remove_commander_hp(fd, fd->tip->commander, crush_value, true); - } - } - unsigned leech_value = std::min(att_dmg, att_status->skill(leech)); + unsigned leech_value = std::min(att_dmg, att_status->skill()); if(leech_value > 0 && skill_check(fd, att_status, nullptr)) { - count_achievement(fd, att_status); _DEBUG_MSG(1, "%s leeches %u health\n", status_description(att_status).c_str(), leech_value); add_hp(fd, att_status, leech_value); } @@ -1874,75 +945,21 @@ bool attack_phase(Field* fd) { return false; } - unsigned num_attacks(1); -#if not defined(TYRANT_UNLEASHED) - unsigned flurry_value = att_status->skill(flurry); - if(flurry_value > 0 && fd->flip() && skill_check(fd, att_status, nullptr)) + + if (alive_assault(def_assaults, fd->current_ci)) { - count_achievement(fd, att_status); - _DEBUG_MSG(1, "%s activates Flurry %u\n", status_description(att_status).c_str(), flurry_value); - num_attacks += flurry_value; + PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); } -#endif - for(unsigned attack_index(0); attack_index < num_attacks && can_attack(att_status) && fd->tip->commander.m_hp > 0; ++attack_index) + else { - // 3 possibilities: - // - 1. attack against the assault in front - // - 2. swipe attack the assault in front and adjacent assaults if any - // * Attack Commander/wall if opposing Assault is already dead before Swipe finishes (Swipe will attempt to attack the third Assault) - // See http://www.kongregate.com/forums/65-tyrant/topics/289416?page=22#posts-6861970 - // - 3. attack against the commander or walls (if there is no assault or if the attacker has the fear attribute) - // Check if attack mode is 1. or 2. (there is a living assault card in front, and no fear) - if(alive_assault(def_assaults, fd->current_ci) && !(att_status->has_skill(fear) && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) - { - // attack mode 1. - if(!(att_status->has_skill(swipe) && skill_check(fd, att_status, nullptr) && count_achievement(fd, att_status))) - { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); - } - // attack mode 2. - else - { - // perform_skill_swipe - _DEBUG_MSG(1, "%s activates Swipe\n", status_description(att_status).c_str()); - // attack the card on the left - if(fd->current_ci > 0 && alive_assault(def_assaults, fd->current_ci - 1)) - { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci-1]}.op(); - } - if(fd->end || !can_attack(att_status)) { return true; } - // attack the card in front (or attacks the commander if the card in front is just died) - if(alive_assault(def_assaults, fd->current_ci)) - { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); - } - else - { - attack_commander(fd, att_status); - } - if(fd->end || !can_attack(att_status)) { return true; } - // attack the card on the right - if(alive_assault(def_assaults, fd->current_ci + 1)) - { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci+1]}.op(); - } - } - } - // attack mode 3. - else - { - attack_commander(fd, att_status); - } + // might be blocked by walls + attack_commander(fd, att_status); } + return true; } //---------------------- $65 active skills implementation ---------------------- -unsigned strike_damage(CardStatus* target, unsigned v) -{ - return(safe_minus(v + target->m_enfeebled, target->m_protected)); -} - template< bool C , typename T1 @@ -1963,328 +980,148 @@ struct if_ }; template -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ assert(false); return(false); } +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ return dst->m_hp > 0; } template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - const auto& mod = s.mod; - if(can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))) - (src->m_player != c->m_player || mod == SkillMod::on_death ? (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)) : - mod == SkillMod::on_attacked ? is_active_next_turn(c) : - is_active(c) && !is_attacking_or_has_attacked(c))) - { - for(auto& s: c->m_card->m_skills[SkillMod::on_activate]) - { - // Any quantifiable skill except augment - if(s.x > 0 && s.id != augment && s.id != summon) { return(true); } - } - bool need_add_skill = true; - auto mod = SkillMod::on_activate; - if(may_change_skill(fd, c, mod)) - { - auto s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, 0, no_skill, false, mod}, mod, need_add_skill); - assert(s.id != no_skill); - if(s.x > 0 && s.id != augment && s.id != summon) { return(true); } - } - } - return(false); + return dst->has_skill(s.s) && ((BEGIN_DEFENSIVE < s.s && s.s < END_DEFENSIVE) || is_active(dst)); } template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ - const auto& mod = s.mod; - return(!c->m_chaosed && can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c))); - (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : - mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : - is_active(c) || is_active_next_turn(c))); -} +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ return(can_be_healed(dst)); } template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ - return(fd->effect != Effect::decay && - c->m_hp > 0 && ( - c->m_chaosed || - c->m_diseased || - c->m_enfeebled > 0 || - (c->m_frozen && c->m_delay == 0) || - c->m_immobilized || - c->m_jammed || - c->m_poisoned || - c->m_stunned || - c->m_sundered - )); +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ + return can_act(dst) && is_active_next_turn(dst); } template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - bool need_add_skill = true; - auto mod = SkillMod::on_activate; - if(may_change_skill(fd, c, mod)) + if (dst->m_overloaded || ! (is_active(dst) && can_act(dst))) { - auto battleground_s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, 0, no_skill, false, mod}, mod, need_add_skill); - assert(battleground_s.id != no_skill); - if(battleground_s.id == s.s) { return(true); } + return false; } - return c->has_skill(s.s) && (defensive_skills.find(s.s) != defensive_skills.end() || is_active(c)); -} - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ return(c->m_hp > 0); } - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ return(c->m_hp > 0 && !c->m_jammed && !c->m_frozen); } - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ return(can_be_healed(c)); } - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ return(c->m_faction != bloodthirsty); } - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ -#if defined(TYRANT_UNLEASHED) - return can_act(c) && is_active_next_turn(c); -#else - const auto& mod = s.mod; - return can_act(c) && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)); - (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : - mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : - is_active(c) || is_active_next_turn(c)); -#endif -} - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ return(c->m_hp > 0); } - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ return(c->m_hp > 0); } - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ -#if defined(TYRANT_UNLEASHED) - return can_attack(c) && is_active(c) && !is_attacking_or_has_attacked(c); -#else - const auto& mod = s.mod; - return can_attack(c) && !c->m_sundered && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)); - (src->m_player != c->m_player || mod == SkillMod::on_death ? (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)) : - mod == SkillMod::on_attacked ? is_active_next_turn(c) : - is_active(c) && !is_attacking_or_has_attacked(c)); -#endif -} - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ return(can_be_healed(c)); } - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ return(c->m_delay > 0); } - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ return(c->m_hp > 0); } - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ return(c->m_hp > 0); } - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ return(can_be_healed(c)); } - -template<> -inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* c, const SkillSpec& s) -{ -#if defined(TYRANT_UNLEASHED) - return can_attack(c) && attack_power(c) > 0 && is_active_next_turn(c); -#else - const auto& mod = s.mod; - return can_attack(c) && attack_power(c) > 0 && // (fd->tapi == c->m_player ? is_active(c) && !is_attacking_or_has_attacked(c) : is_active_next_turn(c)); - (mod == SkillMod::on_attacked ? is_active(c) && c->m_index > fd->current_ci : - mod == SkillMod::on_death ? c->m_index >= src->m_index && (fd->tapi != src->m_player ? is_active(c) : is_active_next_turn(c)) : - is_active(c) || is_active_next_turn(c)); -#endif -} - -template -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) -{ assert(false); } - -template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) -{ - c->m_augmented += s.x; -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) -{ - // backfire damage counts in ARD. - remove_commander_hp(fd, *c, s.x, true); -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) -{ - c->m_chaosed = true; -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) -{ - c->m_chaosed = false; - c->m_diseased = false; - c->m_enfeebled = 0; - c->m_frozen = false; - c->m_immobilized = false; - c->m_jammed = false; - c->m_poisoned = 0; - c->m_stunned = 0; - c->m_sundered = false; -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) -{ - c->m_enfeebled += s.x; -} - -template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) -{ - c->m_enhanced_value[s.s] += s.x; + bool has_inhibited_unit = false; + for (const auto & c: fd->players[dst->m_player]->assaults.m_indirect) + { + if (c->m_hp > 0 && c->m_inhibited) + { + has_inhibited_unit = true; + break; + } + } + for (const auto & s: dst->m_card->m_skills) + { + if (BEGIN_ACTIVATION_HARMFUL < s.id && s.id < END_ACTIVATION_HARMFUL) + { + return true; + } + if (has_inhibited_unit && (/* s.id == enhance ||*/ s.id == heal || /*s.id == overload ||*/ s.id == protect || s.id == rally)) + { + return true; + } + } + return false; } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - c->m_frozen = true; + return can_attack(dst) && is_active(dst) && !is_attacking_or_has_attacked(dst); } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - add_hp(fd, c, s.x); + return can_attack(dst) && attack_power(dst) > 0 && is_active_next_turn(dst); } -template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) -{ - c->m_faction = bloodthirsty; - c->m_infused = true; -} +template +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ assert(false); } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - c->m_jammed = true; + dst->m_enfeebled += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - c->m_protected += s.x; + dst->m_enhanced_value[s.s] += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - c->m_rallied += s.x; + add_hp(fd, dst, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - add_hp(fd, c, s.x); + dst->m_jammed = true; } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - c->m_delay = safe_minus(c->m_delay, s.x); + dst->m_overloaded = true; } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - // shock damage counts in ARD. (if attacker ever has the skill) - remove_commander_hp(fd, *c, s.x, true); + dst->m_protected += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - remove_hp(fd, *c, s.x); + dst->m_rallied += s.x; } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - remove_hp(fd, *c, strike_damage(c, s.x)); + remove_hp(fd, *dst, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - add_hp(fd, c, s.x); + unsigned strike_dmg = safe_minus(s.x + dst->m_enfeebled, src->m_overloaded ? 0 : dst->protected_value()); + remove_hp(fd, *dst, strike_dmg); } template<> -inline void perform_skill(Field* fd, CardStatus* c, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - c->m_weakened += s.x; + dst->m_weakened += s.x; } template -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s, bool is_helpful_skill) +inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) { -#if defined(TYRANT_UNLEASHED) - if(s.y == allfactions) - { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s, is_helpful_skill](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); - } - else - { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s, is_helpful_skill](CardStatus* c){return((c->m_faction == s.y || s.y == progenitor) && skill_predicate(fd, src_status, c, s));})); - } -#else if(s.y == allfactions) { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s, is_helpful_skill](CardStatus* c){return(!(is_helpful_skill && c->m_phased) && skill_predicate(fd, src_status, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); } else { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s, is_helpful_skill](CardStatus* c){return(c->m_faction == s.y && !(is_helpful_skill && c->m_phased) && skill_predicate(fd, src_status, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return((c->m_faction == s.y || s.y == progenitor || fd->effect == progenitors) && skill_predicate(fd, src_status, c, s));})); } -#endif -} - -template<> -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s, bool is_helpful_skill) -{ - // mimiced supply by a structure, etc ? - if(!(src_status->m_card->m_type == CardType::assault)) { return(0); } - const unsigned min_index(src_status->m_index - (src_status->m_index == 0 ? 0 : 1)); - const unsigned max_index(src_status->m_index + (src_status->m_index == cards.size() - 1 ? 0 : 1)); - return(fd->make_selection_array(cards.begin() + min_index, cards.begin() + max_index + 1, [fd, src_status, s, is_helpful_skill](CardStatus* c){return(!(is_helpful_skill && c->m_phased) && skill_predicate(fd, src_status, c, s));})); } inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src_status) { - return(fd->players[src_status->m_chaosed ? src_status->m_player : opponent(src_status->m_player)]->assaults.m_indirect); + return(fd->players[opponent(src_status->m_player)]->assaults.m_indirect); } inline std::vector& skill_targets_allied_assault(Field* fd, CardStatus* src_status) @@ -2294,7 +1131,7 @@ inline std::vector& skill_targets_allied_assault(Field* fd, CardSta inline std::vector& skill_targets_hostile_structure(Field* fd, CardStatus* src_status) { - return(fd->players[src_status->m_chaosed ? src_status->m_player : opponent(src_status->m_player)]->structures.m_indirect); + return(fd->players[opponent(src_status->m_player)]->structures.m_indirect); } inline std::vector& skill_targets_allied_structure(Field* fd, CardStatus* src_status) @@ -2309,37 +1146,20 @@ std::vector& skill_targets(Field* fd, CardStatus* src_status) throw; } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_assault(fd, src_status)); } template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } - template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_assault(fd, src_status)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ - if(fd->effect == Effect::copycat) - { return(skill_targets_allied_assault(fd, src_status)); } - else - { return(skill_targets_hostile_assault(fd, src_status)); } -} +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } @@ -2347,90 +1167,30 @@ template<> std::vector& skill_targets(Field* fd, CardStatu template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_structure(fd, src_status)); } - -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_assault(fd, src_status)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_assault(fd, src_status)); } template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_structure(fd, src_status)); } -template -void maybeTriggerRegen(Field* fd) -{ -} - -template<> -void maybeTriggerRegen(Field* fd) -{ - fd->skill_queue.emplace_front(nullptr, SkillSpec{trigger_regen, 0, allfactions, 0, no_skill, false, SkillMod::on_activate}); -} - -CardStatus* select_interceptable(Field* fd, CardStatus* src_status, unsigned index) -{ - CardStatus* status(fd->selection_array[index]); - // do not intercept skills from allied units (Chaosed / Infuse) - if(src_status->m_player == status->m_player) - { - return(fd->selection_array[index]); - } - if(index > 0) - { - CardStatus* left_status(fd->selection_array[index - 1]); - if(left_status->has_skill(intercept) && left_status->m_index == status->m_index - 1 && left_status->m_player == status->m_player && skill_check(fd, left_status, status)) - { - count_achievement(fd, left_status); - _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(left_status).c_str(), status_description(status).c_str()); - return(fd->selection_array[index - 1]); - } - } - if(index + 1 < fd->selection_array.size()) - { - CardStatus* right_status(fd->selection_array[index + 1]); - if(right_status->has_skill(intercept) && right_status->m_index == status->m_index + 1 && right_status->m_player == status->m_player && skill_check(fd, right_status, status)) - { - count_achievement(fd, right_status); - _DEBUG_MSG(1, "%s intercepts for %s\n", status_description(right_status).c_str(), status_description(status).c_str()); - return(fd->selection_array[index + 1]); - } - } - return(fd->selection_array[index]); -} - template -bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_status, const SkillSpec& s, bool is_evadable, bool is_count_achievement) +bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_status, const SkillSpec& s, bool is_evadable) { if(skill_check(fd, src_status, dst_status)) { - if(is_evadable && -#if defined(TYRANT_UNLEASHED) - dst_status->m_evaded < dst_status->skill(evade) && -#else - (dst_status->has_skill(evade) || (fd->effect == Effect::quicksilver && dst_status->m_card->m_type == CardType::assault)) && fd->flip() && -#endif + if (is_evadable && + dst_status->m_evaded < dst_status->skill() && skill_check(fd, dst_status, src_status)) { ++ dst_status->m_evaded; - count_achievement(fd, dst_status); _DEBUG_MSG(1, "%s %s on %s but it evades\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst_status).c_str()); return(false); } - if(is_count_achievement) - { - count_achievement(fd, src_status); - } _DEBUG_MSG(1, "%s %s on %s\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst_status).c_str()); - perform_skill(fd, dst_status, s); + perform_skill(fd, src_status, dst_status, s); if (s.c > 0) { src_status->m_skill_cd[skill_id] = s.c; @@ -2441,357 +1201,71 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ return(false); } -bool check_and_perform_blitz(Field* fd, CardStatus* src_status) -{ - if(skill_check(fd, src_status, nullptr)) - { - count_achievement(fd, src_status); - _DEBUG_MSG(1, "%s activates Blitz opposing %s\n", status_description(src_status).c_str(), status_description(&fd->tip->assaults[src_status->m_index]).c_str()); - src_status->m_blitzing = true; - return(true); - } - return(false); -} - -bool check_and_perform_recharge(Field* fd, CardStatus* src_status) -{ - if(fd->flip() && skill_check(fd, src_status, nullptr)) - { - count_achievement(fd, src_status); - _DEBUG_MSG(1, "%s activates Recharge\n", status_description(src_status).c_str()); - fd->tap->deck->place_at_bottom(src_status->m_card); - return(true); - } - return(false); -} - -bool check_and_perform_refresh(Field* fd, CardStatus* src_status) -{ - if(skill_check(fd, src_status, nullptr)) - { - count_achievement(fd, src_status); - _DEBUG_MSG(1, "%s refreshes, hp -> %u\n", status_description(src_status).c_str(), src_status->m_card->m_health); - add_hp(fd, src_status, src_status->m_card->m_health); - return(true); - } - return(false); -} - template -void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +size_t select_targets(Field* fd, CardStatus* src_status, const SkillSpec& s) { std::vector& cards(skill_targets(fd, src_status)); - if(select_fast(fd, src_status, cards, s, false) == 0) + size_t n_candidates = select_fast(fd, src_status, cards, s); + if (n_candidates == 0) { - return; + return n_candidates; } _DEBUG_SELECTION("%s", skill_names[skill_id].c_str()); - unsigned index_start, index_end; - if(s.all) // target all + unsigned n_targets = s.n > 0 ? s.n : 1; + if (s.all || n_targets >= n_candidates) // target all { - index_start = 0; - index_end = fd->selection_array.size() - 1; + return n_candidates; } - else + for (unsigned i = 0; i < n_targets; ++i) { - index_start = index_end = fd->rand(0, fd->selection_array.size() - 1); + std::swap(fd->selection_array[i], fd->selection_array[fd->rand(i, n_candidates - 1)]); } - bool is_count_achievement(true); - for(unsigned s_index(index_start); s_index <= index_end; ++s_index) + fd->selection_array.resize(n_targets); + if (n_targets > 1) { - if(!skill_roll(fd)) - { - _DEBUG_MSG(2, "%s misses the 50%% chance to activate %s on %s\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(fd->selection_array[s_index]).c_str()); - continue; - } - CardStatus* c(s.all ? fd->selection_array[s_index] : select_interceptable(fd, src_status, s_index)); - if(check_and_perform_skill(fd, src_status, c, s, true, is_count_achievement)) - { - // Count at most once even targeting "All" - is_count_achievement = false; - // Payback - if(c->has_skill(payback) && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status) && skill_check(fd, src_status, c)) - { - count_achievement(fd, c); - _DEBUG_MSG(1, "%s paybacks (%s) on %s\n", status_description(c).c_str(), skill_short_description(s).c_str(), status_description(src_status).c_str()); - perform_skill(fd, src_status, s); - } - } + std::sort(fd->selection_array.begin(), fd->selection_array.end(), [](const CardStatus * a, const CardStatus * b) { return a->m_index < b->m_index; }); } - maybeTriggerRegen::T>(fd); - prepend_on_death(fd); + return n_targets; } template -inline void check_and_perform_emulate(Field* fd, CardStatus* src_status, CardStatus* opposite_status, const SkillSpec& s) +void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { - Hand* hand = fd->players[opponent(opposite_status->m_player)]; - if(hand->assaults.size() > opposite_status->m_index) + select_targets(fd, src_status, s); + for (CardStatus * c: fd->selection_array) { - CardStatus& emulator = hand->assaults[opposite_status->m_index]; - if(emulator.has_skill(emulate) && skill_predicate(fd, src_status, &emulator, s) && skill_check(fd, &emulator, nullptr)) - { - count_achievement(fd, &emulator); - _DEBUG_MSG(1, "Emulate (%s) on %s\n", skill_short_description(s).c_str(), status_description(&emulator).c_str()); - perform_skill(fd, &emulator, s); - } + check_and_perform_skill(fd, src_status, c, s, ! src_status->m_overloaded); } } template void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { - std::vector& cards(skill_targets(fd, src_status)); - if(select_fast(fd, src_status, cards, s, true) == 0) - { - return; - } - _DEBUG_SELECTION("%s", skill_names[skill_id].c_str()); - unsigned index_start, index_end; - if(s.all || skill_id == supply) // target all or supply - { - index_start = 0; - index_end = fd->selection_array.size() - 1; - } - else - { - index_start = index_end = fd->rand(0, fd->selection_array.size() - 1); - } - bool is_count_achievement(true); - for(unsigned s_index(index_start); s_index <= index_end; ++s_index) + select_targets(fd, src_status, s); + for (CardStatus * dst: fd->selection_array) { - CardStatus* c(fd->selection_array[s_index]); - // So far no friendly activation skill needs to roll 50% but check it for completeness. - if(!skill_roll(fd)) - { - _DEBUG_MSG(2, "%s misses the 50%% chance to activate %s on %s\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(c).c_str()); - continue; - } -#if defined(TYRANT_UNLEASHED) - if(c->m_inhibited > 0) + if(dst->m_inhibited > 0 && ! src_status->m_overloaded) { - _DEBUG_MSG(1, "%s %s on %s but it is inhibited\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(c).c_str()); - -- c->m_inhibited; + _DEBUG_MSG(1, "%s %s on %s but it is inhibited\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst).c_str()); + -- dst->m_inhibited; continue; } -#endif - if(check_and_perform_skill(fd, src_status, c, s, false, is_count_achievement)) - { - // Count at most once even targeting "All" - is_count_achievement = false; - // Tribute - if(c->has_skill(tribute) && skill_predicate(fd, src_status, src_status, s) && fd->flip() && skill_check(fd, c, src_status)) - { - count_achievement(fd, c); - _DEBUG_MSG(1, "Tribute (%s) on %s\n", skill_short_description(s).c_str(), status_description(src_status).c_str()); - perform_skill(fd, src_status, s); - check_and_perform_emulate(fd, src_status, src_status, s); - } - check_and_perform_emulate(fd, src_status, c, s); - } - } -} - -void perform_backfire(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - check_and_perform_skill(fd, src_status, &fd->players[src_status->m_player]->commander, s, false, true); -} - -void perform_infuse(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - const auto &cards = boost::join(fd->tap->assaults.m_indirect, fd->tip->assaults.m_indirect); - if(fd->make_selection_array(cards.begin(), cards.end(), [fd, s](CardStatus* c){return(skill_predicate(fd, &fd->tap->commander, c, s));}) > 0) - { - _DEBUG_SELECTION("%s", skill_names[infuse].c_str()); - CardStatus* c(select_interceptable(fd, src_status, fd->rand(0, fd->selection_array.size() - 1))); - check_and_perform_skill(fd, src_status, c, s, true, true); - } -} - -inline void perform_recharge(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - check_and_perform_recharge(fd, src_status); -} - -// a summoned card's on play skills seem to be evaluated before any other skills on the skill queue. -inline void prepend_skills(Field* fd, CardStatus* status) -{ - for(auto& skill: boost::adaptors::reverse(status->m_card->m_skills[SkillMod::on_play])) - { - fd->skill_queue.emplace_front(status, skill); - } -} - -template -void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s); - -void perform_split(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - perform_summon(fd, src_status, SkillSpec{summon, src_status->m_card->m_id, s.y, s.c, s.s, s.all, s.mod}); -} - -template -void perform_summon(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - unsigned player = src_status->m_player; - const auto& mod = s.mod; - // Split and Summon on Play are not counted towards the Summon Limit. - if(skill_id == summon && mod != SkillMod::on_play) - { - if(fd->players[player]->available_summons == 0) - { - return; - } - -- fd->players[player]->available_summons; - if(fd->players[player]->available_summons == 0) - { - _DEBUG_MSG(1, "** Reaching summon limit, this is the last summon.\n"); - } - } - unsigned summoned_id = s.x; - const Card* summoned = 0; - if(summoned_id != 0) - { - summoned = fd->cards.by_id(summoned_id); - } - else - { - Faction summond_faction = s.y; - do { - summoned = fd->random_in_vector(fd->cards.player_assaults); - } while(summond_faction != allfactions && summond_faction != summoned->m_faction); - } - assert(summoned->m_type == CardType::assault || summoned->m_type == CardType::structure); - Hand* hand{fd->players[player]}; - count_achievement(fd, src_status); - Storage* storage{summoned->m_type == CardType::assault ? &hand->assaults : &hand->structures}; - CardStatus& card_status(storage->add_back()); - card_status.set(summoned); - card_status.m_index = storage->size() - 1; - card_status.m_player = player; - if(summoned->m_type == CardType::assault && fd->effect == Effect::harsh_conditions) - { - ++card_status.m_delay; - } - card_status.m_is_summoned = true; - _DEBUG_MSG(1, "%s %s %s %u [%s]\n", status_description(src_status).c_str(), skill_names[skill_id].c_str(), cardtype_names[summoned->m_type].c_str(), card_status.m_index, card_description(fd->cards, summoned).c_str()); - prepend_skills(fd, &card_status); - // Summon X (Genesis effect) does not activate Blitz for X - if(s.x != 0 && card_status.has_skill(blitz)) - { - check_and_perform_blitz(fd, &card_status); - } - if(summoned->m_type == CardType::assault) - { - if(fd->effect == Effect::toxic) - { - card_status.m_poisoned = 1; - } - else if(fd->effect == Effect::decay) - { - card_status.m_poisoned = 1; - card_status.m_diseased = true; - } + check_and_perform_skill(fd, src_status, dst, s, false); } } -void perform_trigger_regen(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - check_regeneration(fd); -} - -void perform_shock(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - check_and_perform_skill(fd, src_status, &fd->tip->commander, s, false, true); -} - -// Special rules for mimic : -// cannot mimic mimic, -// structures cannot mimic supply, -// and is not affected by payback. -void perform_mimic(Field* fd, CardStatus* src_status, const SkillSpec& s) -{ - // mimic cannot be triggered by anything. So it should be the only skill in the unresolved skill table. - // so we can probably clear it safely. This is necessary, because mimic calls resolve_skill as well (infinite loop). - fd->skill_queue.clear(); - std::vector& cards(skill_targets(fd, src_status)); - if(select_fast(fd, src_status, cards, s, false) == 0) - { - return; - } - _DEBUG_SELECTION("%s", skill_names[mimic].c_str()); - CardStatus* c(select_interceptable(fd, src_status, fd->rand(0, fd->selection_array.size() - 1))); - // evade check for mimic - // individual skills are subject to evade checks too, - // but resolve_skill will handle those. - if( -#if defined(TYRANT_UNLEASHED) - c->m_evaded < c->skill(evade) && -#else - (c->has_skill(evade) || (fd->effect == Effect::quicksilver && c->m_card->m_type == CardType::assault)) && fd->flip() && -#endif - skill_check(fd, c, src_status)) - { - ++ c->m_evaded; - count_achievement(fd, c); - _DEBUG_MSG(1, "%s %s on %s but it evades\n", status_description(src_status).c_str(), skill_names[s.id].c_str(), status_description(c).c_str()); - return; - } - count_achievement(fd, src_status); - _DEBUG_MSG(1, "%s %s on %s\n", status_description(src_status).c_str(), skill_names[s.id].c_str(), status_description(c).c_str()); - auto mod = SkillMod::on_activate; - bool need_add_skill = may_change_skill(fd, c, mod); - for(auto& skill: c->m_card->m_skills[SkillMod::on_activate]) - { - if(src_status->m_card->m_type != CardType::action && src_status->m_hp == 0) - { break; } - if(skill.id == mimic || skill.id == split || skill_table[skill.id] == nullptr || - (skill.id == supply && src_status->m_card->m_type != CardType::assault)) - { continue; } - auto& battleground_s = need_add_skill ? apply_battleground_effect(fd, c, skill, mod, need_add_skill) : skill; - SkillSpec mimic_s{battleground_s.id, battleground_s.x, allfactions, battleground_s.c, battleground_s.s, battleground_s.all, mod}; - _DEBUG_MSG(2, "Evaluating %s mimiced skill %s\n", status_description(c).c_str(), skill_description(fd->cards, mimic_s).c_str()); - fd->skill_queue.emplace_back(src_status, mimic_s); - resolve_skill(fd); - if(__builtin_expect(fd->end, false)) { break; } - check_regeneration(fd); - } - if(need_add_skill) - { - auto battleground_s = apply_battleground_effect(fd, c, SkillSpec{no_skill, 0, allfactions, 0, no_skill, false, mod}, mod, need_add_skill); - assert(battleground_s.id != no_skill); - SkillSpec mimic_s{battleground_s.id, battleground_s.x, allfactions, battleground_s.c, battleground_s.s, battleground_s.all, mod}; - _DEBUG_MSG(2, "Evaluating %s mimiced skill %s\n", status_description(c).c_str(), skill_description(fd->cards, mimic_s).c_str()); - fd->skill_queue.emplace_back(src_status, mimic_s); - resolve_skill(fd); - } -} //------------------------------------------------------------------------------ void fill_skill_table() { memset(skill_table, 0, sizeof skill_table); - skill_table[augment] = perform_targetted_allied_fast; - skill_table[backfire] = perform_backfire; - skill_table[chaos] = perform_targetted_hostile_fast; - skill_table[cleanse] = perform_targetted_allied_fast; skill_table[enfeeble] = perform_targetted_hostile_fast; skill_table[enhance] = perform_targetted_allied_fast; - skill_table[freeze] = perform_targetted_hostile_fast; skill_table[heal] = perform_targetted_allied_fast; - skill_table[infuse] = perform_infuse; skill_table[jam] = perform_targetted_hostile_fast; - skill_table[mimic] = perform_mimic; + skill_table[overload] = perform_targetted_allied_fast; skill_table[protect] = perform_targetted_allied_fast; skill_table[rally] = perform_targetted_allied_fast; - skill_table[recharge] = perform_recharge; - skill_table[repair] = perform_targetted_allied_fast; - skill_table[rush] = perform_targetted_allied_fast; - skill_table[shock] = perform_shock; skill_table[siege] = perform_targetted_hostile_fast; - skill_table[supply] = perform_targetted_allied_fast; - skill_table[split] = perform_split; skill_table[strike] = perform_targetted_hostile_fast; - skill_table[summon] = perform_summon; - skill_table[trigger_regen] = perform_trigger_regen; skill_table[weaken] = perform_targetted_hostile_fast; } diff --git a/sim.h b/sim.h index 77db3c4a..2a84afe5 100644 --- a/sim.h +++ b/sim.h @@ -125,46 +125,35 @@ struct CardStatus const Card* m_card; unsigned m_index; unsigned m_player; - unsigned m_augmented; unsigned m_berserk; - bool m_blitzing; - bool m_chaosed; unsigned m_corroded_rate; unsigned m_corroded_weakened; unsigned m_delay; - bool m_diseased; unsigned m_evaded; unsigned m_enfeebled; Faction m_faction; - bool m_frozen; unsigned m_hp; - bool m_immobilized; - bool m_infused; unsigned m_inhibited; bool m_jammed; - bool m_phased; + bool m_overloaded; unsigned m_poisoned; unsigned m_protected; unsigned m_rallied; - unsigned m_stunned; - bool m_sundered; unsigned m_weakened; - bool m_temporary_split; - bool m_is_summoned; // is this card summoned (or split)? CardStep m_step; -// begin for TYRANT_UNLEASHED unsigned m_enhanced_value[num_skills]; unsigned m_skill_cd[num_skills]; -// end CardStatus() {} void set(const Card* card); void set(const Card& card); std::string description(); - bool has_skill(Skill skill, SkillMod::SkillMod mod=SkillMod::on_activate) const; - unsigned skill(Skill skill, SkillMod::SkillMod mod=SkillMod::on_activate) const; + bool has_skill(Skill skill_id) const; + template bool has_skill() const; + template unsigned skill() const; unsigned enhanced(Skill skill) const; + unsigned protected_value() const; }; //------------------------------------------------------------------------------ // Represents a particular draw from a deck. @@ -210,7 +199,6 @@ class Field const Effect effect; Skill bg_enhanced_skill; unsigned bg_enhanced_value; - const Achievement& achievement; // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque> skill_queue; @@ -219,7 +207,6 @@ class Field enum phase { playcard_phase, - legion_phase, commander_phase, structures_phase, assaults_phase, @@ -231,14 +218,10 @@ class Field // Meaningless in playcard_phase, // otherwise is the index of the current card in players->structures or players->assaults unsigned current_ci; - unsigned last_decision_turn; // unsigned points_since_last_decision; - unsigned fusion_count; - std::vector achievement_counter; - Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, OptimizationMode optimization_mode_, - Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_, const Achievement& achievement_) : + Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_) : end{false}, re(re_), cards(cards_), @@ -248,8 +231,7 @@ class Field optimization_mode(optimization_mode_), effect(effect_), bg_enhanced_skill(bg_enhanced_skill_), - bg_enhanced_value(bg_enhanced_value_), - achievement(achievement_) + bg_enhanced_value(bg_enhanced_value_) { } @@ -273,48 +255,6 @@ class Field template inline unsigned make_selection_array(CardsIter first, CardsIter last, Functor f); inline void print_selection_array(); - - template - inline void set_counter(T& container, unsigned key, unsigned value) - { - auto x = container.find(key); - if(x != container.end()) - { - achievement_counter[x->second] = value; - } - } - - template - inline void inc_counter(T& container, unsigned key, unsigned value = 1) - { - auto x = container.find(key); - if(x != container.end()) - { - achievement_counter[x->second] += value; -#if 0 - if(achievement.req_counter[x->second].predict_monoinc(achievement_counter[x->second]) < 0) - { - end = true; - } -#endif - } - } - - template - inline void update_max_counter(T& container, unsigned key, unsigned value) - { - auto x = container.find(key); - if(x != container.end() && achievement_counter[x->second] < value) - { - achievement_counter[x->second] = value; -#if 0 - if(achievement.req_counter[x->second].predict_monoinc(achievement_counter[x->second]) < 0) - { - end = true; - } -#endif - } - } }; #endif diff --git a/tyrant.cpp b/tyrant.cpp index 6e3f0ead..c5896a8e 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -7,90 +7,42 @@ const std::string faction_names[Faction::num_factions] = std::string skill_names[Skill::num_skills] = { - // Placeholder for new gained skill from battleground effect: + // Placeholder for no-skill: "", // Attack: "0", - // Activation (Including Destroyed): - "Augment", "Backfire", "Chaos", "Cleanse", "Enfeeble", - "Freeze", "Heal", "Infuse", "Jam", - "Mimic", "Protect", "Rally", "Recharge", "Repair", "Rush", "Shock", - "Siege", "Split", "Strike", "Summon", "Supply", - "trigger_regen", - "Weaken", + // Activation: + "", + "Enfeeble", "Jam", "Siege", "Strike", "Weaken", + "", + "", + "Enhance", "Heal", "Overload", "Protect", "Rally", + "", + // Defensive: + "", + "Armored", "Corrosive", "Counter", "Evade", "Wall", + "", // Combat-Modifier: - "AntiAir", "Burst", "Fear", "Flurry", "Pierce", "Swipe", "Valor", + "Flurry", "Pierce", // Damage-Dependant: - "Berserk", "Crush", "Disease", "Immobilize", "Inhibit", "Leech", "Phase", "Poison", "Siphon", "Sunder", - // Defensive: - "Armored", "Corrosive", "Counter", "Emulate", "Evade", "Flying", "Intercept", "Payback", "Refresh", "Regenerate", "Stun", "Tribute", "Wall", - // Triggered: - "Blitz", "Legion", - // Tyrant Unleashed: - "Enhance", - // Static (Ignored): - "Fusion", - /* "Blizzard", "Mist", */ + "Berserk", "Inhibit", "Leech", "Poison", }; -std::unordered_set> helpful_skills{ - augment, cleanse, enhance, heal, protect, rally, repair, rush, supply, -}; - -std::unordered_set> defensive_skills{ - armored, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, tribute, wall, -}; - -std::string skill_activation_modifier_names[SkillMod::num_skill_activation_modifiers] = {"", " on Play", " on Attacked", " on Kill", " on Death", }; +std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Structure", }; -std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Structure", "Action", }; - -#if defined(TYRANT_UNLEASHED) std::string rarity_names[6]{"", "common", "rare", "epic", "legendary", "vindicator", }; -#else -std::string rarity_names[5]{"", "common", "uncommon", "rare", "legendary", }; -#endif -// begin for TYRANT_UNLEASHED unsigned upgrade_cost[]{0, 5, 15, 30, 75, 150}; unsigned salvaging_income[][7]{{}, {0, 1, 2, 5}, {0, 5, 10, 15, 20}, {0, 20, 25, 30, 40, 50, 65}, {0, 40, 45, 60, 75, 100, 125}, {0, 80, 85, 100, 125, 175, 250}}; -// end std::string decktype_names[DeckType::num_decktypes]{"Deck", "Mission", "Raid", "Quest", "Custom Deck", }; std::string effect_names[Effect::num_effects] = { "None", - "Time Surge", - "Copycat", - "Quicksilver", - "Decay", - "High Skies", - "Impenetrable", - "Invigorate", - "Clone Project", - "Friendly Fire", - "Genesis", - "Artillery Strike", - "Photon Shield", - "Decrepit", - "Forcefield", - "Chilling Touch", - "Clone Experiment", - "Toxic", - "Haunt", - "United Front", - "Harsh Conditions", -}; - -std::string achievement_misc_req_names[AchievementMiscReq::num_achievement_misc_reqs] = { - "Kill units with skill: flying", - "Skill activated: (any)", - "Turns", - "Damage", - "Total damage to the enemy Commander" + "Progenitors", }; -unsigned debug_print(0); +signed debug_print(0); unsigned debug_cached(0); bool debug_line(false); std::string debug_str(""); diff --git a/tyrant.h b/tyrant.h index a7797863..550210c3 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "1.3.0" +#define TYRANT_OPTIMIZER_VERSION "2.0.0" #include #include @@ -23,53 +23,34 @@ extern const std::string faction_names[num_factions]; enum Skill { + // Placeholder for no-skill: no_skill, // Attack: attack, - // Activation (including Destroyed): - augment, backfire, chaos, cleanse, enfeeble, freeze, heal, infuse, jam, - mimic, protect, rally, recharge, repair, rush, shock, siege, split, strike, summon, supply, - trigger_regen, // not actually a skill; handles regeneration after strike/siege - weaken, + // Activation: + BEGIN_ACTIVATION_HARMFUL, // TODO skill traits + enfeeble, jam, siege, strike, weaken, + END_ACTIVATION_HARMFUL, + BEGIN_ACTIVATION_HELPFUL, + enhance, heal, overload, protect, rally, + END_ACTIVATION_HELPFUL, + // Defensive: + BEGIN_DEFENSIVE, + armored, corrosive, counter, evade, wall, + END_DEFENSIVE, // Combat-Modifier: - antiair, burst, fear, flurry, pierce, swipe, valor, + flurry, pierce, // Damage-Dependant: - berserk, crush, disease, immobilize, inhibit, leech, phase, poison, siphon, sunder, - // Defensive: - armored, corrosive, counter, emulate, evade, flying, intercept, payback, refresh, regenerate, stun, tribute, wall, - // Triggered: - blitz, legion, - // Tyrant Unleashed: - enhance, - // Static, ignored: - fusion, - /* blizzard, mist, */ - // Placeholder for new gained skill from battleground effect: + berserk, inhibit, leech, poison, num_skills }; extern std::string skill_names[num_skills]; -extern std::unordered_set> helpful_skills; -extern std::unordered_set> defensive_skills; - -namespace SkillMod { -enum SkillMod -{ - on_activate, - on_play, - on_attacked, - on_kill, - on_death, - num_skill_activation_modifiers -}; -} -extern std::string skill_activation_modifier_names[SkillMod::num_skill_activation_modifiers]; namespace CardType { enum CardType { commander, assault, structure, - action, num_cardtypes }; } @@ -78,10 +59,8 @@ extern std::string cardtype_names[CardType::num_cardtypes]; extern std::string rarity_names[]; -// begin for TYRANT_UNLEASHED extern unsigned upgrade_cost[]; extern unsigned salvaging_income[][7]; -// end namespace DeckType { enum DeckType { @@ -98,54 +77,21 @@ extern std::string decktype_names[DeckType::num_decktypes]; enum Effect { none, - time_surge, - copycat, - quicksilver, - decay, - high_skies, - impenetrable, - invigorate, - clone_project, - friendly_fire, - genesis, - artillery_strike, - photon_shield, - decrepit, - forcefield, - chilling_touch, - clone_experiment, - toxic, - haunt, - united_front, - harsh_conditions, + progenitors, num_effects }; extern std::string effect_names[Effect::num_effects]; -enum AchievementMiscReq -{ - unit_with_flying_killed, // 104 Sky Control - skill_activated, // 105 Brute Strength - turns, // all "Speedy" and "Slow" - damage, // 168 SMASH!; 183 Rally Free Zone - com_total, // 169 Overkill; 170 EXTREME Overkill!!! - num_achievement_misc_reqs -}; - -extern std::string achievement_misc_req_names[num_achievement_misc_reqs]; - enum gamemode_t { fight, surge, - tournament }; enum class OptimizationMode { winrate, - achievement, raid, defense }; @@ -177,10 +123,10 @@ struct SkillSpec Skill id; unsigned x; Faction y; + unsigned n; unsigned c; Skill s; bool all; - SkillMod::SkillMod mod; }; // -------------------------------------------------------------------------------- @@ -194,7 +140,7 @@ std::string to_string(const T val) } //---------------------- Debugging stuff --------------------------------------- -extern unsigned debug_print; +extern signed debug_print; extern unsigned debug_cached; extern bool debug_line; extern std::string debug_str; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index d1c1152e..21641f24 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -30,10 +30,10 @@ #include #include #include +#include #include "card.h" #include "cards.h" #include "deck.h" -#include "achievement.h" #include "read.h" #include "sim.h" #include "tyrant.h" @@ -44,8 +44,7 @@ namespace { gamemode_t gamemode{fight}; OptimizationMode optimization_mode{OptimizationMode::winrate}; std::map owned_cards; - std::map buyable_cards; - bool use_owned_cards{false}; + bool use_owned_cards{true}; unsigned min_deck_len{1}; unsigned max_deck_len{10}; unsigned fund{0}; @@ -146,17 +145,11 @@ unsigned get_deck_cost(const Deck * deck) for(auto it: num_in_deck) { unsigned card_id = it.first->m_id; - unsigned num_to_buy = safe_minus(it.second, owned_cards[card_id]); -// std::cout << "BUY " << it.first->m_name << " +" << num_to_buy << " =" << it.second << ".\n"; // XXX - if(num_to_buy > 0) + if (it.second > owned_cards[card_id]) { - auto buyable_iter = buyable_cards.find(card_id); - if(buyable_iter == buyable_cards.end()) - { return UINT_MAX; } - deck_cost += num_to_buy * buyable_iter->second; + return UINT_MAX; } } -// std::cout << "\n"; // XXX return deck_cost; } @@ -258,66 +251,25 @@ bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, cons return true; } -// @claim_all: true = claim all cards; false = claim only non-buyable cards. -// @is_reward: not claim a card if there is upgraded version (assuming the reward card has been upgraded). -void claim_cards(const std::vector & card_list, bool claim_all, bool is_reward=false) +void claim_cards(const std::vector & card_list) { std::map num_cards; get_required_cards_before_upgrade(card_list, num_cards); - for(auto it: num_cards) + for(const auto & it: num_cards) { const Card * card = it.first; - if(claim_all || buyable_cards.find(card->m_id) == buyable_cards.end()) + unsigned num_to_claim = safe_minus(it.second, owned_cards[card->m_id]); + if(num_to_claim > 0) { - unsigned num_to_claim = safe_minus(it.second, owned_cards[card->m_id]); - if (is_reward) + owned_cards[card->m_id] += num_to_claim; + if(debug_print) { - for (const auto & it : card->m_used_for_cards) // low-priority FIXME: upgraded several times - { - num_to_claim = safe_minus(num_to_claim, owned_cards[it.first->m_id] * it.second); - } - } - if(num_to_claim > 0) - { - owned_cards[card->m_id] += num_to_claim; - if(debug_print) - { - std::cout << "Claim " << card->m_name << " (" << num_to_claim << ")" << std::endl; - } + std::cout << "Claim " << card->m_name << " (" << num_to_claim << ")" << std::endl; } } } } -//------------------------------------------------------------------------------ -inline bool suitable_non_commander(const Deck& deck, unsigned slot, const Card* card) -{ -#if not defined(TYRANT_UNLEASHED) - assert(card->m_type != CardType::commander); - if(card->m_rarity == 4) // legendary - 1 per deck - { - for(unsigned i(0); i < deck.cards.size(); ++i) - { - if(i != slot && deck.cards[i]->m_rarity == 4) - { - return false; - } - } - } - if(card->m_unique) // unique - 1 card with same id per deck - { - for(unsigned i(0); i < deck.cards.size(); ++i) - { - if(i != slot && deck.cards[i]->m_base_id == card->m_base_id) - { - return false; - } - } - } -#endif - return true; -} - //------------------------------------------------------------------------------ Results compute_score(const std::pair> , unsigned>& results, std::vector& factors) { @@ -361,62 +313,60 @@ struct SimulationData std::mt19937 re; const Cards& cards; const Decks& decks; - std::shared_ptr att_deck; - Hand att_hand; - std::vector> def_decks; - std::vector def_hands; + std::shared_ptr your_deck; + Hand your_hand; + std::vector> enemy_decks; + std::vector enemy_hands; std::vector factors; gamemode_t gamemode; enum Effect effect; Skill bg_enhanced_skill; unsigned bg_enhanced_value; - const Achievement& achievement; - SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_def_decks_, std::vector factors_, gamemode_t gamemode_, - enum Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_, const Achievement& achievement_) : + SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_enemy_decks_, std::vector factors_, gamemode_t gamemode_, + enum Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_) : re(seed), cards(cards_), decks(decks_), - att_deck(), - att_hand(nullptr), - def_decks(num_def_decks_), + your_deck(), + your_hand(nullptr), + enemy_decks(num_enemy_decks_), factors(factors_), gamemode(gamemode_), effect(effect_), bg_enhanced_skill(bg_enhanced_skill_), - bg_enhanced_value(bg_enhanced_value_), - achievement(achievement_) + bg_enhanced_value(bg_enhanced_value_) { - for(auto def_deck: def_decks) + for (size_t i = 0; i < num_enemy_decks_; ++i) { - def_hands.emplace_back(new Hand(nullptr)); + enemy_hands.emplace_back(new Hand(nullptr)); } } ~SimulationData() { - for(auto hand: def_hands) { delete(hand); } + for(auto hand: enemy_hands) { delete(hand); } } - void set_decks(const Deck* const att_deck_, std::vector const & def_decks_) + void set_decks(const Deck* const your_deck_, std::vector const & enemy_decks_) { - att_deck.reset(att_deck_->clone()); - att_hand.deck = att_deck.get(); - for(unsigned i(0); i < def_decks_.size(); ++i) + your_deck.reset(your_deck_->clone()); + your_hand.deck = your_deck.get(); + for(unsigned i(0); i < enemy_decks_.size(); ++i) { - def_decks[i].reset(def_decks_[i]->clone()); - def_hands[i]->deck = def_decks[i].get(); + enemy_decks[i].reset(enemy_decks_[i]->clone()); + enemy_hands[i]->deck = enemy_decks[i].get(); } } inline std::vector> evaluate() { std::vector> res; - for(Hand* def_hand: def_hands) + for(Hand* enemy_hand: enemy_hands) { - att_hand.reset(re); - def_hand->reset(re); - Field fd(re, cards, att_hand, *def_hand, gamemode, optimization_mode, effect != Effect::none ? effect : def_hand->deck->effect, bg_enhanced_skill, bg_enhanced_value, achievement); + your_hand.reset(re); + enemy_hand->reset(re); + Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, effect != Effect::none ? effect : enemy_hand->deck->effect, bg_enhanced_skill, bg_enhanced_value); Results result(play(&fd)); res.emplace_back(result); } @@ -441,35 +391,33 @@ class Process boost::mutex shared_mutex; const Cards& cards; const Decks& decks; - Deck* att_deck; - const std::vector def_decks; + Deck* your_deck; + const std::vector enemy_decks; std::vector factors; gamemode_t gamemode; enum Effect effect; Skill bg_enhanced_skill; unsigned bg_enhanced_value; - Achievement achievement; - Process(unsigned num_threads_, const Cards& cards_, const Decks& decks_, Deck* att_deck_, std::vector def_decks_, std::vector factors_, gamemode_t gamemode_, - enum Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_, const Achievement& achievement_) : + Process(unsigned num_threads_, const Cards& cards_, const Decks& decks_, Deck* your_deck_, std::vector enemy_decks_, std::vector factors_, gamemode_t gamemode_, + enum Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_) : num_threads(num_threads_), main_barrier(num_threads+1), cards(cards_), decks(decks_), - att_deck(att_deck_), - def_decks(def_decks_), + your_deck(your_deck_), + enemy_decks(enemy_decks_), factors(factors_), gamemode(gamemode_), effect(effect_), bg_enhanced_skill(bg_enhanced_skill_), - bg_enhanced_value(bg_enhanced_value_), - achievement(achievement_) + bg_enhanced_value(bg_enhanced_value_) { destroy_threads = false; unsigned seed(time(0)); for(unsigned i(0); i < num_threads; ++i) { - threads_data.push_back(new SimulationData(seed + i, cards, decks, def_decks.size(), factors, gamemode, effect, bg_enhanced_skill, bg_enhanced_value, achievement)); + threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, effect, bg_enhanced_skill, bg_enhanced_value)); threads.push_back(new boost::thread(thread_evaluate, std::ref(main_barrier), std::ref(shared_mutex), std::ref(*threads_data.back()), std::ref(*this), i)); } } @@ -485,7 +433,7 @@ class Process std::pair> , unsigned> evaluate(unsigned num_iterations) { thread_num_iterations = num_iterations; - thread_results = std::vector>(def_decks.size()); + thread_results = std::vector>(enemy_decks.size()); thread_total = 0; thread_compare = false; // unlock all the threads @@ -498,7 +446,7 @@ class Process std::pair> , unsigned> compare(unsigned num_iterations, long double prev_score) { thread_num_iterations = num_iterations; - thread_results = std::vector>(def_decks.size()); + thread_results = std::vector>(enemy_decks.size()); thread_total = 0; thread_prev_score = prev_score; thread_compare = true; @@ -520,7 +468,7 @@ void thread_evaluate(boost::barrier& main_barrier, while(true) { main_barrier.wait(); - sim.set_decks(p.att_deck, p.def_decks); + sim.set_decks(p.your_deck, p.enemy_decks); if(destroy_threads) { return; } while(true) @@ -586,7 +534,7 @@ void print_score_info(const std::pair> , unsigned> { auto final = compute_score(results, factors); std::cout << final.points << " ("; - for(auto val: results.first) + for(const auto & val: results.first) { switch(optimization_mode) { @@ -608,7 +556,7 @@ void print_results(const std::pair> , unsigned>& r if(optimization_mode == OptimizationMode::raid) { std::cout << "win%: " << (final.wins + final.draws) * 100.0 << " ("; - for(auto val: results.first) + for(const auto & val: results.first) { std::cout << val.wins + val.draws << " "; } @@ -616,7 +564,7 @@ void print_results(const std::pair> , unsigned>& r } std::cout << (optimization_mode == OptimizationMode::raid ? "slay%: " : "win%: ") << final.wins * 100.0 << " ("; - for(auto val: results.first) + for(const auto & val: results.first) { std::cout << val.wins << " "; } @@ -625,7 +573,7 @@ void print_results(const std::pair> , unsigned>& r if(optimization_mode != OptimizationMode::raid) { std::cout << "stall%: " << final.draws * 100.0 << " ("; - for(auto val: results.first) + for(const auto & val: results.first) { std::cout << val.draws << " "; } @@ -633,7 +581,7 @@ void print_results(const std::pair> , unsigned>& r } std::cout << "loss%: " << final.losses * 100.0 << " ("; - for(auto val: results.first) + for(const auto & val: results.first) { std::cout << val.losses << " "; } @@ -643,7 +591,7 @@ void print_results(const std::pair> , unsigned>& r { case OptimizationMode::raid: std::cout << "ard: " << final.points << " ("; - for(auto val: results.first) + for(const auto & val: results.first) { std::cout << val.points << " "; } @@ -653,14 +601,6 @@ void print_results(const std::pair> , unsigned>& r std::cout << "stdev: " << sqrt(final.sq_points - final.points * final.points) << std::endl; } break; - case OptimizationMode::achievement: - std::cout << "achievement%: " << final.points << " ("; - for(auto val: results.first) - { - std::cout << val.points / 100 << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; - break; default: break; } @@ -682,9 +622,6 @@ void print_deck_inline(const unsigned deck_cost, const Results scor } std::cout << ") "; break; - case OptimizationMode::achievement: - std::cout << "(" << score.wins * 100.0 << "% win) "; - break; case OptimizationMode::defense: std::cout << "(" << score.draws * 100.0 << "% stall) "; break; @@ -799,8 +736,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcommander = best_commander; d1->cards = best_cards; if (card_candidate ? - ((slot_i < best_cards.size() && card_candidate->m_name == best_cards[slot_i]->m_name) || // Omega -> Omega - !suitable_non_commander(*d1, slot_i, card_candidate)) + (slot_i < best_cards.size() && card_candidate->m_name == best_cards[slot_i]->m_name) // Omega -> Omega : (slot_i == best_cards.size())) // void -> void { continue; } @@ -931,8 +867,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std d1->commander = best_commander; d1->cards = best_cards; if (card_candidate ? - ((from_slot < best_cards.size() && (from_slot == to_slot && card_candidate->m_name == best_cards[to_slot]->m_name)) || // 2 Omega -> 2 Omega - !suitable_non_commander(*d1, from_slot, card_candidate)) + (from_slot < best_cards.size() && (from_slot == to_slot && card_candidate->m_name == best_cards[to_slot]->m_name)) // 2 Omega -> 2 Omega : (from_slot == best_cards.size())) // void -> void { continue; } @@ -1052,19 +987,6 @@ enum Operation { debuguntil }; //------------------------------------------------------------------------------ -void print_available_decks(Decks & decks, bool allow_card_pool) -{ - std::cout << "Available decks: (use double-quoted name)" << std::endl; - std::cout << "(All missions, omitted because the list is too long.)" << std::endl; - for(auto& deck: decks.decks) - { - if(deck.decktype != DeckType::mission && (allow_card_pool || deck.raid_cards.empty())) - { - std::cout << deck.short_description() << "\n"; - } - } -} -//------------------------------------------------------------------------------ void print_available_effects() { std::cout << "Available effects:" << std::endl; @@ -1088,23 +1010,21 @@ void usage(int argc, char** argv) " example: \'fear:0.2;slowroll:0.8\' means fear is the defense deck 20% of the time, while slowroll is the defense deck 80% of the time.\n" "\n" "Flags:\n" - " -A : optimize for the achievement specified by either id or name.\n" " -e : set the battleground effect. effect is automatically set when applicable.\n" " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" " -s: use surge (default is fight).\n" " -t : set the number of threads, default is 4.\n" - " -turnlimit : set the number of turns in a battle, default is 50 (can be used for speedy achievements).\n" " win: simulate/optimize for win rate. default for non-raids.\n" " defense: simulate/optimize for win rate + stall rate. can be used for defending deck or win rate oriented raid simulations.\n" - " raid: simulate/optimize for average raid damage (ARD). default for raids.\n" +// " raid: simulate/optimize for average raid damage (ARD). default for raids.\n" "Flags for climb:\n" " -c: don't try to optimize the commander.\n" " -L : restrict deck size between and .\n" - " -o: restrict to the owned cards listed in \"ownedcards.txt\".\n" + " -o: restrict to the owned cards listed in \"data/ownedcards.txt\".\n" " -o=: restrict to the owned cards listed in .\n" " fund : fund gold to buy/upgrade cards. prices are specified in ownedcards file.\n" " target : stop as soon as the score reaches .\n" - " -u: don't upgrade owned cards. (by default, upgrade owned cards when needed)\n" +// " -u: don't upgrade owned cards. (by default, upgrade owned cards when needed)\n" "\n" "Operations:\n" " sim : simulate battles to evaluate a deck.\n" @@ -1119,207 +1039,104 @@ void usage(int argc, char** argv) int main(int argc, char** argv) { - if(argc == 1) { usage(argc, argv); return(0); } - if(argc <= 2 && strcmp(argv[1], "-version") == 0) + if (argc == 2 && strcmp(argv[1], "-version") == 0) { -#if defined(TYRANT_UNLEASHED) std::cout << "Tyrant Unleashed Optimizer " << TYRANT_OPTIMIZER_VERSION << std::endl; -#else - std::cout << "Tyrant Optimizer " << TYRANT_OPTIMIZER_VERSION << std::endl; -#endif return(0); } - unsigned num_threads = 4; - DeckStrategy::DeckStrategy att_strategy(DeckStrategy::random); - DeckStrategy::DeckStrategy def_strategy(DeckStrategy::random); - Cards all_cards; - read_cards(all_cards); - read_card_abbrs(all_cards, "cardabbrs.txt"); - Decks decks; - Achievement achievement; - load_decks_xml(decks, all_cards); - load_decks(decks, all_cards); -#if defined(TYRANT_UNLEASHED) - load_recipes_xml(all_cards); -#endif - fill_skill_table(); - - if(argc <= 2) + if (argc <= 2) { - print_available_decks(decks, true); - return(4); - } + usage(argc, argv); + return(0); - int argIndex(1); - if (strcmp(argv[argIndex], "ext_b64") == 0) - { - hash_to_ids = hash_to_ids_ext_b64; - encode_deck = encode_deck_ext_b64; - argIndex += 1; - } - else if (strcmp(argv[argIndex], "wmt_b64") == 0) - { - hash_to_ids = hash_to_ids_wmt_b64; - encode_deck = encode_deck_wmt_b64; - argIndex += 1; - } - else if (strcmp(argv[argIndex], "ddd_b64") == 0) - { - hash_to_ids = hash_to_ids_ddd_b64; - encode_deck = encode_deck_ddd_b64; - argIndex += 1; } - std::string att_deck_name{argv[argIndex]}; - auto && deck_list_parsed = parse_deck_list(argv[argIndex + 1], decks); - argIndex += 2; - - Deck* att_deck{nullptr}; - std::vector def_decks; - std::vector def_decks_factors; - enum Effect effect(Effect::none); - Skill bg_enhanced_skill(no_skill); - unsigned bg_enhanced_value(0); - bool keep_commander{false}; - bool fixed_len{false}; - std::vector> todo; + unsigned opt_num_threads(4); + DeckStrategy::DeckStrategy opt_your_strategy(DeckStrategy::random); + DeckStrategy::DeckStrategy opt_enemy_strategy(DeckStrategy::random); + std::string opt_forts, opt_enemy_forts; + std::string opt_hand, opt_enemy_hand; + std::vector opt_owned_cards_str_list; + bool opt_do_optimization(false); + bool opt_keep_commander{false}; + std::vector> opt_todo; + std::string opt_effect; + enum Effect opt_effect_id(Effect::none); + Skill opt_bg_enhanced_skill(no_skill); + unsigned opt_bg_enhanced_value(0); - try - { - att_deck = find_deck(decks, all_cards, att_deck_name); - } - catch(const std::runtime_error& e) - { - std::cerr << "Error: Deck " << att_deck_name << ": " << e.what() << std::endl; - return(5); - } - if(att_deck == nullptr) - { - std::cerr << "Error: Invalid attack deck name/hash " << att_deck_name << ".\n"; - } - else if(!att_deck->raid_cards.empty()) - { - std::cerr << "Error: Invalid attack deck " << att_deck_name << ": has optional cards.\n"; - att_deck = nullptr; - } - if(att_deck == nullptr) + for(int argIndex = 3; argIndex < argc; ++argIndex) { - print_available_decks(decks, false); - return(5); - } - - for(auto deck_parsed: deck_list_parsed) - { - Deck* def_deck{nullptr}; - try + // Codec + if (strcmp(argv[argIndex], "ext_b64") == 0) { - def_deck = find_deck(decks, all_cards, deck_parsed.first); + hash_to_ids = hash_to_ids_ext_b64; + encode_deck = encode_deck_ext_b64; } - catch(const std::runtime_error& e) + else if (strcmp(argv[argIndex], "wmt_b64") == 0) { - std::cerr << "Error: Deck " << deck_parsed.first << ": " << e.what() << std::endl; - return(5); + hash_to_ids = hash_to_ids_wmt_b64; + encode_deck = encode_deck_wmt_b64; } - if(def_deck == nullptr) + else if (strcmp(argv[argIndex], "ddd_b64") == 0) { - std::cerr << "Error: Invalid defense deck name/hash " << deck_parsed.first << ".\n"; - print_available_decks(decks, true); - return(5); + hash_to_ids = hash_to_ids_ddd_b64; + encode_deck = encode_deck_ddd_b64; } - if(def_deck->decktype == DeckType::raid) + // Base Game Mode + else if (strcmp(argv[argIndex], "fight") == 0) { - optimization_mode = OptimizationMode::raid; - turn_limit = 30; - target_score = 250; + gamemode = fight; } - def_decks.push_back(def_deck); - def_decks_factors.push_back(deck_parsed.second); - } - - std::map effect_map; - for(unsigned i(0); i < Effect::num_effects; ++i) - { - effect_map[effect_names[i]] = static_cast(i); - std::stringstream ss; - ss << i; - effect_map[ss.str()] = static_cast(i); - } - - for(; argIndex < argc; ++argIndex) - { - if(strcmp(argv[argIndex], "win") == 0) + else if (strcmp(argv[argIndex], "-s") == 0 || strcmp(argv[argIndex], "surge") == 0) + { + gamemode = surge; + } + // Base Scoring Mode + else if (strcmp(argv[argIndex], "win") == 0) { optimization_mode = OptimizationMode::winrate; } - else if(strcmp(argv[argIndex], "raid") == 0) + else if (strcmp(argv[argIndex], "defense") == 0) + { + optimization_mode = OptimizationMode::defense; + } + else if (strcmp(argv[argIndex], "raid") == 0) { optimization_mode = OptimizationMode::raid; turn_limit = 30; target_score = 250; } - else if(strcmp(argv[argIndex], "defense") == 0) + // Mode Package + else if (strcmp(argv[argIndex], "pvp") == 0) + { + gamemode = fight; + optimization_mode = OptimizationMode::winrate; + } + else if (strcmp(argv[argIndex], "pvp-defense") == 0) { + gamemode = surge; optimization_mode = OptimizationMode::defense; } - else if(strcmp(argv[argIndex], "-A") == 0) + else if (strcmp(argv[argIndex], "gw") == 0) { - try - { - read_achievement(decks, all_cards, achievement, argv[argIndex + 1]); - optimization_mode = OptimizationMode::achievement; - } - catch(const std::runtime_error& e) - { - std::cerr << "Error: Achievement " << argv[argIndex + 1] << ": " << e.what() << std::endl; - return(1); - } - for(auto def_deck: def_decks) - { - if(def_deck->decktype != DeckType::mission) - { - std::cerr << "Error: Enemy's deck must be mission for achievement." << std::endl; - return(1); - } - if(!achievement.mission_condition.check(def_deck->id)) - { - std::cerr << "Error: Wrong mission [" << def_deck->name << "] for achievement." << std::endl; - return(1); - } - } - argIndex += 1; + gamemode = surge; + optimization_mode = OptimizationMode::winrate; } - else if(strcmp(argv[argIndex], "-c") == 0) + else if (strcmp(argv[argIndex], "gw-defense") == 0) { - keep_commander = true; + gamemode = fight; + optimization_mode = OptimizationMode::defense; } - else if(strcmp(argv[argIndex], "-e") == 0) + // Others + else if (strcmp(argv[argIndex], "-c") == 0) { -#if defined(TYRANT_UNLEASHED) - std::string arg_skill(argv[argIndex + 1]); - bg_enhanced_skill = skill_name_to_id(arg_skill.c_str()); - if(bg_enhanced_skill == no_skill) - { - std::cout << "The skill '" << arg_skill << "' was not found. "; - return(6); - } - bg_enhanced_value = atoi(argv[argIndex + 2]); - argIndex += 2; -#else - std::string arg_effect(argv[argIndex + 1]); - const auto & x = effect_map.find(arg_effect); - if(x == effect_map.end()) - { - std::cout << "The effect '" << arg_effect << "' was not found. "; - print_available_effects(); - return(6); - } - effect = static_cast(x->second); - argIndex += 1; -#endif + opt_keep_commander = true; } - else if(strcmp(argv[argIndex], "-fixedlen") == 0) + else if (strcmp(argv[argIndex], "-e") == 0) { - fixed_len = true; + opt_effect = argv[argIndex + 1]; + argIndex += 1; } else if(strcmp(argv[argIndex], "-L") == 0) { @@ -1327,63 +1144,48 @@ int main(int argc, char** argv) max_deck_len = atoi(argv[argIndex + 2]); argIndex += 2; } + else if(strcmp(argv[argIndex], "-o-") == 0) + { + use_owned_cards = false; + } else if(strcmp(argv[argIndex], "-o") == 0) { - read_owned_cards(all_cards, owned_cards, buyable_cards, "ownedcards.txt"); + opt_owned_cards_str_list.push_back("data/ownedcards.txt"); use_owned_cards = true; } else if(strncmp(argv[argIndex], "-o=", 3) == 0) { - read_owned_cards(all_cards, owned_cards, buyable_cards, argv[argIndex] + 3); + opt_owned_cards_str_list.push_back(argv[argIndex] + 3); use_owned_cards = true; } - else if(strncmp(argv[argIndex], "-o+", 3) == 0) - { - unsigned completed_mission_id = atoi(argv[argIndex] + 3); - // claim reward cards as owned; use filter file if you want to exclude - for(auto def_deck: def_decks) - { - unsigned prev_mission_id = def_deck->mission_req; - while(prev_mission_id > completed_mission_id) - { - auto prev_deck = decks.by_type_id[{DeckType::mission, prev_mission_id}]; - prev_mission_id = prev_deck->mission_req; - claim_cards(prev_deck->reward_cards, true, true); - } - } - } else if(strcmp(argv[argIndex], "fund") == 0) { fund = atoi(argv[argIndex+1]); argIndex += 1; } - else if(strcmp(argv[argIndex], "-r") == 0 || strcmp(argv[argIndex], "ordered") == 0) + else if (strcmp(argv[argIndex], "random") == 0) { - att_strategy = DeckStrategy::ordered; + opt_your_strategy = DeckStrategy::random; } - else if(strcmp(argv[argIndex], "exact-ordered") == 0) - { - att_strategy = DeckStrategy::exact_ordered; - } - else if(strcmp(argv[argIndex], "defender:ordered") == 0) + else if(strcmp(argv[argIndex], "-r") == 0 || strcmp(argv[argIndex], "ordered") == 0) { - def_strategy = DeckStrategy::ordered; + opt_your_strategy = DeckStrategy::ordered; } - else if(strcmp(argv[argIndex], "defender:exact-ordered") == 0) + else if(strcmp(argv[argIndex], "exact-ordered") == 0) { - def_strategy = DeckStrategy::exact_ordered; + opt_your_strategy = DeckStrategy::exact_ordered; } - else if(strcmp(argv[argIndex], "-s") == 0 || strcmp(argv[argIndex], "surge") == 0) + else if(strcmp(argv[argIndex], "enemy:ordered") == 0) { - gamemode = surge; + opt_enemy_strategy = DeckStrategy::ordered; } - else if(strcmp(argv[argIndex], "tournament") == 0) + else if(strcmp(argv[argIndex], "enemy:exact-ordered") == 0) { - gamemode = tournament; + opt_enemy_strategy = DeckStrategy::exact_ordered; } else if(strcmp(argv[argIndex], "-t") == 0) { - num_threads = atoi(argv[argIndex+1]); + opt_num_threads = atoi(argv[argIndex+1]); argIndex += 1; } else if(strcmp(argv[argIndex], "target") == 0) @@ -1391,7 +1193,7 @@ int main(int argc, char** argv) target_score = atof(argv[argIndex+1]); argIndex += 1; } - else if(strcmp(argv[argIndex], "-turnlimit") == 0) + else if(strcmp(argv[argIndex], "turnlimit") == 0) { turn_limit = atoi(argv[argIndex+1]); argIndex += 1; @@ -1408,61 +1210,59 @@ int main(int argc, char** argv) { use_harmonic_mean = true; } + else if(strcmp(argv[argIndex], "-v") == 0) + { + -- debug_print; + } else if(strcmp(argv[argIndex], "+v") == 0) { ++ debug_print; } else if(strcmp(argv[argIndex], "hand") == 0) // set initial hand for test { - att_deck->set_given_hand(argv[argIndex + 1]); + opt_hand = argv[argIndex + 1]; argIndex += 1; } - else if(strcmp(argv[argIndex], "defender:hand") == 0) // set enemies' initial hand for test + else if(strcmp(argv[argIndex], "enemy:hand") == 0) // set enemies' initial hand for test { - for(auto def_deck: def_decks) - { - def_deck->set_given_hand(argv[argIndex + 1]); - } + opt_enemy_hand = argv[argIndex + 1]; argIndex += 1; } - else if(strcmp(argv[argIndex], "forts") == 0) // set forts + else if (strcmp(argv[argIndex], "yf") == 0 || strcmp(argv[argIndex], "yfort") == 0) // set forts { - att_deck->set_forts(argv[argIndex + 1]); + opt_forts = std::string(argv[argIndex + 1]) + ","; argIndex += 1; } - else if(strcmp(argv[argIndex], "defender:forts") == 0) // set enemies' forts + else if (strcmp(argv[argIndex], "ef") == 0 || strcmp(argv[argIndex], "efort") == 0) // set enemies' forts { - for(auto def_deck: def_decks) - { - def_deck->set_forts(argv[argIndex + 1]); - } + opt_enemy_forts = std::string(argv[argIndex + 1]) + ","; argIndex += 1; } else if(strcmp(argv[argIndex], "sim") == 0) { - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); argIndex += 1; } else if(strcmp(argv[argIndex], "climb") == 0) { - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, climb)); + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, climb)); + opt_do_optimization = true; argIndex += 1; } else if(strcmp(argv[argIndex], "reorder") == 0) { - att_strategy = DeckStrategy::ordered; - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, reorder)); + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, reorder)); argIndex += 1; } else if(strcmp(argv[argIndex], "debug") == 0) { - todo.push_back(std::make_tuple(0u, 0u, debug)); + opt_todo.push_back(std::make_tuple(0u, 0u, debug)); } else if(strcmp(argv[argIndex], "debuguntil") == 0) { // output the debug info for the first battle that min_score <= score <= max_score. // E.g., 0 0: lose; 100 100: win (non-raid); 150 250: at least 150 damage (raid). - todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), debuguntil)); + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), debuguntil)); argIndex += 2; } else @@ -1472,43 +1272,154 @@ int main(int argc, char** argv) } } - // Force to claim non-buyable cards in your initial deck. - if(use_owned_cards) + Cards all_cards; + Decks decks; + load_cards_xml(all_cards, "data/cards.xml"); + read_card_abbrs(all_cards, "data/cardabbrs.txt"); + load_decks_xml(decks, all_cards, "data/missions.xml"); + load_custom_decks(decks, all_cards, "data/customdecks.txt"); + load_recipes_xml(all_cards, "data/fusion_recipes_cj2.xml"); + fill_skill_table(); + + if (opt_do_optimization and use_owned_cards) + { + if (opt_owned_cards_str_list.empty()) + { // load default file is specify no file + opt_owned_cards_str_list.push_back("data/ownedcards.txt"); + } + for (const auto & oc_str: opt_owned_cards_str_list) + { + read_owned_cards(all_cards, owned_cards, oc_str); + } + } + + if (! opt_effect.empty()) { - claim_cards({att_deck->commander}, fund == 0, false); - claim_cards(att_deck->cards, fund == 0, false); + std::map effect_map; + for(unsigned i(0); i < Effect::num_effects; ++i) + { + effect_map[boost::to_lower_copy(effect_names[i])] = static_cast(i); + std::stringstream ss; + ss << i; + effect_map[ss.str()] = static_cast(i); + } + std::vector tokens; + boost::split(tokens, opt_effect, boost::is_any_of(" -")); + opt_bg_enhanced_skill = skill_name_to_id(tokens[0]); + if (tokens.size() >= 2 && opt_bg_enhanced_skill != no_skill) + { + opt_bg_enhanced_value = atoi(tokens[1].c_str()); + } + else + { + const auto & x = effect_map.find(boost::to_lower_copy(opt_effect)); + if(x == effect_map.end()) + { + std::cout << "The effect '" << opt_effect << "' was not found. "; + print_available_effects(); + return(6); + } + opt_effect_id = static_cast(x->second); + } + } + + std::string your_deck_name{argv[1]}; + std::string enemy_deck_list{argv[2]}; + auto && deck_list_parsed = parse_deck_list(enemy_deck_list, decks); + + Deck* your_deck{nullptr}; + std::vector enemy_decks; + std::vector enemy_decks_factors; + + try + { + your_deck = find_deck(decks, all_cards, your_deck_name); + } + catch(const std::runtime_error& e) + { + std::cerr << "Error: Deck " << your_deck_name << ": " << e.what() << std::endl; + return(5); + } + if(your_deck == nullptr) + { + std::cerr << "Error: Invalid attack deck name/hash " << your_deck_name << ".\n"; + } + else if(!your_deck->raid_cards.empty()) + { + std::cerr << "Error: Invalid attack deck " << your_deck_name << ": has optional cards.\n"; + your_deck = nullptr; + } + if(your_deck == nullptr) + { + usage(argc, argv); + return(5); } - att_deck->strategy = att_strategy; - for(auto def_deck: def_decks) + your_deck->strategy = opt_your_strategy; + your_deck->set_forts(opt_forts); + your_deck->set_given_hand(opt_hand); + if (opt_keep_commander) { - def_deck->strategy = def_strategy; + your_deck->card_marks[-1] = '!'; } - if(keep_commander) + for(auto deck_parsed: deck_list_parsed) { - att_deck->card_marks[-1] = '!'; + Deck* enemy_deck{nullptr}; + try + { + enemy_deck = find_deck(decks, all_cards, deck_parsed.first); + } + catch(const std::runtime_error& e) + { + std::cerr << "Error: Deck " << deck_parsed.first << ": " << e.what() << std::endl; + return(5); + } + if(enemy_deck == nullptr) + { + std::cerr << "Error: Invalid defense deck name/hash " << deck_parsed.first << ".\n"; + usage(argc, argv); + return(5); + } + if(enemy_deck->decktype == DeckType::raid) + { + optimization_mode = OptimizationMode::raid; + turn_limit = 30; + target_score = 250; + } + enemy_deck->strategy = opt_enemy_strategy; + enemy_deck->set_forts(opt_enemy_forts); + enemy_deck->set_given_hand(opt_enemy_hand); + enemy_decks.push_back(enemy_deck); + enemy_decks_factors.push_back(deck_parsed.second); } - if(fixed_len) + + // Force to claim cards in your initial deck. + if (opt_do_optimization and use_owned_cards) { - min_deck_len = max_deck_len = att_deck->cards.size(); + claim_cards({your_deck->commander}); + claim_cards(your_deck->cards); } - std::cout << "Your Deck: " << (debug_print ? att_deck->long_description() : att_deck->medium_description()) << std::endl; - for (unsigned i(0); i < def_decks.size(); ++i) + std::cout << "Your Deck: " << (debug_print ? your_deck->long_description() : your_deck->medium_description()) << std::endl; + for (unsigned i(0); i < enemy_decks.size(); ++i) + { + std::cout << "Enemy's Deck:" << enemy_decks_factors[i] << ": " << (debug_print ? enemy_decks[i]->long_description() : enemy_decks[i]->medium_description()) << std::endl; + } + if(opt_effect_id != Effect::none) { - std::cout << "Enemy's Deck:" << def_decks_factors[i] << ": " << (debug_print ? def_decks[i]->long_description() : def_decks[i]->medium_description()) << std::endl; + std::cout << "Effect: " << effect_names[opt_effect_id] << std::endl; } - if(effect != Effect::none) + else if(opt_bg_enhanced_skill != no_skill) { - std::cout << "Effect: " << effect_names[effect] << std::endl; + std::cout << "Effect: (Enhance all) " << skill_names[opt_bg_enhanced_skill] << " " << opt_bg_enhanced_value << std::endl; } - Process p(num_threads, all_cards, decks, att_deck, def_decks, def_decks_factors, gamemode, effect, bg_enhanced_skill, bg_enhanced_value, achievement); + Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, opt_effect_id, opt_bg_enhanced_skill, opt_bg_enhanced_value); { //ScopeClock timer; - for(auto op: todo) + for(auto op: opt_todo) { switch(std::get<2>(op)) { @@ -1518,43 +1429,39 @@ int main(int argc, char** argv) break; } case climb: { - switch (att_strategy) + switch (opt_your_strategy) { case DeckStrategy::random: - hill_climbing(std::get<0>(op), att_deck, p, att_deck->card_marks); + hill_climbing(std::get<0>(op), your_deck, p, your_deck->card_marks); break; // case DeckStrategy::ordered: // case DeckStrategy::exact_ordered: default: - hill_climbing_ordered(std::get<0>(op), att_deck, p, att_deck->card_marks); + hill_climbing_ordered(std::get<0>(op), your_deck, p, your_deck->card_marks); break; } break; } case reorder: { - min_deck_len = max_deck_len = att_deck->cards.size(); + your_deck->strategy = DeckStrategy::ordered; + min_deck_len = max_deck_len = your_deck->cards.size(); use_owned_cards = true; auto_upgrade_cards = false; owned_cards.clear(); - claim_cards({att_deck->commander}, true, false); - claim_cards(att_deck->cards, true, false); - hill_climbing_ordered(std::get<0>(op), att_deck, p, att_deck->card_marks); + claim_cards({your_deck->commander}); + claim_cards(your_deck->cards); + hill_climbing_ordered(std::get<0>(op), your_deck, p, your_deck->card_marks); break; } case debug: { - unsigned saved_num_threads = num_threads; - num_threads = 1; ++ debug_print; debug_str.clear(); auto results = p.evaluate(1); print_results(results, p.factors); -- debug_print; - num_threads = saved_num_threads; break; } case debuguntil: { - unsigned saved_num_threads = num_threads; - num_threads = 1; ++ debug_print; ++ debug_cached; while(1) @@ -1571,7 +1478,6 @@ int main(int argc, char** argv) } -- debug_cached; -- debug_print; - num_threads = saved_num_threads; break; } } diff --git a/xml.cpp b/xml.cpp index 4ecc8591..ba54e60b 100644 --- a/xml.cpp +++ b/xml.cpp @@ -6,11 +6,11 @@ #include #include #include +#include #include "rapidxml.hpp" #include "card.h" #include "cards.h" #include "deck.h" -#include "achievement.h" #include "tyrant.h" //---------------------- $20 cards.xml parsing --------------------------------- // Sets: 1 enclave; 2 nexus; 3 blight; 4 purity; 5 homeworld; @@ -21,7 +21,6 @@ using namespace rapidxml; Faction map_to_faction(unsigned i) { -#if defined(TYRANT_UNLEASHED) return(i == 1 ? imperial : i == 2 ? raider : i == 3 ? bloodthirsty : @@ -29,14 +28,6 @@ Faction map_to_faction(unsigned i) i == 5 ? righteous : i == 6 ? progenitor : allfactions); -#else - return(i == 1 ? imperial : - i == 9 ? raider : - i == 3 ? bloodthirsty : - i == 4 ? xeno : - i == 8 ? righteous : - allfactions); -#endif } CardType::CardType map_to_type(unsigned i) @@ -44,23 +35,21 @@ CardType::CardType map_to_type(unsigned i) return(i == 1 ? CardType::commander : i == 2 ? CardType::assault : i == 4 ? CardType::structure : - i == 8 ? CardType::action : CardType::num_cardtypes); } -Skill skill_name_to_id(const char* name) +Skill skill_name_to_id(const std::string & name) { static std::map skill_map; if(skill_map.empty()) { for(unsigned i(0); i < Skill::num_skills; ++i) { - std::string skill_id{skill_names[i]}; - std::transform(skill_id.begin(), skill_id.end(), skill_id.begin(), ::tolower); + std::string skill_id = boost::to_lower_copy(skill_names[i]); skill_map[skill_id] = i; } } - auto x = skill_map.find(name); + auto x = skill_map.find(boost::to_lower_copy(name)); return x == skill_map.end() ? no_skill : (Skill)x->second; } @@ -93,34 +82,16 @@ Skill skill_target_skill(xml_node<>* skill) } //------------------------------------------------------------------------------ -void load_decks_xml(Decks& decks, const Cards& all_cards) +void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_filename) { try { - read_missions(decks, all_cards, "missions.xml"); + read_missions(decks, all_cards, mission_filename); } - catch(const rapidxml::parse_error& e) + catch (const rapidxml::parse_error& e) { - std::cout << "\nException while loading decks from file missions.xml\n"; + std::cout << "\nFailed to parse file data/missions.xml\n"; } -#if not defined(TYRANT_UNLEASHED) - try - { - read_raids(decks, all_cards, "raids.xml"); - } - catch(const rapidxml::parse_error& e) - { - std::cout << "\nException while loading decks from file raids.xml\n"; - } - try - { - read_quests(decks, all_cards, "quests.xml"); - } - catch(const rapidxml::parse_error& e) - { - std::cout << "\nException while loading decks from file quests.xml\n"; - } -#endif } //------------------------------------------------------------------------------ @@ -161,14 +132,9 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) xml_node<>* card_id_node = card_node->first_node("card_id"); assert(id_node || card_id_node); xml_node<>* name_node(card_node->first_node("name")); - xml_node<>* hidden_node(card_node->first_node("hidden")); - xml_node<>* replace_node(card_node->first_node("replace")); xml_node<>* attack_node(card_node->first_node("attack")); xml_node<>* health_node(card_node->first_node("health")); xml_node<>* cost_node(card_node->first_node("cost")); - xml_node<>* unique_node(card_node->first_node("unique")); - xml_node<>* reserve_node(card_node->first_node("reserve")); - xml_node<>* base_card_node(card_node->first_node("base_card")); xml_node<>* rarity_node(card_node->first_node("rarity")); xml_node<>* type_node(card_node->first_node("type")); xml_node<>* set_node(card_node->first_node("set")); @@ -193,29 +159,30 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) { card->m_type = CardType::commander; } else if (card->m_id < 3000) { card->m_type = CardType::structure; } +#if 0 else if (card->m_id < 4000) { card->m_type = CardType::action; } +#endif + else if (card->m_id < 8000) + { card->m_type = CardType::assault; } + else if (card->m_id < 10000) + { card->m_type = CardType::structure; } else + { card->m_type = cost_node ? (attack_node ? CardType::assault : CardType::structure) : CardType::commander; } +#if 0 { card->m_type = cost_node ? (attack_node ? CardType::assault : CardType::structure) : (health_node ? CardType::commander : CardType::action); } +#endif } - if(hidden_node) { card->m_hidden = atoi(hidden_node->value()); } - if(replace_node) { card->m_replace = atoi(replace_node->value()); } if(attack_node) { card->m_attack = atoi(attack_node->value()); } if(health_node) { card->m_health = atoi(health_node->value()); } if(cost_node) { card->m_delay = atoi(cost_node->value()); } - if(unique_node) { card->m_unique = true; } - if(reserve_node) { card->m_reserve = atoi(reserve_node->value()); } - if(base_card_node) { card->m_base_id = atoi(base_card_node->value()); } if(rarity_node) { card->m_rarity = atoi(rarity_node->value()); } if(type_node) { card->m_faction = map_to_faction(atoi(type_node->value())); } card->m_set = set; if (card_node->first_node("skill")) { // inherit no skill if there is skill node - for (unsigned mod = 0; mod < SkillMod::num_skill_activation_modifiers; ++ mod) - { - card->m_skills[mod].clear(); - } + card->m_skills.clear(); memset(card->m_skill_value, 0, sizeof card->m_skill_value); } for(xml_node<>* skill_node = card_node->first_node("skill"); @@ -224,27 +191,15 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) { Skill skill_id = skill_name_to_id(skill_node->first_attribute("id")->value()); if(skill_id == no_skill) { continue; } - - bool all(skill_node->first_attribute("all")); - bool played(skill_node->first_attribute("played")); - bool attacked(skill_node->first_attribute("attacked")); - bool kill(skill_node->first_attribute("kill")); - bool died(skill_node->first_attribute("died")); - bool normal(!(played || died || attacked || kill)); - auto x = node_value(skill_node, "x", 0); auto y = skill_faction(skill_node); + auto n = node_value(skill_node, "n", 0); auto c = node_value(skill_node, "c", 0); auto s = skill_target_skill(skill_node); - - if (played) { card->add_skill(skill_id, x, y, c, s, all, SkillMod::on_play); } - if (attacked) { card->add_skill(skill_id, x, y, c, s, all, SkillMod::on_attacked); } - if (kill) { card->add_skill(skill_id, x, y, c, s, all, SkillMod::on_kill); } - if (died) { card->add_skill(skill_id, x, y, c, s, all, SkillMod::on_death); } - if (normal) { card->add_skill(skill_id, x, y, c, s, all); } + bool all(skill_node->first_attribute("all")); + card->add_skill(skill_id, x, y, n, c, s, all); } all_cards.cards.push_back(card); -#if defined(TYRANT_UNLEASHED) Card * top_card = card; for(xml_node<>* upgrade_node = card_node->first_node("upgrade"); upgrade_node; @@ -268,24 +223,19 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) pre_upgraded_card->m_used_for_cards[top_card] = 1; } card->m_top_level_card = top_card; -#endif } -void read_cards(Cards& all_cards) +void load_cards_xml(Cards & all_cards, const char * filename) { std::vector buffer; xml_document<> doc; - parse_file("cards.xml", buffer, doc); + parse_file(filename, buffer, doc); xml_node<>* root = doc.first_node(); if(!root) { return; } -#if 0 - unsigned nb_cards(0); - std::map sets_counts; -#endif for(xml_node<>* card_node = root->first_node("unit"); card_node; card_node = card_node->next_sibling("unit")) @@ -294,13 +244,6 @@ void read_cards(Cards& all_cards) parse_card_node(all_cards, card, card_node); } all_cards.organize(); -#if 0 - std::cout << "nb cards: " << nb_cards << "\n"; - for(auto counts: sets_counts) - { - std::cout << "set " << counts.first << ": " << counts.second << "\n"; - } -#endif } //------------------------------------------------------------------------------ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const char* effect_node_name, DeckType::DeckType decktype, unsigned id, std::string base_deck_name, bool has_levels=false) @@ -351,7 +294,6 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch unsigned mission_req(mission_req_node ? atoi(mission_req_node->value()) : 0); xml_node<>* effect_id_node(node->first_node(effect_node_name)); Effect effect = effect_id_node ? static_cast(atoi(effect_id_node->value())) : Effect::none; -#if defined(TYRANT_UNLEASHED) if (has_levels) { for (unsigned level = 1; level <= 9; ++ level) @@ -365,11 +307,9 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch decks.by_name[alt_name] = deck; } } -#endif decks.decks.push_back(Deck{all_cards, decktype, id, base_deck_name, effect}); Deck* deck = &decks.decks.back(); deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); -#if defined(TYRANT_UNLEASHED) if (has_levels) { // upgrade cards in deck deck->commander = deck->commander->m_top_level_card; @@ -381,7 +321,6 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch { card = card->m_top_level_card; } } } -#endif std::string alt_name = decktype_names[decktype] + " #" + to_string(id); decks.by_name[base_deck_name] = deck; decks.by_name[alt_name] = deck; @@ -439,40 +378,13 @@ void read_raids(Decks& decks, const Cards& all_cards, std::string filename) read_deck(decks, all_cards, raid_node, "effect", DeckType::raid, id, deck_name); } } -//------------------------------------------------------------------------------ -void read_quests(Decks& decks, const Cards& all_cards, std::string filename) -{ - std::vector buffer; - xml_document<> doc; - parse_file(filename.c_str(), buffer, doc); - xml_node<>* root = doc.first_node(); - - if(!root) - { - return; - } - - // Seems always_cards is empty for all quests. - std::vector always_cards; - - for(xml_node<>* quest_node = root->first_node("step"); - quest_node; - quest_node = quest_node->next_sibling("step")) - { - xml_node<>* id_node(quest_node->first_node("id")); - assert(id_node); - unsigned id(id_node ? atoi(id_node->value()) : 0); - std::string deck_name{"Step " + std::string{id_node->value()}}; - read_deck(decks, all_cards, quest_node, "battleground_id", DeckType::quest, id, deck_name); - } -} //------------------------------------------------------------------------------ -void load_recipes_xml(Cards& all_cards) +void load_recipes_xml(Cards& all_cards, const char * filename) { std::vector buffer; xml_document<> doc; - parse_file("fusion_recipes_cj2.xml", buffer, doc); + parse_file(filename, buffer, doc); xml_node<>* root = doc.first_node(); if(!root) @@ -502,161 +414,3 @@ void load_recipes_xml(Cards& all_cards) } } -//------------------------------------------------------------------------------ -Comparator get_comparator(xml_node<>* node, Comparator default_comparator) -{ - xml_attribute<>* compare(node->first_attribute("compare")); - if(!compare) { return default_comparator; } - else if(strcmp(compare->value(), "equal") == 0) { return equal; } - else if(strcmp(compare->value(), "great_equal") == 0) { return great_equal; } - else if(strcmp(compare->value(), "less_equal") == 0) { return less_equal; } - else { throw std::runtime_error(std::string("Not implemented: compare=\"") + compare->value() + "\""); } -} - -void read_achievement(Decks& decks, const Cards& all_cards, Achievement& achievement, const char* achievement_id_name, std::string filename/* = "achievements.xml"*/) -{ - std::vector buffer; - xml_document<> doc; - parse_file(filename.c_str(), buffer, doc); - xml_node<>* root = doc.first_node(); - - if(!root) - { - throw std::runtime_error("Failed to parse " + filename); - } - - for(xml_node<>* achievement_node = root->first_node("achievement"); - achievement_node; - achievement_node = achievement_node->next_sibling("achievement")) - { - xml_node<>* id_node(achievement_node->first_node("id")); - xml_node<>* name_node(achievement_node->first_node("name")); - if(!id_node || !name_node || (strcmp(id_node->value(), achievement_id_name) != 0 && strcmp(name_node->value(), achievement_id_name) != 0)) { continue; } - achievement.id = atoi(id_node->value()); - achievement.name = name_node->value(); - std::cout << "Achievement " << id_node->value() << " " << name_node->value() << ": " << achievement_node->first_node("desc")->value() << std::endl; - xml_node<>* type_node(achievement_node->first_node("type")); - xml_attribute<>* mission_id(type_node ? type_node->first_attribute("mission_id") : NULL); - if(!type_node || !mission_id) - { - throw std::runtime_error("Must be 'mission' type."); - } - assert(strcmp(type_node->first_attribute("winner")->value(), "1") == 0); - if(strcmp(mission_id->value(), "*") != 0) - { - achievement.mission_condition.init(atoi(mission_id->value()), get_comparator(type_node, equal)); - std::cout << " Mission" << achievement.mission_condition.str() << " (" << decks.by_type_id[{DeckType::mission, atoi(mission_id->value())}]->name << ") and win" << std::endl; - } - for (xml_node<>* req_node = achievement_node->first_node("req"); - req_node; - req_node = req_node->next_sibling("req")) - { - Comparator comparator = get_comparator(req_node, great_equal); - xml_attribute<>* skill_id_node(req_node->first_attribute("skill_id")); - xml_attribute<>* unit_id(req_node->first_attribute("unit_id")); - xml_attribute<>* unit_type(req_node->first_attribute("unit_type")); - xml_attribute<>* unit_race(req_node->first_attribute("unit_race")); - xml_attribute<>* unit_rarity(req_node->first_attribute("unit_rarity")); - xml_attribute<>* num_turns(req_node->first_attribute("num_turns")); - xml_attribute<>* num_used(req_node->first_attribute("num_used")); - xml_attribute<>* num_played(req_node->first_attribute("num_played")); - xml_attribute<>* num_killed(req_node->first_attribute("num_killed")); - xml_attribute<>* num_killed_with(req_node->first_attribute("num_killed_with")); - xml_attribute<>* damage(req_node->first_attribute("damage")); - xml_attribute<>* com_total(req_node->first_attribute("com_total")); - xml_attribute<>* only(req_node->first_attribute("only")); - if(skill_id_node && num_used) - { - auto skill_id = skill_name_to_id(skill_id_node->value()); - if(skill_id == no_skill) - { - throw std::runtime_error(std::string("Unknown skill ") + skill_id_node->value()); - } - achievement.skill_used[skill_id] = achievement.req_counter.size(); - achievement.req_counter.emplace_back(atoi(num_used->value()), comparator); - std::cout << " Use skills: " << skill_id_node->value() << achievement.req_counter.back().str() << std::endl; - } - else if(unit_id && num_played) - { - achievement.unit_played[atoi(unit_id->value())] = achievement.req_counter.size(); - achievement.req_counter.emplace_back(atoi(num_played->value()), comparator); - std::cout << " Play units: " << all_cards.by_id(atoi(unit_id->value()))->m_name << achievement.req_counter.back().str() << std::endl; - } - else if(unit_type && num_played) - { - auto i = map_to_type(atoi(unit_type->value())); - if(i == CardType::num_cardtypes) - { - throw std::runtime_error(std::string("Unknown unit_type ") + unit_type->value()); - } - achievement.unit_type_played[i] = achievement.req_counter.size(); - achievement.req_counter.emplace_back(atoi(num_played->value()), comparator); - std::cout << " Play units of type: " << cardtype_names[i] << achievement.req_counter.back().str() << std::endl; - } - else if(unit_race && num_played) - { - auto i = map_to_faction(atoi(unit_race->value())); - if(i == Faction::allfactions) - { - throw std::runtime_error(std::string("Unknown unit_race ") + unit_race->value()); - } - achievement.unit_faction_played[i] = achievement.req_counter.size(); - achievement.req_counter.emplace_back(atoi(num_played->value()), comparator); - std::cout << " Play units of race (faction): " << faction_names[i] << achievement.req_counter.back().str() << std::endl; - } - else if(unit_rarity && num_played) - { - achievement.unit_rarity_played[atoi(unit_rarity->value())] = achievement.req_counter.size(); - achievement.req_counter.emplace_back(atoi(num_played->value()), comparator); - std::cout << " Play units of rarity: " << rarity_names[atoi(unit_rarity->value())] << achievement.req_counter.back().str() << std::endl; - } - else if(unit_type && num_killed) - { - auto i = map_to_type(atoi(unit_type->value())); - if(i == CardType::num_cardtypes) - { - throw std::runtime_error(std::string("Unknown unit_type ") + unit_type->value()); - } - achievement.unit_type_killed[i] = achievement.req_counter.size(); - achievement.req_counter.emplace_back(atoi(num_killed->value()), comparator); - std::cout << " Kill units of type: " << cardtype_names[i] << achievement.req_counter.back().str() << std::endl; - } - else if(num_killed_with && skill_id_node && strcmp(skill_id_node->value(), "flying") == 0) - { - achievement.misc_req[AchievementMiscReq::unit_with_flying_killed] = achievement.req_counter.size(); - achievement.req_counter.emplace_back(atoi(num_killed_with->value()), comparator); - std::cout << " " << achievement_misc_req_names[AchievementMiscReq::unit_with_flying_killed] << achievement.req_counter.back().str() << std::endl; - } - else if(only && skill_id_node && strcmp(skill_id_node->value(), "0") == 0) - { - achievement.misc_req[AchievementMiscReq::skill_activated] = achievement.req_counter.size(); - achievement.req_counter.emplace_back(0, equal); - std::cout << " " << achievement_misc_req_names[AchievementMiscReq::skill_activated] << achievement.req_counter.back().str() << std::endl; - } - else if(num_turns) - { - achievement.misc_req[AchievementMiscReq::turns] = achievement.req_counter.size(); - achievement.req_counter.emplace_back(atoi(num_turns->value()), comparator); - std::cout << " " << achievement_misc_req_names[AchievementMiscReq::turns] << achievement.req_counter.back().str() << std::endl; - } - else if(damage) - { - achievement.misc_req[AchievementMiscReq::damage] = achievement.req_counter.size(); - achievement.req_counter.emplace_back(atoi(damage->value()), comparator); - std::cout << " " << achievement_misc_req_names[AchievementMiscReq::damage] << achievement.req_counter.back().str() << std::endl; - } - else if(com_total) - { - achievement.misc_req[AchievementMiscReq::com_total] = achievement.req_counter.size(); - achievement.req_counter.emplace_back(atoi(com_total->value()), comparator); - std::cout << " " << achievement_misc_req_names[AchievementMiscReq::com_total] << achievement.req_counter.back().str() << std::endl; - } - else - { - throw std::runtime_error("Not implemented."); - } - } - return; - } - throw std::runtime_error("No such achievement."); -} diff --git a/xml.h b/xml.h index d955436d..0b4f76b5 100644 --- a/xml.h +++ b/xml.h @@ -8,13 +8,11 @@ class Cards; class Decks; class Achievement; -Skill skill_name_to_id(const char* name); -void load_decks_xml(Decks& decks, const Cards& cards); -void load_recipes_xml(Cards& cards); -void read_cards(Cards& cards); -void read_missions(Decks& decks, const Cards& cards, std::string filename); -void read_raids(Decks& decks, const Cards& cards, std::string filename); -void read_quests(Decks& decks, const Cards& cards, std::string filename); -void read_achievement(Decks& decks, const Cards& cards, Achievement& achievement, const char* achievement_name, std::string filename="achievements.xml"); +Skill skill_name_to_id(const std::string & name); +void load_cards_xml(Cards & all_cards, const char * filename); +void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_filename); +void load_recipes_xml(Cards& all_cards, const char * filename); +void read_missions(Decks& decks, const Cards& all_cards, std::string filename); +void read_raids(Decks& decks, const Cards& all_cards, std::string filename); #endif From ceb480ae88e27dbc3f1ed6f46d6e7b9ac9801a18 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Tue, 19 Aug 2014 18:28:26 +0800 Subject: [PATCH 228/406] Fix minor bugs and behaviors. --- deck.cpp | 4 +++ read.cpp | 7 ++--- sim.cpp | 10 +++---- tyrant.h | 2 +- tyrant_optimize.cpp | 68 ++++++++++++++++++++++----------------------- 5 files changed, 46 insertions(+), 45 deletions(-) diff --git a/deck.cpp b/deck.cpp index 8e170bfb..cc361a9f 100644 --- a/deck.cpp +++ b/deck.cpp @@ -454,6 +454,10 @@ std::string Deck::long_description() const } } } + for (const Card * fort: fort_cards) + { + ios << card_description(all_cards, fort) << "\n"; + } if (! reward_cards.empty()) { ios << "Reward Cards: "; diff --git a/read.cpp b/read.cpp index daad48db..9df4de54 100644 --- a/read.cpp +++ b/read.cpp @@ -188,8 +188,7 @@ void parse_card_spec(const Cards& all_cards, std::string& card_spec, unsigned& c // } simple_name = simplify_name(abbr_it->second); } - auto card_it = all_cards.player_cards_by_name.find({simple_name, 0}); - if (card_it == all_cards.player_cards_by_name.end()) { card_it = all_cards.player_cards_by_name.find({simple_name, 1}); } + auto card_it = all_cards.player_cards_by_name.find(simple_name); auto card_id_iter = advance_until(simple_name.begin(), simple_name.end(), [](char c){return(c=='[');}); if(card_it != all_cards.player_cards_by_name.end()) { @@ -252,13 +251,13 @@ unsigned read_card_abbrs(Cards& all_cards, const std::string& filename) continue; } abbr_string_iter = advance_until(abbr_string_iter + 1, abbr_string.end(), [](const char& c){return(c != ' ');}); - if(all_cards.player_cards_by_name.find({abbr_name, 0}) != all_cards.player_cards_by_name.end()) + if(all_cards.player_cards_by_name.find(abbr_name) != all_cards.player_cards_by_name.end()) { std::cerr << "Warning in card abbreviation file " << filename << " at line " << num_line << ": ignored because the name has been used by an existing card." << std::endl; } else { - all_cards.player_cards_abbr[abbr_name] = std::string{abbr_string_iter, abbr_string.end()}; + all_cards.player_cards_abbr[simplify_name(abbr_name)] = std::string{abbr_string_iter, abbr_string.end()}; } } } diff --git a/sim.cpp b/sim.cpp index ebfcecb3..cbdcf612 100644 --- a/sim.cpp +++ b/sim.cpp @@ -842,7 +842,7 @@ struct PerformAttack std::string desc; if(def_status->m_enfeebled > 0) { - if(debug_print) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } + if(debug_print > 0) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } att_dmg += def_status->m_enfeebled; } // prevent damage @@ -851,21 +851,21 @@ struct PerformAttack unsigned armored_value = def_status->skill(); if(armored_value > 0) { - if(debug_print) { reduced_desc += to_string(armored_value) + "(armored)"; } + if(debug_print > 0) { reduced_desc += to_string(armored_value) + "(armored)"; } reduced_dmg += armored_value; } if(def_status->protected_value() > 0) { - if(debug_print) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->protected_value()) + "(protected)"; } + if(debug_print > 0) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->protected_value()) + "(protected)"; } reduced_dmg += def_status->protected_value(); } if(reduced_dmg > 0 && att_status->skill() > 0) { - if(debug_print) { reduced_desc += "-" + to_string(att_status->skill()) + "(pierce)"; } + if(debug_print > 0) { reduced_desc += "-" + to_string(att_status->skill()) + "(pierce)"; } reduced_dmg = safe_minus(reduced_dmg, att_status->skill()); } att_dmg = safe_minus(att_dmg, reduced_dmg); - if(debug_print) + if(debug_print > 0) { if(!reduced_desc.empty()) { desc += "-[" + reduced_desc + "]"; } if(!desc.empty()) { desc += "=" + to_string(att_dmg); } diff --git a/tyrant.h b/tyrant.h index 550210c3..8809448e 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.0.0" +#define TYRANT_OPTIMIZER_VERSION "2.0.1" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 21641f24..6f363393 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -48,7 +48,6 @@ namespace { unsigned min_deck_len{1}; unsigned max_deck_len{10}; unsigned fund{0}; - bool auto_upgrade_cards{true}; long double target_score{100}; bool show_stdev{false}; bool use_harmonic_mean{false}; @@ -112,12 +111,13 @@ unsigned get_required_cards_before_upgrade(const std::vector & car ++ num_cards[card]; unresolved_cards.insert(card); } - while (!unresolved_cards.empty()) + // un-upgrade only if fund is used + while (fund > 0 && !unresolved_cards.empty()) { auto card_it = unresolved_cards.end(); auto card = *(-- card_it); unresolved_cards.erase(card_it); - if(auto_upgrade_cards && owned_cards[card->m_id] < num_cards[card] && !card->m_recipe_cards.empty()) + if(owned_cards[card->m_id] < num_cards[card] && !card->m_recipe_cards.empty()) { unsigned num_under = num_cards[card] - owned_cards[card->m_id]; num_cards[card] = owned_cards[card->m_id]; @@ -262,7 +262,7 @@ void claim_cards(const std::vector & card_list) if(num_to_claim > 0) { owned_cards[card->m_id] += num_to_claim; - if(debug_print) + if(debug_print > 0) { std::cout << "Claim " << card->m_name << " (" << num_to_claim << ")" << std::endl; } @@ -1022,9 +1022,8 @@ void usage(int argc, char** argv) " -L : restrict deck size between and .\n" " -o: restrict to the owned cards listed in \"data/ownedcards.txt\".\n" " -o=: restrict to the owned cards listed in .\n" - " fund : fund gold to buy/upgrade cards. prices are specified in ownedcards file.\n" + " fund : invest SP to upgrade cards.\n" " target : stop as soon as the score reaches .\n" -// " -u: don't upgrade owned cards. (by default, upgrade owned cards when needed)\n" "\n" "Operations:\n" " sim : simulate battles to evaluate a deck.\n" @@ -1042,12 +1041,12 @@ int main(int argc, char** argv) if (argc == 2 && strcmp(argv[1], "-version") == 0) { std::cout << "Tyrant Unleashed Optimizer " << TYRANT_OPTIMIZER_VERSION << std::endl; - return(0); + return 0; } if (argc <= 2) { usage(argc, argv); - return(0); + return 0; } @@ -1129,11 +1128,11 @@ int main(int argc, char** argv) optimization_mode = OptimizationMode::defense; } // Others - else if (strcmp(argv[argIndex], "-c") == 0) + else if (strcmp(argv[argIndex], "keep-commander") == 0 || strcmp(argv[argIndex], "-c") == 0) { opt_keep_commander = true; } - else if (strcmp(argv[argIndex], "-e") == 0) + else if (strcmp(argv[argIndex], "effect") == 0 || strcmp(argv[argIndex], "-e") == 0) { opt_effect = argv[argIndex + 1]; argIndex += 1; @@ -1183,7 +1182,7 @@ int main(int argc, char** argv) { opt_enemy_strategy = DeckStrategy::exact_ordered; } - else if(strcmp(argv[argIndex], "-t") == 0) + else if(strcmp(argv[argIndex], "threads") == 0 || strcmp(argv[argIndex], "-t") == 0) { opt_num_threads = atoi(argv[argIndex+1]); argIndex += 1; @@ -1198,10 +1197,6 @@ int main(int argc, char** argv) turn_limit = atoi(argv[argIndex+1]); argIndex += 1; } - else if(strcmp(argv[argIndex], "-u") == 0) - { - auto_upgrade_cards = false; - } else if(strcmp(argv[argIndex], "+stdev") == 0) { show_stdev = true; @@ -1268,7 +1263,7 @@ int main(int argc, char** argv) else { std::cerr << "Error: Unknown option " << argv[argIndex] << std::endl; - return(1); + return 0; } } @@ -1317,7 +1312,7 @@ int main(int argc, char** argv) { std::cout << "The effect '" << opt_effect << "' was not found. "; print_available_effects(); - return(6); + return 0; } opt_effect_id = static_cast(x->second); } @@ -1338,7 +1333,7 @@ int main(int argc, char** argv) catch(const std::runtime_error& e) { std::cerr << "Error: Deck " << your_deck_name << ": " << e.what() << std::endl; - return(5); + return 0; } if(your_deck == nullptr) { @@ -1352,7 +1347,7 @@ int main(int argc, char** argv) if(your_deck == nullptr) { usage(argc, argv); - return(5); + return 0; } your_deck->strategy = opt_your_strategy; @@ -1373,13 +1368,13 @@ int main(int argc, char** argv) catch(const std::runtime_error& e) { std::cerr << "Error: Deck " << deck_parsed.first << ": " << e.what() << std::endl; - return(5); + return 0; } if(enemy_deck == nullptr) { std::cerr << "Error: Invalid defense deck name/hash " << deck_parsed.first << ".\n"; usage(argc, argv); - return(5); + return 0; } if(enemy_deck->decktype == DeckType::raid) { @@ -1401,18 +1396,21 @@ int main(int argc, char** argv) claim_cards(your_deck->cards); } - std::cout << "Your Deck: " << (debug_print ? your_deck->long_description() : your_deck->medium_description()) << std::endl; - for (unsigned i(0); i < enemy_decks.size(); ++i) - { - std::cout << "Enemy's Deck:" << enemy_decks_factors[i] << ": " << (debug_print ? enemy_decks[i]->long_description() : enemy_decks[i]->medium_description()) << std::endl; - } - if(opt_effect_id != Effect::none) + if (debug_print >= 0) { - std::cout << "Effect: " << effect_names[opt_effect_id] << std::endl; - } - else if(opt_bg_enhanced_skill != no_skill) - { - std::cout << "Effect: (Enhance all) " << skill_names[opt_bg_enhanced_skill] << " " << opt_bg_enhanced_value << std::endl; + std::cout << "Your Deck: " << (debug_print > 0 ? your_deck->long_description() : your_deck->medium_description()) << std::endl; + for (unsigned i(0); i < enemy_decks.size(); ++i) + { + std::cout << "Enemy's Deck:" << enemy_decks_factors[i] << ": " << (debug_print > 0 ? enemy_decks[i]->long_description() : enemy_decks[i]->medium_description()) << std::endl; + } + if(opt_effect_id != Effect::none) + { + std::cout << "Effect: " << effect_names[opt_effect_id] << std::endl; + } + else if(opt_bg_enhanced_skill != no_skill) + { + std::cout << "Effect: (Enhance all) " << skill_names[opt_bg_enhanced_skill] << " " << opt_bg_enhanced_value << std::endl; + } } Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, opt_effect_id, opt_bg_enhanced_skill, opt_bg_enhanced_value); @@ -1444,9 +1442,9 @@ int main(int argc, char** argv) } case reorder: { your_deck->strategy = DeckStrategy::ordered; - min_deck_len = max_deck_len = your_deck->cards.size(); use_owned_cards = true; - auto_upgrade_cards = false; + min_deck_len = max_deck_len = your_deck->cards.size(); + fund = 0; owned_cards.clear(); claim_cards({your_deck->commander}); claim_cards(your_deck->cards); @@ -1483,5 +1481,5 @@ int main(int argc, char** argv) } } } - return(0); + return 0; } From 6c7f1e92616ec1b53f7c039ebf1a878746e8988a Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 21 Aug 2014 08:54:28 +0800 Subject: [PATCH 229/406] Modify error message. --- cards.cpp | 2 +- deck.cpp | 4 ++-- read.cpp | 12 ++++++++++-- tyrant_optimize.cpp | 1 - xml.cpp | 17 +++++++++-------- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/cards.cpp b/cards.cpp index 7334add2..09c20bf9 100644 --- a/cards.cpp +++ b/cards.cpp @@ -50,7 +50,7 @@ const Card* Cards::by_id(unsigned id) const std::map::const_iterator cardIter{cards_by_id.find(id)}; if(cardIter == cards_by_id.end()) { - throw std::runtime_error("While trying to find the card with id " + to_string(id) + ": no such key in the cards_by_id map."); + throw std::runtime_error("No card with id " + to_string(id)); } else { diff --git a/deck.cpp b/deck.cpp index cc361a9f..01f01a6e 100644 --- a/deck.cpp +++ b/deck.cpp @@ -258,7 +258,7 @@ const std::pair, std::map> string_to_ids(con { if (! error_list.empty()) { - std::cerr << "Warning while resolving " << description << ": "; + std::cerr << "Warning: Ignore some cards while resolving " << description << ": "; for (auto error: error_list) { std::cerr << '[' << error << ']'; @@ -273,7 +273,7 @@ const std::pair, std::map> string_to_ids(con } catch(std::exception& e) { - std::cerr << "Error while resolving " << description << ": " << e.what() << std::endl; + std::cerr << "Error: Failed to resolve " << description << ": " << e.what() << std::endl; throw; } return {card_ids, card_marks}; diff --git a/read.cpp b/read.cpp index 9df4de54..135ac0b0 100644 --- a/read.cpp +++ b/read.cpp @@ -148,9 +148,17 @@ DeckList parse_deck_list(std::string list_string, const Decks& decks) auto deck_name = *deck_token; double factor = 1.0d; ++ deck_token; - if(deck_token != deck_tokens.end()) + if (deck_token != deck_tokens.end()) { - factor = boost::lexical_cast(*deck_token); + try + { + factor = boost::lexical_cast(*deck_token); + } + catch (const boost::bad_lexical_cast & e) + { + std::cerr << "Warning: Is ':' a typo? Skip deck [" << list_token << "]\n"; + continue; + } } auto && decklist = expand_deck_to_list(deck_name, decks); for (const auto & it : decklist) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 6f363393..3b685001 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1047,7 +1047,6 @@ int main(int argc, char** argv) { usage(argc, argv); return 0; - } unsigned opt_num_threads(4); diff --git a/xml.cpp b/xml.cpp index ba54e60b..24fe1f7b 100644 --- a/xml.cpp +++ b/xml.cpp @@ -140,13 +140,6 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) xml_node<>* set_node(card_node->first_node("set")); int set(set_node ? atoi(set_node->value()) : card->m_set); xml_node<>* level_node(card_node->first_node("level")); -#if 0 - if (set > 0) // not AI only - { - nb_cards++; - sets_counts[set]++; - } -#endif if (id_node) { card->m_base_id = card->m_id = atoi(id_node->value()); } else if (card_id_node) { card->m_id = atoi(card_id_node->value()); } if (name_node) { card->m_name = name_node->value(); } @@ -350,7 +343,15 @@ void read_missions(Decks& decks, const Cards& all_cards, std::string filename) unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(mission_node->first_node("name")); std::string deck_name{name_node->value()}; - read_deck(decks, all_cards, mission_node, "effect", DeckType::mission, id, deck_name, true); + try + { + read_deck(decks, all_cards, mission_node, "effect", DeckType::mission, id, deck_name, true); + } + catch (const std::runtime_error& e) + { + std::cerr << "Warning: Failed to parse mission [" << deck_name << "] in file " << filename << ": [" << e.what() << "]. Skip the mission.\n"; + continue; + } } } //------------------------------------------------------------------------------ From 79b976a9790ae4dae37763320c450977e5d7b455 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 21 Aug 2014 08:55:45 +0800 Subject: [PATCH 230/406] Fix skill name Armor. --- sim.cpp | 12 ++++++------ tyrant.cpp | 2 +- tyrant.h | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sim.cpp b/sim.cpp index cbdcf612..ff8c3009 100644 --- a/sim.cpp +++ b/sim.cpp @@ -848,11 +848,11 @@ struct PerformAttack // prevent damage std::string reduced_desc; unsigned reduced_dmg(0); - unsigned armored_value = def_status->skill(); - if(armored_value > 0) + unsigned armor_value = def_status->skill(); + if(armor_value > 0) { - if(debug_print > 0) { reduced_desc += to_string(armored_value) + "(armored)"; } - reduced_dmg += armored_value; + if(debug_print > 0) { reduced_desc += to_string(armor_value) + "(armor)"; } + reduced_dmg += armor_value; } if(def_status->protected_value() > 0) { @@ -1109,13 +1109,13 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, c template inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) { - if(s.y == allfactions) + if(s.y == allfactions || fd->effect == progenitors) { return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); } else { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return((c->m_faction == s.y || s.y == progenitor || fd->effect == progenitors) && skill_predicate(fd, src_status, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return((c->m_faction == s.y || s.y == progenitor) && skill_predicate(fd, src_status, c, s));})); } } diff --git a/tyrant.cpp b/tyrant.cpp index c5896a8e..9657c8c6 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -20,7 +20,7 @@ std::string skill_names[Skill::num_skills] = "", // Defensive: "", - "Armored", "Corrosive", "Counter", "Evade", "Wall", + "Armor", "Corrosive", "Counter", "Evade", "Wall", "", // Combat-Modifier: "Flurry", "Pierce", diff --git a/tyrant.h b/tyrant.h index 8809448e..62e3d9c5 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.0.1" +#define TYRANT_OPTIMIZER_VERSION "2.0.2" #include #include @@ -36,7 +36,7 @@ enum Skill END_ACTIVATION_HELPFUL, // Defensive: BEGIN_DEFENSIVE, - armored, corrosive, counter, evade, wall, + armor, corrosive, counter, evade, wall, END_DEFENSIVE, // Combat-Modifier: flurry, pierce, From 5a9ecc6183294199e4c7687d8435d73e0fc07c8d Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 21 Aug 2014 14:27:19 +0800 Subject: [PATCH 231/406] Support -o=. --- Makefile | 1 - read.cpp | 60 +++++++++++++++++++++++++++++++-------------- tyrant_optimize.cpp | 8 ++++-- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index a955b230..a80241ea 100644 --- a/Makefile +++ b/Makefile @@ -16,4 +16,3 @@ $(MAIN): $(OBJS) clean: del /q $(MAIN).exe obj\*.o - diff --git a/read.cpp b/read.cpp index 135ac0b0..8e5efa6a 100644 --- a/read.cpp +++ b/read.cpp @@ -342,12 +342,51 @@ unsigned read_custom_decks(Decks& decks, Cards& all_cards, std::string filename) return(0); } +void add_owned_card(Cards& all_cards, std::map& owned_cards, std::string& card_spec) +{ + unsigned card_id{0}; + unsigned card_num{1}; + char num_sign{0}; + char mark{0}; + parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign, mark); + all_cards.by_id(card_id); // check that the id is valid + assert(mark == 0); + if(num_sign == 0) + { + owned_cards[card_id] = card_num; + } + else if(num_sign == '+') + { + owned_cards[card_id] += card_num; + } + else if(num_sign == '-') + { + owned_cards[card_id] = owned_cards[card_id] > card_num ? owned_cards[card_id] - card_num : 0; + } +} + void read_owned_cards(Cards& all_cards, std::map& owned_cards, std::string filename) { std::ifstream owned_file{filename}; if(!owned_file.good()) { - std::cerr << "Warning: Owned cards file '" << filename << "' does not exist.\n"; + // try parse the string as a cards instead of as a filename + try + { + std::string card_list(filename); + boost::tokenizer> card_tokens{card_list, boost::char_delimiters_separator{false, ",", ""}}; + auto token_iter = card_tokens.begin(); + for (; token_iter != card_tokens.end(); ++token_iter) + { + std::string card_spec(*token_iter); + add_owned_card(all_cards, owned_cards, card_spec); + } + } + catch (std::exception& e) + { + std::cerr << "Error: Failed to parse owned cards: '" << filename << "' is neither a file nor a valid set of cards (" << e.what() << ")" << std::endl; + exit(0); + } return; } unsigned num_line(0); @@ -362,24 +401,7 @@ void read_owned_cards(Cards& all_cards, std::map& owned_card } try { - unsigned card_id{0}; - unsigned card_num{1}; - char num_sign{0}; - char mark{0}; - parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign, mark); - assert(mark == 0); - if(num_sign == 0) - { - owned_cards[card_id] = card_num; - } - else if(num_sign == '+') - { - owned_cards[card_id] += card_num; - } - else if(num_sign == '-') - { - owned_cards[card_id] = owned_cards[card_id] > card_num ? owned_cards[card_id] - card_num : 0; - } + add_owned_card(all_cards, owned_cards, card_spec); } catch(std::exception& e) { diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 3b685001..433dbaae 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -998,7 +998,8 @@ void print_available_effects() void usage(int argc, char** argv) { - std::cout << "usage: " << argv[0] << " Your_Deck Enemy_Deck [Flags] [Operations]\n" + std::cout << "Tyrant Unleashed Optimizer (TUO) " << TYRANT_OPTIMIZER_VERSION << "\n" + "usage: " << argv[0] << " Your_Deck Enemy_Deck [Flags] [Operations]\n" "\n" "Your_Deck:\n" " the name/hash/cards of a custom deck.\n" @@ -1442,7 +1443,10 @@ int main(int argc, char** argv) case reorder: { your_deck->strategy = DeckStrategy::ordered; use_owned_cards = true; - min_deck_len = max_deck_len = your_deck->cards.size(); + if (min_deck_len == 1 && max_deck_len == 10) + { + min_deck_len = max_deck_len = your_deck->cards.size(); + } fund = 0; owned_cards.clear(); claim_cards({your_deck->commander}); From 77349fa92840d91175655e971b667663c0300eac Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 21 Aug 2014 14:28:56 +0800 Subject: [PATCH 232/406] Fix compile complaint. --- sim.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sim.cpp b/sim.cpp index ff8c3009..b4840dfd 100644 --- a/sim.cpp +++ b/sim.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include From 95b7c4487410fc4aa471f33c3c5f24cbe42926bc Mon Sep 17 00:00:00 2001 From: Konstantin Svintsov Date: Thu, 21 Aug 2014 13:35:14 +0700 Subject: [PATCH 233/406] - OS X compilation fixes --- .gitignore | 1 + Makefile.osx | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile.osx diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..66fa671a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +obj/** diff --git a/Makefile.osx b/Makefile.osx new file mode 100644 index 00000000..d552719c --- /dev/null +++ b/Makefile.osx @@ -0,0 +1,18 @@ +MAIN := tu_optimize +SRCS := $(wildcard *.cpp) +OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) +INCS := $(wildcard *.h) + +CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -I/usr/local/include +LDFLAGS := -lboost_system-mt -lboost_thread-mt -lboost_filesystem-mt -lboost_regex-mt -L/usr/local/lib + +all: $(MAIN) + +obj/%.o: %.cpp ${INCS} obj + $(CXX) $(CPPFLAGS) -o $@ -c $< + +$(MAIN): $(OBJS) + $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +clean: + rm -f $(MAIN) obj/*.o From 777176acd44e968c2e2deb7e9942005a02c2c86d Mon Sep 17 00:00:00 2001 From: Konstantin Svintsov Date: Thu, 21 Aug 2014 13:36:15 +0700 Subject: [PATCH 234/406] - OS X compilation fixes --- .gitignore | 1 + Makefile.osx | 2 +- cards.h | 3 ++- deck.cpp | 2 +- deck.h | 13 ++++++++++--- read.cpp | 4 ++-- sim.h | 1 + tyrant_optimize.cpp | 2 +- 8 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 66fa671a..01604330 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ obj/** +tu_optimize diff --git a/Makefile.osx b/Makefile.osx index d552719c..fba69b3e 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -3,7 +3,7 @@ SRCS := $(wildcard *.cpp) OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) INCS := $(wildcard *.h) -CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -I/usr/local/include +CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -I/usr/local/include -Wno-deprecated-register LDFLAGS := -lboost_system-mt -lboost_thread-mt -lboost_filesystem-mt -lboost_regex-mt -L/usr/local/lib all: $(MAIN) diff --git a/cards.h b/cards.h index 3a02d766..6584afbc 100644 --- a/cards.h +++ b/cards.h @@ -7,8 +7,9 @@ class Card; -struct Cards +class Cards { +public: ~Cards(); std::vector cards; diff --git a/deck.cpp b/deck.cpp index cc361a9f..1ac26530 100644 --- a/deck.cpp +++ b/deck.cpp @@ -281,7 +281,7 @@ const std::pair, std::map> string_to_ids(con namespace range = boost::range; -void Deck::set(const std::vector& ids, const std::map marks) +void Deck::set(const std::vector& ids, const std::map &marks) { commander = nullptr; strategy = DeckStrategy::random; diff --git a/deck.h b/deck.h index 3eb75578..c3dd4e15 100644 --- a/deck.h +++ b/deck.h @@ -38,8 +38,9 @@ extern DeckEncoder encode_deck; //------------------------------------------------------------------------------ // No support for ordered raid decks -struct Deck +class Deck { +public: const Cards& all_cards; DeckType::DeckType decktype; unsigned id; @@ -99,7 +100,12 @@ struct Deck mission_req = mission_req_; } - void set(const std::vector& ids, const std::map marks = {}); + void set(const std::vector& ids, const std::map &marks); + void set(const std::vector& ids) + { + std::map empty; + set(ids, empty); + } void set(const std::string& deck_string_); void resolve(); void set_given_hand(const std::string& deck_string_); @@ -130,8 +136,9 @@ struct Deck }; typedef std::map DeckList; -struct Decks +class Decks { +public: std::list decks; std::map, Deck*> by_type_id; std::map by_name; diff --git a/read.cpp b/read.cpp index 9df4de54..ac6c40ab 100644 --- a/read.cpp +++ b/read.cpp @@ -88,7 +88,7 @@ DeckList expand_deck_to_list(std::string deck_name, const Decks& decks) if (expanding_decks.count(deck_name)) { std::cerr << "Warning: circular referred deck: " << deck_name << std::endl; - return {}; + return DeckList(); } auto deck_string = deck_name; const auto & deck = decks.by_name.find(deck_name); @@ -146,7 +146,7 @@ DeckList parse_deck_list(std::string list_string, const Decks& decks) boost::tokenizer> deck_tokens{list_token, boost::char_delimiters_separator{false, ":", ""}}; auto deck_token = deck_tokens.begin(); auto deck_name = *deck_token; - double factor = 1.0d; + double factor = 1.0; ++ deck_token; if(deck_token != deck_tokens.end()) { diff --git a/sim.h b/sim.h index 2a84afe5..3d79a1e9 100644 --- a/sim.h +++ b/sim.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "tyrant.h" diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 6f363393..817cda5e 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -506,7 +506,7 @@ void thread_evaluate(boost::barrier& main_barrier, { score_accum_d += thread_score_local[i] * sim.factors[i]; } - score_accum_d /= std::accumulate(sim.factors.begin(), sim.factors.end(), .0d); + score_accum_d /= std::accumulate(sim.factors.begin(), sim.factors.end(), .0); score_accum = score_accum_d; } else From 48d2814225c1c86abe44620a110cbe1a38da7b69 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 21 Aug 2014 16:11:55 +0800 Subject: [PATCH 235/406] Fix skill name Armor/Armored (both acceptable). --- xml.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/xml.cpp b/xml.cpp index 24fe1f7b..fe845e5e 100644 --- a/xml.cpp +++ b/xml.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ CardType::CardType map_to_type(unsigned i) Skill skill_name_to_id(const std::string & name) { static std::map skill_map; + static std::set unknown_skills; if(skill_map.empty()) { for(unsigned i(0); i < Skill::num_skills; ++i) @@ -48,9 +50,22 @@ Skill skill_name_to_id(const std::string & name) std::string skill_id = boost::to_lower_copy(skill_names[i]); skill_map[skill_id] = i; } + skill_map["armored"] = skill_map["armor"]; // Special case for Armor: id and name differ } auto x = skill_map.find(boost::to_lower_copy(name)); - return x == skill_map.end() ? no_skill : (Skill)x->second; + if (x == skill_map.end()) + { + if (unknown_skills.count(name) == 0) + { // Warn only once for each unknown skill + unknown_skills.insert(name); + std::cerr << "Warning: Ignore unknown skill [" << name << "]\n"; + } + return no_skill; + } + else + { + return (Skill)x->second; + } } Faction skill_faction(xml_node<>* skill) From 0a2901f71fdb67d100615ff8e9677e805d845520 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 21 Aug 2014 16:12:19 +0800 Subject: [PATCH 236/406] Fix faction Progenitor. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index b4840dfd..290fd9b8 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1116,7 +1116,7 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector } else { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return((c->m_faction == s.y || s.y == progenitor) && skill_predicate(fd, src_status, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return((c->m_faction == s.y || c->m_faction == progenitor) && skill_predicate(fd, src_status, c, s));})); } } From 75f1f2cd3cb3b32aa48c56bf7e551757fd9718ae Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 22 Aug 2014 11:17:45 +0800 Subject: [PATCH 237/406] Support raid levels. Add raid Apocalypse. --- data/raids.xml | 29 +++++++++++++++++++++++ deck.cpp | 18 +++++++------- deck.h | 5 +++- sim.cpp | 8 ++----- tyrant.h | 3 ++- tyrant_optimize.cpp | 26 ++++++++++++--------- xml.cpp | 57 ++++++++++++++++++++++++++------------------- xml.h | 2 +- 8 files changed, 95 insertions(+), 53 deletions(-) create mode 100644 data/raids.xml diff --git a/data/raids.xml b/data/raids.xml new file mode 100644 index 00000000..f91bb160 --- /dev/null +++ b/data/raids.xml @@ -0,0 +1,29 @@ + + + + + + 1 + Apocalypse + 25000 + 1373 + 26 + + + 7695 + 7695 + 7705 + 7705 + 7715 + 7715 + 7725 +` 7725 + 7735 + 7735 + 7745 + 7745 + + + + + diff --git a/deck.cpp b/deck.cpp index d9bedcb9..09c034bb 100644 --- a/deck.cpp +++ b/deck.cpp @@ -526,9 +526,9 @@ const Card* Deck::upgrade_card(const Card* card, std::mt19937& re) unsigned ups = card->m_top_level_card->m_level - card->m_level; if (upgrade_chance > 0 && ups > 0) { - for (std::mt19937::result_type rnd = re(); ups > 0; -- ups, rnd /= 9) + for (std::mt19937::result_type rnd = re(); ups > 0; -- ups, rnd = ups % 5 == 0 ? re() : rnd / upgrade_max_chance) { - if (rnd % 9 < upgrade_chance) + if (rnd % upgrade_max_chance < upgrade_chance) { card = card->upgraded(); } @@ -553,13 +553,6 @@ void Deck::shuffle(std::mt19937& re) { shuffled_cards.clear(); boost::insert(shuffled_cards, shuffled_cards.end(), cards); - if (upgrade_chance > 0) - { - for (auto && card: shuffled_cards) - { - card = upgrade_card(card, re); - } - } if(!raid_cards.empty()) { if(strategy != DeckStrategy::random) @@ -573,6 +566,13 @@ void Deck::shuffle(std::mt19937& re) shuffled_cards.insert(shuffled_cards.end(), card_pool.second.begin(), card_pool.second.begin() + card_pool.first); } } + if (upgrade_chance > 0) + { + for (auto && card: shuffled_cards) + { + card = upgrade_card(card, re); + } + } if(strategy == DeckStrategy::ordered) { unsigned i = 0; diff --git a/deck.h b/deck.h index c3dd4e15..427cc9a5 100644 --- a/deck.h +++ b/deck.h @@ -46,7 +46,8 @@ class Deck unsigned id; std::string name; Effect effect; // for quests - unsigned upgrade_chance; // n/9 chance to upgrade; = level - 1 for level 1 to 9 and 0 for level 10 + unsigned upgrade_chance; // probability chance/max_change to upgrade; = level - 1 for level 1 to (max_level - 1) and 0 for max_level (directly use full upgraded cards) + unsigned upgrade_max_chance; DeckStrategy::DeckStrategy strategy; const Card* commander; @@ -71,6 +72,7 @@ class Deck std::string name_ = "", Effect effect_ = Effect::none, unsigned upgrade_chance_ = 0, + unsigned upgrade_max_chance_ = 1, DeckStrategy::DeckStrategy strategy_ = DeckStrategy::random) : all_cards(all_cards_), decktype(decktype_), @@ -78,6 +80,7 @@ class Deck name(name_), effect(Effect::none), upgrade_chance(upgrade_chance_), + upgrade_max_chance(upgrade_max_chance_), strategy(strategy_), commander(nullptr), mission_req(0) diff --git a/sim.cpp b/sim.cpp index 290fd9b8..7cf132e7 100644 --- a/sim.cpp +++ b/sim.cpp @@ -313,8 +313,6 @@ void PlayCard::setStorage() storage = &fd->tap->structures; } //------------------------------------------------------------------------------ -inline bool is_attacking_or_has_attacked(CardStatus* c) { return(c->m_step >= CardStep::attacking); } -inline bool is_attacking(CardStatus* c) { return(c->m_step == CardStep::attacking); } inline bool has_attacked(CardStatus* c) { return(c->m_step == CardStep::attacked); } inline bool is_jammed(CardStatus* c) { return(c->m_jammed); } inline bool is_active(CardStatus* c) { return(c->m_delay == 0); } @@ -427,13 +425,11 @@ Results play(Field* fd) for(unsigned action_index(0); action_index < num_actions; ++action_index) { // Evaluate skills - current_status->m_step = CardStep::none; evaluate_skills(fd, current_status, current_status->m_card->m_skills); // no commander-killing skill yet // if(__builtin_expect(fd->end, false)) { break; } // Attack if(can_attack(current_status)) { - current_status->m_step = CardStep::attacking; attacked = attack_phase(fd) || attacked; if(__builtin_expect(fd->end, false)) { break; } } @@ -1003,7 +999,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, co template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - if (dst->m_overloaded || ! (is_active(dst) && can_act(dst))) + if (dst->m_overloaded || has_attacked(dst) || !(is_active(dst) && can_act(dst))) { return false; } @@ -1033,7 +1029,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* ds template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - return can_attack(dst) && is_active(dst) && !is_attacking_or_has_attacked(dst); + return can_attack(dst) && is_active(dst) && !has_attacked(dst); } template<> diff --git a/tyrant.h b/tyrant.h index 62e3d9c5..a19b7003 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.0.2" +#define TYRANT_OPTIMIZER_VERSION "2.1.0" #include #include @@ -91,6 +91,7 @@ enum gamemode_t enum class OptimizationMode { + notset, winrate, raid, defense diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 790f8c63..3a4d1d28 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -42,7 +42,7 @@ namespace { gamemode_t gamemode{fight}; - OptimizationMode optimization_mode{OptimizationMode::winrate}; + OptimizationMode optimization_mode{OptimizationMode::notset}; std::map owned_cards; bool use_owned_cards{true}; unsigned min_deck_len{1}; @@ -514,7 +514,7 @@ void thread_evaluate(boost::barrier& main_barrier, score_accum = thread_score_local[0]; } bool compare_stop(false); - long double best_possible = (optimization_mode == OptimizationMode::raid ? 250 : 100); + long double best_possible = 100; // Get a loose (better than no) upper bound. TODO: Improve it. compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum / best_possible, 0.01) * best_possible < thread_prev_score); if(compare_stop) @@ -1017,7 +1017,7 @@ void usage(int argc, char** argv) " -t : set the number of threads, default is 4.\n" " win: simulate/optimize for win rate. default for non-raids.\n" " defense: simulate/optimize for win rate + stall rate. can be used for defending deck or win rate oriented raid simulations.\n" -// " raid: simulate/optimize for average raid damage (ARD). default for raids.\n" + " raid: simulate/optimize for average raid damage (ARD). default for raids.\n" "Flags for climb:\n" " -c: don't try to optimize the commander.\n" " -L : restrict deck size between and .\n" @@ -1103,8 +1103,6 @@ int main(int argc, char** argv) else if (strcmp(argv[argIndex], "raid") == 0) { optimization_mode = OptimizationMode::raid; - turn_limit = 30; - target_score = 250; } // Mode Package else if (strcmp(argv[argIndex], "pvp") == 0) @@ -1256,7 +1254,7 @@ int main(int argc, char** argv) else if(strcmp(argv[argIndex], "debuguntil") == 0) { // output the debug info for the first battle that min_score <= score <= max_score. - // E.g., 0 0: lose; 100 100: win (non-raid); 150 250: at least 150 damage (raid). + // E.g., 0 0: lose; 100 100: win (non-raid); 20 100: at least 20 damage (raid). opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), debuguntil)); argIndex += 2; } @@ -1271,7 +1269,7 @@ int main(int argc, char** argv) Decks decks; load_cards_xml(all_cards, "data/cards.xml"); read_card_abbrs(all_cards, "data/cardabbrs.txt"); - load_decks_xml(decks, all_cards, "data/missions.xml"); + load_decks_xml(decks, all_cards, "data/missions.xml", "data/raids.xml"); load_custom_decks(decks, all_cards, "data/customdecks.txt"); load_recipes_xml(all_cards, "data/fusion_recipes_cj2.xml"); fill_skill_table(); @@ -1376,11 +1374,17 @@ int main(int argc, char** argv) usage(argc, argv); return 0; } - if(enemy_deck->decktype == DeckType::raid) + if (optimization_mode == OptimizationMode::notset) { - optimization_mode = OptimizationMode::raid; - turn_limit = 30; - target_score = 250; + if (enemy_deck->decktype == DeckType::raid) + { + // optimization_mode = OptimizationMode::raid; // TODO how ARD is calculated? + optimization_mode = OptimizationMode::winrate; + } + else + { + optimization_mode = OptimizationMode::winrate; + } } enemy_deck->strategy = opt_enemy_strategy; enemy_deck->set_forts(opt_enemy_forts); diff --git a/xml.cpp b/xml.cpp index fe845e5e..eb334d6e 100644 --- a/xml.cpp +++ b/xml.cpp @@ -97,7 +97,7 @@ Skill skill_target_skill(xml_node<>* skill) } //------------------------------------------------------------------------------ -void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_filename) +void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_filename, const char * raid_filename) { try { @@ -105,7 +105,15 @@ void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_f } catch (const rapidxml::parse_error& e) { - std::cout << "\nFailed to parse file data/missions.xml\n"; + std::cerr << "\nFailed to parse file [" << mission_filename << "]. Skip it.\n"; + } + try + { + read_raids(decks, all_cards, raid_filename); + } + catch(const rapidxml::parse_error& e) + { + std::cerr << "\nFailed to parse file [" << raid_filename << "]. Skip it.\n"; } } @@ -254,7 +262,7 @@ void load_cards_xml(Cards & all_cards, const char * filename) all_cards.organize(); } //------------------------------------------------------------------------------ -Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const char* effect_node_name, DeckType::DeckType decktype, unsigned id, std::string base_deck_name, bool has_levels=false) +Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const char* effect_node_name, DeckType::DeckType decktype, unsigned id, std::string base_deck_name) { xml_node<>* commander_node(node->first_node("commander")); unsigned card_id = atoi(commander_node->value()); @@ -263,6 +271,8 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch std::vector>> some_cards; std::vector reward_cards; xml_node<>* deck_node(node->first_node("deck")); + xml_node<>* levels_node(node->first_node("levels")); + unsigned max_level = levels_node ? atoi(levels_node->value()) : 10; xml_node<>* always_node{deck_node->first_node("always_include")}; for(xml_node<>* card_node = (always_node ? always_node : deck_node)->first_node("card"); card_node; @@ -302,33 +312,32 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch unsigned mission_req(mission_req_node ? atoi(mission_req_node->value()) : 0); xml_node<>* effect_id_node(node->first_node(effect_node_name)); Effect effect = effect_id_node ? static_cast(atoi(effect_id_node->value())) : Effect::none; - if (has_levels) + + for (unsigned level = 1; level < max_level; ++ level) { - for (unsigned level = 1; level <= 9; ++ level) - { - std::string deck_name = base_deck_name + "-" + to_string(level); - decks.decks.push_back(Deck{all_cards, decktype, id, deck_name, effect, level - 1}); - Deck* deck = &decks.decks.back(); - deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); - std::string alt_name = decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(level); - decks.by_name[deck_name] = deck; - decks.by_name[alt_name] = deck; - } + std::string deck_name = base_deck_name + "-" + to_string(level); + decks.decks.push_back(Deck{all_cards, decktype, id, deck_name, effect, level - 1, max_level - 1}); + Deck* deck = &decks.decks.back(); + deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); + std::string alt_name = decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(level); + decks.by_name[deck_name] = deck; + decks.by_name[alt_name] = deck; } + decks.decks.push_back(Deck{all_cards, decktype, id, base_deck_name, effect}); Deck* deck = &decks.decks.back(); deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); - if (has_levels) - { // upgrade cards in deck - deck->commander = deck->commander->m_top_level_card; - for (auto && card: deck->cards) + + // upgrade cards for full-level missions/raids + deck->commander = deck->commander->m_top_level_card; + for (auto && card: deck->cards) + { card = card->m_top_level_card; } + for (auto && pool: deck->raid_cards) + { + for (auto && card: pool.second) { card = card->m_top_level_card; } - for (auto && pool: deck->raid_cards) - { - for (auto && card: pool.second) - { card = card->m_top_level_card; } - } } + std::string alt_name = decktype_names[decktype] + " #" + to_string(id); decks.by_name[base_deck_name] = deck; decks.by_name[alt_name] = deck; @@ -360,7 +369,7 @@ void read_missions(Decks& decks, const Cards& all_cards, std::string filename) std::string deck_name{name_node->value()}; try { - read_deck(decks, all_cards, mission_node, "effect", DeckType::mission, id, deck_name, true); + read_deck(decks, all_cards, mission_node, "effect", DeckType::mission, id, deck_name); } catch (const std::runtime_error& e) { diff --git a/xml.h b/xml.h index 0b4f76b5..7f61a630 100644 --- a/xml.h +++ b/xml.h @@ -10,7 +10,7 @@ class Achievement; Skill skill_name_to_id(const std::string & name); void load_cards_xml(Cards & all_cards, const char * filename); -void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_filename); +void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_filename, const char * raid_filename); void load_recipes_xml(Cards& all_cards, const char * filename); void read_missions(Decks& decks, const Cards& all_cards, std::string filename); void read_raids(Decks& decks, const Cards& all_cards, std::string filename); From 7cbd1acfb970a706a0f1dac839f6a3738173315b Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 22 Aug 2014 14:24:25 +0800 Subject: [PATCH 238/406] Add mode Raid: Optimize against Average Raid Damage (ARD). --- sim.cpp | 32 ++++++++++++-------------------- sim.h | 7 +++---- tyrant.h | 2 +- tyrant_optimize.cpp | 28 +++++++--------------------- 4 files changed, 23 insertions(+), 46 deletions(-) diff --git a/sim.cpp b/sim.cpp index 7cf132e7..06ba341a 100644 --- a/sim.cpp +++ b/sim.cpp @@ -226,12 +226,7 @@ void resolve_skill(Field* fd) auto& status(std::get<0>(skill_instance)); const auto& skill(std::get<1>(skill_instance)); fd->skill_queue.pop_front(); - if(!status) - { - // trigger_regen - skill_table[skill.id](fd, status, skill); - } - else if(!status->m_jammed) + if (!status->m_jammed) { unsigned enhanced_value = status->enhanced(skill.id); auto& enhanced_s = enhanced_value > 0 ? apply_enhance(skill, enhanced_value) : skill; @@ -334,6 +329,7 @@ Results play(Field* fd) fd->tap = fd->players[fd->tapi]; fd->tip = fd->players[fd->tipi]; fd->end = false; + fd->n_player_kills = 0; // Play fortresses for (unsigned _ = 0; _ < 2; ++ _) @@ -477,25 +473,15 @@ Results play(Field* fd) std::swap(fd->tap, fd->tip); ++fd->turn; } + unsigned raid_damage = 15 + fd->n_player_kills - (10 * fd->players[1]->commander.m_hp / fd->players[1]->commander.m_card->m_health); // you lose if(fd->players[0]->commander.m_hp == 0) { _DEBUG_MSG(1, "You lose.\n"); - return {0, 0, 1, 0, 0}; - } - // you win in raid - if(fd->optimization_mode == OptimizationMode::raid) - { - if(fd->players[1]->commander.m_hp == 0) - { - _DEBUG_MSG(1, "You win (boss killed).\n"); - return {1, 0, 0, fd->players[1]->commander.m_card->m_health + 50, 0}; - } + if (fd->optimization_mode == OptimizationMode::raid) + { return {0, 0, 1, raid_damage, 0}; } else - { - _DEBUG_MSG(1, "You win (survival).\n"); - return {0, 1, 0, fd->players[1]->commander.m_card->m_health - fd->players[1]->commander.m_hp, 0}; - } + { return {0, 0, 1, 0, 0}; } } // you win if(fd->players[1]->commander.m_hp == 0) @@ -508,6 +494,8 @@ Results play(Field* fd) _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); if (fd->optimization_mode == OptimizationMode::defense) { return {1, 1, 0, 100, 0}; } + else if (fd->optimization_mode == OptimizationMode::raid) + { return {0, 1, 0, raid_damage, 0}; } else { return {0, 1, 0, 0, 0}; } } @@ -542,6 +530,10 @@ void remove_hp(Field* fd, CardStatus& status, unsigned dmg) if(status.m_hp == 0) { _DEBUG_MSG(1, "%s dies\n", status_description(&status).c_str()); + if (status.m_player == 1) + { + fd->n_player_kills += 1; + } } } inline bool is_it_dead(CardStatus& c) diff --git a/sim.h b/sim.h index 3d79a1e9..7076c2ea 100644 --- a/sim.h +++ b/sim.h @@ -203,8 +203,7 @@ class Field // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque> skill_queue; - std::vector killed_with_on_death; - std::vector killed_with_regen; + unsigned n_player_kills; enum phase { playcard_phase, @@ -219,7 +218,6 @@ class Field // Meaningless in playcard_phase, // otherwise is the index of the current card in players->structures or players->assaults unsigned current_ci; -// unsigned points_since_last_decision; Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, OptimizationMode optimization_mode_, Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_) : @@ -232,7 +230,8 @@ class Field optimization_mode(optimization_mode_), effect(effect_), bg_enhanced_skill(bg_enhanced_skill_), - bg_enhanced_value(bg_enhanced_value_) + bg_enhanced_value(bg_enhanced_value_), + n_player_kills(0) { } diff --git a/tyrant.h b/tyrant.h index a19b7003..e639c2d0 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.1.0" +#define TYRANT_OPTIMIZER_VERSION "2.1.1" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 3a4d1d28..cdd6d4cb 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -553,32 +553,19 @@ void print_results(const std::pair> , unsigned>& r { auto final = compute_score(results, factors); - if(optimization_mode == OptimizationMode::raid) - { - std::cout << "win%: " << (final.wins + final.draws) * 100.0 << " ("; - for(const auto & val: results.first) - { - std::cout << val.wins + val.draws << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; - } - - std::cout << (optimization_mode == OptimizationMode::raid ? "slay%: " : "win%: ") << final.wins * 100.0 << " ("; + std::cout << "win%: " << final.wins * 100.0 << " ("; for(const auto & val: results.first) { std::cout << val.wins << " "; } std::cout << "/ " << results.second << ")" << std::endl; - if(optimization_mode != OptimizationMode::raid) + std::cout << "stall%: " << final.draws * 100.0 << " ("; + for(const auto & val: results.first) { - std::cout << "stall%: " << final.draws * 100.0 << " ("; - for(const auto & val: results.first) - { - std::cout << val.draws << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; + std::cout << val.draws << " "; } + std::cout << "/ " << results.second << ")" << std::endl; std::cout << "loss%: " << final.losses * 100.0 << " ("; for(const auto & val: results.first) @@ -615,7 +602,7 @@ void print_deck_inline(const unsigned deck_cost, const Results scor switch(optimization_mode) { case OptimizationMode::raid: - std::cout << "(" << (score.wins + score.draws) * 100 << "% win, " << score.wins * 100.0 << "% slay"; + std::cout << "(" << score.wins * 100 << "% win, " << score.draws * 100 << "% stall"; if (show_stdev) { std::cout << ", " << sqrt(score.sq_points - score.points * score.points) << " stdev"; @@ -1378,8 +1365,7 @@ int main(int argc, char** argv) { if (enemy_deck->decktype == DeckType::raid) { - // optimization_mode = OptimizationMode::raid; // TODO how ARD is calculated? - optimization_mode = OptimizationMode::winrate; + optimization_mode = OptimizationMode::raid; } else { From 906667242aa377a1112f09453affee70e9056ed9 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 23 Aug 2014 00:44:12 +0800 Subject: [PATCH 239/406] Update raids.xml. --- data/raids.xml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index f91bb160..7e5f4811 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -7,20 +7,22 @@ Apocalypse 25000 1373 - 26 + 26 - + 7695 - 7695 - 7705 7705 7715 - 7715 7725 -` 7725 7735 - 7735 7745 + + + 7695 + 7705 + 7715 + 7725 + 7735 7745 From 3a10cadd9992aecef75240dfab535c3352f3f4fd Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 23 Aug 2014 15:46:02 +0800 Subject: [PATCH 240/406] Fine tune to rnd. --- deck.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deck.cpp b/deck.cpp index 09c034bb..54d59099 100644 --- a/deck.cpp +++ b/deck.cpp @@ -526,8 +526,9 @@ const Card* Deck::upgrade_card(const Card* card, std::mt19937& re) unsigned ups = card->m_top_level_card->m_level - card->m_level; if (upgrade_chance > 0 && ups > 0) { - for (std::mt19937::result_type rnd = re(); ups > 0; -- ups, rnd = ups % 5 == 0 ? re() : rnd / upgrade_max_chance) + for (; ups > 0; -- ups) { + std::mt19937::result_type rnd = re(); if (rnd % upgrade_max_chance < upgrade_chance) { card = card->upgraded(); From 4c5e79af86682ee91cc7808ca56a03e38733471c Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 23 Aug 2014 23:25:27 +0800 Subject: [PATCH 241/406] Show error message instead of exception on wrong fortress flag. --- tyrant_optimize.cpp | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index cdd6d4cb..97e35bf1 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1210,12 +1210,12 @@ int main(int argc, char** argv) } else if (strcmp(argv[argIndex], "yf") == 0 || strcmp(argv[argIndex], "yfort") == 0) // set forts { - opt_forts = std::string(argv[argIndex + 1]) + ","; + opt_forts = std::string(argv[argIndex + 1]); argIndex += 1; } else if (strcmp(argv[argIndex], "ef") == 0 || strcmp(argv[argIndex], "efort") == 0) // set enemies' forts { - opt_enemy_forts = std::string(argv[argIndex + 1]) + ","; + opt_enemy_forts = std::string(argv[argIndex + 1]); argIndex += 1; } else if(strcmp(argv[argIndex], "sim") == 0) @@ -1336,7 +1336,18 @@ int main(int argc, char** argv) } your_deck->strategy = opt_your_strategy; - your_deck->set_forts(opt_forts); + if (!opt_forts.empty()) + { + try + { + your_deck->set_forts(opt_forts + ","); + } + catch(const std::runtime_error& e) + { + std::cerr << "Error: yf " << opt_forts << ": " << e.what() << std::endl; + return 0; + } + } your_deck->set_given_hand(opt_hand); if (opt_keep_commander) { @@ -1373,7 +1384,18 @@ int main(int argc, char** argv) } } enemy_deck->strategy = opt_enemy_strategy; - enemy_deck->set_forts(opt_enemy_forts); + if (!opt_enemy_forts.empty()) + { + try + { + enemy_deck->set_forts(opt_enemy_forts + ","); + } + catch(const std::runtime_error& e) + { + std::cerr << "Error: yf " << opt_forts << ": " << e.what() << std::endl; + return 0; + } + } enemy_deck->set_given_hand(opt_enemy_hand); enemy_decks.push_back(enemy_deck); enemy_decks_factors.push_back(deck_parsed.second); From 29b8330c2eecac8ee2789f23ad498d382387b617 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 24 Aug 2014 07:43:24 +0800 Subject: [PATCH 242/406] Show more debug info for mission/raid levels. --- deck.cpp | 49 +++++++++++++++++++++++++++++++++---------------- deck.h | 1 + sim.cpp | 4 ++-- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/deck.cpp b/deck.cpp index 54d59099..4df956cd 100644 --- a/deck.cpp +++ b/deck.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -424,11 +425,7 @@ std::string Deck::long_description() const } if(commander) { - ios << card_description(all_cards, commander) << "\n"; - if (upgrade_chance > 0 && commander != commander->m_top_level_card) - { - ios << "->" << card_description(all_cards, commander->m_top_level_card) << "\n"; - } + show_upgrades(ios, commander, ""); } else { @@ -436,29 +433,21 @@ std::string Deck::long_description() const } for(const Card* card: cards) { - ios << " " << card_description(all_cards, card) << "\n"; - if (upgrade_chance > 0 && card != card->m_top_level_card) - { - ios << " ->" << card_description(all_cards, card->m_top_level_card) << "\n"; - } + show_upgrades(ios, card, " "); } for(auto& pool: raid_cards) { ios << pool.first << " of:\n"; for(auto& card: pool.second) { - ios << " " << card_description(all_cards, card) << "\n"; - if (upgrade_chance > 0 && card != card->m_top_level_card) - { - ios << " ->" << card_description(all_cards, card->m_top_level_card) << "\n"; - } + show_upgrades(ios, card, " "); } } for (const Card * fort: fort_cards) { ios << card_description(all_cards, fort) << "\n"; } - if (! reward_cards.empty()) + if (debug_print >= 2 && !reward_cards.empty()) { ios << "Reward Cards: "; for (const auto & card : reward_cards) @@ -470,6 +459,34 @@ std::string Deck::long_description() const return ios.str(); } +void Deck::show_upgrades(std::stringstream &ios, const Card* card, const char * leading_chars) const +{ + ios << leading_chars << card_description(all_cards, card) << "\n"; + if (upgrade_chance == 0 || card == card->m_top_level_card) + { + return; + } + if (debug_print < 2 && decktype != DeckType::raid) + { + ios << leading_chars << "-> " << card_description(all_cards, card->m_top_level_card) << "\n"; + return; + } + // nCm * p^m / q^(n-m) + double p = 1.0 * upgrade_chance / upgrade_max_chance; + double q = 1.0 - p; + unsigned n = card->m_top_level_card->m_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 != card->m_top_level_card) + { + 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)); diff --git a/deck.h b/deck.h index 427cc9a5..eef9d028 100644 --- a/deck.h +++ b/deck.h @@ -131,6 +131,7 @@ class Deck std::string short_description() const; std::string medium_description() const; std::string long_description() const; + void show_upgrades(std::stringstream &ios, const Card* card, const char * leading_chars) const; const Card* next(); const Card* upgrade_card(const Card* card, std::mt19937& re); const Card* get_commander(std::mt19937& re); diff --git a/sim.cpp b/sim.cpp index 06ba341a..a2482e3c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -110,7 +110,7 @@ std::string skill_description(const Cards& cards, const SkillSpec& s) (s.all ? " all" : s.n == 0 ? "" : std::string(" ") + to_string(s.n)) + (s.y == allfactions ? "" : std::string(" ") + faction_names[s.y]) + (s.s == no_skill ? "" : std::string(" ") + skill_names[s.s]) + - (s.x == 0 ? "" : std::string(" ") + to_string(s.x)) + + (s.x == 0 || s.x == s.n ? "" : std::string(" ") + to_string(s.x)) + (s.c == 0 ? "" : std::string(" every ") + to_string(s.c)); } std::string skill_short_description(const SkillSpec& s) @@ -1233,7 +1233,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil select_targets(fd, src_status, s); for (CardStatus * dst: fd->selection_array) { - if(dst->m_inhibited > 0 && ! src_status->m_overloaded) + if(dst->m_inhibited > 0 && !src_status->m_overloaded) { _DEBUG_MSG(1, "%s %s on %s but it is inhibited\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst).c_str()); -- dst->m_inhibited; From bfac729d00988eee0b64b46574c8a482da097994 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 25 Aug 2014 13:53:14 +0800 Subject: [PATCH 243/406] Fix false alert of BGE. --- tyrant_optimize.cpp | 17 +++++++++++++---- xml.cpp | 6 +++--- xml.h | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 97e35bf1..0b602df8 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "card.h" #include "cards.h" #include "deck.h" @@ -976,7 +977,7 @@ enum Operation { //------------------------------------------------------------------------------ void print_available_effects() { - std::cout << "Available effects:" << std::endl; + std::cout << "Available effects besides \" X\":" << std::endl; for(int i(1); i < Effect::num_effects; ++ i) { std::cout << i << " \"" << effect_names[i] << "\"" << std::endl; @@ -1285,17 +1286,25 @@ int main(int argc, char** argv) } std::vector tokens; boost::split(tokens, opt_effect, boost::is_any_of(" -")); - opt_bg_enhanced_skill = skill_name_to_id(tokens[0]); + opt_bg_enhanced_skill = skill_name_to_id(tokens[0], false); if (tokens.size() >= 2 && opt_bg_enhanced_skill != no_skill) { - opt_bg_enhanced_value = atoi(tokens[1].c_str()); + try + { + opt_bg_enhanced_value = boost::lexical_cast(tokens[1]); + } + catch (const boost::bad_lexical_cast & e) + { + std::cerr << "Error: Expect a number in effect \"" << opt_effect << "\".\n"; + return 0; + } } else { const auto & x = effect_map.find(boost::to_lower_copy(opt_effect)); if(x == effect_map.end()) { - std::cout << "The effect '" << opt_effect << "' was not found. "; + std::cout << "Error: The effect \"" << opt_effect << "\" was not found.\n"; print_available_effects(); return 0; } diff --git a/xml.cpp b/xml.cpp index eb334d6e..e2328a90 100644 --- a/xml.cpp +++ b/xml.cpp @@ -39,7 +39,7 @@ CardType::CardType map_to_type(unsigned i) CardType::num_cardtypes); } -Skill skill_name_to_id(const std::string & name) +Skill skill_name_to_id(const std::string & name, bool do_warn) { static std::map skill_map; static std::set unknown_skills; @@ -55,10 +55,10 @@ Skill skill_name_to_id(const std::string & name) auto x = skill_map.find(boost::to_lower_copy(name)); if (x == skill_map.end()) { - if (unknown_skills.count(name) == 0) + if (do_warn and unknown_skills.count(name) == 0) { // Warn only once for each unknown skill unknown_skills.insert(name); - std::cerr << "Warning: Ignore unknown skill [" << name << "]\n"; + std::cerr << "Warning: Ignore unknown skill [" << name << "] in data/cards.xml\n"; } return no_skill; } diff --git a/xml.h b/xml.h index 7f61a630..cef2d809 100644 --- a/xml.h +++ b/xml.h @@ -8,7 +8,7 @@ class Cards; class Decks; class Achievement; -Skill skill_name_to_id(const std::string & name); +Skill skill_name_to_id(const std::string & name, bool do_warn=true); void load_cards_xml(Cards & all_cards, const char * filename); void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_filename, const char * raid_filename); void load_recipes_xml(Cards& all_cards, const char * filename); From 63c958ba99ea31302038df2a57699273f10612c1 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 25 Aug 2014 13:55:59 +0800 Subject: [PATCH 244/406] Version 2.1.2. --- tyrant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tyrant.h b/tyrant.h index e639c2d0..217c969f 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.1.1" +#define TYRANT_OPTIMIZER_VERSION "2.1.2" #include #include From fe68bd5e68f4f98089c0c3d03a2d35aef6e9b7d9 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 27 Aug 2014 22:59:36 +0800 Subject: [PATCH 245/406] Fix bug: wrong results when against your own deck with fortresses. --- tyrant_optimize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 0b602df8..e3cff545 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1322,7 +1322,7 @@ int main(int argc, char** argv) try { - your_deck = find_deck(decks, all_cards, your_deck_name); + your_deck = find_deck(decks, all_cards, your_deck_name)->clone(); } catch(const std::runtime_error& e) { From 79d51b8ef57c0b539b6f2a0e05be3cb57eb0a072 Mon Sep 17 00:00:00 2001 From: Konstantin Svintsov Date: Fri, 29 Aug 2014 17:59:58 +0700 Subject: [PATCH 246/406] - fix coredump in case of recipes for unknown cards (for example, fusion_recipes_cj2.xml is newer than cards.xml) --- xml.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/xml.cpp b/xml.cpp index e2328a90..6978ae91 100644 --- a/xml.cpp +++ b/xml.cpp @@ -425,6 +425,11 @@ void load_recipes_xml(Cards& all_cards, const char * filename) if (!card_id_node) { continue; } unsigned card_id(atoi(card_id_node->value())); Card * card = all_cards.cards_by_id[card_id]; + if (!card) { + std::cerr << "Could not find card by id " << card_id << std::endl; + continue; + } + for(xml_node<>* resource_node = recipe_node->first_node("resource"); resource_node; resource_node = resource_node->next_sibling("resource")) From 1ea68c32b5e67781f672f447c1a60fe27524d1e9 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 5 Sep 2014 05:52:09 +0800 Subject: [PATCH 247/406] Support more BGEs. --- sim.cpp | 9 +++---- sim.h | 8 +++--- tyrant.cpp | 2 +- tyrant.h | 4 +-- tyrant_optimize.cpp | 60 ++++++++++++++++++++++++++++++--------------- 5 files changed, 50 insertions(+), 33 deletions(-) diff --git a/sim.cpp b/sim.cpp index a2482e3c..fdc6f4c2 100644 --- a/sim.cpp +++ b/sim.cpp @@ -380,12 +380,11 @@ Results play(Field* fd) } if(__builtin_expect(fd->end, false)) { break; } - if (fd->bg_enhanced_skill != no_skill) + if (fd->bg_skill.id != no_skill) { // Evaluate TU Battleground effect (Enhance all) - SkillSpec battleground_s = {enhance, fd->bg_enhanced_value, allfactions, 0, 0, fd->bg_enhanced_skill, true}; - _DEBUG_MSG(2, "Evaluating Battleground skill %s\n", skill_description(fd->cards, battleground_s).c_str()); - fd->skill_queue.emplace_back(&fd->tap->commander, battleground_s); + _DEBUG_MSG(2, "Evaluating Battleground skill %s\n", skill_description(fd->cards, fd->bg_skill).c_str()); + fd->skill_queue.emplace_back(&fd->tap->commander, fd->bg_skill); resolve_skill(fd); } @@ -1098,7 +1097,7 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, c template inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) { - if(s.y == allfactions || fd->effect == progenitors) + if(s.y == allfactions || fd->effect == metamorphosis) { return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); } diff --git a/sim.h b/sim.h index 7076c2ea..a4fd0281 100644 --- a/sim.h +++ b/sim.h @@ -198,8 +198,7 @@ class Field gamemode_t gamemode; OptimizationMode optimization_mode; const Effect effect; - Skill bg_enhanced_skill; - unsigned bg_enhanced_value; + SkillSpec bg_skill; // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque> skill_queue; @@ -220,7 +219,7 @@ class Field unsigned current_ci; Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, OptimizationMode optimization_mode_, - Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_) : + Effect effect_, SkillSpec bg_skill_) : end{false}, re(re_), cards(cards_), @@ -229,8 +228,7 @@ class Field gamemode(gamemode_), optimization_mode(optimization_mode_), effect(effect_), - bg_enhanced_skill(bg_enhanced_skill_), - bg_enhanced_value(bg_enhanced_value_), + bg_skill(bg_skill_), n_player_kills(0) { } diff --git a/tyrant.cpp b/tyrant.cpp index 9657c8c6..e98205d9 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -39,7 +39,7 @@ std::string decktype_names[DeckType::num_decktypes]{"Deck", "Mission", "Raid", " std::string effect_names[Effect::num_effects] = { "None", - "Progenitors", + "Metamorphosis", }; signed debug_print(0); diff --git a/tyrant.h b/tyrant.h index 217c969f..71682e18 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.1.2" +#define TYRANT_OPTIMIZER_VERSION "2.1.3" #include #include @@ -77,7 +77,7 @@ extern std::string decktype_names[DeckType::num_decktypes]; enum Effect { none, - progenitors, + metamorphosis, num_effects }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index e3cff545..6ab726c0 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -321,11 +321,10 @@ struct SimulationData std::vector factors; gamemode_t gamemode; enum Effect effect; - Skill bg_enhanced_skill; - unsigned bg_enhanced_value; + SkillSpec bg_skill; SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_enemy_decks_, std::vector factors_, gamemode_t gamemode_, - enum Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_) : + enum Effect effect_, SkillSpec bg_skill_) : re(seed), cards(cards_), decks(decks_), @@ -335,8 +334,7 @@ struct SimulationData factors(factors_), gamemode(gamemode_), effect(effect_), - bg_enhanced_skill(bg_enhanced_skill_), - bg_enhanced_value(bg_enhanced_value_) + bg_skill(bg_skill_) { for (size_t i = 0; i < num_enemy_decks_; ++i) { @@ -367,7 +365,7 @@ struct SimulationData { your_hand.reset(re); enemy_hand->reset(re); - Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, effect != Effect::none ? effect : enemy_hand->deck->effect, bg_enhanced_skill, bg_enhanced_value); + Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, effect != Effect::none ? effect : enemy_hand->deck->effect, bg_skill); Results result(play(&fd)); res.emplace_back(result); } @@ -397,11 +395,10 @@ class Process std::vector factors; gamemode_t gamemode; enum Effect effect; - Skill bg_enhanced_skill; - unsigned bg_enhanced_value; + SkillSpec bg_skill; Process(unsigned num_threads_, const Cards& cards_, const Decks& decks_, Deck* your_deck_, std::vector enemy_decks_, std::vector factors_, gamemode_t gamemode_, - enum Effect effect_, Skill bg_enhanced_skill_, unsigned bg_enhanced_value_) : + enum Effect effect_, SkillSpec bg_skill_) : num_threads(num_threads_), main_barrier(num_threads+1), cards(cards_), @@ -411,14 +408,13 @@ class Process factors(factors_), gamemode(gamemode_), effect(effect_), - bg_enhanced_skill(bg_enhanced_skill_), - bg_enhanced_value(bg_enhanced_value_) + bg_skill(bg_skill_) { destroy_threads = false; unsigned seed(time(0)); for(unsigned i(0); i < num_threads; ++i) { - threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, effect, bg_enhanced_skill, bg_enhanced_value)); + threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, effect, bg_skill)); threads.push_back(new boost::thread(thread_evaluate, std::ref(main_barrier), std::ref(shared_mutex), std::ref(*threads_data.back()), std::ref(*this), i)); } } @@ -1025,6 +1021,8 @@ void usage(int argc, char** argv) ; } +std::string skill_description(const Cards& cards, const SkillSpec& s); + int main(int argc, char** argv) { if (argc == 2 && strcmp(argv[1], "-version") == 0) @@ -1049,8 +1047,7 @@ int main(int argc, char** argv) std::vector> opt_todo; std::string opt_effect; enum Effect opt_effect_id(Effect::none); - Skill opt_bg_enhanced_skill(no_skill); - unsigned opt_bg_enhanced_value(0); + SkillSpec opt_bg_skill{no_skill, 0, allfactions, 0, 0, no_skill, false}; for(int argIndex = 3; argIndex < argc; ++argIndex) { @@ -1286,18 +1283,41 @@ int main(int argc, char** argv) } std::vector tokens; boost::split(tokens, opt_effect, boost::is_any_of(" -")); - opt_bg_enhanced_skill = skill_name_to_id(tokens[0], false); - if (tokens.size() >= 2 && opt_bg_enhanced_skill != no_skill) + + opt_bg_skill.id = skill_name_to_id(tokens[0], false); + unsigned skill_index = 1; + if (tokens.size() >= 2 && opt_bg_skill.id != no_skill) { try { - opt_bg_enhanced_value = boost::lexical_cast(tokens[1]); + if (skill_index < tokens.size() && tokens[skill_index] == "all") + { + opt_bg_skill.all = true; + skill_index += 1; + } + if (skill_index < tokens.size()) + { + opt_bg_skill.s = skill_name_to_id(tokens[skill_index], false); + if (opt_bg_skill.s != no_skill) + { + skill_index += 1; + } + } + if (skill_index < tokens.size()) + { + opt_bg_skill.x = boost::lexical_cast(tokens[skill_index]); + } } catch (const boost::bad_lexical_cast & e) { std::cerr << "Error: Expect a number in effect \"" << opt_effect << "\".\n"; return 0; } + catch (std::exception & e) + { + std::cerr << "Error: effect \"" << opt_effect << ": " << e.what() << "\".\n"; + return 0; + } } else { @@ -1428,13 +1448,13 @@ int main(int argc, char** argv) { std::cout << "Effect: " << effect_names[opt_effect_id] << std::endl; } - else if(opt_bg_enhanced_skill != no_skill) + else if(opt_bg_skill.id != no_skill) { - std::cout << "Effect: (Enhance all) " << skill_names[opt_bg_enhanced_skill] << " " << opt_bg_enhanced_value << std::endl; + std::cout << "Effect: " << skill_description(all_cards, opt_bg_skill) << std::endl; } } - Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, opt_effect_id, opt_bg_enhanced_skill, opt_bg_enhanced_value); + Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, opt_effect_id, opt_bg_skill); { //ScopeClock timer; From e83d6c0c1c6e5ee9d78c4618e0812ce2039f6481 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 8 Sep 2014 14:36:30 +0800 Subject: [PATCH 248/406] Add mode Brawl: Optimize against brawl score. Refactor hill climbing. --- deck.h | 12 -- sim.cpp | 32 +++-- sim.h | 15 +++ tyrant.cpp | 2 +- tyrant.h | 8 +- tyrant_optimize.cpp | 302 +++++++++++++++++++++++++++++++------------- 6 files changed, 257 insertions(+), 114 deletions(-) diff --git a/deck.h b/deck.h index eef9d028..16392c9d 100644 --- a/deck.h +++ b/deck.h @@ -114,18 +114,6 @@ class Deck void set_given_hand(const std::string& deck_string_); void set_forts(const std::string& deck_string_); - template - Container card_ids() const - { - Container results; - results.insert(results.end(), commander->m_id); - for(auto card: cards) - { - results.insert(results.end(), card->m_id); - } - return(results); - } - Deck* clone() const; std::string hash() const; std::string short_description() const; diff --git a/sim.cpp b/sim.cpp index fdc6f4c2..6815f33c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -473,30 +473,38 @@ Results play(Field* fd) ++fd->turn; } unsigned raid_damage = 15 + fd->n_player_kills - (10 * fd->players[1]->commander.m_hp / fd->players[1]->commander.m_card->m_health); + unsigned brawl_score = 50 + fd->n_player_kills * 2 - fd->turn / 2; // Note that turn is +1, which is intentional // you lose if(fd->players[0]->commander.m_hp == 0) { _DEBUG_MSG(1, "You lose.\n"); - if (fd->optimization_mode == OptimizationMode::raid) - { return {0, 0, 1, raid_damage, 0}; } - else - { return {0, 0, 1, 0, 0}; } + switch (fd->optimization_mode) + { + case OptimizationMode::raid: return {0, 0, 1, raid_damage, 0}; + case OptimizationMode::brawl: return {0, 0, 1, 5, 0}; + default: return {0, 0, 1, 0, 0}; + } } // you win if(fd->players[1]->commander.m_hp == 0) { _DEBUG_MSG(1, "You win.\n"); - return {1, 0, 0, 100, 0}; + switch (fd->optimization_mode) + { + case OptimizationMode::brawl: return {1, 0, 0, brawl_score, 0}; + default: return {1, 0, 0, 100, 0}; + } } if (fd->turn > turn_limit) { _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); - if (fd->optimization_mode == OptimizationMode::defense) - { return {1, 1, 0, 100, 0}; } - else if (fd->optimization_mode == OptimizationMode::raid) - { return {0, 1, 0, raid_damage, 0}; } - else - { return {0, 1, 0, 0, 0}; } + switch (fd->optimization_mode) + { + case OptimizationMode::defense: return {1, 1, 0, 100, 0}; + case OptimizationMode::raid: return {0, 1, 0, raid_damage, 0}; + case OptimizationMode::brawl: return {0, 1, 0, 5, 0}; + default: return {0, 1, 0, 0, 0}; + } } // Huh? How did we get here? @@ -931,7 +939,7 @@ bool attack_phase(Field* fd) Storage& def_assaults(fd->tip->assaults); if(attack_power(att_status) == 0) { - return false; + return false; } if (alive_assault(def_assaults, fd->current_ci)) diff --git a/sim.h b/sim.h index a4fd0281..2dfee642 100644 --- a/sim.h +++ b/sim.h @@ -46,6 +46,21 @@ struct Results } }; +typedef std::pair>, unsigned> EvaluatedResults; + +template +struct FinalResults +{ + result_type wins; + result_type draws; + result_type losses; + result_type points; + result_type sq_points; + result_type points_lower_bound; + result_type points_upper_bound; + uint64_t n_sims; +}; + void fill_skill_table(); Results play(Field* fd); void modify_cards(Cards& cards, enum Effect effect); diff --git a/tyrant.cpp b/tyrant.cpp index e98205d9..971fdc27 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -23,7 +23,7 @@ std::string skill_names[Skill::num_skills] = "Armor", "Corrosive", "Counter", "Evade", "Wall", "", // Combat-Modifier: - "Flurry", "Pierce", + "Flurry", "Pierce", "Valor", // Damage-Dependant: "Berserk", "Inhibit", "Leech", "Poison", }; diff --git a/tyrant.h b/tyrant.h index 71682e18..b9b70faf 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.1.3" +#define TYRANT_OPTIMIZER_VERSION "2.1.4" #include #include @@ -39,7 +39,7 @@ enum Skill armor, corrosive, counter, evade, wall, END_DEFENSIVE, // Combat-Modifier: - flurry, pierce, + flurry, pierce, valor, // Damage-Dependant: berserk, inhibit, leech, poison, num_skills @@ -93,8 +93,10 @@ enum class OptimizationMode { notset, winrate, + defense, + war, + brawl, raid, - defense }; struct true_ {}; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 6ab726c0..68426547 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -50,6 +50,9 @@ namespace { unsigned max_deck_len{10}; unsigned fund{0}; long double target_score{100}; + long double min_increment_of_score{0}; + long double confidence_level{0.99}; + bool show_ci{false}; bool show_stdev{false}; bool use_harmonic_mean{false}; } @@ -249,7 +252,7 @@ bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, cons } } deck_cost = get_deck_cost(deck); - return true; + return !cards_in.empty() || !cards_out.empty(); } void claim_cards(const std::vector & card_list) @@ -272,18 +275,34 @@ void claim_cards(const std::vector & card_list) } //------------------------------------------------------------------------------ -Results compute_score(const std::pair> , unsigned>& results, std::vector& factors) +FinalResults compute_score(const EvaluatedResults& results, std::vector& factors) { - Results final{0, 0, 0, 0, 0}; + FinalResults final{0, 0, 0, 0, 0, 0, 0, results.second}; + long double max_possible = 100; + switch (optimization_mode) + { + case OptimizationMode::brawl: max_possible = 60; break; + default: max_possible = 100; break; + } for(unsigned index(0); index < results.first.size(); ++index) { final.wins += results.first[index].wins * factors[index]; final.draws += results.first[index].draws * factors[index]; final.losses += results.first[index].losses * factors[index]; + auto lower_bound = boost::math::binomial_distribution<>::find_lower_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; + auto upper_bound = boost::math::binomial_distribution<>::find_upper_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; if(use_harmonic_mean) - { final.points += factors[index] / results.first[index].points; } + { + final.points += factors[index] / results.first[index].points; + final.points_lower_bound += factors[index] / lower_bound; + final.points_upper_bound += factors[index] / upper_bound; + } else - { final.points += results.first[index].points * factors[index]; } + { + final.points += results.first[index].points * factors[index]; + final.points_lower_bound += lower_bound * factors[index]; + final.points_upper_bound += upper_bound * factors[index]; + } final.sq_points += results.first[index].sq_points * factors[index] * factors[index]; } long double factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); @@ -291,17 +310,24 @@ Results compute_score(const std::pair final.draws /= factor_sum * (long double)results.second; final.losses /= factor_sum * (long double)results.second; if(use_harmonic_mean) - { final.points = factor_sum / ((long double)results.second * final.points); } + { + final.points = factor_sum / ((long double)results.second * final.points); + final.points_lower_bound = factor_sum / final.points_lower_bound; + final.points_upper_bound = factor_sum / final.points_upper_bound; + } else - { final.points /= factor_sum * (long double)results.second; } + { + final.points /= factor_sum * (long double)results.second; + final.points_lower_bound /= factor_sum; + final.points_upper_bound /= factor_sum; + } final.sq_points /= factor_sum * factor_sum * (long double)results.second; return final; } //------------------------------------------------------------------------------ volatile unsigned thread_num_iterations{0}; // written by threads -std::vector> thread_results; // written by threads -volatile unsigned thread_total{0}; // written by threads -volatile long double thread_prev_score{0.0}; +EvaluatedResults *thread_results{nullptr}; // written by threads +volatile const FinalResults *thread_best_results{nullptr}; volatile bool thread_compare{false}; volatile bool thread_compare_stop{false}; // written by threads volatile bool destroy_threads; @@ -427,32 +453,38 @@ class Process for(auto data: threads_data) { delete(data); } } - std::pair> , unsigned> evaluate(unsigned num_iterations) + EvaluatedResults & evaluate(unsigned num_iterations, EvaluatedResults & evaluated_results) { - thread_num_iterations = num_iterations; - thread_results = std::vector>(enemy_decks.size()); - thread_total = 0; + if (num_iterations <= evaluated_results.second) + { + return evaluated_results; + } + thread_num_iterations = num_iterations - evaluated_results.second; + thread_results = &evaluated_results; thread_compare = false; // unlock all the threads main_barrier.wait(); // wait for the threads main_barrier.wait(); - return(std::make_pair(thread_results, thread_total)); + return evaluated_results; } - std::pair> , unsigned> compare(unsigned num_iterations, long double prev_score) + EvaluatedResults & compare(unsigned num_iterations, EvaluatedResults & evaluated_results, const FinalResults & best_results) { - thread_num_iterations = num_iterations; - thread_results = std::vector>(enemy_decks.size()); - thread_total = 0; - thread_prev_score = prev_score; + if (num_iterations <= evaluated_results.second) + { + return evaluated_results; + } + thread_num_iterations = num_iterations - evaluated_results.second; + thread_results = &evaluated_results; + thread_best_results = &best_results; thread_compare = true; thread_compare_stop = false; // unlock all the threads main_barrier.wait(); // wait for the threads main_barrier.wait(); - return(std::make_pair(thread_results, thread_total)); + return evaluated_results; } }; //------------------------------------------------------------------------------ @@ -483,14 +515,14 @@ void thread_evaluate(boost::barrier& main_barrier, shared_mutex.unlock(); //>>>> std::vector> result{sim.evaluate()}; shared_mutex.lock(); //<<<< - std::vector thread_score_local(thread_results.size(), 0u); //! + std::vector thread_score_local(thread_results->first.size(), 0u); //! for(unsigned index(0); index < result.size(); ++index) { - thread_results[index] += result[index]; //! - thread_score_local[index] = thread_results[index].points; //! + thread_results->first[index] += result[index]; //! + thread_score_local[index] = thread_results->first[index].points; //! } - ++thread_total; //! - unsigned thread_total_local{thread_total}; //! + ++thread_results->second; //! + unsigned thread_total_local{thread_results->second}; //! shared_mutex.unlock(); //>>>> if(thread_compare && thread_id == 0 && thread_total_local > 1) { @@ -511,9 +543,15 @@ void thread_evaluate(boost::barrier& main_barrier, score_accum = thread_score_local[0]; } bool compare_stop(false); - long double best_possible = 100; + long double max_possible = 100; + switch (optimization_mode) + { + case OptimizationMode::brawl: max_possible = 60; break; + default: max_possible = 100; break; + } // Get a loose (better than no) upper bound. TODO: Improve it. - compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum / best_possible, 0.01) * best_possible < thread_prev_score); + compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum / max_possible, 1 - confidence_level) * max_possible < + thread_best_results->points + min_increment_of_score); if(compare_stop) { shared_mutex.lock(); //<<<< @@ -527,15 +565,21 @@ void thread_evaluate(boost::barrier& main_barrier, } } //------------------------------------------------------------------------------ -void print_score_info(const std::pair> , unsigned>& results, std::vector& factors) +void print_score_info(const EvaluatedResults& results, std::vector& factors) { auto final = compute_score(results, factors); std::cout << final.points << " ("; + if (show_ci) + { + std::cout << final.points_lower_bound << "-" << final.points_upper_bound << ", "; + } for(const auto & val: results.first) { switch(optimization_mode) { case OptimizationMode::raid: + case OptimizationMode::brawl: + case OptimizationMode::war: std::cout << val.points << " "; break; default: @@ -546,7 +590,7 @@ void print_score_info(const std::pair> , unsigned> std::cout << "/ " << results.second << ")" << std::endl; } //------------------------------------------------------------------------------ -void print_results(const std::pair> , unsigned>& results, std::vector& factors) +void print_results(const EvaluatedResults& results, std::vector& factors) { auto final = compute_score(results, factors); @@ -574,12 +618,18 @@ void print_results(const std::pair> , unsigned>& r switch(optimization_mode) { case OptimizationMode::raid: - std::cout << "ard: " << final.points << " ("; + case OptimizationMode::brawl: + case OptimizationMode::war: + std::cout << "score: " << final.points << " ("; for(const auto & val: results.first) { std::cout << val.points << " "; } std::cout << "/ " << results.second << ")" << std::endl; + if (show_ci) + { + std::cout << "ci: " << final.points_lower_bound << " - " << final.points_upper_bound << std::endl; + } if (show_stdev) { std::cout << "stdev: " << sqrt(final.sq_points - final.points * final.points) << std::endl; @@ -590,7 +640,7 @@ void print_results(const std::pair> , unsigned>& r } } //------------------------------------------------------------------------------ -void print_deck_inline(const unsigned deck_cost, const Results score, Deck * deck) +void print_deck_inline(const unsigned deck_cost, const FinalResults score, Deck * deck) { if(fund > 0) { @@ -599,7 +649,13 @@ void print_deck_inline(const unsigned deck_cost, const Results scor switch(optimization_mode) { case OptimizationMode::raid: + case OptimizationMode::brawl: + case OptimizationMode::war: std::cout << "(" << score.wins * 100 << "% win, " << score.draws * 100 << "% stall"; + if (show_ci) + { + std::cout << ", " << score.points_lower_bound << "-" << score.points_upper_bound; + } if (show_stdev) { std::cout << ", " << sqrt(score.sq_points - score.points * score.points) << " stdev"; @@ -643,13 +699,15 @@ void print_deck_inline(const unsigned deck_cost, const Results scor std::cout << std::endl; } //------------------------------------------------------------------------------ -void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) +void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) { - auto results = proc.evaluate(num_iterations); + EvaluatedResults zero_results{EvaluatedResults::first_type(proc.enemy_decks.size()), 0}; + auto best_deck = d1->hash(); + std::map evaluated_decks{{best_deck, zero_results}}; + EvaluatedResults & results = proc.evaluate(num_min_iterations, evaluated_decks.begin()->second); print_score_info(results, proc.factors); auto current_score = compute_score(results, proc.factors); auto best_score = current_score; - std::map, unsigned> evaluated_decks{{d1->card_ids>(), num_iterations}}; // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); @@ -664,20 +722,36 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map> cards_out, cards_in; - for(unsigned slot_i(0), dead_slot(0); (deck_has_been_improved || slot_i != dead_slot) && best_score.points - target_score < -1e-9; slot_i = (slot_i + 1) % std::min(max_deck_len, best_cards.size() + 1)) + for(unsigned slot_i(0), dead_slot(0); + best_score.n_sims < num_iterations || best_score.points - target_score < -1e-9; + slot_i = (slot_i + 1) % std::min(max_deck_len, best_cards.size() + 1)) { - if(deck_has_been_improved) + if (deck_has_been_improved) { dead_slot = slot_i; deck_has_been_improved = false; } + else if (slot_i == dead_slot) + { + if (best_score.n_sims >= num_iterations) + { + break; + } + auto & prev_results = evaluated_decks[best_deck]; + skipped_simulations += prev_results.second; + // Re-evaluate the best deck + auto evaluate_result = proc.evaluate(std::min(prev_results.second * 10, num_iterations), prev_results); + best_score = compute_score(evaluate_result, proc.factors); + std::cout << "Results refined: "; + print_score_info(evaluate_result, proc.factors); + } if(!card_marks.count(-1)) { for(const Card* commander_candidate: proc.cards.player_commanders) { // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); - if(commander_candidate->m_name == best_commander->m_name) + if (commander_candidate->m_name == best_commander->m_name) { continue; } d1->cards = best_cards; // Place it in the deck and restore other cards @@ -687,25 +761,26 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcommander = commander_candidate; if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in)) { continue; } - auto &&cur_deck = d1->card_ids>(); - if (evaluated_decks.count(cur_deck) > 0) + auto && cur_deck = d1->hash(); + auto && emplace_rv = evaluated_decks.emplace(cur_deck, zero_results); + auto & prev_results = emplace_rv.first->second; + if (!emplace_rv.second) { - skipped_simulations += evaluated_decks[cur_deck]; - continue; + skipped_simulations += prev_results.second; } // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score.points); + auto compare_results = proc.compare(best_score.n_sims, prev_results, best_score); current_score = compute_score(compare_results, proc.factors); - evaluated_decks[cur_deck] = compare_results.second; // Is it better ? - if(current_score.points > best_score.points) + if (current_score.points > best_score.points + min_increment_of_score) { std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; // Then update best score/commander, print stuff best_score = current_score; + best_deck = cur_deck; best_commander = d1->commander; best_cards = d1->cards; - deck_has_been_improved = true; +// deck_has_been_improved = true; print_score_info(compare_results, proc.factors); print_deck_inline(deck_cost, best_score, d1); } @@ -722,7 +797,7 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapm_name == best_cards[slot_i]->m_name) // Omega -> Omega : - (slot_i == best_cards.size())) // void -> void + (slot_i == best_cards.size())) // void -> void { continue; } cards_out.clear(); if (slot_i < d1->cards.size()) @@ -733,22 +808,23 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::mapcards.size() < min_deck_len) { continue; } - auto &&cur_deck = d1->card_ids>(); - if (evaluated_decks.count(cur_deck) > 0) + auto && cur_deck = d1->hash(); + auto && emplace_rv = evaluated_decks.emplace(cur_deck, zero_results); + auto & prev_results = emplace_rv.first->second; + if (!emplace_rv.second) { - skipped_simulations += evaluated_decks[cur_deck]; - continue; + skipped_simulations += prev_results.second; } // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score.points); + auto compare_results = proc.compare(best_score.n_sims, prev_results, best_score); current_score = compute_score(compare_results, proc.factors); - evaluated_decks[cur_deck] = compare_results.second; // Is it better ? - if(current_score.points > best_score.points) + if (current_score.points > best_score.points + min_increment_of_score) { std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; // Then update best score/slot, print stuff best_score = current_score; + best_deck = cur_deck; best_commander = d1->commander; best_cards = d1->cards; deck_has_been_improved = true; @@ -763,19 +839,21 @@ void hill_climbing(unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) +void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) { - auto results = proc.evaluate(num_iterations); + EvaluatedResults zero_results{EvaluatedResults::first_type(proc.enemy_decks.size()), 0}; + auto best_deck = d1->hash(); + std::map evaluated_decks{{best_deck, zero_results}}; + EvaluatedResults & results = proc.evaluate(num_min_iterations, evaluated_decks.begin()->second); print_score_info(results, proc.factors); auto current_score = compute_score(results, proc.factors); auto best_score = current_score; - std::map, unsigned> evaluated_decks{{d1->card_ids>(), num_iterations}}; // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); @@ -790,13 +868,29 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; std::vector> cards_out, cards_in; - for(unsigned from_slot(0), dead_slot(0); (deck_has_been_improved || from_slot != dead_slot) && best_score.points - target_score < -1e-9; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) + for(unsigned from_slot(0), dead_slot(0); + best_score.n_sims < num_iterations || best_score.points - target_score < -1e-9; + from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(deck_has_been_improved) { dead_slot = from_slot; deck_has_been_improved = false; } + else if (from_slot == dead_slot) + { + if (best_score.n_sims >= num_iterations) + { + break; + } + auto & prev_results = evaluated_decks[best_deck]; + skipped_simulations += prev_results.second; + // Re-evaluate the best deck + auto evaluate_result = proc.evaluate(std::min(prev_results.second * 10, num_iterations), prev_results); + best_score = compute_score(evaluate_result, proc.factors); + std::cout << "Results refined: "; + print_score_info(evaluate_result, proc.factors); + } if(!card_marks.count(-1)) { for(const Card* commander_candidate: proc.cards.player_commanders) @@ -805,7 +899,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std { break; } // Various checks to check if the card is accepted assert(commander_candidate->m_type == CardType::commander); - if(commander_candidate->m_name == best_commander->m_name) + if (commander_candidate->m_name == best_commander->m_name) { continue; } d1->cards = best_cards; // Place it in the deck @@ -814,22 +908,23 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std d1->commander = commander_candidate; if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in)) { continue; } - auto &&cur_deck = d1->card_ids>(); - if (evaluated_decks.count(cur_deck) > 0) + auto && cur_deck = d1->hash(); + auto && emplace_rv = evaluated_decks.emplace(cur_deck, zero_results); + auto & prev_results = emplace_rv.first->second; + if (!emplace_rv.second) { - skipped_simulations += evaluated_decks[cur_deck]; - continue; + skipped_simulations += prev_results.second; } // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score.points); + auto compare_results = proc.compare(best_score.n_sims, prev_results, best_score); current_score = compute_score(compare_results, proc.factors); - evaluated_decks[cur_deck] = compare_results.second; // Is it better ? - if(current_score.points > best_score.points) + if (current_score.points > best_score.points + min_increment_of_score) { std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; // Then update best score/commander, print stuff best_score = current_score; + best_deck = cur_deck; best_commander = commander_candidate; best_cards = d1->cards; deck_has_been_improved = true; @@ -853,7 +948,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std if (card_candidate ? (from_slot < best_cards.size() && (from_slot == to_slot && card_candidate->m_name == best_cards[to_slot]->m_name)) // 2 Omega -> 2 Omega : - (from_slot == best_cards.size())) // void -> void + (from_slot == best_cards.size())) // void -> void { continue; } cards_out.clear(); if (from_slot < d1->cards.size()) @@ -864,22 +959,23 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std if (! adjust_deck(d1, from_slot, to_slot, card_candidate, fund, re, deck_cost, cards_out, cards_in) || d1->cards.size() < min_deck_len) { continue; } - auto &&cur_deck = d1->card_ids>(); - if (evaluated_decks.count(cur_deck) > 0) + auto && cur_deck = d1->hash(); + auto && emplace_rv = evaluated_decks.emplace(cur_deck, zero_results); + auto & prev_results = emplace_rv.first->second; + if (!emplace_rv.second) { - skipped_simulations += evaluated_decks[cur_deck]; - continue; + skipped_simulations += prev_results.second; } // Evaluate new deck - auto compare_results = proc.compare(num_iterations, best_score.points); + auto compare_results = proc.compare(best_score.n_sims, prev_results, best_score); current_score = compute_score(compare_results, proc.factors); - evaluated_decks[cur_deck] = compare_results.second; // Is it better ? - if(current_score.points > best_score.points) + if (current_score.points > best_score.points + min_increment_of_score) { // Then update best score/slot, print stuff std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; best_score = current_score; + best_deck = cur_deck; best_commander = d1->commander; best_cards = d1->cards; deck_has_been_improved = true; @@ -895,7 +991,7 @@ void hill_climbing_ordered(unsigned num_iterations, Deck* d1, Process& proc, std } unsigned simulations = 0; for(auto evaluation: evaluated_decks) - { simulations += evaluation.second; } + { simulations += evaluation.second.second; } std::cout << "Evaluated " << evaluated_decks.size() << " decks (" << simulations << " + " << skipped_simulations << " simulations)." << std::endl; std::cout << "Optimized Deck: "; print_deck_inline(get_deck_cost(d1), best_score, d1); @@ -1022,6 +1118,7 @@ void usage(int argc, char** argv) } std::string skill_description(const Cards& cards, const SkillSpec& s); +extern void(*skill_table[num_skills])(Field*, CardStatus* src_status, const SkillSpec&); int main(int argc, char** argv) { @@ -1100,6 +1197,16 @@ int main(int argc, char** argv) gamemode = surge; optimization_mode = OptimizationMode::defense; } + else if (strcmp(argv[argIndex], "brawl") == 0) + { + gamemode = surge; + optimization_mode = OptimizationMode::brawl; + } + else if (strcmp(argv[argIndex], "brawl-defense") == 0) + { + gamemode = fight; + optimization_mode = OptimizationMode::brawl; + } else if (strcmp(argv[argIndex], "gw") == 0) { gamemode = surge; @@ -1180,6 +1287,20 @@ int main(int argc, char** argv) turn_limit = atoi(argv[argIndex+1]); argIndex += 1; } + else if(strcmp(argv[argIndex], "mis") == 0) + { + min_increment_of_score = atof(argv[argIndex+1]); + argIndex += 1; + } + else if(strcmp(argv[argIndex], "cl") == 0) + { + confidence_level = atof(argv[argIndex+1]); + argIndex += 1; + } + else if(strcmp(argv[argIndex], "+ci") == 0) + { + show_ci = true; + } else if(strcmp(argv[argIndex], "+stdev") == 0) { show_stdev = true; @@ -1221,15 +1342,21 @@ int main(int argc, char** argv) opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); argIndex += 1; } + else if(strcmp(argv[argIndex], "climbex") == 0) + { + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), climb)); + opt_do_optimization = true; + argIndex += 2; + } else if(strcmp(argv[argIndex], "climb") == 0) { - opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, climb)); + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), climb)); opt_do_optimization = true; argIndex += 1; } else if(strcmp(argv[argIndex], "reorder") == 0) { - opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, reorder)); + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), reorder)); argIndex += 1; } else if(strcmp(argv[argIndex], "debug") == 0) @@ -1286,11 +1413,11 @@ int main(int argc, char** argv) opt_bg_skill.id = skill_name_to_id(tokens[0], false); unsigned skill_index = 1; - if (tokens.size() >= 2 && opt_bg_skill.id != no_skill) + if (skill_table[opt_bg_skill.id] != nullptr) { try { - if (skill_index < tokens.size() && tokens[skill_index] == "all") + if (skill_index < tokens.size() && boost::to_lower_copy(tokens[skill_index]) == "all") { opt_bg_skill.all = true; skill_index += 1; @@ -1463,7 +1590,8 @@ int main(int argc, char** argv) switch(std::get<2>(op)) { case simulate: { - auto results = p.evaluate(std::get<0>(op)); + EvaluatedResults results{EvaluatedResults::first_type(enemy_decks.size()), 0}; + results = p.evaluate(std::get<0>(op), results); print_results(results, p.factors); break; } @@ -1471,12 +1599,12 @@ int main(int argc, char** argv) switch (opt_your_strategy) { case DeckStrategy::random: - hill_climbing(std::get<0>(op), your_deck, p, your_deck->card_marks); + hill_climbing(std::get<0>(op), std::get<1>(op), your_deck, p, your_deck->card_marks); break; // case DeckStrategy::ordered: // case DeckStrategy::exact_ordered: default: - hill_climbing_ordered(std::get<0>(op), your_deck, p, your_deck->card_marks); + hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, your_deck->card_marks); break; } break; @@ -1492,13 +1620,14 @@ int main(int argc, char** argv) owned_cards.clear(); claim_cards({your_deck->commander}); claim_cards(your_deck->cards); - hill_climbing_ordered(std::get<0>(op), your_deck, p, your_deck->card_marks); + hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, your_deck->card_marks); break; } case debug: { ++ debug_print; debug_str.clear(); - auto results = p.evaluate(1); + EvaluatedResults results{EvaluatedResults::first_type(enemy_decks.size()), 0}; + results = p.evaluate(1, results); print_results(results, p.factors); -- debug_print; break; @@ -1509,7 +1638,8 @@ int main(int argc, char** argv) while(1) { debug_str.clear(); - auto results = p.evaluate(1); + EvaluatedResults results{EvaluatedResults::first_type(enemy_decks.size()), 0}; + results = p.evaluate(1, results); auto score = compute_score(results, p.factors); if(score.points >= std::get<0>(op) && score.points <= std::get<1>(op)) { From fad006a38c6623d6d077e64399b775cdabc9d746 Mon Sep 17 00:00:00 2001 From: emcn Date: Tue, 9 Sep 2014 09:44:48 +0200 Subject: [PATCH 249/406] Patched for GNU C++ compatibility --- tyrant_optimize.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 68426547..843a1782 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -762,7 +762,7 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in)) { continue; } auto && cur_deck = d1->hash(); - auto && emplace_rv = evaluated_decks.emplace(cur_deck, zero_results); + auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); auto & prev_results = emplace_rv.first->second; if (!emplace_rv.second) { @@ -809,7 +809,7 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d d1->cards.size() < min_deck_len) { continue; } auto && cur_deck = d1->hash(); - auto && emplace_rv = evaluated_decks.emplace(cur_deck, zero_results); + auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); auto & prev_results = emplace_rv.first->second; if (!emplace_rv.second) { @@ -909,7 +909,7 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in)) { continue; } auto && cur_deck = d1->hash(); - auto && emplace_rv = evaluated_decks.emplace(cur_deck, zero_results); + auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); auto & prev_results = emplace_rv.first->second; if (!emplace_rv.second) { @@ -960,7 +960,7 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, d1->cards.size() < min_deck_len) { continue; } auto && cur_deck = d1->hash(); - auto && emplace_rv = evaluated_decks.emplace(cur_deck, zero_results); + auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); auto & prev_results = emplace_rv.first->second; if (!emplace_rv.second) { From f37a25313bacfcb967a4c720650d023c9354bf1a Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 12 Sep 2014 06:25:01 +0800 Subject: [PATCH 250/406] Add Valor skill. --- sim.cpp | 17 ++++++++++++++++- tyrant.h | 2 +- tyrant_optimize.cpp | 22 ++++++++++++++-------- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/sim.cpp b/sim.cpp index 6815f33c..973b446f 100644 --- a/sim.cpp +++ b/sim.cpp @@ -529,6 +529,12 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(can_be_healed(c)); } +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(ref->m_card->m_type == CardType::assault && attack_power(ref) > attack_power(c)); +} + void remove_hp(Field* fd, CardStatus& status, unsigned dmg) { assert(status.m_hp > 0); @@ -783,7 +789,16 @@ struct PerformAttack void op() { unsigned pre_modifier_dmg = attack_power(att_status); + // valor boost + unsigned valor_value = att_status->skill(); + if (valor_value > 0 && skill_check(fd, att_status, def_status)) + { + _DEBUG_MSG(1, "%s activates Valor %u\n", status_description(att_status).c_str(), valor_value); + pre_modifier_dmg += valor_value; + att_status->m_rallied += valor_value; + } if(pre_modifier_dmg == 0) { return; } + // Evaluation order: // modify damage // deal damage @@ -834,8 +849,8 @@ struct PerformAttack assert(att_status->m_card->m_type == CardType::assault); assert(pre_modifier_dmg > 0); att_dmg = pre_modifier_dmg; - // enhance damage std::string desc; + // enhance damage if(def_status->m_enfeebled > 0) { if(debug_print > 0) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } diff --git a/tyrant.h b/tyrant.h index b9b70faf..b925f1e1 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.1.4" +#define TYRANT_OPTIMIZER_VERSION "2.1.5" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 843a1782..274ead5e 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -722,16 +722,14 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; std::vector> cards_out, cards_in; - for(unsigned slot_i(0), dead_slot(0); - best_score.n_sims < num_iterations || best_score.points - target_score < -1e-9; - slot_i = (slot_i + 1) % std::min(max_deck_len, best_cards.size() + 1)) + for(unsigned slot_i(0), dead_slot(0); ; slot_i = (slot_i + 1) % std::min(max_deck_len, best_cards.size() + 1)) { if (deck_has_been_improved) { dead_slot = slot_i; deck_has_been_improved = false; } - else if (slot_i == dead_slot) + else if (slot_i == dead_slot || best_score.points - target_score > -1e-9) { if (best_score.n_sims >= num_iterations) { @@ -744,6 +742,11 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d best_score = compute_score(evaluate_result, proc.factors); std::cout << "Results refined: "; print_score_info(evaluate_result, proc.factors); + dead_slot = slot_i; + } + if (best_score.points - target_score > -1e-9) + { + continue; } if(!card_marks.count(-1)) { @@ -868,16 +871,14 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; std::vector> cards_out, cards_in; - for(unsigned from_slot(0), dead_slot(0); - best_score.n_sims < num_iterations || best_score.points - target_score < -1e-9; - from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) + for(unsigned from_slot(0), dead_slot(0); ; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { if(deck_has_been_improved) { dead_slot = from_slot; deck_has_been_improved = false; } - else if (from_slot == dead_slot) + else if (from_slot == dead_slot || best_score.points - target_score > -1e-9) { if (best_score.n_sims >= num_iterations) { @@ -890,6 +891,11 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, best_score = compute_score(evaluate_result, proc.factors); std::cout << "Results refined: "; print_score_info(evaluate_result, proc.factors); + dead_slot = from_slot; + } + if (best_score.points - target_score > -1e-9) + { + continue; } if(!card_marks.count(-1)) { From 8a741b3bc4fdbc2135cfd70cf197f519cfca8f91 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 12 Sep 2014 06:31:38 +0800 Subject: [PATCH 251/406] Support raid #2 Jotun Max. --- data/raids.xml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/data/raids.xml b/data/raids.xml index 7e5f4811..a2c104c6 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -27,5 +27,27 @@ - + + 2 + Jotun Max + 25000 + 1413 + 26 + + + 10551 + 10551 + 10521 + 10521 + 10541 + 10541 + 10531 + 10531 + 10511 + 10511 + + + + + From 73cd3c7705fbd17b26f83cb3eda70d298b743075 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 15 Sep 2014 11:32:51 +0800 Subject: [PATCH 252/406] Support -10 and -26. --- xml.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xml.cpp b/xml.cpp index 6978ae91..3d15018a 100644 --- a/xml.cpp +++ b/xml.cpp @@ -319,9 +319,8 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch decks.decks.push_back(Deck{all_cards, decktype, id, deck_name, effect, level - 1, max_level - 1}); Deck* deck = &decks.decks.back(); deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); - std::string alt_name = decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(level); decks.by_name[deck_name] = deck; - decks.by_name[alt_name] = deck; + decks.by_name[decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(level)] = deck; } decks.decks.push_back(Deck{all_cards, decktype, id, base_deck_name, effect}); @@ -338,9 +337,10 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch { card = card->m_top_level_card; } } - std::string alt_name = decktype_names[decktype] + " #" + to_string(id); decks.by_name[base_deck_name] = deck; - decks.by_name[alt_name] = deck; + decks.by_name[base_deck_name + "-" + to_string(max_level)] = deck; + decks.by_name[decktype_names[decktype] + " #" + to_string(id)] = deck; + decks.by_name[decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(max_level)] = deck; decks.by_type_id[{decktype, id}] = deck; return deck; } From b3f8ad881c3c50b5a656288802a708182c7163fe Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 18 Sep 2014 23:54:58 +0800 Subject: [PATCH 253/406] Fix error message for wrong efort input. --- tyrant_optimize.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 274ead5e..b3ca133b 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1145,6 +1145,7 @@ int main(int argc, char** argv) std::string opt_forts, opt_enemy_forts; std::string opt_hand, opt_enemy_hand; std::vector opt_owned_cards_str_list; + std::vector opt_custom_cards_str_list; bool opt_do_optimization(false); bool opt_keep_commander{false}; std::vector> opt_todo; @@ -1253,6 +1254,14 @@ int main(int argc, char** argv) opt_owned_cards_str_list.push_back(argv[argIndex] + 3); use_owned_cards = true; } + else if(strcmp(argv[argIndex], "-C") == 0) + { + opt_custom_cards_str_list.push_back("data/customcards.txt"); + } + else if(strncmp(argv[argIndex], "-C=", 3) == 0) + { + opt_custom_cards_str_list.push_back(argv[argIndex] + 3); + } else if(strcmp(argv[argIndex], "fund") == 0) { fund = atoi(argv[argIndex+1]); @@ -1386,6 +1395,12 @@ int main(int argc, char** argv) Cards all_cards; Decks decks; load_cards_xml(all_cards, "data/cards.xml"); +#if 0 + for (const auto & cc_str: opt_custom_cards_str_list) + { + process_custom_cards(cc_str); + } +#endif read_card_abbrs(all_cards, "data/cardabbrs.txt"); load_decks_xml(decks, all_cards, "data/missions.xml", "data/raids.xml"); load_custom_decks(decks, all_cards, "data/customdecks.txt"); @@ -1554,7 +1569,7 @@ int main(int argc, char** argv) } catch(const std::runtime_error& e) { - std::cerr << "Error: yf " << opt_forts << ": " << e.what() << std::endl; + std::cerr << "Error: ef " << opt_enemy_forts << ": " << e.what() << std::endl; return 0; } } From 881f7f6b336d0b0c801013c9913b53f49f968a76 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 18 Sep 2014 23:59:22 +0800 Subject: [PATCH 254/406] Update Valor skill. --- sim.cpp | 56 ++++++++++++++++++++++++++++++++++++++++---------------- sim.h | 13 ++++++++----- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/sim.cpp b/sim.cpp index 973b446f..a680a674 100644 --- a/sim.cpp +++ b/sim.cpp @@ -14,7 +14,7 @@ #include "deck.h" //------------------------------------------------------------------------------ -inline std::string status_description(CardStatus* status) +inline std::string status_description(const CardStatus* status) { return status->description(); } @@ -79,27 +79,30 @@ inline void CardStatus::set(const Card& card) m_card = &card; m_index = 0; m_player = 0; + m_delay = card.m_delay; + m_faction = card.m_faction; + m_hp = card.m_health; + m_step = CardStep::none; + m_berserk = 0; m_corroded_rate = 0; m_corroded_weakened = 0; - m_delay = card.m_delay; m_evaded = 0; m_enfeebled = 0; - m_faction = card.m_faction; - m_hp = card.m_health; m_inhibited = 0; m_jammed = false; m_overloaded = false; m_poisoned = 0; m_protected = 0; m_rallied = 0; + m_valored = false; m_weakened = 0; - m_step = CardStep::none; + std::memset(m_enhanced_value, 0, sizeof m_enhanced_value); std::memset(m_skill_cd, 0, sizeof m_skill_cd); } //------------------------------------------------------------------------------ -inline int attack_power(CardStatus* att) +inline int attack_power(const CardStatus* att) { return(safe_minus(att->m_card->m_attack + att->m_berserk + att->m_rallied, att->m_weakened + att->m_corroded_weakened)); } @@ -146,7 +149,7 @@ std::string card_description(const Cards& cards, const Card* c) return(desc); } //------------------------------------------------------------------------------ -std::string CardStatus::description() +std::string CardStatus::description() const { std::string desc; switch(m_card->m_type) @@ -319,6 +322,7 @@ inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_ //------------------------------------------------------------------------------ void turn_start_phase(Field* fd); void turn_end_phase(Field* fd); +bool check_and_perform_valor(Field* fd, CardStatus* src_status); // return value : (raid points) -> attacker wins, 0 -> defender wins Results play(Field* fd) { @@ -416,6 +420,7 @@ Results play(Field* fd) } else { + check_and_perform_valor(fd, current_status); unsigned num_actions(1); for(unsigned action_index(0); action_index < num_actions; ++action_index) { @@ -532,7 +537,7 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { - return(ref->m_card->m_type == CardType::assault && attack_power(ref) > attack_power(c)); + return !c->m_valored; } void remove_hp(Field* fd, CardStatus& status, unsigned dmg) @@ -789,14 +794,6 @@ struct PerformAttack void op() { unsigned pre_modifier_dmg = attack_power(att_status); - // valor boost - unsigned valor_value = att_status->skill(); - if (valor_value > 0 && skill_check(fd, att_status, def_status)) - { - _DEBUG_MSG(1, "%s activates Valor %u\n", status_description(att_status).c_str(), valor_value); - pre_modifier_dmg += valor_value; - att_status->m_rallied += valor_value; - } if(pre_modifier_dmg == 0) { return; } // Evaluation order: @@ -1212,6 +1209,33 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ return(false); } +bool check_and_perform_valor(Field* fd, CardStatus* src_status) +{ + unsigned valor_value = src_status->skill(); + if (valor_value > 0 && skill_check(fd, src_status, nullptr)) + { + src_status->m_valored = true; + unsigned opponent_player = opponent(src_status->m_player); + const CardStatus * dst_status = fd->players[opponent_player]->assaults.size() <= src_status->m_index ? + &fd->players[opponent_player]->assaults[src_status->m_index] : + nullptr; + if (dst_status == nullptr || dst_status->m_hp <= 0) + { + _DEBUG_MSG(1, "%s loses Valor (no blocker)\n", status_description(src_status).c_str()); + return false; + } + else if (attack_power(dst_status) <= attack_power(src_status)) + { + _DEBUG_MSG(1, "%s loses Valor (weak blocker %s)\n", status_description(src_status).c_str(), status_description(dst_status).c_str()); + return false; + } + _DEBUG_MSG(1, "%s activates Valor %u (as if Berserked)\n", status_description(src_status).c_str(), valor_value); + src_status->m_berserk += valor_value; + return true; + } + return false; +} + template size_t select_targets(Field* fd, CardStatus* src_status, const SkillSpec& s) { diff --git a/sim.h b/sim.h index 2dfee642..6c45d7b3 100644 --- a/sim.h +++ b/sim.h @@ -141,22 +141,25 @@ struct CardStatus const Card* m_card; unsigned m_index; unsigned m_player; + unsigned m_delay; + Faction m_faction; + unsigned m_hp; + CardStep m_step; + unsigned m_berserk; unsigned m_corroded_rate; unsigned m_corroded_weakened; - unsigned m_delay; unsigned m_evaded; unsigned m_enfeebled; - Faction m_faction; - unsigned m_hp; unsigned m_inhibited; bool m_jammed; bool m_overloaded; unsigned m_poisoned; unsigned m_protected; unsigned m_rallied; + bool m_valored; unsigned m_weakened; - CardStep m_step; + unsigned m_enhanced_value[num_skills]; unsigned m_skill_cd[num_skills]; @@ -164,7 +167,7 @@ struct CardStatus void set(const Card* card); void set(const Card& card); - std::string description(); + std::string description() const; bool has_skill(Skill skill_id) const; template bool has_skill() const; template unsigned skill() const; From be87fde910586b82ab8736e07c6ae41f9fa64a91 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 20 Sep 2014 20:51:28 +0800 Subject: [PATCH 255/406] Fix raid/mission levels. --- deck.cpp | 71 ++++++++++++++++++++++++++------------------------------ deck.h | 25 +++++++++++--------- sim.cpp | 7 +++--- tyrant.h | 2 +- xml.cpp | 26 ++++++++++++--------- 5 files changed, 66 insertions(+), 65 deletions(-) diff --git a/deck.cpp b/deck.cpp index 4df956cd..fe21d379 100644 --- a/deck.cpp +++ b/deck.cpp @@ -284,20 +284,20 @@ namespace range = boost::range; void Deck::set(const std::vector& ids, const std::map &marks) { - commander = nullptr; + base_commander = nullptr; strategy = DeckStrategy::random; for(auto id: ids) { const Card* card{all_cards.by_id(id)}; if(card->m_type == CardType::commander) { - if(commander == nullptr) + if (base_commander == nullptr) { - commander = card; + base_commander = card; } else { - throw std::runtime_error("While constructing a deck: two commanders detected (" + card->m_name + " and " + commander->m_name + ")"); + throw std::runtime_error("While constructing a deck: two commanders detected (" + card->m_name + " and " + base_commander->m_name + ")"); } } else @@ -305,7 +305,7 @@ void Deck::set(const std::vector& ids, const std::map &m cards.emplace_back(card); } } - if(commander == nullptr) + if (base_commander == nullptr) { throw std::runtime_error("While constructing a deck: no commander found"); } @@ -319,7 +319,7 @@ void Deck::set(const std::string& deck_string_) void Deck::resolve() { - if(commander != nullptr) + if (base_commander != nullptr) { return; } @@ -351,11 +351,11 @@ std::string Deck::hash() const { auto sorted_cards = cards; std::sort(sorted_cards.begin(), sorted_cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); - encode_deck(ios, commander, sorted_cards); + encode_deck(ios, base_commander, sorted_cards); } else { - encode_deck(ios, commander, cards); + encode_deck(ios, base_commander, cards); } return ios.str(); } @@ -381,13 +381,9 @@ std::string Deck::medium_description() const { std::stringstream ios; ios << short_description() << std::endl; - if(commander) + if (base_commander) { - ios << commander->m_name; - if (upgrade_chance > 0 && commander != commander->m_top_level_card) - { - ios << "+"; - } + ios << base_commander->m_name; } else { @@ -396,10 +392,6 @@ std::string Deck::medium_description() const for(const Card * card: cards) { ios << ", " << card->m_name; - if (upgrade_chance > 0 && card != card->m_top_level_card) - { - ios << "+"; - } } unsigned num_pool_cards = 0; for(auto& pool: raid_cards) @@ -410,6 +402,10 @@ std::string Deck::medium_description() const { ios << ", and " << num_pool_cards << " cards from pool"; } + if (upgrade_points > 0) + { + ios << " +" << upgrade_points << "/" << upgrade_opportunities; + } return ios.str(); } @@ -423,9 +419,9 @@ std::string Deck::long_description() const { ios << "Effect: " << effect_names[effect] << "\n"; } - if(commander) + if (base_commander) { - show_upgrades(ios, commander, ""); + show_upgrades(ios, base_commander, ""); } else { @@ -462,7 +458,7 @@ std::string Deck::long_description() const void Deck::show_upgrades(std::stringstream &ios, const Card* card, const char * leading_chars) const { ios << leading_chars << card_description(all_cards, card) << "\n"; - if (upgrade_chance == 0 || card == card->m_top_level_card) + if (upgrade_points == 0 || card == card->m_top_level_card) { return; } @@ -472,7 +468,7 @@ void Deck::show_upgrades(std::stringstream &ios, const Card* card, const char * return; } // nCm * p^m / q^(n-m) - double p = 1.0 * upgrade_chance / upgrade_max_chance; + double p = 1.0 * upgrade_points / upgrade_opportunities; double q = 1.0 - p; unsigned n = card->m_top_level_card->m_level - card->m_level; unsigned m = 0; @@ -538,37 +534,33 @@ const Card* Deck::next() throw std::runtime_error("Unknown strategy for deck."); } -const Card* Deck::upgrade_card(const Card* card, std::mt19937& re) +const Card* Deck::upgrade_card(const Card* card, std::mt19937& re, unsigned &remaining_upgrade_points, unsigned &remaining_upgrade_opportunities) { - unsigned ups = card->m_top_level_card->m_level - card->m_level; - if (upgrade_chance > 0 && ups > 0) + unsigned oppos = card->m_top_level_card->m_level - card->m_level; + if (remaining_upgrade_points > 0) { - for (; ups > 0; -- ups) + for (; oppos > 0; -- oppos) { std::mt19937::result_type rnd = re(); - if (rnd % upgrade_max_chance < upgrade_chance) + if (rnd % remaining_upgrade_opportunities < remaining_upgrade_points) { card = card->upgraded(); + -- remaining_upgrade_points; } + -- remaining_upgrade_opportunities; } } return card; } -const Card* Deck::get_commander(std::mt19937& re) +const Card* Deck::get_commander() { - if (upgrade_chance == 0) - { - return(commander); - } - else - { - return(upgrade_card(commander, re)); - } + return base_commander; } void Deck::shuffle(std::mt19937& re) { + commander = base_commander; shuffled_cards.clear(); boost::insert(shuffled_cards, shuffled_cards.end(), cards); if(!raid_cards.empty()) @@ -584,11 +576,14 @@ void Deck::shuffle(std::mt19937& re) shuffled_cards.insert(shuffled_cards.end(), card_pool.second.begin(), card_pool.second.begin() + card_pool.first); } } - if (upgrade_chance > 0) + if (upgrade_points > 0) { + unsigned remaining_upgrade_points = upgrade_points; + unsigned remaining_upgrade_opportunities = upgrade_opportunities; + commander = upgrade_card(commander, re, remaining_upgrade_points, remaining_upgrade_opportunities); for (auto && card: shuffled_cards) { - card = upgrade_card(card, re); + card = upgrade_card(card, re, remaining_upgrade_points, remaining_upgrade_opportunities); } } if(strategy == DeckStrategy::ordered) diff --git a/deck.h b/deck.h index 16392c9d..659ef6dc 100644 --- a/deck.h +++ b/deck.h @@ -46,15 +46,17 @@ class Deck unsigned id; std::string name; Effect effect; // for quests - unsigned upgrade_chance; // probability chance/max_change to upgrade; = level - 1 for level 1 to (max_level - 1) and 0 for max_level (directly use full upgraded cards) - unsigned upgrade_max_chance; + unsigned upgrade_points; + unsigned upgrade_opportunities; DeckStrategy::DeckStrategy strategy; - const Card* commander; + const Card* base_commander; std::vector cards; - std::map card_marks; // : -1 indicating the commander. E.g, used as a mark to be kept in attacking deck when optimizing. + + const Card* commander; std::deque shuffled_cards; + // card id -> card order std::map> order; std::vector>> raid_cards; @@ -71,17 +73,18 @@ class Deck unsigned id_ = 0, std::string name_ = "", Effect effect_ = Effect::none, - unsigned upgrade_chance_ = 0, - unsigned upgrade_max_chance_ = 1, + unsigned upgrade_points_ = 0, + unsigned upgrade_opportunities_ = 0, DeckStrategy::DeckStrategy strategy_ = DeckStrategy::random) : all_cards(all_cards_), decktype(decktype_), id(id_), name(name_), effect(Effect::none), - upgrade_chance(upgrade_chance_), - upgrade_max_chance(upgrade_max_chance_), + upgrade_points(upgrade_points_), + upgrade_opportunities(upgrade_opportunities_), strategy(strategy_), + base_commander(nullptr), commander(nullptr), mission_req(0) { @@ -96,7 +99,7 @@ class Deck std::vector reward_cards_ = {}, unsigned mission_req_ = 0) { - commander = commander_; + base_commander = commander_; cards = std::vector(std::begin(cards_), std::end(cards_)); raid_cards = std::vector>>(raid_cards_); reward_cards = std::vector(reward_cards_); @@ -121,8 +124,8 @@ class Deck std::string long_description() const; void show_upgrades(std::stringstream &ios, const Card* card, const char * leading_chars) const; const Card* next(); - const Card* upgrade_card(const Card* card, std::mt19937& re); - const Card* get_commander(std::mt19937& re); + const Card* upgrade_card(const Card* card, std::mt19937& re, unsigned &remaining_upgrade_points, unsigned &remaining_upgrade_opportunities); + const Card* get_commander(); void shuffle(std::mt19937& re); void place_at_bottom(const Card* card); }; diff --git a/sim.cpp b/sim.cpp index a680a674..c3b3bebc 100644 --- a/sim.cpp +++ b/sim.cpp @@ -199,8 +199,8 @@ void Hand::reset(std::mt19937& re) { assaults.reset(); structures.reset(); - commander.set(deck->get_commander(re)); deck->shuffle(re); + commander.set(deck->commander); } //---------------------- $40 Game rules implementation ------------------------- // Everything about how a battle plays out, except the following: @@ -588,8 +588,7 @@ void turn_start_phase(Field* fd) cooldown_skills(fd->tap->commander); // Active player's assault cards: // update index - // reduce delay; [TU] reduce skill cooldown - // [WM:T] apply poison damage + // reduce delay; reduce skill cooldown { auto& assaults(fd->tap->assaults); for(unsigned index(0), end(assaults.size()); @@ -608,7 +607,7 @@ void turn_start_phase(Field* fd) } // Active player's structure cards: // update index - // reduce delay; [TU] reduce skill cooldown + // reduce delay; reduce skill cooldown { auto& structures(fd->tap->structures); for(unsigned index(0), end(structures.size()); diff --git a/tyrant.h b/tyrant.h index b925f1e1..78d59e70 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.1.5" +#define TYRANT_OPTIMIZER_VERSION "2.1.6" #include #include diff --git a/xml.cpp b/xml.cpp index 3d15018a..72f58971 100644 --- a/xml.cpp +++ b/xml.cpp @@ -265,8 +265,9 @@ void load_cards_xml(Cards & all_cards, const char * filename) Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const char* effect_node_name, DeckType::DeckType decktype, unsigned id, std::string base_deck_name) { xml_node<>* commander_node(node->first_node("commander")); - unsigned card_id = atoi(commander_node->value()); - const Card* commander_card{all_cards.by_id(card_id)}; + const Card* card = all_cards.by_id(atoi(commander_node->value())); + const Card* commander_card{card}; + unsigned upgrade_opportunities = card->m_top_level_card->m_level - card->m_level; std::vector always_cards; std::vector>> some_cards; std::vector reward_cards; @@ -278,8 +279,9 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch card_node; card_node = card_node->next_sibling("card")) { - card_id = atoi(card_node->value()); - always_cards.push_back(all_cards.by_id(card_id)); + card = all_cards.by_id(atoi(card_node->value())); + always_cards.push_back(card); + upgrade_opportunities += card->m_top_level_card->m_level - card->m_level; } for(xml_node<>* pool_node = deck_node->first_node("card_pool"); pool_node; @@ -287,15 +289,17 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch { unsigned num_cards_from_pool(atoi(pool_node->first_attribute("amount")->value())); std::vector cards_from_pool; - + unsigned upgrade_points = 0; for(xml_node<>* card_node = pool_node->first_node("card"); card_node; card_node = card_node->next_sibling("card")) { - unsigned card_id(atoi(card_node->value())); - cards_from_pool.push_back(all_cards.by_id(card_id)); + card = all_cards.by_id(atoi(card_node->value())); + cards_from_pool.push_back(card); + upgrade_points += card->m_top_level_card->m_level - card->m_level; } some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); + upgrade_opportunities += upgrade_points * num_cards_from_pool / cards_from_pool.size(); } xml_node<>* rewards_node(node->first_node("rewards")); if(decktype == DeckType::mission && rewards_node) @@ -304,8 +308,8 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch card_node; card_node = card_node->next_sibling("card")) { - unsigned card_id(atoi(card_node->value())); - reward_cards.push_back(all_cards.by_id(card_id)); + card = all_cards.by_id(atoi(card_node->value())); + reward_cards.push_back(card); } } xml_node<>* mission_req_node(node->first_node(decktype == DeckType::mission ? "req" : "mission_req")); @@ -316,7 +320,7 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch for (unsigned level = 1; level < max_level; ++ level) { std::string deck_name = base_deck_name + "-" + to_string(level); - decks.decks.push_back(Deck{all_cards, decktype, id, deck_name, effect, level - 1, max_level - 1}); + decks.decks.push_back(Deck{all_cards, decktype, id, deck_name, effect, (upgrade_opportunities + 1) * (level - 1) / (max_level - 1), upgrade_opportunities}); Deck* deck = &decks.decks.back(); deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); decks.by_name[deck_name] = deck; @@ -328,7 +332,7 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); // upgrade cards for full-level missions/raids - deck->commander = deck->commander->m_top_level_card; + deck->base_commander = deck->base_commander->m_top_level_card; for (auto && card: deck->cards) { card = card->m_top_level_card; } for (auto && pool: deck->raid_cards) From 9124cc2428a2b2610db28f5ac55db6696b122e23 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 20 Sep 2014 22:43:32 +0800 Subject: [PATCH 256/406] Fix typo. --- deck.cpp | 33 ++++++++++++++------------------- deck.h | 9 ++++----- sim.cpp | 4 ++-- xml.cpp | 2 +- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/deck.cpp b/deck.cpp index fe21d379..c483642f 100644 --- a/deck.cpp +++ b/deck.cpp @@ -284,20 +284,20 @@ namespace range = boost::range; void Deck::set(const std::vector& ids, const std::map &marks) { - base_commander = nullptr; + commander = nullptr; strategy = DeckStrategy::random; for(auto id: ids) { const Card* card{all_cards.by_id(id)}; if(card->m_type == CardType::commander) { - if (base_commander == nullptr) + if (commander == nullptr) { - base_commander = card; + commander = card; } else { - throw std::runtime_error("While constructing a deck: two commanders detected (" + card->m_name + " and " + base_commander->m_name + ")"); + throw std::runtime_error("While constructing a deck: two commanders detected (" + card->m_name + " and " + commander->m_name + ")"); } } else @@ -305,7 +305,7 @@ void Deck::set(const std::vector& ids, const std::map &m cards.emplace_back(card); } } - if (base_commander == nullptr) + if (commander == nullptr) { throw std::runtime_error("While constructing a deck: no commander found"); } @@ -319,7 +319,7 @@ void Deck::set(const std::string& deck_string_) void Deck::resolve() { - if (base_commander != nullptr) + if (commander != nullptr) { return; } @@ -351,11 +351,11 @@ std::string Deck::hash() const { auto sorted_cards = cards; std::sort(sorted_cards.begin(), sorted_cards.end(), [](const Card* a, const Card* b) { return a->m_id < b->m_id; }); - encode_deck(ios, base_commander, sorted_cards); + encode_deck(ios, commander, sorted_cards); } else { - encode_deck(ios, base_commander, cards); + encode_deck(ios, commander, cards); } return ios.str(); } @@ -381,9 +381,9 @@ std::string Deck::medium_description() const { std::stringstream ios; ios << short_description() << std::endl; - if (base_commander) + if (commander) { - ios << base_commander->m_name; + ios << commander->m_name; } else { @@ -419,9 +419,9 @@ std::string Deck::long_description() const { ios << "Effect: " << effect_names[effect] << "\n"; } - if (base_commander) + if (commander) { - show_upgrades(ios, base_commander, ""); + show_upgrades(ios, commander, ""); } else { @@ -553,14 +553,9 @@ const Card* Deck::upgrade_card(const Card* card, std::mt19937& re, unsigned &rem return card; } -const Card* Deck::get_commander() -{ - return base_commander; -} - void Deck::shuffle(std::mt19937& re) { - commander = base_commander; + shuffled_commander = commander; shuffled_cards.clear(); boost::insert(shuffled_cards, shuffled_cards.end(), cards); if(!raid_cards.empty()) @@ -580,7 +575,7 @@ void Deck::shuffle(std::mt19937& re) { unsigned remaining_upgrade_points = upgrade_points; unsigned remaining_upgrade_opportunities = upgrade_opportunities; - commander = upgrade_card(commander, re, remaining_upgrade_points, remaining_upgrade_opportunities); + shuffled_commander = upgrade_card(commander, re, remaining_upgrade_points, remaining_upgrade_opportunities); for (auto && card: shuffled_cards) { card = upgrade_card(card, re, remaining_upgrade_points, remaining_upgrade_opportunities); diff --git a/deck.h b/deck.h index 659ef6dc..0960c335 100644 --- a/deck.h +++ b/deck.h @@ -50,11 +50,11 @@ class Deck unsigned upgrade_opportunities; DeckStrategy::DeckStrategy strategy; - const Card* base_commander; + const Card* commander; std::vector cards; std::map card_marks; // : -1 indicating the commander. E.g, used as a mark to be kept in attacking deck when optimizing. - const Card* commander; + const Card* shuffled_commander; std::deque shuffled_cards; // card id -> card order @@ -84,8 +84,8 @@ class Deck upgrade_points(upgrade_points_), upgrade_opportunities(upgrade_opportunities_), strategy(strategy_), - base_commander(nullptr), commander(nullptr), + shuffled_commander(nullptr), mission_req(0) { } @@ -99,7 +99,7 @@ class Deck std::vector reward_cards_ = {}, unsigned mission_req_ = 0) { - base_commander = commander_; + commander = commander_; cards = std::vector(std::begin(cards_), std::end(cards_)); raid_cards = std::vector>>(raid_cards_); reward_cards = std::vector(reward_cards_); @@ -125,7 +125,6 @@ class Deck void show_upgrades(std::stringstream &ios, const Card* card, const char * leading_chars) const; const Card* next(); const Card* upgrade_card(const Card* card, std::mt19937& re, unsigned &remaining_upgrade_points, unsigned &remaining_upgrade_opportunities); - const Card* get_commander(); void shuffle(std::mt19937& re); void place_at_bottom(const Card* card); }; diff --git a/sim.cpp b/sim.cpp index c3b3bebc..cee55a74 100644 --- a/sim.cpp +++ b/sim.cpp @@ -200,7 +200,7 @@ void Hand::reset(std::mt19937& re) assaults.reset(); structures.reset(); deck->shuffle(re); - commander.set(deck->commander); + commander.set(deck->shuffled_commander); } //---------------------- $40 Game rules implementation ------------------------- // Everything about how a battle plays out, except the following: @@ -1215,7 +1215,7 @@ bool check_and_perform_valor(Field* fd, CardStatus* src_status) { src_status->m_valored = true; unsigned opponent_player = opponent(src_status->m_player); - const CardStatus * dst_status = fd->players[opponent_player]->assaults.size() <= src_status->m_index ? + const CardStatus * dst_status = fd->players[opponent_player]->assaults.size() > src_status->m_index ? &fd->players[opponent_player]->assaults[src_status->m_index] : nullptr; if (dst_status == nullptr || dst_status->m_hp <= 0) diff --git a/xml.cpp b/xml.cpp index 72f58971..b5164752 100644 --- a/xml.cpp +++ b/xml.cpp @@ -332,7 +332,7 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); // upgrade cards for full-level missions/raids - deck->base_commander = deck->base_commander->m_top_level_card; + deck->commander = deck->commander->m_top_level_card; for (auto && card: deck->cards) { card = card->m_top_level_card; } for (auto && pool: deck->raid_cards) From b4ac287dd825b962d5e8b601bb4a49a208a13381 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 1 Oct 2014 22:03:08 +0800 Subject: [PATCH 257/406] Improve error message for wrong hash. --- deck.cpp | 11 +++++++++++ tyrant_optimize.cpp | 28 +++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/deck.cpp b/deck.cpp index c483642f..3d23d94e 100644 --- a/deck.cpp +++ b/deck.cpp @@ -271,6 +271,17 @@ const std::pair, std::map> string_to_ids(con try { hash_to_ids(deck_string.c_str(), card_ids); + for (auto & card_id: card_ids) + { + try + { + all_cards.by_id(card_id); + } + catch(std::exception& e) + { + throw std::runtime_error(std::string("Deck not found. Error to treat as hash: ") + e.what()); + } + } } catch(std::exception& e) { diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index b3ca133b..8a837904 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -102,7 +102,13 @@ Deck* find_deck(Decks& decks, const Cards& all_cards, std::string deck_name) decks.decks.push_back(Deck{all_cards}); Deck* deck = &decks.decks.back(); deck->set(deck_name); - deck->resolve(); + try + { + deck->resolve(); + } + catch (std::exception & e) + { + } return(deck); } //---------------------- $80 deck optimization --------------------------------- @@ -1525,7 +1531,15 @@ int main(int argc, char** argv) return 0; } } - your_deck->set_given_hand(opt_hand); + try + { + your_deck->set_given_hand(opt_hand); + } + catch(const std::runtime_error& e) + { + std::cerr << "Error: hand " << opt_hand << ": " << e.what() << std::endl; + return 0; + } if (opt_keep_commander) { your_deck->card_marks[-1] = '!'; @@ -1573,7 +1587,15 @@ int main(int argc, char** argv) return 0; } } - enemy_deck->set_given_hand(opt_enemy_hand); + try + { + enemy_deck->set_given_hand(opt_enemy_hand); + } + catch(const std::runtime_error& e) + { + std::cerr << "Error: enemy:hand " << opt_enemy_hand << ": " << e.what() << std::endl; + return 0; + } enemy_decks.push_back(enemy_deck); enemy_decks_factors.push_back(deck_parsed.second); } From 8a959c385b7eaa82dba04c0ce25b0b0beba00a4e Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 10 Oct 2014 16:15:23 +0800 Subject: [PATCH 258/406] Add BGE Reaping. --- sim.cpp | 57 ++++++++++++++++++++++++++++++++++----------- sim.h | 1 + tyrant.cpp | 4 ++++ tyrant.h | 6 ++++- tyrant_optimize.cpp | 22 +++++++++++------ 5 files changed, 68 insertions(+), 22 deletions(-) diff --git a/sim.cpp b/sim.cpp index cee55a74..efd5b7a9 100644 --- a/sim.cpp +++ b/sim.cpp @@ -220,6 +220,25 @@ SkillSpec apply_enhance(const SkillSpec& s, unsigned enhanced_value) return(enahnced_s); } //------------------------------------------------------------------------------ +void prepend_bge_reaping(Field* fd) +{ + if (fd->bg_skill.id != reaping) + { + return; + } + std::vector> od_skills; + for(auto status: fd->killed_with_on_death) + { + SkillSpec ss_heal{heal, fd->bg_skill.x, allfactions, 0, 0, no_skill, true,}; + SkillSpec ss_rally{rally, fd->bg_skill.x, allfactions, 0, 0, no_skill, true,}; + _DEBUG_MSG(2, "Preparing %s skill %s and %s\n", status_description(status).c_str(), skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); + od_skills.emplace_back(status, ss_heal); + od_skills.emplace_back(status, ss_rally); + } + fd->skill_queue.insert(fd->skill_queue.begin(), od_skills.begin(), od_skills.end()); + fd->killed_with_on_death.clear(); +} +//------------------------------------------------------------------------------ void(*skill_table[num_skills])(Field*, CardStatus* src_status, const SkillSpec&); void resolve_skill(Field* fd) { @@ -384,7 +403,7 @@ Results play(Field* fd) } if(__builtin_expect(fd->end, false)) { break; } - if (fd->bg_skill.id != no_skill) + if (fd->bg_skill.id != no_skill && skill_table[fd->bg_skill.id]) { // Evaluate TU Battleground effect (Enhance all) _DEBUG_MSG(2, "Evaluating Battleground skill %s\n", skill_description(fd->cards, fd->bg_skill).c_str()); @@ -540,15 +559,20 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return !c->m_valored; } -void remove_hp(Field* fd, CardStatus& status, unsigned dmg) +void remove_hp(Field* fd, CardStatus* status, unsigned dmg) { - assert(status.m_hp > 0); - _DEBUG_MSG(2, "%s takes %u damage\n", status_description(&status).c_str(), dmg); - status.m_hp = safe_minus(status.m_hp, dmg); - if(status.m_hp == 0) + assert(status->m_hp > 0); + _DEBUG_MSG(2, "%s takes %u damage\n", status_description(status).c_str(), dmg); + status->m_hp = safe_minus(status->m_hp, dmg); + if(status->m_hp == 0) { - _DEBUG_MSG(1, "%s dies\n", status_description(&status).c_str()); - if (status.m_player == 1) + _DEBUG_MSG(1, "%s dies\n", status_description(status).c_str()); + // Assume only assaults get reaping effect + if(fd->bg_skill.id == reaping && status->m_card->m_type == CardType::assault) + { + fd->killed_with_on_death.push_back(status); + } + if (status->m_player == 1) { fd->n_player_kills += 1; } @@ -675,7 +699,7 @@ void turn_end_phase(Field* fd) if(poison_dmg > 0) { _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg); - remove_hp(fd, status, poison_dmg); + remove_hp(fd, &status, poison_dmg); } #if 0 // not need to fade out in own turn in TU @@ -723,6 +747,8 @@ void turn_end_phase(Field* fd) } } #endif + prepend_bge_reaping(fd); + resolve_skill(fd); remove_dead(fd->tap->assaults); remove_dead(fd->tap->structures); remove_dead(fd->tip->assaults); @@ -819,7 +845,7 @@ struct PerformAttack // perform_skill_counter unsigned counter_dmg(counter_damage(fd, att_status, def_status)); _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); - remove_hp(fd, *att_status, counter_dmg); + remove_hp(fd, att_status, counter_dmg); } unsigned berserk_value = att_status->skill(); if(berserk_value > 0 && skill_check(fd, att_status, nullptr)) @@ -837,6 +863,8 @@ struct PerformAttack } do_leech(); } + prepend_bge_reaping(fd); + resolve_skill(fd); } template @@ -883,7 +911,7 @@ struct PerformAttack template void attack_damage() { - remove_hp(fd, *def_status, att_dmg); + remove_hp(fd, def_status, att_dmg); killed_by_attack = def_status->m_hp == 0; } @@ -1039,7 +1067,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* ds template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - return can_attack(dst) && is_active(dst) && !has_attacked(dst); + return can_attack(dst) && (fd->tapi == dst->m_player ? is_active(dst) && !has_attacked(dst) : is_active_next_turn(dst)); } template<> @@ -1097,14 +1125,14 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, co template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - remove_hp(fd, *dst, s.x); + remove_hp(fd, dst, s.x); } template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { unsigned strike_dmg = safe_minus(s.x + dst->m_enfeebled, src->m_overloaded ? 0 : dst->protected_value()); - remove_hp(fd, *dst, strike_dmg); + remove_hp(fd, dst, strike_dmg); } template<> @@ -1270,6 +1298,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski { check_and_perform_skill(fd, src_status, c, s, ! src_status->m_overloaded); } + prepend_bge_reaping(fd); } template diff --git a/sim.h b/sim.h index 6c45d7b3..3b591092 100644 --- a/sim.h +++ b/sim.h @@ -220,6 +220,7 @@ class Field // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque> skill_queue; + std::vector killed_with_on_death; unsigned n_player_kills; enum phase { diff --git a/tyrant.cpp b/tyrant.cpp index 971fdc27..2790c561 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -26,6 +26,10 @@ std::string skill_names[Skill::num_skills] = "Flurry", "Pierce", "Valor", // Damage-Dependant: "Berserk", "Inhibit", "Leech", "Poison", + // Pseudo-Skill for BGE: + "", + "Reaping", + "", }; std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Structure", }; diff --git a/tyrant.h b/tyrant.h index 78d59e70..6bc90dc1 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.1.6" +#define TYRANT_OPTIMIZER_VERSION "2.2.0" #include #include @@ -42,6 +42,10 @@ enum Skill flurry, pierce, valor, // Damage-Dependant: berserk, inhibit, leech, poison, + // Pseudo-Skill for BGE: + BEGIN_BGE_SKILL, + reaping, + END_BGE_SKILL, num_skills }; extern std::string skill_names[num_skills]; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 8a837904..42eb0f82 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1079,15 +1079,24 @@ enum Operation { debuguntil }; //------------------------------------------------------------------------------ +extern void(*skill_table[num_skills])(Field*, CardStatus* src_status, const SkillSpec&); void print_available_effects() { - std::cout << "Available effects besides \" X\":" << std::endl; - for(int i(1); i < Effect::num_effects; ++ i) + std::cout << "Available effects (case-insensitive):\n" + " Enfeeble all X\n" + " Heal all X\n" + " Protect all X\n" + " Rally all X\n" + " Siege all X\n" + " Strike all X\n" + " Weaken all X\n" + " Enhance all X, where is either of Armor, Berserk, Corrosive, Counter, Enfeeble, Evade, Heal, Inhibit, Leech, Pierce, Poison, Protect, Rally, Siege, Strike, Weaken\n" + " Reaping X\n"; + for (int i(1); i < Effect::num_effects; ++ i) { - std::cout << i << " \"" << effect_names[i] << "\"" << std::endl; + std::cout << " " << effect_names[i] << "\n"; } } - void usage(int argc, char** argv) { std::cout << "Tyrant Unleashed Optimizer (TUO) " << TYRANT_OPTIMIZER_VERSION << "\n" @@ -1103,7 +1112,7 @@ void usage(int argc, char** argv) " example: \'fear:0.2;slowroll:0.8\' means fear is the defense deck 20% of the time, while slowroll is the defense deck 80% of the time.\n" "\n" "Flags:\n" - " -e : set the battleground effect. effect is automatically set when applicable.\n" + " -e \"\": set the battleground effect. effect is automatically set when applicable.\n" " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" " -s: use surge (default is fight).\n" " -t : set the number of threads, default is 4.\n" @@ -1130,7 +1139,6 @@ void usage(int argc, char** argv) } std::string skill_description(const Cards& cards, const SkillSpec& s); -extern void(*skill_table[num_skills])(Field*, CardStatus* src_status, const SkillSpec&); int main(int argc, char** argv) { @@ -1440,7 +1448,7 @@ int main(int argc, char** argv) opt_bg_skill.id = skill_name_to_id(tokens[0], false); unsigned skill_index = 1; - if (skill_table[opt_bg_skill.id] != nullptr) + if (skill_table[opt_bg_skill.id] != nullptr || (BEGIN_BGE_SKILL < opt_bg_skill.id && opt_bg_skill.id < END_BGE_SKILL)) { try { From e0451d48cb352095b7c7d4807d204479545caaa4 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 22 Oct 2014 02:42:28 +0800 Subject: [PATCH 259/406] Update Valor skill: triggered before commander acts. --- data/raids.xml | 2 -- sim.cpp | 63 +++++++++++++++++++++++++++----------------------- sim.h | 1 - tyrant.h | 2 +- 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index a2c104c6..ceafd5f3 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -5,7 +5,6 @@ 1 Apocalypse - 25000 1373 26 @@ -31,7 +30,6 @@ 2 Jotun Max - 25000 1413 26 diff --git a/sim.cpp b/sim.cpp index efd5b7a9..fa26b9b9 100644 --- a/sim.cpp +++ b/sim.cpp @@ -95,7 +95,6 @@ inline void CardStatus::set(const Card& card) m_poisoned = 0; m_protected = 0; m_rallied = 0; - m_valored = false; m_weakened = 0; std::memset(m_enhanced_value, 0, sizeof m_enhanced_value); @@ -259,6 +258,7 @@ void resolve_skill(Field* fd) } //------------------------------------------------------------------------------ bool attack_phase(Field* fd); +bool check_and_perform_valor(Field* fd, CardStatus* src_status); void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills) { assert(status); @@ -315,6 +315,10 @@ struct PlayCard status->m_index = storage->size() - 1; status->m_player = fd->tapi; _DEBUG_MSG(1, "%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), static_cast(storage->size() - 1), card_description(fd->cards, card).c_str()); + if (status->m_delay == 0) + { + check_and_perform_valor(fd, status); + } } }; // assault @@ -341,7 +345,6 @@ inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_ //------------------------------------------------------------------------------ void turn_start_phase(Field* fd); void turn_end_phase(Field* fd); -bool check_and_perform_valor(Field* fd, CardStatus* src_status); // return value : (raid points) -> attacker wins, 0 -> defender wins Results play(Field* fd) { @@ -439,7 +442,6 @@ Results play(Field* fd) } else { - check_and_perform_valor(fd, current_status); unsigned num_actions(1); for(unsigned action_index(0); action_index < num_actions; ++action_index) { @@ -553,12 +555,6 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(can_be_healed(c)); } -template<> -inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) -{ - return !c->m_valored; -} - void remove_hp(Field* fd, CardStatus* status, unsigned dmg) { assert(status->m_hp > 0); @@ -568,7 +564,7 @@ void remove_hp(Field* fd, CardStatus* status, unsigned dmg) { _DEBUG_MSG(1, "%s dies\n", status_description(status).c_str()); // Assume only assaults get reaping effect - if(fd->bg_skill.id == reaping && status->m_card->m_type == CardType::assault) + if(fd->bg_skill.id == reaping && status->m_card->m_type != CardType::commander) { fd->killed_with_on_death.push_back(status); } @@ -595,21 +591,21 @@ inline void add_hp(Field* fd, CardStatus* target, unsigned v) { target->m_hp = std::min(target->m_hp + v, target->m_card->m_health); } -void cooldown_skills(CardStatus & status) +void cooldown_skills(CardStatus * status) { - for (const auto & ss : status.m_card->m_skills) + for (const auto & ss : status->m_card->m_skills) { - if (status.m_skill_cd[ss.id] > 0) + if (status->m_skill_cd[ss.id] > 0) { - _DEBUG_MSG(2, "%s reduces timer (%u) of skill %s\n", status_description(&status).c_str(), status.m_skill_cd[ss.id], skill_names[ss.id].c_str()); - -- status.m_skill_cd[ss.id]; + _DEBUG_MSG(2, "%s reduces timer (%u) of skill %s\n", status_description(status).c_str(), status->m_skill_cd[ss.id], skill_names[ss.id].c_str()); + -- status->m_skill_cd[ss.id]; } } } void turn_start_phase(Field* fd) { // Active player's commander card: - cooldown_skills(fd->tap->commander); + cooldown_skills(&fd->tap->commander); // Active player's assault cards: // update index // reduce delay; reduce skill cooldown @@ -619,14 +615,21 @@ void turn_start_phase(Field* fd) index < end; ++index) { - CardStatus& status(assaults[index]); - status.m_index = index; - if(status.m_delay > 0) + CardStatus * status = &assaults[index]; + status->m_index = index; + if(status->m_delay > 0) { - _DEBUG_MSG(1, "%s reduces its timer\n", status_description(&status).c_str()); - -- status.m_delay; + _DEBUG_MSG(1, "%s reduces its timer\n", status_description(status).c_str()); + -- status->m_delay; + if (status->m_delay == 0) + { + check_and_perform_valor(fd, status); + } + } + else + { + cooldown_skills(status); } - cooldown_skills(status); } } // Active player's structure cards: @@ -638,14 +641,17 @@ void turn_start_phase(Field* fd) index < end; ++index) { - CardStatus& status(structures[index]); - status.m_index = index; - if(status.m_delay > 0) + CardStatus * status = &structures[index]; + status->m_index = index; + if(status->m_delay > 0) + { + _DEBUG_MSG(1, "%s reduces its timer\n", status_description(status).c_str()); + --status->m_delay; + } + else { - _DEBUG_MSG(1, "%s reduces its timer\n", status_description(&status).c_str()); - --status.m_delay; + cooldown_skills(status); } - cooldown_skills(status); } } // Defending player's assault cards: @@ -1241,7 +1247,6 @@ bool check_and_perform_valor(Field* fd, CardStatus* src_status) unsigned valor_value = src_status->skill(); if (valor_value > 0 && skill_check(fd, src_status, nullptr)) { - src_status->m_valored = true; unsigned opponent_player = opponent(src_status->m_player); const CardStatus * dst_status = fd->players[opponent_player]->assaults.size() > src_status->m_index ? &fd->players[opponent_player]->assaults[src_status->m_index] : diff --git a/sim.h b/sim.h index 3b591092..f4d570bf 100644 --- a/sim.h +++ b/sim.h @@ -157,7 +157,6 @@ struct CardStatus unsigned m_poisoned; unsigned m_protected; unsigned m_rallied; - bool m_valored; unsigned m_weakened; unsigned m_enhanced_value[num_skills]; diff --git a/tyrant.h b/tyrant.h index 6bc90dc1..10e5dfbf 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.2.0" +#define TYRANT_OPTIMIZER_VERSION "2.2.1" #include #include From 09a8321dea1fa000a13843b0021d06378e6a49ec Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 29 Oct 2014 09:51:13 +0800 Subject: [PATCH 260/406] Update Brawl mode. --- sim.cpp | 2 +- tyrant.cpp | 2 ++ tyrant.h | 4 +++- tyrant_optimize.cpp | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/sim.cpp b/sim.cpp index fa26b9b9..a43ee5a8 100644 --- a/sim.cpp +++ b/sim.cpp @@ -499,7 +499,7 @@ Results play(Field* fd) ++fd->turn; } unsigned raid_damage = 15 + fd->n_player_kills - (10 * fd->players[1]->commander.m_hp / fd->players[1]->commander.m_card->m_health); - unsigned brawl_score = 50 + fd->n_player_kills * 2 - fd->turn / 2; // Note that turn is +1, which is intentional + unsigned brawl_score = 70 - (p1_size - fd->n_player_kills) * 2 - fd->turn / 2; // Note that turn is +1, which is intentional // you lose if(fd->players[0]->commander.m_hp == 0) { diff --git a/tyrant.cpp b/tyrant.cpp index 2790c561..6efa23bf 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -26,6 +26,8 @@ std::string skill_names[Skill::num_skills] = "Flurry", "Pierce", "Valor", // Damage-Dependant: "Berserk", "Inhibit", "Leech", "Poison", + // Triggered: + "Legion", // Pseudo-Skill for BGE: "", "Reaping", diff --git a/tyrant.h b/tyrant.h index 10e5dfbf..1a3b3f97 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.2.1" +#define TYRANT_OPTIMIZER_VERSION "2.3.0" #include #include @@ -42,6 +42,8 @@ enum Skill flurry, pierce, valor, // Damage-Dependant: berserk, inhibit, leech, poison, + // Triggered: + legion, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, reaping, diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 42eb0f82..8329e8ee 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -287,7 +287,7 @@ FinalResults compute_score(const EvaluatedResults& results, std::ve long double max_possible = 100; switch (optimization_mode) { - case OptimizationMode::brawl: max_possible = 60; break; + case OptimizationMode::brawl: max_possible = 70; break; default: max_possible = 100; break; } for(unsigned index(0); index < results.first.size(); ++index) @@ -552,7 +552,7 @@ void thread_evaluate(boost::barrier& main_barrier, long double max_possible = 100; switch (optimization_mode) { - case OptimizationMode::brawl: max_possible = 60; break; + case OptimizationMode::brawl: max_possible = 70; break; default: max_possible = 100; break; } // Get a loose (better than no) upper bound. TODO: Improve it. From 6ddf0d6bdda22e884a6effb7d3461e17b842e021 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 30 Oct 2014 19:01:16 +0800 Subject: [PATCH 261/406] Support raid #3 Forsaken Horde. --- data/raids.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index ceafd5f3..1f5c4a07 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -48,4 +48,25 @@ + + 3 + Forsaken Horde + 1523 + 26 + + + 12051 + 12051 + 12061 + 12061 + 12071 + 12071 + 12081 + 12081 + 12091 + 12091 + + + + From f5a24e3f3887fad75ccac69728d465f17e9750ce Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 30 Oct 2014 19:01:40 +0800 Subject: [PATCH 262/406] Add Legion skill. --- sim.cpp | 38 ++++++++++++++++++++++++++++++++++++++ sim.h | 1 + 2 files changed, 39 insertions(+) diff --git a/sim.cpp b/sim.cpp index a43ee5a8..903336a6 100644 --- a/sim.cpp +++ b/sim.cpp @@ -345,6 +345,7 @@ inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_ //------------------------------------------------------------------------------ void turn_start_phase(Field* fd); void turn_end_phase(Field* fd); +void evaluate_legion(Field* fd); // return value : (raid points) -> attacker wins, 0 -> defender wins Results play(Field* fd) { @@ -414,6 +415,10 @@ Results play(Field* fd) resolve_skill(fd); } + // Evaluate Legion skill + fd->current_phase = Field::legion_phase; + evaluate_legion(fd); + // Evaluate commander fd->current_phase = Field::commander_phase; evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); @@ -760,6 +765,39 @@ void turn_end_phase(Field* fd) remove_dead(fd->tip->assaults); remove_dead(fd->tip->structures); } +void evaluate_legion(Field* fd) +{ + // NOT Honor progenitors + auto& assaults = fd->tap->assaults; + for(fd->current_ci = 0; fd->current_ci < assaults.size(); ++fd->current_ci) + { + CardStatus* status(&assaults[fd->current_ci]); + unsigned legion_base = status->skill(); + if(legion_base == 0) + { + continue; + } + unsigned legion_size(0); + legion_size += status->m_index > 0 && assaults[status->m_index - 1].m_hp > 0 && assaults[status->m_index - 1].m_faction == status->m_faction; + legion_size += status->m_index + 1 < assaults.size() && assaults[status->m_index + 1].m_hp > 0 && assaults[status->m_index + 1].m_faction == status->m_faction; + if(legion_size == 0) + { + continue; + } + // skill_check + bool do_rally = status->m_hp > 0 && (fd->tapi == status->m_player ? is_active(status) : is_active_next_turn(status)); + if (!do_rally) + { + continue; + } + unsigned legion_value = legion_base * legion_size; + _DEBUG_MSG(1, "%s activates Legion %u, rallied by %u\n", status_description(status).c_str(), legion_base, legion_value); + if(do_rally) + { + status->m_rallied += legion_value; + } + } +} //---------------------- $50 attack by assault card implementation ------------- // Counter damage dealt to the attacker (att) by defender (def) // pre-condition: only valid if m_card->m_counter > 0 diff --git a/sim.h b/sim.h index f4d570bf..d5d9c633 100644 --- a/sim.h +++ b/sim.h @@ -224,6 +224,7 @@ class Field enum phase { playcard_phase, + legion_phase, commander_phase, structures_phase, assaults_phase, From 941cdd287d1f0b32df8173b7ec64edcc451bff06 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 30 Oct 2014 20:52:48 +0800 Subject: [PATCH 263/406] Update Legion skill. --- sim.cpp | 56 ++++++++++++++++++-------------------------------------- tyrant.h | 2 +- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/sim.cpp b/sim.cpp index 903336a6..159c415c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -345,7 +345,6 @@ inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_ //------------------------------------------------------------------------------ void turn_start_phase(Field* fd); void turn_end_phase(Field* fd); -void evaluate_legion(Field* fd); // return value : (raid points) -> attacker wins, 0 -> defender wins Results play(Field* fd) { @@ -415,10 +414,6 @@ Results play(Field* fd) resolve_skill(fd); } - // Evaluate Legion skill - fd->current_phase = Field::legion_phase; - evaluate_legion(fd); - // Evaluate commander fd->current_phase = Field::commander_phase; evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); @@ -765,39 +760,6 @@ void turn_end_phase(Field* fd) remove_dead(fd->tip->assaults); remove_dead(fd->tip->structures); } -void evaluate_legion(Field* fd) -{ - // NOT Honor progenitors - auto& assaults = fd->tap->assaults; - for(fd->current_ci = 0; fd->current_ci < assaults.size(); ++fd->current_ci) - { - CardStatus* status(&assaults[fd->current_ci]); - unsigned legion_base = status->skill(); - if(legion_base == 0) - { - continue; - } - unsigned legion_size(0); - legion_size += status->m_index > 0 && assaults[status->m_index - 1].m_hp > 0 && assaults[status->m_index - 1].m_faction == status->m_faction; - legion_size += status->m_index + 1 < assaults.size() && assaults[status->m_index + 1].m_hp > 0 && assaults[status->m_index + 1].m_faction == status->m_faction; - if(legion_size == 0) - { - continue; - } - // skill_check - bool do_rally = status->m_hp > 0 && (fd->tapi == status->m_player ? is_active(status) : is_active_next_turn(status)); - if (!do_rally) - { - continue; - } - unsigned legion_value = legion_base * legion_size; - _DEBUG_MSG(1, "%s activates Legion %u, rallied by %u\n", status_description(status).c_str(), legion_base, legion_value); - if(do_rally) - { - status->m_rallied += legion_value; - } - } -} //---------------------- $50 attack by assault card implementation ------------- // Counter damage dealt to the attacker (att) by defender (def) // pre-condition: only valid if m_card->m_counter > 0 @@ -919,6 +881,24 @@ struct PerformAttack att_dmg = pre_modifier_dmg; std::string desc; // enhance damage + unsigned legion_base = att_status->skill(); + if (legion_base > 0) + { + auto & assaults = fd->tap->assaults; + unsigned legion_size = 0; + legion_size += att_status->m_index > 0 && assaults[att_status->m_index - 1].m_hp > 0 && assaults[att_status->m_index - 1].m_faction == att_status->m_faction; + legion_size += att_status->m_index + 1 < assaults.size() && assaults[att_status->m_index + 1].m_hp > 0 && assaults[att_status->m_index + 1].m_faction == att_status->m_faction; + if (legion_size > 0) + { + // skill_check + if (att_status->m_hp > 0 && is_active(att_status)) + { + unsigned legion_value = legion_base * legion_size; + if (debug_print > 0) { desc += "+" + to_string(legion_value) + "(legion)"; } + att_dmg += legion_value; + } + } + } if(def_status->m_enfeebled > 0) { if(debug_print > 0) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } diff --git a/tyrant.h b/tyrant.h index 1a3b3f97..8782f0ee 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.3.0" +#define TYRANT_OPTIMIZER_VERSION "2.3.1" #include #include From e2769323a89665f649306f6cdbdb939e2dba65bd Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 31 Oct 2014 00:52:42 +0800 Subject: [PATCH 264/406] Add endgame flag: attempt max-level and fused cards only. --- card.h | 2 ++ tyrant_optimize.cpp | 44 ++++++++++++++++++++++++++++++++++++++------ xml.cpp | 2 ++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/card.h b/card.h index 75fc33e1..f8219db0 100644 --- a/card.h +++ b/card.h @@ -16,6 +16,7 @@ class Card unsigned m_health; unsigned m_id; unsigned m_level; + unsigned m_fusion_level; std::string m_name; unsigned m_rarity; unsigned m_set; @@ -36,6 +37,7 @@ class Card m_health(0), m_id(0), m_level(1), + m_fusion_level(0), m_name(""), m_rarity(1), m_set(0), diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 8329e8ee..c8fb6f90 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -52,6 +52,8 @@ namespace { long double target_score{100}; long double min_increment_of_score{0}; long double confidence_level{0.99}; + bool use_top_level_card{false}; + bool use_fused_card_level{0}; bool show_ci{false}; bool show_stdev{false}; bool use_harmonic_mean{false}; @@ -195,11 +197,31 @@ bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, cons } bool is_random = deck->strategy == DeckStrategy::random; std::vector cards = deck->cards; - deck->cards.clear(); - deck->cards.emplace_back(card); - cards_in.emplace_back(is_random ? -1 : to_slot, card); + card = card->m_top_level_card; { - // try to add commander into the deck, defuse/downgrade it if necessary + // try to add new card into the deck, unfuse/downgrade it if necessary + std::stack candidate_cards; + candidate_cards.emplace(card); + while (! candidate_cards.empty()) + { + const Card* card_in = candidate_cards.top(); + candidate_cards.pop(); + deck->cards.clear(); + deck->cards.emplace_back(card_in); + deck_cost = get_deck_cost(deck); + if (use_top_level_card || deck_cost <= fund) + { break; } + for (auto recipe_it : card_in->m_recipe_cards) + { candidate_cards.emplace(recipe_it.first); } + } + if (deck_cost > fund) + { + return false; + } + cards_in.emplace_back(is_random ? -1 : to_slot, deck->cards[0]); + } + { + // try to add commander into the deck, unfuse/downgrade it if necessary std::stack candidate_cards; const Card * old_commander = deck->commander; candidate_cards.emplace(deck->commander); @@ -229,7 +251,7 @@ bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, cons { std::shuffle(cards.begin(), cards.end(), re); } for (signed i = 0; i < (signed)cards.size(); ++ i) { - // try to add cards[i] into the deck, defuse/downgrade it if necessary + // try to add cards[i] into the deck, unfuse/downgrade it if necessary auto saved_cards = deck->cards; auto in_it = deck->cards.end() - (i < to_slot); in_it = deck->cards.insert(in_it, nullptr); @@ -801,10 +823,12 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d std::shuffle(non_commander_cards.begin(), non_commander_cards.end(), re); for(const Card* card_candidate: non_commander_cards) { + if (card_candidate && card_candidate->m_fusion_level < use_fused_card_level) + { continue; } d1->commander = best_commander; d1->cards = best_cards; if (card_candidate ? - (slot_i < best_cards.size() && card_candidate->m_name == best_cards[slot_i]->m_name) // Omega -> Omega + (slot_i < best_cards.size() && card_candidate->m_name == best_cards[slot_i]->m_name) // Omega -> Omega : (slot_i == best_cards.size())) // void -> void { continue; } @@ -951,6 +975,8 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, std::shuffle(non_commander_cards.begin(), non_commander_cards.end(), re); for(const Card* card_candidate: non_commander_cards) { + if (card_candidate && card_candidate->m_fusion_level < use_fused_card_level) + { continue; } // Various checks to check if the card is accepted assert(!card_candidate || card_candidate->m_type != CardType::commander); for(unsigned to_slot(card_candidate ? 0 : best_cards.size() - 1); to_slot < best_cards.size() + (from_slot < best_cards.size() ? 0 : 1); ++to_slot) @@ -1301,6 +1327,12 @@ int main(int argc, char** argv) { opt_enemy_strategy = DeckStrategy::exact_ordered; } + else if (strcmp(argv[argIndex], "endgame") == 0) + { + use_top_level_card = true; + use_fused_card_level = atoi(argv[argIndex+1]); + argIndex += 1; + } else if(strcmp(argv[argIndex], "threads") == 0 || strcmp(argv[argIndex], "-t") == 0) { opt_num_threads = atoi(argv[argIndex+1]); diff --git a/xml.cpp b/xml.cpp index b5164752..b505897f 100644 --- a/xml.cpp +++ b/xml.cpp @@ -163,10 +163,12 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) xml_node<>* set_node(card_node->first_node("set")); int set(set_node ? atoi(set_node->value()) : card->m_set); xml_node<>* level_node(card_node->first_node("level")); + xml_node<>* fusion_level_node(card_node->first_node("fusion_level")); if (id_node) { card->m_base_id = card->m_id = atoi(id_node->value()); } else if (card_id_node) { card->m_id = atoi(card_id_node->value()); } if (name_node) { card->m_name = name_node->value(); } if (level_node) { card->m_level = atoi(level_node->value()); } + if (fusion_level_node) { card->m_fusion_level = atoi(fusion_level_node->value()); } if (id_node) { if (card->m_id < 1000) From 75557e8f08993787dadab06b933b967192d1340f Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 31 Oct 2014 01:20:42 +0800 Subject: [PATCH 265/406] Improve message. --- tyrant_optimize.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index c8fb6f90..bbf99421 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -294,9 +294,9 @@ void claim_cards(const std::vector & card_list) if(num_to_claim > 0) { owned_cards[card->m_id] += num_to_claim; - if(debug_print > 0) + if (debug_print >= 0) { - std::cout << "Claim " << card->m_name << " (" << num_to_claim << ")" << std::endl; + std::cerr << "WARNING: " << card->m_name << " is in your initial deck but not in owned cards: adding " << num_to_claim << " to owned card list.\n"; } } } From 9b61ac11c68bb6f7abe360edd0aaedb9c672bb49 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 31 Oct 2014 02:02:54 +0800 Subject: [PATCH 266/406] Improve warning message: cards in your initial deck are not in ownedcards.txt. --- tyrant_optimize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index bbf99421..73004d43 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -296,7 +296,7 @@ void claim_cards(const std::vector & card_list) owned_cards[card->m_id] += num_to_claim; if (debug_print >= 0) { - std::cerr << "WARNING: " << card->m_name << " is in your initial deck but not in owned cards: adding " << num_to_claim << " to owned card list.\n"; + std::cerr << "WARNING: Need extra " << num_to_claim << " " << card->m_name << " to build your initial deck: adding to owned card list.\n"; } } } From 7ba901873266c11a10568f1cc22372b6f489189e Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sun, 2 Nov 2014 00:41:39 +0800 Subject: [PATCH 267/406] Add Payback skill. --- sim.cpp | 22 ++++++++++++++++++++-- sim.h | 1 + tyrant.cpp | 2 +- tyrant.h | 4 ++-- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/sim.cpp b/sim.cpp index 159c415c..78f36a70 100644 --- a/sim.cpp +++ b/sim.cpp @@ -92,6 +92,7 @@ inline void CardStatus::set(const Card& card) m_inhibited = 0; m_jammed = false; m_overloaded = false; + m_paybacked = 0; m_poisoned = 0; m_protected = 0; m_rallied = 0; @@ -555,6 +556,12 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(can_be_healed(c)); } +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(ref->m_card->m_type == CardType::assault && ref->m_hp > 0); +} + void remove_hp(Field* fd, CardStatus* status, unsigned dmg) { assert(status->m_hp > 0); @@ -733,6 +740,7 @@ void turn_end_phase(Field* fd) status.m_protected = 0; // so far only useful in Defending turn status.m_evaded = 0; + status.m_paybacked = 0; std::memset(status.m_enhanced_value, 0, sizeof status.m_enhanced_value); } } @@ -1317,9 +1325,19 @@ template void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { select_targets(fd, src_status, s); - for (CardStatus * c: fd->selection_array) + for (CardStatus * dst_status: fd->selection_array) { - check_and_perform_skill(fd, src_status, c, s, ! src_status->m_overloaded); + if (check_and_perform_skill(fd, src_status, dst_status, s, ! src_status->m_overloaded)) + { + // Payback + if(dst_status->m_paybacked < dst_status->skill() && skill_check(fd, dst_status, src_status) && + skill_predicate(fd, src_status, src_status, s) && skill_check(fd, src_status, dst_status)) + { + ++ dst_status->m_paybacked; + _DEBUG_MSG(1, "%s Payback %s on %s\n", status_description(dst_status).c_str(), skill_short_description(s).c_str(), status_description(src_status).c_str()); + perform_skill(fd, dst_status, src_status, s); + } + } } prepend_bge_reaping(fd); } diff --git a/sim.h b/sim.h index d5d9c633..e55ddc92 100644 --- a/sim.h +++ b/sim.h @@ -154,6 +154,7 @@ struct CardStatus unsigned m_inhibited; bool m_jammed; bool m_overloaded; + unsigned m_paybacked; unsigned m_poisoned; unsigned m_protected; unsigned m_rallied; diff --git a/tyrant.cpp b/tyrant.cpp index 6efa23bf..912fe2f4 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -20,7 +20,7 @@ std::string skill_names[Skill::num_skills] = "", // Defensive: "", - "Armor", "Corrosive", "Counter", "Evade", "Wall", + "Armor", "Corrosive", "Counter", "Evade", "Payback", "Wall", "", // Combat-Modifier: "Flurry", "Pierce", "Valor", diff --git a/tyrant.h b/tyrant.h index 8782f0ee..08502212 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.3.1" +#define TYRANT_OPTIMIZER_VERSION "2.4.0" #include #include @@ -36,7 +36,7 @@ enum Skill END_ACTIVATION_HELPFUL, // Defensive: BEGIN_DEFENSIVE, - armor, corrosive, counter, evade, wall, + armor, corrosive, counter, evade, payback, wall, END_DEFENSIVE, // Combat-Modifier: flurry, pierce, valor, From 11b90a33adbbfbc65213aaf42872fe3d66bf7c59 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 14 Nov 2014 22:54:09 +0800 Subject: [PATCH 268/406] Support raid #4 Oluth. --- data/raids.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 1f5c4a07..942ff937 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -69,4 +69,25 @@ + + 4 + Oluth + 1545 + 26 + + + 12294 + 12294 + 12304 + 12304 + 12314 + 12314 + 12324 + 12324 + 12334 + 12334 + + + + From d2000cb675368ea1145ab53e444af659fda9f782 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Mon, 17 Nov 2014 00:24:16 +0800 Subject: [PATCH 269/406] Suppress unexpected warning message. --- tyrant_optimize.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 73004d43..b077ed23 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1700,6 +1700,7 @@ int main(int argc, char** argv) min_deck_len = max_deck_len = your_deck->cards.size(); } fund = 0; + debug_print = -1; owned_cards.clear(); claim_cards({your_deck->commander}); claim_cards(your_deck->cards); From e420f74df9143cc36fe3b3009a573a19e9bf4e4d Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 20 Nov 2014 10:23:39 +0800 Subject: [PATCH 270/406] Add BGE Bloodlust. --- Makefile.osx | 4 ++-- sim.cpp | 17 +++++++++++++++-- sim.h | 1 + tyrant.cpp | 2 +- tyrant.h | 2 +- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Makefile.osx b/Makefile.osx index fba69b3e..7ae479b7 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -1,9 +1,9 @@ -MAIN := tu_optimize +MAIN := tuo SRCS := $(wildcard *.cpp) OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) INCS := $(wildcard *.h) -CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -I/usr/local/include -Wno-deprecated-register +CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -I/usr/local/include -Wno-deprecated-register -DNDEBUG LDFLAGS := -lboost_system-mt -lboost_thread-mt -lboost_filesystem-mt -lboost_regex-mt -L/usr/local/lib all: $(MAIN) diff --git a/sim.cpp b/sim.cpp index 78f36a70..a5139d06 100644 --- a/sim.cpp +++ b/sim.cpp @@ -432,6 +432,7 @@ Results play(Field* fd) } // Evaluate assaults fd->current_phase = Field::assaults_phase; + fd->bloodlust_value = 0; for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->assaults.size(); ++fd->current_ci) { // ca: current assault @@ -452,8 +453,15 @@ Results play(Field* fd) // Attack if(can_attack(current_status)) { - attacked = attack_phase(fd) || attacked; - if(__builtin_expect(fd->end, false)) { break; } + if (attack_phase(fd) && !attacked) + { + attacked = true; + if (__builtin_expect(fd->end, false)) { break; } + if (fd->bg_skill.id == bloodlust) + { + fd->bloodlust_value += fd->bg_skill.x; + } + } } else { @@ -907,6 +915,11 @@ struct PerformAttack } } } + if (fd->bloodlust_value > 0) + { + if (debug_print > 0) { desc += "+" + to_string(fd->bloodlust_value) + "(bloodlust)"; } + att_dmg += fd->bloodlust_value; + } if(def_status->m_enfeebled > 0) { if(debug_print > 0) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } diff --git a/sim.h b/sim.h index e55ddc92..ab9b13fa 100644 --- a/sim.h +++ b/sim.h @@ -217,6 +217,7 @@ class Field OptimizationMode optimization_mode; const Effect effect; SkillSpec bg_skill; + unsigned bloodlust_value; // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque> skill_queue; diff --git a/tyrant.cpp b/tyrant.cpp index 912fe2f4..a1eb1f81 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -30,7 +30,7 @@ std::string skill_names[Skill::num_skills] = "Legion", // Pseudo-Skill for BGE: "", - "Reaping", + "Bloodlust", "Reaping", "", }; diff --git a/tyrant.h b/tyrant.h index 08502212..e86271b0 100644 --- a/tyrant.h +++ b/tyrant.h @@ -46,7 +46,7 @@ enum Skill legion, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, - reaping, + bloodlust, reaping, END_BGE_SKILL, num_skills }; From caae795b3b269d9c41f8e3548f4bda7abd6fd920 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 21 Nov 2014 17:09:37 +0800 Subject: [PATCH 271/406] Fix BGE Bloodlust: prevented damage should not count. --- sim.cpp | 33 ++++++++++++++++++--------------- sim.h | 7 +++++-- tyrant.h | 2 +- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/sim.cpp b/sim.cpp index a5139d06..b33c3ae9 100644 --- a/sim.cpp +++ b/sim.cpp @@ -444,6 +444,7 @@ Results play(Field* fd) } else { + fd->assault_bloodlusted = false; unsigned num_actions(1); for(unsigned action_index(0); action_index < num_actions; ++action_index) { @@ -457,10 +458,6 @@ Results play(Field* fd) { attacked = true; if (__builtin_expect(fd->end, false)) { break; } - if (fd->bg_skill.id == bloodlust) - { - fd->bloodlust_value += fd->bg_skill.x; - } } } else @@ -838,10 +835,10 @@ struct PerformAttack {} template - void op() + unsigned op() { unsigned pre_modifier_dmg = attack_power(att_status); - if(pre_modifier_dmg == 0) { return; } + if(pre_modifier_dmg == 0) { return 0; } // Evaluation order: // modify damage @@ -855,11 +852,9 @@ struct PerformAttack if(att_dmg > 0) { attack_damage(); - if(__builtin_expect(fd->end, false)) { return; } + if(__builtin_expect(fd->end, false)) { return att_dmg; } damage_dependant_pre_oa(); - } - if(att_dmg > 0) - { + if(att_status->m_hp > 0) { if(def_status->has_skill() && skill_check(fd, def_status, att_status)) @@ -887,6 +882,7 @@ struct PerformAttack } prepend_bge_reaping(fd); resolve_skill(fd); + return att_dmg; } template @@ -1004,16 +1000,16 @@ void PerformAttack::do_leech() } // General attack phase by the currently evaluated assault, taking into accounts exotic stuff such as flurry,swipe,etc. -void attack_commander(Field* fd, CardStatus* att_status) +unsigned attack_commander(Field* fd, CardStatus* att_status) { CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall if(def_status != nullptr) { - PerformAttack{fd, att_status, def_status}.op(); + return PerformAttack{fd, att_status, def_status}.op(); } else { - PerformAttack{fd, att_status, &fd->tip->commander}.op(); + return PerformAttack{fd, att_status, &fd->tip->commander}.op(); } } // Return true if actually attacks @@ -1026,14 +1022,21 @@ bool attack_phase(Field* fd) return false; } + unsigned att_dmg = 0; if (alive_assault(def_assaults, fd->current_ci)) { - PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); + att_dmg = PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); } else { // might be blocked by walls - attack_commander(fd, att_status); + att_dmg = attack_commander(fd, att_status); + } + + if (att_dmg > 0 && !fd->assault_bloodlusted && fd->bg_skill.id == bloodlust) + { + fd->bloodlust_value += fd->bg_skill.x; + fd->assault_bloodlusted = true; } return true; diff --git a/sim.h b/sim.h index ab9b13fa..02770097 100644 --- a/sim.h +++ b/sim.h @@ -217,12 +217,13 @@ class Field OptimizationMode optimization_mode; const Effect effect; SkillSpec bg_skill; - unsigned bloodlust_value; // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque> skill_queue; std::vector killed_with_on_death; unsigned n_player_kills; + bool assault_bloodlusted; + unsigned bloodlust_value; enum phase { playcard_phase, @@ -250,7 +251,9 @@ class Field optimization_mode(optimization_mode_), effect(effect_), bg_skill(bg_skill_), - n_player_kills(0) + n_player_kills(0), + assault_bloodlusted(false), + bloodlust_value(0) { } diff --git a/tyrant.h b/tyrant.h index e86271b0..afea572e 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.4.0" +#define TYRANT_OPTIMIZER_VERSION "2.4.1" #include #include From 3dc70d07d424bb813c4627649399bd07aebb43be Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 12 Dec 2014 08:36:34 +0800 Subject: [PATCH 272/406] Support Raid #5 Carnifex --- data/raids.xml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 942ff937..1ab8e760 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -90,4 +90,29 @@ + + 5 + Carnifex + 1571 + 26 + + + 13102 + 13112 + 13122 + 13132 + 13142 + 8247 + + + 13102 + 13112 + 13122 + 13132 + 13142 + 8247 + + + + From e1b06637e2daf27cf0c0e90048ad461ee47abc22 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 12 Dec 2014 10:44:07 +0800 Subject: [PATCH 273/406] Support CSV files for customdecks.txt. --- read.cpp | 2 +- tyrant.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/read.cpp b/read.cpp index 60f91f28..bedfed8b 100644 --- a/read.cpp +++ b/read.cpp @@ -308,7 +308,7 @@ unsigned read_custom_decks(Decks& decks, Cards& all_cards, std::string filename) continue; } std::string deck_name; - auto deck_string_iter = read_token(deck_string.begin(), deck_string.end(), [](char c){return(strchr(":", c));}, deck_name); + auto deck_string_iter = read_token(deck_string.begin(), deck_string.end(), [](char c){return(strchr(":,", c));}, deck_name); if(deck_string_iter == deck_string.end() || deck_name.empty()) { std::cerr << "Error in custom deck file " << filename << " at line " << num_line << ", could not read the deck name.\n"; diff --git a/tyrant.h b/tyrant.h index afea572e..e119236e 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.4.1" +#define TYRANT_OPTIMIZER_VERSION "2.4.2" #include #include From cedf84456b7dea00e71a026a8a264a88eb2285bf Mon Sep 17 00:00:00 2001 From: mjdoom Date: Thu, 18 Dec 2014 00:37:36 -0500 Subject: [PATCH 274/406] Add tw flag for simming guild war offense and defense simultaneously --- sim.cpp | 3 +- sim.h | 4 + tyrant.h | 2 + tyrant_optimize.cpp | 337 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 278 insertions(+), 68 deletions(-) diff --git a/sim.cpp b/sim.cpp index b33c3ae9..a842a272 100644 --- a/sim.cpp +++ b/sim.cpp @@ -532,7 +532,8 @@ Results play(Field* fd) _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); switch (fd->optimization_mode) { - case OptimizationMode::defense: return {1, 1, 0, 100, 0}; + //MDJ: Bug returning a win and a loss in defense mode? + case OptimizationMode::defense: return {0, 1, 0, 100, 0}; case OptimizationMode::raid: return {0, 1, 0, raid_damage, 0}; case OptimizationMode::brawl: return {0, 1, 0, 5, 0}; default: return {0, 1, 0, 0, 0}; diff --git a/sim.h b/sim.h index 02770097..42a3db6c 100644 --- a/sim.h +++ b/sim.h @@ -54,6 +54,10 @@ struct FinalResults result_type wins; result_type draws; result_type losses; + //MDJ + result_type wins2; + result_type draws2; + result_type losses2; result_type points; result_type sq_points; result_type points_lower_bound; diff --git a/tyrant.h b/tyrant.h index e119236e..5e7b82e9 100644 --- a/tyrant.h +++ b/tyrant.h @@ -95,12 +95,14 @@ enum gamemode_t surge, }; +//MDJ enum class OptimizationMode { notset, winrate, defense, war, + totalwar, brawl, raid, }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index b077ed23..a49116fd 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -44,6 +44,8 @@ namespace { gamemode_t gamemode{fight}; OptimizationMode optimization_mode{OptimizationMode::notset}; + //MDJ + std::string opt_forts, opt_enemy_forts; std::map owned_cards; bool use_owned_cards{true}; unsigned min_deck_len{1}; @@ -305,51 +307,116 @@ void claim_cards(const std::vector & card_list) //------------------------------------------------------------------------------ FinalResults compute_score(const EvaluatedResults& results, std::vector& factors) { - FinalResults final{0, 0, 0, 0, 0, 0, 0, results.second}; + FinalResults final{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, results.second}; long double max_possible = 100; switch (optimization_mode) { case OptimizationMode::brawl: max_possible = 70; break; default: max_possible = 100; break; } - for(unsigned index(0); index < results.first.size(); ++index) - { - final.wins += results.first[index].wins * factors[index]; - final.draws += results.first[index].draws * factors[index]; - final.losses += results.first[index].losses * factors[index]; - auto lower_bound = boost::math::binomial_distribution<>::find_lower_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; - auto upper_bound = boost::math::binomial_distribution<>::find_upper_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; - if(use_harmonic_mean) - { - final.points += factors[index] / results.first[index].points; - final.points_lower_bound += factors[index] / lower_bound; - final.points_upper_bound += factors[index] / upper_bound; - } - else - { - final.points += results.first[index].points * factors[index]; - final.points_lower_bound += lower_bound * factors[index]; - final.points_upper_bound += upper_bound * factors[index]; - } - final.sq_points += results.first[index].sq_points * factors[index] * factors[index]; - } - long double factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); - final.wins /= factor_sum * (long double)results.second; - final.draws /= factor_sum * (long double)results.second; - final.losses /= factor_sum * (long double)results.second; - if(use_harmonic_mean) - { - final.points = factor_sum / ((long double)results.second * final.points); - final.points_lower_bound = factor_sum / final.points_lower_bound; - final.points_upper_bound = factor_sum / final.points_upper_bound; - } - else - { - final.points /= factor_sum * (long double)results.second; - final.points_lower_bound /= factor_sum; - final.points_upper_bound /= factor_sum; - } - final.sq_points /= factor_sum * factor_sum * (long double)results.second; + if (optimization_mode == OptimizationMode::totalwar) //Double results + { + for (unsigned index(0); index < results.first.size(); ++index) + { + //MDJ (evens are offense and odds are defense) + unsigned findex = 0; + if (index % 2 == 0) + { + final.wins += results.first[index].wins * factors[findex]; + final.draws += results.first[index].draws * factors[findex]; + final.losses += results.first[index].losses * factors[findex]; + } + else + { + final.wins2 += results.first[index].wins * factors[findex]; + final.draws2 += results.first[index].draws * factors[findex]; + final.losses2 += results.first[index].losses * factors[findex]; + } + auto lower_bound = boost::math::binomial_distribution<>::find_lower_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; + auto upper_bound = boost::math::binomial_distribution<>::find_upper_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; + if (use_harmonic_mean) + { + final.points += factors[findex] / results.first[index].points; + final.points_lower_bound += factors[findex] / lower_bound; + final.points_upper_bound += factors[findex] / upper_bound; + } + else + { + final.points += results.first[index].points * factors[findex]; + final.points_lower_bound += lower_bound * factors[findex]; + final.points_upper_bound += upper_bound * factors[findex]; + } + final.sq_points += results.first[index].sq_points * factors[findex] * factors[findex]; + } + long double factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); + final.wins /= factor_sum * (long double)results.second; + final.draws /= factor_sum * (long double)results.second; + final.losses /= factor_sum * (long double)results.second; + final.wins2 /= factor_sum * (long double)results.second; + final.draws2 /= factor_sum * (long double)results.second; + final.losses2 /= factor_sum * (long double)results.second; + //MDJ Twice the results if two sets of sims + long double total_results = results.second; + if (optimization_mode == OptimizationMode::totalwar) + { + total_results *= 2; + } + if (use_harmonic_mean) + { + final.points = factor_sum / (total_results * final.points); + final.points_lower_bound = factor_sum / final.points_lower_bound; + final.points_upper_bound = factor_sum / final.points_upper_bound; + } + else + { + final.points /= factor_sum * total_results; + final.points_lower_bound /= factor_sum; + final.points_upper_bound /= factor_sum; + } + //std::cout << "OWins/ODraws/OLosses/DWins/DDraws/DLosses/Score:" << final.wins << "/" << final.draws << "/" << final.losses << "/" << final.wins2 << "/" << final.draws2 << "/" << final.losses2 << "/" << final.points; + final.sq_points /= factor_sum * factor_sum * total_results; + } + else //No double results + { + for (unsigned index(0); index < results.first.size(); ++index) + { + final.wins += results.first[index].wins * factors[index]; + final.draws += results.first[index].draws * factors[index]; + final.losses += results.first[index].losses * factors[index]; + auto lower_bound = boost::math::binomial_distribution<>::find_lower_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; + auto upper_bound = boost::math::binomial_distribution<>::find_upper_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; + if (use_harmonic_mean) + { + final.points += factors[index] / results.first[index].points; + final.points_lower_bound += factors[index] / lower_bound; + final.points_upper_bound += factors[index] / upper_bound; + } + else + { + final.points += results.first[index].points * factors[index]; + final.points_lower_bound += lower_bound * factors[index]; + final.points_upper_bound += upper_bound * factors[index]; + } + final.sq_points += results.first[index].sq_points * factors[index] * factors[index]; + } + long double factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); + final.wins /= factor_sum * (long double)results.second; + final.draws /= factor_sum * (long double)results.second; + final.losses /= factor_sum * (long double)results.second; + if (use_harmonic_mean) + { + final.points = factor_sum / ((long double)results.second * final.points); + final.points_lower_bound = factor_sum / final.points_lower_bound; + final.points_upper_bound = factor_sum / final.points_upper_bound; + } + else + { + final.points /= factor_sum * (long double)results.second; + final.points_lower_bound /= factor_sum; + final.points_upper_bound /= factor_sum; + } + final.sq_points /= factor_sum * factor_sum * (long double)results.second; + } return final; } //------------------------------------------------------------------------------ @@ -422,6 +489,59 @@ struct SimulationData Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, effect != Effect::none ? effect : enemy_hand->deck->effect, bg_skill); Results result(play(&fd)); res.emplace_back(result); + //MDJ + if (optimization_mode == OptimizationMode::totalwar) + { + if (!opt_enemy_forts.empty()) + { + try + { + your_hand.deck->set_forts(opt_enemy_forts + ","); + } + catch (const std::runtime_error& e) + { + std::cerr << "Error: ef " << opt_enemy_forts << ": " << e.what() << std::endl; + } + } + if (!opt_forts.empty()) + { + try + { + enemy_hand->deck->set_forts(opt_forts + ","); + } + catch (const std::runtime_error& e) + { + std::cerr << "Error: yf " << opt_forts << ": " << e.what() << std::endl; + } + } + your_hand.reset(re); + enemy_hand->reset(re); + Field fd1(re, cards, your_hand, *enemy_hand, gamemode_t::fight, OptimizationMode::defense, effect != Effect::none ? effect : enemy_hand->deck->effect, bg_skill); + Results result1(play(&fd1)); + res.emplace_back(result1); + if (!opt_forts.empty()) + { + try + { + your_hand.deck->set_forts(opt_forts + ","); + } + catch (const std::runtime_error& e) + { + std::cerr << "Error: yf " << opt_forts << ": " << e.what() << std::endl; + } + } + if (!opt_enemy_forts.empty()) + { + try + { + enemy_hand->deck->set_forts(opt_enemy_forts + ","); + } + catch (const std::runtime_error& e) + { + std::cerr << "Error: ef " << opt_enemy_forts << ": " << e.what() << std::endl; + } + } + } } return(res); } @@ -540,6 +660,7 @@ void thread_evaluate(boost::barrier& main_barrier, else { --thread_num_iterations; //! + //MDJ shared_mutex.unlock(); //>>>> std::vector> result{sim.evaluate()}; shared_mutex.lock(); //<<<< @@ -621,27 +742,74 @@ void print_score_info(const EvaluatedResults& results, std::vector& void print_results(const EvaluatedResults& results, std::vector& factors) { auto final = compute_score(results, factors); + //MDJ + if (optimization_mode == OptimizationMode::totalwar) + { + std::cout << "Offense win%: " << final.wins * 100.0 << " ("; + for (unsigned index(0); index < results.first.size(); index = index+2) + { + std::cout << results.first[index].wins << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; - std::cout << "win%: " << final.wins * 100.0 << " ("; - for(const auto & val: results.first) - { - std::cout << val.wins << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; + std::cout << "Offense stall%: " << final.draws * 100.0 << " ("; + for (unsigned index(0); index < results.first.size(); index = index + 2) + { + std::cout << results.first[index].draws << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; - std::cout << "stall%: " << final.draws * 100.0 << " ("; - for(const auto & val: results.first) - { - std::cout << val.draws << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; + std::cout << "Offense loss%: " << final.losses * 100.0 << " ("; + for (unsigned index(0); index < results.first.size(); index = index + 2) + { + std::cout << results.first[index].losses << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; - std::cout << "loss%: " << final.losses * 100.0 << " ("; - for(const auto & val: results.first) - { - std::cout << val.losses << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; + std::cout << "Defense win%: " << final.wins2 * 100.0 << " ("; + for (unsigned index(1); index < results.first.size(); index = index + 2) + { + std::cout << results.first[index].wins << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; + + std::cout << "Defense stall%: " << final.draws2 * 100.0 << " ("; + for (unsigned index(1); index < results.first.size(); index = index + 2) + { + std::cout << results.first[index].draws << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; + + std::cout << "Defense loss%: " << final.losses2 * 100.0 << " ("; + for (unsigned index(1); index < results.first.size(); index = index + 2) + { + std::cout << results.first[index].losses << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; + } + else + { + std::cout << "win%: " << final.wins * 100.0 << " ("; + for (const auto & val : results.first) + { + std::cout << val.wins << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; + + std::cout << "stall%: " << final.draws * 100.0 << " ("; + for (const auto & val : results.first) + { + std::cout << val.draws << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; + + std::cout << "loss%: " << final.losses * 100.0 << " ("; + for (const auto & val : results.first) + { + std::cout << val.losses << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; + } switch(optimization_mode) { @@ -692,7 +860,10 @@ void print_deck_inline(const unsigned deck_cost, const FinalResults break; case OptimizationMode::defense: std::cout << "(" << score.draws * 100.0 << "% stall) "; - break; + break; + case OptimizationMode::totalwar: + std::cout << "(Offense: " << score.wins * 100 << "% win, Defense: " << (score.wins2 + score.draws2) * 100 << "% win/stall) "; + break; default: break; } @@ -729,13 +900,21 @@ void print_deck_inline(const unsigned deck_cost, const FinalResults //------------------------------------------------------------------------------ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) { - EvaluatedResults zero_results{EvaluatedResults::first_type(proc.enemy_decks.size()), 0}; + //MDJ + EvaluatedResults zero_results; + if (optimization_mode == OptimizationMode::totalwar) + { + zero_results = { EvaluatedResults::first_type(proc.enemy_decks.size() * 2), 0 }; + } + else{ + zero_results = { EvaluatedResults::first_type(proc.enemy_decks.size()), 0 }; + } auto best_deck = d1->hash(); std::map evaluated_decks{{best_deck, zero_results}}; EvaluatedResults & results = proc.evaluate(num_min_iterations, evaluated_decks.begin()->second); print_score_info(results, proc.factors); auto current_score = compute_score(results, proc.factors); - auto best_score = current_score; + auto best_score = current_score; // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); @@ -800,8 +979,8 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d skipped_simulations += prev_results.second; } // Evaluate new deck - auto compare_results = proc.compare(best_score.n_sims, prev_results, best_score); - current_score = compute_score(compare_results, proc.factors); + auto compare_results = proc.compare(best_score.n_sims, prev_results, best_score); + current_score = compute_score(compare_results, proc.factors); // Is it better ? if (current_score.points > best_score.points + min_increment_of_score) { @@ -880,7 +1059,15 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d //------------------------------------------------------------------------------ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) { - EvaluatedResults zero_results{EvaluatedResults::first_type(proc.enemy_decks.size()), 0}; + //MDJ + EvaluatedResults zero_results; + if (optimization_mode == OptimizationMode::totalwar) + { + zero_results = { EvaluatedResults::first_type(proc.enemy_decks.size() * 2), 0 }; + } + else{ + zero_results = { EvaluatedResults::first_type(proc.enemy_decks.size()), 0 }; + } auto best_deck = d1->hash(); std::map evaluated_decks{{best_deck, zero_results}}; EvaluatedResults & results = proc.evaluate(num_min_iterations, evaluated_decks.begin()->second); @@ -1182,7 +1369,8 @@ int main(int argc, char** argv) unsigned opt_num_threads(4); DeckStrategy::DeckStrategy opt_your_strategy(DeckStrategy::random); DeckStrategy::DeckStrategy opt_enemy_strategy(DeckStrategy::random); - std::string opt_forts, opt_enemy_forts; + //MDJ + //std::string opt_forts, opt_enemy_forts; std::string opt_hand, opt_enemy_hand; std::vector opt_owned_cards_str_list; std::vector opt_custom_cards_str_list; @@ -1264,6 +1452,12 @@ int main(int argc, char** argv) gamemode = fight; optimization_mode = OptimizationMode::defense; } + //MDJ + else if (strcmp(argv[argIndex], "tw") == 0) + { + gamemode = surge; + optimization_mode = OptimizationMode::totalwar; + } // Others else if (strcmp(argv[argIndex], "keep-commander") == 0 || strcmp(argv[argIndex], "-c") == 0) { @@ -1530,6 +1724,7 @@ int main(int argc, char** argv) std::string enemy_deck_list{argv[2]}; auto && deck_list_parsed = parse_deck_list(enemy_deck_list, decks); + //MDJ Deck* your_deck{nullptr}; std::vector enemy_decks; std::vector enemy_decks_factors; @@ -1587,7 +1782,7 @@ int main(int argc, char** argv) for(auto deck_parsed: deck_list_parsed) { - Deck* enemy_deck{nullptr}; + Deck* enemy_deck{nullptr}; try { enemy_deck = find_deck(decks, all_cards, deck_parsed.first); @@ -1672,8 +1867,16 @@ int main(int argc, char** argv) { switch(std::get<2>(op)) { - case simulate: { - EvaluatedResults results{EvaluatedResults::first_type(enemy_decks.size()), 0}; + case simulate: { + //MDJ + EvaluatedResults results; + if (optimization_mode == OptimizationMode::totalwar) + { + results = { EvaluatedResults::first_type(enemy_decks.size() * 2), 0 }; + } + else{ + results = { EvaluatedResults::first_type(enemy_decks.size()), 0 }; + } results = p.evaluate(std::get<0>(op), results); print_results(results, p.factors); break; From 587329819a03960c8317869fb64bfbb7528970d0 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Fri, 23 Jan 2015 11:27:37 +0800 Subject: [PATCH 275/406] Support Raid #6 Karkinos. --- data/raids.xml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 1ab8e760..12dfe0fc 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -115,4 +115,31 @@ + + 6 + Karkinos + 1620 + 26 + + + 8353 + 8363 + 14167 + 14177 + 14187 + 14197 + 14207 + + + 8353 + 8363 + 14167 + 14177 + 14187 + 14197 + 14207 + + + + From 844901c84c699606b3b4fead3fa9e59a5d5133b4 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Sat, 24 Jan 2015 19:42:09 +0800 Subject: [PATCH 276/406] Update Raid #6 Karkinos. --- data/raids.xml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index 12dfe0fc..a812a88f 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -121,23 +121,19 @@ 1620 26 - + 8353 8363 14167 - 14177 - 14187 - 14197 - 14207 - - - 8353 - 8363 14167 14177 + 14177 14187 + 14187 + 14197 14197 14207 + 14207 From 8dec5e853c299218ea59846ecc17bbad1a1a71d6 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 29 Jan 2015 12:36:29 +0800 Subject: [PATCH 277/406] Fix skill Overload. --- sim.cpp | 4 ++++ tyrant.h | 2 +- tyrant_optimize.cpp | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index a842a272..30168a0e 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1101,6 +1101,10 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* ds } for (const auto & s: dst->m_card->m_skills) { + if (dst->m_skill_cd[s.id] > 0) + { + continue; + } if (BEGIN_ACTIVATION_HARMFUL < s.id && s.id < END_ACTIVATION_HARMFUL) { return true; diff --git a/tyrant.h b/tyrant.h index 5e7b82e9..26a0cea5 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.4.2" +#define TYRANT_OPTIMIZER_VERSION "2.4.3" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index a49116fd..bf45cb13 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -319,7 +319,7 @@ FinalResults compute_score(const EvaluatedResults& results, std::ve for (unsigned index(0); index < results.first.size(); ++index) { //MDJ (evens are offense and odds are defense) - unsigned findex = 0; + unsigned findex = index / 2; if (index % 2 == 0) { final.wins += results.first[index].wins * factors[findex]; From d385ba79c82a2b5c7cfd036989fc29dd03093da2 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 29 Jan 2015 18:18:59 +0800 Subject: [PATCH 278/406] Shrink your deck if too long. --- deck.cpp | 8 ++++++++ deck.h | 1 + tyrant_optimize.cpp | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/deck.cpp b/deck.cpp index 3d23d94e..3274edf2 100644 --- a/deck.cpp +++ b/deck.cpp @@ -339,6 +339,14 @@ void Deck::resolve() deck_string.clear(); } +void Deck::shrink(const unsigned deck_len) +{ + if (cards.size() > deck_len) + { + cards.resize(deck_len); + } +} + void Deck::set_given_hand(const std::string& deck_string) { auto && id_marks = string_to_ids(all_cards, deck_string, "hand"); diff --git a/deck.h b/deck.h index 0960c335..65c797ff 100644 --- a/deck.h +++ b/deck.h @@ -114,6 +114,7 @@ class Deck } void set(const std::string& deck_string_); void resolve(); + void shrink(const unsigned deck_len); void set_given_hand(const std::string& deck_string_); void set_forts(const std::string& deck_string_); diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index bf45cb13..fe7cbcfd 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1753,6 +1753,14 @@ int main(int argc, char** argv) return 0; } + if (your_deck->cards.size() > max_deck_len) + { + your_deck->shrink(max_deck_len); + if (debug_print >= 0) + { + std::cerr << "WARNING: Too many cards in your deck. Trimmed.\n"; + } + } your_deck->strategy = opt_your_strategy; if (!opt_forts.empty()) { From 9c924866f59da9e699c404d040652beb20fe08f0 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Thu, 29 Jan 2015 18:24:28 +0800 Subject: [PATCH 279/406] Add back Lock Card function. --- tyrant_optimize.cpp | 63 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index fe7cbcfd..59abd782 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +62,8 @@ namespace { bool use_harmonic_mean{false}; } +typedef std::unordered_map Requirement; + using namespace std::placeholders; //------------------------------------------------------------------------------ std::string card_id_name(const Card* card) @@ -285,6 +288,28 @@ bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, cons return !cards_in.empty() || !cards_out.empty(); } +bool check_requirements(const Deck* deck, const Requirement & requirement) +{ + if (requirement.empty()) + { + return true; + } + Requirement num_cards; + num_cards[deck->commander] = 1; + for (auto card: deck->cards) + { + ++ num_cards[card]; + } + for (auto it: requirement) + { + if (num_cards[it.first] < it.second) + { + return false; + } + } + return true; +} + void claim_cards(const std::vector & card_list) { std::map num_cards; @@ -898,7 +923,7 @@ void print_deck_inline(const unsigned deck_cost, const FinalResults std::cout << std::endl; } //------------------------------------------------------------------------------ -void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) +void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, Requirement requirement) { //MDJ EvaluatedResults zero_results; @@ -955,7 +980,7 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d { continue; } - if(!card_marks.count(-1)) + if (requirement.count(best_commander) == 0) { for(const Card* commander_candidate: proc.cards.player_commanders) { @@ -969,7 +994,8 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d cards_out.emplace_back(-1, best_commander); cards_out = {{-1, best_commander}}; d1->commander = commander_candidate; - if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in)) + if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in) || + ! check_requirements(d1, requirement)) { continue; } auto && cur_deck = d1->hash(); auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); @@ -1018,7 +1044,8 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d d1->cards.erase(d1->cards.begin() + slot_i); } if (! adjust_deck(d1, slot_i, slot_i, card_candidate, fund, re, deck_cost, cards_out, cards_in) || - d1->cards.size() < min_deck_len) + d1->cards.size() < min_deck_len || + ! check_requirements(d1, requirement)) { continue; } auto && cur_deck = d1->hash(); auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); @@ -1057,7 +1084,7 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d print_deck_inline(get_deck_cost(d1), best_score, d1); } //------------------------------------------------------------------------------ -void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, std::map card_marks) +void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, Requirement requirement) { //MDJ EvaluatedResults zero_results; @@ -1114,7 +1141,7 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, { continue; } - if(!card_marks.count(-1)) + if (requirement.count(best_commander) == 0) { for(const Card* commander_candidate: proc.cards.player_commanders) { @@ -1129,7 +1156,8 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, cards_out.clear(); cards_out.emplace_back(-1, best_commander); d1->commander = commander_candidate; - if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in)) + if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in) || + ! check_requirements(d1, requirement)) { continue; } auto && cur_deck = d1->hash(); auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); @@ -1182,7 +1210,8 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, d1->cards.erase(d1->cards.begin() + from_slot); } if (! adjust_deck(d1, from_slot, to_slot, card_candidate, fund, re, deck_cost, cards_out, cards_in) || - d1->cards.size() < min_deck_len) + d1->cards.size() < min_deck_len || + ! check_requirements(d1, requirement)) { continue; } auto && cur_deck = d1->hash(); auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); @@ -1726,6 +1755,7 @@ int main(int argc, char** argv) //MDJ Deck* your_deck{nullptr}; + Requirement requirement; std::vector enemy_decks; std::vector enemy_decks_factors; @@ -1785,7 +1815,16 @@ int main(int argc, char** argv) } if (opt_keep_commander) { - your_deck->card_marks[-1] = '!'; + requirement[your_deck->commander] = 1; + } + for (auto && card_mark: your_deck->card_marks) + { + auto && card = your_deck->cards[card_mark.first]; + auto mark = card_mark.second; + if (mark == '!') + { + requirement[card] += 1; + } } for(auto deck_parsed: deck_list_parsed) @@ -1893,12 +1932,12 @@ int main(int argc, char** argv) switch (opt_your_strategy) { case DeckStrategy::random: - hill_climbing(std::get<0>(op), std::get<1>(op), your_deck, p, your_deck->card_marks); + hill_climbing(std::get<0>(op), std::get<1>(op), your_deck, p, requirement); break; // case DeckStrategy::ordered: // case DeckStrategy::exact_ordered: default: - hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, your_deck->card_marks); + hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, requirement); break; } break; @@ -1915,7 +1954,7 @@ int main(int argc, char** argv) owned_cards.clear(); claim_cards({your_deck->commander}); claim_cards(your_deck->cards); - hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, your_deck->card_marks); + hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, requirement); break; } case debug: { From bbe87c9257ad40874b32ac666ccab4539562d72d Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 11 Feb 2015 10:25:25 +0800 Subject: [PATCH 280/406] Fix skill Flurry: works for structures. --- sim.cpp | 27 ++++++++++++++++++++++++--- tyrant.h | 2 +- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/sim.cpp b/sim.cpp index 30168a0e..1729b9eb 100644 --- a/sim.cpp +++ b/sim.cpp @@ -424,10 +424,31 @@ Results play(Field* fd) fd->current_phase = Field::structures_phase; for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->structures.size(); ++fd->current_ci) { - CardStatus& current_status(fd->tap->structures[fd->current_ci]); - if(current_status.m_delay == 0 && current_status.m_hp > 0) + CardStatus* current_status(&fd->tap->structures[fd->current_ci]); + if(!is_active(current_status) || !can_act(current_status)) + { + _DEBUG_MSG(2, "Structure %s cannot take action.\n", status_description(current_status).c_str()); + } + else { - evaluate_skills(fd, ¤t_status, current_status.m_card->m_skills); + unsigned num_actions(1); + for(unsigned action_index(0); action_index < num_actions; ++action_index) + { + evaluate_skills(fd, current_status, current_status->m_card->m_skills); + // Flurry + if (can_act(current_status) && fd->tip->commander.m_hp > 0 && current_status->has_skill() && current_status->m_skill_cd[flurry] == 0) + { + _DEBUG_MSG(1, "%s activates Flurry\n", status_description(current_status).c_str()); + num_actions = 2; + for (const auto & ss : current_status->m_card->m_skills) + { + if (ss.id == flurry) + { + current_status->m_skill_cd[flurry] = ss.c; + } + } + } + } } } // Evaluate assaults diff --git a/tyrant.h b/tyrant.h index 26a0cea5..2e9f655c 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.4.3" +#define TYRANT_OPTIMIZER_VERSION "2.4.4" #include #include From 932fbe5b9d50c7671f96b726c72a38dbfe39bee2 Mon Sep 17 00:00:00 2001 From: "9andor@gmail.com" <9andor@gmail.com> Date: Wed, 11 Feb 2015 17:32:43 +0800 Subject: [PATCH 281/406] Fix bug: endgame should test maxeded cards only. --- tyrant_optimize.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 59abd782..01487ed3 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -56,7 +56,7 @@ namespace { long double min_increment_of_score{0}; long double confidence_level{0.99}; bool use_top_level_card{false}; - bool use_fused_card_level{0}; + unsigned use_fused_card_level{0}; bool show_ci{false}; bool show_stdev{false}; bool use_harmonic_mean{false}; @@ -268,7 +268,7 @@ bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, cons candidate_cards.pop(); *in_it = card_in; deck_cost = get_deck_cost(deck); - if (deck_cost <= fund) + if (use_top_level_card || deck_cost <= fund) { break; } for (auto recipe_it : card_in->m_recipe_cards) { candidate_cards.emplace(recipe_it.first); } @@ -1028,7 +1028,7 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d std::shuffle(non_commander_cards.begin(), non_commander_cards.end(), re); for(const Card* card_candidate: non_commander_cards) { - if (card_candidate && card_candidate->m_fusion_level < use_fused_card_level) + if (card_candidate && (card_candidate->m_fusion_level < use_fused_card_level || (use_top_level_card && card_candidate->m_level < card_candidate->m_top_level_card->m_level))) { continue; } d1->commander = best_commander; d1->cards = best_cards; @@ -1190,7 +1190,7 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, std::shuffle(non_commander_cards.begin(), non_commander_cards.end(), re); for(const Card* card_candidate: non_commander_cards) { - if (card_candidate && card_candidate->m_fusion_level < use_fused_card_level) + if (card_candidate && (card_candidate->m_fusion_level < use_fused_card_level || (use_top_level_card && card_candidate->m_level < card_candidate->m_top_level_card->m_level))) { continue; } // Various checks to check if the card is accepted assert(!card_candidate || card_candidate->m_type != CardType::commander); From 826ea09dfd576134e96ed5b9493c1ad779e29344 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 19 Feb 2015 22:24:05 +0800 Subject: [PATCH 282/406] Fix skill Payback (Issue #22) --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 1729b9eb..f2b12e88 100644 --- a/sim.cpp +++ b/sim.cpp @@ -735,7 +735,7 @@ void turn_end_phase(Field* fd) status.m_weakened = 0; status.m_step = CardStep::none; status.m_inhibited = 0; - unsigned poison_dmg = safe_minus(status.m_poisoned, status.protected_value()); + unsigned poison_dmg = safe_minus(status.m_poisoned + (status.m_poisoned ? status.m_enfeebled : 0), status.protected_value()); if(poison_dmg > 0) { _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg); From 7e3487d89023e57760e45df38b102f05cbd7a0da Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 19 Feb 2015 22:37:01 +0800 Subject: [PATCH 283/406] Support Raid #7 Brood Mother. --- data/raids.xml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index a812a88f..4b3e6e3f 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -138,4 +138,29 @@ + + 7 + Brood Mother + 1652 + 26 + + + 14810 + 14820 + 14830 + 14840 + 14850 + 14860 + + + 14810 + 14820 + 14830 + 14840 + 14850 + 14860 + + + + From ec4933d9e4ca9f02582fb926342c0d02a7b4f229 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 19 Feb 2015 22:37:52 +0800 Subject: [PATCH 284/406] Version 2.4.5. --- tyrant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tyrant.h b/tyrant.h index 2e9f655c..7ac63811 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.4.4" +#define TYRANT_OPTIMIZER_VERSION "2.4.5" #include #include From d7d4efe2d75806167e909d2360aa3466f4880c3a Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 20 Feb 2015 21:48:19 +0800 Subject: [PATCH 285/406] Fix bug: "Lock" commander. --- tyrant_optimize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 01487ed3..5759dc41 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1819,7 +1819,7 @@ int main(int argc, char** argv) } for (auto && card_mark: your_deck->card_marks) { - auto && card = your_deck->cards[card_mark.first]; + auto && card = card_mark.first < 0 ? your_deck->commander : your_deck->cards[card_mark.first]; auto mark = card_mark.second; if (mark == '!') { From b2bbef18d411731d3561ee8df93ab061fae6e15b Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 24 Feb 2015 23:44:49 +0800 Subject: [PATCH 286/406] Support Campaign. Add flag vip. --- Makefile.osx | 4 +- data/raids.xml | 141 ++++++++++++++++++++++++++++++++++++++++++++ deck.cpp | 9 +++ deck.h | 3 + sim.cpp | 11 +++- tyrant.cpp | 2 +- tyrant.h | 4 +- tyrant_optimize.cpp | 29 +++++++++ xml.cpp | 12 ++++ 9 files changed, 209 insertions(+), 6 deletions(-) diff --git a/Makefile.osx b/Makefile.osx index 7ae479b7..8ce768a5 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -3,8 +3,8 @@ SRCS := $(wildcard *.cpp) OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) INCS := $(wildcard *.h) -CPPFLAGS := -Wall -Werror -std=gnu++11 -O3 -I/usr/local/include -Wno-deprecated-register -DNDEBUG -LDFLAGS := -lboost_system-mt -lboost_thread-mt -lboost_filesystem-mt -lboost_regex-mt -L/usr/local/lib +CPPFLAGS := -Wall -Werror -std=c++11 -stdlib=libc++ -O3 -I/usr/local/include -Wno-deprecated-register -DNDEBUG +LDFLAGS := -lboost_system-mt -lboost_thread-mt -lboost_filesystem-mt -lboost_regex-mt -L/usr/local/lib -Bstatic all: $(MAIN) diff --git a/data/raids.xml b/data/raids.xml index 4b3e6e3f..38b8c7bc 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -163,4 +163,145 @@ + + + 1 + Tartarus Vanguard 1 + 1187 + 3 + + + 8287 + 7358 + 6869 + 5420 + 5026 + 11029 + 14342 + 7143 + 4616 + + + + + + 2 + Tartarus Vanguard 2 + 1187 + 3 + + + 6081 + 5319 + 14894 + 6769 + 4153 + 14083 + 5641 + 14342 + + + + + + 3 + Tartarus Vanguard 3 + 1662 + 3 + + + 4153 + 13920 + 12994 + 11613 + 5896 + 12470 + 11613 + 14894 + 11859 + + + + + + 4 + Tartarus Vanguard 4 + 1193 + 3 + + + 11511 + 4640 + 14888 + 14702 + 6004 + 14396 + 4555 + 13012 + + + + + + 5 + Tartarus Vanguard 5 + 1193 + 3 + + + 4370 + 5784 + 6615 + 13782 + 5044 + 5992 + 14888 + 7971 + 12778 + + + + + + 6 + Tartarus Vanguard 6 + 1668 + 3 + + + 10884 + 13638 + 12778 + 12326 + 14053 + 8305 + 11102 + 4555 + 7047 + 14888 + + + + + + 7 + Tartarus Vanguard 7 + 1674 + 3 + + + 7293 + 8241 + 11511 + 12204 + 14894 + 7065 + 12482 + 14888 + 13999 + 13764 + + + + diff --git a/deck.cpp b/deck.cpp index 3274edf2..255d65a0 100644 --- a/deck.cpp +++ b/deck.cpp @@ -347,6 +347,15 @@ void Deck::shrink(const unsigned 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); + } +} + void Deck::set_given_hand(const std::string& deck_string) { auto && id_marks = string_to_ids(all_cards, deck_string, "hand"); diff --git a/deck.h b/deck.h index 65c797ff..015bad0b 100644 --- a/deck.h +++ b/deck.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "tyrant.h" #include "card.h" @@ -64,6 +65,7 @@ class Deck unsigned mission_req; std::string deck_string; + std::set vip_cards; std::vector given_hand; std::vector fort_cards; @@ -115,6 +117,7 @@ class Deck void set(const std::string& deck_string_); void resolve(); void shrink(const unsigned deck_len); + void set_vip_cards(const std::string& deck_string_); void set_given_hand(const std::string& deck_string_); void set_forts(const std::string& deck_string_); diff --git a/sim.cpp b/sim.cpp index f2b12e88..d4e9d67e 100644 --- a/sim.cpp +++ b/sim.cpp @@ -527,6 +527,7 @@ Results play(Field* fd) } unsigned raid_damage = 15 + fd->n_player_kills - (10 * fd->players[1]->commander.m_hp / fd->players[1]->commander.m_card->m_health); unsigned brawl_score = 70 - (p1_size - fd->n_player_kills) * 2 - fd->turn / 2; // Note that turn is +1, which is intentional + unsigned campaign_score = 100 - (std::min(10u, fd->turn / 2) - fd->players[0]->assaults.size() - fd->players[0]->structures.size()) * 10; // ditto // you lose if(fd->players[0]->commander.m_hp == 0) { @@ -545,6 +546,7 @@ Results play(Field* fd) switch (fd->optimization_mode) { case OptimizationMode::brawl: return {1, 0, 0, brawl_score, 0}; + case OptimizationMode::campaign: return {1, 0, 0, campaign_score, 0}; default: return {1, 0, 0, 100, 0}; } } @@ -553,7 +555,6 @@ Results play(Field* fd) _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); switch (fd->optimization_mode) { - //MDJ: Bug returning a win and a loss in defense mode? case OptimizationMode::defense: return {0, 1, 0, 100, 0}; case OptimizationMode::raid: return {0, 1, 0, raid_damage, 0}; case OptimizationMode::brawl: return {0, 1, 0, 5, 0}; @@ -606,6 +607,12 @@ void remove_hp(Field* fd, CardStatus* status, unsigned dmg) { fd->n_player_kills += 1; } + if (status->m_player == 0 && fd->players[0]->deck->vip_cards.count(status->m_card->m_id)) + { + _DEBUG_MSG(1, "%s dies\n", status_description(status).c_str()); + fd->players[0]->commander.m_hp = 0; + fd->end = true; + } } } inline bool is_it_dead(CardStatus& c) @@ -823,7 +830,7 @@ inline bool alive_assault(Storage& assaults, unsigned index) void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg, bool count_points) { - assert(status.m_hp > 0); + //assert(status.m_hp > 0); assert(status.m_card->m_type == CardType::commander); _DEBUG_MSG(2, "%s takes %u damage\n", status_description(&status).c_str(), dmg); status.m_hp = safe_minus(status.m_hp, dmg); diff --git a/tyrant.cpp b/tyrant.cpp index a1eb1f81..d63b7c46 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -41,7 +41,7 @@ std::string rarity_names[6]{"", "common", "rare", "epic", "legendary", "vindicat unsigned upgrade_cost[]{0, 5, 15, 30, 75, 150}; unsigned salvaging_income[][7]{{}, {0, 1, 2, 5}, {0, 5, 10, 15, 20}, {0, 20, 25, 30, 40, 50, 65}, {0, 40, 45, 60, 75, 100, 125}, {0, 80, 85, 100, 125, 175, 250}}; -std::string decktype_names[DeckType::num_decktypes]{"Deck", "Mission", "Raid", "Quest", "Custom Deck", }; +std::string decktype_names[DeckType::num_decktypes]{"Deck", "Mission", "Raid", "Campaign", "Quest", "Custom Deck", }; std::string effect_names[Effect::num_effects] = { "None", diff --git a/tyrant.h b/tyrant.h index 7ac63811..8d27bc5d 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.4.5" +#define TYRANT_OPTIMIZER_VERSION "2.5.0" #include #include @@ -73,6 +73,7 @@ enum DeckType { deck, mission, raid, + campaign, quest, custom_deck, num_decktypes @@ -105,6 +106,7 @@ enum class OptimizationMode totalwar, brawl, raid, + campaign, }; struct true_ {}; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 5759dc41..044eda81 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -752,6 +752,7 @@ void print_score_info(const EvaluatedResults& results, std::vector& switch(optimization_mode) { case OptimizationMode::raid: + case OptimizationMode::campaign: case OptimizationMode::brawl: case OptimizationMode::war: std::cout << val.points << " "; @@ -839,6 +840,7 @@ void print_results(const EvaluatedResults& results, std::vector& fa switch(optimization_mode) { case OptimizationMode::raid: + case OptimizationMode::campaign: case OptimizationMode::brawl: case OptimizationMode::war: std::cout << "score: " << final.points << " ("; @@ -870,6 +872,7 @@ void print_deck_inline(const unsigned deck_cost, const FinalResults switch(optimization_mode) { case OptimizationMode::raid: + case OptimizationMode::campaign: case OptimizationMode::brawl: case OptimizationMode::war: std::cout << "(" << score.wins * 100 << "% win, " << score.draws * 100 << "% stall"; @@ -1401,6 +1404,7 @@ int main(int argc, char** argv) //MDJ //std::string opt_forts, opt_enemy_forts; std::string opt_hand, opt_enemy_hand; + std::string opt_vip; std::vector opt_owned_cards_str_list; std::vector opt_custom_cards_str_list; bool opt_do_optimization(false); @@ -1451,6 +1455,11 @@ int main(int argc, char** argv) optimization_mode = OptimizationMode::raid; } // Mode Package + else if (strcmp(argv[argIndex], "campaign") == 0) + { + gamemode = surge; + optimization_mode = OptimizationMode::campaign; + } else if (strcmp(argv[argIndex], "pvp") == 0) { gamemode = fight; @@ -1601,6 +1610,11 @@ int main(int argc, char** argv) { ++ debug_print; } + else if(strcmp(argv[argIndex], "vip") == 0) + { + opt_vip = argv[argIndex + 1]; + argIndex += 1; + } else if(strcmp(argv[argIndex], "hand") == 0) // set initial hand for test { opt_hand = argv[argIndex + 1]; @@ -1756,6 +1770,7 @@ int main(int argc, char** argv) //MDJ Deck* your_deck{nullptr}; Requirement requirement; + Requirement vip_cards; std::vector enemy_decks; std::vector enemy_decks_factors; @@ -1805,6 +1820,15 @@ int main(int argc, char** argv) } } try + { + your_deck->set_vip_cards(opt_vip); + } + catch(const std::runtime_error& e) + { + std::cerr << "Error: vip " << opt_vip << ": " << e.what() << std::endl; + return 0; + } + try { your_deck->set_given_hand(opt_hand); } @@ -1851,6 +1875,11 @@ int main(int argc, char** argv) { optimization_mode = OptimizationMode::raid; } + else if (enemy_deck->decktype == DeckType::campaign) + { + gamemode = surge; + optimization_mode = OptimizationMode::campaign; + } else { optimization_mode = OptimizationMode::winrate; diff --git a/xml.cpp b/xml.cpp index b505897f..dcfd13d1 100644 --- a/xml.cpp +++ b/xml.cpp @@ -408,6 +408,18 @@ void read_raids(Decks& decks, const Cards& all_cards, std::string filename) std::string deck_name{name_node->value()}; read_deck(decks, all_cards, raid_node, "effect", DeckType::raid, id, deck_name); } + + for(xml_node<>* campaign_node = root->first_node("campaign"); + campaign_node; + campaign_node = campaign_node->next_sibling("campaign")) + { + xml_node<>* id_node(campaign_node->first_node("id")); + assert(id_node); + unsigned id(id_node ? atoi(id_node->value()) : 0); + xml_node<>* name_node(campaign_node->first_node("name")); + std::string deck_name{name_node->value()}; + read_deck(decks, all_cards, campaign_node, "effect", DeckType::campaign, id, deck_name); + } } //------------------------------------------------------------------------------ From 9da5a0a93b29fc1c40edfcbfd2641d6d46216b76 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 25 Feb 2015 07:10:30 +0800 Subject: [PATCH 287/406] Fix bug: score shorter deck correctly in campaign mode. --- sim.cpp | 2 +- tyrant.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index d4e9d67e..eef49c39 100644 --- a/sim.cpp +++ b/sim.cpp @@ -527,7 +527,7 @@ Results play(Field* fd) } unsigned raid_damage = 15 + fd->n_player_kills - (10 * fd->players[1]->commander.m_hp / fd->players[1]->commander.m_card->m_health); unsigned brawl_score = 70 - (p1_size - fd->n_player_kills) * 2 - fd->turn / 2; // Note that turn is +1, which is intentional - unsigned campaign_score = 100 - (std::min(10u, fd->turn / 2) - fd->players[0]->assaults.size() - fd->players[0]->structures.size()) * 10; // ditto + unsigned campaign_score = 100 - (std::min(p0_size, fd->turn / 2) - fd->players[0]->assaults.size() - fd->players[0]->structures.size()) * 10; // ditto // you lose if(fd->players[0]->commander.m_hp == 0) { diff --git a/tyrant.h b/tyrant.h index 8d27bc5d..eba121fd 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.5.0" +#define TYRANT_OPTIMIZER_VERSION "2.5.1" #include #include From 48bcf58df1f4c88442e8282cc32adc589024e831 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 25 Feb 2015 07:23:05 +0800 Subject: [PATCH 288/406] Fix campaign decks Tartarus Vanguard. --- data/raids.xml | 78 +++++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index 38b8c7bc..ae4366f2 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -171,15 +171,16 @@ 3 - 8287 - 7358 - 6869 - 5420 + 4358 + 4616 5026 + 5420 + 6869 + 7143 + 8287 11029 14342 - 7143 - 4616 + 14894 @@ -191,14 +192,16 @@ 3 - 6081 + 4153 + 4232 + 4495 5319 - 14894 + 5641 + 6081 6769 - 4153 + 11613 14083 - 5641 - 14342 + 14894 @@ -210,15 +213,16 @@ 3 - 4153 - 13920 - 12994 - 11613 + 2227 5896 - 12470 11613 - 14894 + 11613 11859 + 12470 + 12994 + 13398 + 13920 + 14894 @@ -230,14 +234,16 @@ 3 - 11511 + 4555 4640 - 14888 - 14702 6004 - 14396 - 4555 + 6004 + 8329 + 11511 13012 + 14396 + 14702 + 14888 @@ -250,14 +256,15 @@ 4370 - 5784 - 6615 - 13782 5044 + 5784 5992 - 14888 + 6615 + 7509 7971 12778 + 13782 + 14888 @@ -269,15 +276,14 @@ 3 + 4555 + 8305 10884 - 13638 + 11102 12778 - 12326 + 13236 + 13638 14053 - 8305 - 11102 - 4555 - 7047 14888 @@ -290,16 +296,16 @@ 3 + 7065 7293 8241 11511 12204 - 14894 - 7065 12482 - 14888 - 13999 13764 + 13999 + 14888 + 14894 From 111424118565d5e711b865e3ac171a6fb6f277c1 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 26 Feb 2015 11:28:01 +0800 Subject: [PATCH 289/406] Fix level 6 deck of Tartarus Vanguard. --- data/raids.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/raids.xml b/data/raids.xml index ae4366f2..0cb429db 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -277,6 +277,7 @@ 4555 + 7047 8305 10884 11102 From b0c65976a137b5b6cec0240b97c9f6b20e046f09 Mon Sep 17 00:00:00 2001 From: David Holmer Date: Thu, 1 Jan 2015 15:50:00 -0500 Subject: [PATCH 290/406] Fix segfault when initial deck has no commander Helpful error message printed instead. Before: $ ./tuo.exe "Dreamhaunter, Xillanail, Ezamit Tranq" "106. World's End-9" ordered climb 1000 -o="" Segmentation fault (core dumped) After: $ ./tuo.exe "Dreamhaunter, Xillanail, Ezamit Tranq" "106. World's End-9" ordered climb 1000 -o="" Error: Deck Dreamhaunter, Xillanail, Ezamit Tranq: While constructing a deck: no commander found --- tyrant_optimize.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 044eda81..d5882c16 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -109,13 +109,7 @@ Deck* find_deck(Decks& decks, const Cards& all_cards, std::string deck_name) decks.decks.push_back(Deck{all_cards}); Deck* deck = &decks.decks.back(); deck->set(deck_name); - try - { - deck->resolve(); - } - catch (std::exception & e) - { - } + deck->resolve(); return(deck); } //---------------------- $80 deck optimization --------------------------------- From 9894e6f0b44094c3641a446baec5155f88e85a7b Mon Sep 17 00:00:00 2001 From: David Holmer Date: Thu, 1 Jan 2015 15:55:43 -0500 Subject: [PATCH 291/406] Warning instead of error on multiple commanders in initial deck First commander listed is used for simulation. WARNING is printed for additional commanders. Example: $ ./tuo.exe "Barracus, Dreamhaunter, Xillanail, Nexor, Ezamit Tranq" "106. World's End-9" ordered climb 1000 -o="" WARNING: Ignoring additional commander Nexor (Barracus already in deck) WARNING: Need extra 1 Barracus to build your initial deck: adding to owned card list. WARNING: Need extra 1 Xillanail to build your initial deck: adding to owned card list. WARNING: Need extra 1 Dreamhaunter to build your initial deck: adding to owned card list. WARNING: Need extra 1 Ezamit Tranq to build your initial deck: adding to owned card list. Your Deck: Deck: IFhNpcPnCOr Barracus, Dreamhaunter, Xillanail, Ezamit Tranq Enemy's Deck:1: Mission #106 "106. World's End-9": aFhN3L7fDiTBkYIkRJkNNkMRkTClJIl Constantine, Bolt Crag-1, Fierce Brute-1, Glorious Vigil-1, Longshot Precise-1, Pantheon Disciple-1, Radiant Dawnbringer-3, Ayrkrane Vik-2, Crogall Stout-1, Utopia Lantern-1, Shining Sanctuary-3 +40/45 88.5 (885 / 1000) 88.5: Barracus, Dreamhaunter, Xillanail, Ezamit Tranq Deck improved: IFhcPnNpCOr: 0 [301] Dreamhaunter -> 1 [301] Dreamhaunter: 90.6 (906 / 1000) 90.6: Barracus, Xillanail, Dreamhaunter, Ezamit Tranq Deck improved: IFhCOrcPnNp: 2 [11714] Ezamit Tranq -> 0 [11714] Ezamit Tranq: 95.9 (959 / 1000) 95.9: Barracus, Ezamit Tranq, Xillanail, Dreamhaunter Evaluated 12 decks (11462 + 345764 simulations). Optimized Deck: 95.9: Barracus, Ezamit Tranq, Xillanail, Dreamhaunter --- deck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deck.cpp b/deck.cpp index 255d65a0..b73af91e 100644 --- a/deck.cpp +++ b/deck.cpp @@ -308,7 +308,7 @@ void Deck::set(const std::vector& ids, const std::map &m } else { - throw std::runtime_error("While constructing a deck: two commanders detected (" + card->m_name + " and " + commander->m_name + ")"); + std::cerr << "WARNING: Ignoring additional commander " << card->m_name << " (" << commander->m_name << " already in deck)\n"; } } else From e45b90ad97ecd628a9fd82e1475eb830da51e420 Mon Sep 17 00:00:00 2001 From: David Holmer Date: Mon, 2 Feb 2015 11:53:44 -0500 Subject: [PATCH 292/406] Improve handling of over sized initial decks v2.4.3 introduced a "shrink" function to fix the issue that climb would sometimes result in a >10 card deck when given a >10 card initial deck. Move the call to shrink until AFTER the call to claim_cards has happened. This allows user to pass an over sized deck on purpose to a climb and get the best 10 card deck without having to muck around with setting up owned cards. --- tyrant_optimize.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index d5882c16..4e91e3f2 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1792,14 +1792,6 @@ int main(int argc, char** argv) return 0; } - if (your_deck->cards.size() > max_deck_len) - { - your_deck->shrink(max_deck_len); - if (debug_print >= 0) - { - std::cerr << "WARNING: Too many cards in your deck. Trimmed.\n"; - } - } your_deck->strategy = opt_your_strategy; if (!opt_forts.empty()) { @@ -1912,6 +1904,19 @@ int main(int argc, char** argv) claim_cards(your_deck->cards); } + // shrink any oversized deck to maximum of 10 cards + commander + // NOTE: do this AFTER the call to claim_cards so that passing an initial deck of >10 cards + // can be used as a "shortcut" for adding them to owned cards. Also this allows climb + // to figure out which are the best 10, rather than restricting climb to the first 10. + if (your_deck->cards.size() > max_deck_len) + { + your_deck->shrink(max_deck_len); + if (debug_print >= 0) + { + std::cerr << "WARNING: Too many cards in your deck. Trimmed.\n"; + } + } + if (debug_print >= 0) { std::cout << "Your Deck: " << (debug_print > 0 ? your_deck->long_description() : your_deck->medium_description()) << std::endl; From 39f3a91964a4fcd38411f4324a9af48348544200 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 8 Mar 2015 11:43:37 +0800 Subject: [PATCH 293/406] Update formula of brawl score. --- Makefile.osx | 2 +- sim.cpp | 45 +++++++++++++++++---------------------------- sim.h | 3 +-- tyrant.cpp | 5 ++++- tyrant.h | 6 +++++- tyrant_optimize.cpp | 14 ++------------ 6 files changed, 30 insertions(+), 45 deletions(-) diff --git a/Makefile.osx b/Makefile.osx index 8ce768a5..14683286 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -8,7 +8,7 @@ LDFLAGS := -lboost_system-mt -lboost_thread-mt -lboost_filesystem-mt -lboost_reg all: $(MAIN) -obj/%.o: %.cpp ${INCS} obj +obj/%.o: %.cpp ${INCS} $(CXX) $(CPPFLAGS) -o $@ -c $< $(MAIN): $(OBJS) diff --git a/sim.cpp b/sim.cpp index eef49c39..484cc916 100644 --- a/sim.cpp +++ b/sim.cpp @@ -143,7 +143,7 @@ std::string card_description(const Cards& cards, const Card* c) assert(false); break; } - if(c->m_rarity == 4) { desc += " legendary"; } + if(c->m_rarity >= 4) { desc += " " + rarity_names[c->m_rarity]; } if(c->m_faction != allfactions) { desc += " " + faction_names[c->m_faction]; } for(auto& skill: c->m_skills) { desc += ", " + skill_description(cards, skill); } return(desc); @@ -356,7 +356,6 @@ Results play(Field* fd) fd->tap = fd->players[fd->tapi]; fd->tip = fd->players[fd->tipi]; fd->end = false; - fd->n_player_kills = 0; // Play fortresses for (unsigned _ = 0; _ < 2; ++ _) @@ -369,15 +368,6 @@ Results play(Field* fd) std::swap(fd->tap, fd->tip); } -#if 0 - // ANP: Last decision point is second-to-last card played. - fd->points_since_last_decision = 0; -#endif - unsigned p0_size = fd->players[0]->deck->cards.size(); - unsigned p1_size = fd->players[1]->deck->cards.size(); - fd->players[0]->available_summons = 29 + p0_size; - fd->players[1]->available_summons = 29 + p1_size; - while(__builtin_expect(fd->turn <= turn_limit && !fd->end, true)) { fd->current_phase = Field::playcard_phase; @@ -525,9 +515,8 @@ Results play(Field* fd) std::swap(fd->tap, fd->tip); ++fd->turn; } - unsigned raid_damage = 15 + fd->n_player_kills - (10 * fd->players[1]->commander.m_hp / fd->players[1]->commander.m_card->m_health); - unsigned brawl_score = 70 - (p1_size - fd->n_player_kills) * 2 - fd->turn / 2; // Note that turn is +1, which is intentional - unsigned campaign_score = 100 - (std::min(p0_size, fd->turn / 2) - fd->players[0]->assaults.size() - fd->players[0]->structures.size()) * 10; // ditto + const auto & p = fd->players; + unsigned raid_damage = 15 + (std::min(p[1]->deck->cards.size(), (fd->turn + 1) / 2) - p[1]->assaults.size() - p[1]->structures.size()) - (10 * p[1]->commander.m_hp / p[1]->commander.m_card->m_health); // you lose if(fd->players[0]->commander.m_hp == 0) { @@ -545,8 +534,20 @@ Results play(Field* fd) _DEBUG_MSG(1, "You win.\n"); switch (fd->optimization_mode) { - case OptimizationMode::brawl: return {1, 0, 0, brawl_score, 0}; - case OptimizationMode::campaign: return {1, 0, 0, campaign_score, 0}; + case OptimizationMode::brawl: + { + unsigned brawl_score = 57 + - (10 * (p[0]->commander.m_card->m_health - p[0]->commander.m_hp) / p[0]->commander.m_card->m_health) + + (p[0]->assaults.size() + p[0]->structures.size() + p[0]->deck->shuffled_cards.size()) + - (p[1]->assaults.size() + p[1]->structures.size() + p[1]->deck->shuffled_cards.size()) + - fd->turn / 4; + return {1, 0, 0, brawl_score, 0}; + } + case OptimizationMode::campaign: + { + unsigned campaign_score = 100 - (std::min(p[0]->deck->cards.size(), (fd->turn + 1) / 2) - p[0]->assaults.size() - p[0]->structures.size()) * 10; + return {1, 0, 0, campaign_score, 0}; + } default: return {1, 0, 0, 100, 0}; } } @@ -603,10 +604,6 @@ void remove_hp(Field* fd, CardStatus* status, unsigned dmg) { fd->killed_with_on_death.push_back(status); } - if (status->m_player == 1) - { - fd->n_player_kills += 1; - } if (status->m_player == 0 && fd->players[0]->deck->vip_cards.count(status->m_card->m_id)) { _DEBUG_MSG(1, "%s dies\n", status_description(status).c_str()); @@ -834,14 +831,6 @@ void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg, bool count assert(status.m_card->m_type == CardType::commander); _DEBUG_MSG(2, "%s takes %u damage\n", status_description(&status).c_str(), dmg); status.m_hp = safe_minus(status.m_hp, dmg); - // ANP: If commander is enemy's, player gets points equal to damage. - // Points are awarded for overkill, so it is correct to simply add dmg. - if(count_points && status.m_player == 1) - { -#if 0 - fd->points_since_last_decision += dmg; -#endif - } if(status.m_hp == 0) { _DEBUG_MSG(1, "%s dies\n", status_description(&status).c_str()); diff --git a/sim.h b/sim.h index 42a3db6c..6da8a04d 100644 --- a/sim.h +++ b/sim.h @@ -46,7 +46,7 @@ struct Results } }; -typedef std::pair>, unsigned> EvaluatedResults; +typedef std::pair>, unsigned> EvaluatedResults; template struct FinalResults @@ -198,7 +198,6 @@ class Hand CardStatus commander; Storage assaults; Storage structures; - unsigned available_summons; }; //------------------------------------------------------------------------------ // struct Field is the data model of a battle: diff --git a/tyrant.cpp b/tyrant.cpp index d63b7c46..521f9c76 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -36,11 +36,14 @@ std::string skill_names[Skill::num_skills] = std::string cardtype_names[CardType::num_cardtypes]{"Commander", "Assault", "Structure", }; -std::string rarity_names[6]{"", "common", "rare", "epic", "legendary", "vindicator", }; +std::string rarity_names[6]{"", "common", "rare", "epic", "legend", "vindi", }; unsigned upgrade_cost[]{0, 5, 15, 30, 75, 150}; unsigned salvaging_income[][7]{{}, {0, 1, 2, 5}, {0, 5, 10, 15, 20}, {0, 20, 25, 30, 40, 50, 65}, {0, 40, 45, 60, 75, 100, 125}, {0, 80, 85, 100, 125, 175, 250}}; +signed min_possible_score[]{0, 0, 0, 10, 0, 5, 5, 0}; +signed max_possible_score[]{100, 100, 100, 100, 100, 67, 100, 100}; + std::string decktype_names[DeckType::num_decktypes]{"Deck", "Mission", "Raid", "Campaign", "Quest", "Custom Deck", }; std::string effect_names[Effect::num_effects] = { diff --git a/tyrant.h b/tyrant.h index eba121fd..1a1fd5a2 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.5.1" +#define TYRANT_OPTIMIZER_VERSION "2.5.2" #include #include @@ -107,8 +107,12 @@ enum class OptimizationMode brawl, raid, campaign, + num_optimization_mode }; +extern signed min_possible_score[(size_t)OptimizationMode::num_optimization_mode]; +extern signed max_possible_score[(size_t)OptimizationMode::num_optimization_mode]; + struct true_ {}; struct false_ {}; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 044eda81..7c33ec66 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -333,12 +333,7 @@ void claim_cards(const std::vector & card_list) FinalResults compute_score(const EvaluatedResults& results, std::vector& factors) { FinalResults final{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, results.second}; - long double max_possible = 100; - switch (optimization_mode) - { - case OptimizationMode::brawl: max_possible = 70; break; - default: max_possible = 100; break; - } + long double max_possible = max_possible_score[(size_t)optimization_mode]; if (optimization_mode == OptimizationMode::totalwar) //Double results { for (unsigned index(0); index < results.first.size(); ++index) @@ -717,12 +712,7 @@ void thread_evaluate(boost::barrier& main_barrier, score_accum = thread_score_local[0]; } bool compare_stop(false); - long double max_possible = 100; - switch (optimization_mode) - { - case OptimizationMode::brawl: max_possible = 70; break; - default: max_possible = 100; break; - } + long double max_possible = max_possible_score[(size_t)optimization_mode]; // Get a loose (better than no) upper bound. TODO: Improve it. compare_stop = (boost::math::binomial_distribution<>::find_upper_bound_on_p(thread_total_local, score_accum / max_possible, 1 - confidence_level) * max_possible < thread_best_results->points + min_increment_of_score); From 17dd84262306f4e856ca66870541dda0048cef55 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 9 Mar 2015 09:23:53 +0800 Subject: [PATCH 294/406] Add flag freeze N: freeze first N cards. --- deck.cpp | 68 --------------------------------------------- read.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++ read.h | 1 + tyrant_optimize.cpp | 15 ++++++++-- 4 files changed, 82 insertions(+), 70 deletions(-) diff --git a/deck.cpp b/deck.cpp index b73af91e..336c063f 100644 --- a/deck.cpp +++ b/deck.cpp @@ -223,74 +223,6 @@ void encode_deck_ddd_b64(std::stringstream &ios, const Card* commander, std::vec DeckDecoder hash_to_ids = hash_to_ids_ext_b64; DeckEncoder encode_deck = encode_deck_ext_b64; -const std::pair, std::map> string_to_ids(const Cards& all_cards, const std::string& deck_string, const std::string & description) -{ - std::vector card_ids; - std::map card_marks; - std::vector error_list; - boost::tokenizer> deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; - auto token_iter = deck_tokens.begin(); - signed p = -1; - for(; token_iter != deck_tokens.end(); ++token_iter) - { - std::string card_spec(*token_iter); - unsigned card_id{0}; - unsigned card_num{1}; - char num_sign{0}; - char mark{0}; - try - { - parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign, mark); - assert(num_sign == 0); - for(unsigned i(0); i < card_num; ++i) - { - card_ids.push_back(card_id); - if(mark) { card_marks[p] = mark; } - ++ p; - } - } - catch(std::exception& e) - { - error_list.push_back(e.what()); - continue; - } - } - if (! card_ids.empty()) - { - if (! error_list.empty()) - { - std::cerr << "Warning: Ignore some cards while resolving " << description << ": "; - for (auto error: error_list) - { - std::cerr << '[' << error << ']'; - } - std::cerr << std::endl; - } - return {card_ids, card_marks}; - } - try - { - hash_to_ids(deck_string.c_str(), card_ids); - for (auto & card_id: card_ids) - { - try - { - all_cards.by_id(card_id); - } - catch(std::exception& e) - { - throw std::runtime_error(std::string("Deck not found. Error to treat as hash: ") + e.what()); - } - } - } - catch(std::exception& e) - { - std::cerr << "Error: Failed to resolve " << description << ": " << e.what() << std::endl; - throw; - } - return {card_ids, card_marks}; -} - namespace range = boost::range; void Deck::set(const std::vector& ids, const std::map &marks) diff --git a/read.cpp b/read.cpp index bedfed8b..70e6ee10 100644 --- a/read.cpp +++ b/read.cpp @@ -226,6 +226,74 @@ void parse_card_spec(const Cards& all_cards, std::string& card_spec, unsigned& c } } +const std::pair, std::map> string_to_ids(const Cards& all_cards, const std::string& deck_string, const std::string & description) +{ + std::vector card_ids; + std::map card_marks; + std::vector error_list; + boost::tokenizer> deck_tokens{deck_string, boost::char_delimiters_separator{false, ":,", ""}}; + auto token_iter = deck_tokens.begin(); + signed p = -1; + for(; token_iter != deck_tokens.end(); ++token_iter) + { + std::string card_spec(*token_iter); + unsigned card_id{0}; + unsigned card_num{1}; + char num_sign{0}; + char mark{0}; + try + { + parse_card_spec(all_cards, card_spec, card_id, card_num, num_sign, mark); + assert(num_sign == 0); + for(unsigned i(0); i < card_num; ++i) + { + card_ids.push_back(card_id); + if(mark) { card_marks[p] = mark; } + ++ p; + } + } + catch(std::exception& e) + { + error_list.push_back(e.what()); + continue; + } + } + if (! card_ids.empty()) + { + if (! error_list.empty()) + { + std::cerr << "Warning: Ignore some cards while resolving " << description << ": "; + for (auto error: error_list) + { + std::cerr << '[' << error << ']'; + } + std::cerr << std::endl; + } + return {card_ids, card_marks}; + } + try + { + hash_to_ids(deck_string.c_str(), card_ids); + for (auto & card_id: card_ids) + { + try + { + all_cards.by_id(card_id); + } + catch(std::exception& e) + { + throw std::runtime_error(std::string("Deck not found. Error to treat as hash: ") + e.what()); + } + } + } + catch(std::exception& e) + { + std::cerr << "Error: Failed to resolve " << description << ": " << e.what() << std::endl; + throw; + } + return {card_ids, card_marks}; +} + unsigned read_card_abbrs(Cards& all_cards, const std::string& filename) { if(!boost::filesystem::exists(filename)) diff --git a/read.h b/read.h index 74cc1f7f..82508c54 100644 --- a/read.h +++ b/read.h @@ -14,6 +14,7 @@ class Deck; void load_custom_decks(Decks& decks, Cards& cards, const char * filename); DeckList parse_deck_list(std::string list_string, const Decks& decks); void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark); +const std::pair, std::map> string_to_ids(const Cards& all_cards, const std::string& deck_string, const std::string & description); unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename); void read_owned_cards(Cards& cards, std::map& owned_cards, std::string filename); unsigned read_card_abbrs(Cards& cards, const std::string& filename); diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index fff0e654..70c8b5e0 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -51,6 +51,7 @@ namespace { bool use_owned_cards{true}; unsigned min_deck_len{1}; unsigned max_deck_len{10}; + unsigned freezed_cards{0}; unsigned fund{0}; long double target_score{100}; long double min_increment_of_score{0}; @@ -1102,8 +1103,12 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; std::vector> cards_out, cards_in; - for(unsigned from_slot(0), dead_slot(0); ; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) + for(unsigned from_slot(freezed_cards), dead_slot(freezed_cards); ; from_slot = (from_slot + 1) % std::min(max_deck_len, d1->cards.size() + 1)) { + if (from_slot < freezed_cards) + { + continue; + } if(deck_has_been_improved) { dead_slot = from_slot; @@ -1181,7 +1186,7 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, { continue; } // Various checks to check if the card is accepted assert(!card_candidate || card_candidate->m_type != CardType::commander); - for(unsigned to_slot(card_candidate ? 0 : best_cards.size() - 1); to_slot < best_cards.size() + (from_slot < best_cards.size() ? 0 : 1); ++to_slot) + for(unsigned to_slot(card_candidate ? freezed_cards : best_cards.size() - 1); to_slot < best_cards.size() + (from_slot < best_cards.size() ? 0 : 1); ++to_slot) { d1->commander = best_commander; d1->cards = best_cards; @@ -1490,6 +1495,11 @@ int main(int argc, char** argv) opt_effect = argv[argIndex + 1]; argIndex += 1; } + else if (strcmp(argv[argIndex], "freeze") == 0 || strcmp(argv[argIndex], "-F") == 0) + { + freezed_cards = atoi(argv[argIndex + 1]); + argIndex += 1; + } else if(strcmp(argv[argIndex], "-L") == 0) { min_deck_len = atoi(argv[argIndex + 1]); @@ -1906,6 +1916,7 @@ int main(int argc, char** argv) std::cerr << "WARNING: Too many cards in your deck. Trimmed.\n"; } } + freezed_cards = std::min(freezed_cards, your_deck->cards.size()); if (debug_print >= 0) { From 73a2ff3cd4dd37ccf6ccaece365666f03a1b1337 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 15 Mar 2015 17:44:05 +0800 Subject: [PATCH 295/406] Fix skill Flurry: works for commander. --- sim.cpp | 131 +++++++++++++++++++++++++------------------------------- 1 file changed, 59 insertions(+), 72 deletions(-) diff --git a/sim.cpp b/sim.cpp index 484cc916..47034785 100644 --- a/sim.cpp +++ b/sim.cpp @@ -258,26 +258,70 @@ void resolve_skill(Field* fd) } } //------------------------------------------------------------------------------ +inline bool has_attacked(CardStatus* c) { return(c->m_step == CardStep::attacked); } +inline bool is_jammed(CardStatus* c) { return(c->m_jammed); } +inline bool is_active(CardStatus* c) { return(c->m_delay == 0); } +inline bool is_active_next_turn(CardStatus* c) { return(c->m_delay <= 1); } +inline bool can_act(CardStatus* c) { return(c->m_hp > 0 && !is_jammed(c)); } +inline bool can_attack(CardStatus* c) { return(can_act(c)); } +// Can be healed / repaired +inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health); } +//------------------------------------------------------------------------------ bool attack_phase(Field* fd); bool check_and_perform_valor(Field* fd, CardStatus* src_status); -void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills) +template +void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills, bool* attacked=nullptr) { assert(status); - assert(fd->skill_queue.size() == 0); - for(auto& ss: skills) + unsigned num_actions(1); + for (unsigned action_index(0); action_index < num_actions; ++ action_index) { - if (skill_table[ss.id] == nullptr) + assert(fd->skill_queue.size() == 0); + for (auto & ss: skills) { - continue; + if (skill_table[ss.id] == nullptr) + { + continue; + } + if (status->m_skill_cd[ss.id] > 0) + { + continue; + } + _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, ss).c_str()); + fd->skill_queue.emplace_back(status, ss); + resolve_skill(fd); + if(__builtin_expect(fd->end, false)) { break; } } - if (status->m_skill_cd[ss.id] > 0) + if (type == CardType::assault) { - continue; + // no commander-killing skill yet // if(__builtin_expect(fd->end, false)) { break; } + // Attack + if (can_attack(status)) + { + if (attack_phase(fd) && !*attacked) + { + *attacked = true; + if (__builtin_expect(fd->end, false)) { break; } + } + } + else + { + _DEBUG_MSG(2, "Assault %s cannot take attack.\n", status_description(status).c_str()); + } + } + // Flurry + if (can_act(status) && fd->tip->commander.m_hp > 0 && status->has_skill() && status->m_skill_cd[flurry] == 0) + { + _DEBUG_MSG(1, "%s activates Flurry\n", status_description(status).c_str()); + num_actions = 2; + for (const auto & ss : skills) + { + if (ss.id == flurry) + { + status->m_skill_cd[flurry] = ss.c; + } + } } - _DEBUG_MSG(2, "Evaluating %s skill %s\n", status_description(status).c_str(), skill_description(fd->cards, ss).c_str()); - fd->skill_queue.emplace_back(status, ss); - resolve_skill(fd); - if(__builtin_expect(fd->end, false)) { break; } } } @@ -335,15 +379,6 @@ void PlayCard::setStorage() storage = &fd->tap->structures; } //------------------------------------------------------------------------------ -inline bool has_attacked(CardStatus* c) { return(c->m_step == CardStep::attacked); } -inline bool is_jammed(CardStatus* c) { return(c->m_jammed); } -inline bool is_active(CardStatus* c) { return(c->m_delay == 0); } -inline bool is_active_next_turn(CardStatus* c) { return(c->m_delay <= 1); } -inline bool can_act(CardStatus* c) { return(c->m_hp > 0 && !is_jammed(c)); } -inline bool can_attack(CardStatus* c) { return(can_act(c)); } -// Can be healed / repaired -inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health); } -//------------------------------------------------------------------------------ void turn_start_phase(Field* fd); void turn_end_phase(Field* fd); // return value : (raid points) -> attacker wins, 0 -> defender wins @@ -407,7 +442,7 @@ Results play(Field* fd) // Evaluate commander fd->current_phase = Field::commander_phase; - evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); + evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); if(__builtin_expect(fd->end, false)) { break; } // Evaluate structures @@ -421,24 +456,7 @@ Results play(Field* fd) } else { - unsigned num_actions(1); - for(unsigned action_index(0); action_index < num_actions; ++action_index) - { - evaluate_skills(fd, current_status, current_status->m_card->m_skills); - // Flurry - if (can_act(current_status) && fd->tip->commander.m_hp > 0 && current_status->has_skill() && current_status->m_skill_cd[flurry] == 0) - { - _DEBUG_MSG(1, "%s activates Flurry\n", status_description(current_status).c_str()); - num_actions = 2; - for (const auto & ss : current_status->m_card->m_skills) - { - if (ss.id == flurry) - { - current_status->m_skill_cd[flurry] = ss.c; - } - } - } - } + evaluate_skills(fd, current_status, current_status->m_card->m_skills); } } // Evaluate assaults @@ -456,39 +474,8 @@ Results play(Field* fd) else { fd->assault_bloodlusted = false; - unsigned num_actions(1); - for(unsigned action_index(0); action_index < num_actions; ++action_index) - { - // Evaluate skills - evaluate_skills(fd, current_status, current_status->m_card->m_skills); - // no commander-killing skill yet // if(__builtin_expect(fd->end, false)) { break; } - // Attack - if(can_attack(current_status)) - { - if (attack_phase(fd) && !attacked) - { - attacked = true; - if (__builtin_expect(fd->end, false)) { break; } - } - } - else - { - _DEBUG_MSG(2, "Assault %s cannot take attack.\n", status_description(current_status).c_str()); - } - // Flurry - if (can_act(current_status) && fd->tip->commander.m_hp > 0 && current_status->has_skill() && current_status->m_skill_cd[flurry] == 0) - { - _DEBUG_MSG(1, "%s activates Flurry\n", status_description(current_status).c_str()); - num_actions = 2; - for (const auto & ss : current_status->m_card->m_skills) - { - if (ss.id == flurry) - { - current_status->m_skill_cd[flurry] = ss.c; - } - } - } - } + evaluate_skills(fd, current_status, current_status->m_card->m_skills, &attacked); + if (__builtin_expect(fd->end, false)) { break; } } if (current_status->m_corroded_rate > 0) { From e99259227b1ed1bac2c3677cd65f867dfce24616 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 15 Mar 2015 17:46:03 +0800 Subject: [PATCH 296/406] Support Campaign Brimstone. --- cards.cpp | 2 +- data/raids.xml | 467 ++++++++++++++++++++++++++++++++++++++++++++++++- tyrant.h | 2 +- xml.cpp | 23 ++- 4 files changed, 483 insertions(+), 11 deletions(-) diff --git a/cards.cpp b/cards.cpp index 09c20bf9..6ddc665a 100644 --- a/cards.cpp +++ b/cards.cpp @@ -16,7 +16,7 @@ std::string simplify_name(const std::string& card_name) std::string simple_name; for(auto c : card_name) { - if(!strchr(";:, \"'-", c)) + if(!strchr(";:,\"' ", c)) { simple_name += ::tolower(c); } diff --git a/data/raids.xml b/data/raids.xml index 0cb429db..a6ae7644 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -2,6 +2,7 @@ + - + + + + 211 + Brim101 + Brimstone Normal 1 + 1358 + 1 + + + 4060 + 5737 + 8402 + 12704 + 12879 + 15065 + 15065 + 15074 + 15134 + 15151 + + + + + + 212 + Brim102 + Brimstone Normal 2 + 1358 + 1 + + + 4060 + 8402 + 12704 + 12879 + 14082 + 15065 + 15065 + 15071 + 15134 + 15151 + + + + + + 213 + Brim103 + Brimstone Normal 3 + 1681 + 1 + + + 4060 + 8402 + 12704 + 12879 + 14082 + 15065 + 15065 + 15071 + 15134 + 15151 + + + + + + 214 + Brim104 + Brimstone Normal 4 + 1032 + 1 + + + 4060 + 5737 + 8402 + 12704 + 12879 + 15065 + 15065 + 15074 + 15134 + 15151 + + + + + + 215 + Brim105 + Brimstone Normal 5 + 1032 + 1 + + + 4811 + 8065 + 11004 + 12601 + 13325 + 15037 + 15065 + 15071 + 15071 + 15151 + + + + + + 216 + Brim106 + Brimstone Normal 6 + 1687 + 1 + + + 4811 + 8286 + 11312 + 11642 + 11948 + 12607 + 15065 + 15071 + 15071 + 15151 + + + + + + 217 + Brim107 + Brimstone Normal 7 + 1692 + 1 + + + 7688 + 8286 + 10174 + 12391 + 14299 + 14959 + 15065 + 15071 + 15151 + 15151 + + + + + + 221 + Brim201 + Brimstone Heroic 1 + 1360 + 1 + + + 5743 + 8408 + 10180 + 10860 + 12710 + 15067 + 15067 + 15073 + 15153 + 15197 + + + + + + 222 + Brim202 + Brimstone Heroic 2 + 1360 + 1 + + + 7688 + 8408 + 10180 + 12710 + 14088 + 15067 + 15067 + 15073 + 15153 + 15197 + + + + + + 223 + Brim203 + Brimstone Heroic 3 + 1683 + 1 + + + 4158 + 7688 + 8408 + 12710 + 12885 + 14088 + 15067 + 15067 + 15073 + 15153 + + + + + + 224 + Brim204 + Brimstone Heroic 4 + 1034 + 1 + + + 7148 + 7688 + 8071 + 11010 + 13331 + 15043 + 15067 + 15073 + 15073 + 15153 + + + + + + 225 + Brim205 + Brimstone Heroic 5 + 1034 + 1 + + + 5743 + 7148 + 8071 + 11954 + 12607 + 15043 + 15067 + 15073 + 15073 + 15153 + + + + + + 226 + Brim206 + Brimstone Heroic 6 + 1689 + 1 + + + 8292 + 10180 + 11648 + 12397 + 14088 + 14971 + 15067 + 15073 + 15073 + 15153 + + + + + + 227 + Brim207 + Brimstone Heroic 7 + 1697 + 1 + + + 7688 + 8292 + 10180 + 11618 + 12397 + 14971 + 15067 + 15073 + 15153 + 15153 + + + + + + 231 + Brim301 + Brimstone Mythic 1 + 1360 + 1 + + + 5749 + 8408 + 10048 + 10186 + 10438 + 15068 + 15068 + 15074 + 15156 + 15197 + + + + + + 232 + Brim302 + Brimstone Mythic 2 + 1360 + 1 + + + 8408 + 10186 + 10438 + 10871 + 11498 + 12710 + 15068 + 15068 + 15074 + 15156 + + + + + + 233 + Brim303 + Brimstone Mythic 3 + 1685 + 1 + + + 7694 + 8408 + 10186 + 11408 + 12891 + 14094 + 15068 + 15068 + 15074 + 15156 + + + + + + 234 + Brim304 + Brimstone Mythic 4 + 1034 + 1 + + + 5749 + 8071 + 10048 + 10438 + 11016 + 13337 + 15068 + 15074 + 15074 + 15156 + + + + + + 235 + Brim305 + Brimstone Mythic 5 + 1034 + 1 + + + 7148 + 7694 + 8071 + 11960 + 12891 + 14305 + 15068 + 15074 + 15074 + 15156 + + + + + + 236 + Brim306 + Brimstone Mythic 6 + 1691 + 1 + + + 7382 + 7694 + 8292 + 10186 + 10734 + 14971 + 15068 + 15074 + 15074 + 15156 + + + + + + 237 + Brim307 + Brimstone Mythic 7 + 1697 + 1 + + + 7382 + 8292 + 10186 + 11618 + 12397 + 14743 + 15068 + 15074 + 15156 + 15156 + + + diff --git a/tyrant.h b/tyrant.h index 1a1fd5a2..1263d58c 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.5.2" +#define TYRANT_OPTIMIZER_VERSION "2.5.3" #include #include diff --git a/xml.cpp b/xml.cpp index dcfd13d1..e814b32b 100644 --- a/xml.cpp +++ b/xml.cpp @@ -334,13 +334,16 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); // upgrade cards for full-level missions/raids - deck->commander = deck->commander->m_top_level_card; - for (auto && card: deck->cards) - { card = card->m_top_level_card; } - for (auto && pool: deck->raid_cards) + if (max_level > 1) { - for (auto && card: pool.second) + deck->commander = deck->commander->m_top_level_card; + for (auto && card: deck->cards) { card = card->m_top_level_card; } + for (auto && pool: deck->raid_cards) + { + for (auto && card: pool.second) + { card = card->m_top_level_card; } + } } decks.by_name[base_deck_name] = deck; @@ -416,9 +419,13 @@ void read_raids(Decks& decks, const Cards& all_cards, std::string filename) xml_node<>* id_node(campaign_node->first_node("id")); assert(id_node); unsigned id(id_node ? atoi(id_node->value()) : 0); - xml_node<>* name_node(campaign_node->first_node("name")); - std::string deck_name{name_node->value()}; - read_deck(decks, all_cards, campaign_node, "effect", DeckType::campaign, id, deck_name); + for (auto && name_node = campaign_node->first_node("name"); + name_node; + name_node = campaign_node->next_sibling("name")) + { + std::string deck_name{name_node->value()}; + read_deck(decks, all_cards, campaign_node, "effect", DeckType::campaign, id, name_node->value()); + } } } From eaac3535acd30479ce5b2c768b0c7522ad0362e6 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 18 Mar 2015 04:49:59 +0800 Subject: [PATCH 297/406] Fix campaign Brimstone. --- data/raids.xml | 6 +----- tyrant.h | 2 +- xml.cpp | 3 +-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index a6ae7644..834fa8bd 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -2,7 +2,6 @@ - - 211 @@ -604,7 +600,7 @@ 227 Brim207 Brimstone Heroic 7 - 1697 + 1694 1 diff --git a/tyrant.h b/tyrant.h index 1263d58c..08dad966 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.5.3" +#define TYRANT_OPTIMIZER_VERSION "2.5.4" #include #include diff --git a/xml.cpp b/xml.cpp index e814b32b..02e7ef1e 100644 --- a/xml.cpp +++ b/xml.cpp @@ -421,9 +421,8 @@ void read_raids(Decks& decks, const Cards& all_cards, std::string filename) unsigned id(id_node ? atoi(id_node->value()) : 0); for (auto && name_node = campaign_node->first_node("name"); name_node; - name_node = campaign_node->next_sibling("name")) + name_node = name_node->next_sibling("name")) { - std::string deck_name{name_node->value()}; read_deck(decks, all_cards, campaign_node, "effect", DeckType::campaign, id, name_node->value()); } } From 880d018652284980b98cba0c4329d609de45f931 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 18 Mar 2015 17:07:07 +0800 Subject: [PATCH 298/406] Add skills Refresh and Evolve. --- card.h | 2 +- cards.cpp | 4 +- data/raids.xml | 25 +++++++++ sim.cpp | 130 ++++++++++++++++++++++++++++++-------------- sim.h | 6 +- tyrant.cpp | 4 +- tyrant.h | 7 ++- tyrant_optimize.cpp | 2 +- xml.cpp | 9 +-- 9 files changed, 133 insertions(+), 56 deletions(-) diff --git a/card.h b/card.h index f8219db0..99d5e425 100644 --- a/card.h +++ b/card.h @@ -51,7 +51,7 @@ class Card std::memset(m_skill_value, 0, sizeof m_skill_value); } - void add_skill(Skill id, unsigned x, Faction y, unsigned n, unsigned c, Skill s, bool all); + void add_skill(Skill id, unsigned x, Faction y, unsigned n, unsigned c, Skill s, Skill s2, bool all); const Card* upgraded() const { return this == m_top_level_card ? this : m_used_for_cards.begin()->first; } }; diff --git a/cards.cpp b/cards.cpp index 6ddc665a..c2efaaed 100644 --- a/cards.cpp +++ b/cards.cpp @@ -148,7 +148,7 @@ void Cards::organize() } // class Card -void Card::add_skill(Skill id, unsigned x, Faction y, unsigned n, unsigned c, Skill s, bool all) +void Card::add_skill(Skill id, unsigned x, Faction y, unsigned n, unsigned c, Skill s, Skill s2, bool all) { for(auto it = m_skills.begin(); it != m_skills.end(); ++ it) { @@ -158,7 +158,7 @@ void Card::add_skill(Skill id, unsigned x, Faction y, unsigned n, unsigned c, Sk break; } } - m_skills.push_back({id, x, y, n, c, s, all}); + m_skills.push_back({id, x, y, n, c, s, s2, all}); m_skill_value[id] = x ? x : n ? n : 1; } diff --git a/data/raids.xml b/data/raids.xml index 834fa8bd..04bf9065 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -163,6 +163,31 @@ + + 8 + Lernaean Hydra + 1698 + 26 + + + 8548 + 15480 + 15490 + 15500 + 15510 + 15520 + + + 8548 + 15480 + 15490 + 15500 + 15510 + 15520 + + + + 1 Tartarus Vanguard 1 diff --git a/sim.cpp b/sim.cpp index 47034785..ace0566e 100644 --- a/sim.cpp +++ b/sim.cpp @@ -42,26 +42,24 @@ inline void Field::print_selection_array() #endif } //------------------------------------------------------------------------------ -inline bool CardStatus::has_skill(Skill skill_id) const +inline unsigned CardStatus::skill_base_value(Skill skill_id) const { - return m_card->m_skill_value[skill_id]; + return m_card->m_skill_value[skill_id + m_primary_skill_offset[skill_id]]; } //------------------------------------------------------------------------------ -template -inline bool CardStatus::has_skill() const +inline unsigned CardStatus::skill(Skill skill_id) const { - return m_card->m_skill_value[skill_id]; + return skill_base_value(skill_id) + enhanced(skill_id); } //------------------------------------------------------------------------------ -template -inline unsigned CardStatus::skill() const +inline bool CardStatus::has_skill(Skill skill_id) const { - return m_card->m_skill_value[skill_id] + enhanced(skill_id); + return skill_base_value(skill_id); } //------------------------------------------------------------------------------ -inline unsigned CardStatus::enhanced(Skill skill) const +inline unsigned CardStatus::enhanced(Skill skill_id) const { - return m_enhanced_value[skill]; + return m_enhanced_value[skill_id + m_primary_skill_offset[skill_id]]; } //------------------------------------------------------------------------------ inline unsigned CardStatus::protected_value() const @@ -98,6 +96,8 @@ inline void CardStatus::set(const Card& card) m_rallied = 0; m_weakened = 0; + std::memset(m_primary_skill_offset, 0, sizeof m_primary_skill_offset); + std::memset(m_evolved_skill_offset, 0, sizeof m_evolved_skill_offset); std::memset(m_enhanced_value, 0, sizeof m_enhanced_value); std::memset(m_skill_cd, 0, sizeof m_skill_cd); } @@ -113,6 +113,7 @@ std::string skill_description(const Cards& cards, const SkillSpec& s) (s.all ? " all" : s.n == 0 ? "" : std::string(" ") + to_string(s.n)) + (s.y == allfactions ? "" : std::string(" ") + faction_names[s.y]) + (s.s == no_skill ? "" : std::string(" ") + skill_names[s.s]) + + (s.s2 == no_skill ? "" : std::string(" ") + skill_names[s.s2]) + (s.x == 0 || s.x == s.n ? "" : std::string(" ") + to_string(s.x)) + (s.c == 0 ? "" : std::string(" every ") + to_string(s.c)); } @@ -121,6 +122,7 @@ std::string skill_short_description(const SkillSpec& s) // NOTE: not support summon return skill_names[s.id] + (s.s == no_skill ? "" : std::string(" ") + skill_names[s.s]) + + (s.s2 == no_skill ? "" : std::string(" ") + skill_names[s.s2]) + (s.x == 0 ? "" : std::string(" ") + to_string(s.x)); } //------------------------------------------------------------------------------ @@ -213,6 +215,13 @@ inline unsigned opponent(unsigned player) return((player + 1) % 2); } //------------------------------------------------------------------------------ +SkillSpec apply_evolve(const SkillSpec& s, signed offset) +{ + SkillSpec evolved_s = s; + evolved_s.id = static_cast(evolved_s.id + offset); + return(evolved_s); +} +//------------------------------------------------------------------------------ SkillSpec apply_enhance(const SkillSpec& s, unsigned enhanced_value) { SkillSpec enahnced_s = s; @@ -229,8 +238,8 @@ void prepend_bge_reaping(Field* fd) std::vector> od_skills; for(auto status: fd->killed_with_on_death) { - SkillSpec ss_heal{heal, fd->bg_skill.x, allfactions, 0, 0, no_skill, true,}; - SkillSpec ss_rally{rally, fd->bg_skill.x, allfactions, 0, 0, no_skill, true,}; + SkillSpec ss_heal{heal, fd->bg_skill.x, allfactions, 0, 0, no_skill, no_skill, true,}; + SkillSpec ss_rally{rally, fd->bg_skill.x, allfactions, 0, 0, no_skill, no_skill, true,}; _DEBUG_MSG(2, "Preparing %s skill %s and %s\n", status_description(status).c_str(), skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); od_skills.emplace_back(status, ss_heal); od_skills.emplace_back(status, ss_rally); @@ -246,14 +255,16 @@ void resolve_skill(Field* fd) { auto skill_instance(fd->skill_queue.front()); auto& status(std::get<0>(skill_instance)); - const auto& skill(std::get<1>(skill_instance)); + const auto& ss(std::get<1>(skill_instance)); fd->skill_queue.pop_front(); if (!status->m_jammed) { - unsigned enhanced_value = status->enhanced(skill.id); - auto& enhanced_s = enhanced_value > 0 ? apply_enhance(skill, enhanced_value) : skill; + signed evolved_offset = status->m_evolved_skill_offset[ss.id]; + auto& evolved_s = status->m_evolved_skill_offset[ss.id] != 0 ? apply_evolve(ss, evolved_offset) : ss; + unsigned enhanced_value = status->enhanced(evolved_s.id); + auto& enhanced_s = enhanced_value > 0 ? apply_enhance(evolved_s, enhanced_value) : evolved_s; auto& modified_s = enhanced_s; - skill_table[skill.id](fd, status, modified_s); + skill_table[modified_s.id](fd, status, modified_s); } } } @@ -279,6 +290,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector assert(fd->skill_queue.size() == 0); for (auto & ss: skills) { + // check if activation skill, assuming activation skills can be evolved from only activation skills if (skill_table[ss.id] == nullptr) { continue; @@ -294,7 +306,6 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector } if (type == CardType::assault) { - // no commander-killing skill yet // if(__builtin_expect(fd->end, false)) { break; } // Attack if (can_attack(status)) { @@ -310,15 +321,16 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector } } // Flurry - if (can_act(status) && fd->tip->commander.m_hp > 0 && status->has_skill() && status->m_skill_cd[flurry] == 0) + if (can_act(status) && fd->tip->commander.m_hp > 0 && status->has_skill(flurry) && status->m_skill_cd[flurry] == 0) { _DEBUG_MSG(1, "%s activates Flurry\n", status_description(status).c_str()); num_actions = 2; for (const auto & ss : skills) { - if (ss.id == flurry) + Skill evolved_skill_id = static_cast(ss.id + status->m_evolved_skill_offset[ss.id]); + if (evolved_skill_id == flurry) { - status->m_skill_cd[flurry] = ss.c; + status->m_skill_cd[ss.id] = ss.c; } } } @@ -578,6 +590,12 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(ref->m_card->m_type == CardType::assault && ref->m_hp > 0); } +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(can_be_healed(c)); +} + void remove_hp(Field* fd, CardStatus* status, unsigned dmg) { assert(status->m_hp > 0); @@ -726,6 +744,12 @@ void turn_end_phase(Field* fd) status.m_weakened = 0; status.m_step = CardStep::none; status.m_inhibited = 0; + unsigned refresh_value = status.skill(refresh); + if (refresh_value > 0 && skill_check(fd, &status, nullptr)) + { + _DEBUG_MSG(1, "%s refreshes %u health\n", status_description(&status).c_str(), refresh_value); + add_hp(fd, &status, refresh_value); + } unsigned poison_dmg = safe_minus(status.m_poisoned + (status.m_poisoned ? status.m_enfeebled : 0), status.protected_value()); if(poison_dmg > 0) { @@ -760,6 +784,8 @@ void turn_end_phase(Field* fd) status.m_evaded = 0; status.m_paybacked = 0; std::memset(status.m_enhanced_value, 0, sizeof status.m_enhanced_value); + std::memset(status.m_primary_skill_offset, 0, sizeof status.m_primary_skill_offset); + std::memset(status.m_evolved_skill_offset, 0, sizeof status.m_evolved_skill_offset); } } // Defending player's structure cards: @@ -792,14 +818,14 @@ void turn_end_phase(Field* fd) inline unsigned counter_damage(Field* fd, CardStatus* att, CardStatus* def) { assert(att->m_card->m_type == CardType::assault); - return(safe_minus(def->skill() + att->m_enfeebled, att->protected_value())); + return(safe_minus(def->skill(counter) + att->m_enfeebled, att->protected_value())); } inline CardStatus* select_first_enemy_wall(Field* fd) { for(unsigned i(0); i < fd->tip->structures.size(); ++i) { CardStatus& c(fd->tip->structures[i]); - if(c.has_skill() && c.m_hp > 0 && skill_check(fd, &c, nullptr)) + if(c.has_skill(wall) && c.m_hp > 0 && skill_check(fd, &c, nullptr)) { return(&c); } @@ -862,20 +888,20 @@ struct PerformAttack if(att_status->m_hp > 0) { - if(def_status->has_skill() && skill_check(fd, def_status, att_status)) + if(def_status->has_skill(counter) && skill_check(fd, def_status, att_status)) { // perform_skill_counter unsigned counter_dmg(counter_damage(fd, att_status, def_status)); _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, att_status, counter_dmg); } - unsigned berserk_value = att_status->skill(); + unsigned berserk_value = att_status->skill(berserk); if(berserk_value > 0 && skill_check(fd, att_status, nullptr)) { // perform_skill_berserk att_status->m_berserk += berserk_value; } - unsigned corrosive_value = def_status->skill(); + unsigned corrosive_value = def_status->skill(corrosive); if (corrosive_value > att_status->m_corroded_rate && skill_check(fd, def_status, att_status)) { // perform_skill_corrosive @@ -898,7 +924,7 @@ struct PerformAttack att_dmg = pre_modifier_dmg; std::string desc; // enhance damage - unsigned legion_base = att_status->skill(); + unsigned legion_base = att_status->skill(legion); if (legion_base > 0) { auto & assaults = fd->tap->assaults; @@ -929,7 +955,7 @@ struct PerformAttack // prevent damage std::string reduced_desc; unsigned reduced_dmg(0); - unsigned armor_value = def_status->skill(); + unsigned armor_value = def_status->skill(armor); if(armor_value > 0) { if(debug_print > 0) { reduced_desc += to_string(armor_value) + "(armor)"; } @@ -940,10 +966,10 @@ struct PerformAttack if(debug_print > 0) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->protected_value()) + "(protected)"; } reduced_dmg += def_status->protected_value(); } - if(reduced_dmg > 0 && att_status->skill() > 0) + if(reduced_dmg > 0 && att_status->skill(pierce) > 0) { - if(debug_print > 0) { reduced_desc += "-" + to_string(att_status->skill()) + "(pierce)"; } - reduced_dmg = safe_minus(reduced_dmg, att_status->skill()); + if(debug_print > 0) { reduced_desc += "-" + to_string(att_status->skill(pierce)) + "(pierce)"; } + reduced_dmg = safe_minus(reduced_dmg, att_status->skill(pierce)); } att_dmg = safe_minus(att_dmg, reduced_dmg); if(debug_print > 0) @@ -977,14 +1003,14 @@ void PerformAttack::attack_damage() template<> void PerformAttack::damage_dependant_pre_oa() { - unsigned poison_value = att_status->skill(); + unsigned poison_value = att_status->skill(poison); if(poison_value > def_status->m_poisoned && skill_check(fd, att_status, def_status)) { // perform_skill_poison _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), poison_value); def_status->m_poisoned = poison_value; } - unsigned inhibit_value = att_status->skill(); + unsigned inhibit_value = att_status->skill(inhibit); if (inhibit_value > def_status->m_inhibited && skill_check(fd, att_status, def_status)) { // perform_skill_inhibit @@ -996,7 +1022,7 @@ void PerformAttack::damage_dependant_pre_oa() template<> void PerformAttack::do_leech() { - unsigned leech_value = std::min(att_dmg, att_status->skill()); + unsigned leech_value = std::min(att_dmg, att_status->skill(leech)); if(leech_value > 0 && skill_check(fd, att_status, nullptr)) { _DEBUG_MSG(1, "%s leeches %u health\n", status_description(att_status).c_str(), leech_value); @@ -1077,6 +1103,12 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst return dst->has_skill(s.s) && ((BEGIN_DEFENSIVE < s.s && s.s < END_DEFENSIVE) || is_active(dst)); } +template<> +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ + return dst->has_skill(s.s) && !dst->has_skill(s.s2) && ((BEGIN_DEFENSIVE < s.s2 && s.s2 < END_DEFENSIVE) || is_active(dst)); +} + template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { return(can_be_healed(dst)); } @@ -1103,17 +1135,18 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* ds break; } } - for (const auto & s: dst->m_card->m_skills) + for (const auto & ss: dst->m_card->m_skills) { - if (dst->m_skill_cd[s.id] > 0) + if (dst->m_skill_cd[ss.id] > 0) { continue; } - if (BEGIN_ACTIVATION_HARMFUL < s.id && s.id < END_ACTIVATION_HARMFUL) + Skill evolved_skill_id = static_cast(ss.id + dst->m_evolved_skill_offset[ss.id]); + if (BEGIN_ACTIVATION_HARMFUL < evolved_skill_id && evolved_skill_id < END_ACTIVATION_HARMFUL) { return true; } - if (has_inhibited_unit && (/* s.id == enhance ||*/ s.id == heal || /*s.id == overload ||*/ s.id == protect || s.id == rally)) + if (has_inhibited_unit && (evolved_skill_id == heal || evolved_skill_id == protect || evolved_skill_id == rally)) { return true; } @@ -1146,7 +1179,18 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - dst->m_enhanced_value[s.s] += s.x; + dst->m_enhanced_value[s.s + dst->m_primary_skill_offset[s.s]] += s.x; +} + +template<> +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ + auto primary_s1 = dst->m_primary_skill_offset[s.s] + s.s; + auto primary_s2 = dst->m_primary_skill_offset[s.s2] + s.s2; + dst->m_primary_skill_offset[s.s] = primary_s2 - s.s; + dst->m_primary_skill_offset[s.s2] = primary_s1 - s.s2; + dst->m_evolved_skill_offset[primary_s1] = s.s2 - primary_s1; + dst->m_evolved_skill_offset[primary_s2] = s.s - primary_s2; } template<> @@ -1244,6 +1288,9 @@ template<> std::vector& skill_targets(Field* fd, CardStat template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } @@ -1274,7 +1321,7 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ if(skill_check(fd, src_status, dst_status)) { if (is_evadable && - dst_status->m_evaded < dst_status->skill() && + dst_status->m_evaded < dst_status->skill(evade) && skill_check(fd, dst_status, src_status)) { ++ dst_status->m_evaded; @@ -1295,7 +1342,7 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ bool check_and_perform_valor(Field* fd, CardStatus* src_status) { - unsigned valor_value = src_status->skill(); + unsigned valor_value = src_status->skill(valor); if (valor_value > 0 && skill_check(fd, src_status, nullptr)) { unsigned opponent_player = opponent(src_status->m_player); @@ -1355,7 +1402,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski if (check_and_perform_skill(fd, src_status, dst_status, s, ! src_status->m_overloaded)) { // Payback - if(dst_status->m_paybacked < dst_status->skill() && skill_check(fd, dst_status, src_status) && + if(dst_status->m_paybacked < dst_status->skill(payback) && skill_check(fd, dst_status, src_status) && skill_predicate(fd, src_status, src_status, s) && skill_check(fd, src_status, dst_status)) { ++ dst_status->m_paybacked; @@ -1389,6 +1436,7 @@ void fill_skill_table() memset(skill_table, 0, sizeof skill_table); skill_table[enfeeble] = perform_targetted_hostile_fast; skill_table[enhance] = perform_targetted_allied_fast; + skill_table[evolve] = perform_targetted_allied_fast; skill_table[heal] = perform_targetted_allied_fast; skill_table[jam] = perform_targetted_hostile_fast; skill_table[overload] = perform_targetted_allied_fast; diff --git a/sim.h b/sim.h index 6da8a04d..8e7b5a86 100644 --- a/sim.h +++ b/sim.h @@ -164,6 +164,8 @@ struct CardStatus unsigned m_rallied; unsigned m_weakened; + signed m_primary_skill_offset[num_skills]; + signed m_evolved_skill_offset[num_skills]; unsigned m_enhanced_value[num_skills]; unsigned m_skill_cd[num_skills]; @@ -172,9 +174,9 @@ struct CardStatus void set(const Card* card); void set(const Card& card); std::string description() const; + inline unsigned skill_base_value(Skill skill_id) const; + unsigned skill(Skill skill_id) const; bool has_skill(Skill skill_id) const; - template bool has_skill() const; - template unsigned skill() const; unsigned enhanced(Skill skill) const; unsigned protected_value() const; }; diff --git a/tyrant.cpp b/tyrant.cpp index 521f9c76..e49dd309 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -16,11 +16,11 @@ std::string skill_names[Skill::num_skills] = "Enfeeble", "Jam", "Siege", "Strike", "Weaken", "", "", - "Enhance", "Heal", "Overload", "Protect", "Rally", + "Enhance", "Evolve", "Heal", "Overload", "Protect", "Rally", "", // Defensive: "", - "Armor", "Corrosive", "Counter", "Evade", "Payback", "Wall", + "Armor", "Avenge", "Corrosive", "Counter", "Evade", "Payback", "Refresh", "Wall", "", // Combat-Modifier: "Flurry", "Pierce", "Valor", diff --git a/tyrant.h b/tyrant.h index 08dad966..a59a31b2 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.5.4" +#define TYRANT_OPTIMIZER_VERSION "2.6.0" #include #include @@ -32,11 +32,11 @@ enum Skill enfeeble, jam, siege, strike, weaken, END_ACTIVATION_HARMFUL, BEGIN_ACTIVATION_HELPFUL, - enhance, heal, overload, protect, rally, + enhance, evolve, heal, overload, protect, rally, END_ACTIVATION_HELPFUL, // Defensive: BEGIN_DEFENSIVE, - armor, corrosive, counter, evade, payback, wall, + armor, avenge, corrosive, counter, evade, payback, refresh, wall, END_DEFENSIVE, // Combat-Modifier: flurry, pierce, valor, @@ -143,6 +143,7 @@ struct SkillSpec unsigned n; unsigned c; Skill s; + Skill s2; bool all; }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 70c8b5e0..c2c9c1a6 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1401,7 +1401,7 @@ int main(int argc, char** argv) std::vector> opt_todo; std::string opt_effect; enum Effect opt_effect_id(Effect::none); - SkillSpec opt_bg_skill{no_skill, 0, allfactions, 0, 0, no_skill, false}; + SkillSpec opt_bg_skill{no_skill, 0, allfactions, 0, 0, no_skill, no_skill, false}; for(int argIndex = 3; argIndex < argc; ++argIndex) { diff --git a/xml.cpp b/xml.cpp index 02e7ef1e..5a29f81e 100644 --- a/xml.cpp +++ b/xml.cpp @@ -85,10 +85,10 @@ unsigned node_value(xml_node<>* skill, const char* attribute, unsigned default_v return value_node ? atoi(value_node->value()) : default_value; } -Skill skill_target_skill(xml_node<>* skill) +Skill skill_target_skill(xml_node<>* skill, const char* attribute) { Skill s(no_skill); - xml_attribute<>* x(skill->first_attribute("s")); + xml_attribute<>* x(skill->first_attribute(attribute)); if(x) { s = skill_name_to_id(x->value()); @@ -213,9 +213,10 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) auto y = skill_faction(skill_node); auto n = node_value(skill_node, "n", 0); auto c = node_value(skill_node, "c", 0); - auto s = skill_target_skill(skill_node); + auto s = skill_target_skill(skill_node, "s"); + auto s2 = skill_target_skill(skill_node, "s2"); bool all(skill_node->first_attribute("all")); - card->add_skill(skill_id, x, y, n, c, s, all); + card->add_skill(skill_id, x, y, n, c, s, s2, all); } all_cards.cards.push_back(card); Card * top_card = card; From 1b3222313d1f504ff6391cccdc8400b4149383af Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 18 Mar 2015 17:32:54 +0800 Subject: [PATCH 299/406] Update Raid deck. --- data/raids.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/data/raids.xml b/data/raids.xml index 04bf9065..cc46cf44 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -178,7 +178,6 @@ 15520 - 8548 15480 15490 15500 From 89556ed893f5824e9ba1befad8677178760b0839 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 21 Mar 2015 12:59:42 +0800 Subject: [PATCH 300/406] Update Raid deck. --- data/raids.xml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index cc46cf44..369ec82c 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -172,18 +172,15 @@ 8548 15480 - 15490 - 15500 - 15510 - 15520 - - 15480 15490 15500 + 15500 + 15510 15510 15520 - + 15520 + From 5cb8df3c8d437c630932373d2990c89f8d114cff Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 22 Mar 2015 17:50:02 +0800 Subject: [PATCH 301/406] Reorganize status. Fix skill Evade on structure. --- sim.cpp | 97 ++++++++++++++++++++++++++++---------------------------- sim.h | 2 +- tyrant.h | 2 +- 3 files changed, 50 insertions(+), 51 deletions(-) diff --git a/sim.cpp b/sim.cpp index ace0566e..077d5255 100644 --- a/sim.cpp +++ b/sim.cpp @@ -85,8 +85,8 @@ inline void CardStatus::set(const Card& card) m_berserk = 0; m_corroded_rate = 0; m_corroded_weakened = 0; - m_evaded = 0; m_enfeebled = 0; + m_evaded = 0; m_inhibited = 0; m_jammed = false; m_overloaded = false; @@ -193,6 +193,13 @@ std::string CardStatus::description() const if(m_poisoned > 0) { desc += ", poisoned " + to_string(m_poisoned); } if(m_protected > 0) { desc += ", protected " + to_string(m_protected); } // if(m_step != CardStep::none) { desc += ", Step " + to_string(static_cast(m_step)); } + for (const auto & ss: m_card->m_skills) + { + std::string skill_desc; + if (m_evolved_skill_offset[ss.id] != 0) { skill_desc += "->" + skill_names[ss.id + m_evolved_skill_offset[ss.id]]; } + if (m_enhanced_value[ss.id] != 0) { skill_desc += " +" + to_string(m_enhanced_value[ss.id]); } + if (!skill_desc.empty()) { desc += ", " + skill_names[ss.id] + skill_desc; } + } desc += "]"; return(desc); } @@ -724,11 +731,9 @@ void turn_start_phase(Field* fd) } void turn_end_phase(Field* fd) { - // Active player's assault cards: - // remove jam, rally, weaken - // apply poison damage + // Inactive player's assault cards: { - auto& assaults(fd->tap->assaults); + auto& assaults(fd->tip->assaults); for(unsigned index(0), end(assaults.size()); index < end; ++index) @@ -738,73 +743,67 @@ void turn_end_phase(Field* fd) { continue; } - status.m_jammed = false; - status.m_overloaded = false; - status.m_rallied = 0; - status.m_weakened = 0; - status.m_step = CardStep::none; - status.m_inhibited = 0; - unsigned refresh_value = status.skill(refresh); - if (refresh_value > 0 && skill_check(fd, &status, nullptr)) - { - _DEBUG_MSG(1, "%s refreshes %u health\n", status_description(&status).c_str(), refresh_value); - add_hp(fd, &status, refresh_value); - } - unsigned poison_dmg = safe_minus(status.m_poisoned + (status.m_poisoned ? status.m_enfeebled : 0), status.protected_value()); - if(poison_dmg > 0) - { - _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg); - remove_hp(fd, &status, poison_dmg); - } -#if 0 - // not need to fade out in own turn in TU status.m_enfeebled = 0; -#endif + status.m_protected = 0; + std::memset(status.m_primary_skill_offset, 0, sizeof status.m_primary_skill_offset); + std::memset(status.m_evolved_skill_offset, 0, sizeof status.m_evolved_skill_offset); + std::memset(status.m_enhanced_value, 0, sizeof status.m_enhanced_value); + status.m_evaded = 0; // so far only useful in Inactive turn + status.m_paybacked = 0; // ditto } } - // Active player's structure cards: - // nothing so far - - // Defending player's assault cards: - // remove enfeeble, protect + // Inactive player's structure cards: { - auto& assaults(fd->tip->assaults); - for(unsigned index(0), end(assaults.size()); + auto& structures(fd->tip->structures); + for(unsigned index(0), end(structures.size()); index < end; ++index) { - CardStatus& status(assaults[index]); + CardStatus& status(structures[index]); if (status.m_hp <= 0) { continue; } - status.m_enfeebled = 0; - status.m_protected = 0; - // so far only useful in Defending turn - status.m_evaded = 0; - status.m_paybacked = 0; - std::memset(status.m_enhanced_value, 0, sizeof status.m_enhanced_value); - std::memset(status.m_primary_skill_offset, 0, sizeof status.m_primary_skill_offset); - std::memset(status.m_evolved_skill_offset, 0, sizeof status.m_evolved_skill_offset); + status.m_evaded = 0; // so far only useful in Inactive turn } } - // Defending player's structure cards: - // nothing so far -#if 0 + + // Active player's assault cards: { - auto& structures(fd->tip->structures); - for(unsigned index(0), end(structures.size()); + auto& assaults(fd->tap->assaults); + for(unsigned index(0), end(assaults.size()); index < end; ++index) { - CardStatus& status(structures[index]); + CardStatus& status(assaults[index]); if (status.m_hp <= 0) { continue; } + unsigned refresh_value = status.skill(refresh); + if (refresh_value > 0 && skill_check(fd, &status, nullptr)) + { + _DEBUG_MSG(1, "%s refreshes %u health\n", status_description(&status).c_str(), refresh_value); + add_hp(fd, &status, refresh_value); + } + unsigned poison_dmg = safe_minus(status.m_poisoned + (status.m_poisoned ? status.m_enfeebled : 0), status.protected_value()); + if(poison_dmg > 0) + { + _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg); + remove_hp(fd, &status, poison_dmg); + } + // end of the opponent's next turn for enemy units + status.m_jammed = false; + status.m_rallied = 0; + status.m_weakened = 0; + status.m_inhibited = 0; + status.m_overloaded = false; + status.m_step = CardStep::none; } } -#endif + // Active player's structure cards: + // nothing so far + prepend_bge_reaping(fd); resolve_skill(fd); remove_dead(fd->tap->assaults); diff --git a/sim.h b/sim.h index 8e7b5a86..9436d2e6 100644 --- a/sim.h +++ b/sim.h @@ -153,8 +153,8 @@ struct CardStatus unsigned m_berserk; unsigned m_corroded_rate; unsigned m_corroded_weakened; - unsigned m_evaded; unsigned m_enfeebled; + unsigned m_evaded; unsigned m_inhibited; bool m_jammed; bool m_overloaded; diff --git a/tyrant.h b/tyrant.h index a59a31b2..dff86f1b 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.6.0" +#define TYRANT_OPTIMIZER_VERSION "2.6.1" #include #include From edd29509bfefa22f65e68cc72a93b2ee917b841d Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 8 Apr 2015 13:27:48 +0800 Subject: [PATCH 302/406] Add skill Avenge. --- sim.cpp | 92 +++++++++++++++++++++++++++++++++++--------------------- sim.h | 5 +-- tyrant.h | 2 +- 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/sim.cpp b/sim.cpp index 077d5255..0128e2c3 100644 --- a/sim.cpp +++ b/sim.cpp @@ -79,10 +79,10 @@ inline void CardStatus::set(const Card& card) m_player = 0; m_delay = card.m_delay; m_faction = card.m_faction; - m_hp = card.m_health; + m_attack = card.m_attack; + m_hp = m_max_hp = card.m_health; m_step = CardStep::none; - m_berserk = 0; m_corroded_rate = 0; m_corroded_weakened = 0; m_enfeebled = 0; @@ -104,7 +104,7 @@ inline void CardStatus::set(const Card& card) //------------------------------------------------------------------------------ inline int attack_power(const CardStatus* att) { - return(safe_minus(att->m_card->m_attack + att->m_berserk + att->m_rallied, att->m_weakened + att->m_corroded_weakened)); + return(safe_minus(att->m_card->m_attack + att->m_rallied, att->m_weakened + att->m_corroded_weakened)); } //------------------------------------------------------------------------------ std::string skill_description(const Cards& cards, const SkillSpec& s) @@ -165,10 +165,9 @@ std::string CardStatus::description() const switch(m_card->m_type) { case CardType::assault: - desc += " att:" + to_string(m_card->m_attack); + desc += " att:" + to_string(m_attack); { std::string att_desc; - if(m_berserk > 0) { att_desc += "+" + to_string(m_berserk) + "(berserk)"; } if(m_rallied > 0) { att_desc += "+" + to_string(m_rallied) + "(rallied)"; } if(m_weakened > 0) { att_desc += "-" + to_string(m_weakened) + "(weakened)"; } if(m_corroded_weakened > 0) { att_desc += "-" + to_string(m_corroded_weakened) + "(corroded)"; } @@ -236,23 +235,51 @@ SkillSpec apply_enhance(const SkillSpec& s, unsigned enhanced_value) return(enahnced_s); } //------------------------------------------------------------------------------ -void prepend_bge_reaping(Field* fd) +void prepend_on_death(Field* fd) { - if (fd->bg_skill.id != reaping) - { - return; - } std::vector> od_skills; - for(auto status: fd->killed_with_on_death) + for (auto status: fd->killed_units) { - SkillSpec ss_heal{heal, fd->bg_skill.x, allfactions, 0, 0, no_skill, no_skill, true,}; - SkillSpec ss_rally{rally, fd->bg_skill.x, allfactions, 0, 0, no_skill, no_skill, true,}; - _DEBUG_MSG(2, "Preparing %s skill %s and %s\n", status_description(status).c_str(), skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); - od_skills.emplace_back(status, ss_heal); - od_skills.emplace_back(status, ss_rally); + // avenge + if (status->m_card->m_type == CardType::assault) + { + auto & assaults = fd->players[status->m_player]->assaults; + if (status->m_index > 0) + { + auto left_status = &assaults[status->m_index - 1]; + unsigned avenge_value = left_status->skill(avenge); + if (left_status->m_hp > 0 && avenge_value > 0) + { + _DEBUG_MSG(1, "%s activates Avenge %u\n", status_description(left_status).c_str(), avenge_value); + left_status->m_attack += avenge_value; + left_status->m_max_hp += avenge_value; + left_status->m_hp += avenge_value; + } + } + if (status->m_index + 1 < assaults.size()) + { + auto right_status = &assaults[status->m_index + 1]; + unsigned avenge_value = right_status->skill(avenge); + if (right_status->m_hp > 0 && avenge_value > 0) + { + _DEBUG_MSG(1, "%s activates Avenge %u\n", status_description(right_status).c_str(), avenge_value); + right_status->m_attack += avenge_value; + right_status->m_max_hp += avenge_value; + right_status->m_hp += avenge_value; + } + } + } + if (fd->bg_skill.id == reaping) + { + SkillSpec ss_heal{heal, fd->bg_skill.x, allfactions, 0, 0, no_skill, no_skill, true,}; + SkillSpec ss_rally{rally, fd->bg_skill.x, allfactions, 0, 0, no_skill, no_skill, true,}; + _DEBUG_MSG(2, "Preparing %s skill %s and %s\n", status_description(status).c_str(), skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); + od_skills.emplace_back(status, ss_heal); + od_skills.emplace_back(status, ss_rally); + } } fd->skill_queue.insert(fd->skill_queue.begin(), od_skills.begin(), od_skills.end()); - fd->killed_with_on_death.clear(); + fd->killed_units.clear(); } //------------------------------------------------------------------------------ void(*skill_table[num_skills])(Field*, CardStatus* src_status, const SkillSpec&); @@ -283,7 +310,7 @@ inline bool is_active_next_turn(CardStatus* c) { return(c->m_delay <= 1); } inline bool can_act(CardStatus* c) { return(c->m_hp > 0 && !is_jammed(c)); } inline bool can_attack(CardStatus* c) { return(can_act(c)); } // Can be healed / repaired -inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_card->m_health); } +inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_max_hp); } //------------------------------------------------------------------------------ bool attack_phase(Field* fd); bool check_and_perform_valor(Field* fd, CardStatus* src_status); @@ -500,7 +527,7 @@ Results play(Field* fd) { if (attacked) { - unsigned v = std::min(current_status->m_corroded_rate, safe_minus(current_status->m_card->m_attack + current_status->m_berserk, current_status->m_corroded_weakened)); + unsigned v = std::min(current_status->m_corroded_rate, safe_minus(current_status->m_card->m_attack, current_status->m_corroded_weakened)); _DEBUG_MSG(1, "%s loses Attack by %u.\n", status_description(current_status).c_str(), v); current_status->m_corroded_weakened += v; } @@ -522,7 +549,7 @@ Results play(Field* fd) ++fd->turn; } const auto & p = fd->players; - unsigned raid_damage = 15 + (std::min(p[1]->deck->cards.size(), (fd->turn + 1) / 2) - p[1]->assaults.size() - p[1]->structures.size()) - (10 * p[1]->commander.m_hp / p[1]->commander.m_card->m_health); + unsigned raid_damage = 15 + (std::min(p[1]->deck->cards.size(), (fd->turn + 1) / 2) - p[1]->assaults.size() - p[1]->structures.size()) - (10 * p[1]->commander.m_hp / p[1]->commander.m_max_hp); // you lose if(fd->players[0]->commander.m_hp == 0) { @@ -543,7 +570,7 @@ Results play(Field* fd) case OptimizationMode::brawl: { unsigned brawl_score = 57 - - (10 * (p[0]->commander.m_card->m_health - p[0]->commander.m_hp) / p[0]->commander.m_card->m_health) + - (10 * (p[0]->commander.m_max_hp - p[0]->commander.m_hp) / p[0]->commander.m_max_hp) + (p[0]->assaults.size() + p[0]->structures.size() + p[0]->deck->shuffled_cards.size()) - (p[1]->assaults.size() + p[1]->structures.size() + p[1]->deck->shuffled_cards.size()) - fd->turn / 4; @@ -611,10 +638,9 @@ void remove_hp(Field* fd, CardStatus* status, unsigned dmg) if(status->m_hp == 0) { _DEBUG_MSG(1, "%s dies\n", status_description(status).c_str()); - // Assume only assaults get reaping effect - if(fd->bg_skill.id == reaping && status->m_card->m_type != CardType::commander) + if(status->m_card->m_type != CardType::commander) { - fd->killed_with_on_death.push_back(status); + fd->killed_units.push_back(status); } if (status->m_player == 0 && fd->players[0]->deck->vip_cards.count(status->m_card->m_id)) { @@ -639,7 +665,7 @@ inline void remove_dead(Storage& storage) } inline void add_hp(Field* fd, CardStatus* target, unsigned v) { - target->m_hp = std::min(target->m_hp + v, target->m_card->m_health); + target->m_hp = std::min(target->m_hp + v, target->m_max_hp); } void cooldown_skills(CardStatus * status) { @@ -804,7 +830,7 @@ void turn_end_phase(Field* fd) // Active player's structure cards: // nothing so far - prepend_bge_reaping(fd); + prepend_on_death(fd); resolve_skill(fd); remove_dead(fd->tap->assaults); remove_dead(fd->tap->structures); @@ -858,10 +884,9 @@ struct PerformAttack CardStatus* att_status; CardStatus* def_status; unsigned att_dmg; - bool killed_by_attack; PerformAttack(Field* fd_, CardStatus* att_status_, CardStatus* def_status_) : - fd(fd_), att_status(att_status_), def_status(def_status_), att_dmg(0), killed_by_attack(false) + fd(fd_), att_status(att_status_), def_status(def_status_), att_dmg(0) {} template @@ -898,7 +923,7 @@ struct PerformAttack if(berserk_value > 0 && skill_check(fd, att_status, nullptr)) { // perform_skill_berserk - att_status->m_berserk += berserk_value; + att_status->m_attack += berserk_value; } unsigned corrosive_value = def_status->skill(corrosive); if (corrosive_value > att_status->m_corroded_rate && skill_check(fd, def_status, att_status)) @@ -910,7 +935,7 @@ struct PerformAttack } do_leech(); } - prepend_bge_reaping(fd); + prepend_on_death(fd); resolve_skill(fd); return att_dmg; } @@ -983,7 +1008,6 @@ struct PerformAttack void attack_damage() { remove_hp(fd, def_status, att_dmg); - killed_by_attack = def_status->m_hp == 0; } template @@ -1358,8 +1382,8 @@ bool check_and_perform_valor(Field* fd, CardStatus* src_status) _DEBUG_MSG(1, "%s loses Valor (weak blocker %s)\n", status_description(src_status).c_str(), status_description(dst_status).c_str()); return false; } - _DEBUG_MSG(1, "%s activates Valor %u (as if Berserked)\n", status_description(src_status).c_str(), valor_value); - src_status->m_berserk += valor_value; + _DEBUG_MSG(1, "%s activates Valor %u\n", status_description(src_status).c_str(), valor_value); + src_status->m_attack += valor_value; return true; } return false; @@ -1410,7 +1434,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski } } } - prepend_bge_reaping(fd); + prepend_on_death(fd); } template diff --git a/sim.h b/sim.h index 9436d2e6..9a442376 100644 --- a/sim.h +++ b/sim.h @@ -147,10 +147,11 @@ struct CardStatus unsigned m_player; unsigned m_delay; Faction m_faction; + unsigned m_attack; unsigned m_hp; + unsigned m_max_hp; CardStep m_step; - unsigned m_berserk; unsigned m_corroded_rate; unsigned m_corroded_weakened; unsigned m_enfeebled; @@ -225,7 +226,7 @@ class Field // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque> skill_queue; - std::vector killed_with_on_death; + std::vector killed_units; unsigned n_player_kills; bool assault_bloodlusted; unsigned bloodlust_value; diff --git a/tyrant.h b/tyrant.h index dff86f1b..964757b7 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.6.1" +#define TYRANT_OPTIMIZER_VERSION "2.7.0" #include #include From 041b88a40f97370bd7fb013d8561e9d03621e072 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 8 Apr 2015 18:39:23 +0800 Subject: [PATCH 303/406] Fix bug: berserk. --- sim.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index 0128e2c3..52245fdb 100644 --- a/sim.cpp +++ b/sim.cpp @@ -104,7 +104,7 @@ inline void CardStatus::set(const Card& card) //------------------------------------------------------------------------------ inline int attack_power(const CardStatus* att) { - return(safe_minus(att->m_card->m_attack + att->m_rallied, att->m_weakened + att->m_corroded_weakened)); + return(safe_minus(att->m_attack + att->m_rallied, att->m_weakened + att->m_corroded_weakened)); } //------------------------------------------------------------------------------ std::string skill_description(const Cards& cards, const SkillSpec& s) @@ -527,7 +527,7 @@ Results play(Field* fd) { if (attacked) { - unsigned v = std::min(current_status->m_corroded_rate, safe_minus(current_status->m_card->m_attack, current_status->m_corroded_weakened)); + unsigned v = std::min(current_status->m_corroded_rate, safe_minus(current_status->m_attack, current_status->m_corroded_weakened)); _DEBUG_MSG(1, "%s loses Attack by %u.\n", status_description(current_status).c_str(), v); current_status->m_corroded_weakened += v; } From bddfdcd559e9e09917c5272211e98e1264759613 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 8 Apr 2015 18:40:56 +0800 Subject: [PATCH 304/406] Version 2.7.1 --- tyrant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tyrant.h b/tyrant.h index 964757b7..711dc074 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.7.0" +#define TYRANT_OPTIMIZER_VERSION "2.7.1" #include #include From 27ef28b306e8934af12da54d7a22064616899c99 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 9 Apr 2015 22:52:53 +0800 Subject: [PATCH 305/406] Support Raid #9 Talos (spoiler). --- data/raids.xml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 369ec82c..5efbf49a 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -184,6 +184,30 @@ + + 9 + Talos + 1732 + 26 + + + 16136 + 16146 + 16156 + 16166 + 16176 + 8630 + + + 16136 + 16146 + 16156 + 16166 + 16176 + + + + 1 Tartarus Vanguard 1 From c4bae0c23d6f6cf2656da40e150a44fe045f328d Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 17 Apr 2015 11:14:20 +0800 Subject: [PATCH 306/406] Add decks for campaign Secrets Revealed. --- data/raids.xml | 462 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 5efbf49a..8840b4bb 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -817,4 +817,466 @@ + + 311 + Secr101 + Secrets Revealed Normal 1 + 1018 + 1 + + + 4468 + 4468 + 6329 + 6644 + 6644 + 12520 + 14103 + 15910 + 15914 + 15920 + + + + + + 312 + Secr102 + Secrets Revealed Normal 2 + 1018 + 1 + + + 517 + 517 + 951 + 5502 + 6335 + 6896 + 6896 + 15910 + 15914 + 15920 + + + + + + 313 + Secr103 + Secrets Revealed Normal 3 + 1714 + 1 + + + 7406 + 11468 + 12523 + 13487 + 13565 + 13644 + 15913 + 15913 + 15916 + 15920 + + + + + + 314 + Secr104 + Secrets Revealed Normal 4 + 1018 + 1 + + + 8173 + 11143 + 11143 + 11999 + 12716 + 13469 + 15113 + 15908 + 15916 + 15920 + + + + + + 315 + Secr105 + Secrets Revealed Normal 5 + 1018 + 1 + + + 6644 + 6644 + 6644 + 12008 + 14007 + 14100 + 14672 + 15913 + 15916 + 15920 + + + + + + 316 + Secr106 + Secrets Revealed Normal 6 + 1720 + 1 + + + 543 + 11239 + 13433 + 14008 + 14100 + 14617 + 15913 + 15919 + 15919 + 15920 + + + + + + 317 + Secr107 + Secrets Revealed Normal 7 + 1726 + 1 + + + 4921 + 10758 + 11239 + 12722 + 13029 + 14010 + 15913 + 15919 + 15920 + 15920 + + + + + + 321 + Secr201 + Secrets Revealed Heroic 1 + 1044 + 1 + + + 6332 + 7671 + 12523 + 13481 + 14106 + 14674 + 15601 + 15913 + 15919 + 15922 + + + + + + 322 + Secr202 + Secrets Revealed Heroic 2 + 1044 + 1 + + + 4289 + 4738 + 5508 + 6339 + 11828 + 12526 + 12526 + 15913 + 15919 + 15922 + + + + + + 323 + Secr203 + Secrets Revealed Heroic 3 + 1716 + 1 + + + 7412 + 11828 + 12526 + 13493 + 13571 + 13652 + 15913 + 15913 + 15919 + 15922 + + + + + + 324 + Secr204 + Secrets Revealed Heroic 4 + 1201 + 1 + + + 11149 + 11149 + 11245 + 14013 + 14617 + 15116 + 15245 + 15913 + 15919 + 15922 + + + + + + 325 + Secr205 + Secrets Revealed Heroic 5 + 1201 + 1 + + + 8462 + 12722 + 13475 + 14317 + 14674 + 15395 + 15907 + 15913 + 15919 + 15922 + + + + + + 326 + Secr206 + Secrets Revealed Heroic 6 + 1722 + 1 + + + 854 + 11245 + 13439 + 14013 + 14013 + 14106 + 14620 + 15919 + 15919 + 15922 + + + + + + 327 + Secr207 + Secrets Revealed Heroic 7 + 1728 + 1 + + + 10764 + 11245 + 11531 + 12728 + 13032 + 14016 + 15913 + 15919 + 15922 + 15922 + + + + + + 331 + Secr301 + Secrets Revealed Mythic 1 + 1046 + 1 + + + 6344 + 12529 + 12535 + 13571 + 13571 + 13649 + 14112 + 15913 + 15919 + 15925 + + + + + + 332 + Secr302 + Secrets Revealed Mythic 2 + 1046 + 1 + + + 4741 + 6344 + 7676 + 11444 + 11444 + 12499 + 13655 + 15913 + 15919 + 15925 + + + + + + 333 + Secr303 + Secrets Revealed Mythic 3 + 1719 + 1 + + + 7418 + 11834 + 12535 + 13499 + 13571 + 13661 + 15913 + 15913 + 15919 + 15925 + + + + + + 334 + Secr304 + Secrets Revealed Mythic 4 + 1204 + 1 + + + 12643 + 12795 + 14112 + 14112 + 14677 + 14677 + 15913 + 15919 + 15925 + 16299 + + + + + + 335 + Secr305 + Secrets Revealed Mythic 5 + 1204 + 1 + + + 7166 + 12646 + 12801 + 14413 + 14677 + 14779 + 15913 + 15919 + 15925 + 16299 + + + + + + 336 + Secr306 + Secrets Revealed Mythic 6 + 1725 + 1 + + + 4177 + 11251 + 13445 + 14022 + 14112 + 14623 + 15913 + 15919 + 15919 + 15925 + + + + + + 337 + Secr307 + Secrets Revealed Mythic 7 + 1731 + 1 + + + 7178 + 7532 + 10770 + 11251 + 14022 + 14022 + 15913 + 15919 + 15925 + 15925 + + + + From 73c713c0d2b8533c4be99d1030ca66a80ed8f41a Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 18 Apr 2015 21:07:33 +0800 Subject: [PATCH 307/406] Update campaign deck. --- data/raids.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/raids.xml b/data/raids.xml index 8840b4bb..3cdb9147 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -1093,9 +1093,9 @@ 11245 13439 14013 - 14013 14106 14620 + 15913 15919 15919 15922 From 10039f72e7d226296ab38db472e756490496f68c Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 20 Apr 2015 23:32:41 +0800 Subject: [PATCH 308/406] Support more BGE. --- sim.cpp | 2 +- tyrant.h | 2 +- tyrant_optimize.cpp | 14 +++++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index 52245fdb..6bcce975 100644 --- a/sim.cpp +++ b/sim.cpp @@ -114,7 +114,7 @@ std::string skill_description(const Cards& cards, const SkillSpec& s) (s.y == allfactions ? "" : std::string(" ") + faction_names[s.y]) + (s.s == no_skill ? "" : std::string(" ") + skill_names[s.s]) + (s.s2 == no_skill ? "" : std::string(" ") + skill_names[s.s2]) + - (s.x == 0 || s.x == s.n ? "" : std::string(" ") + to_string(s.x)) + + (s.x == 0 ? "" : std::string(" ") + to_string(s.x)) + (s.c == 0 ? "" : std::string(" every ") + to_string(s.c)); } std::string skill_short_description(const SkillSpec& s) diff --git a/tyrant.h b/tyrant.h index 711dc074..d801d696 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.7.1" +#define TYRANT_OPTIMIZER_VERSION "2.7.2" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index c2c9c1a6..c5c17fef 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1720,6 +1720,11 @@ int main(int argc, char** argv) opt_bg_skill.all = true; skill_index += 1; } + else if (skill_index + 1 < tokens.size() && isdigit(*tokens[skill_index].c_str())) + { + opt_bg_skill.n = boost::lexical_cast(tokens[skill_index]); + skill_index += 1; + } if (skill_index < tokens.size()) { opt_bg_skill.s = skill_name_to_id(tokens[skill_index], false); @@ -1730,7 +1735,14 @@ int main(int argc, char** argv) } if (skill_index < tokens.size()) { - opt_bg_skill.x = boost::lexical_cast(tokens[skill_index]); + if (opt_bg_skill.id == jam || opt_bg_skill.id == overload) + { + opt_bg_skill.n = boost::lexical_cast(tokens[skill_index]); + } + else + { + opt_bg_skill.x = boost::lexical_cast(tokens[skill_index]); + } } } catch (const boost::bad_lexical_cast & e) From 2e777b180846070b80870a7f07a2d3af171eb4fe Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 21 Apr 2015 19:06:40 +0800 Subject: [PATCH 309/406] Support Evolve BGE. --- tyrant_optimize.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index c5c17fef..39d2a8b9 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1731,6 +1731,14 @@ int main(int argc, char** argv) if (opt_bg_skill.s != no_skill) { skill_index += 1; + if (skill_index < tokens.size()) + { + opt_bg_skill.s2 = skill_name_to_id(tokens[skill_index], false); + if (opt_bg_skill.s2 != no_skill) + { + skill_index += 1; + } + } } } if (skill_index < tokens.size()) From 34d87c22ad85a36558ece9dcdece65ce6a33572c Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 5 May 2015 23:05:02 +0800 Subject: [PATCH 310/406] Update OSX Makefile. --- Makefile.osx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.osx b/Makefile.osx index 14683286..7534af94 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -4,7 +4,7 @@ OBJS := $(patsubst %.cpp,obj/%.o,$(SRCS)) INCS := $(wildcard *.h) CPPFLAGS := -Wall -Werror -std=c++11 -stdlib=libc++ -O3 -I/usr/local/include -Wno-deprecated-register -DNDEBUG -LDFLAGS := -lboost_system-mt -lboost_thread-mt -lboost_filesystem-mt -lboost_regex-mt -L/usr/local/lib -Bstatic +LDFLAGS := lib/libboost_system-mt.a lib/libboost_thread-mt.a lib/libboost_filesystem-mt.a lib/libboost_regex-mt.a -L/usr/local/lib -Bstatic all: $(MAIN) From 6346b2e361aa43d038f23ac414c75c8987cbb956 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 7 May 2015 14:01:17 +0800 Subject: [PATCH 311/406] Add BGE Counterflux. Support multiple BGEs. --- deck.cpp | 13 ---- deck.h | 6 -- sim.cpp | 30 +++++---- sim.h | 12 ++-- tyrant.cpp | 13 ++-- tyrant.h | 16 ++--- tyrant_optimize.cpp | 155 ++++++++++++++++++++++---------------------- xml.cpp | 30 +++------ 8 files changed, 118 insertions(+), 157 deletions(-) diff --git a/deck.cpp b/deck.cpp index 336c063f..72016a8a 100644 --- a/deck.cpp +++ b/deck.cpp @@ -375,10 +375,6 @@ std::string Deck::long_description() const { std::stringstream ios; ios << medium_description() << "\n"; - if(effect != Effect::none) - { - ios << "Effect: " << effect_names[effect] << "\n"; - } if (commander) { show_upgrades(ios, commander, ""); @@ -403,15 +399,6 @@ std::string Deck::long_description() const { ios << card_description(all_cards, fort) << "\n"; } - if (debug_print >= 2 && !reward_cards.empty()) - { - ios << "Reward Cards: "; - for (const auto & card : reward_cards) - { - ios << card->m_name << ", "; - } - ios << "\n"; - } return ios.str(); } diff --git a/deck.h b/deck.h index 015bad0b..61a78caf 100644 --- a/deck.h +++ b/deck.h @@ -46,7 +46,6 @@ class Deck DeckType::DeckType decktype; unsigned id; std::string name; - Effect effect; // for quests unsigned upgrade_points; unsigned upgrade_opportunities; DeckStrategy::DeckStrategy strategy; @@ -61,7 +60,6 @@ class Deck // card id -> card order std::map> order; std::vector>> raid_cards; - std::vector reward_cards; unsigned mission_req; std::string deck_string; @@ -74,7 +72,6 @@ class Deck DeckType::DeckType decktype_ = DeckType::deck, unsigned id_ = 0, std::string name_ = "", - Effect effect_ = Effect::none, unsigned upgrade_points_ = 0, unsigned upgrade_opportunities_ = 0, DeckStrategy::DeckStrategy strategy_ = DeckStrategy::random) : @@ -82,7 +79,6 @@ class Deck decktype(decktype_), id(id_), name(name_), - effect(Effect::none), upgrade_points(upgrade_points_), upgrade_opportunities(upgrade_opportunities_), strategy(strategy_), @@ -98,13 +94,11 @@ class Deck const Card* commander_, const std::vector& cards_, std::vector>> raid_cards_ = {}, - std::vector reward_cards_ = {}, unsigned mission_req_ = 0) { commander = commander_; cards = std::vector(std::begin(cards_), std::end(cards_)); raid_cards = std::vector>>(raid_cards_); - reward_cards = std::vector(reward_cards_); mission_req = mission_req_; } diff --git a/sim.cpp b/sim.cpp index 6bcce975..b7272a40 100644 --- a/sim.cpp +++ b/sim.cpp @@ -269,11 +269,11 @@ void prepend_on_death(Field* fd) } } } - if (fd->bg_skill.id == reaping) + if (fd->bg_effects.count(reaping)) { - SkillSpec ss_heal{heal, fd->bg_skill.x, allfactions, 0, 0, no_skill, no_skill, true,}; - SkillSpec ss_rally{rally, fd->bg_skill.x, allfactions, 0, 0, no_skill, no_skill, true,}; - _DEBUG_MSG(2, "Preparing %s skill %s and %s\n", status_description(status).c_str(), skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); + SkillSpec ss_heal{heal, fd->bg_effects.at(reaping), allfactions, 0, 0, no_skill, no_skill, true,}; + SkillSpec ss_rally{rally, fd->bg_effects.at(reaping), allfactions, 0, 0, no_skill, no_skill, true,}; + _DEBUG_MSG(2, "Reaping: Preparing %s skill %s and %s\n", status_description(status).c_str(), skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); od_skills.emplace_back(status, ss_heal); od_skills.emplace_back(status, ss_rally); } @@ -478,13 +478,14 @@ Results play(Field* fd) } if(__builtin_expect(fd->end, false)) { break; } - if (fd->bg_skill.id != no_skill && skill_table[fd->bg_skill.id]) + // Evaluate activation Battleground skills + for (const auto & bg_skill: fd->bg_skills) { - // Evaluate TU Battleground effect (Enhance all) - _DEBUG_MSG(2, "Evaluating Battleground skill %s\n", skill_description(fd->cards, fd->bg_skill).c_str()); - fd->skill_queue.emplace_back(&fd->tap->commander, fd->bg_skill); + _DEBUG_MSG(2, "Evaluating BG skill %s\n", skill_description(fd->cards, bg_skill).c_str()); + fd->skill_queue.emplace_back(&fd->tap->commander, bg_skill); resolve_skill(fd); } + if (__builtin_expect(fd->end, false)) { break; } // Evaluate commander fd->current_phase = Field::commander_phase; @@ -918,6 +919,13 @@ struct PerformAttack unsigned counter_dmg(counter_damage(fd, att_status, def_status)); _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, att_status, counter_dmg); + if (fd->bg_effects.count(counterflux)) + { + unsigned flux_value = (def_status->skill(counter) + 1) / 2; + _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n", status_description(def_status).c_str(), flux_value); + add_hp(fd, def_status, flux_value); + def_status->m_attack += flux_value; + } } unsigned berserk_value = att_status->skill(berserk); if(berserk_value > 0 && skill_check(fd, att_status, nullptr)) @@ -1087,9 +1095,9 @@ bool attack_phase(Field* fd) att_dmg = attack_commander(fd, att_status); } - if (att_dmg > 0 && !fd->assault_bloodlusted && fd->bg_skill.id == bloodlust) + if (att_dmg > 0 && !fd->assault_bloodlusted && fd->bg_effects.count(bloodlust)) { - fd->bloodlust_value += fd->bg_skill.x; + fd->bloodlust_value += fd->bg_effects.at(bloodlust); fd->assault_bloodlusted = true; } @@ -1268,7 +1276,7 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, c template inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) { - if(s.y == allfactions || fd->effect == metamorphosis) + if (s.y == allfactions || fd->bg_effects.count(metamorphosis)) { return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); } diff --git a/sim.h b/sim.h index 9a442376..4b9e11fe 100644 --- a/sim.h +++ b/sim.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -67,7 +68,6 @@ struct FinalResults void fill_skill_table(); Results play(Field* fd); -void modify_cards(Cards& cards, enum Effect effect); // Pool-based indexed storage. //---------------------- Pool-based indexed storage ---------------------------- template @@ -221,8 +221,8 @@ class Field unsigned turn; gamemode_t gamemode; OptimizationMode optimization_mode; - const Effect effect; - SkillSpec bg_skill; + std::unordered_map bg_effects; // passive BGE + std::vector bg_skills; // active BGE, casted every turn // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque> skill_queue; @@ -247,7 +247,7 @@ class Field unsigned current_ci; Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, OptimizationMode optimization_mode_, - Effect effect_, SkillSpec bg_skill_) : + std::unordered_map& bg_effects_, std::vector& bg_skills_) : end{false}, re(re_), cards(cards_), @@ -255,8 +255,8 @@ class Field turn(1), gamemode(gamemode_), optimization_mode(optimization_mode_), - effect(effect_), - bg_skill(bg_skill_), + bg_effects(bg_effects_), + bg_skills(bg_skills_), n_player_kills(0), assault_bloodlusted(false), bloodlust_value(0) diff --git a/tyrant.cpp b/tyrant.cpp index e49dd309..03ef0d92 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -16,7 +16,7 @@ std::string skill_names[Skill::num_skills] = "Enfeeble", "Jam", "Siege", "Strike", "Weaken", "", "", - "Enhance", "Evolve", "Heal", "Overload", "Protect", "Rally", + "Enhance", "Evolve", "Heal", "Mend", "Overload", "Protect", "Rally", "", // Defensive: "", @@ -25,12 +25,12 @@ std::string skill_names[Skill::num_skills] = // Combat-Modifier: "Flurry", "Pierce", "Valor", // Damage-Dependant: - "Berserk", "Inhibit", "Leech", "Poison", + "Berserk", "Inhibit", "Leech", "Poison", "Venom", // Triggered: "Legion", - // Pseudo-Skill for BGE: + // Pseudo-skill for passive BGEs: "", - "Bloodlust", "Reaping", + "Bloodlust", "Reaping", "Metamorphosis", "Counterflux", "", }; @@ -46,11 +46,6 @@ signed max_possible_score[]{100, 100, 100, 100, 100, 67, 100, 100}; std::string decktype_names[DeckType::num_decktypes]{"Deck", "Mission", "Raid", "Campaign", "Quest", "Custom Deck", }; -std::string effect_names[Effect::num_effects] = { - "None", - "Metamorphosis", -}; - signed debug_print(0); unsigned debug_cached(0); bool debug_line(false); diff --git a/tyrant.h b/tyrant.h index d801d696..6490ce8b 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.7.2" +#define TYRANT_OPTIMIZER_VERSION "2.8.0" #include #include @@ -32,7 +32,7 @@ enum Skill enfeeble, jam, siege, strike, weaken, END_ACTIVATION_HARMFUL, BEGIN_ACTIVATION_HELPFUL, - enhance, evolve, heal, overload, protect, rally, + enhance, evolve, heal, mend, overload, protect, rally, END_ACTIVATION_HELPFUL, // Defensive: BEGIN_DEFENSIVE, @@ -41,12 +41,12 @@ enum Skill // Combat-Modifier: flurry, pierce, valor, // Damage-Dependant: - berserk, inhibit, leech, poison, + berserk, inhibit, leech, poison, venom, // Triggered: legion, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, - bloodlust, reaping, + bloodlust, reaping, metamorphosis, counterflux, END_BGE_SKILL, num_skills }; @@ -82,14 +82,6 @@ enum DeckType { extern std::string decktype_names[DeckType::num_decktypes]; -enum Effect { - none, - metamorphosis, - num_effects -}; - -extern std::string effect_names[Effect::num_effects]; - enum gamemode_t { fight, diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 39d2a8b9..75b5ba47 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -456,11 +456,11 @@ struct SimulationData std::vector enemy_hands; std::vector factors; gamemode_t gamemode; - enum Effect effect; - SkillSpec bg_skill; + std::unordered_map bg_effects; + std::vector bg_skills; SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_enemy_decks_, std::vector factors_, gamemode_t gamemode_, - enum Effect effect_, SkillSpec bg_skill_) : + std::unordered_map& bg_effects_, std::vector& bg_skills_) : re(seed), cards(cards_), decks(decks_), @@ -469,8 +469,8 @@ struct SimulationData enemy_decks(num_enemy_decks_), factors(factors_), gamemode(gamemode_), - effect(effect_), - bg_skill(bg_skill_) + bg_effects(bg_effects_), + bg_skills(bg_skills_) { for (size_t i = 0; i < num_enemy_decks_; ++i) { @@ -501,7 +501,7 @@ struct SimulationData { your_hand.reset(re); enemy_hand->reset(re); - Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, effect != Effect::none ? effect : enemy_hand->deck->effect, bg_skill); + Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, bg_effects, bg_skills); Results result(play(&fd)); res.emplace_back(result); //MDJ @@ -531,7 +531,7 @@ struct SimulationData } your_hand.reset(re); enemy_hand->reset(re); - Field fd1(re, cards, your_hand, *enemy_hand, gamemode_t::fight, OptimizationMode::defense, effect != Effect::none ? effect : enemy_hand->deck->effect, bg_skill); + Field fd1(re, cards, your_hand, *enemy_hand, gamemode_t::fight, OptimizationMode::defense, bg_effects, bg_skills); Results result1(play(&fd1)); res.emplace_back(result1); if (!opt_forts.empty()) @@ -583,11 +583,11 @@ class Process const std::vector enemy_decks; std::vector factors; gamemode_t gamemode; - enum Effect effect; - SkillSpec bg_skill; + std::unordered_map bg_effects; + std::vector bg_skills; Process(unsigned num_threads_, const Cards& cards_, const Decks& decks_, Deck* your_deck_, std::vector enemy_decks_, std::vector factors_, gamemode_t gamemode_, - enum Effect effect_, SkillSpec bg_skill_) : + std::unordered_map& bg_effects_, std::vector& bg_skills_) : num_threads(num_threads_), main_barrier(num_threads+1), cards(cards_), @@ -596,14 +596,14 @@ class Process enemy_decks(enemy_decks_), factors(factors_), gamemode(gamemode_), - effect(effect_), - bg_skill(bg_skill_) + bg_effects(bg_effects_), + bg_skills(bg_skills_) { destroy_threads = false; unsigned seed(time(0)); for(unsigned i(0); i < num_threads; ++i) { - threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, effect, bg_skill)); + threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, bg_effects, bg_skills)); threads.push_back(new boost::thread(thread_evaluate, std::ref(main_barrier), std::ref(shared_mutex), std::ref(*threads_data.back()), std::ref(*this), i)); } } @@ -1316,20 +1316,11 @@ enum Operation { extern void(*skill_table[num_skills])(Field*, CardStatus* src_status, const SkillSpec&); void print_available_effects() { - std::cout << "Available effects (case-insensitive):\n" - " Enfeeble all X\n" - " Heal all X\n" - " Protect all X\n" - " Rally all X\n" - " Siege all X\n" - " Strike all X\n" - " Weaken all X\n" - " Enhance all X, where is either of Armor, Berserk, Corrosive, Counter, Enfeeble, Evade, Heal, Inhibit, Leech, Pierce, Poison, Protect, Rally, Siege, Strike, Weaken\n" - " Reaping X\n"; - for (int i(1); i < Effect::num_effects; ++ i) - { - std::cout << " " << effect_names[i] << "\n"; - } + std::cout << "Available effects besides activation skills:\n" + " Bloodlust X\n" + " Reaping X\n" + " Metamorphosis\n" + " Counterflux\n"; } void usage(int argc, char** argv) { @@ -1346,7 +1337,7 @@ void usage(int argc, char** argv) " example: \'fear:0.2;slowroll:0.8\' means fear is the defense deck 20% of the time, while slowroll is the defense deck 80% of the time.\n" "\n" "Flags:\n" - " -e \"\": set the battleground effect. effect is automatically set when applicable.\n" + " -e \"\": set the battleground effect; you may use -e multiple times.\n" " -r: the attack deck is played in order instead of randomly (respects the 3 cards drawn limit).\n" " -s: use surge (default is fight).\n" " -t : set the number of threads, default is 4.\n" @@ -1399,9 +1390,9 @@ int main(int argc, char** argv) bool opt_do_optimization(false); bool opt_keep_commander{false}; std::vector> opt_todo; - std::string opt_effect; - enum Effect opt_effect_id(Effect::none); - SkillSpec opt_bg_skill{no_skill, 0, allfactions, 0, 0, no_skill, no_skill, false}; + std::vector opt_effects; + std::unordered_map opt_bg_effects; + std::vector opt_bg_skills; for(int argIndex = 3; argIndex < argc; ++argIndex) { @@ -1492,7 +1483,7 @@ int main(int argc, char** argv) } else if (strcmp(argv[argIndex], "effect") == 0 || strcmp(argv[argIndex], "-e") == 0) { - opt_effect = argv[argIndex + 1]; + opt_effects.push_back(argv[argIndex + 1]); argIndex += 1; } else if (strcmp(argv[argIndex], "freeze") == 0 || strcmp(argv[argIndex], "-F") == 0) @@ -1696,45 +1687,50 @@ int main(int argc, char** argv) } } - if (! opt_effect.empty()) + for (const auto & opt_effect: opt_effects) { - std::map effect_map; - for(unsigned i(0); i < Effect::num_effects; ++i) - { - effect_map[boost::to_lower_copy(effect_names[i])] = static_cast(i); - std::stringstream ss; - ss << i; - effect_map[ss.str()] = static_cast(i); - } - std::vector tokens; - boost::split(tokens, opt_effect, boost::is_any_of(" -")); - - opt_bg_skill.id = skill_name_to_id(tokens[0], false); - unsigned skill_index = 1; - if (skill_table[opt_bg_skill.id] != nullptr || (BEGIN_BGE_SKILL < opt_bg_skill.id && opt_bg_skill.id < END_BGE_SKILL)) + try { - try + std::vector tokens; + boost::split(tokens, opt_effect, boost::is_any_of(" -")); + Skill skill_id = skill_name_to_id(tokens[0], false); + unsigned skill_index = 1; + if (BEGIN_BGE_SKILL < skill_id && skill_id < END_BGE_SKILL) { + // passive BGE + if (skill_index < tokens.size()) + { + opt_bg_effects[skill_id] = boost::lexical_cast(tokens[skill_index]); + } + else + { + opt_bg_effects[skill_id] = 0; + } + } + else if (skill_table[skill_id] != nullptr) + { + // activation BG skill + SkillSpec bg_skill{skill_id, 0, allfactions, 0, 0, no_skill, no_skill, false}; if (skill_index < tokens.size() && boost::to_lower_copy(tokens[skill_index]) == "all") { - opt_bg_skill.all = true; + bg_skill.all = true; skill_index += 1; } else if (skill_index + 1 < tokens.size() && isdigit(*tokens[skill_index].c_str())) { - opt_bg_skill.n = boost::lexical_cast(tokens[skill_index]); + bg_skill.n = boost::lexical_cast(tokens[skill_index]); skill_index += 1; } if (skill_index < tokens.size()) { - opt_bg_skill.s = skill_name_to_id(tokens[skill_index], false); - if (opt_bg_skill.s != no_skill) + bg_skill.s = skill_name_to_id(tokens[skill_index], false); + if (bg_skill.s != no_skill) { skill_index += 1; if (skill_index < tokens.size()) { - opt_bg_skill.s2 = skill_name_to_id(tokens[skill_index], false); - if (opt_bg_skill.s2 != no_skill) + bg_skill.s2 = skill_name_to_id(tokens[skill_index], false); + if (bg_skill.s2 != no_skill) { skill_index += 1; } @@ -1743,37 +1739,33 @@ int main(int argc, char** argv) } if (skill_index < tokens.size()) { - if (opt_bg_skill.id == jam || opt_bg_skill.id == overload) + if (bg_skill.id == jam || bg_skill.id == overload) { - opt_bg_skill.n = boost::lexical_cast(tokens[skill_index]); + bg_skill.n = boost::lexical_cast(tokens[skill_index]); } else { - opt_bg_skill.x = boost::lexical_cast(tokens[skill_index]); + bg_skill.x = boost::lexical_cast(tokens[skill_index]); } } + opt_bg_skills.push_back(bg_skill); } - catch (const boost::bad_lexical_cast & e) - { - std::cerr << "Error: Expect a number in effect \"" << opt_effect << "\".\n"; - return 0; - } - catch (std::exception & e) + else { - std::cerr << "Error: effect \"" << opt_effect << ": " << e.what() << "\".\n"; + std::cerr << "Error: unrecognized effect \"" << opt_effect << "\".\n"; + print_available_effects(); return 0; } } - else + catch (const boost::bad_lexical_cast & e) { - const auto & x = effect_map.find(boost::to_lower_copy(opt_effect)); - if(x == effect_map.end()) - { - std::cout << "Error: The effect \"" << opt_effect << "\" was not found.\n"; - print_available_effects(); - return 0; - } - opt_effect_id = static_cast(x->second); + std::cerr << "Error: Expect a number in effect \"" << opt_effect << "\".\n"; + return 0; + } + catch (std::exception & e) + { + std::cerr << "Error: effect \"" << opt_effect << ": " << e.what() << "\".\n"; + return 0; } } @@ -1945,17 +1937,24 @@ int main(int argc, char** argv) { std::cout << "Enemy's Deck:" << enemy_decks_factors[i] << ": " << (debug_print > 0 ? enemy_decks[i]->long_description() : enemy_decks[i]->medium_description()) << std::endl; } - if(opt_effect_id != Effect::none) + for (const auto & bg_effect: opt_bg_effects) { - std::cout << "Effect: " << effect_names[opt_effect_id] << std::endl; + if (bg_effect.second == 0) + { + std::cout << "BG Effect: " << skill_names[bg_effect.first] << std::endl; + } + else + { + std::cout << "BG Effect: " << skill_names[bg_effect.first] << " " << bg_effect.second << std::endl; + } } - else if(opt_bg_skill.id != no_skill) + for (const auto & bg_skill: opt_bg_skills) { - std::cout << "Effect: " << skill_description(all_cards, opt_bg_skill) << std::endl; + std::cout << "BG Skill: " << skill_description(all_cards, bg_skill) << std::endl; } } - Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, opt_effect_id, opt_bg_skill); + Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, opt_bg_effects, opt_bg_skills); { //ScopeClock timer; diff --git a/xml.cpp b/xml.cpp index 5a29f81e..0b1fcf6a 100644 --- a/xml.cpp +++ b/xml.cpp @@ -265,7 +265,7 @@ void load_cards_xml(Cards & all_cards, const char * filename) all_cards.organize(); } //------------------------------------------------------------------------------ -Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const char* effect_node_name, DeckType::DeckType decktype, unsigned id, std::string base_deck_name) +Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType::DeckType decktype, unsigned id, std::string base_deck_name) { xml_node<>* commander_node(node->first_node("commander")); const Card* card = all_cards.by_id(atoi(commander_node->value())); @@ -273,7 +273,6 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch unsigned upgrade_opportunities = card->m_top_level_card->m_level - card->m_level; std::vector always_cards; std::vector>> some_cards; - std::vector reward_cards; xml_node<>* deck_node(node->first_node("deck")); xml_node<>* levels_node(node->first_node("levels")); unsigned max_level = levels_node ? atoi(levels_node->value()) : 10; @@ -304,35 +303,22 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, const ch some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); upgrade_opportunities += upgrade_points * num_cards_from_pool / cards_from_pool.size(); } - xml_node<>* rewards_node(node->first_node("rewards")); - if(decktype == DeckType::mission && rewards_node) - { - for(xml_node<>* card_node = rewards_node->first_node("card"); - card_node; - card_node = card_node->next_sibling("card")) - { - card = all_cards.by_id(atoi(card_node->value())); - reward_cards.push_back(card); - } - } xml_node<>* mission_req_node(node->first_node(decktype == DeckType::mission ? "req" : "mission_req")); unsigned mission_req(mission_req_node ? atoi(mission_req_node->value()) : 0); - xml_node<>* effect_id_node(node->first_node(effect_node_name)); - Effect effect = effect_id_node ? static_cast(atoi(effect_id_node->value())) : Effect::none; for (unsigned level = 1; level < max_level; ++ level) { std::string deck_name = base_deck_name + "-" + to_string(level); - decks.decks.push_back(Deck{all_cards, decktype, id, deck_name, effect, (upgrade_opportunities + 1) * (level - 1) / (max_level - 1), upgrade_opportunities}); + decks.decks.push_back(Deck{all_cards, decktype, id, deck_name, (upgrade_opportunities + 1) * (level - 1) / (max_level - 1), upgrade_opportunities}); Deck* deck = &decks.decks.back(); - deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); + deck->set(commander_card, always_cards, some_cards, mission_req); decks.by_name[deck_name] = deck; decks.by_name[decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(level)] = deck; } - decks.decks.push_back(Deck{all_cards, decktype, id, base_deck_name, effect}); + decks.decks.push_back(Deck{all_cards, decktype, id, base_deck_name}); Deck* deck = &decks.decks.back(); - deck->set(commander_card, always_cards, some_cards, reward_cards, mission_req); + deck->set(commander_card, always_cards, some_cards, mission_req); // upgrade cards for full-level missions/raids if (max_level > 1) @@ -379,7 +365,7 @@ void read_missions(Decks& decks, const Cards& all_cards, std::string filename) std::string deck_name{name_node->value()}; try { - read_deck(decks, all_cards, mission_node, "effect", DeckType::mission, id, deck_name); + read_deck(decks, all_cards, mission_node, DeckType::mission, id, deck_name); } catch (const std::runtime_error& e) { @@ -410,7 +396,7 @@ void read_raids(Decks& decks, const Cards& all_cards, std::string filename) unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(raid_node->first_node("name")); std::string deck_name{name_node->value()}; - read_deck(decks, all_cards, raid_node, "effect", DeckType::raid, id, deck_name); + read_deck(decks, all_cards, raid_node, DeckType::raid, id, deck_name); } for(xml_node<>* campaign_node = root->first_node("campaign"); @@ -424,7 +410,7 @@ void read_raids(Decks& decks, const Cards& all_cards, std::string filename) name_node; name_node = name_node->next_sibling("name")) { - read_deck(decks, all_cards, campaign_node, "effect", DeckType::campaign, id, name_node->value()); + read_deck(decks, all_cards, campaign_node, DeckType::campaign, id, name_node->value()); } } } From f29efc8b643d7fccee721db778ec0e790690b481 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 7 May 2015 15:36:32 +0800 Subject: [PATCH 312/406] Add flag _ to load extra data files. --- read.cpp | 22 +++++++++------------- read.h | 5 ++--- tyrant_optimize.cpp | 41 ++++++++++++++++++++++------------------- xml.cpp | 32 +++++++++++++++++--------------- xml.h | 10 +++++----- 5 files changed, 55 insertions(+), 55 deletions(-) diff --git a/read.cpp b/read.cpp index 70e6ee10..1830df16 100644 --- a/read.cpp +++ b/read.cpp @@ -16,14 +16,6 @@ #include "cards.h" #include "deck.h" -void load_custom_decks(Decks& decks, Cards& all_cards, const char * filename) -{ - if(boost::filesystem::exists(filename)) - { - read_custom_decks(decks, all_cards, filename); - } -} - template Iterator advance_until(Iterator it, Iterator it_end, Functor f) { while(it != it_end) @@ -354,13 +346,17 @@ unsigned read_card_abbrs(Cards& all_cards, const std::string& filename) // Error codes: // 2 -> file not readable // 3 -> error while parsing file -unsigned read_custom_decks(Decks& decks, Cards& all_cards, std::string filename) +unsigned load_custom_decks(Decks& decks, Cards& all_cards, const std::string & filename) { + if (!boost::filesystem::exists(filename)) + { + return 0; + } std::ifstream decks_file(filename); - if(!decks_file.is_open()) + if (!decks_file.is_open()) { std::cerr << "Error: Custom deck file " << filename << " could not be opened\n"; - return(2); + return 2; } unsigned num_line(0); decks_file.exceptions(std::ifstream::badbit); @@ -405,7 +401,7 @@ unsigned read_custom_decks(Decks& decks, Cards& all_cards, std::string filename) std::cerr << " at line " << num_line; } std::cerr << ": " << e.what() << ".\n"; - return(3); + return 3; } return(0); } @@ -433,7 +429,7 @@ void add_owned_card(Cards& all_cards, std::map& owned_cards, } } -void read_owned_cards(Cards& all_cards, std::map& owned_cards, std::string filename) +void read_owned_cards(Cards& all_cards, std::map& owned_cards, const std::string & filename) { std::ifstream owned_file{filename}; if(!owned_file.good()) diff --git a/read.h b/read.h index 82508c54..49045a1a 100644 --- a/read.h +++ b/read.h @@ -11,12 +11,11 @@ class Cards; class Decks; class Deck; -void load_custom_decks(Decks& decks, Cards& cards, const char * filename); DeckList parse_deck_list(std::string list_string, const Decks& decks); void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark); const std::pair, std::map> string_to_ids(const Cards& all_cards, const std::string& deck_string, const std::string & description); -unsigned read_custom_decks(Decks& decks, Cards& cards, std::string filename); -void read_owned_cards(Cards& cards, std::map& owned_cards, std::string filename); +unsigned load_custom_decks(Decks& decks, Cards& cards, const std::string & filename); +void read_owned_cards(Cards& cards, std::map& owned_cards, const std::string & filename); unsigned read_card_abbrs(Cards& cards, const std::string& filename); #endif diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 75b5ba47..401463b6 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -40,7 +40,6 @@ #include "sim.h" #include "tyrant.h" #include "xml.h" -//#include "timer.hpp" namespace { gamemode_t gamemode{fight}; @@ -1385,8 +1384,8 @@ int main(int argc, char** argv) //std::string opt_forts, opt_enemy_forts; std::string opt_hand, opt_enemy_hand; std::string opt_vip; + std::vector fn_suffix_list{"",}; std::vector opt_owned_cards_str_list; - std::vector opt_custom_cards_str_list; bool opt_do_optimization(false); bool opt_keep_commander{false}; std::vector> opt_todo; @@ -1511,13 +1510,9 @@ int main(int argc, char** argv) opt_owned_cards_str_list.push_back(argv[argIndex] + 3); use_owned_cards = true; } - else if(strcmp(argv[argIndex], "-C") == 0) + else if(strncmp(argv[argIndex], "_", 1) == 0) { - opt_custom_cards_str_list.push_back("data/customcards.txt"); - } - else if(strncmp(argv[argIndex], "-C=", 3) == 0) - { - opt_custom_cards_str_list.push_back(argv[argIndex] + 3); + fn_suffix_list.push_back(argv[argIndex]); } else if(strcmp(argv[argIndex], "fund") == 0) { @@ -1662,24 +1657,32 @@ int main(int argc, char** argv) Cards all_cards; Decks decks; - load_cards_xml(all_cards, "data/cards.xml"); -#if 0 - for (const auto & cc_str: opt_custom_cards_str_list) + for (const auto & suffix: fn_suffix_list) { - process_custom_cards(cc_str); + load_cards_xml(all_cards, "data/cards" + suffix + ".xml", suffix.empty()); } -#endif - read_card_abbrs(all_cards, "data/cardabbrs.txt"); - load_decks_xml(decks, all_cards, "data/missions.xml", "data/raids.xml"); - load_custom_decks(decks, all_cards, "data/customdecks.txt"); - load_recipes_xml(all_cards, "data/fusion_recipes_cj2.xml"); + all_cards.organize(); + for (const auto & suffix: fn_suffix_list) + { + load_decks_xml(decks, all_cards, "data/missions" + suffix + ".xml", "data/raids" + suffix + ".xml", suffix.empty()); + load_recipes_xml(all_cards, "data/fusion_recipes_cj2" + suffix + ".xml", suffix.empty()); + read_card_abbrs(all_cards, "data/cardabbrs" + suffix + ".txt"); + } + for (const auto & suffix: fn_suffix_list) + { + load_custom_decks(decks, all_cards, "data/customdecks" + suffix + ".txt"); + } + fill_skill_table(); if (opt_do_optimization and use_owned_cards) { if (opt_owned_cards_str_list.empty()) - { // load default file is specify no file - opt_owned_cards_str_list.push_back("data/ownedcards.txt"); + { // load default files only if specify no -o= + for (const auto & suffix: fn_suffix_list) + { + opt_owned_cards_str_list.push_back("data/ownedcards" + suffix + ".txt"); + } } for (const auto & oc_str: opt_owned_cards_str_list) { diff --git a/xml.cpp b/xml.cpp index 0b1fcf6a..5b8617af 100644 --- a/xml.cpp +++ b/xml.cpp @@ -97,11 +97,11 @@ Skill skill_target_skill(xml_node<>* skill, const char* attribute) } //------------------------------------------------------------------------------ -void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_filename, const char * raid_filename) +void load_decks_xml(Decks& decks, const Cards& all_cards, const std::string & mission_filename, const std::string & raid_filename, bool do_warn_on_missing=true) { try { - read_missions(decks, all_cards, mission_filename); + read_missions(decks, all_cards, mission_filename, do_warn_on_missing); } catch (const rapidxml::parse_error& e) { @@ -109,7 +109,7 @@ void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_f } try { - read_raids(decks, all_cards, raid_filename); + read_raids(decks, all_cards, raid_filename, do_warn_on_missing); } catch(const rapidxml::parse_error& e) { @@ -118,12 +118,15 @@ void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_f } //------------------------------------------------------------------------------ -void parse_file(const char* filename, std::vector& buffer, xml_document<>& doc) +void parse_file(const std::string & filename, std::vector& buffer, xml_document<>& doc, bool do_warn_on_missing=true) { std::ifstream cards_stream(filename, std::ios::binary); - if(!cards_stream.good()) + if (!cards_stream.good()) { - std::cout << "Warning: The file '" << filename << "' does not exist. Proceeding without reading from this file.\n"; + if (do_warn_on_missing) + { + std::cerr << "Warning: The file '" << filename << "' does not exist. Proceeding without reading from this file.\n"; + } buffer.resize(1); buffer[0] = 0; doc.parse<0>(&buffer[0]); @@ -244,11 +247,11 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) card->m_top_level_card = top_card; } -void load_cards_xml(Cards & all_cards, const char * filename) +void load_cards_xml(Cards & all_cards, const std::string & filename, bool do_warn_on_missing=true) { std::vector buffer; xml_document<> doc; - parse_file(filename, buffer, doc); + parse_file(filename, buffer, doc, do_warn_on_missing); xml_node<>* root = doc.first_node(); if(!root) @@ -262,7 +265,6 @@ void load_cards_xml(Cards & all_cards, const char * filename) auto card = new Card(); parse_card_node(all_cards, card, card_node); } - all_cards.organize(); } //------------------------------------------------------------------------------ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType::DeckType decktype, unsigned id, std::string base_deck_name) @@ -341,11 +343,11 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType return deck; } //------------------------------------------------------------------------------ -void read_missions(Decks& decks, const Cards& all_cards, std::string filename) +void read_missions(Decks& decks, const Cards& all_cards, const std::string & filename, bool do_warn_on_missing=true) { std::vector buffer; xml_document<> doc; - parse_file(filename.c_str(), buffer, doc); + parse_file(filename.c_str(), buffer, doc, do_warn_on_missing); xml_node<>* root = doc.first_node(); if(!root) @@ -375,11 +377,11 @@ void read_missions(Decks& decks, const Cards& all_cards, std::string filename) } } //------------------------------------------------------------------------------ -void read_raids(Decks& decks, const Cards& all_cards, std::string filename) +void read_raids(Decks& decks, const Cards& all_cards, const std::string & filename, bool do_warn_on_missing=true) { std::vector buffer; xml_document<> doc; - parse_file(filename.c_str(), buffer, doc); + parse_file(filename.c_str(), buffer, doc, do_warn_on_missing); xml_node<>* root = doc.first_node(); if(!root) @@ -416,11 +418,11 @@ void read_raids(Decks& decks, const Cards& all_cards, std::string filename) } //------------------------------------------------------------------------------ -void load_recipes_xml(Cards& all_cards, const char * filename) +void load_recipes_xml(Cards& all_cards, const std::string & filename, bool do_warn_on_missing=true) { std::vector buffer; xml_document<> doc; - parse_file(filename, buffer, doc); + parse_file(filename, buffer, doc, do_warn_on_missing); xml_node<>* root = doc.first_node(); if(!root) diff --git a/xml.h b/xml.h index cef2d809..682cb897 100644 --- a/xml.h +++ b/xml.h @@ -9,10 +9,10 @@ class Decks; class Achievement; Skill skill_name_to_id(const std::string & name, bool do_warn=true); -void load_cards_xml(Cards & all_cards, const char * filename); -void load_decks_xml(Decks& decks, const Cards& all_cards, const char * mission_filename, const char * raid_filename); -void load_recipes_xml(Cards& all_cards, const char * filename); -void read_missions(Decks& decks, const Cards& all_cards, std::string filename); -void read_raids(Decks& decks, const Cards& all_cards, std::string filename); +void load_cards_xml(Cards & all_cards, const std::string & filename, bool do_warn_on_missing); +void load_decks_xml(Decks& decks, const Cards& all_cards, const std::string & mission_filename, const std::string & raid_filename, bool do_warn_on_missing); +void load_recipes_xml(Cards& all_cards, const std::string & filename, bool do_warn_on_missing); +void read_missions(Decks& decks, const Cards& all_cards, const std::string & filename, bool do_warn_on_missing); +void read_raids(Decks& decks, const Cards& all_cards, const std::string & filename, bool do_warn_on_missing); #endif From 42dd9462e97ee4367d8ddf5143b3588b9c889e45 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 8 May 2015 17:56:07 +0800 Subject: [PATCH 313/406] Fix bug for flag _. --- tyrant.h | 2 +- tyrant_optimize.cpp | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/tyrant.h b/tyrant.h index 6490ce8b..36f2f424 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.8.0" +#define TYRANT_OPTIMIZER_VERSION "2.8.1" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 401463b6..627e5452 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -25,14 +25,15 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include +#include #include +#include +#include +#include +#include +#include +#include #include "card.h" #include "cards.h" #include "deck.h" @@ -1681,7 +1682,11 @@ int main(int argc, char** argv) { // load default files only if specify no -o= for (const auto & suffix: fn_suffix_list) { - opt_owned_cards_str_list.push_back("data/ownedcards" + suffix + ".txt"); + std::string filename = "data/ownedcards" + suffix + ".txt"; + if (boost::filesystem::exists(filename)) + { + opt_owned_cards_str_list.push_back(filename); + } } } for (const auto & oc_str: opt_owned_cards_str_list) From ca796257751b9f50387bade7b6059b48ec53b386 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 13 May 2015 10:48:03 +0800 Subject: [PATCH 314/406] Offer unlimited common/rare cards as material for fund under flag endgame 1|2. --- tyrant_optimize.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 627e5452..7609df8b 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -129,7 +129,8 @@ unsigned get_required_cards_before_upgrade(const std::vector & car auto card_it = unresolved_cards.end(); auto card = *(-- card_it); unresolved_cards.erase(card_it); - if(owned_cards[card->m_id] < num_cards[card] && !card->m_recipe_cards.empty()) + if ((use_fused_card_level > 0 && card->m_set == 1000 && card->m_rarity <= 2 && card->m_level == 1) || // assume unlimited common/rare level-1 cards (standard set) under endgame 1|2 + (owned_cards[card->m_id] < num_cards[card] && !card->m_recipe_cards.empty())) { unsigned num_under = num_cards[card] - owned_cards[card->m_id]; num_cards[card] = owned_cards[card->m_id]; From b69c1f5d203a3256cd3c2755fdf4941e295f00f0 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 14 May 2015 12:40:47 +0800 Subject: [PATCH 315/406] Fix bug: Counterflux should work on assaults only. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index b7272a40..7024eaa0 100644 --- a/sim.cpp +++ b/sim.cpp @@ -919,7 +919,7 @@ struct PerformAttack unsigned counter_dmg(counter_damage(fd, att_status, def_status)); _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, att_status, counter_dmg); - if (fd->bg_effects.count(counterflux)) + if (def_cardtype == CardType::assault && fd->bg_effects.count(counterflux)) { unsigned flux_value = (def_status->skill(counter) + 1) / 2; _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n", status_description(def_status).c_str(), flux_value); From 77f87819898189b7f68d0ca241d4a317dfee30c2 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 16 May 2015 00:24:23 +0800 Subject: [PATCH 316/406] Fix bug: Counterflux should not heal died units. --- sim.cpp | 2 +- tyrant.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index 7024eaa0..64c3d906 100644 --- a/sim.cpp +++ b/sim.cpp @@ -919,7 +919,7 @@ struct PerformAttack unsigned counter_dmg(counter_damage(fd, att_status, def_status)); _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, att_status, counter_dmg); - if (def_cardtype == CardType::assault && fd->bg_effects.count(counterflux)) + if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects.count(counterflux)) { unsigned flux_value = (def_status->skill(counter) + 1) / 2; _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n", status_description(def_status).c_str(), flux_value); diff --git a/tyrant.h b/tyrant.h index 36f2f424..cd7ca60d 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.8.1" +#define TYRANT_OPTIMIZER_VERSION "2.8.2" #include #include From ede1448ea4100a5b06183b5d4b14911801987dd2 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 16 May 2015 00:28:36 +0800 Subject: [PATCH 317/406] Support Campaign Deidon. --- data/raids.xml | 462 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 3cdb9147..4015c3e1 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -1279,4 +1279,466 @@ + + 411 + Deid101 + Deidon Normal 1 + 1022 + 1 + + + 4472 + 4472 + 6648 + 6648 + 12822 + 16054 + 16746 + 16768 + 16774 + 16780 + + + + + + 412 + Deid102 + Deidon Normal 2 + 1022 + 1 + + + 443 + 443 + 6652 + 6652 + 7322 + 7907 + 12810 + 16768 + 16774 + 16780 + + + + + + 413 + Deid103 + Deidon Normal 3 + 1742 + 1 + + + 7322 + 11537 + 12807 + 14420 + 14426 + 16752 + 16773 + 16773 + 16774 + 16780 + + + + + + 414 + Deid104 + Deidon Normal 4 + 1022 + 1 + + + 443 + 443 + 2292 + 13958 + 16033 + 16120 + 16587 + 16768 + 16774 + 16780 + + + + + + 415 + Deid105 + Deidon Normal 5 + 1022 + 1 + + + 2292 + 4476 + 4476 + 4476 + 10773 + 11324 + 15323 + 16768 + 16774 + 16780 + + + + + + 416 + Deid106 + Deidon Normal 6 + 1748 + 1 + + + 5384 + 8346 + 10210 + 10780 + 12038 + 13815 + 16768 + 16779 + 16779 + 16780 + + + + + + 417 + Deid107 + Deidon Normal 7 + 1754 + 1 + + + 2578 + 6602 + 12439 + 13181 + 14034 + 15078 + 16768 + 16774 + 16780 + 16780 + + + + + + 421 + Deid201 + Deidon Heroic 1 + 1049 + 1 + + + 6929 + 7914 + 7920 + 12825 + 14426 + 16051 + 16749 + 16770 + 16776 + 16782 + + + + + + 422 + Deid202 + Deidon Heroic 2 + 1049 + 1 + + + 6932 + 6932 + 7328 + 12813 + 13418 + 14425 + 16755 + 16770 + 16776 + 16782 + + + + + + 423 + Deid203 + Deidon Heroic 3 + 1744 + 1 + + + 12813 + 12951 + 13418 + 13418 + 16057 + 16761 + 16773 + 16773 + 16776 + 16782 + + + + + + 424 + Deid204 + Deidon Heroic 4 + 1051 + 1 + + + 2572 + 11816 + 14023 + 14136 + 15703 + 16045 + 16045 + 16770 + 16776 + 16782 + + + + + + 425 + Deid205 + Deidon Heroic 5 + 1051 + 1 + + + 14635 + 15320 + 15329 + 15709 + 16194 + 16449 + 16770 + 16776 + 16782 + 16921 + + + + + + 426 + Deid206 + Deidon Heroic 6 + 1750 + 1 + + + 247 + 10213 + 12445 + 15709 + 16551 + 16770 + 16779 + 16779 + 16782 + 16959 + + + + + + 427 + Deid207 + Deidon Heroic 7 + 1756 + 1 + + + 2578 + 10782 + 13823 + 14032 + 14136 + 15084 + 16770 + 16776 + 16782 + 16782 + + + + + + 431 + Deid301 + Deidon Mythic 1 + 1051 + 1 + + + 7922 + 12837 + 12957 + 14431 + 14431 + 16063 + 16761 + 16773 + 16779 + 16785 + + + + + + 432 + Deid302 + Deidon Mythic 2 + 1051 + 1 + + + 7334 + 7922 + 11546 + 11546 + 12819 + 12957 + 12957 + 16773 + 16779 + 16785 + + + + + + 433 + Deid303 + Deidon Mythic 3 + 1747 + 1 + + + 7922 + 12963 + 13421 + 14437 + 15757 + 16599 + 16773 + 16773 + 16779 + 16785 + + + + + + 434 + Deid304 + Deidon Mythic 4 + 1052 + 1 + + + 2578 + 2578 + 12939 + 13901 + 14569 + 14569 + 16045 + 16773 + 16779 + 16785 + + + + + + 435 + Deid305 + Deidon Mythic 5 + 1052 + 1 + + + 12451 + 13385 + 15709 + 15709 + 15811 + 16203 + 16203 + 16773 + 16779 + 16785 + + + + + + 436 + Deid306 + Deidon Mythic 6 + 1753 + 1 + + + 10788 + 13679 + 14269 + 15547 + 15709 + 16773 + 16779 + 16779 + 16785 + 16929 + + + + + + 437 + Deid307 + Deidon Mythic 7 + 1759 + 1 + + + 11822 + 13823 + 14148 + 14269 + 14275 + 15092 + 16773 + 16779 + 16785 + 16785 + + + + From bf9cc158b145bc29b145606b8cc6ae09375e4ce1 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 17 May 2015 07:49:04 +0800 Subject: [PATCH 318/406] Support BGE Counterflux Y where Y is the denominator (X/Y). Y=4 by default. --- sim.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 64c3d906..b4b2cc30 100644 --- a/sim.cpp +++ b/sim.cpp @@ -921,7 +921,8 @@ struct PerformAttack remove_hp(fd, att_status, counter_dmg); if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects.count(counterflux)) { - unsigned flux_value = (def_status->skill(counter) + 1) / 2; + unsigned flux_denominator = fd->bg_effects.at(counterflux) ? fd->bg_effects.at(counterflux) : 4; + unsigned flux_value = (def_status->skill(counter) - 1) / flux_denominator + 1; _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n", status_description(def_status).c_str(), flux_value); add_hp(fd, def_status, flux_value); def_status->m_attack += flux_value; From 4e4aa665893225e516dd8785aa1251041d6868b0 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 17 May 2015 08:36:26 +0800 Subject: [PATCH 319/406] Show amount of units in optimized deck. --- tyrant_optimize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 7609df8b..1b643575 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -909,7 +909,7 @@ void print_deck_inline(const unsigned deck_cost, const FinalResults { std::cout << " #" << num_repeat; } - std::cout << std::endl; + std::cout << " = " << deck->cards.size() << " units" << std::endl; } //------------------------------------------------------------------------------ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, Requirement requirement) From e19f738d7f58b1c0dd09f6db26bc1f5adcd19a50 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 19 May 2015 10:44:12 +0800 Subject: [PATCH 320/406] Add skill Venom. --- sim.cpp | 22 ++++++++++++++++------ tyrant.cpp | 2 +- tyrant.h | 4 ++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/sim.cpp b/sim.cpp index b4b2cc30..cf4716a1 100644 --- a/sim.cpp +++ b/sim.cpp @@ -813,11 +813,14 @@ void turn_end_phase(Field* fd) _DEBUG_MSG(1, "%s refreshes %u health\n", status_description(&status).c_str(), refresh_value); add_hp(fd, &status, refresh_value); } - unsigned poison_dmg = safe_minus(status.m_poisoned + (status.m_poisoned ? status.m_enfeebled : 0), status.protected_value()); - if(poison_dmg > 0) + if (status.m_poisoned > 0) { - _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg); - remove_hp(fd, &status, poison_dmg); + unsigned poison_dmg = safe_minus(status.m_poisoned + status.m_enfeebled, status.protected_value()); + if (poison_dmg > 0) + { + _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg); + remove_hp(fd, &status, poison_dmg); + } } // end of the opponent's next turn for enemy units status.m_jammed = false; @@ -975,6 +978,12 @@ struct PerformAttack } } } + unsigned venom_value = att_status->skill(venom); + if (venom_value && def_status->m_poisoned > 0) + { + if (debug_print > 0) { desc += "+" + to_string(venom_value) + "(venom)"; } + att_dmg += venom_value; + } if (fd->bloodlust_value > 0) { if (debug_print > 0) { desc += "+" + to_string(fd->bloodlust_value) + "(bloodlust)"; } @@ -1035,8 +1044,8 @@ void PerformAttack::attack_damage() template<> void PerformAttack::damage_dependant_pre_oa() { - unsigned poison_value = att_status->skill(poison); - if(poison_value > def_status->m_poisoned && skill_check(fd, att_status, def_status)) + unsigned poison_value = std::max(att_status->skill(poison), att_status->skill(venom)); + if (poison_value > def_status->m_poisoned && skill_check(fd, att_status, def_status)) { // perform_skill_poison _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), poison_value); @@ -1466,6 +1475,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil void fill_skill_table() { memset(skill_table, 0, sizeof skill_table); + skill_table[besiege] = perform_targetted_hostile_fast; skill_table[enfeeble] = perform_targetted_hostile_fast; skill_table[enhance] = perform_targetted_allied_fast; skill_table[evolve] = perform_targetted_allied_fast; diff --git a/tyrant.cpp b/tyrant.cpp index 03ef0d92..fb179c30 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -13,7 +13,7 @@ std::string skill_names[Skill::num_skills] = "0", // Activation: "", - "Enfeeble", "Jam", "Siege", "Strike", "Weaken", + "Besiege", "Enfeeble", "Jam", "Siege", "Strike", "Weaken", "", "", "Enhance", "Evolve", "Heal", "Mend", "Overload", "Protect", "Rally", diff --git a/tyrant.h b/tyrant.h index cd7ca60d..f204d4ea 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.8.2" +#define TYRANT_OPTIMIZER_VERSION "2.9.0" #include #include @@ -29,7 +29,7 @@ enum Skill attack, // Activation: BEGIN_ACTIVATION_HARMFUL, // TODO skill traits - enfeeble, jam, siege, strike, weaken, + besiege, enfeeble, jam, siege, strike, weaken, END_ACTIVATION_HARMFUL, BEGIN_ACTIVATION_HELPFUL, enhance, evolve, heal, mend, overload, protect, rally, From ec0411372a227f12025fb7b835622ba1d4dba525 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 21 May 2015 07:41:01 +0800 Subject: [PATCH 321/406] Move amount of units to beginning of line. --- tyrant_optimize.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 1b643575..9ded2858 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -851,6 +851,7 @@ void print_results(const EvaluatedResults& results, std::vector& fa //------------------------------------------------------------------------------ void print_deck_inline(const unsigned deck_cost, const FinalResults score, Deck * deck) { + std::cout << deck->cards.size() << " units: "; if(fund > 0) { std::cout << "$" << deck_cost << " "; @@ -909,7 +910,7 @@ void print_deck_inline(const unsigned deck_cost, const FinalResults { std::cout << " #" << num_repeat; } - std::cout << " = " << deck->cards.size() << " units" << std::endl; + std::cout << std::endl; } //------------------------------------------------------------------------------ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, Requirement requirement) From db5e6ef0cfeb4fc3203ce60b73c82d01957a9391 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 21 May 2015 07:41:41 +0800 Subject: [PATCH 322/406] Support Raid #10 Gore Typhon --- data/raids.xml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 4015c3e1..24317d66 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -2,6 +2,8 @@ + + 1 Apocalypse @@ -208,6 +210,35 @@ + + 10 + Gore Typhon + 1760 + 26 + + + 30236 + 30246 + 30256 + 30266 + 30276 + 8658 + + + 30236 + 30246 + 30256 + 30266 + 30276 + + + + + + + + + 1 Tartarus Vanguard 1 @@ -1741,4 +1772,6 @@ + + From 492216e51f6496f3cfe49013e771c16488237766 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 21 May 2015 17:53:41 +0800 Subject: [PATCH 323/406] Fix debug info. --- sim.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index cf4716a1..bcc2df3d 100644 --- a/sim.cpp +++ b/sim.cpp @@ -351,7 +351,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector } else { - _DEBUG_MSG(2, "Assault %s cannot take attack.\n", status_description(status).c_str()); + _DEBUG_MSG(2, "%s cannot take attack.\n", status_description(status).c_str()); } } // Flurry @@ -499,7 +499,7 @@ Results play(Field* fd) CardStatus* current_status(&fd->tap->structures[fd->current_ci]); if(!is_active(current_status) || !can_act(current_status)) { - _DEBUG_MSG(2, "Structure %s cannot take action.\n", status_description(current_status).c_str()); + _DEBUG_MSG(2, "%s cannot take action.\n", status_description(current_status).c_str()); } else { @@ -516,7 +516,7 @@ Results play(Field* fd) bool attacked = false; if(!is_active(current_status) || !can_act(current_status)) { - _DEBUG_MSG(2, "Assault %s cannot take action.\n", status_description(current_status).c_str()); + _DEBUG_MSG(2, "%s cannot take action.\n", status_description(current_status).c_str()); } else { From 009f400c3154a0798f63621a3fd2063b03670c4b Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 22 May 2015 08:31:22 +0800 Subject: [PATCH 324/406] Fix bug: raid damage. --- deck.cpp | 1 + deck.h | 7 +++++++ sim.cpp | 4 ++-- tyrant.h | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/deck.cpp b/deck.cpp index 72016a8a..03ceca5f 100644 --- a/deck.cpp +++ b/deck.cpp @@ -252,6 +252,7 @@ void Deck::set(const std::vector& ids, const std::map &m { throw std::runtime_error("While constructing a deck: no commander found"); } + deck_size = ids.size(); card_marks = marks; } diff --git a/deck.h b/deck.h index 61a78caf..f5cd7814 100644 --- a/deck.h +++ b/deck.h @@ -60,6 +60,7 @@ class Deck // card id -> card order std::map> order; std::vector>> raid_cards; + unsigned deck_size; unsigned mission_req; std::string deck_string; @@ -84,6 +85,7 @@ class Deck strategy(strategy_), commander(nullptr), shuffled_commander(nullptr), + deck_size(0), mission_req(0) { } @@ -99,6 +101,11 @@ class Deck commander = commander_; cards = std::vector(std::begin(cards_), std::end(cards_)); raid_cards = std::vector>>(raid_cards_); + deck_size = cards.size(); + for (const auto & pool: raid_cards) + { + deck_size += pool.first; + } mission_req = mission_req_; } diff --git a/sim.cpp b/sim.cpp index bcc2df3d..06be3ddc 100644 --- a/sim.cpp +++ b/sim.cpp @@ -550,7 +550,7 @@ Results play(Field* fd) ++fd->turn; } const auto & p = fd->players; - unsigned raid_damage = 15 + (std::min(p[1]->deck->cards.size(), (fd->turn + 1) / 2) - p[1]->assaults.size() - p[1]->structures.size()) - (10 * p[1]->commander.m_hp / p[1]->commander.m_max_hp); + unsigned raid_damage = 15 + (std::min(p[1]->deck->deck_size, (fd->turn + 1) / 2) - p[1]->assaults.size() - p[1]->structures.size()) - (10 * p[1]->commander.m_hp / p[1]->commander.m_max_hp); // you lose if(fd->players[0]->commander.m_hp == 0) { @@ -579,7 +579,7 @@ Results play(Field* fd) } case OptimizationMode::campaign: { - unsigned campaign_score = 100 - (std::min(p[0]->deck->cards.size(), (fd->turn + 1) / 2) - p[0]->assaults.size() - p[0]->structures.size()) * 10; + unsigned campaign_score = 100 - (std::min(p[0]->deck->deck_size, (fd->turn + 1) / 2) - p[0]->assaults.size() - p[0]->structures.size()) * 10; return {1, 0, 0, campaign_score, 0}; } default: return {1, 0, 0, 100, 0}; diff --git a/tyrant.h b/tyrant.h index f204d4ea..961ceab3 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.9.0" +#define TYRANT_OPTIMIZER_VERSION "2.9.1" #include #include From 74b29caa784b4b3536db5239fd33cc7261722ca0 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 23 May 2015 07:26:46 +0800 Subject: [PATCH 325/406] Update Raid #10 "Gore Typhon Raid". --- data/raids.xml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index 24317d66..c5098102 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -212,25 +212,22 @@ 10 - Gore Typhon + Gore Typhon Raid 1760 26 30236 30246 + 30246 + 30256 30256 30266 + 30266 30276 8658 + 8686 - - 30236 - 30246 - 30256 - 30266 - 30276 - From 0d1dbf0cf182218aeabda4083008d974f9deb119 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 12 Jun 2015 05:07:51 +0800 Subject: [PATCH 326/406] Simulate in-game behavior of Evolve: target on inactive units even if s2 is an activation skill. --- sim.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sim.cpp b/sim.cpp index 06be3ddc..84d5e89a 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1147,6 +1147,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { + return dst->has_skill(s.s) && !dst->has_skill(s.s2); // XXX in-game behavior return dst->has_skill(s.s) && !dst->has_skill(s.s2) && ((BEGIN_DEFENSIVE < s.s2 && s.s2 < END_DEFENSIVE) || is_active(dst)); } From 884fb727f34b4094685a5d5e0729592320f76e93 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 12 Jun 2015 09:52:07 +0800 Subject: [PATCH 327/406] Fix bug: campaign score. --- deck.cpp | 2 +- sim.cpp | 2 +- tyrant.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deck.cpp b/deck.cpp index 03ceca5f..c2bb5dc2 100644 --- a/deck.cpp +++ b/deck.cpp @@ -252,7 +252,7 @@ void Deck::set(const std::vector& ids, const std::map &m { throw std::runtime_error("While constructing a deck: no commander found"); } - deck_size = ids.size(); + deck_size = cards.size(); card_marks = marks; } diff --git a/sim.cpp b/sim.cpp index 84d5e89a..95dedd6c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -579,7 +579,7 @@ Results play(Field* fd) } case OptimizationMode::campaign: { - unsigned campaign_score = 100 - (std::min(p[0]->deck->deck_size, (fd->turn + 1) / 2) - p[0]->assaults.size() - p[0]->structures.size()) * 10; + unsigned campaign_score = 100 - 10 * (std::min(p[0]->deck->cards.size(), (fd->turn + 1) / 2) - p[0]->assaults.size() - p[0]->structures.size()); return {1, 0, 0, campaign_score, 0}; } default: return {1, 0, 0, 100, 0}; diff --git a/tyrant.h b/tyrant.h index 961ceab3..06158502 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.9.1" +#define TYRANT_OPTIMIZER_VERSION "2.9.2" #include #include From 14486c088db8fa5e17b73ef9d3cef30fd4bf1b44 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 12 Jun 2015 09:53:19 +0800 Subject: [PATCH 328/406] Support Campaign Kalihmah. --- data/raids.xml | 590 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 590 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index c5098102..a9b9f2db 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -1769,6 +1769,596 @@ + + 511 + Kali101 + Kalihmah Normal 1 + 1150 + 1 + + + 4460 + 4460 + 4460 + 6636 + 10369 + 10647 + 12768 + 30450 + 30454 + 30460 + + + + + + 512 + Kali102 + Kalihmah Normal 2 + 1150 + 1 + + + 4460 + 4460 + 6636 + 6636 + 12197 + 12278 + 15712 + 30450 + 30454 + 30460 + + + + + + 513 + Kali103 + Kalihmah Normal 3 + 1776 + 1 + + + 650 + 8648 + 11414 + 12898 + 13005 + 16512 + 30450 + 30450 + 30454 + 30460 + + + + + + 514 + Kali104 + Kalihmah Normal 4 + 1150 + 1 + + + 6632 + 6632 + 7220 + 8648 + 12508 + 14695 + 14977 + 30448 + 30456 + 30460 + + + + + + 515 + Kali105 + Kalihmah Normal 5 + 1150 + 1 + + + 4506 + 6636 + 6636 + 6636 + 12894 + 14389 + 16401 + 30448 + 30456 + 30460 + + + + + + 516 + Kali106 + Kalihmah Normal 6 + 1782 + 1 + + + 5276 + 5279 + 11840 + 12281 + 15937 + 30348 + 30448 + 30456 + 30456 + 30460 + + + + + + 517 + Kali107 + Kalihmah Normal 7 + 1788 + 1 + + + 11275 + 11502 + 13349 + 14593 + 15631 + 16989 + 30448 + 30454 + 30460 + 30460 + + + + + + 521 + Kali201 + Kalihmah Heroic 1 + 1796 + 1 + + + 8435 + 10372 + 10372 + 15014 + 15571 + 15724 + 16266 + 30453 + 30456 + 30462 + + + + + + 522 + Kali202 + Kalihmah Heroic 2 + 1797 + 1 + + + 2283 + 7058 + 10378 + 10384 + 10384 + 10656 + 16887 + 30453 + 30456 + 30462 + + + + + + 523 + Kali203 + Kalihmah Heroic 3 + 1778 + 1 + + + 10749 + 10749 + 11420 + 14707 + 14983 + 30309 + 30453 + 30453 + 30456 + 30462 + + + + + + 524 + Kali204 + Kalihmah Heroic 4 + 1213 + 1 + + + 5652 + 7628 + 10749 + 12511 + 13011 + 14704 + 14704 + 30450 + 30459 + 30462 + + + + + + 525 + Kali205 + Kalihmah Heroic 5 + 1213 + 1 + + + 4512 + 11843 + 11852 + 12900 + 13017 + 14395 + 16521 + 30450 + 30459 + 30462 + + + + + + 526 + Kali206 + Kalihmah Heroic 6 + 1784 + 1 + + + 5282 + 11504 + 12287 + 12287 + 15679 + 16357 + 30450 + 30459 + 30459 + 30462 + + + + + + 527 + Kali207 + Kalihmah Heroic 7 + 1793 + 1 + + + 4083 + 11912 + 13349 + 14049 + 14605 + 16989 + 30450 + 30456 + 30462 + 30462 + + + + + + 531 + Kali301 + Kalihmah Mythic 1 + 1799 + 1 + + + 7292 + 10384 + 15019 + 15019 + 15727 + 30453 + 30459 + 30465 + + + + + + 532 + Kali302 + Kalihmah Mythic 2 + 1799 + 1 + + + 10662 + 10883 + 10883 + 11227 + 11227 + 15583 + 15583 + 30453 + 30459 + 30465 + + + + + + 533 + Kali303 + Kalihmah Mythic 3 + 1781 + 1 + + + 11426 + 11426 + 12909 + 13553 + 14707 + 14707 + 30453 + 30453 + 30459 + 30465 + + + + + + 534 + Kali304 + Kalihmah Mythic 4 + 1216 + 1 + + + 8657 + 10752 + 14707 + 14707 + 14989 + 14989 + 30453 + 30459 + 30465 + 30597 + + + + + + 535 + Kali305 + Kalihmah Mythic 5 + 1216 + 1 + + + 4512 + 4560 + 4560 + 16527 + 16527 + 30315 + 30315 + 30453 + 30459 + 30465 + + + + + + 536 + Kali306 + Kalihmah Mythic 6 + 1787 + 1 + + + 5288 + 5288 + 5789 + 10883 + 16947 + 16989 + 30453 + 30459 + 30459 + 30465 + + + + + + 531 + Kali301 + Kalihmah Mythic 1 + 1799 + 1 + + + 7292 + 10384 + 15019 + 15019 + 15727 + 30453 + 30459 + 30465 + + + + + + 532 + Kali302 + Kalihmah Mythic 2 + 1799 + 1 + + + 10662 + 10883 + 10883 + 11227 + 11227 + 15583 + 15583 + 30453 + 30459 + 30465 + + + + + + 533 + Kali303 + Kalihmah Mythic 3 + 1781 + 1 + + + 11426 + 11426 + 12909 + 13553 + 14707 + 14707 + 30453 + 30453 + 30459 + 30465 + + + + + + 534 + Kali304 + Kalihmah Mythic 4 + 1216 + 1 + + + 8657 + 10752 + 14707 + 14707 + 14989 + 14989 + 30453 + 30459 + 30465 + 30597 + + + + + + 535 + Kali305 + Kalihmah Mythic 5 + 1216 + 1 + + + 4512 + 4560 + 4560 + 16527 + 16527 + 30315 + 30315 + 30453 + 30459 + 30465 + + + + + + 536 + Kali306 + Kalihmah Mythic 6 + 1787 + 1 + + + 5288 + 5288 + 5789 + 10883 + 16947 + 16989 + 30453 + 30459 + 30459 + 30465 + + + + + + 537 + Kali307 + Kalihmah Mythic 7 + 1793 + 1 + + + 7298 + 7976 + 13355 + 13355 + 14052 + 15745 + 30453 + 30459 + 30465 + 30465 + + + + From 75e2cf4e3661ad69ff0e49477fa146c6fc68f1d3 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 12 Jun 2015 17:21:39 +0800 Subject: [PATCH 329/406] Support BGE Fortification. --- sim.cpp | 20 ++++++++++++++++++++ tyrant.cpp | 2 +- tyrant.h | 4 ++-- tyrant_optimize.cpp | 3 ++- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/sim.cpp b/sim.cpp index 95dedd6c..d695249c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -998,6 +998,26 @@ struct PerformAttack std::string reduced_desc; unsigned reduced_dmg(0); unsigned armor_value = def_status->skill(armor); + if (fd->bg_effects.count(fortification)) + { + auto & assaults = fd->players[def_status->m_player]->assaults; + if (def_status->m_index > 0) + { + auto left_status = &assaults[def_status->m_index - 1]; + if (left_status->m_hp > 0) + { + armor_value = std::max(armor_value, left_status->skill(armor)); + } + } + if (def_status->m_index + 1 < assaults.size()) + { + auto right_status = &assaults[def_status->m_index + 1]; + if (right_status->m_hp > 0) + { + armor_value = std::max(armor_value, right_status->skill(armor)); + } + } + } if(armor_value > 0) { if(debug_print > 0) { reduced_desc += to_string(armor_value) + "(armor)"; } diff --git a/tyrant.cpp b/tyrant.cpp index fb179c30..c37c167a 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -30,7 +30,7 @@ std::string skill_names[Skill::num_skills] = "Legion", // Pseudo-skill for passive BGEs: "", - "Bloodlust", "Reaping", "Metamorphosis", "Counterflux", + "Bloodlust", "Reaping", "Metamorphosis", "Counterflux", "Fortification", "", }; diff --git a/tyrant.h b/tyrant.h index 06158502..c28ebe45 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.9.2" +#define TYRANT_OPTIMIZER_VERSION "2.9.3" #include #include @@ -46,7 +46,7 @@ enum Skill legion, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, - bloodlust, reaping, metamorphosis, counterflux, + bloodlust, reaping, metamorphosis, counterflux, fortification, END_BGE_SKILL, num_skills }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 9ded2858..250072e8 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1322,7 +1322,8 @@ void print_available_effects() " Bloodlust X\n" " Reaping X\n" " Metamorphosis\n" - " Counterflux\n"; + " Counterflux\n" + " Fortification\n"; } void usage(int argc, char** argv) { From 4a2106bf3ffe69e1ca4b36866f6cba1c74c18208 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 17 Jun 2015 11:53:15 +0800 Subject: [PATCH 330/406] Support -e "": no effect. --- tyrant_optimize.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 250072e8..a9e70a51 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1700,6 +1700,10 @@ int main(int argc, char** argv) for (const auto & opt_effect: opt_effects) { + if (opt_effect.empty()) + { + continue; + } try { std::vector tokens; From 0aa49159dbfea2b2c6554eb669ec5d5334d19d2e Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 17 Jun 2015 11:54:12 +0800 Subject: [PATCH 331/406] Add skill Mend. --- sim.cpp | 40 +++++++++++++++++++++++++++++++++++++++- tyrant.h | 2 +- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index d695249c..01cd41b2 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1171,6 +1171,10 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, return dst->has_skill(s.s) && !dst->has_skill(s.s2) && ((BEGIN_DEFENSIVE < s.s2 && s.s2 < END_DEFENSIVE) || is_active(dst)); } +template<> +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ return(can_be_healed(dst)); } + template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { return(can_be_healed(dst)); } @@ -1255,6 +1259,12 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, c dst->m_evolved_skill_offset[primary_s2] = s.s - primary_s2; } +template<> +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ + add_hp(fd, dst, s.x); +} + template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { @@ -1317,6 +1327,30 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector } } +template<> +inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) +{ + fd->selection_array.clear(); + auto & assaults = fd->players[src_status->m_player]->assaults; + if (src_status->m_index > 0) + { + auto left_status = &assaults[src_status->m_index - 1]; + if (skill_predicate(fd, src_status, left_status, s)) + { + fd->selection_array.push_back(left_status); + } + } + if (src_status->m_index + 1 < assaults.size()) + { + auto right_status = &assaults[src_status->m_index + 1]; + if (skill_predicate(fd, src_status, right_status, s)) + { + fd->selection_array.push_back(right_status); + } + } + return fd->selection_array.size(); +} + inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src_status) { return(fd->players[opponent(src_status->m_player)]->assaults.m_indirect); @@ -1353,6 +1387,9 @@ template<> std::vector& skill_targets(Field* fd, CardStatu template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } @@ -1439,7 +1476,7 @@ size_t select_targets(Field* fd, CardStatus* src_status, const SkillSpec& s) } _DEBUG_SELECTION("%s", skill_names[skill_id].c_str()); unsigned n_targets = s.n > 0 ? s.n : 1; - if (s.all || n_targets >= n_candidates) // target all + if (s.all || n_targets >= n_candidates || skill_id == mend) // target all or mend { return n_candidates; } @@ -1502,6 +1539,7 @@ void fill_skill_table() skill_table[evolve] = perform_targetted_allied_fast; skill_table[heal] = perform_targetted_allied_fast; skill_table[jam] = perform_targetted_hostile_fast; + skill_table[mend] = perform_targetted_allied_fast; skill_table[overload] = perform_targetted_allied_fast; skill_table[protect] = perform_targetted_allied_fast; skill_table[rally] = perform_targetted_allied_fast; diff --git a/tyrant.h b/tyrant.h index c28ebe45..d41d209b 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.9.3" +#define TYRANT_OPTIMIZER_VERSION "2.10.0" #include #include From 89c26080ba64cfe9c58527ed4903660c4b0c9343 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 18 Jun 2015 14:21:43 +0800 Subject: [PATCH 332/406] Fix bug: BGE Fortification. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 01cd41b2..b7a804c1 100644 --- a/sim.cpp +++ b/sim.cpp @@ -998,7 +998,7 @@ struct PerformAttack std::string reduced_desc; unsigned reduced_dmg(0); unsigned armor_value = def_status->skill(armor); - if (fd->bg_effects.count(fortification)) + if (def_status->m_card->m_type == CardType::assault && fd->bg_effects.count(fortification)) { auto & assaults = fd->players[def_status->m_player]->assaults; if (def_status->m_index > 0) From 156ea97efc717d71db3969dbaa8495b23b2d5a9f Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 18 Jun 2015 14:27:11 +0800 Subject: [PATCH 333/406] Support Raid #11 "Supremacy Raid". --- data/raids.xml | 21 +++++++++++++++++++++ tyrant.h | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/data/raids.xml b/data/raids.xml index a9b9f2db..6d15deab 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -231,6 +231,27 @@ + + 11 + Supremacy Raid + 1815 + 26 + + + 31116 + 31116 + 31126 + 31126 + 31136 + 31136 + 31146 + 31146 + 31156 + 31156 + + + + diff --git a/tyrant.h b/tyrant.h index d41d209b..f1ddfef2 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.0" +#define TYRANT_OPTIMIZER_VERSION "2.10.1" #include #include From 16778b3dcbe4844eda168acbaf3468dff3817c9f Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 26 Jun 2015 20:53:35 +0800 Subject: [PATCH 334/406] Update card id ranges. --- xml.cpp | 84 +++++++++++++++++++++++++++------------------------------ 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/xml.cpp b/xml.cpp index 5b8617af..587e9c68 100644 --- a/xml.cpp +++ b/xml.cpp @@ -20,25 +20,6 @@ // mission only and test cards have no set using namespace rapidxml; -Faction map_to_faction(unsigned i) -{ - return(i == 1 ? imperial : - i == 2 ? raider : - i == 3 ? bloodthirsty : - i == 4 ? xeno : - i == 5 ? righteous : - i == 6 ? progenitor : - allfactions); -} - -CardType::CardType map_to_type(unsigned i) -{ - return(i == 1 ? CardType::commander : - i == 2 ? CardType::assault : - i == 4 ? CardType::structure : - CardType::num_cardtypes); -} - Skill skill_name_to_id(const std::string & name, bool do_warn) { static std::map skill_map; @@ -70,13 +51,12 @@ Skill skill_name_to_id(const std::string & name, bool do_warn) Faction skill_faction(xml_node<>* skill) { - unsigned unmapped_faction(0); xml_attribute<>* y(skill->first_attribute("y")); - if(y) + if (y) { - unmapped_faction = atoi(y->value()); + return static_cast(atoi(y->value())); } - return(unmapped_faction == 0 ? allfactions : map_to_faction(unmapped_faction)); + return allfactions; } unsigned node_value(xml_node<>* skill, const char* attribute, unsigned default_value = 0) @@ -172,33 +152,47 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) if (name_node) { card->m_name = name_node->value(); } if (level_node) { card->m_level = atoi(level_node->value()); } if (fusion_level_node) { card->m_fusion_level = atoi(fusion_level_node->value()); } + if (attack_node) { card->m_attack = atoi(attack_node->value()); } + if (health_node) { card->m_health = atoi(health_node->value()); } + if (cost_node) { card->m_delay = atoi(cost_node->value()); } if (id_node) { - if (card->m_id < 1000) - { card->m_type = CardType::assault; } - else if (card->m_id < 2000) + if (cost_node) + { + if (attack_node) + { + if (card->m_attack == 0) + { + if (card->m_id < 1000) + { card->m_type = CardType::assault; } + else if (card->m_id < 2000) + { card->m_type = CardType::commander; } + else if (card->m_id < 3000) + { card->m_type = CardType::structure; } + else if (card->m_id < 8000) + { card->m_type = CardType::assault; } + else if (card->m_id < 10000) + { card->m_type = CardType::structure; } + else if (card->m_id < 17000) + { card->m_type = CardType::assault; } + else if (card->m_id < 25000) + { card->m_type = CardType::structure; } + else if (card->m_id < 30000) + { card->m_type = CardType::commander; } + else + { card->m_type = CardType::assault; } + } + else // attack > 0: must be assault + { card->m_type = CardType::assault; } + } + else // no attack_node: must be structure + { card->m_type = CardType::structure; } + } + else // no cost_node: must be commander { card->m_type = CardType::commander; } - else if (card->m_id < 3000) - { card->m_type = CardType::structure; } -#if 0 - else if (card->m_id < 4000) - { card->m_type = CardType::action; } -#endif - else if (card->m_id < 8000) - { card->m_type = CardType::assault; } - else if (card->m_id < 10000) - { card->m_type = CardType::structure; } - else - { card->m_type = cost_node ? (attack_node ? CardType::assault : CardType::structure) : CardType::commander; } -#if 0 - { card->m_type = cost_node ? (attack_node ? CardType::assault : CardType::structure) : (health_node ? CardType::commander : CardType::action); } -#endif } - if(attack_node) { card->m_attack = atoi(attack_node->value()); } - if(health_node) { card->m_health = atoi(health_node->value()); } - if(cost_node) { card->m_delay = atoi(cost_node->value()); } if(rarity_node) { card->m_rarity = atoi(rarity_node->value()); } - if(type_node) { card->m_faction = map_to_faction(atoi(type_node->value())); } + if(type_node) { card->m_faction = static_cast(atoi(type_node->value())); } card->m_set = set; if (card_node->first_node("skill")) From fe9d59838746ee2f375cfa36ed4cc326ebf967b8 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 28 Jun 2015 00:27:50 +0800 Subject: [PATCH 335/406] Remove flags tw and +stdev. Add flag quest. --- sim.cpp | 86 ++++-- sim.h | 51 +++- tyrant.cpp | 8 +- tyrant.h | 23 +- tyrant_optimize.cpp | 687 +++++++++++++++++++------------------------- xml.cpp | 7 +- 6 files changed, 432 insertions(+), 430 deletions(-) diff --git a/sim.cpp b/sim.cpp index b7a804c1..4ac9aedf 100644 --- a/sim.cpp +++ b/sim.cpp @@ -357,6 +357,10 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector // Flurry if (can_act(status) && fd->tip->commander.m_hp > 0 && status->has_skill(flurry) && status->m_skill_cd[flurry] == 0) { + if (status->m_player == 0) + { + fd->inc_counter(QuestType::skill_use, flurry); + } _DEBUG_MSG(1, "%s activates Flurry\n", status_description(status).c_str()); num_actions = 2; for (const auto & ss : skills) @@ -405,6 +409,11 @@ struct PlayCard status->set(card); status->m_index = storage->size() - 1; status->m_player = fd->tapi; + if (status->m_player == 0) + { + fd->inc_counter(QuestType::faction_card_use, card->m_faction); + fd->inc_counter(QuestType::type_card_use, type); + } _DEBUG_MSG(1, "%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), static_cast(storage->size() - 1), card_description(fd->cards, card).c_str()); if (status->m_delay == 0) { @@ -551,15 +560,18 @@ Results play(Field* fd) } const auto & p = fd->players; unsigned raid_damage = 15 + (std::min(p[1]->deck->deck_size, (fd->turn + 1) / 2) - p[1]->assaults.size() - p[1]->structures.size()) - (10 * p[1]->commander.m_hp / p[1]->commander.m_max_hp); + unsigned quest_score = fd->quest.must_fulfill ? (fd->quest_counter >= fd->quest.quest_value ? fd->quest.quest_score : 0) : std::min(fd->quest.quest_score, fd->quest.quest_score * fd->quest_counter / fd->quest.quest_value); + _DEBUG_MSG(1, "Quest: %u / %u = %u%%.\n", fd->quest_counter, fd->quest.quest_value, quest_score); // you lose if(fd->players[0]->commander.m_hp == 0) { _DEBUG_MSG(1, "You lose.\n"); switch (fd->optimization_mode) { - case OptimizationMode::raid: return {0, 0, 1, raid_damage, 0}; - case OptimizationMode::brawl: return {0, 0, 1, 5, 0}; - default: return {0, 0, 1, 0, 0}; + case OptimizationMode::raid: return {0, 0, 1, raid_damage}; + case OptimizationMode::brawl: return {0, 0, 1, 5}; + case OptimizationMode::quest: return {0, 0, 1, fd->quest.must_win ? 0 : quest_score}; + default: return {0, 0, 1, 0}; } } // you win @@ -575,14 +587,16 @@ Results play(Field* fd) + (p[0]->assaults.size() + p[0]->structures.size() + p[0]->deck->shuffled_cards.size()) - (p[1]->assaults.size() + p[1]->structures.size() + p[1]->deck->shuffled_cards.size()) - fd->turn / 4; - return {1, 0, 0, brawl_score, 0}; + return {1, 0, 0, brawl_score}; } case OptimizationMode::campaign: { unsigned campaign_score = 100 - 10 * (std::min(p[0]->deck->cards.size(), (fd->turn + 1) / 2) - p[0]->assaults.size() - p[0]->structures.size()); - return {1, 0, 0, campaign_score, 0}; + return {1, 0, 0, campaign_score}; } - default: return {1, 0, 0, 100, 0}; + case OptimizationMode::quest: return {1, 0, 0, fd->quest.win_score + quest_score}; + default: + return {1, 0, 0, 100}; } } if (fd->turn > turn_limit) @@ -590,16 +604,17 @@ Results play(Field* fd) _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); switch (fd->optimization_mode) { - case OptimizationMode::defense: return {0, 1, 0, 100, 0}; - case OptimizationMode::raid: return {0, 1, 0, raid_damage, 0}; - case OptimizationMode::brawl: return {0, 1, 0, 5, 0}; - default: return {0, 1, 0, 0, 0}; + case OptimizationMode::defense: return {0, 1, 0, 100}; + case OptimizationMode::raid: return {0, 1, 0, raid_damage}; + case OptimizationMode::brawl: return {0, 1, 0, 5}; + case OptimizationMode::quest: return {0, 1, 0, fd->quest.must_win ? 0 : quest_score}; + default: return {0, 1, 0, 0}; } } // Huh? How did we get here? assert(false); - return {0, 0, 0, 0, 0}; + return {0, 0, 0, 0}; } // Check if a skill actually proc'ed. @@ -638,6 +653,14 @@ void remove_hp(Field* fd, CardStatus* status, unsigned dmg) status->m_hp = safe_minus(status->m_hp, dmg); if(status->m_hp == 0) { + if (status->m_player == 1) + { + if (status->m_card->m_type == CardType::assault) + { + fd->inc_counter(QuestType::faction_assault_card_kill, status->m_card->m_faction); + } + fd->inc_counter(QuestType::type_card_kill, status->m_card->m_type); + } _DEBUG_MSG(1, "%s dies\n", status_description(status).c_str()); if(status->m_card->m_type != CardType::commander) { @@ -645,7 +668,6 @@ void remove_hp(Field* fd, CardStatus* status, unsigned dmg) } if (status->m_player == 0 && fd->players[0]->deck->vip_cards.count(status->m_card->m_id)) { - _DEBUG_MSG(1, "%s dies\n", status_description(status).c_str()); fd->players[0]->commander.m_hp = 0; fd->end = true; } @@ -818,6 +840,10 @@ void turn_end_phase(Field* fd) unsigned poison_dmg = safe_minus(status.m_poisoned + status.m_enfeebled, status.protected_value()); if (poison_dmg > 0) { + if (status.m_player == 1) + { + fd->inc_counter(QuestType::skill_damage, poison, poison_dmg); + } _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg); remove_hp(fd, &status, poison_dmg); } @@ -920,6 +946,11 @@ struct PerformAttack { // perform_skill_counter unsigned counter_dmg(counter_damage(fd, att_status, def_status)); + if (def_status->m_player == 0) + { + fd->inc_counter(QuestType::skill_use, counter); + fd->inc_counter(QuestType::skill_damage, counter, counter_dmg); + } _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, att_status, counter_dmg); if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects.count(counterflux)) @@ -936,6 +967,10 @@ struct PerformAttack { // perform_skill_berserk att_status->m_attack += berserk_value; + if (att_status->m_player == 0) + { + fd->inc_counter(QuestType::skill_use, berserk); + } } unsigned corrosive_value = def_status->skill(corrosive); if (corrosive_value > att_status->m_corroded_rate && skill_check(fd, def_status, att_status)) @@ -1068,6 +1103,10 @@ void PerformAttack::damage_dependant_pre_oa() if (poison_value > def_status->m_poisoned && skill_check(fd, att_status, def_status)) { // perform_skill_poison + if (att_status->m_player == 0) + { + fd->inc_counter(QuestType::skill_use, poison); + } _DEBUG_MSG(1, "%s poisons %s by %u\n", status_description(att_status).c_str(), status_description(def_status).c_str(), poison_value); def_status->m_poisoned = poison_value; } @@ -1086,6 +1125,10 @@ void PerformAttack::do_leech() unsigned leech_value = std::min(att_dmg, att_status->skill(leech)); if(leech_value > 0 && skill_check(fd, att_status, nullptr)) { + if (att_status->m_player == 0) + { + fd->inc_counter(QuestType::skill_use, leech); + } _DEBUG_MSG(1, "%s leeches %u health\n", status_description(att_status).c_str(), leech_value); add_hp(fd, att_status, leech_value); } @@ -1415,10 +1458,15 @@ template<> std::vector& skill_targets(Field* fd, CardStatus* { return(skill_targets_hostile_structure(fd, src_status)); } template -bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_status, const SkillSpec& s, bool is_evadable) +bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_status, const SkillSpec& s, bool is_evadable, bool & has_counted_quest) { if(skill_check(fd, src_status, dst_status)) { + if (src_status->m_player == 0 && ! has_counted_quest) + { + fd->inc_counter(QuestType::skill_use, skill_id); + has_counted_quest = true; + } if (is_evadable && dst_status->m_evaded < dst_status->skill(evade) && skill_check(fd, dst_status, src_status)) @@ -1458,6 +1506,10 @@ bool check_and_perform_valor(Field* fd, CardStatus* src_status) _DEBUG_MSG(1, "%s loses Valor (weak blocker %s)\n", status_description(src_status).c_str(), status_description(dst_status).c_str()); return false; } + if (src_status->m_player == 0) + { + fd->inc_counter(QuestType::skill_use, valor); + } _DEBUG_MSG(1, "%s activates Valor %u\n", status_description(src_status).c_str(), valor_value); src_status->m_attack += valor_value; return true; @@ -1496,9 +1548,10 @@ template void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { select_targets(fd, src_status, s); + bool has_counted_quest = false; for (CardStatus * dst_status: fd->selection_array) { - if (check_and_perform_skill(fd, src_status, dst_status, s, ! src_status->m_overloaded)) + if (check_and_perform_skill(fd, src_status, dst_status, s, ! src_status->m_overloaded, has_counted_quest)) { // Payback if(dst_status->m_paybacked < dst_status->skill(payback) && skill_check(fd, dst_status, src_status) && @@ -1517,6 +1570,7 @@ template void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { select_targets(fd, src_status, s); + bool has_counted_quest = false; for (CardStatus * dst: fd->selection_array) { if(dst->m_inhibited > 0 && !src_status->m_overloaded) @@ -1525,7 +1579,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil -- dst->m_inhibited; continue; } - check_and_perform_skill(fd, src_status, dst, s, false); + check_and_perform_skill(fd, src_status, dst, s, false, has_counted_quest); } } @@ -1533,7 +1587,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil void fill_skill_table() { memset(skill_table, 0, sizeof skill_table); - skill_table[besiege] = perform_targetted_hostile_fast; + skill_table[mortar] = perform_targetted_hostile_fast; skill_table[enfeeble] = perform_targetted_hostile_fast; skill_table[enhance] = perform_targetted_allied_fast; skill_table[evolve] = perform_targetted_allied_fast; diff --git a/sim.h b/sim.h index 4b9e11fe..473d5391 100644 --- a/sim.h +++ b/sim.h @@ -34,7 +34,6 @@ struct Results result_type draws; result_type losses; result_type points; - result_type sq_points; template Results& operator+=(const Results& other) { @@ -42,7 +41,6 @@ struct Results draws += other.draws; losses += other.losses; points += other.points; - sq_points += other.points * other.points; return *this; } }; @@ -55,12 +53,7 @@ struct FinalResults result_type wins; result_type draws; result_type losses; - //MDJ - result_type wins2; - result_type draws2; - result_type losses2; result_type points; - result_type sq_points; result_type points_lower_bound; result_type points_upper_bound; uint64_t n_sims; @@ -202,6 +195,27 @@ class Hand Storage assaults; Storage structures; }; + +struct Quest +{ + QuestType::QuestType quest_type; + unsigned quest_key; + unsigned quest_value; + unsigned quest_score; // score for quest goal + unsigned win_score; // score for win regardless quest goal + bool must_fulfill; // true: score iff value is reached; false: score proportion to achieved value + bool must_win; // true: score only if win + Quest() : + quest_type(QuestType::none), + quest_key(0), + quest_value(0), + quest_score(100), + win_score(0), + must_fulfill(false), + must_win(false) + {} +}; + //------------------------------------------------------------------------------ // struct Field is the data model of a battle: // an attacker and a defender deck, list of assaults and structures, etc. @@ -221,15 +235,13 @@ class Field unsigned turn; gamemode_t gamemode; OptimizationMode optimization_mode; + const Quest quest; std::unordered_map bg_effects; // passive BGE std::vector bg_skills; // active BGE, casted every turn // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque> skill_queue; std::vector killed_units; - unsigned n_player_kills; - bool assault_bloodlusted; - unsigned bloodlust_value; enum phase { playcard_phase, @@ -246,7 +258,11 @@ class Field // otherwise is the index of the current card in players->structures or players->assaults unsigned current_ci; - Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, OptimizationMode optimization_mode_, + bool assault_bloodlusted; + unsigned bloodlust_value; + unsigned quest_counter; + + Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, OptimizationMode optimization_mode_, const Quest & quest_, std::unordered_map& bg_effects_, std::vector& bg_skills_) : end{false}, re(re_), @@ -255,11 +271,12 @@ class Field turn(1), gamemode(gamemode_), optimization_mode(optimization_mode_), + quest(quest_), bg_effects(bg_effects_), bg_skills(bg_skills_), - n_player_kills(0), assault_bloodlusted(false), - bloodlust_value(0) + bloodlust_value(0), + quest_counter(0) { } @@ -283,6 +300,14 @@ class Field template inline unsigned make_selection_array(CardsIter first, CardsIter last, Functor f); inline void print_selection_array(); + + inline void inc_counter(QuestType::QuestType quest_type, unsigned quest_key, unsigned value = 1) + { + if (quest.quest_type == quest_type && quest.quest_key == quest_key) + { + quest_counter += value; + } + } }; #endif diff --git a/tyrant.cpp b/tyrant.cpp index c37c167a..36753275 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -13,7 +13,7 @@ std::string skill_names[Skill::num_skills] = "0", // Activation: "", - "Besiege", "Enfeeble", "Jam", "Siege", "Strike", "Weaken", + "Enfeeble", "Jam", "Mortar", "Siege", "Strike", "Weaken", "", "", "Enhance", "Evolve", "Heal", "Mend", "Overload", "Protect", "Rally", @@ -41,10 +41,10 @@ std::string rarity_names[6]{"", "common", "rare", "epic", "legend", "vindi", }; unsigned upgrade_cost[]{0, 5, 15, 30, 75, 150}; unsigned salvaging_income[][7]{{}, {0, 1, 2, 5}, {0, 5, 10, 15, 20}, {0, 20, 25, 30, 40, 50, 65}, {0, 40, 45, 60, 75, 100, 125}, {0, 80, 85, 100, 125, 175, 250}}; -signed min_possible_score[]{0, 0, 0, 10, 0, 5, 5, 0}; -signed max_possible_score[]{100, 100, 100, 100, 100, 67, 100, 100}; +signed min_possible_score[]{0, 0, 0, 10, 5, 5, 0, 0}; +signed max_possible_score[]{100, 100, 100, 100, 67, 100, 100, 100}; -std::string decktype_names[DeckType::num_decktypes]{"Deck", "Mission", "Raid", "Campaign", "Quest", "Custom Deck", }; +std::string decktype_names[DeckType::num_decktypes]{"Deck", "Mission", "Raid", "Campaign", "Custom Deck", }; signed debug_print(0); unsigned debug_cached(0); diff --git a/tyrant.h b/tyrant.h index f1ddfef2..1f8986ee 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.1" +#define TYRANT_OPTIMIZER_VERSION "2.10.2" #include #include @@ -29,7 +29,7 @@ enum Skill attack, // Activation: BEGIN_ACTIVATION_HARMFUL, // TODO skill traits - besiege, enfeeble, jam, siege, strike, weaken, + enfeeble, jam, mortar, siege, strike, weaken, END_ACTIVATION_HARMFUL, BEGIN_ACTIVATION_HELPFUL, enhance, evolve, heal, mend, overload, protect, rally, @@ -74,7 +74,6 @@ enum DeckType { mission, raid, campaign, - quest, custom_deck, num_decktypes }; @@ -88,17 +87,31 @@ enum gamemode_t surge, }; -//MDJ +namespace QuestType +{ +enum QuestType +{ + none, + skill_use, + skill_damage, + faction_card_use, + type_card_use, + faction_assault_card_kill, + type_card_kill, + num_objective_types +}; +} + enum class OptimizationMode { notset, winrate, defense, war, - totalwar, brawl, raid, campaign, + quest, num_optimization_mode }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index a9e70a51..faedca99 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -42,11 +42,14 @@ #include "tyrant.h" #include "xml.h" +struct Requirement +{ + std::unordered_map num_cards; +}; + namespace { gamemode_t gamemode{fight}; OptimizationMode optimization_mode{OptimizationMode::notset}; - //MDJ - std::string opt_forts, opt_enemy_forts; std::map owned_cards; bool use_owned_cards{true}; unsigned min_deck_len{1}; @@ -59,12 +62,11 @@ namespace { bool use_top_level_card{false}; unsigned use_fused_card_level{0}; bool show_ci{false}; - bool show_stdev{false}; bool use_harmonic_mean{false}; + Requirement requirement; + Quest quest; } -typedef std::unordered_map Requirement; - using namespace std::placeholders; //------------------------------------------------------------------------------ std::string card_id_name(const Card* card) @@ -266,6 +268,8 @@ bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, cons deck_cost = get_deck_cost(deck); if (use_top_level_card || deck_cost <= fund) { break; } + if (i < freezed_cards) + { return false; } for (auto recipe_it : card_in->m_recipe_cards) { candidate_cards.emplace(recipe_it.first); } } @@ -284,26 +288,77 @@ bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, cons return !cards_in.empty() || !cards_out.empty(); } -bool check_requirements(const Deck* deck, const Requirement & requirement) +unsigned check_requirement(const Deck* deck, const Requirement & requirement, const Quest & quest) { - if (requirement.empty()) - { - return true; - } - Requirement num_cards; - num_cards[deck->commander] = 1; - for (auto card: deck->cards) + unsigned gap = 0; + if (!requirement.num_cards.empty()) { - ++ num_cards[card]; + std::unordered_map num_cards; + num_cards[deck->commander] = 1; + for (auto card: deck->cards) + { + ++ num_cards[card]; + } + for (auto it: requirement.num_cards) + { + gap += safe_minus(it.second, num_cards[it.first]); + } } - for (auto it: requirement) + if (quest.quest_type != QuestType::none) { - if (num_cards[it.first] < it.second) + unsigned potential_value = 0; + switch (quest.quest_type) { - return false; + case QuestType::skill_use: + case QuestType::skill_damage: + for (const auto & ss: deck->commander->m_skills) + { + if (quest.quest_key == ss.id) + { + potential_value = quest.quest_value; + break; + } + } + break; + case QuestType::faction_assault_card_kill: + case QuestType::type_card_kill: + potential_value = quest.quest_value; + break; + default: + break; + } + for (auto card: deck->cards) + { + switch (quest.quest_type) + { + case QuestType::skill_use: + case QuestType::skill_damage: + for (const auto & ss: card->m_skills) + { + if (quest.quest_key == ss.id) + { + potential_value = quest.quest_value; + break; + } + } + break; + case QuestType::faction_card_use: + potential_value += (quest.quest_key == card->m_faction); + break; + case QuestType::type_card_use: + potential_value += (quest.quest_key == card->m_type); + break; + default: + break; + } + if (potential_value >= (quest.must_fulfill ? quest.quest_value : 1)) + { + break; + } } + gap += safe_minus(quest.must_fulfill ? quest.quest_value : 1, potential_value); } - return true; + return gap; } void claim_cards(const std::vector & card_list) @@ -328,111 +383,44 @@ void claim_cards(const std::vector & card_list) //------------------------------------------------------------------------------ FinalResults compute_score(const EvaluatedResults& results, std::vector& factors) { - FinalResults final{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, results.second}; + FinalResults final{0, 0, 0, 0, 0, 0, results.second}; long double max_possible = max_possible_score[(size_t)optimization_mode]; - if (optimization_mode == OptimizationMode::totalwar) //Double results - { - for (unsigned index(0); index < results.first.size(); ++index) - { - //MDJ (evens are offense and odds are defense) - unsigned findex = index / 2; - if (index % 2 == 0) - { - final.wins += results.first[index].wins * factors[findex]; - final.draws += results.first[index].draws * factors[findex]; - final.losses += results.first[index].losses * factors[findex]; - } - else - { - final.wins2 += results.first[index].wins * factors[findex]; - final.draws2 += results.first[index].draws * factors[findex]; - final.losses2 += results.first[index].losses * factors[findex]; - } - auto lower_bound = boost::math::binomial_distribution<>::find_lower_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; - auto upper_bound = boost::math::binomial_distribution<>::find_upper_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; - if (use_harmonic_mean) - { - final.points += factors[findex] / results.first[index].points; - final.points_lower_bound += factors[findex] / lower_bound; - final.points_upper_bound += factors[findex] / upper_bound; - } - else - { - final.points += results.first[index].points * factors[findex]; - final.points_lower_bound += lower_bound * factors[findex]; - final.points_upper_bound += upper_bound * factors[findex]; - } - final.sq_points += results.first[index].sq_points * factors[findex] * factors[findex]; - } - long double factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); - final.wins /= factor_sum * (long double)results.second; - final.draws /= factor_sum * (long double)results.second; - final.losses /= factor_sum * (long double)results.second; - final.wins2 /= factor_sum * (long double)results.second; - final.draws2 /= factor_sum * (long double)results.second; - final.losses2 /= factor_sum * (long double)results.second; - //MDJ Twice the results if two sets of sims - long double total_results = results.second; - if (optimization_mode == OptimizationMode::totalwar) - { - total_results *= 2; - } - if (use_harmonic_mean) - { - final.points = factor_sum / (total_results * final.points); - final.points_lower_bound = factor_sum / final.points_lower_bound; - final.points_upper_bound = factor_sum / final.points_upper_bound; - } - else - { - final.points /= factor_sum * total_results; - final.points_lower_bound /= factor_sum; - final.points_upper_bound /= factor_sum; - } - //std::cout << "OWins/ODraws/OLosses/DWins/DDraws/DLosses/Score:" << final.wins << "/" << final.draws << "/" << final.losses << "/" << final.wins2 << "/" << final.draws2 << "/" << final.losses2 << "/" << final.points; - final.sq_points /= factor_sum * factor_sum * total_results; - } - else //No double results - { - for (unsigned index(0); index < results.first.size(); ++index) - { - final.wins += results.first[index].wins * factors[index]; - final.draws += results.first[index].draws * factors[index]; - final.losses += results.first[index].losses * factors[index]; - auto lower_bound = boost::math::binomial_distribution<>::find_lower_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; - auto upper_bound = boost::math::binomial_distribution<>::find_upper_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; - if (use_harmonic_mean) - { - final.points += factors[index] / results.first[index].points; - final.points_lower_bound += factors[index] / lower_bound; - final.points_upper_bound += factors[index] / upper_bound; - } - else - { - final.points += results.first[index].points * factors[index]; - final.points_lower_bound += lower_bound * factors[index]; - final.points_upper_bound += upper_bound * factors[index]; - } - final.sq_points += results.first[index].sq_points * factors[index] * factors[index]; - } - long double factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); - final.wins /= factor_sum * (long double)results.second; - final.draws /= factor_sum * (long double)results.second; - final.losses /= factor_sum * (long double)results.second; - if (use_harmonic_mean) - { - final.points = factor_sum / ((long double)results.second * final.points); - final.points_lower_bound = factor_sum / final.points_lower_bound; - final.points_upper_bound = factor_sum / final.points_upper_bound; - } - else - { - final.points /= factor_sum * (long double)results.second; - final.points_lower_bound /= factor_sum; - final.points_upper_bound /= factor_sum; - } - final.sq_points /= factor_sum * factor_sum * (long double)results.second; - } + for (unsigned index(0); index < results.first.size(); ++index) + { + final.wins += results.first[index].wins * factors[index]; + final.draws += results.first[index].draws * factors[index]; + final.losses += results.first[index].losses * factors[index]; + auto lower_bound = boost::math::binomial_distribution<>::find_lower_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; + auto upper_bound = boost::math::binomial_distribution<>::find_upper_bound_on_p(results.second, results.first[index].points / max_possible, 1 - confidence_level) * max_possible; + if (use_harmonic_mean) + { + final.points += factors[index] / results.first[index].points; + final.points_lower_bound += factors[index] / lower_bound; + final.points_upper_bound += factors[index] / upper_bound; + } + else + { + final.points += results.first[index].points * factors[index]; + final.points_lower_bound += lower_bound * factors[index]; + final.points_upper_bound += upper_bound * factors[index]; + } + } + long double factor_sum = std::accumulate(factors.begin(), factors.end(), 0.); + final.wins /= factor_sum * (long double)results.second; + final.draws /= factor_sum * (long double)results.second; + final.losses /= factor_sum * (long double)results.second; + if (use_harmonic_mean) + { + final.points = factor_sum / ((long double)results.second * final.points); + final.points_lower_bound = factor_sum / final.points_lower_bound; + final.points_upper_bound = factor_sum / final.points_upper_bound; + } + else + { + final.points /= factor_sum * (long double)results.second; + final.points_lower_bound /= factor_sum; + final.points_upper_bound /= factor_sum; + } return final; } //------------------------------------------------------------------------------ @@ -457,10 +445,11 @@ struct SimulationData std::vector enemy_hands; std::vector factors; gamemode_t gamemode; + Quest quest; std::unordered_map bg_effects; std::vector bg_skills; - SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_enemy_decks_, std::vector factors_, gamemode_t gamemode_, + SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_enemy_decks_, std::vector factors_, gamemode_t gamemode_, Quest & quest_, std::unordered_map& bg_effects_, std::vector& bg_skills_) : re(seed), cards(cards_), @@ -470,6 +459,7 @@ struct SimulationData enemy_decks(num_enemy_decks_), factors(factors_), gamemode(gamemode_), + quest(quest_), bg_effects(bg_effects_), bg_skills(bg_skills_) { @@ -502,62 +492,9 @@ struct SimulationData { your_hand.reset(re); enemy_hand->reset(re); - Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, bg_effects, bg_skills); + Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, quest, bg_effects, bg_skills); Results result(play(&fd)); res.emplace_back(result); - //MDJ - if (optimization_mode == OptimizationMode::totalwar) - { - if (!opt_enemy_forts.empty()) - { - try - { - your_hand.deck->set_forts(opt_enemy_forts + ","); - } - catch (const std::runtime_error& e) - { - std::cerr << "Error: ef " << opt_enemy_forts << ": " << e.what() << std::endl; - } - } - if (!opt_forts.empty()) - { - try - { - enemy_hand->deck->set_forts(opt_forts + ","); - } - catch (const std::runtime_error& e) - { - std::cerr << "Error: yf " << opt_forts << ": " << e.what() << std::endl; - } - } - your_hand.reset(re); - enemy_hand->reset(re); - Field fd1(re, cards, your_hand, *enemy_hand, gamemode_t::fight, OptimizationMode::defense, bg_effects, bg_skills); - Results result1(play(&fd1)); - res.emplace_back(result1); - if (!opt_forts.empty()) - { - try - { - your_hand.deck->set_forts(opt_forts + ","); - } - catch (const std::runtime_error& e) - { - std::cerr << "Error: yf " << opt_forts << ": " << e.what() << std::endl; - } - } - if (!opt_enemy_forts.empty()) - { - try - { - enemy_hand->deck->set_forts(opt_enemy_forts + ","); - } - catch (const std::runtime_error& e) - { - std::cerr << "Error: ef " << opt_enemy_forts << ": " << e.what() << std::endl; - } - } - } } return(res); } @@ -584,10 +521,11 @@ class Process const std::vector enemy_decks; std::vector factors; gamemode_t gamemode; + Quest quest; std::unordered_map bg_effects; std::vector bg_skills; - Process(unsigned num_threads_, const Cards& cards_, const Decks& decks_, Deck* your_deck_, std::vector enemy_decks_, std::vector factors_, gamemode_t gamemode_, + Process(unsigned num_threads_, const Cards& cards_, const Decks& decks_, Deck* your_deck_, std::vector enemy_decks_, std::vector factors_, gamemode_t gamemode_, Quest & quest_, std::unordered_map& bg_effects_, std::vector& bg_skills_) : num_threads(num_threads_), main_barrier(num_threads+1), @@ -597,6 +535,7 @@ class Process enemy_decks(enemy_decks_), factors(factors_), gamemode(gamemode_), + quest(quest_), bg_effects(bg_effects_), bg_skills(bg_skills_) { @@ -604,7 +543,7 @@ class Process unsigned seed(time(0)); for(unsigned i(0); i < num_threads; ++i) { - threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, bg_effects, bg_skills)); + threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, quest, bg_effects, bg_skills)); threads.push_back(new boost::thread(thread_evaluate, std::ref(main_barrier), std::ref(shared_mutex), std::ref(*threads_data.back()), std::ref(*this), i)); } } @@ -676,7 +615,6 @@ void thread_evaluate(boost::barrier& main_barrier, else { --thread_num_iterations; //! - //MDJ shared_mutex.unlock(); //>>>> std::vector> result{sim.evaluate()}; shared_mutex.lock(); //<<<< @@ -754,74 +692,33 @@ void print_score_info(const EvaluatedResults& results, std::vector& void print_results(const EvaluatedResults& results, std::vector& factors) { auto final = compute_score(results, factors); - //MDJ - if (optimization_mode == OptimizationMode::totalwar) - { - std::cout << "Offense win%: " << final.wins * 100.0 << " ("; - for (unsigned index(0); index < results.first.size(); index = index+2) - { - std::cout << results.first[index].wins << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; - - std::cout << "Offense stall%: " << final.draws * 100.0 << " ("; - for (unsigned index(0); index < results.first.size(); index = index + 2) - { - std::cout << results.first[index].draws << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; - - std::cout << "Offense loss%: " << final.losses * 100.0 << " ("; - for (unsigned index(0); index < results.first.size(); index = index + 2) - { - std::cout << results.first[index].losses << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; - - std::cout << "Defense win%: " << final.wins2 * 100.0 << " ("; - for (unsigned index(1); index < results.first.size(); index = index + 2) - { - std::cout << results.first[index].wins << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; - - std::cout << "Defense stall%: " << final.draws2 * 100.0 << " ("; - for (unsigned index(1); index < results.first.size(); index = index + 2) - { - std::cout << results.first[index].draws << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; + std::cout << "win%: " << final.wins * 100.0 << " ("; + for (const auto & val : results.first) + { + std::cout << val.wins << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; - std::cout << "Defense loss%: " << final.losses2 * 100.0 << " ("; - for (unsigned index(1); index < results.first.size(); index = index + 2) - { - std::cout << results.first[index].losses << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; - } - else - { - std::cout << "win%: " << final.wins * 100.0 << " ("; - for (const auto & val : results.first) - { - std::cout << val.wins << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; + std::cout << "stall%: " << final.draws * 100.0 << " ("; + for (const auto & val : results.first) + { + std::cout << val.draws << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; - std::cout << "stall%: " << final.draws * 100.0 << " ("; - for (const auto & val : results.first) - { - std::cout << val.draws << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; + std::cout << "loss%: " << final.losses * 100.0 << " ("; + for (const auto & val : results.first) + { + std::cout << val.losses << " "; + } + std::cout << "/ " << results.second << ")" << std::endl; - std::cout << "loss%: " << final.losses * 100.0 << " ("; - for (const auto & val : results.first) - { - std::cout << val.losses << " "; - } - std::cout << "/ " << results.second << ")" << std::endl; - } + if (optimization_mode == OptimizationMode::quest) + { + // points = win% * win_score + (must_win ? win% : 100%) * quest% * quest_score + // quest% = (points - win% * win_score) / (must_win ? win% : 100%) / quest_score + std::cout << "quest%: " << (final.points - final.wins * quest.win_score) / (quest.must_win ? final.wins : 1) / quest.quest_score * 100 << std::endl; + } switch(optimization_mode) { @@ -829,6 +726,7 @@ void print_results(const EvaluatedResults& results, std::vector& fa case OptimizationMode::campaign: case OptimizationMode::brawl: case OptimizationMode::war: + case OptimizationMode::quest: std::cout << "score: " << final.points << " ("; for(const auto & val: results.first) { @@ -839,10 +737,6 @@ void print_results(const EvaluatedResults& results, std::vector& fa { std::cout << "ci: " << final.points_lower_bound << " - " << final.points_upper_bound << std::endl; } - if (show_stdev) - { - std::cout << "stdev: " << sqrt(final.sq_points - final.points * final.points) << std::endl; - } break; default: break; @@ -862,23 +756,21 @@ void print_deck_inline(const unsigned deck_cost, const FinalResults case OptimizationMode::campaign: case OptimizationMode::brawl: case OptimizationMode::war: - std::cout << "(" << score.wins * 100 << "% win, " << score.draws * 100 << "% stall"; - if (show_ci) + case OptimizationMode::quest: + std::cout << "(" << score.wins * 100 << "% win"; + if (optimization_mode == OptimizationMode::quest) { - std::cout << ", " << score.points_lower_bound << "-" << score.points_upper_bound; + std::cout << ", " << (score.points - score.wins * quest.win_score) / (quest.must_win ? score.wins : 1) / quest.quest_score * 100 << "% quest"; } - if (show_stdev) + if (show_ci) { - std::cout << ", " << sqrt(score.sq_points - score.points * score.points) << " stdev"; + std::cout << ", " << score.points_lower_bound << "-" << score.points_upper_bound; } std::cout << ") "; break; case OptimizationMode::defense: std::cout << "(" << score.draws * 100.0 << "% stall) "; break; - case OptimizationMode::totalwar: - std::cout << "(Offense: " << score.wins * 100 << "% win, Defense: " << (score.wins2 + score.draws2) * 100 << "% win/stall) "; - break; default: break; } @@ -913,17 +805,9 @@ void print_deck_inline(const unsigned deck_cost, const FinalResults std::cout << std::endl; } //------------------------------------------------------------------------------ -void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, Requirement requirement) +void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, Requirement & requirement, Quest & quest) { - //MDJ - EvaluatedResults zero_results; - if (optimization_mode == OptimizationMode::totalwar) - { - zero_results = { EvaluatedResults::first_type(proc.enemy_decks.size() * 2), 0 }; - } - else{ - zero_results = { EvaluatedResults::first_type(proc.enemy_decks.size()), 0 }; - } + EvaluatedResults zero_results = { EvaluatedResults::first_type(proc.enemy_decks.size()), 0 }; auto best_deck = d1->hash(); std::map evaluated_decks{{best_deck, zero_results}}; EvaluatedResults & results = proc.evaluate(num_min_iterations, evaluated_decks.begin()->second); @@ -941,6 +825,7 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d fund = std::max(fund, deck_cost); print_deck_inline(deck_cost, best_score, d1); std::mt19937 re(std::chrono::system_clock::now().time_since_epoch().count()); + unsigned best_gap = check_requirement(d1, requirement, quest); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; std::vector> cards_out, cards_in; @@ -953,7 +838,7 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d } else if (slot_i == dead_slot || best_score.points - target_score > -1e-9) { - if (best_score.n_sims >= num_iterations) + if (best_score.n_sims >= num_iterations || best_gap > 0) { break; } @@ -970,7 +855,7 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d { continue; } - if (requirement.count(best_commander) == 0) + if (requirement.num_cards.count(best_commander) == 0) { for(const Card* commander_candidate: proc.cards.player_commanders) { @@ -984,8 +869,10 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d cards_out.emplace_back(-1, best_commander); cards_out = {{-1, best_commander}}; d1->commander = commander_candidate; - if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in) || - ! check_requirements(d1, requirement)) + if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in)) + { continue; } + unsigned new_gap = check_requirement(d1, requirement, quest); + if (new_gap > 0 && new_gap >= best_gap) { continue; } auto && cur_deck = d1->hash(); auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); @@ -998,15 +885,16 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d auto compare_results = proc.compare(best_score.n_sims, prev_results, best_score); current_score = compute_score(compare_results, proc.factors); // Is it better ? - if (current_score.points > best_score.points + min_increment_of_score) + if (new_gap < best_gap || current_score.points > best_score.points + min_increment_of_score) { - std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; // Then update best score/commander, print stuff + std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; + best_gap = new_gap; best_score = current_score; best_deck = cur_deck; best_commander = d1->commander; best_cards = d1->cards; -// deck_has_been_improved = true; + deck_has_been_improved = true; print_score_info(compare_results, proc.factors); print_deck_inline(deck_cost, best_score, d1); } @@ -1034,8 +922,10 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d d1->cards.erase(d1->cards.begin() + slot_i); } if (! adjust_deck(d1, slot_i, slot_i, card_candidate, fund, re, deck_cost, cards_out, cards_in) || - d1->cards.size() < min_deck_len || - ! check_requirements(d1, requirement)) + d1->cards.size() < min_deck_len) + { continue; } + unsigned new_gap = check_requirement(d1, requirement, quest); + if (new_gap > 0 && new_gap >= best_gap) { continue; } auto && cur_deck = d1->hash(); auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); @@ -1048,10 +938,11 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d auto compare_results = proc.compare(best_score.n_sims, prev_results, best_score); current_score = compute_score(compare_results, proc.factors); // Is it better ? - if (current_score.points > best_score.points + min_increment_of_score) + if (new_gap < best_gap || current_score.points > best_score.points + min_increment_of_score) { - std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; // Then update best score/slot, print stuff + std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; + best_gap = new_gap; best_score = current_score; best_deck = cur_deck; best_commander = d1->commander; @@ -1074,17 +965,9 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d print_deck_inline(get_deck_cost(d1), best_score, d1); } //------------------------------------------------------------------------------ -void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, Requirement requirement) +void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, Deck* d1, Process& proc, Requirement & requirement, Quest & quest) { - //MDJ - EvaluatedResults zero_results; - if (optimization_mode == OptimizationMode::totalwar) - { - zero_results = { EvaluatedResults::first_type(proc.enemy_decks.size() * 2), 0 }; - } - else{ - zero_results = { EvaluatedResults::first_type(proc.enemy_decks.size()), 0 }; - } + EvaluatedResults zero_results = { EvaluatedResults::first_type(proc.enemy_decks.size()), 0 }; auto best_deck = d1->hash(); std::map evaluated_decks{{best_deck, zero_results}}; EvaluatedResults & results = proc.evaluate(num_min_iterations, evaluated_decks.begin()->second); @@ -1102,6 +985,7 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, fund = std::max(fund, deck_cost); print_deck_inline(deck_cost, best_score, d1); std::mt19937 re(std::chrono::system_clock::now().time_since_epoch().count()); + unsigned best_gap = check_requirement(d1, requirement, quest); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; std::vector> cards_out, cards_in; @@ -1118,7 +1002,7 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, } else if (from_slot == dead_slot || best_score.points - target_score > -1e-9) { - if (best_score.n_sims >= num_iterations) + if (best_score.n_sims >= num_iterations || best_gap > 0) { break; } @@ -1135,7 +1019,7 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, { continue; } - if (requirement.count(best_commander) == 0) + if (requirement.num_cards.count(best_commander) == 0) { for(const Card* commander_candidate: proc.cards.player_commanders) { @@ -1150,8 +1034,10 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, cards_out.clear(); cards_out.emplace_back(-1, best_commander); d1->commander = commander_candidate; - if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in) || - ! check_requirements(d1, requirement)) + if (! adjust_deck(d1, -1, -1, nullptr, fund, re, deck_cost, cards_out, cards_in)) + { continue; } + unsigned new_gap = check_requirement(d1, requirement, quest); + if (new_gap > 0 && new_gap >= best_gap) { continue; } auto && cur_deck = d1->hash(); auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); @@ -1164,10 +1050,11 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, auto compare_results = proc.compare(best_score.n_sims, prev_results, best_score); current_score = compute_score(compare_results, proc.factors); // Is it better ? - if (current_score.points > best_score.points + min_increment_of_score) + if (new_gap < best_gap || current_score.points > best_score.points + min_increment_of_score) { std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; // Then update best score/commander, print stuff + best_gap = new_gap; best_score = current_score; best_deck = cur_deck; best_commander = commander_candidate; @@ -1204,8 +1091,10 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, d1->cards.erase(d1->cards.begin() + from_slot); } if (! adjust_deck(d1, from_slot, to_slot, card_candidate, fund, re, deck_cost, cards_out, cards_in) || - d1->cards.size() < min_deck_len || - ! check_requirements(d1, requirement)) + d1->cards.size() < min_deck_len) + { continue; } + unsigned new_gap = check_requirement(d1, requirement, quest); + if (new_gap > 0 && new_gap >= best_gap) { continue; } auto && cur_deck = d1->hash(); auto && emplace_rv = evaluated_decks.insert({cur_deck, zero_results}); @@ -1218,10 +1107,11 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, auto compare_results = proc.compare(best_score.n_sims, prev_results, best_score); current_score = compute_score(compare_results, proc.factors); // Is it better ? - if (current_score.points > best_score.points + min_increment_of_score) + if (new_gap < best_gap || current_score.points > best_score.points + min_increment_of_score) { // Then update best score/slot, print stuff std::cout << "Deck improved: " << d1->hash() << ": " << card_slot_id_names(cards_out) << " -> " << card_slot_id_names(cards_in) << ": "; + best_gap = new_gap; best_score = current_score; best_deck = cur_deck; best_commander = d1->commander; @@ -1245,68 +1135,6 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, print_deck_inline(get_deck_cost(d1), best_score, d1); } //------------------------------------------------------------------------------ -// Implements iteration over all combination of k elements from n elements. -// parameter firstIndexLimit: this is a ugly hack used to implement the special condition that -// a deck could be expected to contain at least 1 assault card. Thus the first element -// will be chosen among the assault cards only, instead of all cards. -// It works on the condition that the assault cards are sorted first in the list of cards, -// thus have indices 0..firstIndexLimit-1. -class Combination -{ -public: - Combination(unsigned all_, unsigned choose_, unsigned firstIndexLimit_ = 0) : - all(all_), - choose(choose_), - firstIndexLimit(firstIndexLimit_ == 0 ? all_ - choose_ : firstIndexLimit_), - indices(choose_, 0), - indicesLimits(choose_, 0) - { - assert(choose > 0); - assert(choose <= all); - assert(firstIndexLimit <= all); - indicesLimits[0] = firstIndexLimit; - for(unsigned i(1); i < choose; ++i) - { - indices[i] = i; - indicesLimits[i] = all - choose + i; - } - } - - const std::vector& getIndices() - { - return(indices); - } - - bool next() - { - // The end condition's a bit odd here, but index is unsigned. - // The last iteration is when index = 0. - // After that, index = max int, which is clearly >= choose. - for(index = choose - 1; index < choose; --index) - { - if(indices[index] < indicesLimits[index]) - { - ++indices[index]; - for(nextIndex = index + 1; nextIndex < choose; nextIndex++) - { - indices[nextIndex] = indices[index] - index + nextIndex; - } - return(false); - } - } - return(true); - } - -private: - unsigned all; - unsigned choose; - unsigned firstIndexLimit; - std::vector indices; - std::vector indicesLimits; - unsigned index; - unsigned nextIndex; -}; -//------------------------------------------------------------------------------ enum Operation { simulate, climb, @@ -1384,10 +1212,11 @@ int main(int argc, char** argv) unsigned opt_num_threads(4); DeckStrategy::DeckStrategy opt_your_strategy(DeckStrategy::random); DeckStrategy::DeckStrategy opt_enemy_strategy(DeckStrategy::random); - //MDJ - //std::string opt_forts, opt_enemy_forts; + std::string opt_forts, opt_enemy_forts; std::string opt_hand, opt_enemy_hand; std::string opt_vip; + std::string opt_quest; + std::string opt_target_score; std::vector fn_suffix_list{"",}; std::vector opt_owned_cards_str_list; bool opt_do_optimization(false); @@ -1473,12 +1302,6 @@ int main(int argc, char** argv) gamemode = fight; optimization_mode = OptimizationMode::defense; } - //MDJ - else if (strcmp(argv[argIndex], "tw") == 0) - { - gamemode = surge; - optimization_mode = OptimizationMode::totalwar; - } // Others else if (strcmp(argv[argIndex], "keep-commander") == 0 || strcmp(argv[argIndex], "-c") == 0) { @@ -1549,6 +1372,11 @@ int main(int argc, char** argv) use_fused_card_level = atoi(argv[argIndex+1]); argIndex += 1; } + else if (strcmp(argv[argIndex], "quest") == 0) + { + opt_quest = argv[argIndex+1]; + argIndex += 1; + } else if(strcmp(argv[argIndex], "threads") == 0 || strcmp(argv[argIndex], "-t") == 0) { opt_num_threads = atoi(argv[argIndex+1]); @@ -1556,7 +1384,7 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "target") == 0) { - target_score = atof(argv[argIndex+1]); + opt_target_score = argv[argIndex+1]; argIndex += 1; } else if(strcmp(argv[argIndex], "turnlimit") == 0) @@ -1578,10 +1406,6 @@ int main(int argc, char** argv) { show_ci = true; } - else if(strcmp(argv[argIndex], "+stdev") == 0) - { - show_stdev = true; - } else if(strcmp(argv[argIndex], "+hm") == 0) { use_harmonic_mean = true; @@ -1788,10 +1612,7 @@ int main(int argc, char** argv) std::string enemy_deck_list{argv[2]}; auto && deck_list_parsed = parse_deck_list(enemy_deck_list, decks); - //MDJ Deck* your_deck{nullptr}; - Requirement requirement; - Requirement vip_cards; std::vector enemy_decks; std::vector enemy_decks_factors; @@ -1832,6 +1653,7 @@ int main(int argc, char** argv) return 0; } } + try { your_deck->set_vip_cards(opt_vip); @@ -1841,6 +1663,98 @@ int main(int argc, char** argv) std::cerr << "Error: vip " << opt_vip << ": " << e.what() << std::endl; return 0; } + + if (!opt_quest.empty()) + { + try + { + optimization_mode = OptimizationMode::quest; + std::vector tokens; + boost::split(tokens, opt_quest, boost::is_any_of(" -")); + if (tokens.size() < 3) + { + throw std::runtime_error("Expect one of: su n skill; sd n skill; cu n faction/strcture; ck n structure"); + } + auto type_str = boost::to_lower_copy(tokens[0]); + quest.quest_value = boost::lexical_cast(tokens[1]); + auto key_str = boost::to_lower_copy(tokens[2]); + if (type_str == "su" || type_str == "sd") + { + Skill skill_id = skill_name_to_id(key_str, false); + if (skill_id == no_skill) + { + std::cerr << "Error: Expect skill in quest \"" << opt_quest << "\".\n"; + return 0; + } + quest.quest_type = type_str == "su" ? QuestType::skill_use : QuestType::skill_damage; + quest.quest_key = skill_id; + } + else if (type_str == "cu" || type_str == "ck") + { + if (key_str == "assault") + { + quest.quest_type = type_str == "cu" ? QuestType::type_card_use : QuestType::type_card_kill; + quest.quest_key = CardType::assault; + } + else if (key_str == "structure") + { + quest.quest_type = type_str == "cu" ? QuestType::type_card_use : QuestType::type_card_kill; + quest.quest_key = CardType::structure; + } + else + { + for (unsigned i = 1; i < Faction::num_factions; ++ i) + { + if (key_str == boost::to_lower_copy(faction_names[i])) + { + quest.quest_type = type_str == "cu" ? QuestType::faction_card_use : QuestType::faction_assault_card_kill; + quest.quest_key = i; + break; + } + } + if (quest.quest_key == 0) + { + std::cerr << "Error: Expect assault, structure or faction in quest \"" << opt_quest << "\".\n"; + return 0; + } + } + } + else + { + throw std::runtime_error("Expect one of: su n skill; sd n skill; cu n faction/strcture; ck n structure"); + } + quest.quest_score = quest.quest_value; + for (unsigned i = 3; i < tokens.size(); ++ i) + { + const auto & token = tokens[i]; + if (token == "each") + { + quest.must_fulfill = true; + quest.quest_score = 100; + } + else if (token == "win") + { quest.must_win = true; } + else if (token.substr(0, 2) == "q=") + { quest.quest_score = boost::lexical_cast(token.substr(2)); } + else if (token.substr(0, 2) == "w=") + { quest.win_score = boost::lexical_cast(token.substr(2)); } + else + { throw std::runtime_error("Cannot recognize " + token); } + } + max_possible_score[(size_t)optimization_mode] = quest.quest_score + quest.win_score; + } + catch (const boost::bad_lexical_cast & e) + { + std::cerr << "Error: Expect a number in quest \"" << opt_quest << "\".\n"; + return 0; + } + catch (const std::runtime_error& e) + { + std::cerr << "Error: quest " << opt_quest << ": " << e.what() << std::endl; + return 0; + } + } + try { your_deck->set_given_hand(opt_hand); @@ -1850,9 +1764,10 @@ int main(int argc, char** argv) std::cerr << "Error: hand " << opt_hand << ": " << e.what() << std::endl; return 0; } + if (opt_keep_commander) { - requirement[your_deck->commander] = 1; + requirement.num_cards[your_deck->commander] = 1; } for (auto && card_mark: your_deck->card_marks) { @@ -1860,10 +1775,12 @@ int main(int argc, char** argv) auto mark = card_mark.second; if (mark == '!') { - requirement[card] += 1; + requirement.num_cards[card] += 1; } } + target_score = opt_target_score.empty() ? max_possible_score[(size_t)optimization_mode] : boost::lexical_cast(opt_target_score); + for(auto deck_parsed: deck_list_parsed) { Deck* enemy_deck{nullptr}; @@ -1969,7 +1886,7 @@ int main(int argc, char** argv) } } - Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, opt_bg_effects, opt_bg_skills); + Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, quest, opt_bg_effects, opt_bg_skills); { //ScopeClock timer; @@ -1978,15 +1895,7 @@ int main(int argc, char** argv) switch(std::get<2>(op)) { case simulate: { - //MDJ - EvaluatedResults results; - if (optimization_mode == OptimizationMode::totalwar) - { - results = { EvaluatedResults::first_type(enemy_decks.size() * 2), 0 }; - } - else{ - results = { EvaluatedResults::first_type(enemy_decks.size()), 0 }; - } + EvaluatedResults results = { EvaluatedResults::first_type(enemy_decks.size()), 0 }; results = p.evaluate(std::get<0>(op), results); print_results(results, p.factors); break; @@ -1995,12 +1904,12 @@ int main(int argc, char** argv) switch (opt_your_strategy) { case DeckStrategy::random: - hill_climbing(std::get<0>(op), std::get<1>(op), your_deck, p, requirement); + hill_climbing(std::get<0>(op), std::get<1>(op), your_deck, p, requirement, quest); break; // case DeckStrategy::ordered: // case DeckStrategy::exact_ordered: default: - hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, requirement); + hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, requirement, quest); break; } break; @@ -2017,7 +1926,7 @@ int main(int argc, char** argv) owned_cards.clear(); claim_cards({your_deck->commander}); claim_cards(your_deck->cards); - hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, requirement); + hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, requirement, quest); break; } case debug: { diff --git a/xml.cpp b/xml.cpp index 587e9c68..8aa6fb0a 100644 --- a/xml.cpp +++ b/xml.cpp @@ -31,15 +31,16 @@ Skill skill_name_to_id(const std::string & name, bool do_warn) std::string skill_id = boost::to_lower_copy(skill_names[i]); skill_map[skill_id] = i; } - skill_map["armored"] = skill_map["armor"]; // Special case for Armor: id and name differ + skill_map["armored"] = skill_map["armor"]; // Special case for Armor: id and name differ + skill_map["besiege"] = skill_map["mortar"]; // Special case for Mortar: id and name differ } auto x = skill_map.find(boost::to_lower_copy(name)); if (x == skill_map.end()) { if (do_warn and unknown_skills.count(name) == 0) - { // Warn only once for each unknown skill + { // Warn only once for each new skill unknown_skills.insert(name); - std::cerr << "Warning: Ignore unknown skill [" << name << "] in data/cards.xml\n"; + std::cerr << "Warning: Ignore new skill [" << name << "] in data/cards.xml\n"; } return no_skill; } From aad11091697a5360b0b04d792981b7e1050f6a92 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 28 Jun 2015 00:35:24 +0800 Subject: [PATCH 336/406] No longer warn for new skills. --- tyrant_optimize.cpp | 8 ++++---- xml.cpp | 8 +------- xml.h | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index faedca99..3f741123 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1532,7 +1532,7 @@ int main(int argc, char** argv) { std::vector tokens; boost::split(tokens, opt_effect, boost::is_any_of(" -")); - Skill skill_id = skill_name_to_id(tokens[0], false); + Skill skill_id = skill_name_to_id(tokens[0]); unsigned skill_index = 1; if (BEGIN_BGE_SKILL < skill_id && skill_id < END_BGE_SKILL) { @@ -1562,13 +1562,13 @@ int main(int argc, char** argv) } if (skill_index < tokens.size()) { - bg_skill.s = skill_name_to_id(tokens[skill_index], false); + bg_skill.s = skill_name_to_id(tokens[skill_index]); if (bg_skill.s != no_skill) { skill_index += 1; if (skill_index < tokens.size()) { - bg_skill.s2 = skill_name_to_id(tokens[skill_index], false); + bg_skill.s2 = skill_name_to_id(tokens[skill_index]); if (bg_skill.s2 != no_skill) { skill_index += 1; @@ -1680,7 +1680,7 @@ int main(int argc, char** argv) auto key_str = boost::to_lower_copy(tokens[2]); if (type_str == "su" || type_str == "sd") { - Skill skill_id = skill_name_to_id(key_str, false); + Skill skill_id = skill_name_to_id(key_str); if (skill_id == no_skill) { std::cerr << "Error: Expect skill in quest \"" << opt_quest << "\".\n"; diff --git a/xml.cpp b/xml.cpp index 8aa6fb0a..7b4cbcd0 100644 --- a/xml.cpp +++ b/xml.cpp @@ -20,10 +20,9 @@ // mission only and test cards have no set using namespace rapidxml; -Skill skill_name_to_id(const std::string & name, bool do_warn) +Skill skill_name_to_id(const std::string & name) { static std::map skill_map; - static std::set unknown_skills; if(skill_map.empty()) { for(unsigned i(0); i < Skill::num_skills; ++i) @@ -37,11 +36,6 @@ Skill skill_name_to_id(const std::string & name, bool do_warn) auto x = skill_map.find(boost::to_lower_copy(name)); if (x == skill_map.end()) { - if (do_warn and unknown_skills.count(name) == 0) - { // Warn only once for each new skill - unknown_skills.insert(name); - std::cerr << "Warning: Ignore new skill [" << name << "] in data/cards.xml\n"; - } return no_skill; } else diff --git a/xml.h b/xml.h index 682cb897..ed2d9cb3 100644 --- a/xml.h +++ b/xml.h @@ -8,7 +8,7 @@ class Cards; class Decks; class Achievement; -Skill skill_name_to_id(const std::string & name, bool do_warn=true); +Skill skill_name_to_id(const std::string & name); void load_cards_xml(Cards & all_cards, const std::string & filename, bool do_warn_on_missing); void load_decks_xml(Decks& decks, const Cards& all_cards, const std::string & mission_filename, const std::string & raid_filename, bool do_warn_on_missing); void load_recipes_xml(Cards& all_cards, const std::string & filename, bool do_warn_on_missing); From ad35dc64b29e5b5bb7cebfa33501a4e564bd081d Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 28 Jun 2015 11:41:37 +0800 Subject: [PATCH 337/406] Fix bug: should not calculate quest score in non-quest mode. --- sim.cpp | 17 ++++++++++++++--- tyrant_optimize.cpp | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/sim.cpp b/sim.cpp index 4ac9aedf..8b4403cf 100644 --- a/sim.cpp +++ b/sim.cpp @@ -559,9 +559,20 @@ Results play(Field* fd) ++fd->turn; } const auto & p = fd->players; - unsigned raid_damage = 15 + (std::min(p[1]->deck->deck_size, (fd->turn + 1) / 2) - p[1]->assaults.size() - p[1]->structures.size()) - (10 * p[1]->commander.m_hp / p[1]->commander.m_max_hp); - unsigned quest_score = fd->quest.must_fulfill ? (fd->quest_counter >= fd->quest.quest_value ? fd->quest.quest_score : 0) : std::min(fd->quest.quest_score, fd->quest.quest_score * fd->quest_counter / fd->quest.quest_value); - _DEBUG_MSG(1, "Quest: %u / %u = %u%%.\n", fd->quest_counter, fd->quest.quest_value, quest_score); + unsigned raid_damage = 0; + unsigned quest_score = 0; + switch (fd->optimization_mode) + { + case OptimizationMode::raid: + raid_damage = 15 + (std::min(p[1]->deck->deck_size, (fd->turn + 1) / 2) - p[1]->assaults.size() - p[1]->structures.size()) - (10 * p[1]->commander.m_hp / p[1]->commander.m_max_hp); + break; + case OptimizationMode::quest: + quest_score = fd->quest.must_fulfill ? (fd->quest_counter >= fd->quest.quest_value ? fd->quest.quest_score : 0) : std::min(fd->quest.quest_score, fd->quest.quest_score * fd->quest_counter / fd->quest.quest_value); + _DEBUG_MSG(1, "Quest: %u / %u = %u%%.\n", fd->quest_counter, fd->quest.quest_value, quest_score); + break; + default: + break; + } // you lose if(fd->players[0]->commander.m_hp == 0) { diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 3f741123..42ccb4ab 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -268,7 +268,7 @@ bool adjust_deck(Deck * deck, const signed from_slot, const signed to_slot, cons deck_cost = get_deck_cost(deck); if (use_top_level_card || deck_cost <= fund) { break; } - if (i < freezed_cards) + if (i < (signed)freezed_cards) { return false; } for (auto recipe_it : card_in->m_recipe_cards) { candidate_cards.emplace(recipe_it.first); } From 40eb90daebed5e5dc5254d725800d8ee6744e69d Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 10 Jul 2015 16:18:12 +0800 Subject: [PATCH 338/406] Support Campaign Warden. --- data/raids.xml | 462 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 6d15deab..0d9b2a5e 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -2380,6 +2380,468 @@ + + 611 + Ward101 + Warden Normal 1 + 1570 + 1 + + + 4439 + 4439 + 6556 + 6556 + 30058 + 30600 + 30708 + 31415 + 31419 + 31425 + + + + + + 612 + Ward102 + Warden Normal 2 + 1570 + 1 + + + 4444 + 4444 + 6560 + 6560 + 16762 + 16788 + 31415 + 31419 + 31425 + 31472 + + + + + + 613 + Ward103 + Warden Normal 3 + 1837 + 1 + + + 4598 + 6435 + 6435 + 31274 + 31418 + 31418 + 31419 + 31425 + 31472 + 31601 + + + + + + 614 + Ward104 + Warden Normal 4 + 1570 + 1 + + + 4439 + 10587 + 10587 + 12476 + 31271 + 31413 + 31421 + 31425 + 31598 + 31598 + + + + + + 615 + Ward105 + Warden Normal 5 + 1570 + 1 + + + 4444 + 4444 + 6560 + 6658 + 12218 + 15823 + 16972 + 31413 + 31421 + 31425 + + + + + + 616 + Ward106 + Warden Normal 6 + 1843 + 1 + + + 4095 + 7772 + 13065 + 13292 + 30097 + 30609 + 31413 + 31424 + 31424 + 31425 + + + + + + 617 + Ward107 + Warden Normal 7 + 1849 + 1 + + + 6750 + 12843 + 12966 + 13505 + 15299 + 31274 + 31418 + 31419 + 31425 + 31425 + + + + + + 621 + Ward201 + Warden Heroic 1 + 1833 + 1 + + + 30064 + 30502 + 30603 + 30612 + 30612 + 30711 + 30711 + 31418 + 31421 + 31427 + + + + + + 622 + Ward202 + Warden Heroic 2 + 1833 + 1 + + + 16767 + 16797 + 30513 + 30516 + 31190 + 31334 + 31418 + 31421 + 31427 + 31478 + + + + + + 623 + Ward203 + Warden Heroic 3 + 1839 + 1 + + + 6441 + 6441 + 6536 + 16977 + 31280 + 31418 + 31418 + 31421 + 31427 + 31604 + + + + + + 624 + Ward204 + Warden Heroic 4 + 1835 + 1 + + + 10590 + 10590 + 12478 + 12478 + 14073 + 31415 + 31424 + 31427 + 31604 + 31604 + + + + + + 625 + Ward205 + Warden Heroic 5 + 1835 + 1 + + + 6664 + 12224 + 12224 + 13071 + 15829 + 15829 + 16977 + 31415 + 31424 + 31427 + + + + + + 626 + Ward206 + Warden Heroic 6 + 1845 + 1 + + + 7592 + 7772 + 13077 + 13316 + 30103 + 30609 + 31415 + 31424 + 31424 + 31427 + + + + + + 627 + Ward207 + Warden Heroic 7 + 1851 + 1 + + + 8384 + 12855 + 12972 + 13511 + 16689 + 31415 + 31421 + 31427 + 31427 + 31661 + + + + + + 631 + Ward301 + Warden Mythic 1 + 1835 + 1 + + + 16851 + 30073 + 30513 + 30513 + 30615 + 30615 + 30723 + 31418 + 31424 + 31430 + + + + + + 632 + Ward302 + Warden Mythic 2 + 1835 + 1 + + + 16371 + 16767 + 16803 + 16851 + 31190 + 31190 + 31418 + 31424 + 31430 + 31484 + + + + + + 633 + Ward303 + Warden Mythic 3 + 1842 + 1 + + + 6548 + 12233 + 13986 + 13986 + 14076 + 31418 + 31418 + 31424 + 31430 + 31610 + + + + + + 634 + Ward304 + Warden Mythic 4 + 1836 + 1 + + + 10596 + 12481 + 12481 + 14076 + 16977 + 31418 + 31424 + 31430 + 31610 + 31610 + + + + + + 635 + Ward305 + Warden Mythic 5 + 1836 + 1 + + + 7592 + 12233 + 12233 + 15829 + 15829 + 16491 + 16977 + 31418 + 31424 + 31430 + + + + + + 636 + Ward306 + Warden Mythic 6 + 1848 + 1 + + + 8575 + 14725 + 15347 + 30109 + 30519 + 31418 + 31424 + 31424 + 31430 + 31484 + + + + + + 637 + Ward307 + Warden Mythic 7 + 1848 + 1 + + + 7820 + 12981 + 14076 + 16689 + 30519 + 31418 + 31424 + 31430 + 31430 + 31670 + + + + From 3d0416ec075a273aee61e50e328393f5794f1eaa Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 10 Jul 2015 20:15:03 +0800 Subject: [PATCH 339/406] Update campaign deck. --- data/raids.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/raids.xml b/data/raids.xml index 0d9b2a5e..670c03fe 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -2824,7 +2824,7 @@ 637 Ward307 Warden Mythic 7 - 1848 + 1854 1 From ebdb9265c27c597ceca4fa8c61004e59b9cec46d Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 11 Jul 2015 20:21:36 +0800 Subject: [PATCH 340/406] Update campaign decks. --- data/raids.xml | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index 670c03fe..8bb3915f 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -2433,14 +2433,14 @@ 4598 - 6435 - 6435 - 31274 - 31418 - 31418 + 6434 + 6434 + 31271 + 31415 + 31415 31419 31425 - 31472 + 31469 31601 @@ -2455,7 +2455,7 @@ 4439 - 10587 + 4439 10587 12476 31271 @@ -2505,8 +2505,8 @@ 30097 30609 31413 - 31424 - 31424 + 31421 + 31421 31425 @@ -2526,7 +2526,7 @@ 13505 15299 31274 - 31418 + 31413 31419 31425 31425 @@ -2549,7 +2549,7 @@ 30612 30711 30711 - 31418 + 31415 31421 31427 @@ -2570,7 +2570,7 @@ 30516 31190 31334 - 31418 + 31415 31421 31427 31478 @@ -2588,14 +2588,14 @@ 6441 6441 - 6536 - 16977 + 6534 + 16975 31280 31418 31418 31421 31427 - 31604 + 31601 @@ -2614,8 +2614,8 @@ 12478 14073 31415 + 31421 31424 - 31427 31604 31604 @@ -2638,7 +2638,7 @@ 15829 16977 31415 - 31424 + 31421 31427 @@ -2740,11 +2740,11 @@ 1 + 6444 6548 12233 - 13986 - 13986 14076 + 31280 31418 31418 31424 From 13a54d52f2cebab4bf6abafc504d2501930a81b7 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 14 Jul 2015 07:15:11 +0800 Subject: [PATCH 341/406] Add quest cs (cards survival). --- sim.cpp | 9 +++++++++ tyrant.h | 1 + tyrant_optimize.cpp | 13 +++++++++++++ 3 files changed, 23 insertions(+) diff --git a/sim.cpp b/sim.cpp index 8b4403cf..9a2acf35 100644 --- a/sim.cpp +++ b/sim.cpp @@ -567,6 +567,15 @@ Results play(Field* fd) raid_damage = 15 + (std::min(p[1]->deck->deck_size, (fd->turn + 1) / 2) - p[1]->assaults.size() - p[1]->structures.size()) - (10 * p[1]->commander.m_hp / p[1]->commander.m_max_hp); break; case OptimizationMode::quest: + if (fd->quest.quest_type == QuestType::card_survival) + { + for (const auto & status: p[0]->assaults.m_indirect) + { fd->quest_counter += (fd->quest.quest_key == status->m_card->m_id); } + for (const auto & status: p[0]->structures.m_indirect) + { fd->quest_counter += (fd->quest.quest_key == status->m_card->m_id); } + for (const auto & card: p[0]->deck->shuffled_cards) + { fd->quest_counter += (fd->quest.quest_key == card->m_id); } + } quest_score = fd->quest.must_fulfill ? (fd->quest_counter >= fd->quest.quest_value ? fd->quest.quest_score : 0) : std::min(fd->quest.quest_score, fd->quest.quest_score * fd->quest_counter / fd->quest.quest_value); _DEBUG_MSG(1, "Quest: %u / %u = %u%%.\n", fd->quest_counter, fd->quest.quest_value, quest_score); break; diff --git a/tyrant.h b/tyrant.h index 1f8986ee..00053e1a 100644 --- a/tyrant.h +++ b/tyrant.h @@ -98,6 +98,7 @@ enum QuestType type_card_use, faction_assault_card_kill, type_card_kill, + card_survival, num_objective_types }; } diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 42ccb4ab..82843bb4 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1719,6 +1719,19 @@ int main(int argc, char** argv) } } } + else if (type_str == "cs") + { + auto card_it = all_cards.player_cards_by_name.find(simplify_name(key_str)); + if (card_it != all_cards.player_cards_by_name.end()) + { + quest.quest_type = QuestType::card_survival; + quest.quest_key = card_it->second->m_id; + } + else { + std::cerr << "Error: Expect card in quest \"" << opt_quest << "\".\n"; + return 0; + } + } else { throw std::runtime_error("Expect one of: su n skill; sd n skill; cu n faction/strcture; ck n structure"); From 33b11e0a1bbef3fe028f6de46cba2fd42d575429 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 14 Jul 2015 08:02:07 +0800 Subject: [PATCH 342/406] Support Raid #12 "Intrepid Raid". --- data/raids.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 8bb3915f..e39a1905 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -252,6 +252,27 @@ + + 12 + Intrepid Raid + 1855 + 26 + + + 8774 + 8784 + 31827 + 31827 + 31837 + 31837 + 31847 + 31847 + 31857 + 31857 + + + + From da1efc49126a15154348822832a2023ed4b0caa7 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 14 Jul 2015 08:02:46 +0800 Subject: [PATCH 343/406] Add skill Mortar. --- sim.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++++----------- tyrant.h | 2 +- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/sim.cpp b/sim.cpp index 9a2acf35..26abb74c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1323,21 +1323,27 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, c } template<> -inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { add_hp(fd, dst, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ + dst->m_jammed = true; +} + +template<> +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { add_hp(fd, dst, s.x); } template<> -inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - dst->m_jammed = true; + remove_hp(fd, dst, dst->m_card->m_type == CardType::structure ? s.x : (s.x + 1) / 2); } template<> @@ -1450,15 +1456,15 @@ template<> std::vector& skill_targets(Field* fd, CardStatu template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } - template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } @@ -1468,15 +1474,15 @@ template<> std::vector& skill_targets(Field* fd, CardStatu template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_hostile_structure(fd, src_status)); } + template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_assault(fd, src_status)); } template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_assault(fd, src_status)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_structure(fd, src_status)); } - template bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_status, const SkillSpec& s, bool is_evadable, bool & has_counted_quest) { @@ -1564,6 +1570,36 @@ size_t select_targets(Field* fd, CardStatus* src_status, const SkillSpec& s) return n_targets; } +template<> +size_t select_targets(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + size_t n_candidates = select_fast(fd, src_status, skill_targets(fd, src_status), s); + if (n_candidates == 0) + { + n_candidates = select_fast(fd, src_status, skill_targets(fd, src_status), s); + if (n_candidates == 0) + { + return n_candidates; + } + } + _DEBUG_SELECTION("%s", skill_names[mortar].c_str()); + unsigned n_targets = s.n > 0 ? s.n : 1; + if (s.all || n_targets >= n_candidates) + { + return n_candidates; + } + for (unsigned i = 0; i < n_targets; ++i) + { + std::swap(fd->selection_array[i], fd->selection_array[fd->rand(i, n_candidates - 1)]); + } + fd->selection_array.resize(n_targets); + if (n_targets > 1) + { + std::sort(fd->selection_array.begin(), fd->selection_array.end(), [](const CardStatus * a, const CardStatus * b) { return a->m_index < b->m_index; }); + } + return n_targets; +} + template void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { @@ -1607,7 +1643,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil void fill_skill_table() { memset(skill_table, 0, sizeof skill_table); - skill_table[mortar] = perform_targetted_hostile_fast; + skill_table[mortar] = perform_targetted_hostile_fast; skill_table[enfeeble] = perform_targetted_hostile_fast; skill_table[enhance] = perform_targetted_allied_fast; skill_table[evolve] = perform_targetted_allied_fast; diff --git a/tyrant.h b/tyrant.h index 00053e1a..c45461ae 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.2" +#define TYRANT_OPTIMIZER_VERSION "2.10.3" #include #include From 1952120eb98d09bfc874a44f40cb84dceb9b16ba Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 14 Jul 2015 20:11:27 +0800 Subject: [PATCH 344/406] v2.10.2 --- tyrant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tyrant.h b/tyrant.h index c45461ae..00053e1a 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.3" +#define TYRANT_OPTIMIZER_VERSION "2.10.2" #include #include From 08dc194fb8ef15fc5589123b16e5687871e02f96 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 15 Jul 2015 15:21:11 +0800 Subject: [PATCH 345/406] Support BGE TurningTides. --- sim.cpp | 112 +++++++++++++++++++++++++++++++++++++---------------- tyrant.cpp | 2 +- tyrant.h | 4 +- 3 files changed, 82 insertions(+), 36 deletions(-) diff --git a/sim.cpp b/sim.cpp index 26abb74c..fb2ac489 100644 --- a/sim.cpp +++ b/sim.cpp @@ -102,7 +102,7 @@ inline void CardStatus::set(const Card& card) std::memset(m_skill_cd, 0, sizeof m_skill_cd); } //------------------------------------------------------------------------------ -inline int attack_power(const CardStatus* att) +inline unsigned attack_power(const CardStatus* att) { return(safe_minus(att->m_attack + att->m_rallied, att->m_weakened + att->m_corroded_weakened)); } @@ -153,12 +153,12 @@ std::string card_description(const Cards& cards, const Card* c) //------------------------------------------------------------------------------ std::string CardStatus::description() const { - std::string desc; + std::string desc = "P" + to_string(m_player) + " "; switch(m_card->m_type) { - case CardType::commander: desc = "Commander "; break; - case CardType::assault: desc = "Assault " + to_string(m_index) + " "; break; - case CardType::structure: desc = "Structure " + to_string(m_index) + " "; break; + case CardType::commander: desc += "Commander "; break; + case CardType::assault: desc += "Assault " + to_string(m_index) + " "; break; + case CardType::structure: desc += "Structure " + to_string(m_index) + " "; break; case CardType::num_cardtypes: assert(false); break; } desc += "[" + m_card->m_name; @@ -291,15 +291,16 @@ void resolve_skill(Field* fd) auto& status(std::get<0>(skill_instance)); const auto& ss(std::get<1>(skill_instance)); fd->skill_queue.pop_front(); - if (!status->m_jammed) + if (status->m_jammed) { - signed evolved_offset = status->m_evolved_skill_offset[ss.id]; - auto& evolved_s = status->m_evolved_skill_offset[ss.id] != 0 ? apply_evolve(ss, evolved_offset) : ss; - unsigned enhanced_value = status->enhanced(evolved_s.id); - auto& enhanced_s = enhanced_value > 0 ? apply_enhance(evolved_s, enhanced_value) : evolved_s; - auto& modified_s = enhanced_s; - skill_table[modified_s.id](fd, status, modified_s); + continue; } + signed evolved_offset = status->m_evolved_skill_offset[ss.id]; + auto& evolved_s = status->m_evolved_skill_offset[ss.id] != 0 ? apply_evolve(ss, evolved_offset) : ss; + unsigned enhanced_value = status->enhanced(evolved_s.id); + auto& enhanced_s = enhanced_value > 0 ? apply_enhance(evolved_s, enhanced_value) : evolved_s; + auto& modified_s = enhanced_s; + skill_table[modified_s.id](fd, status, modified_s); } } //------------------------------------------------------------------------------ @@ -537,7 +538,7 @@ Results play(Field* fd) { if (attacked) { - unsigned v = std::min(current_status->m_corroded_rate, safe_minus(current_status->m_attack, current_status->m_corroded_weakened)); + unsigned v = std::min(current_status->m_corroded_rate, attack_power(current_status)); _DEBUG_MSG(1, "%s loses Attack by %u.\n", status_description(current_status).c_str(), v); current_status->m_corroded_weakened += v; } @@ -1380,7 +1381,7 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, c template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - dst->m_weakened += s.x; + dst->m_weakened += std::min(s.x, attack_power(dst)); } template @@ -1601,42 +1602,87 @@ size_t select_targets(Field* fd, CardStatus* src_status, const SkillSpec } template -void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { select_targets(fd, src_status, s); bool has_counted_quest = false; - for (CardStatus * dst_status: fd->selection_array) + for (CardStatus * dst: fd->selection_array) { - if (check_and_perform_skill(fd, src_status, dst_status, s, ! src_status->m_overloaded, has_counted_quest)) + if(dst->m_inhibited > 0 && !src_status->m_overloaded) { - // Payback - if(dst_status->m_paybacked < dst_status->skill(payback) && skill_check(fd, dst_status, src_status) && - skill_predicate(fd, src_status, src_status, s) && skill_check(fd, src_status, dst_status)) - { - ++ dst_status->m_paybacked; - _DEBUG_MSG(1, "%s Payback %s on %s\n", status_description(dst_status).c_str(), skill_short_description(s).c_str(), status_description(src_status).c_str()); - perform_skill(fd, dst_status, src_status, s); - } + _DEBUG_MSG(1, "%s %s on %s but it is inhibited\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst).c_str()); + -- dst->m_inhibited; + continue; } + check_and_perform_skill(fd, src_status, dst, s, false, has_counted_quest); } - prepend_on_death(fd); } template -void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { select_targets(fd, src_status, s); bool has_counted_quest = false; - for (CardStatus * dst: fd->selection_array) + std::vector paybackers; + if (fd->bg_effects.count(turningtides) && skill_id == weaken) { - if(dst->m_inhibited > 0 && !src_status->m_overloaded) + unsigned turningtides_value = 0; + for (CardStatus * dst_status: fd->selection_array) { - _DEBUG_MSG(1, "%s %s on %s but it is inhibited\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst).c_str()); - -- dst->m_inhibited; - continue; + unsigned old_attack = attack_power(dst_status); + if (check_and_perform_skill(fd, src_status, dst_status, s, ! src_status->m_overloaded, has_counted_quest)) + { + turningtides_value = std::max(turningtides_value, safe_minus(old_attack, attack_power(dst_status))); + // Payback + if(dst_status->m_paybacked < dst_status->skill(payback) && skill_check(fd, dst_status, src_status) && + skill_predicate(fd, src_status, src_status, s) && skill_check(fd, src_status, dst_status)) + { + paybackers.push_back(dst_status); + } + } } - check_and_perform_skill(fd, src_status, dst, s, false, has_counted_quest); + if (turningtides_value > 0) + { + SkillSpec ss_rally{rally, turningtides_value, allfactions, 0, 0, no_skill, no_skill, s.all,}; + _DEBUG_MSG(1, "Turning Tides %u!\n", turningtides_value); + perform_targetted_allied_fast(fd, src_status, ss_rally); + } + for (CardStatus * pb_status: paybackers) + { + ++ pb_status->m_paybacked; + unsigned old_attack = attack_power(src_status); + _DEBUG_MSG(1, "%s Payback %s on %s\n", status_description(pb_status).c_str(), skill_short_description(s).c_str(), status_description(src_status).c_str()); + perform_skill(fd, pb_status, src_status, s); + turningtides_value = std::max(turningtides_value, safe_minus(old_attack, attack_power(src_status))); + if (turningtides_value > 0) + { + SkillSpec ss_rally{rally, turningtides_value, allfactions, 0, 0, no_skill, no_skill, false,}; + _DEBUG_MSG(1, "Paybacked Turning Tides %u!\n", turningtides_value); + perform_targetted_allied_fast(fd, pb_status, ss_rally); + } + } + prepend_on_death(fd); + return; } + for (CardStatus * dst_status: fd->selection_array) + { + if (check_and_perform_skill(fd, src_status, dst_status, s, ! src_status->m_overloaded, has_counted_quest)) + { + // Payback + if(dst_status->m_paybacked < dst_status->skill(payback) && skill_check(fd, dst_status, src_status) && + skill_predicate(fd, src_status, src_status, s) && skill_check(fd, src_status, dst_status)) + { + paybackers.push_back(dst_status); + } + } + } + for (CardStatus * pb_status: paybackers) + { + ++ pb_status->m_paybacked; + _DEBUG_MSG(1, "%s Payback %s on %s\n", status_description(pb_status).c_str(), skill_short_description(s).c_str(), status_description(src_status).c_str()); + perform_skill(fd, pb_status, src_status, s); + } + prepend_on_death(fd); } //------------------------------------------------------------------------------ diff --git a/tyrant.cpp b/tyrant.cpp index 36753275..3ff5188c 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -30,7 +30,7 @@ std::string skill_names[Skill::num_skills] = "Legion", // Pseudo-skill for passive BGEs: "", - "Bloodlust", "Reaping", "Metamorphosis", "Counterflux", "Fortification", + "Bloodlust", "Reaping", "Metamorphosis", "Counterflux", "Fortification", "TurningTides", "", }; diff --git a/tyrant.h b/tyrant.h index 00053e1a..40acac74 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.2" +#define TYRANT_OPTIMIZER_VERSION "2.10.3" #include #include @@ -46,7 +46,7 @@ enum Skill legion, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, - bloodlust, reaping, metamorphosis, counterflux, fortification, + bloodlust, reaping, metamorphosis, counterflux, fortification, turningtides, END_BGE_SKILL, num_skills }; From 6c55306b9ac574f1f5a4254b1efaa686b6652148 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 6 Aug 2015 17:14:01 +0800 Subject: [PATCH 346/406] Fix bug: Mortar should honor Enfeeble and Protect. --- sim.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index fb2ac489..462c7c8c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1344,7 +1344,15 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, con template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - remove_hp(fd, dst, dst->m_card->m_type == CardType::structure ? s.x : (s.x + 1) / 2); + if (dst->m_card->m_type == CardType::structure) + { + remove_hp(fd, dst, s.x); + } + else + { + unsigned strike_dmg = safe_minus((s.x + 1) / 2 + dst->m_enfeebled, src->m_overloaded ? 0 : dst->protected_value()); + remove_hp(fd, dst, strike_dmg); + } } template<> From 878afaa9de6bc9004e5e9166e2f529bdd2e94293 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 12 Aug 2015 17:14:24 -0400 Subject: [PATCH 347/406] Support BGE EnduringRage (no space in name). --- sim.cpp | 8 ++++++++ tyrant.cpp | 2 +- tyrant.h | 4 ++-- tyrant_optimize.cpp | 5 ++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/sim.cpp b/sim.cpp index 462c7c8c..246a3cfd 100644 --- a/sim.cpp +++ b/sim.cpp @@ -992,6 +992,14 @@ struct PerformAttack { fd->inc_counter(QuestType::skill_use, berserk); } + if (fd->bg_effects.count(enduringrage)) + { + unsigned bge_denominator = fd->bg_effects.at(enduringrage) ? fd->bg_effects.at(enduringrage) : 2; + unsigned bge_value = (berserk_value - 1) / bge_denominator + 1; + _DEBUG_MSG(1, "EnduringRage: %s heals and protects itself for %u\n", status_description(att_status).c_str(), bge_value); + add_hp(fd, att_status, bge_value); + att_status->m_protected += bge_value; + } } unsigned corrosive_value = def_status->skill(corrosive); if (corrosive_value > att_status->m_corroded_rate && skill_check(fd, def_status, att_status)) diff --git a/tyrant.cpp b/tyrant.cpp index 3ff5188c..61cbd9d2 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -30,7 +30,7 @@ std::string skill_names[Skill::num_skills] = "Legion", // Pseudo-skill for passive BGEs: "", - "Bloodlust", "Reaping", "Metamorphosis", "Counterflux", "Fortification", "TurningTides", + "Bloodlust", "Reaping", "Metamorphosis", "Counterflux", "Fortification", "TurningTides", "EnduringRage", "", }; diff --git a/tyrant.h b/tyrant.h index 40acac74..0b0cd0e9 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.3" +#define TYRANT_OPTIMIZER_VERSION "2.10.4" #include #include @@ -46,7 +46,7 @@ enum Skill legion, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, - bloodlust, reaping, metamorphosis, counterflux, fortification, turningtides, + bloodlust, reaping, metamorphosis, counterflux, fortification, turningtides, enduringrage, END_BGE_SKILL, num_skills }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 82843bb4..77c46119 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1151,7 +1151,10 @@ void print_available_effects() " Reaping X\n" " Metamorphosis\n" " Counterflux\n" - " Fortification\n"; + " Fortification\n" + " TurningTides\n" + " EnduringRage\n" + ; } void usage(int argc, char** argv) { From 6d248f4bdf193d9a8516f240f36a330413e9a486 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 13 Aug 2015 09:21:49 -0400 Subject: [PATCH 348/406] Cleanup action cards. --- cards.cpp | 1 - cards.h | 1 - tyrant_optimize.cpp | 2 -- 3 files changed, 4 deletions(-) diff --git a/cards.cpp b/cards.cpp index c2efaaed..5a964785 100644 --- a/cards.cpp +++ b/cards.cpp @@ -66,7 +66,6 @@ void Cards::organize() player_commanders.clear(); player_assaults.clear(); player_structures.clear(); - player_actions.clear(); // Round 1: set cards_by_id for(Card* card: cards) { diff --git a/cards.h b/cards.h index 6584afbc..5b8e372f 100644 --- a/cards.h +++ b/cards.h @@ -19,7 +19,6 @@ class Cards std::vector player_commanders; std::vector player_assaults; std::vector player_structures; - std::vector player_actions; std::map player_cards_abbr; const Card * by_id(unsigned id) const; void organize(); diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 77c46119..946357b9 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -817,7 +817,6 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); - non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_actions.begin(), proc.cards.player_actions.end()); non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; @@ -977,7 +976,6 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); - non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_actions.begin(), proc.cards.player_actions.end()); non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; From 29eae12344651f5dbe2cd4ce4c3aad8a00e9fe2c Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 17 Aug 2015 18:47:39 -0400 Subject: [PATCH 349/406] Add skill Swipe. --- sim.cpp | 98 ++++++++++++++++++++++++++---------------------------- sim.h | 1 + tyrant.cpp | 2 +- tyrant.h | 4 +-- 4 files changed, 51 insertions(+), 54 deletions(-) diff --git a/sim.cpp b/sim.cpp index 246a3cfd..fff19c30 100644 --- a/sim.cpp +++ b/sim.cpp @@ -32,6 +32,28 @@ inline unsigned Field::make_selection_array(CardsIter first, CardsIter last, Fun } return(this->selection_array.size()); } +inline const std::vector Field::adjacent_assaults(const CardStatus * status) +{ + std::vector res; + auto & assaults = this->players[status->m_player]->assaults; + if (status->m_index > 0) + { + auto left_status = &assaults[status->m_index - 1]; + if (left_status->m_hp > 0) + { + res.push_back(left_status); + } + } + if (status->m_index + 1 < assaults.size()) + { + auto right_status = &assaults[status->m_index + 1]; + if (right_status->m_hp > 0) + { + res.push_back(right_status); + } + } + return res; +} inline void Field::print_selection_array() { #ifndef NDEBUG @@ -243,29 +265,15 @@ void prepend_on_death(Field* fd) // avenge if (status->m_card->m_type == CardType::assault) { - auto & assaults = fd->players[status->m_player]->assaults; - if (status->m_index > 0) - { - auto left_status = &assaults[status->m_index - 1]; - unsigned avenge_value = left_status->skill(avenge); - if (left_status->m_hp > 0 && avenge_value > 0) - { - _DEBUG_MSG(1, "%s activates Avenge %u\n", status_description(left_status).c_str(), avenge_value); - left_status->m_attack += avenge_value; - left_status->m_max_hp += avenge_value; - left_status->m_hp += avenge_value; - } - } - if (status->m_index + 1 < assaults.size()) + for (auto && adj_status: fd->adjacent_assaults(status)) { - auto right_status = &assaults[status->m_index + 1]; - unsigned avenge_value = right_status->skill(avenge); - if (right_status->m_hp > 0 && avenge_value > 0) + unsigned avenge_value = adj_status->skill(avenge); + if (avenge_value > 0) { - _DEBUG_MSG(1, "%s activates Avenge %u\n", status_description(right_status).c_str(), avenge_value); - right_status->m_attack += avenge_value; - right_status->m_max_hp += avenge_value; - right_status->m_hp += avenge_value; + _DEBUG_MSG(1, "%s activates Avenge %u\n", status_description(adj_status).c_str(), avenge_value); + adj_status->m_attack += avenge_value; + adj_status->m_max_hp += avenge_value; + adj_status->m_hp += avenge_value; } } } @@ -1064,22 +1072,9 @@ struct PerformAttack unsigned armor_value = def_status->skill(armor); if (def_status->m_card->m_type == CardType::assault && fd->bg_effects.count(fortification)) { - auto & assaults = fd->players[def_status->m_player]->assaults; - if (def_status->m_index > 0) + for (auto && adj_status: fd->adjacent_assaults(def_status)) { - auto left_status = &assaults[def_status->m_index - 1]; - if (left_status->m_hp > 0) - { - armor_value = std::max(armor_value, left_status->skill(armor)); - } - } - if (def_status->m_index + 1 < assaults.size()) - { - auto right_status = &assaults[def_status->m_index + 1]; - if (right_status->m_hp > 0) - { - armor_value = std::max(armor_value, right_status->skill(armor)); - } + armor_value = std::max(armor_value, adj_status->skill(armor)); } } if(armor_value > 0) @@ -1163,7 +1158,7 @@ void PerformAttack::do_leech() } } -// General attack phase by the currently evaluated assault, taking into accounts exotic stuff such as flurry,swipe,etc. +// General attack phase by the currently evaluated assault, taking into accounts exotic stuff such as flurry, etc. unsigned attack_commander(Field* fd, CardStatus* att_status) { CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall @@ -1189,7 +1184,18 @@ bool attack_phase(Field* fd) unsigned att_dmg = 0; if (alive_assault(def_assaults, fd->current_ci)) { - att_dmg = PerformAttack{fd, att_status, &fd->tip->assaults[fd->current_ci]}.op(); + CardStatus * def_status = &fd->tip->assaults[fd->current_ci]; + att_dmg = PerformAttack{fd, att_status, def_status}.op(); + unsigned swipe_value = att_status->skill(swipe); + if (swipe_value > 0) + { + for (auto && adj_status: fd->adjacent_assaults(def_status)) + { + unsigned swipe_dmg = safe_minus(swipe_value + def_status->m_enfeebled, def_status->protected_value()); + _DEBUG_MSG(1, "%s swipes %s for %u damage\n", status_description(att_status).c_str(), status_description(adj_status).c_str(), swipe_dmg); + remove_hp(fd, adj_status, swipe_dmg); + } + } } else { @@ -1417,21 +1423,11 @@ template<> inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) { fd->selection_array.clear(); - auto & assaults = fd->players[src_status->m_player]->assaults; - if (src_status->m_index > 0) - { - auto left_status = &assaults[src_status->m_index - 1]; - if (skill_predicate(fd, src_status, left_status, s)) - { - fd->selection_array.push_back(left_status); - } - } - if (src_status->m_index + 1 < assaults.size()) + for (auto && adj_status: fd->adjacent_assaults(src_status)) { - auto right_status = &assaults[src_status->m_index + 1]; - if (skill_predicate(fd, src_status, right_status, s)) + if (skill_predicate(fd, src_status, adj_status, s)) { - fd->selection_array.push_back(right_status); + fd->selection_array.push_back(adj_status); } } return fd->selection_array.size(); diff --git a/sim.h b/sim.h index 473d5391..faea8d24 100644 --- a/sim.h +++ b/sim.h @@ -299,6 +299,7 @@ class Field template inline unsigned make_selection_array(CardsIter first, CardsIter last, Functor f); + inline const std::vector adjacent_assaults(const CardStatus * status); inline void print_selection_array(); inline void inc_counter(QuestType::QuestType quest_type, unsigned quest_key, unsigned value = 1) diff --git a/tyrant.cpp b/tyrant.cpp index 61cbd9d2..abf6429f 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -23,7 +23,7 @@ std::string skill_names[Skill::num_skills] = "Armor", "Avenge", "Corrosive", "Counter", "Evade", "Payback", "Refresh", "Wall", "", // Combat-Modifier: - "Flurry", "Pierce", "Valor", + "Flurry", "Pierce", "Swipe", "Valor", // Damage-Dependant: "Berserk", "Inhibit", "Leech", "Poison", "Venom", // Triggered: diff --git a/tyrant.h b/tyrant.h index 0b0cd0e9..fc6eba52 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.4" +#define TYRANT_OPTIMIZER_VERSION "2.10.5" #include #include @@ -39,7 +39,7 @@ enum Skill armor, avenge, corrosive, counter, evade, payback, refresh, wall, END_DEFENSIVE, // Combat-Modifier: - flurry, pierce, valor, + flurry, pierce, swipe, valor, // Damage-Dependant: berserk, inhibit, leech, poison, venom, // Triggered: From 66744c3ae6e8b2f57e8bff873e124b87e14df7d3 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 20 Aug 2015 10:01:32 -0400 Subject: [PATCH 350/406] Support Raid #13 "Pantheon Raid". --- data/raids.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index e39a1905..15651501 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -273,6 +273,27 @@ + + 13 + Pantheon Raid + 1883 + 26 + + + 32947 + 32947 + 32957 + 32957 + 32967 + 32967 + 32977 + 32977 + 32987 + 32987 + + + + From 084635c2b417a0fdf0fad545f0a9b80f4e02c869 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 20 Aug 2015 10:15:59 -0400 Subject: [PATCH 351/406] Error tolerance for raids.xml. --- xml.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/xml.cpp b/xml.cpp index 7b4cbcd0..f931f614 100644 --- a/xml.cpp +++ b/xml.cpp @@ -387,7 +387,15 @@ void read_raids(Decks& decks, const Cards& all_cards, const std::string & filena unsigned id(id_node ? atoi(id_node->value()) : 0); xml_node<>* name_node(raid_node->first_node("name")); std::string deck_name{name_node->value()}; - read_deck(decks, all_cards, raid_node, DeckType::raid, id, deck_name); + try + { + read_deck(decks, all_cards, raid_node, DeckType::raid, id, deck_name); + } + catch (const std::runtime_error& e) + { + std::cerr << "Warning: Failed to parse raid [" << deck_name << "] in file " << filename << ": [" << e.what() << "]. Skip the raid.\n"; + continue; + } } for(xml_node<>* campaign_node = root->first_node("campaign"); @@ -401,7 +409,15 @@ void read_raids(Decks& decks, const Cards& all_cards, const std::string & filena name_node; name_node = name_node->next_sibling("name")) { - read_deck(decks, all_cards, campaign_node, DeckType::campaign, id, name_node->value()); + try + { + read_deck(decks, all_cards, campaign_node, DeckType::campaign, id, name_node->value()); + } + catch (const std::runtime_error& e) + { + std::cerr << "Warning: Failed to parse campaign [" << name_node->value() << "] in file " << filename << ": [" << e.what() << "]. Skip the campaign.\n"; + continue; + } } } } From 5f9b2b812bfb1a7b36ac16f536a31bf9255b6fb1 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 21 Aug 2015 13:48:44 -0400 Subject: [PATCH 352/406] Fix bug: BGE EnduringRage should not heal dead units (for example killed by Counter damage). --- sim.cpp | 77 +++++++++++++++++++++++++++----------------------------- tyrant.h | 2 +- 2 files changed, 38 insertions(+), 41 deletions(-) diff --git a/sim.cpp b/sim.cpp index fff19c30..30121696 100644 --- a/sim.cpp +++ b/sim.cpp @@ -969,52 +969,49 @@ struct PerformAttack if(__builtin_expect(fd->end, false)) { return att_dmg; } damage_dependant_pre_oa(); - if(att_status->m_hp > 0) + if (att_status->m_hp > 0 && def_status->has_skill(counter) && skill_check(fd, def_status, att_status)) { - if(def_status->has_skill(counter) && skill_check(fd, def_status, att_status)) + // perform_skill_counter + unsigned counter_dmg(counter_damage(fd, att_status, def_status)); + if (def_status->m_player == 0) { - // perform_skill_counter - unsigned counter_dmg(counter_damage(fd, att_status, def_status)); - if (def_status->m_player == 0) - { - fd->inc_counter(QuestType::skill_use, counter); - fd->inc_counter(QuestType::skill_damage, counter, counter_dmg); - } - _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); - remove_hp(fd, att_status, counter_dmg); - if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects.count(counterflux)) - { - unsigned flux_denominator = fd->bg_effects.at(counterflux) ? fd->bg_effects.at(counterflux) : 4; - unsigned flux_value = (def_status->skill(counter) - 1) / flux_denominator + 1; - _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n", status_description(def_status).c_str(), flux_value); - add_hp(fd, def_status, flux_value); - def_status->m_attack += flux_value; - } + fd->inc_counter(QuestType::skill_use, counter); + fd->inc_counter(QuestType::skill_damage, counter, counter_dmg); } - unsigned berserk_value = att_status->skill(berserk); - if(berserk_value > 0 && skill_check(fd, att_status, nullptr)) + _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); + remove_hp(fd, att_status, counter_dmg); + if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects.count(counterflux)) { - // perform_skill_berserk - att_status->m_attack += berserk_value; - if (att_status->m_player == 0) - { - fd->inc_counter(QuestType::skill_use, berserk); - } - if (fd->bg_effects.count(enduringrage)) - { - unsigned bge_denominator = fd->bg_effects.at(enduringrage) ? fd->bg_effects.at(enduringrage) : 2; - unsigned bge_value = (berserk_value - 1) / bge_denominator + 1; - _DEBUG_MSG(1, "EnduringRage: %s heals and protects itself for %u\n", status_description(att_status).c_str(), bge_value); - add_hp(fd, att_status, bge_value); - att_status->m_protected += bge_value; - } + unsigned flux_denominator = fd->bg_effects.at(counterflux) ? fd->bg_effects.at(counterflux) : 4; + unsigned flux_value = (def_status->skill(counter) - 1) / flux_denominator + 1; + _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n", status_description(def_status).c_str(), flux_value); + add_hp(fd, def_status, flux_value); + def_status->m_attack += flux_value; + } + } + unsigned corrosive_value = def_status->skill(corrosive); + if (att_status->m_hp > 0 && corrosive_value > att_status->m_corroded_rate && skill_check(fd, def_status, att_status)) + { + // perform_skill_corrosive + _DEBUG_MSG(1, "%s corrodes %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), corrosive_value); + att_status->m_corroded_rate = corrosive_value; + } + unsigned berserk_value = att_status->skill(berserk); + if (att_status->m_hp > 0 && berserk_value > 0 && skill_check(fd, att_status, nullptr)) + { + // perform_skill_berserk + att_status->m_attack += berserk_value; + if (att_status->m_player == 0) + { + fd->inc_counter(QuestType::skill_use, berserk); } - unsigned corrosive_value = def_status->skill(corrosive); - if (corrosive_value > att_status->m_corroded_rate && skill_check(fd, def_status, att_status)) + if (fd->bg_effects.count(enduringrage)) { - // perform_skill_corrosive - _DEBUG_MSG(1, "%s corrodes %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), corrosive_value); - att_status->m_corroded_rate = corrosive_value; + unsigned bge_denominator = fd->bg_effects.at(enduringrage) ? fd->bg_effects.at(enduringrage) : 2; + unsigned bge_value = (berserk_value - 1) / bge_denominator + 1; + _DEBUG_MSG(1, "EnduringRage: %s heals and protects itself for %u\n", status_description(att_status).c_str(), bge_value); + add_hp(fd, att_status, bge_value); + att_status->m_protected += bge_value; } } do_leech(); diff --git a/tyrant.h b/tyrant.h index fc6eba52..7080605b 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.5" +#define TYRANT_OPTIMIZER_VERSION "2.10.6" #include #include From 827dc74e9f43f5c6f083585ebff0e3a774eb90cc Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 26 Aug 2015 17:52:28 -0400 Subject: [PATCH 353/406] Improve handling of card name duplication. Honor commander_max_level. --- card.h | 2 +- cards.cpp | 88 +++++++++++++++++++++++++-------------------- cards.h | 8 +++-- deck.cpp | 27 +++++++------- deck.h | 7 ++-- read.cpp | 10 ++++-- tyrant.h | 2 +- tyrant_optimize.cpp | 8 ++--- xml.cpp | 27 ++++++++++---- 9 files changed, 108 insertions(+), 71 deletions(-) diff --git a/card.h b/card.h index 99d5e425..919b3c20 100644 --- a/card.h +++ b/card.h @@ -1,9 +1,9 @@ #ifndef CARD_H_INCLUDED #define CARD_H_INCLUDED +#include #include #include -#include #include "tyrant.h" class Card diff --git a/cards.cpp b/cards.cpp index 5a964785..e34bdba6 100644 --- a/cards.cpp +++ b/cards.cpp @@ -1,7 +1,6 @@ #include "cards.h" #include -#include #include #include #include @@ -42,12 +41,12 @@ std::list get_abbreviations(const std::string& name) //------------------------------------------------------------------------------ Cards::~Cards() { - for(Card* c: cards) { delete(c); } + for (Card* c: all_cards) { delete(c); } } const Card* Cards::by_id(unsigned id) const { - std::map::const_iterator cardIter{cards_by_id.find(id)}; + const auto cardIter = cards_by_id.find(id); if(cardIter == cards_by_id.end()) { throw std::runtime_error("No card with id " + to_string(id)); @@ -62,17 +61,17 @@ void Cards::organize() { cards_by_id.clear(); player_cards.clear(); - player_cards_by_name.clear(); + cards_by_name.clear(); player_commanders.clear(); player_assaults.clear(); player_structures.clear(); // Round 1: set cards_by_id - for(Card* card: cards) + for(Card* card: all_cards) { cards_by_id[card->m_id] = card; } - // Round 2: depend on cards_by_id / by_id(); update m_name, [TU] m_top_level_card etc.; set player_cards_by_name; - for(Card* card: cards) + // Round 2: depend on cards_by_id / by_id(); update m_name, [TU] m_top_level_card etc.; set cards_by_name; + for(Card* card: all_cards) { // Remove delimiters from card names size_t pos; @@ -82,17 +81,56 @@ void Cards::organize() } // set m_top_level_card for non base cards card->m_top_level_card = by_id(card->m_base_id)->m_top_level_card; - // add a suffix of level to the name of cards; register as alias for the full-level cards (the formal name is without suffix) + // Cards available ("visible") to players have priority + std::string base_name = card->m_name; if (card == card->m_top_level_card) { - player_cards_by_name[simplify_name(card->m_name + "-" + to_string(card->m_level))] = card; + add_card(card, card->m_name + "-" + to_string(card->m_level)); } else { card->m_name += "-" + to_string(card->m_level); } - // Card available to players - if(card->m_set != 0) + add_card(card, card->m_name); + } +#if 0 // TODO refactor precedence + // Round 3: depend on cards_by_name; set abbreviations + for(Card* card: cards) + { + // generate abbreviations + if(card->m_set > 0) + { + for(auto&& abbr_name : get_abbreviations(card->m_name)) + { + if(abbr_name.length() > 1 && cards_by_name.find(abbr_name) == cards_by_name.end()) + { + player_cards_abbr[abbr_name] = card->m_name; + } + } + } + } +#endif +} + +void Cards::add_card(Card * card, const std::string & name) +{ + std::string simple_name{simplify_name(name)}; + auto card_itr = cards_by_name.find(simple_name); + signed old_visible = card_itr == cards_by_name.end() ? -1 : visible_cardset.count(card_itr->second->m_set); + signed new_visible = visible_cardset.count(card->m_set); + if (card_itr != cards_by_name.end()) + { + if (old_visible == new_visible) + { + ambiguous_names.insert(simple_name); + } + _DEBUG_MSG(2, "Duplicated card name \"%s\" [%u] set=%u (visible=%u) : [%u] set=%u (visible=%u)\n", name.c_str(), card_itr->second->m_id, card_itr->second->m_set, old_visible, card->m_id, card->m_set, new_visible); + } + else if (old_visible < new_visible) + { + ambiguous_names.erase(simple_name); + cards_by_name[simple_name] = card; + if (new_visible) { player_cards.push_back(card); switch(card->m_type) @@ -114,36 +152,8 @@ void Cards::organize() break; } } - std::string simple_name{simplify_name(card->m_name)}; - auto card_itr = player_cards_by_name.find(simple_name); - if (card_itr == player_cards_by_name.end()) - { - player_cards_by_name[simple_name] = card; - } - else - { - // TODO check set visible -// std::cerr << "Duplicated card name [" << card->m_name << "] " << card_itr->second->m_set << ":" << card->m_set << "\n"; // XXX - } - } - } -#if 0 // TODO refactor precedence - // Round 3: depend on player_cards_by_name; set abbreviations - for(Card* card: cards) - { - // generate abbreviations - if(card->m_set > 0) - { - for(auto&& abbr_name : get_abbreviations(card->m_name)) - { - if(abbr_name.length() > 1 && player_cards_by_name.find(abbr_name) == player_cards_by_name.end()) - { - player_cards_abbr[abbr_name] = card->m_name; - } - } } } -#endif } // class Card diff --git a/cards.h b/cards.h index 5b8e372f..c2218254 100644 --- a/cards.h +++ b/cards.h @@ -3,6 +3,7 @@ #include #include +#include #include class Card; @@ -12,16 +13,19 @@ class Cards public: ~Cards(); - std::vector cards; + std::vector all_cards; std::map cards_by_id; std::vector player_cards; - std::map player_cards_by_name; + std::map cards_by_name; std::vector player_commanders; std::vector player_assaults; std::vector player_structures; std::map player_cards_abbr; + std::unordered_set visible_cardset; + std::unordered_set ambiguous_names; const Card * by_id(unsigned id) const; void organize(); + void add_card(Card * card, const std::string & name); }; std::string simplify_name(const std::string& card_name); diff --git a/deck.cpp b/deck.cpp index c2bb5dc2..38770319 100644 --- a/deck.cpp +++ b/deck.cpp @@ -252,6 +252,7 @@ void Deck::set(const std::vector& ids, const std::map &m { 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(); card_marks = marks; } @@ -378,7 +379,7 @@ std::string Deck::long_description() const ios << medium_description() << "\n"; if (commander) { - show_upgrades(ios, commander, ""); + show_upgrades(ios, commander, commander_max_level, ""); } else { @@ -386,14 +387,14 @@ std::string Deck::long_description() const } for(const Card* card: cards) { - show_upgrades(ios, card, " "); + show_upgrades(ios, card, card->m_top_level_card->m_level, " "); } for(auto& pool: raid_cards) { ios << pool.first << " of:\n"; for(auto& card: pool.second) { - show_upgrades(ios, card, " "); + show_upgrades(ios, card, card->m_top_level_card->m_level, " "); } } for (const Card * fort: fort_cards) @@ -403,26 +404,28 @@ std::string Deck::long_description() const return ios.str(); } -void Deck::show_upgrades(std::stringstream &ios, const Card* card, const char * leading_chars) const +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 == card->m_top_level_card) + if (upgrade_points == 0 || card->m_level == card_max_level) { return; } if (debug_print < 2 && decktype != DeckType::raid) { - ios << leading_chars << "-> " << card_description(all_cards, card->m_top_level_card) << "\n"; + 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->m_top_level_card->m_level - card->m_level; + 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 != card->m_top_level_card) + while (card->m_level != card_max_level) { card = card->upgraded(); ++m; @@ -482,9 +485,9 @@ const Card* Deck::next() throw std::runtime_error("Unknown strategy for deck."); } -const Card* Deck::upgrade_card(const Card* card, std::mt19937& re, unsigned &remaining_upgrade_points, unsigned &remaining_upgrade_opportunities) +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->m_top_level_card->m_level - card->m_level; + unsigned oppos = card_max_level - card->m_level; if (remaining_upgrade_points > 0) { for (; oppos > 0; -- oppos) @@ -523,10 +526,10 @@ void Deck::shuffle(std::mt19937& re) { unsigned remaining_upgrade_points = upgrade_points; unsigned remaining_upgrade_opportunities = upgrade_opportunities; - shuffled_commander = upgrade_card(commander, re, remaining_upgrade_points, remaining_upgrade_opportunities); + shuffled_commander = upgrade_card(commander, commander_max_level, re, remaining_upgrade_points, remaining_upgrade_opportunities); for (auto && card: shuffled_cards) { - card = upgrade_card(card, re, remaining_upgrade_points, remaining_upgrade_opportunities); + card = upgrade_card(card, card->m_top_level_card->m_level, re, remaining_upgrade_points, remaining_upgrade_opportunities); } } if(strategy == DeckStrategy::ordered) diff --git a/deck.h b/deck.h index f5cd7814..91aee3fe 100644 --- a/deck.h +++ b/deck.h @@ -51,6 +51,7 @@ class Deck DeckStrategy::DeckStrategy strategy; const Card* commander; + unsigned commander_max_level; std::vector cards; std::map card_marks; // : -1 indicating the commander. E.g, used as a mark to be kept in attacking deck when optimizing. @@ -94,11 +95,13 @@ class Deck void set( const Card* commander_, + unsigned commander_max_level_, const std::vector& cards_, std::vector>> raid_cards_ = {}, unsigned mission_req_ = 0) { commander = commander_; + commander_max_level = commander_max_level_; cards = std::vector(std::begin(cards_), std::end(cards_)); raid_cards = std::vector>>(raid_cards_); deck_size = cards.size(); @@ -127,9 +130,9 @@ class Deck std::string short_description() const; std::string medium_description() const; std::string long_description() const; - void show_upgrades(std::stringstream &ios, const Card* card, const char * leading_chars) const; + void show_upgrades(std::stringstream &ios, const Card* card, unsigned card_max_level, const char * leading_chars) const; const Card* next(); - const Card* upgrade_card(const Card* card, std::mt19937& re, unsigned &remaining_upgrade_points, unsigned &remaining_upgrade_opportunities); + const Card* upgrade_card(const Card* card, unsigned card_max_level, std::mt19937& re, unsigned &remaining_upgrade_points, unsigned &remaining_upgrade_opportunities); void shuffle(std::mt19937& re); void place_at_bottom(const Card* card); }; diff --git a/read.cpp b/read.cpp index 1830df16..b3daa7b7 100644 --- a/read.cpp +++ b/read.cpp @@ -188,11 +188,15 @@ void parse_card_spec(const Cards& all_cards, std::string& card_spec, unsigned& c // } simple_name = simplify_name(abbr_it->second); } - auto card_it = all_cards.player_cards_by_name.find(simple_name); + auto card_it = all_cards.cards_by_name.find(simple_name); auto card_id_iter = advance_until(simple_name.begin(), simple_name.end(), [](char c){return(c=='[');}); - if(card_it != all_cards.player_cards_by_name.end()) + if (card_it != all_cards.cards_by_name.end()) { card_id = card_it->second->m_id; + if (all_cards.ambiguous_names.count(simple_name)) + { + std::cerr << "Warning: There are multiple cards named " << card_name << " in cards.xml. [" << card_id << "] is used.\n"; + } } else if(card_id_iter != simple_name.end()) { @@ -319,7 +323,7 @@ unsigned read_card_abbrs(Cards& all_cards, const std::string& filename) continue; } abbr_string_iter = advance_until(abbr_string_iter + 1, abbr_string.end(), [](const char& c){return(c != ' ');}); - if(all_cards.player_cards_by_name.find(abbr_name) != all_cards.player_cards_by_name.end()) + if(all_cards.cards_by_name.find(abbr_name) != all_cards.cards_by_name.end()) { std::cerr << "Warning in card abbreviation file " << filename << " at line " << num_line << ": ignored because the name has been used by an existing card." << std::endl; } diff --git a/tyrant.h b/tyrant.h index 7080605b..94b5d207 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.6" +#define TYRANT_OPTIMIZER_VERSION "2.10.7" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 946357b9..bb856278 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -817,7 +817,7 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); - non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); + non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; unsigned deck_cost = get_deck_cost(d1); @@ -976,7 +976,7 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, // Non-commander cards auto non_commander_cards = proc.cards.player_assaults; non_commander_cards.insert(non_commander_cards.end(), proc.cards.player_structures.begin(), proc.cards.player_structures.end()); - non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); + non_commander_cards.insert(non_commander_cards.end(), std::initializer_list{NULL,}); const Card* best_commander = d1->commander; std::vector best_cards = d1->cards; unsigned deck_cost = get_deck_cost(d1); @@ -1722,8 +1722,8 @@ int main(int argc, char** argv) } else if (type_str == "cs") { - auto card_it = all_cards.player_cards_by_name.find(simplify_name(key_str)); - if (card_it != all_cards.player_cards_by_name.end()) + auto card_it = all_cards.cards_by_name.find(simplify_name(key_str)); + if (card_it != all_cards.cards_by_name.end()) { quest.quest_type = QuestType::card_survival; quest.quest_key = card_it->second->m_id; diff --git a/xml.cpp b/xml.cpp index f931f614..3d7d7bf5 100644 --- a/xml.cpp +++ b/xml.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -210,7 +209,7 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) bool all(skill_node->first_attribute("all")); card->add_skill(skill_id, x, y, n, c, s, s2, all); } - all_cards.cards.push_back(card); + all_cards.all_cards.push_back(card); Card * top_card = card; for(xml_node<>* upgrade_node = card_node->first_node("upgrade"); upgrade_node; @@ -247,7 +246,18 @@ void load_cards_xml(Cards & all_cards, const std::string & filename, bool do_war { return; } - for(xml_node<>* card_node = root->first_node("unit"); + for (xml_node<>* set_node = root->first_node("cardSet"); + set_node; + set_node = set_node->next_sibling("cardSet")) + { + xml_node<>* id_node(set_node->first_node("id")); + xml_node<>* visible_node = set_node->first_node("visible"); + if (id_node && visible_node && atoi(visible_node->value())) + { + all_cards.visible_cardset.insert(atoi(id_node->value())); + } + } + for (xml_node<>* card_node = root->first_node("unit"); card_node; card_node = card_node->next_sibling("unit")) { @@ -261,7 +271,9 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType xml_node<>* commander_node(node->first_node("commander")); const Card* card = all_cards.by_id(atoi(commander_node->value())); const Card* commander_card{card}; - unsigned upgrade_opportunities = card->m_top_level_card->m_level - card->m_level; + xml_node<>* commander_max_level_node(node->first_node("commander_max_level")); + unsigned commander_max_level = commander_max_level_node ? atoi(commander_max_level_node->value()) : commander_card->m_top_level_card->m_level; + unsigned upgrade_opportunities = commander_max_level - card->m_level; std::vector always_cards; std::vector>> some_cards; xml_node<>* deck_node(node->first_node("deck")); @@ -302,19 +314,20 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType std::string deck_name = base_deck_name + "-" + to_string(level); decks.decks.push_back(Deck{all_cards, decktype, id, deck_name, (upgrade_opportunities + 1) * (level - 1) / (max_level - 1), upgrade_opportunities}); Deck* deck = &decks.decks.back(); - deck->set(commander_card, always_cards, some_cards, mission_req); + deck->set(commander_card, commander_max_level, always_cards, some_cards, mission_req); decks.by_name[deck_name] = deck; decks.by_name[decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(level)] = deck; } decks.decks.push_back(Deck{all_cards, decktype, id, base_deck_name}); Deck* deck = &decks.decks.back(); - deck->set(commander_card, always_cards, some_cards, mission_req); + deck->set(commander_card, commander_max_level, always_cards, some_cards, mission_req); // upgrade cards for full-level missions/raids if (max_level > 1) { - deck->commander = deck->commander->m_top_level_card; + while (deck->commander->m_level < commander_max_level) + { deck->commander = deck->commander->upgraded(); } for (auto && card: deck->cards) { card = card->m_top_level_card; } for (auto && pool: deck->raid_cards) From 3cd92c697848bfe2d312692422f88049bd7e4e4b Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 29 Aug 2015 20:57:48 -0400 Subject: [PATCH 354/406] Support Campaign Gateway. --- data/raids.xml | 847 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 654 insertions(+), 193 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index 15651501..0260f065 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -1370,27 +1370,27 @@ - - 411 - Deid101 - Deidon Normal 1 - 1022 - 1 - - - 4472 - 4472 - 6648 - 6648 - 12822 - 16054 - 16746 - 16768 - 16774 - 16780 - - - + + 411 + Deid101 + Deidon Normal 1 + 1022 + 1 + + + 4472 + 4472 + 6648 + 6648 + 12822 + 16054 + 16746 + 16768 + 16774 + 16780 + + + 412 @@ -1524,49 +1524,49 @@ - - 421 - Deid201 - Deidon Heroic 1 - 1049 - 1 - - - 6929 - 7914 - 7920 - 12825 - 14426 - 16051 - 16749 - 16770 - 16776 - 16782 - - - - - - 422 - Deid202 - Deidon Heroic 2 - 1049 - 1 - - - 6932 - 6932 - 7328 - 12813 - 13418 - 14425 - 16755 - 16770 - 16776 - 16782 - - - + + 421 + Deid201 + Deidon Heroic 1 + 1049 + 1 + + + 6929 + 7914 + 7920 + 12825 + 14426 + 16051 + 16749 + 16770 + 16776 + 16782 + + + + + + 422 + Deid202 + Deidon Heroic 2 + 1049 + 1 + + + 6932 + 6932 + 7328 + 12813 + 13418 + 14425 + 16755 + 16770 + 16776 + 16782 + + + 423 @@ -1590,93 +1590,93 @@ - - 424 - Deid204 - Deidon Heroic 4 - 1051 - 1 - - - 2572 - 11816 - 14023 - 14136 - 15703 - 16045 - 16045 - 16770 - 16776 - 16782 - - - - - - 425 - Deid205 - Deidon Heroic 5 - 1051 - 1 - - - 14635 - 15320 - 15329 - 15709 - 16194 - 16449 - 16770 - 16776 - 16782 - 16921 - - - - - - 426 - Deid206 - Deidon Heroic 6 - 1750 - 1 - - - 247 - 10213 - 12445 - 15709 - 16551 - 16770 - 16779 - 16779 - 16782 - 16959 - - - - - - 427 - Deid207 - Deidon Heroic 7 - 1756 - 1 - - - 2578 - 10782 - 13823 - 14032 - 14136 - 15084 - 16770 - 16776 - 16782 - 16782 - - - + + 424 + Deid204 + Deidon Heroic 4 + 1051 + 1 + + + 2572 + 11816 + 14023 + 14136 + 15703 + 16045 + 16045 + 16770 + 16776 + 16782 + + + + + + 425 + Deid205 + Deidon Heroic 5 + 1051 + 1 + + + 14635 + 15320 + 15329 + 15709 + 16194 + 16449 + 16770 + 16776 + 16782 + 16921 + + + + + + 426 + Deid206 + Deidon Heroic 6 + 1750 + 1 + + + 247 + 10213 + 12445 + 15709 + 16551 + 16770 + 16779 + 16779 + 16782 + 16959 + + + + + + 427 + Deid207 + Deidon Heroic 7 + 1756 + 1 + + + 2578 + 10782 + 13823 + 14032 + 14136 + 15084 + 16770 + 16776 + 16782 + 16782 + + + 431 @@ -1700,27 +1700,27 @@ - - 432 - Deid302 - Deidon Mythic 2 - 1051 - 1 - - - 7334 - 7922 - 11546 - 11546 - 12819 - 12957 - 12957 - 16773 - 16779 - 16785 - - - + + 432 + Deid302 + Deidon Mythic 2 + 1051 + 1 + + + 7334 + 7922 + 11546 + 11546 + 12819 + 12957 + 12957 + 16773 + 16779 + 16785 + + + 433 @@ -1788,27 +1788,27 @@ - - 436 - Deid306 - Deidon Mythic 6 - 1753 - 1 - - - 10788 - 13679 - 14269 - 15547 - 15709 - 16773 - 16779 - 16779 - 16785 - 16929 - - - + + 436 + Deid306 + Deidon Mythic 6 + 1753 + 1 + + + 10788 + 13679 + 14269 + 15547 + 15709 + 16773 + 16779 + 16779 + 16785 + 16929 + + + 437 @@ -2884,6 +2884,467 @@ + + 711 + Gate101 + Gateway Normal 1 + 1259 + 1 + + + 4439 + 4439 + 4456 + 4456 + 6801 + 16692 + 16878 + 32299 + 32305 + 32311 + + + + + + 712 + Gate102 + Gateway Normal 2 + 1007 + 1 + + + 4444 + 4444 + 4460 + 4460 + 8832 + 13580 + 32299 + 32305 + 32311 + 32823 + + + + + + 713 + Gate103 + Gateway Normal 3 + 1865 + 1 + + + 8835 + 13580 + 16620 + 16881 + 31343 + 32301 + 32301 + 32305 + 32311 + 32623 + + + + + + 714 + Gate104 + Gateway Normal 4 + 1011 + 1 + + + 4439 + 4439 + 4456 + 30414 + 31601 + 31926 + 32299 + 32305 + 32311 + 32460 + + + + + + 715 + Gate105 + Gateway Normal 5 + 1015 + 1 + + + 4444 + 4460 + 31277 + 31601 + 31926 + 32299 + 32305 + 32311 + 32460 + 32586 + + + + + + 716 + Gate106 + Gateway Normal 6 + 1871 + 1 + + + 31280 + 31310 + 31403 + 31745 + 32160 + 32299 + 32307 + 32307 + 32311 + 32586 + + + + + + 717 + Gate107 + Gateway Normal 7 + 1877 + 1 + + + 15869 + 30561 + 31310 + 31640 + 32163 + 32299 + 32305 + 32311 + 32311 + 32568 + + + + + + 721 + Gate201 + Gateway Heroic 1 + 1501 + 1 + + + 6813 + 6813 + 7799 + 14804 + 16704 + 16890 + 16890 + 32301 + 32307 + 32313 + + + + + + 722 + Gate202 + Gateway Heroic 2 + 1189 + 1 + + + 8844 + 13586 + 14809 + 31352 + 32301 + 32313 + 32631 + 32832 + + + 7799 + 32307 + + + 7802 + 32310 + + + + + + 723 + Gate203 + Gateway Heroic 3 + 1867 + 1 + + + 13583 + 13583 + 16626 + 16893 + 31704 + 32304 + 32304 + 32307 + 32313 + 32628 + + + + + + 724 + Gate204 + Gateway Heroic 4 + 1196 + 1 + + + 14154 + 30420 + 31364 + 31604 + 31604 + 31932 + 32301 + 32307 + 32313 + 32466 + + + + + + 725 + Gate205 + Gateway Heroic 5 + 1484 + 1 + + + 14157 + 30423 + 30531 + 31226 + 31286 + 31364 + 32301 + 32307 + 32313 + 32589 + + + + + + 726 + Gate206 + Gateway Heroic 6 + 1873 + 1 + + + 31313 + 31409 + 31436 + 31745 + 32166 + 32301 + 32310 + 32310 + 32313 + 32589 + + + + + + 727 + Gate207 + Gateway Heroic 7 + 1879 + 1 + + + 15874 + 30564 + 31319 + 31646 + 32163 + 32301 + 32307 + 32313 + 32313 + 32571 + + + + + + 731 + Gate301 + 1503 + 1 + + + 6816 + 12215 + 16893 + 16893 + 31896 + 32304 + 32310 + 32316 + 32640 + 32640 + + + + + + 732 + Gate302 + 1191 + 1 + + + 8847 + 13589 + 31358 + 31358 + 31712 + 31896 + 32304 + 32310 + 32316 + 32838 + + + + + + 733 + Gate303 + 1870 + 1 + + + 13589 + 13589 + 16893 + 31718 + 32304 + 32304 + 32310 + 32316 + 32634 + 32634 + + + + + + 734 + Gate304 + 1198 + 1 + + + 14166 + 31376 + 31610 + 31610 + 31938 + 31938 + 32304 + 32310 + 32316 + 32472 + + + + + + 735 + Gate305 + 1486 + 1 + + + 14166 + 14166 + 30333 + 30429 + 31232 + 31286 + 32304 + 32310 + 32316 + 32598 + + + + + + 736 + Gate306 + 1876 + 1 + + + 30333 + 31412 + 31448 + 31754 + 32304 + 32310 + 32310 + 32316 + 32598 + 32801 + + + + + + 737 + Gate307 + 1882 + 1 + + + 15775 + 30573 + 30793 + 31992 + 32304 + 32310 + 32316 + 32316 + 32580 + 32801 + + + + From cf9b71fa4e839742a149729b9a79b17b5ff62ace Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 29 Aug 2015 21:05:49 -0400 Subject: [PATCH 355/406] Add alias for Gateway decks. --- data/raids.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 0260f065..d75b98a4 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -3201,6 +3201,7 @@ 731 Gate301 + Gateway Mythic 1 1503 1 @@ -3222,6 +3223,7 @@ 732 Gate302 + Gateway Mythic 2 1191 1 @@ -3243,6 +3245,7 @@ 733 Gate303 + Gateway Mythic 3 1870 1 @@ -3264,6 +3267,7 @@ 734 Gate304 + Gateway Mythic 4 1198 1 @@ -3285,6 +3289,7 @@ 735 Gate305 + Gateway Mythic 5 1486 1 @@ -3306,6 +3311,7 @@ 736 Gate306 + Gateway Mythic 6 1876 1 @@ -3327,6 +3333,7 @@ 737 Gate307 + Gateway Mythic 7 1882 1 From f6af61a361fa48a6e4258b15e2c2c76a8f77e612 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 15 Sep 2015 16:29:35 -0400 Subject: [PATCH 356/406] Support BGE Divert. --- sim.cpp | 30 +++++++++++++++++++++++++++--- tyrant.cpp | 2 +- tyrant.h | 4 ++-- tyrant_optimize.cpp | 22 ++++++++++++++-------- 4 files changed, 44 insertions(+), 14 deletions(-) diff --git a/sim.cpp b/sim.cpp index 30121696..2847d5fd 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1614,17 +1614,41 @@ template void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { select_targets(fd, src_status, s); + unsigned num_inhibited = 0; bool has_counted_quest = false; for (CardStatus * dst: fd->selection_array) { - if(dst->m_inhibited > 0 && !src_status->m_overloaded) + if (dst->m_inhibited > 0 && !src_status->m_overloaded) { _DEBUG_MSG(1, "%s %s on %s but it is inhibited\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst).c_str()); -- dst->m_inhibited; + ++ num_inhibited; continue; } check_and_perform_skill(fd, src_status, dst, s, false, has_counted_quest); } + if (num_inhibited > 0 && fd->bg_effects.count(divert)) + { + SkillSpec diverted_ss = s; + diverted_ss.y = allfactions; + diverted_ss.n = 1; + diverted_ss.all = false; + for (auto i = 0; i < num_inhibited; ++ i) + { + select_targets(fd, &fd->tip->commander, diverted_ss); + for (CardStatus * dst: fd->selection_array) + { + if (dst->m_inhibited > 0) + { + _DEBUG_MSG(1, "%s %s (Diverted) on %s but it is inhibited\n", status_description(src_status).c_str(), skill_short_description(diverted_ss).c_str(), status_description(dst).c_str()); + -- dst->m_inhibited; + continue; + } + _DEBUG_MSG(1, "%s %s (Diverted) on %s\n", status_description(src_status).c_str(), skill_short_description(diverted_ss).c_str(), status_description(dst).c_str()); + perform_skill(fd, src_status, dst, diverted_ss); + } + } + } } template @@ -1653,7 +1677,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski if (turningtides_value > 0) { SkillSpec ss_rally{rally, turningtides_value, allfactions, 0, 0, no_skill, no_skill, s.all,}; - _DEBUG_MSG(1, "Turning Tides %u!\n", turningtides_value); + _DEBUG_MSG(1, "TurningTides %u!\n", turningtides_value); perform_targetted_allied_fast(fd, src_status, ss_rally); } for (CardStatus * pb_status: paybackers) @@ -1666,7 +1690,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski if (turningtides_value > 0) { SkillSpec ss_rally{rally, turningtides_value, allfactions, 0, 0, no_skill, no_skill, false,}; - _DEBUG_MSG(1, "Paybacked Turning Tides %u!\n", turningtides_value); + _DEBUG_MSG(1, "Paybacked TurningTides %u!\n", turningtides_value); perform_targetted_allied_fast(fd, pb_status, ss_rally); } } diff --git a/tyrant.cpp b/tyrant.cpp index abf6429f..553f1920 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -30,7 +30,7 @@ std::string skill_names[Skill::num_skills] = "Legion", // Pseudo-skill for passive BGEs: "", - "Bloodlust", "Reaping", "Metamorphosis", "Counterflux", "Fortification", "TurningTides", "EnduringRage", + "Bloodlust", "Counterflux", "Divert", "EnduringRage", "Fortification", "Metamorphosis", "Reaping", "TurningTides", "", }; diff --git a/tyrant.h b/tyrant.h index 94b5d207..b3518713 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.7" +#define TYRANT_OPTIMIZER_VERSION "2.10.8" #include #include @@ -46,7 +46,7 @@ enum Skill legion, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, - bloodlust, reaping, metamorphosis, counterflux, fortification, turningtides, enduringrage, + bloodlust, counterflux, divert, enduringrage, fortification, metamorphosis, reaping, turningtides, END_BGE_SKILL, num_skills }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index bb856278..c81fc8ed 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1146,12 +1146,13 @@ void print_available_effects() { std::cout << "Available effects besides activation skills:\n" " Bloodlust X\n" - " Reaping X\n" - " Metamorphosis\n" " Counterflux\n" + " Divert\n" + " EnduringRage\n" " Fortification\n" + " Metamorphosis\n" + " Reaping X\n" " TurningTides\n" - " EnduringRage\n" ; } void usage(int argc, char** argv) @@ -1722,14 +1723,19 @@ int main(int argc, char** argv) } else if (type_str == "cs") { - auto card_it = all_cards.cards_by_name.find(simplify_name(key_str)); - if (card_it != all_cards.cards_by_name.end()) + unsigned card_id; + unsigned card_num; + char num_sign; + char mark; + try { + parse_card_spec(all_cards, key_str, card_id, card_num, num_sign, mark); quest.quest_type = QuestType::card_survival; - quest.quest_key = card_it->second->m_id; + quest.quest_key = card_id; } - else { - std::cerr << "Error: Expect card in quest \"" << opt_quest << "\".\n"; + catch (const std::runtime_error& e) + { + std::cerr << "Error: Expect a card in quest \"" << opt_quest << "\".\n"; return 0; } } From 9aa28539a6135afbee92bb89d9d6dc73be816b0c Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 16 Sep 2015 23:16:02 -0400 Subject: [PATCH 357/406] Support optional "to" in -e "Evolve A to B" --- tyrant_optimize.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index c81fc8ed..c84f1742 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1562,12 +1562,16 @@ int main(int argc, char** argv) bg_skill.n = boost::lexical_cast(tokens[skill_index]); skill_index += 1; } - if (skill_index < tokens.size()) + if (skill_index < tokens.size() && bg_skill.id == evolve) { bg_skill.s = skill_name_to_id(tokens[skill_index]); if (bg_skill.s != no_skill) { skill_index += 1; + if (skill_index < tokens.size() && boost::to_lower_copy(tokens[skill_index]) == "to") + { + skill_index += 1; + } if (skill_index < tokens.size()) { bg_skill.s2 = skill_name_to_id(tokens[skill_index]); From ed1af417bff63559566802bf8c14c29cae1c54bb Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 23 Sep 2015 18:36:05 -0400 Subject: [PATCH 358/406] Add skill Rupture. --- sim.cpp | 15 +++++++++++---- tyrant.cpp | 2 +- tyrant.h | 4 ++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/sim.cpp b/sim.cpp index 2847d5fd..c0355bb1 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1047,8 +1047,14 @@ struct PerformAttack } } } + unsigned rupture_value = att_status->skill(rupture); + if (rupture_value > 0) + { + if (debug_print > 0) { desc += "+" + to_string(rupture_value) + "(rupture)"; } + att_dmg += rupture_value; + } unsigned venom_value = att_status->skill(venom); - if (venom_value && def_status->m_poisoned > 0) + if (venom_value > 0 && def_status->m_poisoned > 0) { if (debug_print > 0) { desc += "+" + to_string(venom_value) + "(venom)"; } att_dmg += venom_value; @@ -1084,10 +1090,11 @@ struct PerformAttack if(debug_print > 0) { reduced_desc += (reduced_desc.empty() ? "" : "+") + to_string(def_status->protected_value()) + "(protected)"; } reduced_dmg += def_status->protected_value(); } - if(reduced_dmg > 0 && att_status->skill(pierce) > 0) + unsigned pierce_value = att_status->skill(pierce) + att_status->skill(rupture); + if (reduced_dmg > 0 && pierce_value > 0) { - if(debug_print > 0) { reduced_desc += "-" + to_string(att_status->skill(pierce)) + "(pierce)"; } - reduced_dmg = safe_minus(reduced_dmg, att_status->skill(pierce)); + if (debug_print > 0) { reduced_desc += "-" + to_string(pierce_value) + "(pierce)"; } + reduced_dmg = safe_minus(reduced_dmg, pierce_value); } att_dmg = safe_minus(att_dmg, reduced_dmg); if(debug_print > 0) diff --git a/tyrant.cpp b/tyrant.cpp index 553f1920..56e537a2 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -23,7 +23,7 @@ std::string skill_names[Skill::num_skills] = "Armor", "Avenge", "Corrosive", "Counter", "Evade", "Payback", "Refresh", "Wall", "", // Combat-Modifier: - "Flurry", "Pierce", "Swipe", "Valor", + "Flurry", "Pierce", "Rupture", "Swipe", "Valor", // Damage-Dependant: "Berserk", "Inhibit", "Leech", "Poison", "Venom", // Triggered: diff --git a/tyrant.h b/tyrant.h index b3518713..bd59c580 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.8" +#define TYRANT_OPTIMIZER_VERSION "2.10.9" #include #include @@ -39,7 +39,7 @@ enum Skill armor, avenge, corrosive, counter, evade, payback, refresh, wall, END_DEFENSIVE, // Combat-Modifier: - flurry, pierce, swipe, valor, + flurry, pierce, rupture, swipe, valor, // Damage-Dependant: berserk, inhibit, leech, poison, venom, // Triggered: From 73a634c4d8309793e607c8b59a4b47b12f8f8fca Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 24 Sep 2015 17:15:46 -0400 Subject: [PATCH 359/406] Add quest suoc - skill use on card. --- read.cpp | 2 +- read.h | 2 +- sim.cpp | 6 +++--- sim.h | 5 +++-- tyrant_optimize.cpp | 30 +++++++++++++++++++++++++++++- 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/read.cpp b/read.cpp index b3daa7b7..09a48638 100644 --- a/read.cpp +++ b/read.cpp @@ -161,7 +161,7 @@ DeckList parse_deck_list(std::string list_string, const Decks& decks) return res; } -void parse_card_spec(const Cards& all_cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark) +void parse_card_spec(const Cards& all_cards, const std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark) { // static std::set recognized_abbr; auto card_spec_iter = card_spec.begin(); diff --git a/read.h b/read.h index 49045a1a..2952d5d9 100644 --- a/read.h +++ b/read.h @@ -12,7 +12,7 @@ class Decks; class Deck; DeckList parse_deck_list(std::string list_string, const Decks& decks); -void parse_card_spec(const Cards& cards, std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark); +void parse_card_spec(const Cards& cards, const std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark); const std::pair, std::map> string_to_ids(const Cards& all_cards, const std::string& deck_string, const std::string & description); unsigned load_custom_decks(Decks& decks, Cards& cards, const std::string & filename); void read_owned_cards(Cards& cards, std::map& owned_cards, const std::string & filename); diff --git a/sim.cpp b/sim.cpp index c0355bb1..4a0c0738 100644 --- a/sim.cpp +++ b/sim.cpp @@ -871,7 +871,7 @@ void turn_end_phase(Field* fd) { if (status.m_player == 1) { - fd->inc_counter(QuestType::skill_damage, poison, poison_dmg); + fd->inc_counter(QuestType::skill_damage, poison, 0, poison_dmg); } _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg); remove_hp(fd, &status, poison_dmg); @@ -976,7 +976,7 @@ struct PerformAttack if (def_status->m_player == 0) { fd->inc_counter(QuestType::skill_use, counter); - fd->inc_counter(QuestType::skill_damage, counter, counter_dmg); + fd->inc_counter(QuestType::skill_damage, counter, 0, counter_dmg); } _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, att_status, counter_dmg); @@ -1507,7 +1507,7 @@ bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_ { if (src_status->m_player == 0 && ! has_counted_quest) { - fd->inc_counter(QuestType::skill_use, skill_id); + fd->inc_counter(QuestType::skill_use, skill_id, dst_status->m_card->m_id); has_counted_quest = true; } if (is_evadable && diff --git a/sim.h b/sim.h index faea8d24..b51cc7bc 100644 --- a/sim.h +++ b/sim.h @@ -200,6 +200,7 @@ struct Quest { QuestType::QuestType quest_type; unsigned quest_key; + unsigned quest_2nd_key; unsigned quest_value; unsigned quest_score; // score for quest goal unsigned win_score; // score for win regardless quest goal @@ -302,9 +303,9 @@ class Field inline const std::vector adjacent_assaults(const CardStatus * status); inline void print_selection_array(); - inline void inc_counter(QuestType::QuestType quest_type, unsigned quest_key, unsigned value = 1) + inline void inc_counter(QuestType::QuestType quest_type, unsigned quest_key, unsigned quest_2nd_key = 0, unsigned value = 1) { - if (quest.quest_type == quest_type && quest.quest_key == quest_key) + if (quest.quest_type == quest_type && quest.quest_key == quest_key && (quest.quest_2nd_key == 0 || quest.quest_2nd_key == quest_2nd_key)) { quest_counter += value; } diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index c84f1742..9571fd8b 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -679,6 +679,7 @@ void print_score_info(const EvaluatedResults& results, std::vector& case OptimizationMode::campaign: case OptimizationMode::brawl: case OptimizationMode::war: + case OptimizationMode::quest: std::cout << val.points << " "; break; default: @@ -1684,6 +1685,7 @@ int main(int argc, char** argv) auto type_str = boost::to_lower_copy(tokens[0]); quest.quest_value = boost::lexical_cast(tokens[1]); auto key_str = boost::to_lower_copy(tokens[2]); + unsigned quest_index = 3; if (type_str == "su" || type_str == "sd") { Skill skill_id = skill_name_to_id(key_str); @@ -1743,12 +1745,38 @@ int main(int argc, char** argv) return 0; } } + else if (type_str == "suoc" && tokens.size() >= 4) + { + Skill skill_id = skill_name_to_id(key_str); + if (skill_id == no_skill) + { + std::cerr << "Error: Expect skill in quest \"" << opt_quest << "\".\n"; + return 0; + } + unsigned card_id; + unsigned card_num; + char num_sign; + char mark; + try + { + parse_card_spec(all_cards, boost::to_lower_copy(tokens[3]), card_id, card_num, num_sign, mark); + quest_index += 1; + quest.quest_type = QuestType::skill_use; + quest.quest_key = skill_id; + quest.quest_2nd_key = card_id; + } + catch (const std::runtime_error& e) + { + std::cerr << "Error: Expect a card in quest \"" << opt_quest << "\".\n"; + return 0; + } + } else { throw std::runtime_error("Expect one of: su n skill; sd n skill; cu n faction/strcture; ck n structure"); } quest.quest_score = quest.quest_value; - for (unsigned i = 3; i < tokens.size(); ++ i) + for (unsigned i = quest_index; i < tokens.size(); ++ i) { const auto & token = tokens[i]; if (token == "each") From e9ace4cc017029191478093fb6ce4dad2020c6e4 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 24 Sep 2015 17:25:25 -0400 Subject: [PATCH 360/406] Support Raid #14 "Prometheus Raid". --- data/raids.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index d75b98a4..bf007def 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -294,6 +294,27 @@ + + 14 + Prometheus Raid + 1912 + 26 + + + 33813 + 33813 + 33823 + 33823 + 33833 + 33833 + 33843 + 33843 + 33853 + 33853 + + + + From 5737d955bfd8bb88e17c9e5dfd9dac5544061e9c Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 24 Sep 2015 17:28:21 -0400 Subject: [PATCH 361/406] Fix compiler warning. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 4a0c0738..c17c564a 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1640,7 +1640,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil diverted_ss.y = allfactions; diverted_ss.n = 1; diverted_ss.all = false; - for (auto i = 0; i < num_inhibited; ++ i) + for (unsigned i = 0; i < num_inhibited; ++ i) { select_targets(fd, &fd->tip->commander, diverted_ss); for (CardStatus * dst: fd->selection_array) From a3813894048679878b3dddcb852a9713950ad388 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 25 Sep 2015 00:26:10 -0400 Subject: [PATCH 362/406] Fix bug: enhance BGE. --- tyrant.h | 2 +- tyrant_optimize.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tyrant.h b/tyrant.h index bd59c580..a2bae8e7 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.9" +#define TYRANT_OPTIMIZER_VERSION "2.10.10" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 9571fd8b..162b64bf 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1563,7 +1563,7 @@ int main(int argc, char** argv) bg_skill.n = boost::lexical_cast(tokens[skill_index]); skill_index += 1; } - if (skill_index < tokens.size() && bg_skill.id == evolve) + if (skill_index < tokens.size()) { bg_skill.s = skill_name_to_id(tokens[skill_index]); if (bg_skill.s != no_skill) From 7f0986b4c7633bc2173ef5fa2958e37856c4e1fb Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 27 Sep 2015 17:51:33 -0400 Subject: [PATCH 363/406] Simulate bizarre in-game behavior: Swipe even if attack is 0. --- sim.cpp | 2 ++ tyrant.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index c17c564a..f3c9a893 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1180,10 +1180,12 @@ bool attack_phase(Field* fd) { CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); +#if 0 // Simulate bizarre in-game behavior: Swipe even if attack is 0 if(attack_power(att_status) == 0) { return false; } +#endif unsigned att_dmg = 0; if (alive_assault(def_assaults, fd->current_ci)) diff --git a/tyrant.h b/tyrant.h index a2bae8e7..6cb99e7c 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.10" +#define TYRANT_OPTIMIZER_VERSION "2.10.11" #include #include From c28078ec674205335b6bc227f163545699c19210 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 29 Sep 2015 18:10:17 -0400 Subject: [PATCH 364/406] Revert bizzare in-game behaviors. --- sim.cpp | 3 --- tyrant.cpp | 6 +++--- tyrant.h | 8 ++++---- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/sim.cpp b/sim.cpp index f3c9a893..eeea3d1c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1180,12 +1180,10 @@ bool attack_phase(Field* fd) { CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); -#if 0 // Simulate bizarre in-game behavior: Swipe even if attack is 0 if(attack_power(att_status) == 0) { return false; } -#endif unsigned att_dmg = 0; if (alive_assault(def_assaults, fd->current_ci)) @@ -1251,7 +1249,6 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - return dst->has_skill(s.s) && !dst->has_skill(s.s2); // XXX in-game behavior return dst->has_skill(s.s) && !dst->has_skill(s.s2) && ((BEGIN_DEFENSIVE < s.s2 && s.s2 < END_DEFENSIVE) || is_active(dst)); } diff --git a/tyrant.cpp b/tyrant.cpp index 56e537a2..b5a8884a 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -23,11 +23,11 @@ std::string skill_names[Skill::num_skills] = "Armor", "Avenge", "Corrosive", "Counter", "Evade", "Payback", "Refresh", "Wall", "", // Combat-Modifier: - "Flurry", "Pierce", "Rupture", "Swipe", "Valor", + "Legion", "Pierce", "Rupture", "Swipe", "Venom", // Damage-Dependant: - "Berserk", "Inhibit", "Leech", "Poison", "Venom", + "Berserk", "Inhibit", "Leech", "Poison", // Triggered: - "Legion", + "Flurry", "Valor", // Pseudo-skill for passive BGEs: "", "Bloodlust", "Counterflux", "Divert", "EnduringRage", "Fortification", "Metamorphosis", "Reaping", "TurningTides", diff --git a/tyrant.h b/tyrant.h index 6cb99e7c..cbd65e81 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.11" +#define TYRANT_OPTIMIZER_VERSION "2.10.12" #include #include @@ -39,11 +39,11 @@ enum Skill armor, avenge, corrosive, counter, evade, payback, refresh, wall, END_DEFENSIVE, // Combat-Modifier: - flurry, pierce, rupture, swipe, valor, + legion, pierce, rupture, swipe, venom, // Damage-Dependant: - berserk, inhibit, leech, poison, venom, + berserk, inhibit, leech, poison, // Triggered: - legion, + flurry, valor, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, bloodlust, counterflux, divert, enduringrage, fortification, metamorphosis, reaping, turningtides, From 7479d4fe7777a29f4cd3f817b873c2bc6e568ca6 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 2 Oct 2015 08:43:44 -0400 Subject: [PATCH 365/406] Support Campaign Emrys Ascended (Emry). --- data/raids.xml | 479 +++++++++++++++++++++++++++++++++++++++++++- deck.cpp | 32 ++- deck.h | 10 +- tyrant_optimize.cpp | 2 +- xml.cpp | 11 +- 5 files changed, 511 insertions(+), 23 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index bf007def..6baea112 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -3373,6 +3373,483 @@ - + + 811 + Emry101 + Emrys Ascended Normal 1 + 1022 + 1 + + + 6648 + 6648 + 10243 + 14115 + 33305 + 33447 + 33453 + 33459 + + + 443 + 471 + 4472 + + + + + + 812 + Emry102 + Emrys Ascended Normal 2 + 1022 + 1 + + + 6652 + 6652 + 7109 + 15314 + 16956 + 33447 + 33453 + 33459 + + + 443 + 4476 + 4552 + 4781 + 12503 + 13419 + + + + + + 813 + Emry103 + Emrys Ascended Normal 3 + 1893 + 1 + + + 7112 + 14118 + 16914 + 16956 + 31397 + 32053 + 33449 + 33449 + 33453 + 33459 + + + + + + 814 + Emry104 + Emrys Ascended Normal 4 + 1022 + 1 + + + 6648 + 8489 + 14629 + 15700 + 30871 + 33447 + 33453 + 33459 + + + 443 + 471 + 4472 + 4552 + + + + + + 815 + Emry105 + Emrys Ascended Normal 5 + 1022 + 1 + + + 6652 + 14629 + 15700 + 15799 + 30871 + 31947 + 33447 + 33453 + 33459 + + + 443 + 4476 + 4552 + + + + + + 816 + Emry106 + Emrys Ascended Normal 6 + 1900 + 1 + + + 15796 + 15802 + 31947 + 32475 + 33164 + 33447 + 33455 + 33455 + 33459 + 33704 + + + + + + 817 + Emry107 + Emrys Ascended Normal 7 + 1906 + 1 + + + 12673 + 15697 + 33167 + 33308 + 33434 + 33447 + 33453 + 33459 + 33459 + 33704 + + + + + + 821 + Emry201 + Emrys Ascended Heroic 1 + 1049 + 1 + + + 10255 + 14127 + 14127 + 31054 + 32719 + 33317 + 33317 + 33449 + 33455 + 33461 + + + + + + 822 + Emry202 + Emrys Ascended Heroic 2 + 1049 + 1 + + + 7121 + 15323 + 16962 + 31059 + 32061 + 32722 + 33449 + 33455 + 33461 + 16923 + + + + + + 823 + Emry203 + Emrys Ascended Heroic 3 + 1895 + 1 + + + 5871 + 14130 + 16959 + 16959 + 31409 + 32061 + 33452 + 33452 + 33455 + 33461 + + + + + + 824 + Emry204 + Emrys Ascended Heroic 4 + 1050 + 1 + + + 8495 + 14635 + 15703 + 15703 + 16123 + 30877 + 33449 + 33455 + 33461 + 33596 + + + + + + 825 + Emry205 + Emrys Ascended Heroic 5 + 1050 + 1 + + + 8498 + 15802 + 16123 + 30681 + 31460 + 31956 + 33449 + 33455 + 33461 + 33599 + + + + + + 826 + Emry206 + Emrys Ascended Heroic 6 + 1902 + 1 + + + 7880 + 15802 + 15802 + 32487 + 33170 + 33449 + 33458 + 33458 + 33461 + 33707 + + + + + + 827 + Emry207 + Emrys Ascended Heroic 7 + 1908 + 1 + + + 12678 + 15703 + 33167 + 33311 + 33437 + 33449 + 33455 + 33461 + 33461 + 33713 + + + + + + 831 + Emry301 + Emrys Ascended Mythic 1 + 1051 + 1 + + + 14130 + 14130 + 16557 + 31065 + 31065 + 32728 + 33320 + 33452 + 33458 + 33464 + + + + + + 832 + Emry302 + Emrys Ascended Mythic 2 + 1051 + 1 + + + 5877 + 7124 + 15329 + 16557 + 16929 + 16929 + 16965 + 33452 + 33458 + 33464 + + + + + + 833 + Emry303 + Emrys Ascended Mythic 3 + 1898 + 1 + + + 5883 + 14130 + 16965 + 32064 + 32064 + 33452 + 33452 + 33458 + 33458 + 33464 + + + + + + 834 + Emry304 + Emrys Ascended Mythic 4 + 32550 + 1 + + + 14641 + 14641 + 15709 + 15709 + 16135 + 30883 + 33452 + 33458 + 33464 + 33608 + + + + + + 835 + Emry305 + Emrys Ascended Mythic 5 + 32550 + 1 + + + 8504 + 15086 + 15811 + 30687 + 31956 + 33452 + 33458 + 33464 + 33608 + 33608 + + + + + + 836 + Emry306 + Emrys Ascended Mythic 6 + 1905 + 1 + + + 7892 + 15086 + 15811 + 32490 + 32820 + 33452 + 33452 + 33458 + 33458 + 33464 + + + + + + 837 + Emry307 + Emrys Ascended Mythic 7 + 1911 + 1 + + + 6728 + 10698 + 12687 + 13871 + 32820 + 33446 + 33452 + 33458 + 33464 + 33464 + + + diff --git a/deck.cpp b/deck.cpp index 38770319..c3dd5222 100644 --- a/deck.cpp +++ b/deck.cpp @@ -330,7 +330,7 @@ std::string Deck::short_description() const if(!name.empty()) { ios << " \"" << name << "\""; } if(deck_string.empty()) { - if(raid_cards.empty()) { ios << ": " << hash(); } + if(variable_cards.empty()) { ios << ": " << hash(); } } else { @@ -356,9 +356,9 @@ std::string Deck::medium_description() const ios << ", " << card->m_name; } unsigned num_pool_cards = 0; - for(auto& pool: raid_cards) + for(auto& pool: variable_cards) { - num_pool_cards += pool.first; + num_pool_cards += std::get<0>(pool) * std::get<1>(pool); } if(num_pool_cards > 0) { @@ -389,10 +389,14 @@ std::string Deck::long_description() const { show_upgrades(ios, card, card->m_top_level_card->m_level, " "); } - for(auto& pool: raid_cards) + for(auto& pool: variable_cards) { - ios << pool.first << " of:\n"; - for(auto& card: pool.second) + 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, " "); } @@ -509,17 +513,23 @@ void Deck::shuffle(std::mt19937& re) shuffled_commander = commander; shuffled_cards.clear(); boost::insert(shuffled_cards, shuffled_cards.end(), cards); - if(!raid_cards.empty()) + if(!variable_cards.empty()) { if(strategy != DeckStrategy::random) { throw std::runtime_error("Support only random strategy for raid/quest deck."); } - for(auto& card_pool: raid_cards) + for(auto& card_pool: variable_cards) { - assert(card_pool.first <= card_pool.second.size()); - partial_shuffle(card_pool.second.begin(), card_pool.second.begin() + card_pool.first, card_pool.second.end(), re); - shuffled_cards.insert(shuffled_cards.end(), card_pool.second.begin(), card_pool.second.begin() + card_pool.first); + 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) diff --git a/deck.h b/deck.h index 91aee3fe..35f99548 100644 --- a/deck.h +++ b/deck.h @@ -60,7 +60,7 @@ class Deck // card id -> card order std::map> order; - std::vector>> raid_cards; + std::vector>> variable_cards; // amount, replicates, card pool unsigned deck_size; unsigned mission_req; @@ -97,17 +97,17 @@ class Deck const Card* commander_, unsigned commander_max_level_, const std::vector& cards_, - std::vector>> raid_cards_ = {}, + std::vector>> variable_cards_ = {}, unsigned mission_req_ = 0) { commander = commander_; commander_max_level = commander_max_level_; cards = std::vector(std::begin(cards_), std::end(cards_)); - raid_cards = std::vector>>(raid_cards_); + variable_cards = variable_cards_; deck_size = cards.size(); - for (const auto & pool: raid_cards) + for (const auto & pool: variable_cards) { - deck_size += pool.first; + deck_size += std::get<0>(pool) * std::get<1>(pool); } mission_req = mission_req_; } diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 162b64bf..da8cd992 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1636,7 +1636,7 @@ int main(int argc, char** argv) { std::cerr << "Error: Invalid attack deck name/hash " << your_deck_name << ".\n"; } - else if(!your_deck->raid_cards.empty()) + else if(!your_deck->variable_cards.empty()) { std::cerr << "Error: Invalid attack deck " << your_deck_name << ": has optional cards.\n"; your_deck = nullptr; diff --git a/xml.cpp b/xml.cpp index 3d7d7bf5..84787f73 100644 --- a/xml.cpp +++ b/xml.cpp @@ -275,7 +275,7 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType unsigned commander_max_level = commander_max_level_node ? atoi(commander_max_level_node->value()) : commander_card->m_top_level_card->m_level; unsigned upgrade_opportunities = commander_max_level - card->m_level; std::vector always_cards; - std::vector>> some_cards; + std::vector>> some_cards; xml_node<>* deck_node(node->first_node("deck")); xml_node<>* levels_node(node->first_node("levels")); unsigned max_level = levels_node ? atoi(levels_node->value()) : 10; @@ -293,6 +293,7 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType pool_node = pool_node->next_sibling("card_pool")) { unsigned num_cards_from_pool(atoi(pool_node->first_attribute("amount")->value())); + unsigned replicates(pool_node->first_attribute("replicates") ? atoi(pool_node->first_attribute("replicates")->value()) : 1); std::vector cards_from_pool; unsigned upgrade_points = 0; for(xml_node<>* card_node = pool_node->first_node("card"); @@ -303,8 +304,8 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType cards_from_pool.push_back(card); upgrade_points += card->m_top_level_card->m_level - card->m_level; } - some_cards.push_back(std::make_pair(num_cards_from_pool, cards_from_pool)); - upgrade_opportunities += upgrade_points * num_cards_from_pool / cards_from_pool.size(); + some_cards.push_back(std::make_tuple(num_cards_from_pool, replicates, cards_from_pool)); + upgrade_opportunities += upgrade_points * num_cards_from_pool * replicates / cards_from_pool.size(); } xml_node<>* mission_req_node(node->first_node(decktype == DeckType::mission ? "req" : "mission_req")); unsigned mission_req(mission_req_node ? atoi(mission_req_node->value()) : 0); @@ -330,9 +331,9 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType { deck->commander = deck->commander->upgraded(); } for (auto && card: deck->cards) { card = card->m_top_level_card; } - for (auto && pool: deck->raid_cards) + for (auto && pool: deck->variable_cards) { - for (auto && card: pool.second) + for (auto && card: std::get<2>(pool)) { card = card->m_top_level_card; } } } From 491bb414924875eae28586c547e7faf8e1281b21 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 3 Oct 2015 01:42:22 -0400 Subject: [PATCH 366/406] Simulate in-game behavior: Swipe if attack is corroded to 0. --- data/raids.xml | 1 + sim.cpp | 98 ++++++++++++++++++++++++-------------------------- 2 files changed, 47 insertions(+), 52 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index 6baea112..5732f9ee 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -3843,6 +3843,7 @@ 12687 13871 32820 + 33320 33446 33452 33458 diff --git a/sim.cpp b/sim.cpp index eeea3d1c..a09e7729 100644 --- a/sim.cpp +++ b/sim.cpp @@ -952,7 +952,6 @@ struct PerformAttack unsigned op() { unsigned pre_modifier_dmg = attack_power(att_status); - if(pre_modifier_dmg == 0) { return 0; } // Evaluation order: // modify damage @@ -962,60 +961,58 @@ struct PerformAttack // assaults only: (leech if still alive) modify_attack_damage(pre_modifier_dmg); + if (att_dmg == 0) { return 0; } - if(att_dmg > 0) - { - attack_damage(); - if(__builtin_expect(fd->end, false)) { return att_dmg; } - damage_dependant_pre_oa(); + attack_damage(); + if(__builtin_expect(fd->end, false)) { return att_dmg; } + damage_dependant_pre_oa(); - if (att_status->m_hp > 0 && def_status->has_skill(counter) && skill_check(fd, def_status, att_status)) + if (att_status->m_hp > 0 && def_status->has_skill(counter) && skill_check(fd, def_status, att_status)) + { + // perform_skill_counter + unsigned counter_dmg(counter_damage(fd, att_status, def_status)); + if (def_status->m_player == 0) { - // perform_skill_counter - unsigned counter_dmg(counter_damage(fd, att_status, def_status)); - if (def_status->m_player == 0) - { - fd->inc_counter(QuestType::skill_use, counter); - fd->inc_counter(QuestType::skill_damage, counter, 0, counter_dmg); - } - _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); - remove_hp(fd, att_status, counter_dmg); - if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects.count(counterflux)) - { - unsigned flux_denominator = fd->bg_effects.at(counterflux) ? fd->bg_effects.at(counterflux) : 4; - unsigned flux_value = (def_status->skill(counter) - 1) / flux_denominator + 1; - _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n", status_description(def_status).c_str(), flux_value); - add_hp(fd, def_status, flux_value); - def_status->m_attack += flux_value; - } + fd->inc_counter(QuestType::skill_use, counter); + fd->inc_counter(QuestType::skill_damage, counter, 0, counter_dmg); } - unsigned corrosive_value = def_status->skill(corrosive); - if (att_status->m_hp > 0 && corrosive_value > att_status->m_corroded_rate && skill_check(fd, def_status, att_status)) + _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); + remove_hp(fd, att_status, counter_dmg); + if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects.count(counterflux)) { - // perform_skill_corrosive - _DEBUG_MSG(1, "%s corrodes %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), corrosive_value); - att_status->m_corroded_rate = corrosive_value; + unsigned flux_denominator = fd->bg_effects.at(counterflux) ? fd->bg_effects.at(counterflux) : 4; + unsigned flux_value = (def_status->skill(counter) - 1) / flux_denominator + 1; + _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n", status_description(def_status).c_str(), flux_value); + add_hp(fd, def_status, flux_value); + def_status->m_attack += flux_value; } - unsigned berserk_value = att_status->skill(berserk); - if (att_status->m_hp > 0 && berserk_value > 0 && skill_check(fd, att_status, nullptr)) + } + unsigned corrosive_value = def_status->skill(corrosive); + if (att_status->m_hp > 0 && corrosive_value > att_status->m_corroded_rate && skill_check(fd, def_status, att_status)) + { + // perform_skill_corrosive + _DEBUG_MSG(1, "%s corrodes %s by %u\n", status_description(def_status).c_str(), status_description(att_status).c_str(), corrosive_value); + att_status->m_corroded_rate = corrosive_value; + } + unsigned berserk_value = att_status->skill(berserk); + if (att_status->m_hp > 0 && berserk_value > 0 && skill_check(fd, att_status, nullptr)) + { + // perform_skill_berserk + att_status->m_attack += berserk_value; + if (att_status->m_player == 0) { - // perform_skill_berserk - att_status->m_attack += berserk_value; - if (att_status->m_player == 0) - { - fd->inc_counter(QuestType::skill_use, berserk); - } - if (fd->bg_effects.count(enduringrage)) - { - unsigned bge_denominator = fd->bg_effects.at(enduringrage) ? fd->bg_effects.at(enduringrage) : 2; - unsigned bge_value = (berserk_value - 1) / bge_denominator + 1; - _DEBUG_MSG(1, "EnduringRage: %s heals and protects itself for %u\n", status_description(att_status).c_str(), bge_value); - add_hp(fd, att_status, bge_value); - att_status->m_protected += bge_value; - } + fd->inc_counter(QuestType::skill_use, berserk); + } + if (fd->bg_effects.count(enduringrage)) + { + unsigned bge_denominator = fd->bg_effects.at(enduringrage) ? fd->bg_effects.at(enduringrage) : 2; + unsigned bge_value = (berserk_value - 1) / bge_denominator + 1; + _DEBUG_MSG(1, "EnduringRage: %s heals and protects itself for %u\n", status_description(att_status).c_str(), bge_value); + add_hp(fd, att_status, bge_value); + att_status->m_protected += bge_value; } - do_leech(); } + do_leech(); prepend_on_death(fd); resolve_skill(fd); return att_dmg; @@ -1025,8 +1022,8 @@ struct PerformAttack void modify_attack_damage(unsigned pre_modifier_dmg) { assert(att_status->m_card->m_type == CardType::assault); - assert(pre_modifier_dmg > 0); att_dmg = pre_modifier_dmg; + if (att_dmg == 0) { return; } std::string desc; // enhance damage unsigned legion_base = att_status->skill(legion); @@ -1180,10 +1177,6 @@ bool attack_phase(Field* fd) { CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); - if(attack_power(att_status) == 0) - { - return false; - } unsigned att_dmg = 0; if (alive_assault(def_assaults, fd->current_ci)) @@ -1191,7 +1184,8 @@ bool attack_phase(Field* fd) CardStatus * def_status = &fd->tip->assaults[fd->current_ci]; att_dmg = PerformAttack{fd, att_status, def_status}.op(); unsigned swipe_value = att_status->skill(swipe); - if (swipe_value > 0) +// if (att_dmg > 0 && swipe_value > 0) + if (att_status->m_attack + att_status->m_rallied > att_status->m_weakened && swipe_value > 0) // Bizarre behavior: Swipe activates if attack is corroded to 0 { for (auto && adj_status: fd->adjacent_assaults(def_status)) { From 3e4f8032fa0ea564790547444977699cd9c93549 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 4 Oct 2015 01:38:55 -0400 Subject: [PATCH 367/406] Fix bug: Corrosive status should be removed if attack is 0. --- data/raids.xml | 7 +++++-- sim.cpp | 21 +++++++++++++++++++-- tyrant.h | 2 +- tyrant_optimize.cpp | 8 +++++++- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index 5732f9ee..827b221f 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -3822,10 +3822,13 @@ 32490 32820 33452 - 33452 - 33458 33458 33464 + + 15811 + 33452 + 33458 + diff --git a/sim.cpp b/sim.cpp index a09e7729..4c4ed874 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1178,14 +1178,31 @@ bool attack_phase(Field* fd) CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card Storage& def_assaults(fd->tip->assaults); + if (attack_power(att_status) == 0) + { + { // Bizarre behavior: Swipe activates if attack is corroded to 0 + CardStatus * def_status = &fd->tip->assaults[fd->current_ci]; + unsigned swipe_value = att_status->skill(swipe); + if (alive_assault(def_assaults, fd->current_ci) && att_status->m_attack + att_status->m_rallied > att_status->m_weakened && swipe_value > 0) + { + for (auto && adj_status: fd->adjacent_assaults(def_status)) + { + unsigned swipe_dmg = safe_minus(swipe_value + def_status->m_enfeebled, def_status->protected_value()); + _DEBUG_MSG(1, "%s swipes %s for %u damage\n", status_description(att_status).c_str(), status_description(adj_status).c_str(), swipe_dmg); + remove_hp(fd, adj_status, swipe_dmg); + } + } + } + return false; + } + unsigned att_dmg = 0; if (alive_assault(def_assaults, fd->current_ci)) { CardStatus * def_status = &fd->tip->assaults[fd->current_ci]; att_dmg = PerformAttack{fd, att_status, def_status}.op(); unsigned swipe_value = att_status->skill(swipe); -// if (att_dmg > 0 && swipe_value > 0) - if (att_status->m_attack + att_status->m_rallied > att_status->m_weakened && swipe_value > 0) // Bizarre behavior: Swipe activates if attack is corroded to 0 + if (att_dmg > 0 && swipe_value > 0) { for (auto && adj_status: fd->adjacent_assaults(def_status)) { diff --git a/tyrant.h b/tyrant.h index cbd65e81..c7e2797c 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.12" +#define TYRANT_OPTIMIZER_VERSION "2.10.13" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index da8cd992..2beb9526 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -63,6 +63,7 @@ namespace { unsigned use_fused_card_level{0}; bool show_ci{false}; bool use_harmonic_mean{false}; + unsigned sim_seed{0}; Requirement requirement; Quest quest; } @@ -540,7 +541,7 @@ class Process bg_skills(bg_skills_) { destroy_threads = false; - unsigned seed(time(0)); + unsigned seed(sim_seed ? sim_seed : time(0)); for(unsigned i(0); i < num_threads; ++i) { threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, quest, bg_effects, bg_skills)); @@ -1413,6 +1414,11 @@ int main(int argc, char** argv) { use_harmonic_mean = true; } + else if(strcmp(argv[argIndex], "seed") == 0) + { + sim_seed = atoi(argv[argIndex+1]); + argIndex += 1; + } else if(strcmp(argv[argIndex], "-v") == 0) { -- debug_print; From dd25f149f8a1e4a9137c7364898394d6a4d50bf2 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 4 Oct 2015 12:02:23 -0400 Subject: [PATCH 368/406] Update Campaign decks. --- data/raids.xml | 7 +++++-- tyrant_optimize.cpp | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index 827b221f..8f2e89ac 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -3756,10 +3756,13 @@ 32064 32064 33452 - 33452 - 33458 33458 33464 + + 16965 + 33452 + 33458 + diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 2beb9526..d0f831d5 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -542,6 +542,7 @@ class Process { destroy_threads = false; unsigned seed(sim_seed ? sim_seed : time(0)); + std::cout << "seed " << seed << std::endl; for(unsigned i(0); i < num_threads; ++i) { threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, quest, bg_effects, bg_skills)); From b2e00dfa0746b12f696c9ef19ffcd71e987ba56d Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 9 Oct 2015 10:01:03 -0400 Subject: [PATCH 369/406] Fix bug: Evolve and Enhance should not target jammed units. --- sim.cpp | 40 ++++++++++++++++++++-------------------- tyrant.h | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/sim.cpp b/sim.cpp index 4c4ed874..a9aeb034 100644 --- a/sim.cpp +++ b/sim.cpp @@ -313,11 +313,9 @@ void resolve_skill(Field* fd) } //------------------------------------------------------------------------------ inline bool has_attacked(CardStatus* c) { return(c->m_step == CardStep::attacked); } -inline bool is_jammed(CardStatus* c) { return(c->m_jammed); } -inline bool is_active(CardStatus* c) { return(c->m_delay == 0); } -inline bool is_active_next_turn(CardStatus* c) { return(c->m_delay <= 1); } -inline bool can_act(CardStatus* c) { return(c->m_hp > 0 && !is_jammed(c)); } -inline bool can_attack(CardStatus* c) { return(can_act(c)); } +inline bool can_act(CardStatus* c) { return(c->m_hp > 0 && !c->m_jammed); } +inline bool is_active(CardStatus* c) { return(can_act(c) && c->m_delay == 0); } +inline bool is_active_next_turn(CardStatus* c) { return(can_act(c) && c->m_delay <= 1); } // Can be healed / repaired inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_max_hp); } //------------------------------------------------------------------------------ @@ -350,7 +348,7 @@ void evaluate_skills(Field* fd, CardStatus* status, const std::vector if (type == CardType::assault) { // Attack - if (can_attack(status)) + if (can_act(status)) { if (attack_phase(fd) && !*attacked) { @@ -515,7 +513,7 @@ Results play(Field* fd) for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->structures.size(); ++fd->current_ci) { CardStatus* current_status(&fd->tap->structures[fd->current_ci]); - if(!is_active(current_status) || !can_act(current_status)) + if (!is_active(current_status)) { _DEBUG_MSG(2, "%s cannot take action.\n", status_description(current_status).c_str()); } @@ -532,7 +530,7 @@ Results play(Field* fd) // ca: current assault CardStatus* current_status(&fd->tap->assaults[fd->current_ci]); bool attacked = false; - if(!is_active(current_status) || !can_act(current_status)) + if (!is_active(current_status)) { _DEBUG_MSG(2, "%s cannot take action.\n", status_description(current_status).c_str()); } @@ -663,6 +661,12 @@ inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) return(can_be_healed(c)); } +template<> +inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) +{ + return(is_active(c)); +} + template<> inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref) { @@ -1033,15 +1037,11 @@ struct PerformAttack unsigned legion_size = 0; legion_size += att_status->m_index > 0 && assaults[att_status->m_index - 1].m_hp > 0 && assaults[att_status->m_index - 1].m_faction == att_status->m_faction; legion_size += att_status->m_index + 1 < assaults.size() && assaults[att_status->m_index + 1].m_hp > 0 && assaults[att_status->m_index + 1].m_faction == att_status->m_faction; - if (legion_size > 0) + if (legion_size > 0 && skill_check(fd, att_status, nullptr)) { - // skill_check - if (att_status->m_hp > 0 && is_active(att_status)) - { - unsigned legion_value = legion_base * legion_size; - if (debug_print > 0) { desc += "+" + to_string(legion_value) + "(legion)"; } - att_dmg += legion_value; - } + unsigned legion_value = legion_base * legion_size; + if (debug_print > 0) { desc += "+" + to_string(legion_value) + "(legion)"; } + att_dmg += legion_value; } } unsigned rupture_value = att_status->skill(rupture); @@ -1274,13 +1274,13 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, c template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - return can_act(dst) && is_active_next_turn(dst); + return is_active_next_turn(dst); } template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - if (dst->m_overloaded || has_attacked(dst) || !(is_active(dst) && can_act(dst))) + if (dst->m_overloaded || has_attacked(dst) || !is_active(dst)) { return false; } @@ -1315,13 +1315,13 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* ds template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - return can_attack(dst) && (fd->tapi == dst->m_player ? is_active(dst) && !has_attacked(dst) : is_active_next_turn(dst)); + return fd->tapi == dst->m_player ? is_active(dst) && !has_attacked(dst) : is_active_next_turn(dst); } template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - return can_attack(dst) && attack_power(dst) > 0 && is_active_next_turn(dst); + return attack_power(dst) > 0 && is_active_next_turn(dst); } template diff --git a/tyrant.h b/tyrant.h index c7e2797c..b06005f3 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.13" +#define TYRANT_OPTIMIZER_VERSION "2.10.14" #include #include From 5aa981f7b8882d8d7cbe44e7590947bbd00de835 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 9 Oct 2015 11:17:23 -0400 Subject: [PATCH 370/406] No longer support multiple operations. --- tyrant.h | 2 +- tyrant_optimize.cpp | 153 +++++++++++++++++++++++--------------------- 2 files changed, 82 insertions(+), 73 deletions(-) diff --git a/tyrant.h b/tyrant.h index b06005f3..8c82351f 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.10.14" +#define TYRANT_OPTIMIZER_VERSION "2.11.0" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index d0f831d5..aedddbf5 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -14,17 +14,18 @@ //#define NDEBUG #define BOOST_THREAD_USE_LIB #include +#include #include #include +#include #include -#include -#include #include -#include #include #include +#include #include -#include +#include +#include #include #include #include @@ -541,8 +542,11 @@ class Process bg_skills(bg_skills_) { destroy_threads = false; - unsigned seed(sim_seed ? sim_seed : time(0)); - std::cout << "seed " << seed << std::endl; + unsigned seed(sim_seed ? sim_seed : std::chrono::system_clock::now().time_since_epoch().count() * 2654435761); // Knuth multiplicative hash + if (num_threads_ == 1) + { + std::cout << "RNG seed " << seed << std::endl; + } for(unsigned i(0); i < num_threads; ++i) { threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, quest, bg_effects, bg_skills)); @@ -826,7 +830,7 @@ void hill_climbing(unsigned num_min_iterations, unsigned num_iterations, Deck* d unsigned deck_cost = get_deck_cost(d1); fund = std::max(fund, deck_cost); print_deck_inline(deck_cost, best_score, d1); - std::mt19937 re(std::chrono::system_clock::now().time_since_epoch().count()); + std::mt19937 & re = proc.threads_data[0]->re; unsigned best_gap = check_requirement(d1, requirement, quest); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; @@ -985,7 +989,7 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, unsigned deck_cost = get_deck_cost(d1); fund = std::max(fund, deck_cost); print_deck_inline(deck_cost, best_score, d1); - std::mt19937 re(std::chrono::system_clock::now().time_since_epoch().count()); + std::mt19937 & re = proc.threads_data[0]->re; unsigned best_gap = check_requirement(d1, requirement, quest); bool deck_has_been_improved = true; unsigned long skipped_simulations = 0; @@ -1137,11 +1141,12 @@ void hill_climbing_ordered(unsigned num_min_iterations, unsigned num_iterations, } //------------------------------------------------------------------------------ enum Operation { + noop, simulate, climb, reorder, debug, - debuguntil + debuguntil, }; //------------------------------------------------------------------------------ extern void(*skill_table[num_skills])(Field*, CardStatus* src_status, const SkillSpec&); @@ -1226,7 +1231,7 @@ int main(int argc, char** argv) std::vector opt_owned_cards_str_list; bool opt_do_optimization(false); bool opt_keep_commander{false}; - std::vector> opt_todo; + std::tuple opt_todo; std::vector opt_effects; std::unordered_map opt_bg_effects; std::vector opt_bg_skills; @@ -1455,35 +1460,41 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "sim") == 0) { - opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); + opt_todo = std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate); + if (std::get<0>(opt_todo) < 10) { opt_num_threads = 1; } argIndex += 1; } else if(strcmp(argv[argIndex], "climbex") == 0) { - opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), climb)); + opt_todo = std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), climb); + if (std::get<1>(opt_todo) < 10) { opt_num_threads = 1; } opt_do_optimization = true; argIndex += 2; } else if(strcmp(argv[argIndex], "climb") == 0) { - opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), climb)); + opt_todo = std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), climb); + if (std::get<1>(opt_todo) < 10) { opt_num_threads = 1; } opt_do_optimization = true; argIndex += 1; } else if(strcmp(argv[argIndex], "reorder") == 0) { - opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), reorder)); + opt_todo = std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), reorder); + if (std::get<1>(opt_todo) < 10) { opt_num_threads = 1; } argIndex += 1; } else if(strcmp(argv[argIndex], "debug") == 0) { - opt_todo.push_back(std::make_tuple(0u, 0u, debug)); + opt_todo = std::make_tuple(0u, 0u, debug); + opt_num_threads = 1; } else if(strcmp(argv[argIndex], "debuguntil") == 0) { // output the debug info for the first battle that min_score <= score <= max_score. // E.g., 0 0: lose; 100 100: win (non-raid); 20 100: at least 20 damage (raid). - opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), debuguntil)); + opt_todo = std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), debuguntil); + opt_num_threads = 1; argIndex += 2; } else @@ -1948,76 +1959,74 @@ int main(int argc, char** argv) Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, quest, opt_bg_effects, opt_bg_skills); { - //ScopeClock timer; - for(auto op: opt_todo) + switch(std::get<2>(opt_todo)) { - switch(std::get<2>(op)) + case noop: + break; + case simulate: { + EvaluatedResults results = { EvaluatedResults::first_type(enemy_decks.size()), 0 }; + results = p.evaluate(std::get<0>(opt_todo), results); + print_results(results, p.factors); + break; + } + case climb: { + switch (opt_your_strategy) { - case simulate: { - EvaluatedResults results = { EvaluatedResults::first_type(enemy_decks.size()), 0 }; - results = p.evaluate(std::get<0>(op), results); - print_results(results, p.factors); + case DeckStrategy::random: + hill_climbing(std::get<0>(opt_todo), std::get<1>(opt_todo), your_deck, p, requirement, quest); break; - } - case climb: { - switch (opt_your_strategy) - { - case DeckStrategy::random: - hill_climbing(std::get<0>(op), std::get<1>(op), your_deck, p, requirement, quest); - break; // case DeckStrategy::ordered: // case DeckStrategy::exact_ordered: - default: - hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, requirement, quest); - break; - } + default: + hill_climbing_ordered(std::get<0>(opt_todo), std::get<1>(opt_todo), your_deck, p, requirement, quest); break; } - case reorder: { - your_deck->strategy = DeckStrategy::ordered; - use_owned_cards = true; - if (min_deck_len == 1 && max_deck_len == 10) - { - min_deck_len = max_deck_len = your_deck->cards.size(); - } - fund = 0; - debug_print = -1; - owned_cards.clear(); - claim_cards({your_deck->commander}); - claim_cards(your_deck->cards); - hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, requirement, quest); - break; + break; + } + case reorder: { + your_deck->strategy = DeckStrategy::ordered; + use_owned_cards = true; + if (min_deck_len == 1 && max_deck_len == 10) + { + min_deck_len = max_deck_len = your_deck->cards.size(); } - case debug: { - ++ debug_print; + fund = 0; + debug_print = -1; + owned_cards.clear(); + claim_cards({your_deck->commander}); + claim_cards(your_deck->cards); + hill_climbing_ordered(std::get<0>(opt_todo), std::get<1>(opt_todo), your_deck, p, requirement, quest); + break; + } + case debug: { + ++ debug_print; + debug_str.clear(); + EvaluatedResults results{EvaluatedResults::first_type(enemy_decks.size()), 0}; + results = p.evaluate(1, results); + print_results(results, p.factors); + -- debug_print; + break; + } + case debuguntil: { + ++ debug_print; + ++ debug_cached; + while(1) + { debug_str.clear(); EvaluatedResults results{EvaluatedResults::first_type(enemy_decks.size()), 0}; results = p.evaluate(1, results); - print_results(results, p.factors); - -- debug_print; - break; - } - case debuguntil: { - ++ debug_print; - ++ debug_cached; - while(1) + auto score = compute_score(results, p.factors); + if(score.points >= std::get<0>(opt_todo) && score.points <= std::get<1>(opt_todo)) { - debug_str.clear(); - EvaluatedResults results{EvaluatedResults::first_type(enemy_decks.size()), 0}; - results = p.evaluate(1, results); - auto score = compute_score(results, p.factors); - if(score.points >= std::get<0>(op) && score.points <= std::get<1>(op)) - { - std::cout << debug_str << std::flush; - print_results(results, p.factors); - break; - } + std::cout << debug_str << std::flush; + print_results(results, p.factors); + break; } - -- debug_cached; - -- debug_print; - break; - } } + -- debug_cached; + -- debug_print; + break; + } } } return 0; From a2a8719713c56dd06601696a4d3d52ea197a847d Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 9 Oct 2015 11:52:46 -0400 Subject: [PATCH 371/406] Deck names are case-insensitive now. --- cards.cpp | 2 +- deck.cpp | 12 ++++++++++++ deck.h | 2 ++ read.cpp | 30 +++++++++++++++--------------- read.h | 2 +- tyrant_optimize.cpp | 12 ++++++------ xml.cpp | 12 ++++++------ 7 files changed, 43 insertions(+), 29 deletions(-) diff --git a/cards.cpp b/cards.cpp index e34bdba6..c857d83d 100644 --- a/cards.cpp +++ b/cards.cpp @@ -15,7 +15,7 @@ std::string simplify_name(const std::string& card_name) std::string simple_name; for(auto c : card_name) { - if(!strchr(";:,\"' ", c)) + if(!strchr(";:,\"'! ", c)) { simple_name += ::tolower(c); } diff --git a/deck.cpp b/deck.cpp index c3dd5222..680ed9e7 100644 --- a/deck.cpp +++ b/deck.cpp @@ -583,3 +583,15 @@ void Deck::place_at_bottom(const Card* 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; +} + +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; +} + diff --git a/deck.h b/deck.h index 35f99548..693121aa 100644 --- a/deck.h +++ b/deck.h @@ -141,6 +141,8 @@ typedef std::map DeckList; class Decks { public: + void add_deck(Deck* deck, const std::string& deck_name); + Deck* find_deck_by_name(const std::string& deck_name); std::list decks; std::map, Deck*> by_type_id; std::map by_name; diff --git a/read.cpp b/read.cpp index 09a48638..d7ab04df 100644 --- a/read.cpp +++ b/read.cpp @@ -74,7 +74,7 @@ DeckList & normalize(DeckList & decklist) return decklist; } -DeckList expand_deck_to_list(std::string deck_name, const Decks& decks) +DeckList expand_deck_to_list(std::string deck_name, Decks& decks) { static std::unordered_set expanding_decks; if (expanding_decks.count(deck_name)) @@ -83,11 +83,11 @@ DeckList expand_deck_to_list(std::string deck_name, const Decks& decks) return DeckList(); } auto deck_string = deck_name; - const auto & deck = decks.by_name.find(deck_name); - if (deck != decks.by_name.end()) + Deck* deck = decks.find_deck_by_name(deck_name); + if (deck != nullptr) { - deck_string = deck->second->deck_string; - if (deck_string.find_first_of(";:") != std::string::npos || decks.by_name.find(deck_string) != decks.by_name.end()) + deck_string = deck->deck_string; + if (deck_string.find_first_of(";:") != std::string::npos || decks.find_deck_by_name(deck_string) != nullptr) { // deck_name refers to a deck list expanding_decks.insert(deck_name); @@ -105,11 +105,11 @@ DeckList expand_deck_to_list(std::string deck_name, const Decks& decks) boost::regex regex(regex_string); boost::smatch smatch; expanding_decks.insert(deck_name); - for (const auto & deck: decks.by_name) + for (const auto & deck_it: decks.by_name) { - if (boost::regex_search(deck.first, smatch, regex)) + if (boost::regex_search(deck_it.first, smatch, regex)) { - auto && decklist = expand_deck_to_list(deck.first, decks); + auto && decklist = expand_deck_to_list(deck_it.first, decks); for (const auto & it : decklist) { res[it.first] += it.second; @@ -129,7 +129,7 @@ DeckList expand_deck_to_list(std::string deck_name, const Decks& decks) } } -DeckList parse_deck_list(std::string list_string, const Decks& decks) +DeckList parse_deck_list(std::string list_string, Decks& decks) { DeckList res; boost::tokenizer> list_tokens{list_string, boost::char_delimiters_separator{false, ";", ""}}; @@ -383,18 +383,18 @@ unsigned load_custom_decks(Decks& decks, Cards& all_cards, const std::string & f continue; } deck_string_iter = advance_until(deck_string_iter + 1, deck_string.end(), [](const char& c){return(c != ' ');}); - auto deck_iter = decks.by_name.find(deck_name); - if(deck_iter != decks.by_name.end()) + Deck* deck = decks.find_deck_by_name(deck_name); + if (deck != nullptr) { - std::cerr << "Warning in custom deck file " << filename << " at line " << num_line << ", name conflicts, overrides " << deck_iter->second->short_description() << std::endl; + std::cerr << "Warning in custom deck file " << filename << " at line " << num_line << ", name conflicts, overrides " << deck->short_description() << std::endl; } decks.decks.push_back(Deck{all_cards, DeckType::custom_deck, num_line, deck_name}); - Deck* deck = &decks.decks.back(); + deck = &decks.decks.back(); deck->set(std::string{deck_string_iter, deck_string.end()}); - decks.by_name[deck_name] = deck; + decks.add_deck(deck, deck_name); std::stringstream alt_name; alt_name << decktype_names[deck->decktype] << " #" << deck->id; - decks.by_name[alt_name.str()] = deck; + decks.add_deck(deck, alt_name.str()); } } catch (std::exception& e) diff --git a/read.h b/read.h index 2952d5d9..1d6ad280 100644 --- a/read.h +++ b/read.h @@ -11,7 +11,7 @@ class Cards; class Decks; class Deck; -DeckList parse_deck_list(std::string list_string, const Decks& decks); +DeckList parse_deck_list(std::string list_string, Decks& decks); void parse_card_spec(const Cards& cards, const std::string& card_spec, unsigned& card_id, unsigned& card_num, char& num_sign, char& mark); const std::pair, std::map> string_to_ids(const Cards& all_cards, const std::string& deck_string, const std::string & description); unsigned load_custom_decks(Decks& decks, Cards& cards, const std::string & filename); diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index aedddbf5..a258f31c 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -105,14 +105,14 @@ std::string card_slot_id_names(const std::vector //------------------------------------------------------------------------------ Deck* find_deck(Decks& decks, const Cards& all_cards, std::string deck_name) { - auto it = decks.by_name.find(deck_name); - if(it != decks.by_name.end()) + Deck* deck = decks.find_deck_by_name(deck_name); + if (deck != nullptr) { - it->second->resolve(); - return(it->second); + deck->resolve(); + return(deck); } - decks.decks.push_back(Deck{all_cards}); - Deck* deck = &decks.decks.back(); + decks.decks.emplace_back(Deck{all_cards}); + deck = &decks.decks.back(); deck->set(deck_name); deck->resolve(); return(deck); diff --git a/xml.cpp b/xml.cpp index 84787f73..e614ddab 100644 --- a/xml.cpp +++ b/xml.cpp @@ -316,8 +316,8 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType decks.decks.push_back(Deck{all_cards, decktype, id, deck_name, (upgrade_opportunities + 1) * (level - 1) / (max_level - 1), upgrade_opportunities}); Deck* deck = &decks.decks.back(); deck->set(commander_card, commander_max_level, always_cards, some_cards, mission_req); - decks.by_name[deck_name] = deck; - decks.by_name[decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(level)] = deck; + decks.add_deck(deck, deck_name); + decks.add_deck(deck, decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(level)); } decks.decks.push_back(Deck{all_cards, decktype, id, base_deck_name}); @@ -338,10 +338,10 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType } } - decks.by_name[base_deck_name] = deck; - decks.by_name[base_deck_name + "-" + to_string(max_level)] = deck; - decks.by_name[decktype_names[decktype] + " #" + to_string(id)] = deck; - decks.by_name[decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(max_level)] = deck; + decks.add_deck(deck, base_deck_name); + decks.add_deck(deck, base_deck_name + "-" + to_string(max_level)); + decks.add_deck(deck, decktype_names[decktype] + " #" + to_string(id)); + decks.add_deck(deck, decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(max_level)); decks.by_type_id[{decktype, id}] = deck; return deck; } From 7c06ec561e472aea1b87ba7a2763d30f59a14944 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 27 Oct 2015 20:30:00 -0400 Subject: [PATCH 372/406] Support BGE Brigade. --- sim.cpp | 15 ++++++++++----- tyrant.cpp | 2 +- tyrant.h | 6 +++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/sim.cpp b/sim.cpp index a9aeb034..6e3cbb76 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1030,16 +1030,16 @@ struct PerformAttack if (att_dmg == 0) { return; } std::string desc; // enhance damage + unsigned legion_value = 0; unsigned legion_base = att_status->skill(legion); if (legion_base > 0) { auto & assaults = fd->tap->assaults; - unsigned legion_size = 0; - legion_size += att_status->m_index > 0 && assaults[att_status->m_index - 1].m_hp > 0 && assaults[att_status->m_index - 1].m_faction == att_status->m_faction; - legion_size += att_status->m_index + 1 < assaults.size() && assaults[att_status->m_index + 1].m_hp > 0 && assaults[att_status->m_index + 1].m_faction == att_status->m_faction; - if (legion_size > 0 && skill_check(fd, att_status, nullptr)) + legion_value += att_status->m_index > 0 && assaults[att_status->m_index - 1].m_hp > 0 && assaults[att_status->m_index - 1].m_faction == att_status->m_faction; + legion_value += att_status->m_index + 1 < assaults.size() && assaults[att_status->m_index + 1].m_hp > 0 && assaults[att_status->m_index + 1].m_faction == att_status->m_faction; + if (legion_value > 0 && skill_check(fd, att_status, nullptr)) { - unsigned legion_value = legion_base * legion_size; + legion_value *= legion_base; if (debug_print > 0) { desc += "+" + to_string(legion_value) + "(legion)"; } att_dmg += legion_value; } @@ -1100,6 +1100,11 @@ struct PerformAttack if(!desc.empty()) { desc += "=" + to_string(att_dmg); } _DEBUG_MSG(1, "%s attacks %s for %u%s damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), pre_modifier_dmg, desc.c_str()); } + if (legion_value > 0 && can_be_healed(att_status) && fd->bg_effects.count(brigade)) + { + _DEBUG_MSG(1, "Brigade: %s heals itself for %u\n", status_description(att_status).c_str(), legion_value); + add_hp(fd, att_status, legion_value); + } } template diff --git a/tyrant.cpp b/tyrant.cpp index b5a8884a..d583d2fc 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -30,7 +30,7 @@ std::string skill_names[Skill::num_skills] = "Flurry", "Valor", // Pseudo-skill for passive BGEs: "", - "Bloodlust", "Counterflux", "Divert", "EnduringRage", "Fortification", "Metamorphosis", "Reaping", "TurningTides", + "Bloodlust", "Brigade", "Counterflux", "Divert", "EnduringRage", "Fortification", "Metamorphosis", "Reaping", "TurningTides", "", }; diff --git a/tyrant.h b/tyrant.h index 8c82351f..17774855 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.11.0" +#define TYRANT_OPTIMIZER_VERSION "2.11.1" #include #include @@ -40,13 +40,13 @@ enum Skill END_DEFENSIVE, // Combat-Modifier: legion, pierce, rupture, swipe, venom, - // Damage-Dependant: + // Damage-Dependent: berserk, inhibit, leech, poison, // Triggered: flurry, valor, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, - bloodlust, counterflux, divert, enduringrage, fortification, metamorphosis, reaping, turningtides, + bloodlust, brigade, counterflux, divert, enduringrage, fortification, metamorphosis, reaping, turningtides, END_BGE_SKILL, num_skills }; From 2a8cc9aabea99d5c8219234872fcd243fe51292f Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 27 Oct 2015 20:41:22 -0400 Subject: [PATCH 373/406] Update skills Enhance and Evolve. --- sim.cpp | 8 ++++++-- tyrant.cpp | 4 ++-- tyrant.h | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/sim.cpp b/sim.cpp index 6e3cbb76..af08f97c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1259,13 +1259,17 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const S template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - return dst->has_skill(s.s) && ((BEGIN_DEFENSIVE < s.s && s.s < END_DEFENSIVE) || is_active(dst)); + return dst->has_skill(s.s) && (!(BEGIN_ACTIVATION < s.s && s.s < END_ACTIVATION) || is_active(dst)); } +/* + * Target active units: Activation (Mortar) + * Target everything: Defensive (Refresh), Combat-Modifier (Rupture, Venom) + */ template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - return dst->has_skill(s.s) && !dst->has_skill(s.s2) && ((BEGIN_DEFENSIVE < s.s2 && s.s2 < END_DEFENSIVE) || is_active(dst)); + return dst->has_skill(s.s) && !dst->has_skill(s.s2) && (!(BEGIN_ACTIVATION < s.s2 && s.s2 < END_ACTIVATION) || is_active(dst)); } template<> diff --git a/tyrant.cpp b/tyrant.cpp index d583d2fc..3a837ad6 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -12,12 +12,12 @@ std::string skill_names[Skill::num_skills] = // Attack: "0", // Activation: - "", + "", "", "Enfeeble", "Jam", "Mortar", "Siege", "Strike", "Weaken", "", "", "Enhance", "Evolve", "Heal", "Mend", "Overload", "Protect", "Rally", - "", + "", "", // Defensive: "", "Armor", "Avenge", "Corrosive", "Counter", "Evade", "Payback", "Refresh", "Wall", diff --git a/tyrant.h b/tyrant.h index 17774855..efd2ca00 100644 --- a/tyrant.h +++ b/tyrant.h @@ -28,12 +28,12 @@ enum Skill // Attack: attack, // Activation: - BEGIN_ACTIVATION_HARMFUL, // TODO skill traits + BEGIN_ACTIVATION, BEGIN_ACTIVATION_HARMFUL, // TODO skill traits enfeeble, jam, mortar, siege, strike, weaken, END_ACTIVATION_HARMFUL, BEGIN_ACTIVATION_HELPFUL, enhance, evolve, heal, mend, overload, protect, rally, - END_ACTIVATION_HELPFUL, + END_ACTIVATION_HELPFUL, END_ACTIVATION, // Defensive: BEGIN_DEFENSIVE, armor, avenge, corrosive, counter, evade, payback, refresh, wall, From 3eeea8d8716feea39727f282c1b55f0a061e2e5c Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 6 Nov 2015 11:17:32 -0500 Subject: [PATCH 374/406] Access new XML files. --- sim.cpp | 5 ++++- tyrant.h | 4 ++-- tyrant_optimize.cpp | 10 ++++++---- update_xml.sh | 4 ++++ xml.cpp | 29 +++++++++++++++++++++-------- xml.h | 1 + 6 files changed, 38 insertions(+), 15 deletions(-) create mode 100755 update_xml.sh diff --git a/sim.cpp b/sim.cpp index af08f97c..42fc188b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -418,7 +418,10 @@ struct PlayCard status->m_player = fd->tapi; if (status->m_player == 0) { - fd->inc_counter(QuestType::faction_card_use, card->m_faction); + if (status->m_card->m_type == CardType::assault) + { + fd->inc_counter(QuestType::faction_assault_card_use, card->m_faction); + } fd->inc_counter(QuestType::type_card_use, type); } _DEBUG_MSG(1, "%s plays %s %u [%s]\n", status_description(&fd->tap->commander).c_str(), cardtype_names[type].c_str(), static_cast(storage->size() - 1), card_description(fd->cards, card).c_str()); diff --git a/tyrant.h b/tyrant.h index efd2ca00..97465723 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.11.1" +#define TYRANT_OPTIMIZER_VERSION "2.12.0" #include #include @@ -94,7 +94,7 @@ enum QuestType none, skill_use, skill_damage, - faction_card_use, + faction_assault_card_use, type_card_use, faction_assault_card_kill, type_card_kill, diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index a258f31c..4df99879 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -344,7 +344,7 @@ unsigned check_requirement(const Deck* deck, const Requirement & requirement, co } } break; - case QuestType::faction_card_use: + case QuestType::faction_assault_card_use: potential_value += (quest.quest_key == card->m_faction); break; case QuestType::type_card_use: @@ -1154,6 +1154,7 @@ void print_available_effects() { std::cout << "Available effects besides activation skills:\n" " Bloodlust X\n" + " Brigade\n" " Counterflux\n" " Divert\n" " EnduringRage\n" @@ -1506,9 +1507,10 @@ int main(int argc, char** argv) Cards all_cards; Decks decks; - for (const auto & suffix: fn_suffix_list) + load_skills_set_xml(all_cards, "data/skills_set.xml", true); + for (unsigned section = 1; section <= 9; ++ section) { - load_cards_xml(all_cards, "data/cards" + suffix + ".xml", suffix.empty()); + load_cards_xml(all_cards, "data/cards_section_" + to_string(section) + ".xml", false); } all_cards.organize(); for (const auto & suffix: fn_suffix_list) @@ -1733,7 +1735,7 @@ int main(int argc, char** argv) { if (key_str == boost::to_lower_copy(faction_names[i])) { - quest.quest_type = type_str == "cu" ? QuestType::faction_card_use : QuestType::faction_assault_card_kill; + quest.quest_type = type_str == "cu" ? QuestType::faction_assault_card_use : QuestType::faction_assault_card_kill; quest.quest_key = i; break; } diff --git a/update_xml.sh b/update_xml.sh new file mode 100755 index 00000000..72cfd1ee --- /dev/null +++ b/update_xml.sh @@ -0,0 +1,4 @@ +#!/bin/bash +for fn in fusion_recipes_cj2 missions skills_set `seq -f cards_section_%g 1 8` ; do + curl http://mobile$1.tyrantonline.com/assets/${fn}.xml -R -z data/${fn}.xml -o data/${fn}.xml +done diff --git a/xml.cpp b/xml.cpp index e614ddab..342db0dd 100644 --- a/xml.cpp +++ b/xml.cpp @@ -235,7 +235,27 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) card->m_top_level_card = top_card; } -void load_cards_xml(Cards & all_cards, const std::string & filename, bool do_warn_on_missing=true) +void load_cards_xml(Cards & all_cards, const std::string & filename, bool do_warn_on_missing) +{ + std::vector buffer; + xml_document<> doc; + parse_file(filename, buffer, doc, do_warn_on_missing); + xml_node<>* root = doc.first_node(); + + if(!root) + { + return; + } + for (xml_node<>* card_node = root->first_node("unit"); + card_node; + card_node = card_node->next_sibling("unit")) + { + auto card = new Card(); + parse_card_node(all_cards, card, card_node); + } +} + +void load_skills_set_xml(Cards & all_cards, const std::string & filename, bool do_warn_on_missing) { std::vector buffer; xml_document<> doc; @@ -257,13 +277,6 @@ void load_cards_xml(Cards & all_cards, const std::string & filename, bool do_war all_cards.visible_cardset.insert(atoi(id_node->value())); } } - for (xml_node<>* card_node = root->first_node("unit"); - card_node; - card_node = card_node->next_sibling("unit")) - { - auto card = new Card(); - parse_card_node(all_cards, card, card_node); - } } //------------------------------------------------------------------------------ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType::DeckType decktype, unsigned id, std::string base_deck_name) diff --git a/xml.h b/xml.h index ed2d9cb3..6c161991 100644 --- a/xml.h +++ b/xml.h @@ -10,6 +10,7 @@ class Achievement; Skill skill_name_to_id(const std::string & name); void load_cards_xml(Cards & all_cards, const std::string & filename, bool do_warn_on_missing); +void load_skills_set_xml(Cards & all_cards, const std::string & filename, bool do_warn_on_missing); void load_decks_xml(Decks& decks, const Cards& all_cards, const std::string & mission_filename, const std::string & raid_filename, bool do_warn_on_missing); void load_recipes_xml(Cards& all_cards, const std::string & filename, bool do_warn_on_missing); void read_missions(Decks& decks, const Cards& all_cards, const std::string & filename, bool do_warn_on_missing); From c6b061ea7e4e959f2a9b63aac29a9ff32655f6f6 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 12 Nov 2015 13:34:01 -0500 Subject: [PATCH 375/406] Add skill Rush. --- data/raids.xml | 21 +++++++++++++++++++++ sim.cpp | 32 ++++++++++++++++++++++++++++++++ sim.h | 1 + tyrant.cpp | 4 ++-- tyrant.h | 6 +++--- 5 files changed, 59 insertions(+), 5 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index 8f2e89ac..9b384a84 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -315,6 +315,27 @@ + + 15 + Lithid Raid + 1952 + 26 + + + 35130 + 35130 + 35140 + 35140 + 35150 + 35150 + 35160 + 35160 + 35170 + 35170 + + + + diff --git a/sim.cpp b/sim.cpp index 42fc188b..99bbe11e 100644 --- a/sim.cpp +++ b/sim.cpp @@ -116,6 +116,7 @@ inline void CardStatus::set(const Card& card) m_poisoned = 0; m_protected = 0; m_rallied = 0; + m_rush_attempted = false; m_weakened = 0; std::memset(m_primary_skill_offset, 0, sizeof m_primary_skill_offset); @@ -206,8 +207,10 @@ std::string CardStatus::description() const if(m_delay > 0) { desc += " cd:" + to_string(m_delay); } + // Status w/o value if(m_jammed) { desc += ", jammed"; } if(m_overloaded) { desc += ", overloaded"; } + // Status w/ value if(m_corroded_rate > 0) { desc += ", corroded " + to_string(m_corroded_rate); } if(m_enfeebled > 0) { desc += ", enfeebled " + to_string(m_enfeebled); } if(m_inhibited > 0) { desc += ", inhibited " + to_string(m_inhibited); } @@ -709,6 +712,7 @@ void remove_hp(Field* fd, CardStatus* status, unsigned dmg) } } } + inline bool is_it_dead(CardStatus& c) { if(c.m_hp == 0) // yes it is @@ -1330,6 +1334,12 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, return fd->tapi == dst->m_player ? is_active(dst) && !has_attacked(dst) : is_active_next_turn(dst); } +template<> +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ + return ! src->m_rush_attempted && dst->m_delay >= 1 + (src->m_card->m_type == CardType::assault && dst->m_index < src->m_index); +} + template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { @@ -1413,6 +1423,12 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, co dst->m_rallied += s.x; } +template<> +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ + dst->m_delay -= 1; +} + template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { @@ -1513,6 +1529,9 @@ template<> std::vector& skill_targets(Field* fd, CardStatu template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) +{ return(skill_targets_allied_assault(fd, src_status)); } + template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) { return(skill_targets_hostile_structure(fd, src_status)); } @@ -1680,6 +1699,18 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil } } +void perform_targetted_allied_fast_rush(Field* fd, CardStatus* src_status, const SkillSpec& s) +{ + if (src_status->m_rush_attempted) + { + _DEBUG_MSG(2, "%s does not check Rush again.\n", status_description(src_status).c_str()); + return; + } + _DEBUG_MSG(1, "%s attempts to activate Rush.\n", status_description(src_status).c_str()); + perform_targetted_allied_fast(fd, src_status, s); + src_status->m_rush_attempted = true; +} + template void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) { @@ -1761,6 +1792,7 @@ void fill_skill_table() skill_table[overload] = perform_targetted_allied_fast; skill_table[protect] = perform_targetted_allied_fast; skill_table[rally] = perform_targetted_allied_fast; + skill_table[rush] = perform_targetted_allied_fast_rush; skill_table[siege] = perform_targetted_hostile_fast; skill_table[strike] = perform_targetted_hostile_fast; skill_table[weaken] = perform_targetted_hostile_fast; diff --git a/sim.h b/sim.h index b51cc7bc..e5f100ff 100644 --- a/sim.h +++ b/sim.h @@ -156,6 +156,7 @@ struct CardStatus unsigned m_poisoned; unsigned m_protected; unsigned m_rallied; + bool m_rush_attempted; unsigned m_weakened; signed m_primary_skill_offset[num_skills]; diff --git a/tyrant.cpp b/tyrant.cpp index 3a837ad6..0e0f6daa 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -13,10 +13,10 @@ std::string skill_names[Skill::num_skills] = "0", // Activation: "", "", - "Enfeeble", "Jam", "Mortar", "Siege", "Strike", "Weaken", + "Enfeeble", "Jam", "Mortar", "Siege", "Strike", "Sunder", "Weaken", "", "", - "Enhance", "Evolve", "Heal", "Mend", "Overload", "Protect", "Rally", + "Enhance", "Evolve", "Heal", "Mend", "Overload", "Protect", "Rally", "Rush", "", "", // Defensive: "", diff --git a/tyrant.h b/tyrant.h index 97465723..79aef502 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.12.0" +#define TYRANT_OPTIMIZER_VERSION "2.13.0" #include #include @@ -29,10 +29,10 @@ enum Skill attack, // Activation: BEGIN_ACTIVATION, BEGIN_ACTIVATION_HARMFUL, // TODO skill traits - enfeeble, jam, mortar, siege, strike, weaken, + enfeeble, jam, mortar, siege, strike, sunder, weaken, END_ACTIVATION_HARMFUL, BEGIN_ACTIVATION_HELPFUL, - enhance, evolve, heal, mend, overload, protect, rally, + enhance, evolve, heal, mend, overload, protect, rally, rush, END_ACTIVATION_HELPFUL, END_ACTIVATION, // Defensive: BEGIN_DEFENSIVE, From 6fa616cf8f3ea36a77c8cfb668d5f07b9c85df0d Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 12 Nov 2015 13:44:00 -0500 Subject: [PATCH 376/406] Fix compiler warning sign-compare. --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 99bbe11e..50d04b1a 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1337,7 +1337,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - return ! src->m_rush_attempted && dst->m_delay >= 1 + (src->m_card->m_type == CardType::assault && dst->m_index < src->m_index); + return ! src->m_rush_attempted && dst->m_delay >= 1u + (src->m_card->m_type == CardType::assault && dst->m_index < src->m_index); } template<> From 3820d5c34b10c65531705ed8c67dff00004a2cee Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 12 Nov 2015 16:33:54 -0500 Subject: [PATCH 377/406] Fix skill Rush working with Valor. --- sim.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sim.cpp b/sim.cpp index 50d04b1a..f27100fe 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1427,6 +1427,10 @@ template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { dst->m_delay -= 1; + if (dst->m_delay == 0) + { + check_and_perform_valor(fd, dst); + } } template<> From 19606dbdc173fea93e90da0054c6503897637344 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 14 Nov 2015 21:25:34 -0500 Subject: [PATCH 378/406] Update Lilith Raid deck. --- data/raids.xml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index 9b384a84..fc95962c 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -322,17 +322,26 @@ 26 - 35130 35130 35140 + + + 35140 35140 - 35150 - 35150 35160 + + + 35130 + 35150 35160 35170 + + + 35130 + 35150 + 35160 35170 - + From 2ce1e4f57469888122a9114e06b150b7cca40dd0 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 15 Nov 2015 08:59:35 -0500 Subject: [PATCH 379/406] Update Lilith Raid deck. --- data/raids.xml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index fc95962c..60fe596c 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -324,23 +324,21 @@ 35130 35140 + 35150 + 35160 - 35140 - 35140 - 35160 - - - 35130 35150 35160 - 35170 - + 35130 - 35150 + 35130 + 35140 + 35140 35160 35170 + 35170 From 172b71d085578a4dbaeb0bcaa2e5303540f55d2b Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 29 Nov 2015 03:39:53 -0500 Subject: [PATCH 380/406] Support Campaign Mephisopheles (Meph). --- data/raids.xml | 462 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 60fe596c..9d059181 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -3887,4 +3887,466 @@ + + 911 + Meph101 + Mephisopheles Normal 1 + 1014 + 1 + + + 4456 + 4456 + 6632 + 6632 + 11090 + 16396 + 34229 + 34235 + 34241 + 34663 + + + + + + 912 + Meph102 + Mephisopheles Normal 2 + 1014 + 1 + + + 4460 + 4460 + 6636 + 6636 + 16876 + 31020 + 32821 + 34229 + 34235 + 34241 + + + + + + 913 + Meph103 + Mephisopheles Normal 3 + 1928 + 1 + + + 16398 + 30730 + 31018 + 32137 + 32823 + 34229 + 34229 + 34235 + 34241 + 34717 + + + + + + 914 + Meph104 + Mephisopheles Normal 4 + 1014 + 1 + + + 4456 + 4456 + 6632 + 14690 + 14974 + 15712 + 30224 + 34229 + 34235 + 34241 + + + + + + 915 + Meph105 + Mephisopheles Normal 5 + 1014 + 1 + + + 4460 + 6636 + 14977 + 15712 + 30224 + 33471 + 34229 + 34235 + 34241 + 35078 + + + + + + 916 + Meph106 + Mephisopheles Normal 6 + 1934 + 1 + + + 33399 + 33473 + 33557 + 34229 + 34235 + 34235 + 34241 + 34483 + 34541 + 35081 + + + + + + 917 + Meph107 + Mephisopheles Normal 7 + 1940 + 1 + + + 30799 + 31778 + 31890 + 33563 + 34126 + 34229 + 34235 + 34241 + 34241 + 34486 + + + + + + 921 + Meph201 + Mephisopheles Heroic 1 + 1037 + 1 + + + 11102 + 11102 + 16408 + 16408 + 16518 + 34231 + 34237 + 34243 + 34673 + 34795 + + + + + + 922 + Meph202 + Mephisopheles Heroic 2 + 1037 + 1 + + + 16518 + 16884 + 31024 + 32145 + 32833 + 34231 + 34237 + 34243 + 34727 + 34795 + + + + + + 923 + Meph203 + Mephisopheles Heroic 3 + 1930 + 1 + + + 14482 + 16408 + 30738 + 31020 + 31020 + 34234 + 34234 + 34237 + 34243 + 34723 + + + + + + 924 + Meph204 + Mephisopheles Heroic 4 + 1038 + 1 + + + 14698 + 14980 + 15718 + 30226 + 30226 + 31364 + 33506 + 34231 + 34237 + 34243 + + + + + + 925 + Meph205 + Mephisopheles Heroic 5 + 1038 + 1 + + + 14698 + 31364 + 32535 + 33257 + 33477 + 33509 + 34231 + 34237 + 34243 + 35084 + + + + + + 926 + Meph206 + Mephisopheles Heroic 6 + 1936 + 1 + + + 32439 + 33405 + 33563 + 34231 + 34240 + 34240 + 34243 + 34489 + 34543 + 35082 + + + + + + 927 + Meph207 + Mephisopheles Heroic 7 + 1942 + 1 + + + 30802 + 31781 + 31891 + 33563 + 34129 + 34231 + 34237 + 34243 + 34243 + 34493 + + + + + + 931 + Meph301 + Mephisopheles Mythic 1 + 1039 + 1 + + + 11107 + 16413 + 16413 + 16527 + 16527 + 30931 + 34234 + 34240 + 34246 + 34804 + + + + + + 932 + Meph302 + Mephisopheles Mythic 2 + 1039 + 1 + + + 14491 + 16893 + 30931 + 31029 + 32154 + 32154 + 32838 + 34234 + 34240 + 34246 + + + + + + 933 + Meph303 + Mephisopheles Mythic 3 + 1933 + 1 + + + 14497 + 16413 + 16413 + 31029 + 31029 + 34234 + 34234 + 34240 + 34246 + 34732 + + + + + + 934 + Meph304 + Mephisopheles Mythic 4 + 1040 + 1 + + + 14989 + 14989 + 15727 + 30235 + 30235 + 31376 + 33518 + 34234 + 34240 + 34246 + + + + + + 935 + Meph305 + Mephisopheles Mythic 5 + 1040 + 1 + + + 14707 + 30315 + 33266 + 33482 + 33518 + 33518 + 34234 + 34240 + 34246 + 35093 + + + + + + 936 + Meph306 + Mephisopheles Mythic 6 + 1939 + 1 + + + 30315 + 32454 + 33068 + 33410 + 34234 + 34240 + 34240 + 34246 + 34552 + 35093 + + + + + + 937 + Meph307 + Mephisopheles Mythic 7 + 1945 + 1 + + + 11516 + 14058 + 30811 + 31902 + 33068 + 34138 + 34234 + 34246 + 34246 + 34354 + + + + From 78cca6e55c4ff67f5b5288543dc072a7d2cf0e11 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 3 Dec 2015 13:10:08 -0500 Subject: [PATCH 381/406] Update campaign decks. --- data/raids.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/raids.xml b/data/raids.xml index 9d059181..1b4c2882 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -4342,6 +4342,7 @@ 33068 34138 34234 + 34240 34246 34246 34354 From 5c8fad543ce0a4dc169da7240afb046956fb63d1 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 3 Dec 2015 14:53:08 -0500 Subject: [PATCH 382/406] Support BGE Heroism. --- sim.cpp | 28 ++++++++++++++++++++++++++++ tyrant.cpp | 2 +- tyrant.h | 4 ++-- tyrant_optimize.cpp | 1 + 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index f27100fe..9e855f07 100644 --- a/sim.cpp +++ b/sim.cpp @@ -323,6 +323,8 @@ inline bool is_active_next_turn(CardStatus* c) { return(can_act(c) && c->m_delay inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_max_hp); } //------------------------------------------------------------------------------ bool attack_phase(Field* fd); +template +bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_status, const SkillSpec& s, bool is_evadable, bool & has_counted_quest); bool check_and_perform_valor(Field* fd, CardStatus* src_status); template void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills, bool* attacked=nullptr) @@ -500,6 +502,26 @@ Results play(Field* fd) } if(__builtin_expect(fd->end, false)) { break; } + // Evaluate Heroism Battleground skills + if (fd->bg_effects.count(heroism)) + { + for (CardStatus * status: fd->tap->assaults.m_indirect) + { + unsigned valor_value = status->skill(valor); + if (valor_value <= 0) + { continue; } + SkillSpec ss_protect{protect, valor_value, allfactions, 0, 0, no_skill, no_skill, false,}; + if (status->m_inhibited > 0 && !status->m_overloaded) + { + _DEBUG_MSG(1, "Heroism: %s %s on itself but it is inhibited\n", status_description(status).c_str(), skill_short_description(ss_protect).c_str()); + -- status->m_inhibited; + continue; + } + bool has_counted_quest = false; + check_and_perform_skill(fd, status, status, ss_protect, false, has_counted_quest); + } + } + // Evaluate activation Battleground skills for (const auto & bg_skill: fd->bg_skills) { @@ -1024,6 +1046,12 @@ struct PerformAttack } } do_leech(); + unsigned valor_value = att_status->skill(valor); + if (valor_value > 0 && fd->bg_effects.count(heroism)) + { + _DEBUG_MSG(1, "Heroism: %s gain %u attack\n", status_description(att_status).c_str(), valor_value); + att_status->m_attack += valor_value; + } prepend_on_death(fd); resolve_skill(fd); return att_dmg; diff --git a/tyrant.cpp b/tyrant.cpp index 0e0f6daa..fc5aa40f 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -30,7 +30,7 @@ std::string skill_names[Skill::num_skills] = "Flurry", "Valor", // Pseudo-skill for passive BGEs: "", - "Bloodlust", "Brigade", "Counterflux", "Divert", "EnduringRage", "Fortification", "Metamorphosis", "Reaping", "TurningTides", + "Bloodlust", "Brigade", "Counterflux", "Divert", "EnduringRage", "Fortification", "Heroism", "Metamorphosis", "Reaping", "TurningTides", "", }; diff --git a/tyrant.h b/tyrant.h index 79aef502..7ce461fd 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.13.0" +#define TYRANT_OPTIMIZER_VERSION "2.14.0" #include #include @@ -46,7 +46,7 @@ enum Skill flurry, valor, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, - bloodlust, brigade, counterflux, divert, enduringrage, fortification, metamorphosis, reaping, turningtides, + bloodlust, brigade, counterflux, divert, enduringrage, fortification, heroism, metamorphosis, reaping, turningtides, END_BGE_SKILL, num_skills }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 4df99879..06cfd2ab 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1159,6 +1159,7 @@ void print_available_effects() " Divert\n" " EnduringRage\n" " Fortification\n" + " Heroism\n" " Metamorphosis\n" " Reaping X\n" " TurningTides\n" From 221d29b02a63cd3acecccdf35286093267e5b382 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 3 Dec 2015 14:55:05 -0500 Subject: [PATCH 383/406] Rename BGE Reaping to Revenge. --- sim.cpp | 6 +++--- tyrant.cpp | 2 +- tyrant.h | 2 +- tyrant_optimize.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sim.cpp b/sim.cpp index 9e855f07..6fd884e5 100644 --- a/sim.cpp +++ b/sim.cpp @@ -280,10 +280,10 @@ void prepend_on_death(Field* fd) } } } - if (fd->bg_effects.count(reaping)) + if (fd->bg_effects.count(revenge)) { - SkillSpec ss_heal{heal, fd->bg_effects.at(reaping), allfactions, 0, 0, no_skill, no_skill, true,}; - SkillSpec ss_rally{rally, fd->bg_effects.at(reaping), allfactions, 0, 0, no_skill, no_skill, true,}; + SkillSpec ss_heal{heal, fd->bg_effects.at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; + SkillSpec ss_rally{rally, fd->bg_effects.at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; _DEBUG_MSG(2, "Reaping: Preparing %s skill %s and %s\n", status_description(status).c_str(), skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); od_skills.emplace_back(status, ss_heal); od_skills.emplace_back(status, ss_rally); diff --git a/tyrant.cpp b/tyrant.cpp index fc5aa40f..dad3eb2b 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -30,7 +30,7 @@ std::string skill_names[Skill::num_skills] = "Flurry", "Valor", // Pseudo-skill for passive BGEs: "", - "Bloodlust", "Brigade", "Counterflux", "Divert", "EnduringRage", "Fortification", "Heroism", "Metamorphosis", "Reaping", "TurningTides", + "Bloodlust", "Brigade", "Counterflux", "Divert", "EnduringRage", "Fortification", "Heroism", "Metamorphosis", "Revenge", "TurningTides", "", }; diff --git a/tyrant.h b/tyrant.h index 7ce461fd..12c45239 100644 --- a/tyrant.h +++ b/tyrant.h @@ -46,7 +46,7 @@ enum Skill flurry, valor, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, - bloodlust, brigade, counterflux, divert, enduringrage, fortification, heroism, metamorphosis, reaping, turningtides, + bloodlust, brigade, counterflux, divert, enduringrage, fortification, heroism, metamorphosis, revenge, turningtides, END_BGE_SKILL, num_skills }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 06cfd2ab..df43d072 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1161,7 +1161,7 @@ void print_available_effects() " Fortification\n" " Heroism\n" " Metamorphosis\n" - " Reaping X\n" + " Revenge X\n" " TurningTides\n" ; } From 3f19fe9aba2ee870a13b0225710ef1677c1c3622 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 3 Dec 2015 15:17:19 -0500 Subject: [PATCH 384/406] Support multiple operations again. --- tyrant_optimize.cpp | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index df43d072..9fb18e2c 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1233,7 +1233,7 @@ int main(int argc, char** argv) std::vector opt_owned_cards_str_list; bool opt_do_optimization(false); bool opt_keep_commander{false}; - std::tuple opt_todo; + std::vector> opt_todo; std::vector opt_effects; std::unordered_map opt_bg_effects; std::vector opt_bg_skills; @@ -1462,40 +1462,40 @@ int main(int argc, char** argv) } else if(strcmp(argv[argIndex], "sim") == 0) { - opt_todo = std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate); - if (std::get<0>(opt_todo) < 10) { opt_num_threads = 1; } + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), 0u, simulate)); + if (std::get<0>(opt_todo.back()) < 10) { opt_num_threads = 1; } argIndex += 1; } else if(strcmp(argv[argIndex], "climbex") == 0) { - opt_todo = std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), climb); - if (std::get<1>(opt_todo) < 10) { opt_num_threads = 1; } + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), climb)); + if (std::get<1>(opt_todo.back()) < 10) { opt_num_threads = 1; } opt_do_optimization = true; argIndex += 2; } else if(strcmp(argv[argIndex], "climb") == 0) { - opt_todo = std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), climb); - if (std::get<1>(opt_todo) < 10) { opt_num_threads = 1; } + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), climb)); + if (std::get<1>(opt_todo.back()) < 10) { opt_num_threads = 1; } opt_do_optimization = true; argIndex += 1; } else if(strcmp(argv[argIndex], "reorder") == 0) { - opt_todo = std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), reorder); - if (std::get<1>(opt_todo) < 10) { opt_num_threads = 1; } + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 1]), reorder)); + if (std::get<1>(opt_todo.back()) < 10) { opt_num_threads = 1; } argIndex += 1; } else if(strcmp(argv[argIndex], "debug") == 0) { - opt_todo = std::make_tuple(0u, 0u, debug); + opt_todo.push_back(std::make_tuple(0u, 0u, debug)); opt_num_threads = 1; } else if(strcmp(argv[argIndex], "debuguntil") == 0) { // output the debug info for the first battle that min_score <= score <= max_score. // E.g., 0 0: lose; 100 100: win (non-raid); 20 100: at least 20 damage (raid). - opt_todo = std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), debuguntil); + opt_todo.push_back(std::make_tuple((unsigned)atoi(argv[argIndex + 1]), (unsigned)atoi(argv[argIndex + 2]), debuguntil)); opt_num_threads = 1; argIndex += 2; } @@ -1961,14 +1961,15 @@ int main(int argc, char** argv) Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, quest, opt_bg_effects, opt_bg_skills); + for(auto op: opt_todo) { - switch(std::get<2>(opt_todo)) + switch(std::get<2>(op)) { case noop: break; case simulate: { EvaluatedResults results = { EvaluatedResults::first_type(enemy_decks.size()), 0 }; - results = p.evaluate(std::get<0>(opt_todo), results); + results = p.evaluate(std::get<0>(op), results); print_results(results, p.factors); break; } @@ -1976,12 +1977,12 @@ int main(int argc, char** argv) switch (opt_your_strategy) { case DeckStrategy::random: - hill_climbing(std::get<0>(opt_todo), std::get<1>(opt_todo), your_deck, p, requirement, quest); + hill_climbing(std::get<0>(op), std::get<1>(op), your_deck, p, requirement, quest); break; // case DeckStrategy::ordered: // case DeckStrategy::exact_ordered: default: - hill_climbing_ordered(std::get<0>(opt_todo), std::get<1>(opt_todo), your_deck, p, requirement, quest); + hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, requirement, quest); break; } break; @@ -1998,7 +1999,7 @@ int main(int argc, char** argv) owned_cards.clear(); claim_cards({your_deck->commander}); claim_cards(your_deck->cards); - hill_climbing_ordered(std::get<0>(opt_todo), std::get<1>(opt_todo), your_deck, p, requirement, quest); + hill_climbing_ordered(std::get<0>(op), std::get<1>(op), your_deck, p, requirement, quest); break; } case debug: { @@ -2019,7 +2020,7 @@ int main(int argc, char** argv) EvaluatedResults results{EvaluatedResults::first_type(enemy_decks.size()), 0}; results = p.evaluate(1, results); auto score = compute_score(results, p.factors); - if(score.points >= std::get<0>(opt_todo) && score.points <= std::get<1>(opt_todo)) + if(score.points >= std::get<0>(op) && score.points <= std::get<1>(op)) { std::cout << debug_str << std::flush; print_results(results, p.factors); From c507df3a69cdc961f82ccdff6b98e3998a092069 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 3 Dec 2015 16:39:50 -0500 Subject: [PATCH 385/406] Update the caster of BGE Heroism. --- sim.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sim.cpp b/sim.cpp index 6fd884e5..c4c86459 100644 --- a/sim.cpp +++ b/sim.cpp @@ -505,20 +505,20 @@ Results play(Field* fd) // Evaluate Heroism Battleground skills if (fd->bg_effects.count(heroism)) { - for (CardStatus * status: fd->tap->assaults.m_indirect) + for (CardStatus * dst_status: fd->tap->assaults.m_indirect) { - unsigned valor_value = status->skill(valor); + unsigned valor_value = dst_status->skill(valor); if (valor_value <= 0) { continue; } SkillSpec ss_protect{protect, valor_value, allfactions, 0, 0, no_skill, no_skill, false,}; - if (status->m_inhibited > 0 && !status->m_overloaded) + if (dst_status->m_inhibited > 0) { - _DEBUG_MSG(1, "Heroism: %s %s on itself but it is inhibited\n", status_description(status).c_str(), skill_short_description(ss_protect).c_str()); - -- status->m_inhibited; + _DEBUG_MSG(1, "Heroism: %s on %s but it is inhibited\n", skill_short_description(ss_protect).c_str(), status_description(dst_status).c_str()); + -- dst_status->m_inhibited; continue; } bool has_counted_quest = false; - check_and_perform_skill(fd, status, status, ss_protect, false, has_counted_quest); + check_and_perform_skill(fd, &fd->tap->commander, dst_status, ss_protect, false, has_counted_quest); } } From 097ea9f073506b1a23a5e2facf3321ab0bdad441 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 4 Dec 2015 16:29:48 -0500 Subject: [PATCH 386/406] Rename variables. --- sim.cpp | 236 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/sim.cpp b/sim.cpp index c4c86459..3f2ea8d0 100644 --- a/sim.cpp +++ b/sim.cpp @@ -284,7 +284,7 @@ void prepend_on_death(Field* fd) { SkillSpec ss_heal{heal, fd->bg_effects.at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; SkillSpec ss_rally{rally, fd->bg_effects.at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; - _DEBUG_MSG(2, "Reaping: Preparing %s skill %s and %s\n", status_description(status).c_str(), skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); + _DEBUG_MSG(2, "Revenge: Preparing skill %s and %s\n", skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); od_skills.emplace_back(status, ss_heal); od_skills.emplace_back(status, ss_rally); } @@ -293,7 +293,7 @@ void prepend_on_death(Field* fd) fd->killed_units.clear(); } //------------------------------------------------------------------------------ -void(*skill_table[num_skills])(Field*, CardStatus* src_status, const SkillSpec&); +void(*skill_table[num_skills])(Field*, CardStatus* src, const SkillSpec&); void resolve_skill(Field* fd) { while(!fd->skill_queue.empty()) @@ -324,8 +324,8 @@ inline bool can_be_healed(CardStatus* c) { return(c->m_hp > 0 && c->m_hp < c->m_ //------------------------------------------------------------------------------ bool attack_phase(Field* fd); template -bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_status, const SkillSpec& s, bool is_evadable, bool & has_counted_quest); -bool check_and_perform_valor(Field* fd, CardStatus* src_status); +bool check_and_perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s, bool is_evadable, bool & has_counted_quest); +bool check_and_perform_valor(Field* fd, CardStatus* src); template void evaluate_skills(Field* fd, CardStatus* status, const std::vector& skills, bool* attacked=nullptr) { @@ -505,20 +505,20 @@ Results play(Field* fd) // Evaluate Heroism Battleground skills if (fd->bg_effects.count(heroism)) { - for (CardStatus * dst_status: fd->tap->assaults.m_indirect) + for (CardStatus * dst: fd->tap->assaults.m_indirect) { - unsigned valor_value = dst_status->skill(valor); + unsigned valor_value = dst->skill(valor); if (valor_value <= 0) { continue; } SkillSpec ss_protect{protect, valor_value, allfactions, 0, 0, no_skill, no_skill, false,}; - if (dst_status->m_inhibited > 0) + if (dst->m_inhibited > 0) { - _DEBUG_MSG(1, "Heroism: %s on %s but it is inhibited\n", skill_short_description(ss_protect).c_str(), status_description(dst_status).c_str()); - -- dst_status->m_inhibited; + _DEBUG_MSG(1, "Heroism: %s on %s but it is inhibited\n", skill_short_description(ss_protect).c_str(), status_description(dst).c_str()); + -- dst->m_inhibited; continue; } bool has_counted_quest = false; - check_and_perform_skill(fd, &fd->tap->commander, dst_status, ss_protect, false, has_counted_quest); + check_and_perform_skill(fd, &fd->tap->commander, dst, ss_protect, false, has_counted_quest); } } @@ -1365,7 +1365,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - return ! src->m_rush_attempted && dst->m_delay >= 1u + (src->m_card->m_type == CardType::assault && dst->m_index < src->m_index); + return ! src->m_rush_attempted && dst->m_delay >= (src->m_card->m_type == CardType::assault && dst->m_index < src->m_index ? 2u : 1u); } template<> @@ -1481,25 +1481,25 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, c } template -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) +inline unsigned select_fast(Field* fd, CardStatus* src, const std::vector& cards, const SkillSpec& s) { if (s.y == allfactions || fd->bg_effects.count(metamorphosis)) { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return(skill_predicate(fd, src_status, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src, s](CardStatus* c){return(skill_predicate(fd, src, c, s));})); } else { - return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src_status, s](CardStatus* c){return((c->m_faction == s.y || c->m_faction == progenitor) && skill_predicate(fd, src_status, c, s));})); + return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src, s](CardStatus* c){return((c->m_faction == s.y || c->m_faction == progenitor) && skill_predicate(fd, src, c, s));})); } } template<> -inline unsigned select_fast(Field* fd, CardStatus* src_status, const std::vector& cards, const SkillSpec& s) +inline unsigned select_fast(Field* fd, CardStatus* src, const std::vector& cards, const SkillSpec& s) { fd->selection_array.clear(); - for (auto && adj_status: fd->adjacent_assaults(src_status)) + for (auto && adj_status: fd->adjacent_assaults(src)) { - if (skill_predicate(fd, src_status, adj_status, s)) + if (skill_predicate(fd, src, adj_status, s)) { fd->selection_array.push_back(adj_status); } @@ -1507,137 +1507,137 @@ inline unsigned select_fast(Field* fd, CardStatus* src_status, const std:: return fd->selection_array.size(); } -inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src_status) +inline std::vector& skill_targets_hostile_assault(Field* fd, CardStatus* src) { - return(fd->players[opponent(src_status->m_player)]->assaults.m_indirect); + return(fd->players[opponent(src->m_player)]->assaults.m_indirect); } -inline std::vector& skill_targets_allied_assault(Field* fd, CardStatus* src_status) +inline std::vector& skill_targets_allied_assault(Field* fd, CardStatus* src) { - return(fd->players[src_status->m_player]->assaults.m_indirect); + return(fd->players[src->m_player]->assaults.m_indirect); } -inline std::vector& skill_targets_hostile_structure(Field* fd, CardStatus* src_status) +inline std::vector& skill_targets_hostile_structure(Field* fd, CardStatus* src) { - return(fd->players[opponent(src_status->m_player)]->structures.m_indirect); + return(fd->players[opponent(src->m_player)]->structures.m_indirect); } -inline std::vector& skill_targets_allied_structure(Field* fd, CardStatus* src_status) +inline std::vector& skill_targets_allied_structure(Field* fd, CardStatus* src) { - return(fd->players[src_status->m_player]->structures.m_indirect); + return(fd->players[src->m_player]->structures.m_indirect); } template -std::vector& skill_targets(Field* fd, CardStatus* src_status) +std::vector& skill_targets(Field* fd, CardStatus* src) { std::cerr << "skill_targets: Error: no specialization for " << skill_names[skill] << "\n"; throw; } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_hostile_assault(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_allied_assault(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_allied_assault(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_allied_assault(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_hostile_assault(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_allied_assault(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_allied_assault(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_allied_assault(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_allied_assault(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_allied_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_allied_assault(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_structure(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_hostile_structure(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_hostile_assault(fd, src)); } -template<> std::vector& skill_targets(Field* fd, CardStatus* src_status) -{ return(skill_targets_hostile_assault(fd, src_status)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_hostile_assault(fd, src)); } template -bool check_and_perform_skill(Field* fd, CardStatus* src_status, CardStatus* dst_status, const SkillSpec& s, bool is_evadable, bool & has_counted_quest) +bool check_and_perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s, bool is_evadable, bool & has_counted_quest) { - if(skill_check(fd, src_status, dst_status)) + if(skill_check(fd, src, dst)) { - if (src_status->m_player == 0 && ! has_counted_quest) + if (src->m_player == 0 && ! has_counted_quest) { - fd->inc_counter(QuestType::skill_use, skill_id, dst_status->m_card->m_id); + fd->inc_counter(QuestType::skill_use, skill_id, dst->m_card->m_id); has_counted_quest = true; } if (is_evadable && - dst_status->m_evaded < dst_status->skill(evade) && - skill_check(fd, dst_status, src_status)) + dst->m_evaded < dst->skill(evade) && + skill_check(fd, dst, src)) { - ++ dst_status->m_evaded; - _DEBUG_MSG(1, "%s %s on %s but it evades\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst_status).c_str()); + ++ dst->m_evaded; + _DEBUG_MSG(1, "%s %s on %s but it evades\n", status_description(src).c_str(), skill_short_description(s).c_str(), status_description(dst).c_str()); return(false); } - _DEBUG_MSG(1, "%s %s on %s\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst_status).c_str()); - perform_skill(fd, src_status, dst_status, s); + _DEBUG_MSG(1, "%s %s on %s\n", status_description(src).c_str(), skill_short_description(s).c_str(), status_description(dst).c_str()); + perform_skill(fd, src, dst, s); if (s.c > 0) { - src_status->m_skill_cd[skill_id] = s.c; + src->m_skill_cd[skill_id] = s.c; } return(true); } - _DEBUG_MSG(1, "(CANCELLED) %s %s on %s\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst_status).c_str()); + _DEBUG_MSG(1, "(CANCELLED) %s %s on %s\n", status_description(src).c_str(), skill_short_description(s).c_str(), status_description(dst).c_str()); return(false); } -bool check_and_perform_valor(Field* fd, CardStatus* src_status) +bool check_and_perform_valor(Field* fd, CardStatus* src) { - unsigned valor_value = src_status->skill(valor); - if (valor_value > 0 && skill_check(fd, src_status, nullptr)) + unsigned valor_value = src->skill(valor); + if (valor_value > 0 && skill_check(fd, src, nullptr)) { - unsigned opponent_player = opponent(src_status->m_player); - const CardStatus * dst_status = fd->players[opponent_player]->assaults.size() > src_status->m_index ? - &fd->players[opponent_player]->assaults[src_status->m_index] : + unsigned opponent_player = opponent(src->m_player); + const CardStatus * dst = fd->players[opponent_player]->assaults.size() > src->m_index ? + &fd->players[opponent_player]->assaults[src->m_index] : nullptr; - if (dst_status == nullptr || dst_status->m_hp <= 0) + if (dst == nullptr || dst->m_hp <= 0) { - _DEBUG_MSG(1, "%s loses Valor (no blocker)\n", status_description(src_status).c_str()); + _DEBUG_MSG(1, "%s loses Valor (no blocker)\n", status_description(src).c_str()); return false; } - else if (attack_power(dst_status) <= attack_power(src_status)) + else if (attack_power(dst) <= attack_power(src)) { - _DEBUG_MSG(1, "%s loses Valor (weak blocker %s)\n", status_description(src_status).c_str(), status_description(dst_status).c_str()); + _DEBUG_MSG(1, "%s loses Valor (weak blocker %s)\n", status_description(src).c_str(), status_description(dst).c_str()); return false; } - if (src_status->m_player == 0) + if (src->m_player == 0) { fd->inc_counter(QuestType::skill_use, valor); } - _DEBUG_MSG(1, "%s activates Valor %u\n", status_description(src_status).c_str(), valor_value); - src_status->m_attack += valor_value; + _DEBUG_MSG(1, "%s activates Valor %u\n", status_description(src).c_str(), valor_value); + src->m_attack += valor_value; return true; } return false; } template -size_t select_targets(Field* fd, CardStatus* src_status, const SkillSpec& s) +size_t select_targets(Field* fd, CardStatus* src, const SkillSpec& s) { - std::vector& cards(skill_targets(fd, src_status)); - size_t n_candidates = select_fast(fd, src_status, cards, s); + std::vector& cards(skill_targets(fd, src)); + size_t n_candidates = select_fast(fd, src, cards, s); if (n_candidates == 0) { return n_candidates; @@ -1661,12 +1661,12 @@ size_t select_targets(Field* fd, CardStatus* src_status, const SkillSpec& s) } template<> -size_t select_targets(Field* fd, CardStatus* src_status, const SkillSpec& s) +size_t select_targets(Field* fd, CardStatus* src, const SkillSpec& s) { - size_t n_candidates = select_fast(fd, src_status, skill_targets(fd, src_status), s); + size_t n_candidates = select_fast(fd, src, skill_targets(fd, src), s); if (n_candidates == 0) { - n_candidates = select_fast(fd, src_status, skill_targets(fd, src_status), s); + n_candidates = select_fast(fd, src, skill_targets(fd, src), s); if (n_candidates == 0) { return n_candidates; @@ -1691,21 +1691,21 @@ size_t select_targets(Field* fd, CardStatus* src_status, const SkillSpec } template -void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +void perform_targetted_allied_fast(Field* fd, CardStatus* src, const SkillSpec& s) { - select_targets(fd, src_status, s); + select_targets(fd, src, s); unsigned num_inhibited = 0; bool has_counted_quest = false; for (CardStatus * dst: fd->selection_array) { - if (dst->m_inhibited > 0 && !src_status->m_overloaded) + if (dst->m_inhibited > 0 && !src->m_overloaded) { - _DEBUG_MSG(1, "%s %s on %s but it is inhibited\n", status_description(src_status).c_str(), skill_short_description(s).c_str(), status_description(dst).c_str()); + _DEBUG_MSG(1, "%s %s on %s but it is inhibited\n", status_description(src).c_str(), skill_short_description(s).c_str(), status_description(dst).c_str()); -- dst->m_inhibited; ++ num_inhibited; continue; } - check_and_perform_skill(fd, src_status, dst, s, false, has_counted_quest); + check_and_perform_skill(fd, src, dst, s, false, has_counted_quest); } if (num_inhibited > 0 && fd->bg_effects.count(divert)) { @@ -1720,49 +1720,49 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src_status, const Skil { if (dst->m_inhibited > 0) { - _DEBUG_MSG(1, "%s %s (Diverted) on %s but it is inhibited\n", status_description(src_status).c_str(), skill_short_description(diverted_ss).c_str(), status_description(dst).c_str()); + _DEBUG_MSG(1, "%s %s (Diverted) on %s but it is inhibited\n", status_description(src).c_str(), skill_short_description(diverted_ss).c_str(), status_description(dst).c_str()); -- dst->m_inhibited; continue; } - _DEBUG_MSG(1, "%s %s (Diverted) on %s\n", status_description(src_status).c_str(), skill_short_description(diverted_ss).c_str(), status_description(dst).c_str()); - perform_skill(fd, src_status, dst, diverted_ss); + _DEBUG_MSG(1, "%s %s (Diverted) on %s\n", status_description(src).c_str(), skill_short_description(diverted_ss).c_str(), status_description(dst).c_str()); + perform_skill(fd, src, dst, diverted_ss); } } } } -void perform_targetted_allied_fast_rush(Field* fd, CardStatus* src_status, const SkillSpec& s) +void perform_targetted_allied_fast_rush(Field* fd, CardStatus* src, const SkillSpec& s) { - if (src_status->m_rush_attempted) + if (src->m_rush_attempted) { - _DEBUG_MSG(2, "%s does not check Rush again.\n", status_description(src_status).c_str()); + _DEBUG_MSG(2, "%s does not check Rush again.\n", status_description(src).c_str()); return; } - _DEBUG_MSG(1, "%s attempts to activate Rush.\n", status_description(src_status).c_str()); - perform_targetted_allied_fast(fd, src_status, s); - src_status->m_rush_attempted = true; + _DEBUG_MSG(1, "%s attempts to activate Rush.\n", status_description(src).c_str()); + perform_targetted_allied_fast(fd, src, s); + src->m_rush_attempted = true; } template -void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const SkillSpec& s) +void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& s) { - select_targets(fd, src_status, s); + select_targets(fd, src, s); bool has_counted_quest = false; std::vector paybackers; if (fd->bg_effects.count(turningtides) && skill_id == weaken) { unsigned turningtides_value = 0; - for (CardStatus * dst_status: fd->selection_array) + for (CardStatus * dst: fd->selection_array) { - unsigned old_attack = attack_power(dst_status); - if (check_and_perform_skill(fd, src_status, dst_status, s, ! src_status->m_overloaded, has_counted_quest)) + unsigned old_attack = attack_power(dst); + if (check_and_perform_skill(fd, src, dst, s, ! src->m_overloaded, has_counted_quest)) { - turningtides_value = std::max(turningtides_value, safe_minus(old_attack, attack_power(dst_status))); + turningtides_value = std::max(turningtides_value, safe_minus(old_attack, attack_power(dst))); // Payback - if(dst_status->m_paybacked < dst_status->skill(payback) && skill_check(fd, dst_status, src_status) && - skill_predicate(fd, src_status, src_status, s) && skill_check(fd, src_status, dst_status)) + if(dst->m_paybacked < dst->skill(payback) && skill_check(fd, dst, src) && + skill_predicate(fd, src, src, s) && skill_check(fd, src, dst)) { - paybackers.push_back(dst_status); + paybackers.push_back(dst); } } } @@ -1770,15 +1770,15 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski { SkillSpec ss_rally{rally, turningtides_value, allfactions, 0, 0, no_skill, no_skill, s.all,}; _DEBUG_MSG(1, "TurningTides %u!\n", turningtides_value); - perform_targetted_allied_fast(fd, src_status, ss_rally); + perform_targetted_allied_fast(fd, src, ss_rally); } for (CardStatus * pb_status: paybackers) { ++ pb_status->m_paybacked; - unsigned old_attack = attack_power(src_status); - _DEBUG_MSG(1, "%s Payback %s on %s\n", status_description(pb_status).c_str(), skill_short_description(s).c_str(), status_description(src_status).c_str()); - perform_skill(fd, pb_status, src_status, s); - turningtides_value = std::max(turningtides_value, safe_minus(old_attack, attack_power(src_status))); + unsigned old_attack = attack_power(src); + _DEBUG_MSG(1, "%s Payback %s on %s\n", status_description(pb_status).c_str(), skill_short_description(s).c_str(), status_description(src).c_str()); + perform_skill(fd, pb_status, src, s); + turningtides_value = std::max(turningtides_value, safe_minus(old_attack, attack_power(src))); if (turningtides_value > 0) { SkillSpec ss_rally{rally, turningtides_value, allfactions, 0, 0, no_skill, no_skill, false,}; @@ -1789,23 +1789,23 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src_status, const Ski prepend_on_death(fd); return; } - for (CardStatus * dst_status: fd->selection_array) + for (CardStatus * dst: fd->selection_array) { - if (check_and_perform_skill(fd, src_status, dst_status, s, ! src_status->m_overloaded, has_counted_quest)) + if (check_and_perform_skill(fd, src, dst, s, ! src->m_overloaded, has_counted_quest)) { // Payback - if(dst_status->m_paybacked < dst_status->skill(payback) && skill_check(fd, dst_status, src_status) && - skill_predicate(fd, src_status, src_status, s) && skill_check(fd, src_status, dst_status)) + if(dst->m_paybacked < dst->skill(payback) && skill_check(fd, dst, src) && + skill_predicate(fd, src, src, s) && skill_check(fd, src, dst)) { - paybackers.push_back(dst_status); + paybackers.push_back(dst); } } } for (CardStatus * pb_status: paybackers) { ++ pb_status->m_paybacked; - _DEBUG_MSG(1, "%s Payback %s on %s\n", status_description(pb_status).c_str(), skill_short_description(s).c_str(), status_description(src_status).c_str()); - perform_skill(fd, pb_status, src_status, s); + _DEBUG_MSG(1, "%s Payback %s on %s\n", status_description(pb_status).c_str(), skill_short_description(s).c_str(), status_description(src).c_str()); + perform_skill(fd, pb_status, src, s); } prepend_on_death(fd); } From dc89eb378f0da0ad6bc593e76e39f61c28e60d55 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 4 Dec 2015 16:37:54 -0500 Subject: [PATCH 387/406] Update the caster of skills triggered by BGE (Revenue and Turning Tides) as the commander. Commander here is equivalent to a virtual "battleground" caster except for skill Rush, which is behavior-undefined on commander and BGE. --- sim.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sim.cpp b/sim.cpp index 3f2ea8d0..5ffc3260 100644 --- a/sim.cpp +++ b/sim.cpp @@ -284,9 +284,10 @@ void prepend_on_death(Field* fd) { SkillSpec ss_heal{heal, fd->bg_effects.at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; SkillSpec ss_rally{rally, fd->bg_effects.at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; + CardStatus * commander = &fd->players[status->m_player]->commander; _DEBUG_MSG(2, "Revenge: Preparing skill %s and %s\n", skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); - od_skills.emplace_back(status, ss_heal); - od_skills.emplace_back(status, ss_rally); + od_skills.emplace_back(commander, ss_heal); + od_skills.emplace_back(commander, ss_rally); } } fd->skill_queue.insert(fd->skill_queue.begin(), od_skills.begin(), od_skills.end()); @@ -1770,7 +1771,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& { SkillSpec ss_rally{rally, turningtides_value, allfactions, 0, 0, no_skill, no_skill, s.all,}; _DEBUG_MSG(1, "TurningTides %u!\n", turningtides_value); - perform_targetted_allied_fast(fd, src, ss_rally); + perform_targetted_allied_fast(fd, &fd->players[src->m_player]->commander, ss_rally); } for (CardStatus * pb_status: paybackers) { @@ -1783,7 +1784,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& { SkillSpec ss_rally{rally, turningtides_value, allfactions, 0, 0, no_skill, no_skill, false,}; _DEBUG_MSG(1, "Paybacked TurningTides %u!\n", turningtides_value); - perform_targetted_allied_fast(fd, pb_status, ss_rally); + perform_targetted_allied_fast(fd, &fd->players[pb_status->m_player]->commander, ss_rally); } } prepend_on_death(fd); From 8f6d3fafbed8e08fb06b4aae3d849669de7ea8ca Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 9 Dec 2015 16:33:38 -0500 Subject: [PATCH 388/406] Update BGE Heroism. --- sim.cpp | 6 +++--- tyrant.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sim.cpp b/sim.cpp index 5ffc3260..4e8cb7af 100644 --- a/sim.cpp +++ b/sim.cpp @@ -508,10 +508,10 @@ Results play(Field* fd) { for (CardStatus * dst: fd->tap->assaults.m_indirect) { - unsigned valor_value = dst->skill(valor); - if (valor_value <= 0) + unsigned bge_value = (dst->skill(valor) + 1) / 2; + if (bge_value <= 0) { continue; } - SkillSpec ss_protect{protect, valor_value, allfactions, 0, 0, no_skill, no_skill, false,}; + SkillSpec ss_protect{protect, bge_value, allfactions, 0, 0, no_skill, no_skill, false,}; if (dst->m_inhibited > 0) { _DEBUG_MSG(1, "Heroism: %s on %s but it is inhibited\n", skill_short_description(ss_protect).c_str(), status_description(dst).c_str()); diff --git a/tyrant.h b/tyrant.h index 12c45239..70713d3f 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.14.0" +#define TYRANT_OPTIMIZER_VERSION "2.14.1" #include #include From 7b4caa638a651ecdc7879124a61d483d1e770299 Mon Sep 17 00:00:00 2001 From: Stephan Guenther Date: Thu, 17 Dec 2015 21:56:39 +0100 Subject: [PATCH 389/406] Fix `heroism` BGE Thanks to "othermaciej" who pointed out what is wrong and posted the patch [here][0]. [0]: www.kongregate.com/forums/2468/topics/426677?page=78#posts-9976727 --- sim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim.cpp b/sim.cpp index 4e8cb7af..67fc3a77 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1048,7 +1048,7 @@ struct PerformAttack } do_leech(); unsigned valor_value = att_status->skill(valor); - if (valor_value > 0 && fd->bg_effects.count(heroism)) + if (valor_value > 0 && fd->bg_effects.count(heroism) && def_cardtype == CardType::assault && def_status->m_hp <= 0) { _DEBUG_MSG(1, "Heroism: %s gain %u attack\n", status_description(att_status).c_str(), valor_value); att_status->m_attack += valor_value; From 51e2f056314abf90fe1cdc9ecab52b28418f8dbd Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 20 Dec 2015 03:29:44 -0500 Subject: [PATCH 390/406] v2.14.2 --- tyrant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tyrant.h b/tyrant.h index 70713d3f..89180525 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.14.1" +#define TYRANT_OPTIMIZER_VERSION "2.14.2" #include #include From a6a0a99d6d0e49327dac6097488196b1e68f3e14 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sun, 20 Dec 2015 03:52:36 -0500 Subject: [PATCH 391/406] Add cards_section_9.xml. --- update_xml.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update_xml.sh b/update_xml.sh index 72cfd1ee..e5ecc778 100755 --- a/update_xml.sh +++ b/update_xml.sh @@ -1,4 +1,4 @@ #!/bin/bash -for fn in fusion_recipes_cj2 missions skills_set `seq -f cards_section_%g 1 8` ; do +for fn in fusion_recipes_cj2 missions skills_set `seq -f cards_section_%g 1 9` ; do curl http://mobile$1.tyrantonline.com/assets/${fn}.xml -R -z data/${fn}.xml -o data/${fn}.xml done From a6bb74adf9212e3c554f484ead4910d249293f04 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Tue, 22 Dec 2015 13:37:05 -0500 Subject: [PATCH 392/406] Support fortress in Mission deck. --- deck.cpp | 11 ++++++-- deck.h | 1 + sim.cpp | 2 +- tyrant.h | 2 +- tyrant_optimize.cpp | 2 +- xml.cpp | 62 ++++++++++++++++++++++----------------------- 6 files changed, 43 insertions(+), 37 deletions(-) diff --git a/deck.cpp b/deck.cpp index 680ed9e7..1ae61097 100644 --- a/deck.cpp +++ b/deck.cpp @@ -401,10 +401,11 @@ std::string Deck::long_description() const show_upgrades(ios, card, card->m_top_level_card->m_level, " "); } } - for (const Card * fort: fort_cards) + for (const Card * card: fort_cards) { - ios << card_description(all_cards, fort) << "\n"; + show_upgrades(ios, card, card->m_top_level_card->m_level, ""); } + ios << "\n"; return ios.str(); } @@ -511,6 +512,8 @@ const Card* Deck::upgrade_card(const Card* card, unsigned card_max_level, std::m void Deck::shuffle(std::mt19937& re) { shuffled_commander = commander; + shuffled_forts.clear(); + boost::insert(shuffled_forts, shuffled_forts.end(), fort_cards); shuffled_cards.clear(); boost::insert(shuffled_cards, shuffled_cards.end(), cards); if(!variable_cards.empty()) @@ -537,6 +540,10 @@ void Deck::shuffle(std::mt19937& re) unsigned remaining_upgrade_points = upgrade_points; unsigned remaining_upgrade_opportunities = upgrade_opportunities; shuffled_commander = upgrade_card(commander, commander_max_level, re, remaining_upgrade_points, remaining_upgrade_opportunities); + for (auto && card: shuffled_forts) + { + card = upgrade_card(card, card->m_top_level_card->m_level, re, remaining_upgrade_points, remaining_upgrade_opportunities); + } for (auto && card: shuffled_cards) { card = upgrade_card(card, card->m_top_level_card->m_level, re, remaining_upgrade_points, remaining_upgrade_opportunities); diff --git a/deck.h b/deck.h index 693121aa..3fc88667 100644 --- a/deck.h +++ b/deck.h @@ -56,6 +56,7 @@ class Deck std::map card_marks; // : -1 indicating the commander. E.g, used as a mark to be kept in attacking deck when optimizing. const Card* shuffled_commander; + std::deque shuffled_forts; std::deque shuffled_cards; // card id -> card order diff --git a/sim.cpp b/sim.cpp index 67fc3a77..5c67752f 100644 --- a/sim.cpp +++ b/sim.cpp @@ -466,7 +466,7 @@ Results play(Field* fd) // Play fortresses for (unsigned _ = 0; _ < 2; ++ _) { - for (const Card* played_card: fd->tap->deck->fort_cards) + for (const Card* played_card: fd->tap->deck->shuffled_forts) { PlayCard(played_card, fd).op(); } diff --git a/tyrant.h b/tyrant.h index 89180525..8bd221e4 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.14.2" +#define TYRANT_OPTIMIZER_VERSION "2.15.0" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 9fb18e2c..5e48ab51 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1509,7 +1509,7 @@ int main(int argc, char** argv) Cards all_cards; Decks decks; load_skills_set_xml(all_cards, "data/skills_set.xml", true); - for (unsigned section = 1; section <= 9; ++ section) + for (unsigned section = 1; section <= 10; ++ section) { load_cards_xml(all_cards, "data/cards_section_" + to_string(section) + ".xml", false); } diff --git a/xml.cpp b/xml.cpp index 342db0dd..f61876ac 100644 --- a/xml.cpp +++ b/xml.cpp @@ -151,39 +151,24 @@ void parse_card_node(Cards& all_cards, Card* card, xml_node<>* card_node) if (cost_node) { card->m_delay = atoi(cost_node->value()); } if (id_node) { - if (cost_node) - { - if (attack_node) - { - if (card->m_attack == 0) - { - if (card->m_id < 1000) - { card->m_type = CardType::assault; } - else if (card->m_id < 2000) - { card->m_type = CardType::commander; } - else if (card->m_id < 3000) - { card->m_type = CardType::structure; } - else if (card->m_id < 8000) - { card->m_type = CardType::assault; } - else if (card->m_id < 10000) - { card->m_type = CardType::structure; } - else if (card->m_id < 17000) - { card->m_type = CardType::assault; } - else if (card->m_id < 25000) - { card->m_type = CardType::structure; } - else if (card->m_id < 30000) - { card->m_type = CardType::commander; } - else - { card->m_type = CardType::assault; } - } - else // attack > 0: must be assault - { card->m_type = CardType::assault; } - } - else // no attack_node: must be structure - { card->m_type = CardType::structure; } - } - else // no cost_node: must be commander + if (card->m_id < 1000) + { card->m_type = CardType::assault; } + else if (card->m_id < 2000) + { card->m_type = CardType::commander; } + else if (card->m_id < 3000) + { card->m_type = CardType::structure; } + else if (card->m_id < 8000) + { card->m_type = CardType::assault; } + else if (card->m_id < 10000) + { card->m_type = CardType::structure; } + else if (card->m_id < 17000) + { card->m_type = CardType::assault; } + else if (card->m_id < 25000) + { card->m_type = CardType::structure; } + else if (card->m_id < 30000) { card->m_type = CardType::commander; } + else + { card->m_type = CardType::assault; } } if(rarity_node) { card->m_rarity = atoi(rarity_node->value()); } if(type_node) { card->m_faction = static_cast(atoi(type_node->value())); } @@ -287,6 +272,15 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType xml_node<>* commander_max_level_node(node->first_node("commander_max_level")); unsigned commander_max_level = commander_max_level_node ? atoi(commander_max_level_node->value()) : commander_card->m_top_level_card->m_level; unsigned upgrade_opportunities = commander_max_level - card->m_level; + std::vector fort_cards; + for (xml_node<>* fortress_card_node = node->first_node("fortress_card"); + fortress_card_node; + fortress_card_node = fortress_card_node->next_sibling("fortress_card")) + { + const Card * card = all_cards.by_id(atoi(fortress_card_node->first_attribute("id")->value())); + fort_cards.push_back(card); + upgrade_opportunities += card->m_top_level_card->m_level - card->m_level; + } std::vector always_cards; std::vector>> some_cards; xml_node<>* deck_node(node->first_node("deck")); @@ -329,6 +323,7 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType decks.decks.push_back(Deck{all_cards, decktype, id, deck_name, (upgrade_opportunities + 1) * (level - 1) / (max_level - 1), upgrade_opportunities}); Deck* deck = &decks.decks.back(); deck->set(commander_card, commander_max_level, always_cards, some_cards, mission_req); + deck->fort_cards = fort_cards; decks.add_deck(deck, deck_name); decks.add_deck(deck, decktype_names[decktype] + " #" + to_string(id) + "-" + to_string(level)); } @@ -336,12 +331,15 @@ Deck* read_deck(Decks& decks, const Cards& all_cards, xml_node<>* node, DeckType decks.decks.push_back(Deck{all_cards, decktype, id, base_deck_name}); Deck* deck = &decks.decks.back(); deck->set(commander_card, commander_max_level, always_cards, some_cards, mission_req); + deck->fort_cards = fort_cards; // upgrade cards for full-level missions/raids if (max_level > 1) { while (deck->commander->m_level < commander_max_level) { deck->commander = deck->commander->upgraded(); } + for (auto && card: deck->fort_cards) + { card = card->m_top_level_card; } for (auto && card: deck->cards) { card = card->m_top_level_card; } for (auto && pool: deck->variable_cards) From e66b85fe2bb14e55addfd8f00ebc94723ecd1935 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 6 Jan 2016 02:21:34 -0500 Subject: [PATCH 393/406] Add skill Sunder. --- deck.cpp | 15 ++++--- deck.h | 2 +- sim.cpp | 102 +++++++++++++++++++++++++++----------------- sim.h | 1 + tyrant.h | 2 +- tyrant_optimize.cpp | 4 +- 6 files changed, 78 insertions(+), 48 deletions(-) diff --git a/deck.cpp b/deck.cpp index 1ae61097..5a7d5176 100644 --- a/deck.cpp +++ b/deck.cpp @@ -296,10 +296,9 @@ void Deck::set_given_hand(const std::string& deck_string) given_hand = id_marks.first; } -void Deck::set_forts(const std::string& deck_string) +void Deck::add_forts(const std::string& deck_string) { auto && id_marks = string_to_ids(all_cards, deck_string, "fort_cards"); - fort_cards.clear(); for (auto id: id_marks.first) { fort_cards.push_back(all_cards.by_id(id)); @@ -351,6 +350,10 @@ std::string Deck::medium_description() const { ios << "No commander"; } + for (const Card * card: fort_cards) + { + ios << ", " << card->m_name; + } for(const Card * card: cards) { ios << ", " << card->m_name; @@ -385,6 +388,10 @@ std::string Deck::long_description() const { ios << "No commander\n"; } + for (const Card * card: fort_cards) + { + show_upgrades(ios, card, card->m_top_level_card->m_level, ""); + } for(const Card* card: cards) { show_upgrades(ios, card, card->m_top_level_card->m_level, " "); @@ -401,10 +408,6 @@ std::string Deck::long_description() const show_upgrades(ios, card, card->m_top_level_card->m_level, " "); } } - for (const Card * card: fort_cards) - { - show_upgrades(ios, card, card->m_top_level_card->m_level, ""); - } ios << "\n"; return ios.str(); } diff --git a/deck.h b/deck.h index 3fc88667..3ab5f41e 100644 --- a/deck.h +++ b/deck.h @@ -124,7 +124,7 @@ class Deck void shrink(const unsigned deck_len); void set_vip_cards(const std::string& deck_string_); void set_given_hand(const std::string& deck_string_); - void set_forts(const std::string& deck_string_); + void add_forts(const std::string& deck_string_); Deck* clone() const; std::string hash() const; diff --git a/sim.cpp b/sim.cpp index 5c67752f..a37469c8 100644 --- a/sim.cpp +++ b/sim.cpp @@ -117,6 +117,7 @@ inline void CardStatus::set(const Card& card) m_protected = 0; m_rallied = 0; m_rush_attempted = false; + m_sundered = false; m_weakened = 0; std::memset(m_primary_skill_offset, 0, sizeof m_primary_skill_offset); @@ -210,6 +211,7 @@ std::string CardStatus::description() const // Status w/o value if(m_jammed) { desc += ", jammed"; } if(m_overloaded) { desc += ", overloaded"; } + if(m_sundered) { desc += ", sundered"; } // Status w/ value if(m_corroded_rate > 0) { desc += ", corroded " + to_string(m_corroded_rate); } if(m_enfeebled > 0) { desc += ", enfeebled " + to_string(m_enfeebled); } @@ -274,7 +276,8 @@ void prepend_on_death(Field* fd) if (avenge_value > 0) { _DEBUG_MSG(1, "%s activates Avenge %u\n", status_description(adj_status).c_str(), avenge_value); - adj_status->m_attack += avenge_value; + if (! adj_status->m_sundered) + { adj_status->m_attack += avenge_value; } adj_status->m_max_hp += avenge_value; adj_status->m_hp += avenge_value; } @@ -914,6 +917,7 @@ void turn_end_phase(Field* fd) // end of the opponent's next turn for enemy units status.m_jammed = false; status.m_rallied = 0; + status.m_sundered = false; status.m_weakened = 0; status.m_inhibited = 0; status.m_overloaded = false; @@ -1018,7 +1022,8 @@ struct PerformAttack unsigned flux_value = (def_status->skill(counter) - 1) / flux_denominator + 1; _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n", status_description(def_status).c_str(), flux_value); add_hp(fd, def_status, flux_value); - def_status->m_attack += flux_value; + if (! def_status->m_sundered) + { def_status->m_attack += flux_value; } } } unsigned corrosive_value = def_status->skill(corrosive); @@ -1029,7 +1034,7 @@ struct PerformAttack att_status->m_corroded_rate = corrosive_value; } unsigned berserk_value = att_status->skill(berserk); - if (att_status->m_hp > 0 && berserk_value > 0 && skill_check(fd, att_status, nullptr)) + if (att_status->m_hp > 0 && ! att_status->m_sundered && berserk_value > 0 && skill_check(fd, att_status, nullptr)) { // perform_skill_berserk att_status->m_attack += berserk_value; @@ -1048,7 +1053,7 @@ struct PerformAttack } do_leech(); unsigned valor_value = att_status->skill(valor); - if (valor_value > 0 && fd->bg_effects.count(heroism) && def_cardtype == CardType::assault && def_status->m_hp <= 0) + if (valor_value > 0 && ! att_status->m_sundered && fd->bg_effects.count(heroism) && def_cardtype == CardType::assault && def_status->m_hp <= 0) { _DEBUG_MSG(1, "Heroism: %s gain %u attack\n", status_description(att_status).c_str(), valor_value); att_status->m_attack += valor_value; @@ -1063,44 +1068,48 @@ struct PerformAttack { assert(att_status->m_card->m_type == CardType::assault); att_dmg = pre_modifier_dmg; - if (att_dmg == 0) { return; } + if (att_dmg == 0) + { return; } std::string desc; - // enhance damage unsigned legion_value = 0; - unsigned legion_base = att_status->skill(legion); - if (legion_base > 0) + if (! att_status->m_sundered) { - auto & assaults = fd->tap->assaults; - legion_value += att_status->m_index > 0 && assaults[att_status->m_index - 1].m_hp > 0 && assaults[att_status->m_index - 1].m_faction == att_status->m_faction; - legion_value += att_status->m_index + 1 < assaults.size() && assaults[att_status->m_index + 1].m_hp > 0 && assaults[att_status->m_index + 1].m_faction == att_status->m_faction; - if (legion_value > 0 && skill_check(fd, att_status, nullptr)) + // enhance damage + unsigned legion_base = att_status->skill(legion); + if (legion_base > 0) { - legion_value *= legion_base; - if (debug_print > 0) { desc += "+" + to_string(legion_value) + "(legion)"; } - att_dmg += legion_value; + auto & assaults = fd->tap->assaults; + legion_value += att_status->m_index > 0 && assaults[att_status->m_index - 1].m_hp > 0 && assaults[att_status->m_index - 1].m_faction == att_status->m_faction; + legion_value += att_status->m_index + 1 < assaults.size() && assaults[att_status->m_index + 1].m_hp > 0 && assaults[att_status->m_index + 1].m_faction == att_status->m_faction; + if (legion_value > 0 && skill_check(fd, att_status, nullptr)) + { + legion_value *= legion_base; + if (debug_print > 0) { desc += "+" + to_string(legion_value) + "(legion)"; } + att_dmg += legion_value; + } + } + unsigned rupture_value = att_status->skill(rupture); + if (rupture_value > 0) + { + if (debug_print > 0) { desc += "+" + to_string(rupture_value) + "(rupture)"; } + att_dmg += rupture_value; + } + unsigned venom_value = att_status->skill(venom); + if (venom_value > 0 && def_status->m_poisoned > 0) + { + if (debug_print > 0) { desc += "+" + to_string(venom_value) + "(venom)"; } + att_dmg += venom_value; + } + if (fd->bloodlust_value > 0) + { + if (debug_print > 0) { desc += "+" + to_string(fd->bloodlust_value) + "(bloodlust)"; } + att_dmg += fd->bloodlust_value; + } + if(def_status->m_enfeebled > 0) + { + if(debug_print > 0) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } + att_dmg += def_status->m_enfeebled; } - } - unsigned rupture_value = att_status->skill(rupture); - if (rupture_value > 0) - { - if (debug_print > 0) { desc += "+" + to_string(rupture_value) + "(rupture)"; } - att_dmg += rupture_value; - } - unsigned venom_value = att_status->skill(venom); - if (venom_value > 0 && def_status->m_poisoned > 0) - { - if (debug_print > 0) { desc += "+" + to_string(venom_value) + "(venom)"; } - att_dmg += venom_value; - } - if (fd->bloodlust_value > 0) - { - if (debug_print > 0) { desc += "+" + to_string(fd->bloodlust_value) + "(bloodlust)"; } - att_dmg += fd->bloodlust_value; - } - if(def_status->m_enfeebled > 0) - { - if(debug_print > 0) { desc += "+" + to_string(def_status->m_enfeebled) + "(enfeebled)"; } - att_dmg += def_status->m_enfeebled; } // prevent damage std::string reduced_desc; @@ -1369,6 +1378,12 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, c return ! src->m_rush_attempted && dst->m_delay >= (src->m_card->m_type == CardType::assault && dst->m_index < src->m_index ? 2u : 1u); } +template<> +inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ + return attack_power(dst) > 0 && is_active_next_turn(dst); +} + template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { @@ -1475,6 +1490,13 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, c remove_hp(fd, dst, strike_dmg); } +template<> +inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) +{ + dst->m_sundered = true; + dst->m_weakened += std::min(s.x, attack_power(dst)); +} + template<> inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { @@ -1571,6 +1593,9 @@ template<> std::vector& skill_targets(Field* fd, CardStatus* template<> std::vector& skill_targets(Field* fd, CardStatus* src) { return(skill_targets_hostile_assault(fd, src)); } +template<> std::vector& skill_targets(Field* fd, CardStatus* src) +{ return(skill_targets_hostile_assault(fd, src)); } + template<> std::vector& skill_targets(Field* fd, CardStatus* src) { return(skill_targets_hostile_assault(fd, src)); } @@ -1607,7 +1632,7 @@ bool check_and_perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const bool check_and_perform_valor(Field* fd, CardStatus* src) { unsigned valor_value = src->skill(valor); - if (valor_value > 0 && skill_check(fd, src, nullptr)) + if (valor_value > 0 && ! src->m_sundered && skill_check(fd, src, nullptr)) { unsigned opponent_player = opponent(src->m_player); const CardStatus * dst = fd->players[opponent_player]->assaults.size() > src->m_index ? @@ -1828,5 +1853,6 @@ void fill_skill_table() skill_table[rush] = perform_targetted_allied_fast_rush; skill_table[siege] = perform_targetted_hostile_fast; skill_table[strike] = perform_targetted_hostile_fast; + skill_table[sunder] = perform_targetted_hostile_fast; skill_table[weaken] = perform_targetted_hostile_fast; } diff --git a/sim.h b/sim.h index e5f100ff..d6215d62 100644 --- a/sim.h +++ b/sim.h @@ -157,6 +157,7 @@ struct CardStatus unsigned m_protected; unsigned m_rallied; bool m_rush_attempted; + bool m_sundered; unsigned m_weakened; signed m_primary_skill_offset[num_skills]; diff --git a/tyrant.h b/tyrant.h index 8bd221e4..50a28b77 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.15.0" +#define TYRANT_OPTIMIZER_VERSION "2.16.0" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 5e48ab51..a2c516cb 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1673,7 +1673,7 @@ int main(int argc, char** argv) { try { - your_deck->set_forts(opt_forts + ","); + your_deck->add_forts(opt_forts + ","); } catch(const std::runtime_error& e) { @@ -1893,7 +1893,7 @@ int main(int argc, char** argv) { try { - enemy_deck->set_forts(opt_enemy_forts + ","); + enemy_deck->add_forts(opt_enemy_forts + ","); } catch(const std::runtime_error& e) { From a6486089614dba8a359c5cc579b89b116b23798b Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 6 Jan 2016 02:27:44 -0500 Subject: [PATCH 394/406] Support Raid #16 "Calamity Raid". --- data/raids.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 1b4c2882..72f15d20 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -343,6 +343,27 @@ + + 16 + Calamity Raid + 1986 + 26 + + + 36344 + 36344 + 36354 + 36354 + 36364 + 36364 + 36374 + 36374 + 36384 + 36384 + + + + From 79ab6e89c44e3fbbbbea9d990fe67b95092cba48 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 6 Jan 2016 13:38:52 -0500 Subject: [PATCH 395/406] Fix bug: skill Sunder. --- sim.cpp | 2 +- tyrant.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sim.cpp b/sim.cpp index a37469c8..98fc6fb3 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1369,7 +1369,7 @@ inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* ds template<> inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s) { - return fd->tapi == dst->m_player ? is_active(dst) && !has_attacked(dst) : is_active_next_turn(dst); + return ! dst->m_sundered && (fd->tapi == dst->m_player ? is_active(dst) && !has_attacked(dst) : is_active_next_turn(dst)); } template<> diff --git a/tyrant.h b/tyrant.h index 50a28b77..c74d6685 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.16.0" +#define TYRANT_OPTIMIZER_VERSION "2.16.1" #include #include From 644b686f12ef7869a7debbfec4f9e57ad9aa7d15 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 7 Jan 2016 19:31:11 -0500 Subject: [PATCH 396/406] Update raid deck. --- data/raids.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/raids.xml b/data/raids.xml index 72f15d20..e6847753 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -350,6 +350,7 @@ 26 + 36344 36344 36344 36354 @@ -359,7 +360,6 @@ 36374 36374 36384 - 36384 From 267a563b82316794c18e819b268e753935774f80 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Fri, 5 Feb 2016 10:00:48 -0500 Subject: [PATCH 397/406] Add flag ye and ee. --- data/raids.xml | 462 +++++++++++++++++++++++++++++++++++++++++ sim.cpp | 495 +++++++++++++++++++++++--------------------- sim.h | 10 +- tyrant.h | 2 +- tyrant_optimize.cpp | 198 ++++++++++-------- 5 files changed, 841 insertions(+), 326 deletions(-) diff --git a/data/raids.xml b/data/raids.xml index e6847753..5da34f88 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -4371,4 +4371,466 @@ + + 1011 + Nexu101 + Nexus Arachis Normal 1 + 1018 + 1 + + + 4464 + 4464 + 6640 + 6640 + 34735 + 35504 + 35510 + 35516 + 35704 + 36504 + + + + + + 1012 + Nexu102 + Nexus Arachis Normal 2 + 1018 + 1 + + + 4468 + 4468 + 6644 + 6644 + 33269 + 33683 + 35332 + 35504 + 35510 + 35516 + + + + + + 1013 + Nexu103 + Nexus Arachis Normal 3 + 1962 + 1 + + + 14612 + 33686 + 35332 + 35506 + 35506 + 35510 + 35516 + 35866 + 36004 + 36507 + + + + + + 1014 + Nexu104 + Nexus Arachis Normal 4 + 1018 + 1 + + + 4464 + 4464 + 6640 + 16530 + 30190 + 33182 + 35275 + 35504 + 35510 + 35516 + + + + + + 1015 + Nexu105 + Nexus Arachis Normal 5 + 1018 + 1 + + + 4468 + 6644 + 30190 + 32770 + 33182 + 35275 + 35458 + 35504 + 35510 + 35516 + + + + + + 1016 + Nexu106 + Nexus Arachis Normal 6 + 1968 + 1 + + + 16018 + 32268 + 32770 + 33940 + 35461 + 35504 + 35512 + 35512 + 35516 + 36010 + + + + + + 1017 + Nexu107 + Nexus Arachis Normal 7 + 1974 + 1 + + + 15761 + 32268 + 32568 + 33943 + 34834 + 35347 + 35504 + 35510 + 35516 + 35516 + + + + + + 1021 + Nexu201 + Nexus Arachis Heroic 1 + 1043 + 1 + + + 34747 + 34974 + 35506 + 35512 + 35518 + 35638 + 35716 + 35716 + 36516 + 36516 + + + + + + 1022 + Nexu202 + Nexus Arachis Heroic 2 + 1043 + 1 + + + 14620 + 33278 + 33695 + 34979 + 35338 + 35506 + 35512 + 35518 + 35641 + 36013 + + + + + + 1023 + Nexu203 + Nexus Arachis Heroic 3 + 1964 + 1 + + + 14620 + 34612 + 35335 + 35335 + 35509 + 35509 + 35512 + 35518 + 35878 + 36519 + + + + + + 1024 + Nexu204 + Nexus Arachis Heroic 4 + 1102 + 1 + + + 16536 + 30193 + 30193 + 33188 + 33578 + 33994 + 35281 + 35506 + 35512 + 35518 + + + + + + 1025 + Nexu205 + Nexus Arachis Heroic 5 + 1102 + 1 + + + 16539 + 32773 + 32940 + 33578 + 33997 + 35467 + 35506 + 35512 + 35518 + 36211 + + + + + + 1026 + Nexu206 + Nexus Arachis Heroic 6 + 1970 + 1 + + + 16024 + 32271 + 32699 + 32773 + 33946 + 35506 + 35515 + 35515 + 35518 + 36010 + + + + + + 1027 + Nexu207 + Nexus Arachis Heroic 7 + 1976 + 1 + + + 15766 + 32277 + 32571 + 33943 + 34837 + 35353 + 35506 + 35512 + 35518 + 35518 + + + + + + 1031 + Nexu301 + Nexus Arachis Mythic 1 + 1045 + 1 + + + 30333 + 34985 + 35509 + 35515 + 35521 + 35647 + 35719 + 35719 + 36519 + 36519 + + + + + + 1032 + Nexu302 + Nexus Arachis Mythic 2 + 1045 + 1 + + + 30333 + 33284 + 33698 + 34618 + 35341 + 35509 + 35515 + 35521 + 36019 + 36019 + + + + + + 1033 + Nexu303 + Nexus Arachis Mythic 3 + 1967 + 1 + + + 14623 + 14623 + 34624 + 35341 + 35341 + 35509 + 35509 + 35515 + 35521 + 36519 + + + + + + 1034 + Nexu304 + Nexus Arachis Mythic 4 + 1112 + 1 + + + 30199 + 30199 + 33194 + 33590 + 34006 + 35287 + 35287 + 35509 + 35515 + 35521 + + + + + + 1035 + Nexu305 + Nexus Arachis Mythic 5 + 1112 + 1 + + + 16545 + 32782 + 32946 + 33008 + 34006 + 34006 + 35467 + 35509 + 35515 + 35521 + + + + + + 1036 + Nexu306 + Nexus Arachis Mythic 6 + 1973 + 1 + + + 16027 + 32782 + 33008 + 35509 + 35515 + 35515 + 35521 + 35881 + 36019 + + + + + + + 1037 + Nexu307 + Nexus Arachis Mythic 7 + 1979 + 1 + + + 15775 + 17077 + 32580 + 34606 + 35509 + 35515 + 35521 + 35881 + 36591 + + + + + diff --git a/sim.cpp b/sim.cpp index 98fc6fb3..066463a8 100644 --- a/sim.cpp +++ b/sim.cpp @@ -283,10 +283,10 @@ void prepend_on_death(Field* fd) } } } - if (fd->bg_effects.count(revenge)) + if (fd->bg_effects[status->m_player].count(revenge)) { - SkillSpec ss_heal{heal, fd->bg_effects.at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; - SkillSpec ss_rally{rally, fd->bg_effects.at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; + SkillSpec ss_heal{heal, fd->bg_effects[status->m_player].at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; + SkillSpec ss_rally{rally, fd->bg_effects[status->m_player].at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; CardStatus * commander = &fd->players[status->m_player]->commander; _DEBUG_MSG(2, "Revenge: Preparing skill %s and %s\n", skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); od_skills.emplace_back(commander, ss_heal); @@ -452,229 +452,6 @@ void PlayCard::setStorage() { storage = &fd->tap->structures; } -//------------------------------------------------------------------------------ -void turn_start_phase(Field* fd); -void turn_end_phase(Field* fd); -// return value : (raid points) -> attacker wins, 0 -> defender wins -Results play(Field* fd) -{ - fd->players[0]->commander.m_player = 0; - fd->players[1]->commander.m_player = 1; - fd->tapi = fd->gamemode == surge ? 1 : 0; - fd->tipi = opponent(fd->tapi); - fd->tap = fd->players[fd->tapi]; - fd->tip = fd->players[fd->tipi]; - fd->end = false; - - // Play fortresses - for (unsigned _ = 0; _ < 2; ++ _) - { - for (const Card* played_card: fd->tap->deck->shuffled_forts) - { - PlayCard(played_card, fd).op(); - } - std::swap(fd->tapi, fd->tipi); - std::swap(fd->tap, fd->tip); - } - - while(__builtin_expect(fd->turn <= turn_limit && !fd->end, true)) - { - fd->current_phase = Field::playcard_phase; - // Initialize stuff, remove dead cards - _DEBUG_MSG(1, "------------------------------------------------------------------------\n" - "TURN %u begins for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); - turn_start_phase(fd); - - // Play a card - const Card* played_card(fd->tap->deck->next()); - if(played_card) - { - switch(played_card->m_type) - { - case CardType::assault: - PlayCard(played_card, fd).op(); - break; - case CardType::structure: - PlayCard(played_card, fd).op(); - break; - case CardType::commander: - case CardType::num_cardtypes: - _DEBUG_MSG(0, "Unknown card type: #%u %s: %u\n", played_card->m_id, card_description(fd->cards, played_card).c_str(), played_card->m_type); - assert(false); - break; - } - } - if(__builtin_expect(fd->end, false)) { break; } - - // Evaluate Heroism Battleground skills - if (fd->bg_effects.count(heroism)) - { - for (CardStatus * dst: fd->tap->assaults.m_indirect) - { - unsigned bge_value = (dst->skill(valor) + 1) / 2; - if (bge_value <= 0) - { continue; } - SkillSpec ss_protect{protect, bge_value, allfactions, 0, 0, no_skill, no_skill, false,}; - if (dst->m_inhibited > 0) - { - _DEBUG_MSG(1, "Heroism: %s on %s but it is inhibited\n", skill_short_description(ss_protect).c_str(), status_description(dst).c_str()); - -- dst->m_inhibited; - continue; - } - bool has_counted_quest = false; - check_and_perform_skill(fd, &fd->tap->commander, dst, ss_protect, false, has_counted_quest); - } - } - - // Evaluate activation Battleground skills - for (const auto & bg_skill: fd->bg_skills) - { - _DEBUG_MSG(2, "Evaluating BG skill %s\n", skill_description(fd->cards, bg_skill).c_str()); - fd->skill_queue.emplace_back(&fd->tap->commander, bg_skill); - resolve_skill(fd); - } - if (__builtin_expect(fd->end, false)) { break; } - - // Evaluate commander - fd->current_phase = Field::commander_phase; - evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); - if(__builtin_expect(fd->end, false)) { break; } - - // Evaluate structures - fd->current_phase = Field::structures_phase; - for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->structures.size(); ++fd->current_ci) - { - CardStatus* current_status(&fd->tap->structures[fd->current_ci]); - if (!is_active(current_status)) - { - _DEBUG_MSG(2, "%s cannot take action.\n", status_description(current_status).c_str()); - } - else - { - evaluate_skills(fd, current_status, current_status->m_card->m_skills); - } - } - // Evaluate assaults - fd->current_phase = Field::assaults_phase; - fd->bloodlust_value = 0; - for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->assaults.size(); ++fd->current_ci) - { - // ca: current assault - CardStatus* current_status(&fd->tap->assaults[fd->current_ci]); - bool attacked = false; - if (!is_active(current_status)) - { - _DEBUG_MSG(2, "%s cannot take action.\n", status_description(current_status).c_str()); - } - else - { - fd->assault_bloodlusted = false; - evaluate_skills(fd, current_status, current_status->m_card->m_skills, &attacked); - if (__builtin_expect(fd->end, false)) { break; } - } - if (current_status->m_corroded_rate > 0) - { - if (attacked) - { - unsigned v = std::min(current_status->m_corroded_rate, attack_power(current_status)); - _DEBUG_MSG(1, "%s loses Attack by %u.\n", status_description(current_status).c_str(), v); - current_status->m_corroded_weakened += v; - } - else - { - _DEBUG_MSG(1, "%s loses Status corroded.\n", status_description(current_status).c_str()); - current_status->m_corroded_rate = 0; - current_status->m_corroded_weakened = 0; - } - } - current_status->m_step = CardStep::attacked; - } - fd->current_phase = Field::end_phase; - turn_end_phase(fd); - if(__builtin_expect(fd->end, false)) { break; } - _DEBUG_MSG(1, "TURN %u ends for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); - std::swap(fd->tapi, fd->tipi); - std::swap(fd->tap, fd->tip); - ++fd->turn; - } - const auto & p = fd->players; - unsigned raid_damage = 0; - unsigned quest_score = 0; - switch (fd->optimization_mode) - { - case OptimizationMode::raid: - raid_damage = 15 + (std::min(p[1]->deck->deck_size, (fd->turn + 1) / 2) - p[1]->assaults.size() - p[1]->structures.size()) - (10 * p[1]->commander.m_hp / p[1]->commander.m_max_hp); - break; - case OptimizationMode::quest: - if (fd->quest.quest_type == QuestType::card_survival) - { - for (const auto & status: p[0]->assaults.m_indirect) - { fd->quest_counter += (fd->quest.quest_key == status->m_card->m_id); } - for (const auto & status: p[0]->structures.m_indirect) - { fd->quest_counter += (fd->quest.quest_key == status->m_card->m_id); } - for (const auto & card: p[0]->deck->shuffled_cards) - { fd->quest_counter += (fd->quest.quest_key == card->m_id); } - } - quest_score = fd->quest.must_fulfill ? (fd->quest_counter >= fd->quest.quest_value ? fd->quest.quest_score : 0) : std::min(fd->quest.quest_score, fd->quest.quest_score * fd->quest_counter / fd->quest.quest_value); - _DEBUG_MSG(1, "Quest: %u / %u = %u%%.\n", fd->quest_counter, fd->quest.quest_value, quest_score); - break; - default: - break; - } - // you lose - if(fd->players[0]->commander.m_hp == 0) - { - _DEBUG_MSG(1, "You lose.\n"); - switch (fd->optimization_mode) - { - case OptimizationMode::raid: return {0, 0, 1, raid_damage}; - case OptimizationMode::brawl: return {0, 0, 1, 5}; - case OptimizationMode::quest: return {0, 0, 1, fd->quest.must_win ? 0 : quest_score}; - default: return {0, 0, 1, 0}; - } - } - // you win - if(fd->players[1]->commander.m_hp == 0) - { - _DEBUG_MSG(1, "You win.\n"); - switch (fd->optimization_mode) - { - case OptimizationMode::brawl: - { - unsigned brawl_score = 57 - - (10 * (p[0]->commander.m_max_hp - p[0]->commander.m_hp) / p[0]->commander.m_max_hp) - + (p[0]->assaults.size() + p[0]->structures.size() + p[0]->deck->shuffled_cards.size()) - - (p[1]->assaults.size() + p[1]->structures.size() + p[1]->deck->shuffled_cards.size()) - - fd->turn / 4; - return {1, 0, 0, brawl_score}; - } - case OptimizationMode::campaign: - { - unsigned campaign_score = 100 - 10 * (std::min(p[0]->deck->cards.size(), (fd->turn + 1) / 2) - p[0]->assaults.size() - p[0]->structures.size()); - return {1, 0, 0, campaign_score}; - } - case OptimizationMode::quest: return {1, 0, 0, fd->quest.win_score + quest_score}; - default: - return {1, 0, 0, 100}; - } - } - if (fd->turn > turn_limit) - { - _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); - switch (fd->optimization_mode) - { - case OptimizationMode::defense: return {0, 1, 0, 100}; - case OptimizationMode::raid: return {0, 1, 0, raid_damage}; - case OptimizationMode::brawl: return {0, 1, 0, 5}; - case OptimizationMode::quest: return {0, 1, 0, fd->quest.must_win ? 0 : quest_score}; - default: return {0, 1, 0, 0}; - } - } - - // Huh? How did we get here? - assert(false); - return {0, 0, 0, 0}; -} // Check if a skill actually proc'ed. template @@ -1016,9 +793,9 @@ struct PerformAttack } _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, att_status, counter_dmg); - if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects.count(counterflux)) + if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects[def_status->m_player].count(counterflux)) { - unsigned flux_denominator = fd->bg_effects.at(counterflux) ? fd->bg_effects.at(counterflux) : 4; + unsigned flux_denominator = fd->bg_effects[def_status->m_player].at(counterflux) ? fd->bg_effects[def_status->m_player].at(counterflux) : 4; unsigned flux_value = (def_status->skill(counter) - 1) / flux_denominator + 1; _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n", status_description(def_status).c_str(), flux_value); add_hp(fd, def_status, flux_value); @@ -1042,9 +819,9 @@ struct PerformAttack { fd->inc_counter(QuestType::skill_use, berserk); } - if (fd->bg_effects.count(enduringrage)) + if (fd->bg_effects[att_status->m_player].count(enduringrage)) { - unsigned bge_denominator = fd->bg_effects.at(enduringrage) ? fd->bg_effects.at(enduringrage) : 2; + unsigned bge_denominator = fd->bg_effects[att_status->m_player].at(enduringrage) ? fd->bg_effects[att_status->m_player].at(enduringrage) : 2; unsigned bge_value = (berserk_value - 1) / bge_denominator + 1; _DEBUG_MSG(1, "EnduringRage: %s heals and protects itself for %u\n", status_description(att_status).c_str(), bge_value); add_hp(fd, att_status, bge_value); @@ -1053,7 +830,7 @@ struct PerformAttack } do_leech(); unsigned valor_value = att_status->skill(valor); - if (valor_value > 0 && ! att_status->m_sundered && fd->bg_effects.count(heroism) && def_cardtype == CardType::assault && def_status->m_hp <= 0) + if (valor_value > 0 && ! att_status->m_sundered && fd->bg_effects[att_status->m_player].count(heroism) && def_cardtype == CardType::assault && def_status->m_hp <= 0) { _DEBUG_MSG(1, "Heroism: %s gain %u attack\n", status_description(att_status).c_str(), valor_value); att_status->m_attack += valor_value; @@ -1115,7 +892,7 @@ struct PerformAttack std::string reduced_desc; unsigned reduced_dmg(0); unsigned armor_value = def_status->skill(armor); - if (def_status->m_card->m_type == CardType::assault && fd->bg_effects.count(fortification)) + if (def_status->m_card->m_type == CardType::assault && fd->bg_effects[def_status->m_player].count(fortification)) { for (auto && adj_status: fd->adjacent_assaults(def_status)) { @@ -1145,7 +922,7 @@ struct PerformAttack if(!desc.empty()) { desc += "=" + to_string(att_dmg); } _DEBUG_MSG(1, "%s attacks %s for %u%s damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), pre_modifier_dmg, desc.c_str()); } - if (legion_value > 0 && can_be_healed(att_status) && fd->bg_effects.count(brigade)) + if (legion_value > 0 && can_be_healed(att_status) && fd->bg_effects[att_status->m_player].count(brigade)) { _DEBUG_MSG(1, "Brigade: %s heals itself for %u\n", status_description(att_status).c_str(), legion_value); add_hp(fd, att_status, legion_value); @@ -1268,9 +1045,9 @@ bool attack_phase(Field* fd) att_dmg = attack_commander(fd, att_status); } - if (att_dmg > 0 && !fd->assault_bloodlusted && fd->bg_effects.count(bloodlust)) + if (att_dmg > 0 && !fd->assault_bloodlusted && fd->bg_effects[fd->tapi].count(bloodlust)) { - fd->bloodlust_value += fd->bg_effects.at(bloodlust); + fd->bloodlust_value += fd->bg_effects[fd->tapi].at(bloodlust); fd->assault_bloodlusted = true; } @@ -1506,7 +1283,7 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, c template inline unsigned select_fast(Field* fd, CardStatus* src, const std::vector& cards, const SkillSpec& s) { - if (s.y == allfactions || fd->bg_effects.count(metamorphosis)) + if (s.y == allfactions || fd->bg_effects[src->m_player].count(metamorphosis)) { return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src, s](CardStatus* c){return(skill_predicate(fd, src, c, s));})); } @@ -1733,7 +1510,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src, const SkillSpec& } check_and_perform_skill(fd, src, dst, s, false, has_counted_quest); } - if (num_inhibited > 0 && fd->bg_effects.count(divert)) + if (num_inhibited > 0 && fd->bg_effects[fd->tipi].count(divert)) { SkillSpec diverted_ss = s; diverted_ss.y = allfactions; @@ -1775,7 +1552,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& select_targets(fd, src, s); bool has_counted_quest = false; std::vector paybackers; - if (fd->bg_effects.count(turningtides) && skill_id == weaken) + if (fd->bg_effects[src->m_player].count(turningtides) && skill_id == weaken) { unsigned turningtides_value = 0; for (CardStatus * dst: fd->selection_array) @@ -1836,6 +1613,248 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& prepend_on_death(fd); } +//------------------------------------------------------------------------------ +Results play(Field* fd) +{ + fd->players[0]->commander.m_player = 0; + fd->players[1]->commander.m_player = 1; + fd->tapi = fd->gamemode == surge ? 1 : 0; + fd->tipi = opponent(fd->tapi); + fd->tap = fd->players[fd->tapi]; + fd->tip = fd->players[fd->tipi]; + fd->end = false; + + // Play fortresses + for (unsigned _ = 0; _ < 2; ++ _) + { + for (const Card* played_card: fd->tap->deck->shuffled_forts) + { + PlayCard(played_card, fd).op(); + } + std::swap(fd->tapi, fd->tipi); + std::swap(fd->tap, fd->tip); + } + + while(__builtin_expect(fd->turn <= turn_limit && !fd->end, true)) + { + fd->current_phase = Field::playcard_phase; + // Initialize stuff, remove dead cards + _DEBUG_MSG(1, "------------------------------------------------------------------------\n" + "TURN %u begins for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); + turn_start_phase(fd); + + // Play a card + const Card* played_card(fd->tap->deck->next()); + if(played_card) + { + switch(played_card->m_type) + { + case CardType::assault: + PlayCard(played_card, fd).op(); + break; + case CardType::structure: + PlayCard(played_card, fd).op(); + break; + case CardType::commander: + case CardType::num_cardtypes: + _DEBUG_MSG(0, "Unknown card type: #%u %s: %u\n", played_card->m_id, card_description(fd->cards, played_card).c_str(), played_card->m_type); + assert(false); + break; + } + } + if(__builtin_expect(fd->end, false)) { break; } + + // Evaluate Heroism Battleground skills + if (fd->bg_effects[fd->tapi].count(heroism)) + { + for (CardStatus * dst: fd->tap->assaults.m_indirect) + { + unsigned bge_value = (dst->skill(valor) + 1) / 2; + if (bge_value <= 0) + { continue; } + SkillSpec ss_protect{protect, bge_value, allfactions, 0, 0, no_skill, no_skill, false,}; + if (dst->m_inhibited > 0) + { + _DEBUG_MSG(1, "Heroism: %s on %s but it is inhibited\n", skill_short_description(ss_protect).c_str(), status_description(dst).c_str()); + -- dst->m_inhibited; + if (fd->bg_effects[fd->tipi].count(divert)) + { + SkillSpec diverted_ss = ss_protect; + diverted_ss.y = allfactions; + diverted_ss.n = 1; + diverted_ss.all = false; + // for (unsigned i = 0; i < num_inhibited; ++ i) + { + select_targets(fd, &fd->tip->commander, diverted_ss); + for (CardStatus * dst: fd->selection_array) + { + if (dst->m_inhibited > 0) + { + _DEBUG_MSG(1, "Heroism: %s (Diverted) on %s but it is inhibited\n", skill_short_description(diverted_ss).c_str(), status_description(dst).c_str()); + -- dst->m_inhibited; + continue; + } + _DEBUG_MSG(1, "Heroism: %s (Diverted) on %s\n", skill_short_description(diverted_ss).c_str(), status_description(dst).c_str()); + perform_skill(fd, &fd->tap->commander, dst, diverted_ss); // XXX: the caster + } + } + } + continue; + } + bool has_counted_quest = false; + check_and_perform_skill(fd, &fd->tap->commander, dst, ss_protect, false, has_counted_quest); + } + } + + // Evaluate activation Battleground skills + for (const auto & bg_skill: fd->bg_skills[fd->tapi]) + { + _DEBUG_MSG(2, "Evaluating BG skill %s\n", skill_description(fd->cards, bg_skill).c_str()); + fd->skill_queue.emplace_back(&fd->tap->commander, bg_skill); + resolve_skill(fd); + } + if (__builtin_expect(fd->end, false)) { break; } + + // Evaluate commander + fd->current_phase = Field::commander_phase; + evaluate_skills(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills); + if(__builtin_expect(fd->end, false)) { break; } + + // Evaluate structures + fd->current_phase = Field::structures_phase; + for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->structures.size(); ++fd->current_ci) + { + CardStatus* current_status(&fd->tap->structures[fd->current_ci]); + if (!is_active(current_status)) + { + _DEBUG_MSG(2, "%s cannot take action.\n", status_description(current_status).c_str()); + } + else + { + evaluate_skills(fd, current_status, current_status->m_card->m_skills); + } + } + // Evaluate assaults + fd->current_phase = Field::assaults_phase; + fd->bloodlust_value = 0; + for(fd->current_ci = 0; !fd->end && fd->current_ci < fd->tap->assaults.size(); ++fd->current_ci) + { + // ca: current assault + CardStatus* current_status(&fd->tap->assaults[fd->current_ci]); + bool attacked = false; + if (!is_active(current_status)) + { + _DEBUG_MSG(2, "%s cannot take action.\n", status_description(current_status).c_str()); + } + else + { + fd->assault_bloodlusted = false; + evaluate_skills(fd, current_status, current_status->m_card->m_skills, &attacked); + if (__builtin_expect(fd->end, false)) { break; } + } + if (current_status->m_corroded_rate > 0) + { + if (attacked) + { + unsigned v = std::min(current_status->m_corroded_rate, attack_power(current_status)); + _DEBUG_MSG(1, "%s loses Attack by %u.\n", status_description(current_status).c_str(), v); + current_status->m_corroded_weakened += v; + } + else + { + _DEBUG_MSG(1, "%s loses Status corroded.\n", status_description(current_status).c_str()); + current_status->m_corroded_rate = 0; + current_status->m_corroded_weakened = 0; + } + } + current_status->m_step = CardStep::attacked; + } + fd->current_phase = Field::end_phase; + turn_end_phase(fd); + if(__builtin_expect(fd->end, false)) { break; } + _DEBUG_MSG(1, "TURN %u ends for %s\n", fd->turn, status_description(&fd->tap->commander).c_str()); + std::swap(fd->tapi, fd->tipi); + std::swap(fd->tap, fd->tip); + ++fd->turn; + } + const auto & p = fd->players; + unsigned raid_damage = 0; + unsigned quest_score = 0; + switch (fd->optimization_mode) + { + case OptimizationMode::raid: + raid_damage = 15 + (std::min(p[1]->deck->deck_size, (fd->turn + 1) / 2) - p[1]->assaults.size() - p[1]->structures.size()) - (10 * p[1]->commander.m_hp / p[1]->commander.m_max_hp); + break; + case OptimizationMode::quest: + if (fd->quest.quest_type == QuestType::card_survival) + { + for (const auto & status: p[0]->assaults.m_indirect) + { fd->quest_counter += (fd->quest.quest_key == status->m_card->m_id); } + for (const auto & status: p[0]->structures.m_indirect) + { fd->quest_counter += (fd->quest.quest_key == status->m_card->m_id); } + for (const auto & card: p[0]->deck->shuffled_cards) + { fd->quest_counter += (fd->quest.quest_key == card->m_id); } + } + quest_score = fd->quest.must_fulfill ? (fd->quest_counter >= fd->quest.quest_value ? fd->quest.quest_score : 0) : std::min(fd->quest.quest_score, fd->quest.quest_score * fd->quest_counter / fd->quest.quest_value); + _DEBUG_MSG(1, "Quest: %u / %u = %u%%.\n", fd->quest_counter, fd->quest.quest_value, quest_score); + break; + default: + break; + } + // you lose + if(fd->players[0]->commander.m_hp == 0) + { + _DEBUG_MSG(1, "You lose.\n"); + switch (fd->optimization_mode) + { + case OptimizationMode::raid: return {0, 0, 1, raid_damage}; + case OptimizationMode::brawl: return {0, 0, 1, 5}; + case OptimizationMode::quest: return {0, 0, 1, fd->quest.must_win ? 0 : quest_score}; + default: return {0, 0, 1, 0}; + } + } + // you win + if(fd->players[1]->commander.m_hp == 0) + { + _DEBUG_MSG(1, "You win.\n"); + switch (fd->optimization_mode) + { + case OptimizationMode::brawl: + { + unsigned brawl_score = 57 + - (10 * (p[0]->commander.m_max_hp - p[0]->commander.m_hp) / p[0]->commander.m_max_hp) + + (p[0]->assaults.size() + p[0]->structures.size() + p[0]->deck->shuffled_cards.size()) + - (p[1]->assaults.size() + p[1]->structures.size() + p[1]->deck->shuffled_cards.size()) + - fd->turn / 4; + return {1, 0, 0, brawl_score}; + } + case OptimizationMode::campaign: + { + unsigned campaign_score = 100 - 10 * (std::min(p[0]->deck->cards.size(), (fd->turn + 1) / 2) - p[0]->assaults.size() - p[0]->structures.size()); + return {1, 0, 0, campaign_score}; + } + case OptimizationMode::quest: return {1, 0, 0, fd->quest.win_score + quest_score}; + default: + return {1, 0, 0, 100}; + } + } + if (fd->turn > turn_limit) + { + _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit); + switch (fd->optimization_mode) + { + case OptimizationMode::defense: return {0, 1, 0, 100}; + case OptimizationMode::raid: return {0, 1, 0, raid_damage}; + case OptimizationMode::brawl: return {0, 1, 0, 5}; + case OptimizationMode::quest: return {0, 1, 0, fd->quest.must_win ? 0 : quest_score}; + default: return {0, 1, 0, 0}; + } + } + + // Huh? How did we get here? + assert(false); + return {0, 0, 0, 0}; +} //------------------------------------------------------------------------------ void fill_skill_table() { diff --git a/sim.h b/sim.h index d6215d62..f1384466 100644 --- a/sim.h +++ b/sim.h @@ -239,8 +239,8 @@ class Field gamemode_t gamemode; OptimizationMode optimization_mode; const Quest quest; - std::unordered_map bg_effects; // passive BGE - std::vector bg_skills; // active BGE, casted every turn + std::unordered_map bg_effects[2]; // passive BGE + std::vector bg_skills[2]; // active BGE, casted every turn // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. std::deque> skill_queue; @@ -266,7 +266,7 @@ class Field unsigned quest_counter; Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, OptimizationMode optimization_mode_, const Quest & quest_, - std::unordered_map& bg_effects_, std::vector& bg_skills_) : + std::unordered_map& your_bg_effects_, std::unordered_map& enemy_bg_effects_, std::vector& your_bg_skills_, std::vector& enemy_bg_skills_) : end{false}, re(re_), cards(cards_), @@ -275,8 +275,8 @@ class Field gamemode(gamemode_), optimization_mode(optimization_mode_), quest(quest_), - bg_effects(bg_effects_), - bg_skills(bg_skills_), + bg_effects{your_bg_effects_, enemy_bg_effects_}, + bg_skills{your_bg_skills_, enemy_bg_skills_}, assault_bloodlusted(false), bloodlust_value(0), quest_counter(0) diff --git a/tyrant.h b/tyrant.h index c74d6685..caaab87e 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.16.1" +#define TYRANT_OPTIMIZER_VERSION "2.17.0" #include #include diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index a2c516cb..8d2d83f5 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -448,11 +448,11 @@ struct SimulationData std::vector factors; gamemode_t gamemode; Quest quest; - std::unordered_map bg_effects; - std::vector bg_skills; + std::unordered_map your_bg_effects, enemy_bg_effects; + std::vector your_bg_skills, enemy_bg_skills; SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_enemy_decks_, std::vector factors_, gamemode_t gamemode_, Quest & quest_, - std::unordered_map& bg_effects_, std::vector& bg_skills_) : + std::unordered_map& your_bg_effects_, std::unordered_map& enemy_bg_effects_, std::vector& your_bg_skills_, std::vector& enemy_bg_skills_) : re(seed), cards(cards_), decks(decks_), @@ -462,8 +462,10 @@ struct SimulationData factors(factors_), gamemode(gamemode_), quest(quest_), - bg_effects(bg_effects_), - bg_skills(bg_skills_) + your_bg_effects(your_bg_effects_), + enemy_bg_effects(enemy_bg_effects_), + your_bg_skills(your_bg_skills_), + enemy_bg_skills(enemy_bg_skills_) { for (size_t i = 0; i < num_enemy_decks_; ++i) { @@ -494,7 +496,7 @@ struct SimulationData { your_hand.reset(re); enemy_hand->reset(re); - Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, quest, bg_effects, bg_skills); + Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, quest, your_bg_effects, enemy_bg_effects, your_bg_skills, enemy_bg_skills); Results result(play(&fd)); res.emplace_back(result); } @@ -524,11 +526,11 @@ class Process std::vector factors; gamemode_t gamemode; Quest quest; - std::unordered_map bg_effects; - std::vector bg_skills; + std::unordered_map your_bg_effects, enemy_bg_effects; + std::vector your_bg_skills, enemy_bg_skills; Process(unsigned num_threads_, const Cards& cards_, const Decks& decks_, Deck* your_deck_, std::vector enemy_decks_, std::vector factors_, gamemode_t gamemode_, Quest & quest_, - std::unordered_map& bg_effects_, std::vector& bg_skills_) : + std::unordered_map& your_bg_effects_, std::unordered_map& enemy_bg_effects_, std::vector& your_bg_skills_, std::vector& enemy_bg_skills_) : num_threads(num_threads_), main_barrier(num_threads+1), cards(cards_), @@ -538,8 +540,10 @@ class Process factors(factors_), gamemode(gamemode_), quest(quest_), - bg_effects(bg_effects_), - bg_skills(bg_skills_) + your_bg_effects(your_bg_effects_), + enemy_bg_effects(enemy_bg_effects_), + your_bg_skills(your_bg_skills_), + enemy_bg_skills(enemy_bg_skills_) { destroy_threads = false; unsigned seed(sim_seed ? sim_seed : std::chrono::system_clock::now().time_since_epoch().count() * 2654435761); // Knuth multiplicative hash @@ -549,7 +553,7 @@ class Process } for(unsigned i(0); i < num_threads; ++i) { - threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, quest, bg_effects, bg_skills)); + threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, quest, your_bg_effects, enemy_bg_effects, your_bg_skills, enemy_bg_skills)); threads.push_back(new boost::thread(thread_evaluate, std::ref(main_barrier), std::ref(shared_mutex), std::ref(*threads_data.back()), std::ref(*this), i)); } } @@ -1234,9 +1238,9 @@ int main(int argc, char** argv) bool opt_do_optimization(false); bool opt_keep_commander{false}; std::vector> opt_todo; - std::vector opt_effects; - std::unordered_map opt_bg_effects; - std::vector opt_bg_skills; + std::vector opt_effects[2]; + std::unordered_map opt_bg_effects[2]; + std::vector opt_bg_skills[2]; for(int argIndex = 3; argIndex < argc; ++argIndex) { @@ -1321,7 +1325,18 @@ int main(int argc, char** argv) } else if (strcmp(argv[argIndex], "effect") == 0 || strcmp(argv[argIndex], "-e") == 0) { - opt_effects.push_back(argv[argIndex + 1]); + opt_effects[0].push_back(argv[argIndex + 1]); + opt_effects[1].push_back(argv[argIndex + 1]); + argIndex += 1; + } + else if (strcmp(argv[argIndex], "ye") == 0 || strcmp(argv[argIndex], "yeffect") == 0) + { + opt_effects[0].push_back(argv[argIndex + 1]); + argIndex += 1; + } + else if (strcmp(argv[argIndex], "ee") == 0 || strcmp(argv[argIndex], "eeffect") == 0) + { + opt_effects[1].push_back(argv[argIndex + 1]); argIndex += 1; } else if (strcmp(argv[argIndex], "freeze") == 0 || strcmp(argv[argIndex], "-F") == 0) @@ -1546,93 +1561,96 @@ int main(int argc, char** argv) } } - for (const auto & opt_effect: opt_effects) + for (int player = 0; player <= 1; ++ player) { - if (opt_effect.empty()) - { - continue; - } - try + for (const auto & opt_effect: opt_effects[player]) { - std::vector tokens; - boost::split(tokens, opt_effect, boost::is_any_of(" -")); - Skill skill_id = skill_name_to_id(tokens[0]); - unsigned skill_index = 1; - if (BEGIN_BGE_SKILL < skill_id && skill_id < END_BGE_SKILL) + if (opt_effect.empty()) { - // passive BGE - if (skill_index < tokens.size()) - { - opt_bg_effects[skill_id] = boost::lexical_cast(tokens[skill_index]); - } - else - { - opt_bg_effects[skill_id] = 0; - } + continue; } - else if (skill_table[skill_id] != nullptr) + try { - // activation BG skill - SkillSpec bg_skill{skill_id, 0, allfactions, 0, 0, no_skill, no_skill, false}; - if (skill_index < tokens.size() && boost::to_lower_copy(tokens[skill_index]) == "all") - { - bg_skill.all = true; - skill_index += 1; - } - else if (skill_index + 1 < tokens.size() && isdigit(*tokens[skill_index].c_str())) + std::vector tokens; + boost::split(tokens, opt_effect, boost::is_any_of(" -")); + Skill skill_id = skill_name_to_id(tokens[0]); + unsigned skill_index = 1; + if (BEGIN_BGE_SKILL < skill_id && skill_id < END_BGE_SKILL) { - bg_skill.n = boost::lexical_cast(tokens[skill_index]); - skill_index += 1; + // passive BGE + if (skill_index < tokens.size()) + { + opt_bg_effects[player][skill_id] = boost::lexical_cast(tokens[skill_index]); + } + else + { + opt_bg_effects[player][skill_id] = 0; + } } - if (skill_index < tokens.size()) + else if (skill_table[skill_id] != nullptr) { - bg_skill.s = skill_name_to_id(tokens[skill_index]); - if (bg_skill.s != no_skill) + // activation BG skill + SkillSpec bg_skill{skill_id, 0, allfactions, 0, 0, no_skill, no_skill, false}; + if (skill_index < tokens.size() && boost::to_lower_copy(tokens[skill_index]) == "all") { + bg_skill.all = true; + skill_index += 1; + } + else if (skill_index + 1 < tokens.size() && isdigit(*tokens[skill_index].c_str())) + { + bg_skill.n = boost::lexical_cast(tokens[skill_index]); skill_index += 1; - if (skill_index < tokens.size() && boost::to_lower_copy(tokens[skill_index]) == "to") + } + if (skill_index < tokens.size()) + { + bg_skill.s = skill_name_to_id(tokens[skill_index]); + if (bg_skill.s != no_skill) { skill_index += 1; - } - if (skill_index < tokens.size()) - { - bg_skill.s2 = skill_name_to_id(tokens[skill_index]); - if (bg_skill.s2 != no_skill) + if (skill_index < tokens.size() && boost::to_lower_copy(tokens[skill_index]) == "to") { skill_index += 1; } + if (skill_index < tokens.size()) + { + bg_skill.s2 = skill_name_to_id(tokens[skill_index]); + if (bg_skill.s2 != no_skill) + { + skill_index += 1; + } + } } } - } - if (skill_index < tokens.size()) - { - if (bg_skill.id == jam || bg_skill.id == overload) + if (skill_index < tokens.size()) { - bg_skill.n = boost::lexical_cast(tokens[skill_index]); - } - else - { - bg_skill.x = boost::lexical_cast(tokens[skill_index]); + if (bg_skill.id == jam || bg_skill.id == overload) + { + bg_skill.n = boost::lexical_cast(tokens[skill_index]); + } + else + { + bg_skill.x = boost::lexical_cast(tokens[skill_index]); + } } + opt_bg_skills[player].push_back(bg_skill); + } + else + { + std::cerr << "Error: unrecognized effect \"" << opt_effect << "\".\n"; + print_available_effects(); + return 0; } - opt_bg_skills.push_back(bg_skill); } - else + catch (const boost::bad_lexical_cast & e) { - std::cerr << "Error: unrecognized effect \"" << opt_effect << "\".\n"; - print_available_effects(); + std::cerr << "Error: Expect a number in effect \"" << opt_effect << "\".\n"; + return 0; + } + catch (std::exception & e) + { + std::cerr << "Error: effect \"" << opt_effect << ": " << e.what() << "\".\n"; return 0; } - } - catch (const boost::bad_lexical_cast & e) - { - std::cerr << "Error: Expect a number in effect \"" << opt_effect << "\".\n"; - return 0; - } - catch (std::exception & e) - { - std::cerr << "Error: effect \"" << opt_effect << ": " << e.what() << "\".\n"; - return 0; } } @@ -1938,11 +1956,27 @@ int main(int argc, char** argv) if (debug_print >= 0) { std::cout << "Your Deck: " << (debug_print > 0 ? your_deck->long_description() : your_deck->medium_description()) << std::endl; + for (const auto & bg_effect: opt_bg_effects[0]) + { + if (bg_effect.second == 0) + { + std::cout << "BG Effect: " << skill_names[bg_effect.first] << std::endl; + } + else + { + std::cout << "BG Effect: " << skill_names[bg_effect.first] << " " << bg_effect.second << std::endl; + } + } + for (const auto & bg_skill: opt_bg_skills[0]) + { + std::cout << "BG Skill: " << skill_description(all_cards, bg_skill) << std::endl; + } + for (unsigned i(0); i < enemy_decks.size(); ++i) { std::cout << "Enemy's Deck:" << enemy_decks_factors[i] << ": " << (debug_print > 0 ? enemy_decks[i]->long_description() : enemy_decks[i]->medium_description()) << std::endl; } - for (const auto & bg_effect: opt_bg_effects) + for (const auto & bg_effect: opt_bg_effects[1]) { if (bg_effect.second == 0) { @@ -1953,13 +1987,13 @@ int main(int argc, char** argv) std::cout << "BG Effect: " << skill_names[bg_effect.first] << " " << bg_effect.second << std::endl; } } - for (const auto & bg_skill: opt_bg_skills) + for (const auto & bg_skill: opt_bg_skills[1]) { std::cout << "BG Skill: " << skill_description(all_cards, bg_skill) << std::endl; } } - Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, quest, opt_bg_effects, opt_bg_skills); + Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, quest, opt_bg_effects[0], opt_bg_effects[1], opt_bg_skills[0], opt_bg_skills[1]); for(auto op: opt_todo) { From c0b21d39bed4dc9c0a31960b3ca47012a3a53fb9 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Sat, 6 Feb 2016 21:50:05 -0500 Subject: [PATCH 398/406] Support BGE Virulence. --- deck.cpp | 1 - sim.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++- tyrant.cpp | 2 +- tyrant.h | 2 +- tyrant_optimize.cpp | 3 ++- 5 files changed, 51 insertions(+), 5 deletions(-) diff --git a/deck.cpp b/deck.cpp index 5a7d5176..6c2e80c3 100644 --- a/deck.cpp +++ b/deck.cpp @@ -408,7 +408,6 @@ std::string Deck::long_description() const show_upgrades(ios, card, card->m_top_level_card->m_level, " "); } } - ios << "\n"; return ios.str(); } diff --git a/sim.cpp b/sim.cpp index 066463a8..42d1850a 100644 --- a/sim.cpp +++ b/sim.cpp @@ -264,7 +264,15 @@ SkillSpec apply_enhance(const SkillSpec& s, unsigned enhanced_value) //------------------------------------------------------------------------------ void prepend_on_death(Field* fd) { + if (fd->killed_units.empty()) + { + return; + } std::vector> od_skills; + auto & assaults = fd->players[fd->killed_units[0]->m_player]->assaults; + unsigned stacked_poison_value = 0; + unsigned last_index = 99; + CardStatus * left_virulence_victim = nullptr; for (auto status: fd->killed_units) { // avenge @@ -292,6 +300,42 @@ void prepend_on_death(Field* fd) od_skills.emplace_back(commander, ss_heal); od_skills.emplace_back(commander, ss_rally); } + if (fd->bg_effects[opponent(status->m_player)].count(virulence)) + { + if (status->m_index != last_index + 1) + { + stacked_poison_value = 0; + left_virulence_victim = nullptr; + if (status->m_index > 0) + { + auto left_status = &assaults[status->m_index - 1]; + if (left_status->m_hp > 0) + { + left_virulence_victim = left_status; + } + } + } + if (status->m_poisoned > 0) + { + if (left_virulence_victim != nullptr) + { + _DEBUG_MSG(1, "Virulence: %s spreads left poison +%u to %s\n", status_description(status).c_str(), status->m_poisoned, status_description(left_virulence_victim).c_str()); + left_virulence_victim->m_poisoned += status->m_poisoned; + } + stacked_poison_value += status->m_poisoned; + _DEBUG_MSG(1, "Virulence: %s spreads right poison +%u = %u\n", status_description(status).c_str(), status->m_poisoned, stacked_poison_value); + } + if (status->m_index + 1 < assaults.size()) + { + auto right_status = &assaults[status->m_index + 1]; + if (right_status->m_hp > 0) + { + _DEBUG_MSG(1, "Virulence: spreads stacked poison +%u to %s\n", stacked_poison_value, status_description(right_status).c_str()); + right_status->m_poisoned += stacked_poison_value; + } + } + last_index = status->m_index; + } } fd->skill_queue.insert(fd->skill_queue.begin(), od_skills.begin(), od_skills.end()); fd->killed_units.clear(); @@ -1510,7 +1554,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src, const SkillSpec& } check_and_perform_skill(fd, src, dst, s, false, has_counted_quest); } - if (num_inhibited > 0 && fd->bg_effects[fd->tipi].count(divert)) + if (num_inhibited > 0 && fd->bg_effects[opponent(src->m_player)].count(divert)) { SkillSpec diverted_ss = s; diverted_ss.y = allfactions; @@ -1575,6 +1619,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& _DEBUG_MSG(1, "TurningTides %u!\n", turningtides_value); perform_targetted_allied_fast(fd, &fd->players[src->m_player]->commander, ss_rally); } + prepend_on_death(fd); for (CardStatus * pb_status: paybackers) { ++ pb_status->m_paybacked; @@ -1604,6 +1649,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& } } } + prepend_on_death(fd); for (CardStatus * pb_status: paybackers) { ++ pb_status->m_paybacked; diff --git a/tyrant.cpp b/tyrant.cpp index dad3eb2b..20350eee 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -30,7 +30,7 @@ std::string skill_names[Skill::num_skills] = "Flurry", "Valor", // Pseudo-skill for passive BGEs: "", - "Bloodlust", "Brigade", "Counterflux", "Divert", "EnduringRage", "Fortification", "Heroism", "Metamorphosis", "Revenge", "TurningTides", + "Bloodlust", "Brigade", "Counterflux", "Divert", "EnduringRage", "Fortification", "Heroism", "Metamorphosis", "Revenge", "TurningTides", "Virulence", "", }; diff --git a/tyrant.h b/tyrant.h index caaab87e..6c11d196 100644 --- a/tyrant.h +++ b/tyrant.h @@ -46,7 +46,7 @@ enum Skill flurry, valor, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, - bloodlust, brigade, counterflux, divert, enduringrage, fortification, heroism, metamorphosis, revenge, turningtides, + bloodlust, brigade, counterflux, divert, enduringrage, fortification, heroism, metamorphosis, revenge, turningtides, virulence, END_BGE_SKILL, num_skills }; diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 8d2d83f5..209eb925 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -1167,6 +1167,7 @@ void print_available_effects() " Metamorphosis\n" " Revenge X\n" " TurningTides\n" + " Virulence\n" ; } void usage(int argc, char** argv) @@ -1524,7 +1525,7 @@ int main(int argc, char** argv) Cards all_cards; Decks decks; load_skills_set_xml(all_cards, "data/skills_set.xml", true); - for (unsigned section = 1; section <= 10; ++ section) + for (unsigned section = 0; section <= 10; ++ section) { load_cards_xml(all_cards, "data/cards_section_" + to_string(section) + ".xml", false); } From 68059d74307c30b81c01a1bab08b5ab5632b5f3e Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 8 Feb 2016 12:57:55 -0500 Subject: [PATCH 399/406] Fix bug: crash using Structures and BGE Virulence. --- sim.cpp | 74 +++++++++++++++++++++++++++++--------------------------- tyrant.h | 2 +- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/sim.cpp b/sim.cpp index 42d1850a..2384e91c 100644 --- a/sim.cpp +++ b/sim.cpp @@ -275,9 +275,9 @@ void prepend_on_death(Field* fd) CardStatus * left_virulence_victim = nullptr; for (auto status: fd->killed_units) { - // avenge if (status->m_card->m_type == CardType::assault) { + // Avenge for (auto && adj_status: fd->adjacent_assaults(status)) { unsigned avenge_value = adj_status->skill(avenge); @@ -290,51 +290,53 @@ void prepend_on_death(Field* fd) adj_status->m_hp += avenge_value; } } - } - if (fd->bg_effects[status->m_player].count(revenge)) - { - SkillSpec ss_heal{heal, fd->bg_effects[status->m_player].at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; - SkillSpec ss_rally{rally, fd->bg_effects[status->m_player].at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; - CardStatus * commander = &fd->players[status->m_player]->commander; - _DEBUG_MSG(2, "Revenge: Preparing skill %s and %s\n", skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); - od_skills.emplace_back(commander, ss_heal); - od_skills.emplace_back(commander, ss_rally); - } - if (fd->bg_effects[opponent(status->m_player)].count(virulence)) - { - if (status->m_index != last_index + 1) + // Virulence + if (fd->bg_effects[opponent(status->m_player)].count(virulence)) { - stacked_poison_value = 0; - left_virulence_victim = nullptr; - if (status->m_index > 0) + if (status->m_index != last_index + 1) { - auto left_status = &assaults[status->m_index - 1]; - if (left_status->m_hp > 0) + stacked_poison_value = 0; + left_virulence_victim = nullptr; + if (status->m_index > 0) { - left_virulence_victim = left_status; + auto left_status = &assaults[status->m_index - 1]; + if (left_status->m_hp > 0) + { + left_virulence_victim = left_status; + } } } - } - if (status->m_poisoned > 0) - { - if (left_virulence_victim != nullptr) + if (status->m_poisoned > 0) { - _DEBUG_MSG(1, "Virulence: %s spreads left poison +%u to %s\n", status_description(status).c_str(), status->m_poisoned, status_description(left_virulence_victim).c_str()); - left_virulence_victim->m_poisoned += status->m_poisoned; + if (left_virulence_victim != nullptr) + { + _DEBUG_MSG(1, "Virulence: %s spreads left poison +%u to %s\n", status_description(status).c_str(), status->m_poisoned, status_description(left_virulence_victim).c_str()); + left_virulence_victim->m_poisoned += status->m_poisoned; + } + stacked_poison_value += status->m_poisoned; + _DEBUG_MSG(1, "Virulence: %s spreads right poison +%u = %u\n", status_description(status).c_str(), status->m_poisoned, stacked_poison_value); } - stacked_poison_value += status->m_poisoned; - _DEBUG_MSG(1, "Virulence: %s spreads right poison +%u = %u\n", status_description(status).c_str(), status->m_poisoned, stacked_poison_value); - } - if (status->m_index + 1 < assaults.size()) - { - auto right_status = &assaults[status->m_index + 1]; - if (right_status->m_hp > 0) + if (status->m_index + 1 < assaults.size()) { - _DEBUG_MSG(1, "Virulence: spreads stacked poison +%u to %s\n", stacked_poison_value, status_description(right_status).c_str()); - right_status->m_poisoned += stacked_poison_value; + auto right_status = &assaults[status->m_index + 1]; + if (right_status->m_hp > 0) + { + _DEBUG_MSG(1, "Virulence: spreads stacked poison +%u to %s\n", stacked_poison_value, status_description(right_status).c_str()); + right_status->m_poisoned += stacked_poison_value; + } } + last_index = status->m_index; } - last_index = status->m_index; + } + // Revenge + if (fd->bg_effects[status->m_player].count(revenge)) + { + SkillSpec ss_heal{heal, fd->bg_effects[status->m_player].at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; + SkillSpec ss_rally{rally, fd->bg_effects[status->m_player].at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; + CardStatus * commander = &fd->players[status->m_player]->commander; + _DEBUG_MSG(2, "Revenge: Preparing skill %s and %s\n", skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); + od_skills.emplace_back(commander, ss_heal); + od_skills.emplace_back(commander, ss_rally); } } fd->skill_queue.insert(fd->skill_queue.begin(), od_skills.begin(), od_skills.end()); diff --git a/tyrant.h b/tyrant.h index 6c11d196..5b076dab 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.17.0" +#define TYRANT_OPTIMIZER_VERSION "2.17.1" #include #include From ef2da9c2bc8db1fe230972e5b7bce19591ce9e58 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 10 Feb 2016 23:47:35 -0500 Subject: [PATCH 400/406] Review on-death workflow to fix crash. --- sim.cpp | 21 +++++++++++++-------- tyrant.h | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/sim.cpp b/sim.cpp index 2384e91c..311a9059 100644 --- a/sim.cpp +++ b/sim.cpp @@ -354,6 +354,7 @@ void resolve_skill(Field* fd) fd->skill_queue.pop_front(); if (status->m_jammed) { + _DEBUG_MSG(2, "%s failed to %s because it is Jammed.", status_description(status).c_str(), skill_description(fd->cards, ss).c_str()); continue; } signed evolved_offset = status->m_evolved_skill_offset[ss.id]; @@ -734,7 +735,7 @@ void turn_end_phase(Field* fd) fd->inc_counter(QuestType::skill_damage, poison, 0, poison_dmg); } _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg); - remove_hp(fd, &status, poison_dmg); + remove_hp(fd, &status, poison_dmg); // simultaneous } } // end of the opponent's next turn for enemy units @@ -750,7 +751,7 @@ void turn_end_phase(Field* fd) // Active player's structure cards: // nothing so far - prepend_on_death(fd); + prepend_on_death(fd); // poison resolve_skill(fd); remove_dead(fd->tap->assaults); remove_dead(fd->tap->structures); @@ -839,6 +840,8 @@ struct PerformAttack } _DEBUG_MSG(1, "%s takes %u counter damage from %s\n", status_description(att_status).c_str(), counter_dmg, status_description(def_status).c_str()); remove_hp(fd, att_status, counter_dmg); + prepend_on_death(fd); + resolve_skill(fd); if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects[def_status->m_player].count(counterflux)) { unsigned flux_denominator = fd->bg_effects[def_status->m_player].at(counterflux) ? fd->bg_effects[def_status->m_player].at(counterflux) : 4; @@ -881,8 +884,6 @@ struct PerformAttack _DEBUG_MSG(1, "Heroism: %s gain %u attack\n", status_description(att_status).c_str(), valor_value); att_status->m_attack += valor_value; } - prepend_on_death(fd); - resolve_skill(fd); return att_dmg; } @@ -979,6 +980,8 @@ struct PerformAttack void attack_damage() { remove_hp(fd, def_status, att_dmg); + prepend_on_death(fd); + resolve_skill(fd); } template @@ -1064,6 +1067,8 @@ bool attack_phase(Field* fd) _DEBUG_MSG(1, "%s swipes %s for %u damage\n", status_description(att_status).c_str(), status_description(adj_status).c_str(), swipe_dmg); remove_hp(fd, adj_status, swipe_dmg); } + prepend_on_death(fd); + resolve_skill(fd); } } return false; @@ -1083,6 +1088,8 @@ bool attack_phase(Field* fd) _DEBUG_MSG(1, "%s swipes %s for %u damage\n", status_description(att_status).c_str(), status_description(adj_status).c_str(), swipe_dmg); remove_hp(fd, adj_status, swipe_dmg); } + prepend_on_death(fd); + resolve_skill(fd); } } else @@ -1621,7 +1628,6 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& _DEBUG_MSG(1, "TurningTides %u!\n", turningtides_value); perform_targetted_allied_fast(fd, &fd->players[src->m_player]->commander, ss_rally); } - prepend_on_death(fd); for (CardStatus * pb_status: paybackers) { ++ pb_status->m_paybacked; @@ -1636,7 +1642,6 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& perform_targetted_allied_fast(fd, &fd->players[pb_status->m_player]->commander, ss_rally); } } - prepend_on_death(fd); return; } for (CardStatus * dst: fd->selection_array) @@ -1651,14 +1656,14 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& } } } - prepend_on_death(fd); + prepend_on_death(fd); // skills for (CardStatus * pb_status: paybackers) { ++ pb_status->m_paybacked; _DEBUG_MSG(1, "%s Payback %s on %s\n", status_description(pb_status).c_str(), skill_short_description(s).c_str(), status_description(src).c_str()); perform_skill(fd, pb_status, src, s); } - prepend_on_death(fd); + prepend_on_death(fd); // paybacked skills } //------------------------------------------------------------------------------ diff --git a/tyrant.h b/tyrant.h index 5b076dab..8ef6ee37 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.17.1" +#define TYRANT_OPTIMIZER_VERSION "2.17.2" #include #include From d5932eb11a7005bb9a40dab4ea5d3cf68fe4db36 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Mon, 22 Feb 2016 01:04:09 -0500 Subject: [PATCH 401/406] Support BGE aliases (data/bges.txt). --- read.cpp | 49 +++++++++++++ read.h | 2 +- sim.cpp | 45 +++++++----- sim.h | 6 +- tyrant.h | 3 +- tyrant_optimize.cpp | 174 ++++++++++++++++++++++---------------------- 6 files changed, 169 insertions(+), 110 deletions(-) diff --git a/read.cpp b/read.cpp index d7ab04df..607dbd8a 100644 --- a/read.cpp +++ b/read.cpp @@ -478,3 +478,52 @@ void read_owned_cards(Cards& all_cards, std::map& owned_card } } +unsigned read_bge_aliases(std::unordered_map & bge_aliases, const std::string& filename) +{ + if(!boost::filesystem::exists(filename)) + { + return(0); + } + std::ifstream bgefile(filename); + if(!bgefile.is_open()) + { + std::cerr << "Error: BGE file " << filename << " could not be opened\n"; + return(2); + } + unsigned num_line(0); + bgefile.exceptions(std::ifstream::badbit); + try + { + while(bgefile && !bgefile.eof()) + { + std::string bge_string; + getline(bgefile, bge_string); + ++num_line; + if(bge_string.size() == 0 || strncmp(bge_string.c_str(), "//", 2) == 0) + { + continue; + } + std::string bge_name; + auto bge_string_iter = read_token(bge_string.begin(), bge_string.end(), [](char c){return(c == ':');}, bge_name); + if(bge_string_iter == bge_string.end() || bge_name.empty()) + { + std::cerr << "Error in BGE file " << filename << " at line " << num_line << ", could not read the name.\n"; + continue; + } + bge_string_iter = advance_until(bge_string_iter + 1, bge_string.end(), [](const char& c){return(c != ' ');}); + bge_aliases[simplify_name(bge_name)] = std::string{bge_string_iter, bge_string.end()}; + } + } + catch (std::exception& e) + { + std::cerr << "Exception while parsing the BGE file " << filename; + if(num_line > 0) + { + std::cerr << " at line " << num_line; + } + std::cerr << ": " << e.what() << ".\n"; + return(3); + } + return(0); +} + diff --git a/read.h b/read.h index 1d6ad280..edc642c0 100644 --- a/read.h +++ b/read.h @@ -3,7 +3,6 @@ #include #include -#include #include "deck.h" @@ -17,5 +16,6 @@ const std::pair, std::map> string_to_ids(con unsigned load_custom_decks(Decks& decks, Cards& cards, const std::string & filename); void read_owned_cards(Cards& cards, std::map& owned_cards, const std::string & filename); unsigned read_card_abbrs(Cards& cards, const std::string& filename); +unsigned read_bge_aliases(std::unordered_map & bge_aliases, const std::string & filename); #endif diff --git a/sim.cpp b/sim.cpp index 311a9059..d176576b 100644 --- a/sim.cpp +++ b/sim.cpp @@ -291,7 +291,7 @@ void prepend_on_death(Field* fd) } } // Virulence - if (fd->bg_effects[opponent(status->m_player)].count(virulence)) + if (fd->bg_effects.count(virulence)) { if (status->m_index != last_index + 1) { @@ -329,10 +329,10 @@ void prepend_on_death(Field* fd) } } // Revenge - if (fd->bg_effects[status->m_player].count(revenge)) + if (fd->bg_effects.count(revenge)) { - SkillSpec ss_heal{heal, fd->bg_effects[status->m_player].at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; - SkillSpec ss_rally{rally, fd->bg_effects[status->m_player].at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; + SkillSpec ss_heal{heal, fd->bg_effects.at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; + SkillSpec ss_rally{rally, fd->bg_effects.at(revenge), allfactions, 0, 0, no_skill, no_skill, true,}; CardStatus * commander = &fd->players[status->m_player]->commander; _DEBUG_MSG(2, "Revenge: Preparing skill %s and %s\n", skill_description(fd->cards, ss_heal).c_str(), skill_description(fd->cards, ss_rally).c_str()); od_skills.emplace_back(commander, ss_heal); @@ -842,9 +842,9 @@ struct PerformAttack remove_hp(fd, att_status, counter_dmg); prepend_on_death(fd); resolve_skill(fd); - if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects[def_status->m_player].count(counterflux)) + if (def_cardtype == CardType::assault && def_status->m_hp > 0 && fd->bg_effects.count(counterflux)) { - unsigned flux_denominator = fd->bg_effects[def_status->m_player].at(counterflux) ? fd->bg_effects[def_status->m_player].at(counterflux) : 4; + unsigned flux_denominator = fd->bg_effects.at(counterflux) ? fd->bg_effects.at(counterflux) : 4; unsigned flux_value = (def_status->skill(counter) - 1) / flux_denominator + 1; _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n", status_description(def_status).c_str(), flux_value); add_hp(fd, def_status, flux_value); @@ -868,9 +868,9 @@ struct PerformAttack { fd->inc_counter(QuestType::skill_use, berserk); } - if (fd->bg_effects[att_status->m_player].count(enduringrage)) + if (fd->bg_effects.count(enduringrage)) { - unsigned bge_denominator = fd->bg_effects[att_status->m_player].at(enduringrage) ? fd->bg_effects[att_status->m_player].at(enduringrage) : 2; + unsigned bge_denominator = fd->bg_effects.at(enduringrage) ? fd->bg_effects.at(enduringrage) : 2; unsigned bge_value = (berserk_value - 1) / bge_denominator + 1; _DEBUG_MSG(1, "EnduringRage: %s heals and protects itself for %u\n", status_description(att_status).c_str(), bge_value); add_hp(fd, att_status, bge_value); @@ -879,7 +879,7 @@ struct PerformAttack } do_leech(); unsigned valor_value = att_status->skill(valor); - if (valor_value > 0 && ! att_status->m_sundered && fd->bg_effects[att_status->m_player].count(heroism) && def_cardtype == CardType::assault && def_status->m_hp <= 0) + if (valor_value > 0 && ! att_status->m_sundered && fd->bg_effects.count(heroism) && def_cardtype == CardType::assault && def_status->m_hp <= 0) { _DEBUG_MSG(1, "Heroism: %s gain %u attack\n", status_description(att_status).c_str(), valor_value); att_status->m_attack += valor_value; @@ -939,7 +939,7 @@ struct PerformAttack std::string reduced_desc; unsigned reduced_dmg(0); unsigned armor_value = def_status->skill(armor); - if (def_status->m_card->m_type == CardType::assault && fd->bg_effects[def_status->m_player].count(fortification)) + if (def_status->m_card->m_type == CardType::assault && fd->bg_effects.count(fortification)) { for (auto && adj_status: fd->adjacent_assaults(def_status)) { @@ -969,7 +969,7 @@ struct PerformAttack if(!desc.empty()) { desc += "=" + to_string(att_dmg); } _DEBUG_MSG(1, "%s attacks %s for %u%s damage\n", status_description(att_status).c_str(), status_description(def_status).c_str(), pre_modifier_dmg, desc.c_str()); } - if (legion_value > 0 && can_be_healed(att_status) && fd->bg_effects[att_status->m_player].count(brigade)) + if (legion_value > 0 && can_be_healed(att_status) && fd->bg_effects.count(brigade)) { _DEBUG_MSG(1, "Brigade: %s heals itself for %u\n", status_description(att_status).c_str(), legion_value); add_hp(fd, att_status, legion_value); @@ -1098,9 +1098,9 @@ bool attack_phase(Field* fd) att_dmg = attack_commander(fd, att_status); } - if (att_dmg > 0 && !fd->assault_bloodlusted && fd->bg_effects[fd->tapi].count(bloodlust)) + if (att_dmg > 0 && !fd->assault_bloodlusted && fd->bg_effects.count(bloodlust)) { - fd->bloodlust_value += fd->bg_effects[fd->tapi].at(bloodlust); + fd->bloodlust_value += fd->bg_effects.at(bloodlust); fd->assault_bloodlusted = true; } @@ -1336,7 +1336,7 @@ inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, c template inline unsigned select_fast(Field* fd, CardStatus* src, const std::vector& cards, const SkillSpec& s) { - if (s.y == allfactions || fd->bg_effects[src->m_player].count(metamorphosis)) + if (s.y == allfactions || fd->bg_effects.count(metamorphosis)) { return(fd->make_selection_array(cards.begin(), cards.end(), [fd, src, s](CardStatus* c){return(skill_predicate(fd, src, c, s));})); } @@ -1563,7 +1563,7 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src, const SkillSpec& } check_and_perform_skill(fd, src, dst, s, false, has_counted_quest); } - if (num_inhibited > 0 && fd->bg_effects[opponent(src->m_player)].count(divert)) + if (num_inhibited > 0 && fd->bg_effects.count(divert)) { SkillSpec diverted_ss = s; diverted_ss.y = allfactions; @@ -1589,6 +1589,11 @@ void perform_targetted_allied_fast(Field* fd, CardStatus* src, const SkillSpec& void perform_targetted_allied_fast_rush(Field* fd, CardStatus* src, const SkillSpec& s) { + if (src->m_card->m_type == CardType::commander) + { // BGE skills are casted as by commander + perform_targetted_allied_fast(fd, src, s); + return; + } if (src->m_rush_attempted) { _DEBUG_MSG(2, "%s does not check Rush again.\n", status_description(src).c_str()); @@ -1605,7 +1610,7 @@ void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& select_targets(fd, src, s); bool has_counted_quest = false; std::vector paybackers; - if (fd->bg_effects[src->m_player].count(turningtides) && skill_id == weaken) + if (fd->bg_effects.count(turningtides) && skill_id == weaken) { unsigned turningtides_value = 0; for (CardStatus * dst: fd->selection_array) @@ -1717,8 +1722,8 @@ Results play(Field* fd) } if(__builtin_expect(fd->end, false)) { break; } - // Evaluate Heroism Battleground skills - if (fd->bg_effects[fd->tapi].count(heroism)) + // Evaluate Heroism BGE skills + if (fd->bg_effects.count(heroism)) { for (CardStatus * dst: fd->tap->assaults.m_indirect) { @@ -1730,7 +1735,7 @@ Results play(Field* fd) { _DEBUG_MSG(1, "Heroism: %s on %s but it is inhibited\n", skill_short_description(ss_protect).c_str(), status_description(dst).c_str()); -- dst->m_inhibited; - if (fd->bg_effects[fd->tipi].count(divert)) + if (fd->bg_effects.count(divert)) { SkillSpec diverted_ss = ss_protect; diverted_ss.y = allfactions; @@ -1759,7 +1764,7 @@ Results play(Field* fd) } } - // Evaluate activation Battleground skills + // Evaluate activation BGE skills for (const auto & bg_skill: fd->bg_skills[fd->tapi]) { _DEBUG_MSG(2, "Evaluating BG skill %s\n", skill_description(fd->cards, bg_skill).c_str()); diff --git a/sim.h b/sim.h index f1384466..404a9625 100644 --- a/sim.h +++ b/sim.h @@ -239,7 +239,7 @@ class Field gamemode_t gamemode; OptimizationMode optimization_mode; const Quest quest; - std::unordered_map bg_effects[2]; // passive BGE + std::unordered_map bg_effects; // passive BGE std::vector bg_skills[2]; // active BGE, casted every turn // With the introduction of on death skills, a single skill can trigger arbitrary many skills. // They are stored in this, and cleared after all have been performed. @@ -266,7 +266,7 @@ class Field unsigned quest_counter; Field(std::mt19937& re_, const Cards& cards_, Hand& hand1, Hand& hand2, gamemode_t gamemode_, OptimizationMode optimization_mode_, const Quest & quest_, - std::unordered_map& your_bg_effects_, std::unordered_map& enemy_bg_effects_, std::vector& your_bg_skills_, std::vector& enemy_bg_skills_) : + std::unordered_map& bg_effects_, std::vector& your_bg_skills_, std::vector& enemy_bg_skills_) : end{false}, re(re_), cards(cards_), @@ -275,7 +275,7 @@ class Field gamemode(gamemode_), optimization_mode(optimization_mode_), quest(quest_), - bg_effects{your_bg_effects_, enemy_bg_effects_}, + bg_effects{bg_effects_}, bg_skills{your_bg_skills_, enemy_bg_skills_}, assault_bloodlusted(false), bloodlust_value(0), diff --git a/tyrant.h b/tyrant.h index 8ef6ee37..bc555d03 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,11 +1,12 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.17.2" +#define TYRANT_OPTIMIZER_VERSION "2.18.0" #include #include #include +#include #include enum Faction diff --git a/tyrant_optimize.cpp b/tyrant_optimize.cpp index 209eb925..4e3ee05c 100644 --- a/tyrant_optimize.cpp +++ b/tyrant_optimize.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -448,11 +447,11 @@ struct SimulationData std::vector factors; gamemode_t gamemode; Quest quest; - std::unordered_map your_bg_effects, enemy_bg_effects; + std::unordered_map bg_effects; std::vector your_bg_skills, enemy_bg_skills; SimulationData(unsigned seed, const Cards& cards_, const Decks& decks_, unsigned num_enemy_decks_, std::vector factors_, gamemode_t gamemode_, Quest & quest_, - std::unordered_map& your_bg_effects_, std::unordered_map& enemy_bg_effects_, std::vector& your_bg_skills_, std::vector& enemy_bg_skills_) : + std::unordered_map& bg_effects_, std::vector& your_bg_skills_, std::vector& enemy_bg_skills_) : re(seed), cards(cards_), decks(decks_), @@ -462,8 +461,7 @@ struct SimulationData factors(factors_), gamemode(gamemode_), quest(quest_), - your_bg_effects(your_bg_effects_), - enemy_bg_effects(enemy_bg_effects_), + bg_effects(bg_effects_), your_bg_skills(your_bg_skills_), enemy_bg_skills(enemy_bg_skills_) { @@ -496,7 +494,7 @@ struct SimulationData { your_hand.reset(re); enemy_hand->reset(re); - Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, quest, your_bg_effects, enemy_bg_effects, your_bg_skills, enemy_bg_skills); + Field fd(re, cards, your_hand, *enemy_hand, gamemode, optimization_mode, quest, bg_effects, your_bg_skills, enemy_bg_skills); Results result(play(&fd)); res.emplace_back(result); } @@ -526,11 +524,11 @@ class Process std::vector factors; gamemode_t gamemode; Quest quest; - std::unordered_map your_bg_effects, enemy_bg_effects; + std::unordered_map bg_effects; std::vector your_bg_skills, enemy_bg_skills; Process(unsigned num_threads_, const Cards& cards_, const Decks& decks_, Deck* your_deck_, std::vector enemy_decks_, std::vector factors_, gamemode_t gamemode_, Quest & quest_, - std::unordered_map& your_bg_effects_, std::unordered_map& enemy_bg_effects_, std::vector& your_bg_skills_, std::vector& enemy_bg_skills_) : + std::unordered_map& bg_effects_, std::vector& your_bg_skills_, std::vector& enemy_bg_skills_) : num_threads(num_threads_), main_barrier(num_threads+1), cards(cards_), @@ -540,8 +538,7 @@ class Process factors(factors_), gamemode(gamemode_), quest(quest_), - your_bg_effects(your_bg_effects_), - enemy_bg_effects(enemy_bg_effects_), + bg_effects(bg_effects_), your_bg_skills(your_bg_skills_), enemy_bg_skills(enemy_bg_skills_) { @@ -553,7 +550,7 @@ class Process } for(unsigned i(0); i < num_threads; ++i) { - threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, quest, your_bg_effects, enemy_bg_effects, your_bg_skills, enemy_bg_skills)); + threads_data.push_back(new SimulationData(seed + i, cards, decks, enemy_decks.size(), factors, gamemode, quest, bg_effects, your_bg_skills, enemy_bg_skills)); threads.push_back(new boost::thread(thread_evaluate, std::ref(main_barrier), std::ref(shared_mutex), std::ref(*threads_data.back()), std::ref(*this), i)); } } @@ -1239,8 +1236,8 @@ int main(int argc, char** argv) bool opt_do_optimization(false); bool opt_keep_commander{false}; std::vector> opt_todo; - std::vector opt_effects[2]; - std::unordered_map opt_bg_effects[2]; + std::vector opt_effects[3]; // 0-you; 1-enemy; 2-global + std::unordered_map opt_bg_effects; std::vector opt_bg_skills[2]; for(int argIndex = 3; argIndex < argc; ++argIndex) @@ -1326,8 +1323,7 @@ int main(int argc, char** argv) } else if (strcmp(argv[argIndex], "effect") == 0 || strcmp(argv[argIndex], "-e") == 0) { - opt_effects[0].push_back(argv[argIndex + 1]); - opt_effects[1].push_back(argv[argIndex + 1]); + opt_effects[2].push_back(argv[argIndex + 1]); argIndex += 1; } else if (strcmp(argv[argIndex], "ye") == 0 || strcmp(argv[argIndex], "yeffect") == 0) @@ -1524,6 +1520,7 @@ int main(int argc, char** argv) Cards all_cards; Decks decks; + std::unordered_map bge_aliases; load_skills_set_xml(all_cards, "data/skills_set.xml", true); for (unsigned section = 0; section <= 10; ++ section) { @@ -1540,6 +1537,7 @@ int main(int argc, char** argv) { load_custom_decks(decks, all_cards, "data/customdecks" + suffix + ".txt"); } + read_bge_aliases(bge_aliases, "data/bges.txt"); fill_skill_table(); @@ -1562,9 +1560,9 @@ int main(int argc, char** argv) } } - for (int player = 0; player <= 1; ++ player) + for (int player = 0; player <= 2; ++ player) { - for (const auto & opt_effect: opt_effects[player]) + for (auto && opt_effect: opt_effects[player]) { if (opt_effect.empty()) { @@ -1572,74 +1570,91 @@ int main(int argc, char** argv) } try { - std::vector tokens; - boost::split(tokens, opt_effect, boost::is_any_of(" -")); - Skill skill_id = skill_name_to_id(tokens[0]); - unsigned skill_index = 1; - if (BEGIN_BGE_SKILL < skill_id && skill_id < END_BGE_SKILL) - { - // passive BGE - if (skill_index < tokens.size()) - { - opt_bg_effects[player][skill_id] = boost::lexical_cast(tokens[skill_index]); - } - else - { - opt_bg_effects[player][skill_id] = 0; - } - } - else if (skill_table[skill_id] != nullptr) + std::vector tokens, skill_name_list; + const auto bge_itr = bge_aliases.find(simplify_name(opt_effect)); + boost::split(tokens, bge_itr == bge_aliases.end() ? opt_effect : bge_itr->second, boost::is_any_of(" -")); + boost::split(skill_name_list, tokens[0], boost::is_any_of("+")); + for (auto && skill_name: skill_name_list) { - // activation BG skill - SkillSpec bg_skill{skill_id, 0, allfactions, 0, 0, no_skill, no_skill, false}; - if (skill_index < tokens.size() && boost::to_lower_copy(tokens[skill_index]) == "all") - { - bg_skill.all = true; - skill_index += 1; - } - else if (skill_index + 1 < tokens.size() && isdigit(*tokens[skill_index].c_str())) + Skill skill_id = skill_name_to_id(skill_name); + unsigned skill_index = 1; + if (BEGIN_BGE_SKILL < skill_id && skill_id < END_BGE_SKILL) { - bg_skill.n = boost::lexical_cast(tokens[skill_index]); - skill_index += 1; + // passive BGE (must be global) + if (player != 2) + { + throw std::runtime_error("must be global"); + } + if (skill_index < tokens.size()) + { + opt_bg_effects[skill_id] = boost::lexical_cast(tokens[skill_index]); + } + else + { + opt_bg_effects[skill_id] = 0; + } } - if (skill_index < tokens.size()) + else if (skill_table[skill_id] != nullptr) { - bg_skill.s = skill_name_to_id(tokens[skill_index]); - if (bg_skill.s != no_skill) + // activation BG skill + SkillSpec bg_skill{skill_id, 0, allfactions, 0, 0, no_skill, no_skill, false}; + if (skill_index < tokens.size() && boost::to_lower_copy(tokens[skill_index]) == "all") + { + bg_skill.all = true; + skill_index += 1; + } + else if (skill_index + 1 < tokens.size() && isdigit(*tokens[skill_index].c_str())) { + bg_skill.n = boost::lexical_cast(tokens[skill_index]); skill_index += 1; - if (skill_index < tokens.size() && boost::to_lower_copy(tokens[skill_index]) == "to") + } + if (skill_index < tokens.size()) + { + bg_skill.s = skill_name_to_id(tokens[skill_index]); + if (bg_skill.s != no_skill) { skill_index += 1; - } - if (skill_index < tokens.size()) - { - bg_skill.s2 = skill_name_to_id(tokens[skill_index]); - if (bg_skill.s2 != no_skill) + if (skill_index < tokens.size() && (boost::to_lower_copy(tokens[skill_index]) == "to" || boost::to_lower_copy(tokens[skill_index]) == "into")) { skill_index += 1; } + if (skill_index < tokens.size()) + { + bg_skill.s2 = skill_name_to_id(tokens[skill_index]); + if (bg_skill.s2 != no_skill) + { + skill_index += 1; + } + } } } - } - if (skill_index < tokens.size()) - { - if (bg_skill.id == jam || bg_skill.id == overload) + if (skill_index < tokens.size()) { - bg_skill.n = boost::lexical_cast(tokens[skill_index]); + if (bg_skill.id == jam || bg_skill.id == overload) + { + bg_skill.n = boost::lexical_cast(tokens[skill_index]); + } + else + { + bg_skill.x = boost::lexical_cast(tokens[skill_index]); + } + } + if (player == 2) + { + opt_bg_skills[0].push_back(bg_skill); + opt_bg_skills[1].push_back(bg_skill); } else { - bg_skill.x = boost::lexical_cast(tokens[skill_index]); + opt_bg_skills[player].push_back(bg_skill); } } - opt_bg_skills[player].push_back(bg_skill); - } - else - { - std::cerr << "Error: unrecognized effect \"" << opt_effect << "\".\n"; - print_available_effects(); - return 0; + else + { + std::cerr << "Error: unrecognized effect \"" << opt_effect << "\".\n"; + print_available_effects(); + return 0; + } } } catch (const boost::bad_lexical_cast & e) @@ -1649,7 +1664,7 @@ int main(int argc, char** argv) } catch (std::exception & e) { - std::cerr << "Error: effect \"" << opt_effect << ": " << e.what() << "\".\n"; + std::cerr << "Error: effect \"" << opt_effect << "\": " << e.what() << ".\n"; return 0; } } @@ -1957,27 +1972,20 @@ int main(int argc, char** argv) if (debug_print >= 0) { std::cout << "Your Deck: " << (debug_print > 0 ? your_deck->long_description() : your_deck->medium_description()) << std::endl; - for (const auto & bg_effect: opt_bg_effects[0]) - { - if (bg_effect.second == 0) - { - std::cout << "BG Effect: " << skill_names[bg_effect.first] << std::endl; - } - else - { - std::cout << "BG Effect: " << skill_names[bg_effect.first] << " " << bg_effect.second << std::endl; - } - } for (const auto & bg_skill: opt_bg_skills[0]) { - std::cout << "BG Skill: " << skill_description(all_cards, bg_skill) << std::endl; + std::cout << "Your BG Skill: " << skill_description(all_cards, bg_skill) << std::endl; } for (unsigned i(0); i < enemy_decks.size(); ++i) { std::cout << "Enemy's Deck:" << enemy_decks_factors[i] << ": " << (debug_print > 0 ? enemy_decks[i]->long_description() : enemy_decks[i]->medium_description()) << std::endl; } - for (const auto & bg_effect: opt_bg_effects[1]) + for (const auto & bg_skill: opt_bg_skills[1]) + { + std::cout << "Enemy's BG Skill: " << skill_description(all_cards, bg_skill) << std::endl; + } + for (const auto & bg_effect: opt_bg_effects) { if (bg_effect.second == 0) { @@ -1988,13 +1996,9 @@ int main(int argc, char** argv) std::cout << "BG Effect: " << skill_names[bg_effect.first] << " " << bg_effect.second << std::endl; } } - for (const auto & bg_skill: opt_bg_skills[1]) - { - std::cout << "BG Skill: " << skill_description(all_cards, bg_skill) << std::endl; - } } - Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, quest, opt_bg_effects[0], opt_bg_effects[1], opt_bg_skills[0], opt_bg_skills[1]); + Process p(opt_num_threads, all_cards, decks, your_deck, enemy_decks, enemy_decks_factors, gamemode, quest, opt_bg_effects, opt_bg_skills[0], opt_bg_skills[1]); for(auto op: opt_todo) { From 91b3cddf5cc171f7c54e7818d2775fef1e8a68f0 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Wed, 24 Feb 2016 18:34:33 -0500 Subject: [PATCH 402/406] Support skill Allegiance. --- sim.cpp | 13 +++++++++++++ tyrant.cpp | 2 +- tyrant.h | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sim.cpp b/sim.cpp index d176576b..2d3b6da8 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1721,6 +1721,19 @@ Results play(Field* fd) } } if(__builtin_expect(fd->end, false)) { break; } + // Evaluate skill Allegiance + for (CardStatus * status : fd->tap->assaults.m_indirect) + { + unsigned allegiance_value = status->skill(allegiance); + if (allegiance_value > 0 && status->m_card->m_faction == played_card->m_faction) + { + _DEBUG_MSG(1, "%s activates Allegiance %u\n", status_description(status).c_str(), allegiance_value); + if (! status->m_sundered) + { status->m_attack += allegiance_value; } + status->m_max_hp += allegiance_value; + status->m_hp += allegiance_value; + } + } // Evaluate Heroism BGE skills if (fd->bg_effects.count(heroism)) diff --git a/tyrant.cpp b/tyrant.cpp index 20350eee..ef532eb9 100644 --- a/tyrant.cpp +++ b/tyrant.cpp @@ -27,7 +27,7 @@ std::string skill_names[Skill::num_skills] = // Damage-Dependant: "Berserk", "Inhibit", "Leech", "Poison", // Triggered: - "Flurry", "Valor", + "Allegiance", "Flurry", "Valor", // Pseudo-skill for passive BGEs: "", "Bloodlust", "Brigade", "Counterflux", "Divert", "EnduringRage", "Fortification", "Heroism", "Metamorphosis", "Revenge", "TurningTides", "Virulence", diff --git a/tyrant.h b/tyrant.h index bc555d03..0eb4c4cb 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.18.0" +#define TYRANT_OPTIMIZER_VERSION "2.19.0" #include #include @@ -44,7 +44,7 @@ enum Skill // Damage-Dependent: berserk, inhibit, leech, poison, // Triggered: - flurry, valor, + allegiance, flurry, valor, // Pseudo-Skill for BGE: BEGIN_BGE_SKILL, bloodlust, brigade, counterflux, divert, enduringrage, fortification, heroism, metamorphosis, revenge, turningtides, virulence, From 9718543c84e24ef3de51ad49efa47338e48ff188 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 25 Feb 2016 13:48:54 -0500 Subject: [PATCH 403/406] Support Raid #17 "Warden Raid". --- data/raids.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/data/raids.xml b/data/raids.xml index 5da34f88..3662936f 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -364,6 +364,27 @@ + + 17 + Warden Raid + 25026 + 26 + + + 17192 + 17192 + 37474 + 37474 + 37484 + 37484 + 37494 + 37494 + 37504 + 37504 + + + + From 2bca30a2969daf99ee2a3286517ff342005bfa6c Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 25 Feb 2016 13:49:43 -0500 Subject: [PATCH 404/406] Fix bug: crash. --- sim.cpp | 25 +++++++++++++------------ tyrant.h | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/sim.cpp b/sim.cpp index 2d3b6da8..86032988 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1719,21 +1719,22 @@ Results play(Field* fd) assert(false); break; } - } - if(__builtin_expect(fd->end, false)) { break; } - // Evaluate skill Allegiance - for (CardStatus * status : fd->tap->assaults.m_indirect) - { - unsigned allegiance_value = status->skill(allegiance); - if (allegiance_value > 0 && status->m_card->m_faction == played_card->m_faction) + // Evaluate skill Allegiance + for (CardStatus * status : fd->tap->assaults.m_indirect) { - _DEBUG_MSG(1, "%s activates Allegiance %u\n", status_description(status).c_str(), allegiance_value); - if (! status->m_sundered) - { status->m_attack += allegiance_value; } - status->m_max_hp += allegiance_value; - status->m_hp += allegiance_value; + unsigned allegiance_value = status->skill(allegiance); + assert(status->m_card); + if (allegiance_value > 0 && status->m_hp > 0 && status->m_card->m_faction == played_card->m_faction) + { + _DEBUG_MSG(1, "%s activates Allegiance %u\n", status_description(status).c_str(), allegiance_value); + if (! status->m_sundered) + { status->m_attack += allegiance_value; } + status->m_max_hp += allegiance_value; + status->m_hp += allegiance_value; + } } } + if(__builtin_expect(fd->end, false)) { break; } // Evaluate Heroism BGE skills if (fd->bg_effects.count(heroism)) diff --git a/tyrant.h b/tyrant.h index 0eb4c4cb..12812793 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.19.0" +#define TYRANT_OPTIMIZER_VERSION "2.19.1" #include #include From 01b0edaa2af5fa940ba1b1d863fb7dc49e3154f1 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 25 Feb 2016 14:26:23 -0500 Subject: [PATCH 405/406] Fix bug: Allegiance should be triggered only by *another* card. --- sim.cpp | 29 +++++++++++++++-------------- tyrant.h | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/sim.cpp b/sim.cpp index 86032988..17ecdb30 100644 --- a/sim.cpp +++ b/sim.cpp @@ -1705,20 +1705,6 @@ Results play(Field* fd) const Card* played_card(fd->tap->deck->next()); if(played_card) { - switch(played_card->m_type) - { - case CardType::assault: - PlayCard(played_card, fd).op(); - break; - case CardType::structure: - PlayCard(played_card, fd).op(); - break; - case CardType::commander: - case CardType::num_cardtypes: - _DEBUG_MSG(0, "Unknown card type: #%u %s: %u\n", played_card->m_id, card_description(fd->cards, played_card).c_str(), played_card->m_type); - assert(false); - break; - } // Evaluate skill Allegiance for (CardStatus * status : fd->tap->assaults.m_indirect) { @@ -1733,6 +1719,21 @@ Results play(Field* fd) status->m_hp += allegiance_value; } } + // End Evaluate skill Allegiance + switch(played_card->m_type) + { + case CardType::assault: + PlayCard(played_card, fd).op(); + break; + case CardType::structure: + PlayCard(played_card, fd).op(); + break; + case CardType::commander: + case CardType::num_cardtypes: + _DEBUG_MSG(0, "Unknown card type: #%u %s: %u\n", played_card->m_id, card_description(fd->cards, played_card).c_str(), played_card->m_type); + assert(false); + break; + } } if(__builtin_expect(fd->end, false)) { break; } diff --git a/tyrant.h b/tyrant.h index 12812793..6cd529c0 100644 --- a/tyrant.h +++ b/tyrant.h @@ -1,7 +1,7 @@ #ifndef TYRANT_H_INCLUDED #define TYRANT_H_INCLUDED -#define TYRANT_OPTIMIZER_VERSION "2.19.1" +#define TYRANT_OPTIMIZER_VERSION "2.19.2" #include #include From f700fe543e2fc67fc73bf7da5068414ad8ca4f45 Mon Sep 17 00:00:00 2001 From: andor9 <9andor@gmail.com> Date: Thu, 25 Feb 2016 17:38:46 -0500 Subject: [PATCH 406/406] Update raid deck. --- data/raids.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/raids.xml b/data/raids.xml index 3662936f..3654cbdd 100644 --- a/data/raids.xml +++ b/data/raids.xml @@ -371,7 +371,6 @@ 26 - 17192 17192 37474 37474 @@ -381,6 +380,7 @@ 37494 37504 37504 + 37504