[2021][ruby][22.x]
change oottsokzktxqmlqumwnomzzpytwsmyvo
commit db054f6c37a967f862ce1489bed0bd1607df13a3
author Alpha Chen <alpha@kejadlen.dev>
date
parent tnwowlns
diff --git a/2021/ruby/day_22.rb b/2021/ruby/day_22.rb
new file mode 100644
index 0000000..9f1bf7b
--- /dev/null
+++ b/2021/ruby/day_22.rb
@@ -0,0 +1,109 @@
+require "set"
+
+steps = ARGF.read.scan(/(on|off) x=(.+),y=(.+),z=(.+)/)
+  .map {|power, *ranges| [power == "on", *ranges.map { eval(_1) }] }
+  # .select {|_, *ranges| ranges.all? { _1.begin >= -50 && _1.end <= 50 }}
+
+# reactor = Hash.new(false)
+# steps.each do |power,x,y,z|
+#   x.each do |x|
+#     y.each do |y|
+#       z.each do |z|
+#         reactor[[x,y,z]] = power
+#       end
+#     end
+#   end
+# end
+
+# p reactor.count { _2 }
+
+# xs = steps.flat_map { [_2.begin, _2.end] }.minmax
+# ys = steps.flat_map { [_3.begin, _3.end] }.minmax
+# zs = steps.flat_map { [_4.begin, _4.end] }.minmax
+# ons = Range.new(*xs).sum {|x| Range.new(*ys).sum {|y| Range.new(*zs).count {|z|
+#   step = steps.find { _2.cover?(x) && _3.cover?(y) && _4.cover?(z) }
+#   step ? step[0] : false
+# }}}
+
+# p ons
+
+class Range
+  def overlap(other)
+    if cover?(other.begin)
+      min = other.begin
+      max = [self.end, other.end].min
+      (min..max)
+    elsif cover?(other.end)
+      min = [self.begin, other.begin].max
+      max = other.end
+      (min..max)
+    elsif other.cover?(self.begin)
+      min = self.begin
+      max = [self.end, other.end].min
+      (min..max)
+    elsif other.cover?(self.end)
+      min = [self.begin, other.begin].max
+      max = self.end
+      (min..max)
+    else
+      nil
+    end
+  end
+
+  def split(other)
+    overlap = self.overlap(other)
+    return [] if overlap.nil?
+
+    splits = [ overlap ]
+    splits << (self.begin..overlap.begin-1) if overlap.begin > self.begin
+    splits << (overlap.end+1..self.end)     if overlap.end < self.end
+    splits
+  end
+end
+
+Cube = Struct.new(:xs, :ys, :zs) do
+  def -(other)
+    return self unless o = overlap(other)
+
+    xxs = xs.split(other.xs)
+    yys = ys.split(other.ys)
+    zzs = zs.split(other.zs)
+
+    cuboids = xxs.flat_map {|x| yys.flat_map {|y| zzs.map {|z|
+      Cube.new(x, y, z)
+    }}}
+    cuboids.delete(o)
+
+    cuboids
+  end
+
+  def overlap(other)
+    overlaps = to_a.zip(other.to_a).map { _1.overlap(_2) }
+    return nil if overlaps.any?(&:nil?)
+
+    Cube.new(*overlaps)
+  end
+
+  def overlap?(other) = !overlap(other).nil?
+
+  def volume = (xs.end - xs.begin + 1) * (ys.end - ys.begin + 1) * (zs.end - zs.begin + 1)
+
+  def to_s = "[#{[xs, ys, zs].map(&:to_s).join(", ")}]"
+  alias_method :inspect, :to_s
+end
+
+steps = steps.map {|power, *ranges| [power, Cube.new(*ranges)] }
+
+reactor = Set.new
+steps.each do |power, cube|
+  reactor
+    .select { _1.overlap?(cube) }
+    .each do |overlap|
+      reactor.delete(overlap)
+      reactor.merge(overlap - cube)
+    end
+
+  reactor << cube if power
+end
+
+p reactor.sum(&:volume)