Skip to content

Commit d73a523

Browse files
committed
Day 24 2023 Part 2
1 parent d9816c4 commit d73a523

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed

2023/day-24/day-24-part-2.rb

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env ruby
2+
3+
file_path = File.expand_path("../day-24-input.txt", __FILE__)
4+
input = File.read(file_path)
5+
6+
stones = input.each_line.map {
7+
pos, vel = _1.split(?@)
8+
9+
pos = pos.strip.split(?,).map(&:to_i)
10+
vel = vel.strip.split(?,).map(&:to_i)
11+
12+
[pos, vel]
13+
}
14+
15+
# gem install 'prime'
16+
require 'prime'
17+
18+
def factors_of(number)
19+
primes, powers = number.prime_division.transpose
20+
exponents = powers.map { (0.._1).to_a }
21+
22+
exponents.shift.product(*exponents).map do |powers|
23+
primes
24+
.zip(powers)
25+
.map { |prime, power| prime ** power }
26+
.inject(:*)
27+
end
28+
end
29+
30+
# Infer rock velocities by identifying hailstones with the same velocities
31+
# across one dimension, and identifying the factors in the difference in
32+
# that dimension.
33+
#
34+
# The rock's velocity, vx, must move as factor of the difference of two
35+
# hailstones (with respect to relative velocities of the hailstones):
36+
#
37+
# vx = hailstone velocity +/- factor of the difference between two hailstones
38+
#
39+
# https://www.reddit.com/r/adventofcode/comments/18qcfsp
40+
#
41+
# idx - x, y, z
42+
#
43+
# 0 - x
44+
# 1 - y
45+
# 2 - z
46+
#
47+
def rock_v(idx, stones)
48+
stones
49+
.group_by { |pos, vel| vel[idx] }
50+
.select { |vel, stones| stones.size == 2 }
51+
.transform_values { _1.map { |pos, _vel| pos[idx] } }
52+
.map { |v, p|
53+
[
54+
v,
55+
p.inject(&:-).abs
56+
]
57+
}
58+
.select { |_v, d| d != 0 }
59+
.map { |v, d|
60+
factors_of(d).flat_map { |f| [v - f, v + f] }
61+
}
62+
.inject(&:&)
63+
.first
64+
end
65+
66+
# Find the common intersection point for a series of hail stones.
67+
#
68+
# A moving rock is the same as the keeping the rock stationary and
69+
# adjusting the hailstone velocities relative to the rock's velocity
70+
#
71+
# Components (comp) is an array composed of two dimensions:
72+
#
73+
# 0 - x
74+
# 1 - y
75+
# 2 - z
76+
#
77+
def intersection(stones, comp, rock_v0, rock_v1)
78+
stones.combination(2).map { |s1, s2|
79+
p1, v1 = s1
80+
p2, v2 = s2
81+
82+
a1 = (v1[comp[1]] - rock_v1) / (v1[comp[0]] - rock_v0).to_f
83+
b1 = p1[comp[1]] - (a1 * p1[comp[0]])
84+
85+
a2 = (v2[comp[1]] - rock_v1) / (v2[comp[0]] - rock_v0).to_f
86+
b2 = p2[comp[1]] - (a2 * p2[comp[0]])
87+
88+
# Use y = ax + b and a bit of algebra to solve for x and y
89+
#
90+
# x - component 1
91+
# y - component 2
92+
x = (b2 - b1).to_f / (a1 - a2)
93+
y = a1 * x + b1
94+
95+
[x, y].map(&:round).map(&:to_i)
96+
}
97+
.tally.sort_by(&:last).last.first
98+
end
99+
100+
rvx = rock_v(0, stones)
101+
rvy = rock_v(1, stones)
102+
rvz = rock_v(2, stones)
103+
104+
pp rvx
105+
pp rvy
106+
pp rvz
107+
108+
x, y = intersection(stones.sample(3), [0, 1], rvx, rvy)
109+
_, z = intersection(stones.sample(3), [0, 2], rvx, rvz)
110+
111+
pp x + y + z

0 commit comments

Comments
 (0)