Code for the Advent of Code event
aoc
advent-of-code
1#!/usr/bin/env ruby
2# frozen_string_literal: true
3
4class Cuboid
5 attr_reader :left, :right, :bottom, :top, :front, :back
6
7 def initialize(left, right, bottom, top, front, back)
8 @left = left
9 @right = right
10 @bottom = bottom
11 @top = top
12 @front = front
13 @back = back
14 @removed = []
15 end
16
17 def width = (right - left + 1).abs
18 def height = (top - bottom + 1).abs
19 def depth = (back - front + 1).abs
20
21 def volume
22 (width * depth * height) - @removed.sum(&:volume)
23 end
24
25 def clamp(range)
26 Cuboid.new left.clamp(range), right.clamp(range), bottom.clamp(range), top.clamp(range), front.clamp(range), back.clamp(range)
27 end
28
29 def clamp!(range)
30 @left = left.clamp range
31 @right = right.clamp range
32 @bottom = bottom.clamp range
33 @top = top.clamp range
34 @front = front.clamp range
35 @back = back.clamp range
36 self
37 end
38
39 def intersects?(other)
40 top >= other.bottom && bottom <= other.top &&
41 right >= other.left && left <= other.right &&
42 back >= other.front && front <= other.back
43 end
44
45 def intersection(other)
46 return nil unless intersects? other
47 bottom = [@bottom, other.bottom].max
48 top = [@top, other.top].min
49 left = [@left, other.left].max
50 right = [@right, other.right].min
51 front = [@front, other.front].max
52 back = [@back, other.back].min
53 Cuboid.new(left, right, bottom, top, front, back)
54 end
55
56 def remove!(cuboid)
57 @removed.each { |r| r.remove!(cuboid.intersection(r)) if r.intersects? cuboid }
58 @removed << cuboid
59 end
60
61 def to_s
62 "(X=#{left}..#{right},Y=#{bottom}..#{top},Z=#{front}..#{back})"
63 end
64end
65
66def format_number(n)
67 n.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1 ').reverse
68end
69
70reactor = []
71
72ARGF.each_line do |line|
73 op, ranges = line.split.then { [_1.to_sym, _2] }
74 left, right, bottom, top, front, back = ranges.scan(/-?\d+/).map(&:to_i)
75 cuboid = Cuboid.new(left, right, bottom, top, front, back)
76 intersecting = reactor.select { |c| cuboid.intersects? c }
77 intersecting.each do |c|
78 intersection = c.intersection cuboid
79 c.remove! intersection
80 end
81 reactor << cuboid if op == :on
82end
83
84puts reactor.sum(&:volume)