[2019][ruby][12.1]
change rrnwuwpkvsztmyrttzuutsxorvurxxxy
commit 78eb447b530e00fb104537ef757061480635e0f0
author Alpha Chen <alpha@kejadlen.dev>
date
parent ospuprqx
diff --git a/2019/ruby/day_12.rb b/2019/ruby/day_12.rb
new file mode 100644
index 0000000..d2b48b1
--- /dev/null
+++ b/2019/ruby/day_12.rb
@@ -0,0 +1,93 @@
+require "matrix"
+require "set"
+
+V = Vector
+Moon = Struct.new(:id, :pos, :vel) do
+  def potential_energy
+    pos.map(&:abs).sum
+  end
+
+  def kinetic_energy
+    vel.map(&:abs).sum
+  end
+end
+
+class Moons < SimpleDelegator
+  def self.from(input)
+    moons = input
+      .scan(/x=(-?\d+), y=(-?\d+), z=(-?\d+)/)
+      .map.with_index {|x,i| Moon.new(i, V[*x.map(&:to_i)], V.zero(3)) }
+    new(moons)
+  end
+
+  def step
+    # apply gravity
+    deltas = Hash.new  {|h,k| h[k] = V.zero(3) }
+    combination(2).each do |a, b|
+      delta = V[*a.pos.zip(b.pos).map {|a,b| a <=> b }]
+      deltas[a] -= delta
+      deltas[b] += delta
+    end
+
+    # apply velocity
+    Moons.new(map {|m|
+      v = m.vel + deltas.fetch(m)
+      Moon.new(m.id, m.pos + v, v)
+    })
+  end
+
+  def energy
+    map {|m| m.potential_energy * m.kinetic_energy }.sum
+  end
+end
+
+require "minitest"
+
+class TestMoons < Minitest::Test
+  def setup
+    @moons = Moons.from(<<~MOONS)
+      <x=-1, y=0, z=2>
+      <x=2, y=-10, z=-7>
+      <x=4, y=-8, z=8>
+      <x=3, y=5, z=-1>
+    MOONS
+  end
+
+  def test_from
+    assert_equal Moon.new(0, V[-1, 0, 2], V[0, 0, 0]), @moons.fetch(0)
+    assert_equal Moon.new(3, V[3, 5, -1], V[0, 0, 0]), @moons.fetch(3)
+  end
+
+  def test_step
+    moons = @moons.step
+
+    assert_equal Moon.new(0, V[2, -1, 1], V[3, -1, -1]), moons.fetch(0)
+    assert_equal Moon.new(3, V[2, 2, 0], V[-1, -3, 1]), moons.fetch(3)
+
+    9.times do
+      moons = moons.step
+    end
+
+    assert_equal Moon.new(0, V[2, 1, -3], V[-3, -2, 1]), moons.fetch(0)
+    assert_equal Moon.new(3, V[2, 0, 4], V[1, -1, -1]), moons.fetch(3)
+  end
+
+  def test_energy
+    10.times do
+      @moons = @moons.step
+    end
+
+    assert_equal 179, @moons.energy
+  end
+end
+
+if ARGF.to_io.tty?
+  require "minitest/autorun"
+else
+  moons = Moons.from(ARGF.read)
+
+  1000.times do
+    moons = moons.step
+  end
+  puts moons.energy
+end