From d5b471b68219dbba5d36d6eb39e4cec09ffb739e Mon Sep 17 00:00:00 2001 From: Thomas Schmucker Date: Sun, 5 Jan 2025 13:46:03 +0100 Subject: erster Versuch einer Lösungsannäherung... :) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 2015/src/day22.cpp | 560 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 560 insertions(+) create mode 100644 2015/src/day22.cpp (limited to '2015/src/day22.cpp') diff --git a/2015/src/day22.cpp b/2015/src/day22.cpp new file mode 100644 index 0000000..85b7259 --- /dev/null +++ b/2015/src/day22.cpp @@ -0,0 +1,560 @@ +#include +#include +#include +#include +#include +#include +#include + +#undef NDEBUG +#include + +using namespace std; + +#if 0 +auto& debug = cerr; +#else +struct NullBuffer : streambuf { + int overflow(int chr) override { return chr; } +}; +NullBuffer nullBuffer; +ostream debug{ &nullBuffer }; +#endif + +using Mana = int; +using HitPoints = int; +using Damage = int; +using Health = int; +using Armor = int; + +class Player; +class Boss; +class Spell; + +shared_ptr chooseRandomSpell(Player& player); + +class Spell { +protected: + Spell(string_view name, Mana mana) + : name_(name) + , mana_(mana) + { + } + +public: + Spell(const Spell&) = default; + Spell(Spell&&) = delete; + Spell& operator=(const Spell&) = delete; + Spell& operator=(Spell&&) = delete; + virtual ~Spell() = default; + + virtual void Play(Player& player, Boss& boss) = 0; + + virtual int Timer() + { + return 0; + } + + const string name_; + const Mana mana_; +}; + +class Player { +public: + // NOLINTBEGIN + Player(Mana mana, HitPoints hit_points) + : mana_(mana) + , hit_points_(hit_points) + { + } + + void AddMana(Mana mana) + { + assert(mana > 0); + mana_ += mana; + } + + void SubMana(Mana mana) + { + assert(mana > 0); + mana_ -= mana; + } + + void AddArmor(Armor armor) + { + assert(armor > 0); + armor_ += armor; + } + + void SubArmor(Armor armor) + { + assert(armor > 0); + armor_ -= armor; + } + + void AddHitPoints(HitPoints hit_points) + { + assert(hit_points > 0); + hit_points_ += hit_points; + } + + void SubHitPoints(HitPoints hit_points) + { + assert(hit_points > 0); + hit_points_ -= hit_points; + } + // NOLINTEND + + void Damage(Damage damage) + { + SubHitPoints(max(1, damage - armor_)); + } + + [[nodiscard]] bool CanCastSpell(const shared_ptr& spell) const + { + return mana_ >= spell->mana_; + } + + [[nodiscard]] bool HasLost() const + { + return hit_points_ <= 0; + } + + Mana mana_; + HitPoints hit_points_; + Armor armor_{}; +}; + +class Spells { +public: + Spells() = default; + + void AddSpell(const shared_ptr& spell, Player& player, Boss& boos) + { + debug << "Player casts " << spell->name_ << endl; + player.SubMana(spell->mana_); + + if ( spell->Timer() == 0 ) { + spell->Play(player, boos); + } + else { + effects_.push_back(spell); + } + } + + bool TryAddRandomSpell(Player& player, Boss& boss) + { + while ( true ) { + auto randomSpell = chooseRandomSpell(player); + if ( randomSpell == nullptr ) { + return false; + } + + auto iter = ranges::find_if(effects_, [&randomSpell](const auto& effect) { return effect->name_ == randomSpell->name_; }); + if ( iter != effects_.end() ) { + continue; + } + + AddSpell(randomSpell, player, boss); + } + } + + void Play(Player& player, Boss& boss) + { + for ( const auto& effect: effects_ ) { + effect->Play(player, boss); + } + + effects_.remove_if([](const auto& effect) { + return effect->Timer() == 0; + }); + } + +private: + list> effects_; +}; + +class Boss { + static auto + split(const string& line, char sep = ' ') + { + vector parts; + stringstream input{ line }; + + for ( string part; getline(input, part, sep); ) { + parts.emplace_back(part); + } + + return parts; + } + +public: + explicit Boss(string_view filename) + { + fstream input{ filename }; + + for ( string line; getline(input, line); ) { + auto parts = Boss::split(line); + + if ( parts[0] == "Hit" ) { + hit_points_ = stoi(parts[2]); + } + else if ( parts[0] == "Damage:" ) { + damage_ = stoi(parts[1]); + } + } + } + + Boss(HitPoints hit_points, Damage damage) // NOLINT + : hit_points_(hit_points) + , damage_(damage) + { + } + + void Play(Player& player) const + { + debug << "Boss attacks for " << damage_ << " damage" << endl; + player.Damage(damage_); + } + + void SubHitPoints(HitPoints hit_points) + { + assert(hit_points > 0); // NOLINT + hit_points_ -= hit_points; + } + + [[nodiscard]] bool HasLost() const + { + return hit_points_ <= 0; + } + + HitPoints hit_points_; + Damage damage_; +}; + +ostream& +operator<<(ostream& ostream, const Player& player) +{ + ostream << "- Player has " << player.hit_points_ << " hit points, " << player.armor_ << " armor, " << player.mana_ << " mana"; + return ostream; +} + +ostream& +operator<<(ostream& ostream, const Boss& boss) +{ + ostream << "- Boss has " << boss.hit_points_ << " hit points"; + return ostream; +} + +class MagicMissile : public Spell { +public: + MagicMissile() + : Spell("Magic Missile", Mana(Cost)) + { + } + + void Play(Player& /*player*/, Boss& boss) override + { + boss.SubHitPoints(4); + + debug << "Magic Missile deals 4 damage" << endl; + } + + static const int Cost = 53; +}; + +class Drain : public Spell { +public: + Drain() + : Spell("Drain", Mana(Cost)) + { + } + + void Play(Player& player, Boss& boss) override + { + player.AddHitPoints(2); + boss.SubHitPoints(2); + debug << "Drain deals 2 damage, and heals 2 hit points" << endl; + } + + static const int Cost = 73; +}; + +class Effect : public Spell { +protected: + Effect(string_view name, Mana mana, int timer) // NOLINT + : Spell(name, mana) + , timer_(timer) + { + } + +public: + int Timer() final + { + return timer_; + } + +protected: + int timer_; +}; + +class Shield : public Effect { +public: + explicit Shield(Player& player) + : Effect("Shield", Mana(Cost), RunTime) + , player_(player) + { + debug << "Increasing Armor by " << Armor << endl; + player_.AddArmor(Armor); + } + + ~Shield() override + { + debug << "Decreasing Armor by " << Armor << endl; + player_.SubArmor(Armor); + } + + void Play(Player& /*player*/, Boss& /*boss*/) override + { + timer_--; + debug << "Shields timer is now " << timer_ << endl; + } + + static const Armor Armor = 7; + static const int Cost = 113; + static const int RunTime = 6; + +private: + Player& player_; +}; + +class Poison : public Effect { +public: + Poison() + : Effect("Poison", Mana(Cost), RunTime) + { + } + + void Play(Player& /*player*/, Boss& boss) override + { + timer_--; + + debug << name_ << " deals 3 damage; its timer is now " << timer_ << endl; + boss.SubHitPoints(3); + } + + static const int Cost = 173; + static const int RunTime = 6; +}; + +class Recharge : public Effect { +public: + Recharge() + : Effect("Recharge", Mana(Cost), RunTime) + { + } + + ~Recharge() override + { + debug << "Recharge wears off" << endl; + } + + void Play(Player& player, Boss& /*boss*/) override + { + static const Mana AdditionalMana = 101; + + timer_--; + player.AddMana(AdditionalMana); + debug << name_ << " provides 101 mana; its timer is now " << timer_ << endl; + } + + static const int Cost = 229; + static const int RunTime = 5; +}; + +shared_ptr +chooseRandomSpell(Player& player) +{ + if ( player.mana_ < min({ MagicMissile::Cost, Drain::Cost, Shield::Cost, Poison::Cost, Recharge::Cost }) ) { + return nullptr; + } + + while ( true ) { + static const int NumSpells = 5; + + auto selection = rand() % NumSpells; + + if ( selection == 0 && player.mana_ >= MagicMissile::Cost ) { + return make_shared(); + } + if ( selection == 1 && player.mana_ >= Drain::Cost ) { + return make_shared(); + } + if ( selection == 2 && player.mana_ >= Shield::Cost ) { + return make_shared(player); + } + if ( selection == 3 && player.mana_ >= Poison::Cost ) { + return make_shared(); + } + if ( selection == 4 && player.mana_ >= Recharge::Cost ) { + return make_shared(); + } + } +} + +void +battle1() +{ + Player player(250, 10); + Boss boss(13, 8); + Spells spells; + + // ---------------------- + + cout << "\n-- Player turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + spells.AddSpell(make_shared(), player, boss); + + // ---------------------- + + cout << "\n-- Boss turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + boss.Play(player); + + // ---------------------- + + cout << "\n-- Player turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + spells.AddSpell(make_shared(), player, boss); + + // ---------------------- + + cout << "\n-- Boss turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + + if ( boss.HasLost() ) { + cout << "Boss killed" << endl; + } +} + +void +battle2() +{ + Player player(250, 10); + Boss boss(14, 8); + Spells spells; + + // ---------------------- + + cout << "\n-- Player turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + spells.AddSpell(make_shared(), player, boss); + + // ---------------------- + + cout << "\n-- Boss turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + boss.Play(player); + + // ---------------------- + + cout << "\n-- Player turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + spells.AddSpell(make_shared(player), player, boss); + + // ---------------------- + + cout << "\n-- Boss turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + boss.Play(player); + + // ---------------------- + + cout << "\n-- Player turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + spells.AddSpell(make_shared(), player, boss); + + // ---------------------- + + cout << "\n-- Boss turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + boss.Play(player); + + // ---------------------- + + cout << "\n-- Player turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + spells.AddSpell(make_shared(), player, boss); + + // ---------------------- + + cout << "\n-- Boss turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + boss.Play(player); + + // ---------------------- + + cout << "\n-- Player turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + spells.AddSpell(make_shared(), player, boss); + + // ---------------------- + + cout << "\n-- Boss turn --\n"; + cout << player << '\n' + << boss << endl; + + spells.Play(player, boss); + boss.Play(player); + + if ( boss.HasLost() ) { + cout << "Boss killed" << endl; + } + + cout << "End of Battle" << endl; +} + +int +main() +{ + battle2(); +} -- cgit v1.3