aoc_omni/ruby/2025/8/lib/playground.rb
2025-12-08 04:48:46 -05:00

97 lines
2.0 KiB
Ruby

class Playground
attr_reader :boxes, :circuits, :last_pair
def initialize(boxes)
@boxes = boxes
boxes.each { |box| box.pg = self }
@circuits = boxes.to_h{ |box| [box.index, Circuit.new(box)] }
end
def self.for(input)
new input.split("\n").each_with_index.map(&JunctionBox.method(:for))
end
def distances
boxes.map { |b1| boxes.map { |b2| b1.distance_squared(b2) } }
end
def distance_pairs
distances.each_with_index.map do |dist_row, x|
dist_row.each_with_index.map do |dist, y|
[[x, y].min, [x,y].max, dist]
end
end.flatten(1).uniq.sort_by { |dp| dp[2] }
end
def connect_circuits!(connections=1000)
distance_pairs.first(connections).each do |a, b, _|
connect_circuit!(a, b)
end
end
def connect_all_circuits!
distance_pairs.each do |a, b, _|
connect_circuit!(a, b)
break if all_connected?
end
end
def all_connected?
circuits.values.uniq.length == 1
end
def connect_circuit!(a, b)
return if circuits[a].boxes.include?(boxes[b])
@last_pair = boxes[a], boxes[b]
circuits[a].boxes.push(*Array.new(circuits[b].boxes))
circuits[b].boxes.each { |box| circuits[box.index] = circuits[a] }
end
class JunctionBox
attr_reader :index, :x, :y, :z
attr_accessor :pg
def initialize(index, x, y, z)
@index = index
@x = x
@y = y
@z = z
end
def self.for(line, index)
new index, *line.split(",").map(&:to_i)
end
def distance_squared(other)
return 9999999999999 if self == other
(x - other.x)**2 +
(y - other.y)**2 +
(z - other.z)**2
end
def distances
@distances ||= pg.boxes.map { |box| distance_squared(box) }
end
def to_s
"#{index} (#{x}, #{y}, #{z})"
end
end
class Circuit
attr_reader :boxes
def initialize(box)
@boxes = [box]
end
def to_s
boxes.map(&:index)
end
def length
boxes.length
end
end
end