My Advent of Code solutions in Python. kevinyap.ca/2019/12/going-fast-in-advent-of-code/
advent-of-code python
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 185 lines 5.9 kB view raw
1import fileinput 2import itertools 3import sys 4from collections import namedtuple 5 6class Character: 7 def __init__(self, name, hp, mp, dmg, armour): 8 # Stats 9 self.name = name 10 self.hp = hp 11 self.mp = mp 12 self.dmg = dmg 13 self.armour = armour 14 15 # Status Effects 16 self.shielded = 0 17 self.poisoned = 0 18 self.recharging = 0 19 20 def print_player(self): 21 print '{} has {} hit points, {} mana, {} armour'.format(self.name, self.hp, self.mp, self.armour) 22 23 def print_boss(self): 24 print '{} has {} hit points'.format(self.name, self.hp) 25 26 def process_status(self): 27 if self.shielded: 28 self.shielded -= 1 29 self.armour = 7 30 # print "Shield's timer is now {}.".format(self.shielded) 31 if self.shielded == 0: 32 self.armour = 0 33 # print 'Shield wears off.' 34 35 if self.poisoned: 36 self.poisoned -= 1 37 self.hp -= 3 38 # print 'Poison deals 3 damage; its timer is now {}.'.format(self.poisoned) 39 if self.poisoned == 0: 40 pass 41 # print 'Poison effects expired.' 42 43 if self.recharging: 44 self.recharging -= 1 45 self.mp += 101 46 # print 'Recharge provides 101 mana; its timer is now {}.'.format(self.recharging) 47 if self.recharging == 0: 48 pass 49 # print 'Recharge wears off.' 50 51 52Spell = namedtuple('Spell', ['name', 'cost', 'dmg', 'heal', 'effect', 'duration']) 53 54# Magic Missile costs 53 mana. It instantly does 4 damage. 55# Drain costs 73 mana. It instantly does 2 damage and heals you for 2 hit points. 56# Shield costs 113 mana. It starts an effect that lasts for 6 turns. While it is active, your armor is increased by 7. 57# Poison costs 173 mana. It starts an effect that lasts for 6 turns. At the start of each turn while it is active, it deals the boss 3 damage. 58# Recharge costs 229 mana. It starts an effect that lasts for 5 turns. At the start of each turn while it is active, it gives you 101 new mana. 59SPELLS = [ 60 Spell('Missl', 53, 4, 0, None, None), 61 Spell('Drain', 73, 2, 2, None, None), 62 Spell('Shild', 113, 0, 0, 'shielded', 6), 63 Spell('Poisn', 173, 0, 0, 'poisoned', 6), 64 Spell('Rchrg', 229, 0, 0, 'recharge', 5), 65] 66 67 68def boss_fight(player, boss, spell_order, hard_mode=False): 69 spent_mana = 0 70 effects = [] 71 # print ', '.join(s.name for s in spell_order) 72 73 for spell in spell_order: 74 if hard_mode: 75 player.hp -= 1 76 if player.hp <= 0: 77 # print 'Player died. Game over. :(' 78 return sys.maxint 79 80 # print '-- Player Turn --' 81 # player.print_player() 82 # boss.print_boss() 83 84 player.process_status() 85 boss.process_status() 86 87 88 # Pick a spell to use 89 if spell.cost > player.mp: 90 # print 'Not enough mana to cast.' 91 return sys.maxint 92 93 # Disallow repeated effects 94 if spell.effect == 'shielded' and player.shielded: 95 return sys.maxint 96 elif spell.effect == 'poisoned' and boss.poisoned: 97 return sys.maxint 98 elif spell.effect == 'recharge' and player.recharging: 99 return sys.maxint 100 101 # print '> iKevinY casted {}!'.format(spell.name) 102 spent_mana += spell.cost 103 player.mp -= spell.cost 104 boss.hp -= spell.dmg 105 106 107 if spell.heal: 108 # print 'iKevinY healed for {} hit points.'.format(spell.heal) 109 player.hp += spell.heal 110 111 if spell.effect == 'shielded': 112 player.shielded = spell.duration 113 elif spell.effect == 'poisoned': 114 boss.poisoned = spell.duration 115 elif spell.effect == 'recharge': 116 player.recharging = spell.duration 117 118 if boss.hp <= 0: 119 # print 'Boss died. You win! :)' 120 print spent_mana, '@', ' '.join(s.name for s in spell_order) 121 return spent_mana 122 123 # print '-- Boss Turn --' 124 # player.print_player() 125 # boss.print_boss() 126 player.process_status() 127 boss.process_status() 128 129 if boss.hp <= 0: 130 # print 'Boss died. You win! :)' 131 print spent_mana, '@', ' '.join(s.name for s in spell_order) 132 return spent_mana 133 134 # print '> Boss attacks for {} damage!'.format(max(1, boss.dmg - player.armour)) 135 player.hp -= max(1, boss.dmg - player.armour) 136 if player.hp <= 0: 137 # print 'Player died. Game over. :(' 138 return sys.maxint 139 140 # print 141 142 else: 143 # Fight did not terminate 144 return sys.maxint 145 146 return spent_mana 147 148 149# # HP, Damage 150# boss_data = [] 151 152# for line in fileinput.input(): 153# boss_data.append(int(line.split(': ')[1])) 154 155# boss = Character('BOSS', 13, 0, 8, 0) 156# player = Character('iKevinY', 10, 250, 0, 0) # 50HP, 500MP 157# boss_fight(player, boss, [SPELLS[3], SPELLS[0]]) 158 159# sys.exit() 160 161def simulate(hard_mode): 162 most_efficient = sys.maxint 163 164 for n in range(9, 12): 165 found = False 166 for so in itertools.product(SPELLS, repeat=n): 167 if sum(s.cost for s in so) > most_efficient: 168 continue 169 170 boss = Character('BOSS', 55, 0, 8, 0) # 55hp, 8dmg INPUT 171 player = Character('iKevinY', 50, 500, 0, 0) # 50HP, 500MP 172 173 # boss = Character('BOSS', 13, 0, 8, 0) 174 # player = Character('iKevinY', 10, 250, 0, 0) # 50HP, 500MP 175 a = boss_fight(player, boss, so, hard_mode) 176 177 if a < most_efficient: 178 most_efficient = a 179 found = True 180 181 if found: 182 return most_efficient 183 184print 'Most efficient MP on easy mode: %i' % simulate(hard_mode=False) 185print 'Most efficient MP on hard mode: %i' % simulate(hard_mode=True)