Code for the Advent of Code event
aoc
advent-of-code
1boss_data = {}
2
3stat_map = ['Hit Points']: 'hp', ['Damage']: 'damage', ['Armor']: 'armor'
4
5parse = (line) ->
6 trait, value = line\match '(.+): (%d+)'
7 boss_data[stat_map[trait]] = tonumber value if trait and value
8
9create_item = (name, cost, damage, armor) -> { :name, :cost, :damage, :armor }
10
11shop =
12 weapons: {
13 create_item 'Dagger', 8, 4, 0
14 create_item 'Shortsword', 10, 5, 0
15 create_item 'Warhammer', 25, 6, 0
16 create_item 'Longsword', 40, 7, 0
17 create_item 'Greataxe', 74, 8, 0
18 }
19 armor: {
20 create_item 'Naked', 0, 0, 0
21 create_item 'Leather', 13, 0, 1
22 create_item 'Chainmail', 31, 0, 2
23 create_item 'Splintmail', 53, 0, 3
24 create_item 'Bandedmail', 75, 0, 4
25 create_item 'Platemail', 102, 0, 5
26 }
27 rings: {
28 create_item 'Dummy 1', 0, 0, 0
29 create_item 'Dummy 2', 0, 0, 0
30 create_item 'Damage +1', 25, 1, 0
31 create_item 'Damage +2', 50, 2, 0
32 create_item 'Damage +3', 100, 3, 0
33 create_item 'Defense +1', 20, 0, 1
34 create_item 'Defense +2', 40, 0, 2
35 create_item 'Defense +3', 80, 0, 3
36 }
37
38calculate_combination = (list) ->
39 cost, damage, armor = 0, 0, 0
40
41 with shop.weapons[list[1]]
42 cost += .cost
43 damage += .damage
44 armor += .armor
45
46 with shop.armor[list[2]]
47 cost += .cost
48 damage += .damage
49 armor += .armor
50
51 for i = 3, 4
52 with shop.rings[list[i]]
53 cost += .cost
54 damage += .damage
55 armor += .armor
56
57 cost, damage, armor
58
59copy = (tbl) ->
60 return tbl unless type(tbl) == 'table'
61 { k, copy v for k, v in pairs tbl }
62
63fight = (damage, armor) ->
64 player = hp: 100, :damage, :armor
65 boss = copy boss_data
66
67 while player.hp > 0 and boss.hp > 0
68 hp_change = -player.damage + boss.armor
69 hp_change = -1 if hp_change > -1
70 boss.hp += hp_change
71 hp_change = -boss.damage + player.armor
72 hp_change = -1 if hp_change > -1
73 player.hp += hp_change
74
75 player.hp > 0 and boss.hp < 1
76
77combinations = -> coroutine.wrap ->
78 list = {}
79
80 -- Very dirty and hardcoded way to calculate all possible combinations
81 for w_i = 1, #shop.weapons
82 list[1] = w_i
83 for a_i = 1, #shop.armor
84 list[2] = a_i
85 for r1_i = 1, #shop.rings
86 list[3] = r1_i
87 for r2_i = 1, #shop.rings
88 continue if r2_i == r1_i
89 list[4] = r2_i
90 coroutine.yield list
91
92get_lowest_cost = ->
93 min_cost = 1e9
94
95 for list in combinations!
96 cost, damage, armor = calculate_combination list
97 min_cost = cost if fight(damage, armor) and cost < min_cost
98
99 min_cost
100
101get_highest_cost = ->
102 max_cost = 0
103
104 for list in combinations!
105 cost, damage, armor = calculate_combination list
106 max_cost = cost unless fight(damage, armor) or cost < max_cost
107
108 max_cost
109
110{ :parse, :get_lowest_cost, :get_highest_cost }