#!/usr/bin/python from copy import copy class Entity: pass class Item: pass class Shop: pass # Annoying boilerplate to initialize the shop with the items # given in the text. This is as compact as I could make it. def init_shop(): table_weapons = [ "Dagger 8 4 0", "Shortsword 10 5 0", "Warhammer 25 6 0", "Longsword 40 7 0", "Greataxe 74 8 0" ] table_armour = [ "Leather 13 0 1", "Chainmail 31 0 2", "Splintmail 53 0 3", "Bandedmail 75 0 4", "Platemail 102 0 5", "NONE 0 0 0" # Because it's optional ] table_rings = [ "Damage+1 25 1 0", "Damage+2 50 2 0", "Damage+3 100 3 0", "Defense+1 20 0 1", "Defense+2 40 0 2", "Defense+3 80 0 3", "NONE 0 0 0" # Because it's optional ] tables = [ (table_weapons, "weapons"), (table_armour, "armour"), (table_rings, "rings") ] shop = Shop() shop.weapons = [] shop.armour = [] shop.rings = [] for table, attr in tables: for row in table: fields = row.split() item = Item() item.name = fields[0] item.cost = int(fields[1]) item.damage = int(fields[2]) item.armour = int(fields[3]) # Sometimes I love you, Python. But usually I don't. shop.__dict__[attr].append(item) return shop def will_win(player, boss): turn = 0 while boss.health > 0 and player.health > 0: # Player's turn to attack if turn % 2 == 0: boss.health -= max(1, player.damage - boss.armour) # Boss' turn to attack else: # turn % 2 == 1 player.health -= max(1, boss.damage - player.armour) turn += 1 # Did the player win? return boss.health <= 0 def solve_partn(partn, player_stats, boss_stats): shop = init_shop() min_cost = 666 max_cost = 0 for w in shop.weapons: for a in shop.armour: for r1 in shop.rings: for r2 in shop.rings: # No more than one of each item if r1 == r2: continue boss = copy( boss_stats) player = copy(player_stats) cost = w.cost + a.cost + r1.cost + r2.cost player.damage += w.damage player.damage += r1.damage player.damage += r2.damage player.armour += a.armour player.armour += r1.armour player.armour += r2.armour # Part 1: find lowest cost where player wins if will_win(player, boss): if cost < min_cost: min_cost = cost # Part 2: find highest cost where player loses else: if cost > max_cost: max_cost = cost if partn == 1: return min_cost else: # partn == 2 return max_cost def main(): # Set up player's stats given in puzzle text player = Entity() player.health = 100 player.damage = 0 player.armour = 0 # Read boss' stats from input text file boss = Entity() with open("input.txt", "r") as f: lines = f.read().splitlines() for i in range(len(lines)): # len(lines) = 3 words = lines[i].split() if i == 0: boss.health = int(words[-1]) if i == 1: boss.damage = int(words[-1]) if i == 2: boss.armour = int(words[-1]) print("Part 1 solution:", solve_partn(1, player, boss)) # 121 for me print("Part 2 solution:", solve_partn(2, player, boss)) # 201 for me if __name__ == "__main__": main()