From ca03e4367be9d5e96c5dc6de75e919d57fd2923e Mon Sep 17 00:00:00 2001 From: Bill Rossi Date: Sun, 7 Dec 2025 07:51:28 -0500 Subject: [PATCH] 2025 day 7 --- ruby/2025/7/bin/problem | 8 +++ ruby/2025/7/lib/laboratories.rb | 98 +++++++++++++++++++++++++++ ruby/2025/7/test/test_laboratories.rb | 44 ++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 ruby/2025/7/bin/problem create mode 100644 ruby/2025/7/lib/laboratories.rb create mode 100644 ruby/2025/7/test/test_laboratories.rb diff --git a/ruby/2025/7/bin/problem b/ruby/2025/7/bin/problem new file mode 100644 index 0000000..91fbffb --- /dev/null +++ b/ruby/2025/7/bin/problem @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby + +require "laboratories" + +lab = Laboratories.for STDIN.read.chomp + +puts "Part 1: #{lab.split_count}" +puts "Part 2: #{lab.count_tachyon_lifetimes}" \ No newline at end of file diff --git a/ruby/2025/7/lib/laboratories.rb b/ruby/2025/7/lib/laboratories.rb new file mode 100644 index 0000000..c323dc3 --- /dev/null +++ b/ruby/2025/7/lib/laboratories.rb @@ -0,0 +1,98 @@ +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 diff --git a/ruby/2025/7/test/test_laboratories.rb b/ruby/2025/7/test/test_laboratories.rb new file mode 100644 index 0000000..93a9cc7 --- /dev/null +++ b/ruby/2025/7/test/test_laboratories.rb @@ -0,0 +1,44 @@ +require "minitest/autorun" + +require "laboratories" + +class TestLaboratories < Minitest::Test + def test_split_count + lab = Laboratories.for <<-END +.......S....... +............... +.......^....... +............... +......^.^...... +............... +.....^.^.^..... +............... +....^.^...^.... +............... +...^.^...^.^... +............... +..^...^.....^.. +............... +.^.^.^.^.^...^. +............... +END + assert_equal(21, lab.split_count) + end +end + +class TestLayer < Minitest::Test + def test_split_beams + layer = Laboratories::Layer.for(".S.") + assert_equal([[false, true, false], 0], layer.split_beams([false, false, false])) + + layer = Laboratories::Layer.for("..") + assert_equal([[true, false], 0], layer.split_beams([true, false])) + + layer = Laboratories::Layer.for(".^.") + assert_equal([[true, false, true], 1], layer.split_beams([false, true, false])) + + layer = Laboratories::Layer.for("^.^") + assert_equal([[false, true, false], 0], layer.split_beams([false, true, false])) + assert_equal([[false, true, false], 2], layer.split_beams([true, false, true])) + end +end