Skip to content

Instantly share code, notes, and snippets.

@carlwiedemann
Created December 20, 2023 00:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carlwiedemann/a3c02c21683c8e87d19ee6a077472603 to your computer and use it in GitHub Desktop.
Save carlwiedemann/a3c02c21683c8e87d19ee6a077472603 to your computer and use it in GitHub Desktop.
Advent of Code 2023 day019.rb
require_relative "main"
module Day019
INPUT = File.read('INPUT.txt')
parts = INPUT.split("\n\n")
guides = parts.first.to_lines.map do |guide_line|
guide_pieces = guide_line.split("{")
name = guide_pieces.first
predicates = guide_pieces.last[0..(guide_pieces.last.length - 2)].split(",").map do |predicate|
predicate_parts = predicate.split(":")
if predicate_parts.count == 2
cond_text = predicate_parts.first
parts_gt = predicate_parts.first.split(">")
parts_lt = predicate_parts.first.split("<")
if parts_gt.count == 2
cond = ->(part) { part[parts_gt.first] > parts_gt.last.to_i }
elsif parts_lt.count == 2
cond = ->(part) { part[parts_lt.first] < parts_lt.last.to_i }
else
raise "wat"
end
dest = predicate_parts.last
else
cond_text = "true"
cond = ->(_) { true }
dest = predicate
end
{
cond_text: cond_text,
cond: cond,
dest: dest
}
end
[name, predicates]
end.to_h
parts = parts.last.to_lines.map do |part_text|
part_text.gsub(/[\{\}]/, "").split(",").map do |item|
k, v = item.split("=")
[k, v.to_i]
end.to_h
end
##########
# Part 1 #
##########
is_accepted = ->(part) do
guide_name = "in"
loop do
if guide_name == "A"
return true
elsif guide_name == "R"
return false
else
guide = guides[guide_name]
guide.each do |predicate|
if predicate[:cond].call(part)
guide_name = predicate[:dest]
break
end
end
end
end
end
answer1 = parts.reduce(0) do |memo, part|
is_accepted.call(part) ? memo + part.values.sum : memo
end
pp answer1
##########
# Part 2 #
##########
MAX = 4000
all_acceptances = []
dfs = ->(cursor, ranges) do
return if cursor == "R"
if cursor == "A"
all_acceptances.push(ranges)
else
next_ranges = ranges.clone
last_ranges = next_ranges.clone
guides[cursor].each do |predicate|
parts_gt = predicate[:cond_text].split(">")
parts_lt = predicate[:cond_text].split("<")
if parts_gt.count == 2
char = parts_gt.first
ranges_new = ((parts_gt.last.to_i + 1)..MAX).to_a & next_ranges[char]
next_ranges[char] = ranges_new
elsif parts_lt.count == 2
char = parts_lt.first
ranges_new = (1..(parts_lt.last.to_i - 1)).to_a & next_ranges[char]
next_ranges[char] = ranges_new
end
dfs.call(predicate[:dest], next_ranges)
next_ranges = next_ranges.map do |k, v|
if last_ranges[k] != v
[k, last_ranges[k] - v]
else
[k, v]
end
end.to_h
last_ranges = next_ranges.clone
end
end
end
dfs.call("in", {
"x" => (1..MAX).to_a,
"m" => (1..MAX).to_a,
"a" => (1..MAX).to_a,
"s" => (1..MAX).to_a
})
answer2 = all_acceptances.reduce(0) do |memo, range|
memo + range.values.map(&:count).arith_prod
end
pp answer2
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment