diff --git a/2019/day12/part2.rb b/2019/day12/part2.rb new file mode 100644 index 0000000..4fb7256 --- /dev/null +++ b/2019/day12/part2.rb @@ -0,0 +1,217 @@ +# frozen_string_literal: true + +require 'set' + +Position = Struct.new(:x, :y, :z) do + def to_s + "(#{x}, #{y}, #{z})" + end + + def plus(v) + Position.new(x + v.x, y + v.y, z + v.z) + end +end + +Velocity = Struct.new(:x, :y, :z) do + def to_s + "(#{x}, #{y}, #{z})" + end + + def plus(dx, dy, dz) + Velocity.new(x + dx, y + dy, z + dz) + end +end + +Moon = Struct.new(:pos, :vel) do + def to_s + "pos=#{pos} vel=#{vel}" + end + + def energy + potential_energy * kinetic_energy + end + + def potential_energy + pos.x.abs + pos.y.abs + pos.z.abs + end + + def kinetic_energy + vel.x.abs + vel.y.abs + vel.z.abs + end +end + +class AxisCache + def initialize(axis) + @axis = axis + @store = [] + end + + def <<(moons) + @store << by_axis(moons) + end + + def include?(moons) + @store.include? by_axis(moons) + end + + def index(moons) + @store.index by_axis(moons) + end + + private + + def by_axis(moons) + case @axis + when :x + moons.map { |moon| [moon.pos.x, moon.vel.x] } + when :y + moons.map { |moon| [moon.pos.y, moon.vel.y] } + when :z + moons.map { |moon| [moon.pos.z, moon.vel.z] } + end + end +end + +def parse(input) + positions = [] + input.lines.each do |line| + coords = {} + line.strip[1...-1].split(',').each do |chunk| + key, val = chunk.split('=') + coords[key.strip] = val.to_i + end + positions << Position.new(coords['x'], coords['y'], coords['z']) + end + positions +end + +def pairs_with_indices(arr) + (0...(arr.length)).each do |i| + ((i + 1)...(arr.length)).each do |j| + yield(arr[i], i, arr[j], j) + end + end +end + +def gravity(moons) + dvs = 4.times.map { Velocity.new(0, 0, 0) } + pairs_with_indices(moons) do |a, i, b, j| + # From a's perspective + dx = b.pos.x <=> a.pos.x + dy = b.pos.y <=> a.pos.y + dz = b.pos.z <=> a.pos.z + dvs[i] = dvs[i].plus(dx, dy, dz) + dvs[j] = dvs[j].plus(-dx, -dy, -dz) + end + moons.each_with_index.map do |moon, i| + dv = dvs[i] + Moon.new(moon.pos, moon.vel.plus(dv.x, dv.y, dv.z)) + end +end + +def move(moons) + moons.map do |moon| + Moon.new(moon.pos.plus(moon.vel), moon.vel) + end +end + +def step(moons) + move(gravity(moons)) +end + +def find_repeat(moons) + # x_repeated + nx * x_period + # y_repeated + ny * y_period + # z_repeated + nz * z_period +end + +input = File.read(File.join(__dir__, 'input')) +# input = <<~FIRST +# +# +# +# +# FIRST +# input = <<~SECOND +# +# +# +# +# SECOND +positions = parse(input) +moons = positions.map { |pos| Moon.new(pos, Velocity.new(0, 0, 0)) } + +x = AxisCache.new(:x) +y = AxisCache.new(:y) +z = AxisCache.new(:z) + +x << moons +y << moons +z << moons + +x_first = nil +x_period = nil +y_first = nil +y_period = nil +z_first = nil +z_period = nil + +i = 0 +until x_period && y_period && z_period + i += 1 + puts "#{i} steps" if (i % 100).zero? + moons = move(gravity(moons)) + + if x_period.nil? && x.include?(moons) + x_first ||= x.index moons + x_period ||= i - x_first + end + + if y_period.nil? && y.include?(moons) + y_first ||= y.index moons + y_period ||= i - y_first + end + + if z_period.nil? && z.include?(moons) + z_first ||= z.index moons + z_period ||= i - z_first + end +end +puts i +pp [x_first, x_period] +pp [y_first, y_period] +pp [z_first, z_period] + +def factors(n) + res = Hash.new { |hash, key| hash[key] = 0 } + d = 2 + until n == 1 + while (n % d).zero? + res[d] += 1 + n /= d + end + d += 1 + end + res +end + +def lcm(*ints) + facts = [] + ints.each do |i| + facts << factors(i) + end + res = Hash.new { |hash, key| hash[key] = 0 } + facts.each do |fs| + fs.each_pair do |f, times| + res[f] = [res[f], times].max + end + end + + product = 1 + res.each_pair do |f, rep| + product *= f**rep + end + product +end + +pp lcm(x_period, y_period, z_period)