From 1fb181e7d28bec68500bd3cba97b946f0fde66dd Mon Sep 17 00:00:00 2001 From: Alpha Chen Date: Wed, 22 Dec 2021 10:16:25 -0800 Subject: [PATCH] [2021][ruby][22.x] --- 2021/ruby/day_22.rb | 109 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 2021/ruby/day_22.rb 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)