aboutsummaryrefslogtreecommitdiff
path: root/2015
diff options
context:
space:
mode:
Diffstat (limited to '2015')
-rw-r--r--2015/src/day22.cpp560
1 files changed, 560 insertions, 0 deletions
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 @@
1#include <algorithm>
2#include <fstream>
3#include <iostream>
4#include <list>
5#include <memory>
6#include <sstream>
7#include <string>
8
9#undef NDEBUG
10#include <cassert>
11
12using namespace std;
13
14#if 0
15auto& debug = cerr;
16#else
17struct NullBuffer : streambuf {
18 int overflow(int chr) override { return chr; }
19};
20NullBuffer nullBuffer;
21ostream debug{ &nullBuffer };
22#endif
23
24using Mana = int;
25using HitPoints = int;
26using Damage = int;
27using Health = int;
28using Armor = int;
29
30class Player;
31class Boss;
32class Spell;
33
34shared_ptr<Spell> chooseRandomSpell(Player& player);
35
36class Spell {
37protected:
38 Spell(string_view name, Mana mana)
39 : name_(name)
40 , mana_(mana)
41 {
42 }
43
44public:
45 Spell(const Spell&) = default;
46 Spell(Spell&&) = delete;
47 Spell& operator=(const Spell&) = delete;
48 Spell& operator=(Spell&&) = delete;
49 virtual ~Spell() = default;
50
51 virtual void Play(Player& player, Boss& boss) = 0;
52
53 virtual int Timer()
54 {
55 return 0;
56 }
57
58 const string name_;
59 const Mana mana_;
60};
61
62class Player {
63public:
64 // NOLINTBEGIN
65 Player(Mana mana, HitPoints hit_points)
66 : mana_(mana)
67 , hit_points_(hit_points)
68 {
69 }
70
71 void AddMana(Mana mana)
72 {
73 assert(mana > 0);
74 mana_ += mana;
75 }
76
77 void SubMana(Mana mana)
78 {
79 assert(mana > 0);
80 mana_ -= mana;
81 }
82
83 void AddArmor(Armor armor)
84 {
85 assert(armor > 0);
86 armor_ += armor;
87 }
88
89 void SubArmor(Armor armor)
90 {
91 assert(armor > 0);
92 armor_ -= armor;
93 }
94
95 void AddHitPoints(HitPoints hit_points)
96 {
97 assert(hit_points > 0);
98 hit_points_ += hit_points;
99 }
100
101 void SubHitPoints(HitPoints hit_points)
102 {
103 assert(hit_points > 0);
104 hit_points_ -= hit_points;
105 }
106 // NOLINTEND
107
108 void Damage(Damage damage)
109 {
110 SubHitPoints(max(1, damage - armor_));
111 }
112
113 [[nodiscard]] bool CanCastSpell(const shared_ptr<Spell>& spell) const
114 {
115 return mana_ >= spell->mana_;
116 }
117
118 [[nodiscard]] bool HasLost() const
119 {
120 return hit_points_ <= 0;
121 }
122
123 Mana mana_;
124 HitPoints hit_points_;
125 Armor armor_{};
126};
127
128class Spells {
129public:
130 Spells() = default;
131
132 void AddSpell(const shared_ptr<Spell>& spell, Player& player, Boss& boos)
133 {
134 debug << "Player casts " << spell->name_ << endl;
135 player.SubMana(spell->mana_);
136
137 if ( spell->Timer() == 0 ) {
138 spell->Play(player, boos);
139 }
140 else {
141 effects_.push_back(spell);
142 }
143 }
144
145 bool TryAddRandomSpell(Player& player, Boss& boss)
146 {
147 while ( true ) {
148 auto randomSpell = chooseRandomSpell(player);
149 if ( randomSpell == nullptr ) {
150 return false;
151 }
152
153 auto iter = ranges::find_if(effects_, [&randomSpell](const auto& effect) { return effect->name_ == randomSpell->name_; });
154 if ( iter != effects_.end() ) {
155 continue;
156 }
157
158 AddSpell(randomSpell, player, boss);
159 }
160 }
161
162 void Play(Player& player, Boss& boss)
163 {
164 for ( const auto& effect: effects_ ) {
165 effect->Play(player, boss);
166 }
167
168 effects_.remove_if([](const auto& effect) {
169 return effect->Timer() == 0;
170 });
171 }
172
173private:
174 list<shared_ptr<Spell>> effects_;
175};
176
177class Boss {
178 static auto
179 split(const string& line, char sep = ' ')
180 {
181 vector<string> parts;
182 stringstream input{ line };
183
184 for ( string part; getline(input, part, sep); ) {
185 parts.emplace_back(part);
186 }
187
188 return parts;
189 }
190
191public:
192 explicit Boss(string_view filename)
193 {
194 fstream input{ filename };
195
196 for ( string line; getline(input, line); ) {
197 auto parts = Boss::split(line);
198
199 if ( parts[0] == "Hit" ) {
200 hit_points_ = stoi(parts[2]);
201 }
202 else if ( parts[0] == "Damage:" ) {
203 damage_ = stoi(parts[1]);
204 }
205 }
206 }
207
208 Boss(HitPoints hit_points, Damage damage) // NOLINT
209 : hit_points_(hit_points)
210 , damage_(damage)
211 {
212 }
213
214 void Play(Player& player) const
215 {
216 debug << "Boss attacks for " << damage_ << " damage" << endl;
217 player.Damage(damage_);
218 }
219
220 void SubHitPoints(HitPoints hit_points)
221 {
222 assert(hit_points > 0); // NOLINT
223 hit_points_ -= hit_points;
224 }
225
226 [[nodiscard]] bool HasLost() const
227 {
228 return hit_points_ <= 0;
229 }
230
231 HitPoints hit_points_;
232 Damage damage_;
233};
234
235ostream&
236operator<<(ostream& ostream, const Player& player)
237{
238 ostream << "- Player has " << player.hit_points_ << " hit points, " << player.armor_ << " armor, " << player.mana_ << " mana";
239 return ostream;
240}
241
242ostream&
243operator<<(ostream& ostream, const Boss& boss)
244{
245 ostream << "- Boss has " << boss.hit_points_ << " hit points";
246 return ostream;
247}
248
249class MagicMissile : public Spell {
250public:
251 MagicMissile()
252 : Spell("Magic Missile", Mana(Cost))
253 {
254 }
255
256 void Play(Player& /*player*/, Boss& boss) override
257 {
258 boss.SubHitPoints(4);
259
260 debug << "Magic Missile deals 4 damage" << endl;
261 }
262
263 static const int Cost = 53;
264};
265
266class Drain : public Spell {
267public:
268 Drain()
269 : Spell("Drain", Mana(Cost))
270 {
271 }
272
273 void Play(Player& player, Boss& boss) override
274 {
275 player.AddHitPoints(2);
276 boss.SubHitPoints(2);
277 debug << "Drain deals 2 damage, and heals 2 hit points" << endl;
278 }
279
280 static const int Cost = 73;
281};
282
283class Effect : public Spell {
284protected:
285 Effect(string_view name, Mana mana, int timer) // NOLINT
286 : Spell(name, mana)
287 , timer_(timer)
288 {
289 }
290
291public:
292 int Timer() final
293 {
294 return timer_;
295 }
296
297protected:
298 int timer_;
299};
300
301class Shield : public Effect {
302public:
303 explicit Shield(Player& player)
304 : Effect("Shield", Mana(Cost), RunTime)
305 , player_(player)
306 {
307 debug << "Increasing Armor by " << Armor << endl;
308 player_.AddArmor(Armor);
309 }
310
311 ~Shield() override
312 {
313 debug << "Decreasing Armor by " << Armor << endl;
314 player_.SubArmor(Armor);
315 }
316
317 void Play(Player& /*player*/, Boss& /*boss*/) override
318 {
319 timer_--;
320 debug << "Shields timer is now " << timer_ << endl;
321 }
322
323 static const Armor Armor = 7;
324 static const int Cost = 113;
325 static const int RunTime = 6;
326
327private:
328 Player& player_;
329};
330
331class Poison : public Effect {
332public:
333 Poison()
334 : Effect("Poison", Mana(Cost), RunTime)
335 {
336 }
337
338 void Play(Player& /*player*/, Boss& boss) override
339 {
340 timer_--;
341
342 debug << name_ << " deals 3 damage; its timer is now " << timer_ << endl;
343 boss.SubHitPoints(3);
344 }
345
346 static const int Cost = 173;
347 static const int RunTime = 6;
348};
349
350class Recharge : public Effect {
351public:
352 Recharge()
353 : Effect("Recharge", Mana(Cost), RunTime)
354 {
355 }
356
357 ~Recharge() override
358 {
359 debug << "Recharge wears off" << endl;
360 }
361
362 void Play(Player& player, Boss& /*boss*/) override
363 {
364 static const Mana AdditionalMana = 101;
365
366 timer_--;
367 player.AddMana(AdditionalMana);
368 debug << name_ << " provides 101 mana; its timer is now " << timer_ << endl;
369 }
370
371 static const int Cost = 229;
372 static const int RunTime = 5;
373};
374
375shared_ptr<Spell>
376chooseRandomSpell(Player& player)
377{
378 if ( player.mana_ < min({ MagicMissile::Cost, Drain::Cost, Shield::Cost, Poison::Cost, Recharge::Cost }) ) {
379 return nullptr;
380 }
381
382 while ( true ) {
383 static const int NumSpells = 5;
384
385 auto selection = rand() % NumSpells;
386
387 if ( selection == 0 && player.mana_ >= MagicMissile::Cost ) {
388 return make_shared<MagicMissile>();
389 }
390 if ( selection == 1 && player.mana_ >= Drain::Cost ) {
391 return make_shared<Drain>();
392 }
393 if ( selection == 2 && player.mana_ >= Shield::Cost ) {
394 return make_shared<Shield>(player);
395 }
396 if ( selection == 3 && player.mana_ >= Poison::Cost ) {
397 return make_shared<Poison>();
398 }
399 if ( selection == 4 && player.mana_ >= Recharge::Cost ) {
400 return make_shared<Recharge>();
401 }
402 }
403}
404
405void
406battle1()
407{
408 Player player(250, 10);
409 Boss boss(13, 8);
410 Spells spells;
411
412 // ----------------------
413
414 cout << "\n-- Player turn --\n";
415 cout << player << '\n'
416 << boss << endl;
417
418 spells.Play(player, boss);
419 spells.AddSpell(make_shared<Poison>(), player, boss);
420
421 // ----------------------
422
423 cout << "\n-- Boss turn --\n";
424 cout << player << '\n'
425 << boss << endl;
426
427 spells.Play(player, boss);
428 boss.Play(player);
429
430 // ----------------------
431
432 cout << "\n-- Player turn --\n";
433 cout << player << '\n'
434 << boss << endl;
435
436 spells.Play(player, boss);
437 spells.AddSpell(make_shared<MagicMissile>(), player, boss);
438
439 // ----------------------
440
441 cout << "\n-- Boss turn --\n";
442 cout << player << '\n'
443 << boss << endl;
444
445 spells.Play(player, boss);
446
447 if ( boss.HasLost() ) {
448 cout << "Boss killed" << endl;
449 }
450}
451
452void
453battle2()
454{
455 Player player(250, 10);
456 Boss boss(14, 8);
457 Spells spells;
458
459 // ----------------------
460
461 cout << "\n-- Player turn --\n";
462 cout << player << '\n'
463 << boss << endl;
464
465 spells.Play(player, boss);
466 spells.AddSpell(make_shared<Recharge>(), player, boss);
467
468 // ----------------------
469
470 cout << "\n-- Boss turn --\n";
471 cout << player << '\n'
472 << boss << endl;
473
474 spells.Play(player, boss);
475 boss.Play(player);
476
477 // ----------------------
478
479 cout << "\n-- Player turn --\n";
480 cout << player << '\n'
481 << boss << endl;
482
483 spells.Play(player, boss);
484 spells.AddSpell(make_shared<Shield>(player), player, boss);
485
486 // ----------------------
487
488 cout << "\n-- Boss turn --\n";
489 cout << player << '\n'
490 << boss << endl;
491
492 spells.Play(player, boss);
493 boss.Play(player);
494
495 // ----------------------
496
497 cout << "\n-- Player turn --\n";
498 cout << player << '\n'
499 << boss << endl;
500
501 spells.Play(player, boss);
502 spells.AddSpell(make_shared<Drain>(), player, boss);
503
504 // ----------------------
505
506 cout << "\n-- Boss turn --\n";
507 cout << player << '\n'
508 << boss << endl;
509
510 spells.Play(player, boss);
511 boss.Play(player);
512
513 // ----------------------
514
515 cout << "\n-- Player turn --\n";
516 cout << player << '\n'
517 << boss << endl;
518
519 spells.Play(player, boss);
520 spells.AddSpell(make_shared<Poison>(), player, boss);
521
522 // ----------------------
523
524 cout << "\n-- Boss turn --\n";
525 cout << player << '\n'
526 << boss << endl;
527
528 spells.Play(player, boss);
529 boss.Play(player);
530
531 // ----------------------
532
533 cout << "\n-- Player turn --\n";
534 cout << player << '\n'
535 << boss << endl;
536
537 spells.Play(player, boss);
538 spells.AddSpell(make_shared<MagicMissile>(), player, boss);
539
540 // ----------------------
541
542 cout << "\n-- Boss turn --\n";
543 cout << player << '\n'
544 << boss << endl;
545
546 spells.Play(player, boss);
547 boss.Play(player);
548
549 if ( boss.HasLost() ) {
550 cout << "Boss killed" << endl;
551 }
552
553 cout << "End of Battle" << endl;
554}
555
556int
557main()
558{
559 battle2();
560}