Code for the Advent of Code event
aoc
advent-of-code
1#!/usr/bin/env ruby
2# frozen_string_literal: true
3
4valves = {}
5
6ARGF.readlines.each do |line|
7 next unless line =~ /\b([A-Z]{2})\b.+?(\d+).+?((?:[A-Z]{2}(?:, )?)+)/
8 valves[$1.to_sym] = {
9 rate: $2.to_i,
10 tunnels: $3.split(', ').map(&:to_sym).to_a
11 }
12end
13
14HAS_FLOW = valves.reject { _2[:rate] == 0 }.to_set { |k, _| k }
15
16def solve(valves, memory, mins, current, score, visited, opened)
17 key = [mins, current, score]
18 return memory[key] if memory.key?(key)
19
20 return [score, opened] if mins <= 0
21 return [score, opened] if HAS_FLOW.all? { opened.include? _1 }
22
23 visited = visited.dup
24 visited.add current
25
26 valve = valves[current]
27 rate = valve[:rate]
28
29 max = score
30 max_opened = opened
31
32 tovisit = valve[:tunnels]
33 tovisit.each do |tunnel|
34 simple_max, simple_o = solve(valves, memory, mins - 1, tunnel, score, visited, opened)
35 if simple_max > max
36 max = simple_max
37 max_opened = simple_o
38 end
39 next unless rate > 0 && !opened.include?(current)
40 added = rate * (mins - 1)
41 new_opened = opened.dup
42 new_opened.add current
43 complex_max, complex_o = solve(valves, memory, mins - 2, tunnel, score + added, visited, new_opened)
44 if complex_max > max
45 max = complex_max
46 max_opened = complex_o
47 end
48 end
49
50 memory[key] = [max, max_opened]
51
52 [max, max_opened]
53end
54
55part1, _opened = solve(valves, {}, 30, :AA, 0, Set.new, Set.new)
56
57puts part1
58
59part2_human, opened2_human = solve(valves, {}, 26, :AA, 0, Set.new, Set.new)
60opened2_human.each { valves[_1][:rate] = 0 }
61part2_elephant, opened2_elephant = solve(valves, {}, 26, :AA, 0, Set.new, Set.new)
62
63warn 'PART 2:'
64warn "Human score: #{part2_human} (opened #{opened2_human})"
65warn "Elephant score: #{part2_elephant} (opened #{opened2_elephant})"
66
67puts part2_human + part2_elephant