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