110 lines
2.5 KiB
110 lines
2.5 KiB
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)
|