changeset 35:ca54f9702892

Day 24 part 2 instructions and partial solution
author IBBoard <dev@ibboard.co.uk>
date Thu, 18 Apr 2024 19:54:59 +0100
parents 59620bbc4084
children 7197f31970ff
files day24.txt day24b.rb
diffstat 2 files changed, 86 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/day24.txt	Fri Jan 05 11:42:13 2024 +0000
+++ b/day24.txt	Thu Apr 18 19:54:59 2024 +0100
@@ -56,4 +56,33 @@
 
 Look for future intersections that happen with an X and Y position each at least 200000000000000 and at most 400000000000000. Disregard the Z axis entirely.
 
-Note: Some objects are moving upwards.
\ No newline at end of file
+Note: Some objects are moving upwards.
+
+--- Part 2 ---
+
+The objects won't _actually_ collide because of the Z dimension. So what starting point and velocity is required
+for an additional object to be thrown such that it hits _all_ objects?
+
+For the example above, given a start of 24, 13, 10 and a velocity of -3, 1, 2 then we get:
+
+Object: 19, 13, 30 @ -2, 1, -2
+Collision time: 5
+Collision position: 9, 18, 20
+
+Object: 18, 19, 22 @ -1, -1, -2
+Collision time: 3
+Collision position: 15, 16, 16
+
+Object: 20, 25, 34 @ -2, -2, -4
+Collision time: 4
+Collision position: 12, 17, 18
+
+Object: 12, 31, 28 @ -1, -2, -1
+Collision time: 6
+Collision position: 6, 19, 22
+
+Object: 20, 19, 15 @ 1, -5, -3
+Collision time: 1
+Collision position: 21, 14, 12
+
+Adding the starting position coordinates together, we get 47.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/day24b.rb	Thu Apr 18 19:54:59 2024 +0100
@@ -0,0 +1,56 @@
+#! /usr/bin/env ruby
+
+require 'prime'
+
+if ARGV.length != 1
+    abort("Incorrect arguments - needs input file")
+elsif not File.exist? (ARGV[0])
+abort("File #{ARGV[0]} did not exist")
+end
+
+file = ARGV[0]
+
+MovingObject = Struct.new(:x, :y, :z, :dx, :dy, :dz)
+Position = Struct.new(:x, :y, :z)
+
+objects = File.open(file, "r").each_line(chomp: true).map do |line|
+    pos, vel = line.split(" @ ")
+    position = pos.split(", ").map(&:to_f)
+    velocity = vel.split(",").map(&:to_f)
+    MovingObject.new(position[0], position[1], position[2], velocity[0], velocity[1], velocity[2])
+end
+
+# Lots of answers talking about "gaussian elimination" and other maths, or use external libraries
+# But some people pointed out that if there's two objects moving at the same speed in a given direction
+# then the new object must move at some factor of the difference between their starting points
+# so that it can cover the other two dimensions between them
+matches = objects.combination(2).filter {|a,b| a.dx == b.dx || a.dy == b.dy || a.dz == b.dz}.group_by {|a,b| [a.dx == b.dx, a.dy == b.dy, a.dz == b.dz]}
+puts "#{matches}"
+x_objs = matches[[true,false,false]]
+y_objs = matches[[false,true,false]]
+z_objs = matches[[false,false,true]]
+x_dist = (x_objs[0][0].x - x_objs[0][1].x).abs.to_i
+y_dist = (y_objs[0][0].y - y_objs[0][1].y).abs.to_i
+z_dist = (z_objs[0][0].z - z_objs[0][1].z).abs.to_i
+
+puts "Starting distance gap: same x = #{x_dist}; same y = #{y_dist}; same z = #{z_dist}"
+puts "GCD of x and y: #{x_dist.gcd(y_dist)}"
+puts "GCD of x and z: #{x_dist.gcd(z_dist)}"
+puts "GCD of x, y and z: #{x_dist.gcd(y_dist).gcd(z_dist)}"
+
+# We can't use lowest common factor/greatest common divisor
+# So try prime divisors that people talk about.
+# (DistanceDifference % (RockVelocity-HailVelocity) = 0
+x_candidates = x_objs.reduce(nil) do |accum, pair|
+    a, b = pair
+    x_diff = (a.x - b.x).abs.to_i
+
+    if x_diff > 0
+        candidates = (1..1000).map {|i| x_diff % i == 0 ? i + a.dx.abs : nil}.compact
+        puts "#{x_diff} => #{candidates}"
+        accum.nil? ? candidates : accum & candidates
+    else
+        accum
+    end
+end
+puts x_candidates
\ No newline at end of file