Skip to content

Commit 0530470

Browse files
committed
Solutions for days 19 - 24
1 parent 6b4871d commit 0530470

File tree

9 files changed

+524
-0
lines changed

9 files changed

+524
-0
lines changed

day19/input.txt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
Al => ThF
2+
Al => ThRnFAr
3+
B => BCa
4+
B => TiB
5+
B => TiRnFAr
6+
Ca => CaCa
7+
Ca => PB
8+
Ca => PRnFAr
9+
Ca => SiRnFYFAr
10+
Ca => SiRnMgAr
11+
Ca => SiTh
12+
F => CaF
13+
F => PMg
14+
F => SiAl
15+
H => CRnAlAr
16+
H => CRnFYFYFAr
17+
H => CRnFYMgAr
18+
H => CRnMgYFAr
19+
H => HCa
20+
H => NRnFYFAr
21+
H => NRnMgAr
22+
H => NTh
23+
H => OB
24+
H => ORnFAr
25+
Mg => BF
26+
Mg => TiMg
27+
N => CRnFAr
28+
N => HSi
29+
O => CRnFYFAr
30+
O => CRnMgAr
31+
O => HP
32+
O => NRnFAr
33+
O => OTi
34+
P => CaP
35+
P => PTi
36+
P => SiRnFAr
37+
Si => CaSi
38+
Th => ThCa
39+
Ti => BP
40+
Ti => TiTi
41+
e => HF
42+
e => NAl
43+
e => OMg
44+
45+
ORnPBPMgArCaCaCaSiThCaCaSiThCaCaPBSiRnFArRnFArCaCaSiThCaCaSiThCaCaCaCaCaCaSiRnFYFArSiRnMgArCaSiRnPTiTiBFYPBFArSiRnCaSiRnTiRnFArSiAlArPTiBPTiRnCaSiAlArCaPTiTiBPMgYFArPTiRnFArSiRnCaCaFArRnCaFArCaSiRnSiRnMgArFYCaSiRnMgArCaCaSiThPRnFArPBCaSiRnMgArCaCaSiThCaSiRnTiMgArFArSiThSiThCaCaSiRnMgArCaCaSiRnFArTiBPTiRnCaSiAlArCaPTiRnFArPBPBCaCaSiThCaPBSiThPRnFArSiThCaSiThCaSiThCaPTiBSiRnFYFArCaCaPRnFArPBCaCaPBSiRnTiRnFArCaPRnFArSiRnCaCaCaSiThCaRnCaFArYCaSiRnFArBCaCaCaSiThFArPBFArCaSiRnFArRnCaCaCaFArSiRnFArTiRnPMgArF

day19/solver.exs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
defmodule Day19 do
2+
def load_input file do
3+
[rules, molecule] = File.read!(file)
4+
|> String.split("\n\n")
5+
{rules, inverse_rules} = load_rules(String.split(rules, "\n"), %{}, %{})
6+
atoms = split_molecule(String.strip(molecule))
7+
8+
{rules, inverse_rules, atoms}
9+
end
10+
11+
def load_rules([], rules_map, inverse_rules), do: {rules_map, inverse_rules}
12+
def load_rules [rule|t], rules_map, inverse_rules do
13+
[lhside, rhside] = String.split(rule, "=>")
14+
lhside = String.strip lhside
15+
rhside = String.strip rhside
16+
17+
inverse_rules = Map.put(inverse_rules, String.reverse(rhside), String.reverse(lhside))
18+
19+
case rules_map[lhside] do
20+
nil ->
21+
rules_map = Map.put(rules_map, lhside, [rhside])
22+
n ->
23+
rules_map = Map.put(rules_map, lhside, [rhside|n])
24+
end
25+
load_rules t, rules_map, inverse_rules
26+
end
27+
28+
def split_molecule(mol), do: split_molecule mol, []
29+
defp split_molecule("", atoms), do: atoms
30+
defp split_molecule mol, atoms do
31+
[atom] = Regex.run(~r/[A-Z][a-z]?/, mol)
32+
mol = String.slice(mol, String.length(atom)..String.length(mol))
33+
34+
split_molecule(mol, [atom|atoms])
35+
end
36+
37+
def updated_molecules(_, _, [], posibilities), do: posibilities
38+
def updated_molecules molecules, index, [subtitution|t], posibilities do
39+
molecule = List.replace_at(molecules, index, subtitution)
40+
|> Enum.reverse
41+
|> Enum.join
42+
43+
posibilities = Set.put(posibilities, molecule)
44+
45+
updated_molecules molecules, index, t, posibilities
46+
end
47+
48+
def generate_all_molecules(molecules, rules) do
49+
generate_all_molecules molecules, rules, Enum.count(molecules) - 1, HashSet.new
50+
end
51+
52+
defp generate_all_molecules(_, _, -1, posibilities), do: posibilities
53+
defp generate_all_molecules(molecules, rules, index, posibilities) do
54+
atom_substitutions = rules[Enum.at(molecules, index)] || []
55+
new_molecules = updated_molecules(molecules, index, atom_substitutions, posibilities)
56+
57+
generate_all_molecules(molecules, rules, index - 1, new_molecules)
58+
end
59+
end
60+
61+
{rules, inverse_rules, atoms} =
62+
"input.txt"
63+
|> Day19.load_input
64+
65+
Day19.generate_all_molecules(atoms, rules)
66+
|> Enum.count
67+
|> IO.inspect
68+
69+
70+
# For part2 i got stuck, tried different aproaches and end up giving up
71+
# Ideas from reddit didn't help (niether the actual implementantions there, at least the go & python I tried)
72+
# Finally this guys post is the more clever solution without any real code:
73+
# https://www.reddit.com/r/adventofcode/comments/3xflz8/day_19_solutions/cy4etju
74+
total = Enum.count(atoms)
75+
sides = Enum.count(atoms, fn(x) -> x == "Rn" || x == "Ar" end)
76+
content = Enum.count(atoms, fn(x) -> x == "Y" end)
77+
78+
solution = total - sides - (2 * content) - 1
79+
IO.puts solution
80+
81+

day20/solver.exs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
defmodule Day20 do
2+
def divisors(n) do
3+
#Copy pasted this bit
4+
e = n |> :math.sqrt |> trunc
5+
6+
(1..e)
7+
|> Enum.flat_map(fn
8+
x when rem(n, x) != 0 -> []
9+
x when x != div(n, x) -> [x, div(n, x)]
10+
x -> [x]
11+
end)
12+
end
13+
14+
def house_gifts house, cache do
15+
f = divisors(house) |> Enum.sort |> Enum.filter(fn(x) -> div(house, x) <= 50 end)
16+
gifts = f |> Enum.map(&(&1 * 11)) |> Enum.sum
17+
{gifts, cache}
18+
end
19+
20+
def find_house min do
21+
find_house min, 1, %{} #Head start
22+
end
23+
24+
defp find_house min, house, cache do
25+
{gifts, cache} = house_gifts(house, cache)
26+
case gifts >= min do
27+
true ->
28+
house
29+
false ->
30+
find_house(min, house + 1, cache)
31+
end
32+
end
33+
end
34+
35+
Day20.find_house(33100000)
36+
|> IO.inspect

day21/solver.exs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
defmodule Day21 do
2+
def combination(0, _), do: [[]]
3+
def combination(_, []), do: []
4+
def combination(n, [x|xs]) do
5+
(for y <- combination(n - 1, xs), do: [x|y]) ++ combination(n, xs)
6+
end
7+
8+
def combine_loot weapon, armor, rings do
9+
[weapon, armor, rings]
10+
|> List.flatten
11+
|> List.zip
12+
|> Enum.map(&Tuple.to_list/1)
13+
|> Enum.map(&Enum.sum/1)
14+
|> List.to_tuple
15+
end
16+
17+
def battle({boss_points, _, _}, {_, cost, _, _}, _) when boss_points <= 0 do
18+
{true, cost}
19+
end
20+
21+
def battle(_, {player_points, cost, _, _}, _) when player_points <= 0 do
22+
{false, cost}
23+
end
24+
25+
def battle({boss_points, boss_damage, boss_armor}, {points, cost, damage, armor}, round) do
26+
if rem(round, 2) == 0 do
27+
player_damage = max((damage - boss_armor), 1)
28+
battle({boss_points - player_damage, boss_damage, boss_armor}, {points, cost, damage, armor}, round + 1)
29+
30+
else
31+
boss = max((boss_damage - armor), 1)
32+
battle({boss_points, boss_damage, boss_armor}, {points - boss, cost, damage, armor}, round + 1)
33+
end
34+
end
35+
end
36+
37+
weapons = [
38+
{8, 4, 0},
39+
{10, 5, 0},
40+
{25, 6, 0},
41+
{40, 7, 0},
42+
{74, 8, 0},
43+
]
44+
45+
armor = [
46+
{0, 0, 0}, #No armor
47+
{13, 0, 1},
48+
{31, 0, 2},
49+
{53, 0, 3},
50+
{75, 0, 4},
51+
{102, 0, 5},
52+
]
53+
54+
rings = [
55+
{0, 0, 0}, #No ring
56+
{0, 0, 0}, #No ring
57+
{25, 1, 0},
58+
{50, 2, 0},
59+
{100, 3, 0},
60+
{20, 0, 1},
61+
{40, 0, 2},
62+
{80, 0, 3},
63+
]
64+
65+
boss_stats = {103, 9, 2}
66+
67+
ring_combinations = Day21.combination(2, rings)
68+
69+
(for weapon <- weapons, do:
70+
(for armor <- armor, do:
71+
(for ring <- ring_combinations do
72+
loot_stats = Day21.combine_loot(weapon, armor, ring)
73+
loot_stats = Tuple.insert_at(loot_stats, 0, 100)
74+
Day21.battle(boss_stats, loot_stats, 0)
75+
end)))
76+
|> List.flatten
77+
|> Enum.filter(fn ({win, _}) -> not win end)
78+
|> Enum.max_by(fn ({_, cost}) -> cost end)
79+
|> IO.inspect

day22/solver.exs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
defmodule Day22 do
2+
#This is the crappiest of my solutions
3+
@spells [
4+
{:missile, 53, :effect, 0},
5+
{:drain, 73, :effect, 0},
6+
{:shield, 113, :effect, 6},
7+
{:poison, 173, :effect, 6},
8+
{:recharge, 229, :effect, 5},
9+
]
10+
11+
def apply_spell({:missile, spell_mana, _, _}, {boss_points, boss_damage}, {points, mana, effects, spent_mana}) do
12+
{
13+
{boss_points - 4, boss_damage},
14+
{points, mana - spell_mana, effects, spent_mana + spell_mana}
15+
}
16+
end
17+
18+
def apply_spell({:drain, spell_mana, _, _}, {boss_points, boss_damage}, {points, mana, effects, spent_mana}) do
19+
{
20+
{boss_points - 2, boss_damage},
21+
{points + 2 , mana - spell_mana, effects, spent_mana + spell_mana}
22+
}
23+
end
24+
25+
def apply_spell({spell, spell_mana, :effect, turns}, {boss_points, boss_damage}, {points, mana, effects, spent_mana}) do
26+
effects = Map.put(effects, spell, turns)
27+
{
28+
{boss_points, boss_damage},
29+
{points, mana - spell_mana, effects, spent_mana + spell_mana}
30+
}
31+
end
32+
33+
def apply_effects effects do
34+
armor = if effects[:shield] > 0, do: 7, else: 0
35+
poison = if effects[:poison] > 0, do: 3, else: 0
36+
extra_mana = if effects[:recharge] > 0, do: 101, else: 0
37+
38+
effects = Dict.update(effects, :poison, 0, fn(v) -> max(v - 1, 0) end)
39+
effects = Dict.update(effects, :recharge, 0, fn(v) -> max(v - 1, 0) end)
40+
effects = Dict.update(effects, :shield, 0, fn(v) -> max(v - 1, 0) end)
41+
42+
{armor, poison, extra_mana, effects}
43+
end
44+
45+
def posible_spells mana, effects do
46+
@spells
47+
|> Enum.filter(fn({_, cost, _, _}) -> cost <= mana end)
48+
|> Enum.filter(fn({name, _, _, turns}) -> turns == 0 or effects[name] <= 0 end)
49+
end
50+
51+
def battle({boss_points, _}, {_, _, _, spent_mana}, _, sh) when boss_points <= 0 do
52+
IO.puts spent_mana
53+
{true, spent_mana}
54+
end
55+
56+
def battle(_, {points, _, _, spent_mana}, _, sh) when points <= 0 do
57+
{false, spent_mana}
58+
end
59+
60+
def battle({boss_points, boss_damage}, {points, mana, effects, spent_mana}, round, sh) when rem(round, 2) == 1 do
61+
{armor, poison, extra_mana, effects} = apply_effects effects
62+
dealt_damage = max(boss_damage - armor, 1)
63+
battle({boss_points - poison, boss_damage}, {points - dealt_damage, mana + extra_mana, effects, spent_mana}, round + 1, sh)
64+
end
65+
66+
def battle({boss_points, boss_damage}, {points, mana, effects, spent_mana}, round, spell_history) do
67+
#Part 2
68+
points = points - 1
69+
if points == 0 do
70+
[]
71+
else
72+
{_, poison, extra_mana, effects} = apply_effects effects
73+
spells = posible_spells mana, effects
74+
case spells do
75+
[] ->
76+
[]
77+
_ ->
78+
(for spell <- spells do
79+
s = apply_spell(spell, {boss_points, boss_damage}, {points, mana, effects, spent_mana})
80+
case s do
81+
{false, _} ->
82+
[]
83+
{{b_p, b_d}, {p, m, effects, spent_mana}} ->
84+
battle({b_p - poison, b_d}, {p, m + extra_mana, effects, spent_mana}, round + 1, [spell|spell_history])
85+
end
86+
end)
87+
end
88+
end
89+
end
90+
end
91+
boss_stats = {51, 9}
92+
#boss_stats = {14, 8}
93+
94+
Day22.battle(boss_stats, {50, 500, %{:shield => 0, :poison => 0, :recharge => 0}, 0}, 0, [])
95+
|> List.flatten
96+
|> Enum.filter(fn ({win, _}) -> win end)
97+
|> Enum.min_by(fn ({_, v}) -> v end)
98+
|> IO.inspect

day23/input.txt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
jio a, +16
2+
inc a
3+
inc a
4+
tpl a
5+
tpl a
6+
tpl a
7+
inc a
8+
inc a
9+
tpl a
10+
inc a
11+
inc a
12+
tpl a
13+
tpl a
14+
tpl a
15+
inc a
16+
jmp +23
17+
tpl a
18+
inc a
19+
inc a
20+
tpl a
21+
inc a
22+
inc a
23+
tpl a
24+
tpl a
25+
inc a
26+
inc a
27+
tpl a
28+
inc a
29+
tpl a
30+
inc a
31+
tpl a
32+
inc a
33+
inc a
34+
tpl a
35+
inc a
36+
tpl a
37+
tpl a
38+
inc a
39+
jio a, +8
40+
inc b
41+
jie a, +4
42+
tpl a
43+
inc a
44+
jmp +2
45+
hlf a
46+
jmp -7

0 commit comments

Comments
 (0)