aoc_omni/ruby/2025/7/lib/laboratories.rb
2025-12-07 07:51:28 -05:00

99 lines
2.2 KiB
Ruby

class Laboratories
attr_reader :layers
def initialize(layers)
@layers = layers
end
def self.for(input)
new input.split("\n").map(&Layer.method(:new))
end
def split_count
layers.reduce([layers.first.blank_beams, 0]) do |acc, layer|
out = layer.split_beams acc[0]
[out[0], out[1] + acc[1]]
end[1]
end
def count_tachyon_lifetimes
@memo = {}
count_lifetimes layers.first.line.index("S"), 0
end
def count_lifetimes(position, layer)
return @memo["#{position}/#{layer}"] if @memo.key? "#{position}/#{layer}"
lt = if layer >= layers.count
1
else
if layers[layer].line[position] == "^"
count_lifetimes(position - 1, layer + 1) + count_lifetimes(position + 1, layer + 1)
else
count_lifetimes position, layer + 1
end
end
@memo["#{position}/#{layer}"] = lt
lt
end
class Tachyon
attr_reader :lab, :position, :layer
def initialize(lab, position, layer=0)
@lab = lab
@position = position
@layer = layer
end
def count_lifetimes
if layer >= lab.layers.count
1
else
if at_splitter?
self.class.new(lab, position - 1, layer + 1).count_lifetimes +
self.class.new(lab, position + 1, layer + 1).count_lifetimes
else
self.class.new(lab, position, layer + 1).count_lifetimes
end
end
end
def at_splitter?
lab.layers[layer].line[position] == "^"
end
end
class Layer
attr_reader :line
def initialize(line)
@line = line
end
def self.for(line)
new line.split("")
end
def blank_beams
Array.new(line.length, false)
end
def split_beams(input_beams)
split_count = 0
output = 0.upto(line.length - 1).map do |i|
if line[i] == "^"
split_count += 1 if input_beams[i]
false
elsif i > 0 && line[i - 1] == "^" && input_beams[i - 1]
true
elsif i < (line.length - 1) && line[i + 1] == "^" && input_beams[i + 1]
true
elsif line[i] == "S"
true
else
input_beams[i]
end
end
[output, split_count]
end
end
end