Skip to content

Commit 06c494d

Browse files
committed
Day 24
1 parent 44ad790 commit 06c494d

File tree

2 files changed

+545
-0
lines changed

2 files changed

+545
-0
lines changed

day24.groovy

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
import groovy.time.TimeCategory;
2+
import groovy.time.TimeDuration;
3+
import groovy.transform.EqualsAndHashCode;
4+
import groovy.transform.ToString;
5+
import groovy.transform.TupleConstructor;
6+
7+
@EqualsAndHashCode
8+
@ToString
9+
@TupleConstructor
10+
class Vec {
11+
final BigDecimal x;
12+
final BigDecimal y;
13+
final BigDecimal z;
14+
15+
Vec plus(Vec other) {
16+
return new Vec(
17+
this.x + other.x,
18+
this.y + other.y,
19+
this.z + other.z,
20+
);
21+
}
22+
23+
Vec minus(Vec other) {
24+
return new Vec(
25+
this.x - other.x,
26+
this.y - other.y,
27+
this.z - other.z,
28+
);
29+
}
30+
31+
Vec multiply(BigDecimal scalar) {
32+
return new Vec(
33+
this.x * scalar,
34+
this.y * scalar,
35+
this.z * scalar,
36+
);
37+
}
38+
39+
Vec cross(Vec other) {
40+
return new Vec(
41+
this.y * other.z - this.z * other.y,
42+
this.z * other.x - this.x * other.z,
43+
this.x * other.y - this.y * other.x
44+
);
45+
}
46+
47+
def length() {
48+
Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
49+
}
50+
}
51+
52+
{ // Test: Vec.minus
53+
assert new Vec(1, 2, 3) - new Vec(0, 2, 1) == new Vec(1, 0, 2);
54+
}
55+
56+
{ // Test: Vec.cross
57+
assert new Vec(1, 2, 3).cross(new Vec(4, 5, 6)) == new Vec(-3, 6, -3);
58+
}
59+
60+
{ // Test: Vec.length
61+
assert new Vec(3, 4, 12).length() == 13.0;
62+
}
63+
64+
BigDecimal determinant(Vec a, Vec b, Vec c) {
65+
// ax bx cx
66+
// ay by cy
67+
// az bz cz
68+
BigDecimal p1 = a.x * ((b.y * c.z) - (c.y * b.z));
69+
BigDecimal p2 = b.x * ((a.y * c.z) - (c.y * a.z));
70+
BigDecimal p3 = c.x * ((a.y * b.z) - (b.y * a.z));
71+
return p1 - p2 + p3;
72+
}
73+
74+
{ // Test: determinant
75+
assert determinant(new Vec(1, 4, 5), new Vec(3, 6, 7), new Vec(3, 6, 1)) == 36;
76+
}
77+
78+
Vec[] intersects(Ray a, Ray b) {
79+
Vec p1 = a.position;
80+
Vec v1 = a.velocity;
81+
Vec p2 = b.position;
82+
Vec v2 = b.velocity;
83+
84+
Vec vc = v1.cross(v2);
85+
86+
if (vc.length() == 0) {
87+
return null;
88+
}
89+
90+
def t = determinant((p2 - p1), v2, vc) / Math.pow(vc.length(), 2);
91+
def s = determinant((p2 - p1), v1, vc) / Math.pow(vc.length(), 2);
92+
93+
def l1 = p1 + v1 * t;
94+
def l2 = p2 + v2 * s;
95+
def diff = l1 - l2;
96+
97+
if (t < 0 || s < 0) {
98+
return null;
99+
}
100+
101+
if (diff.length() > 1) {
102+
return null;
103+
}
104+
105+
return [l1, l2];
106+
}
107+
108+
{ // Test: intersects
109+
// assert intersects(new Ray("1, 1, 0 @ 1, 1, 1"), new Ray("3, 3, 0 @ -1, -1, 1"));
110+
// assert !intersects(new Ray("1, 1, 0 @ 1, 1, 1"), new Ray("3, 3, 0 @ 1, 2, 1"));
111+
assert intersects(new Ray("19, 13, 0 @ -2, 1, 0"), new Ray("18, 19, 0 @ -1, -1, 0"));
112+
assert intersects(new Ray("19, 13, 0 @ -2, 1, 0"), new Ray("20, 25, 0 @ -2, -2, 0"));
113+
assert intersects(new Ray("19, 13, 0 @ -2, 1, 0"), new Ray("12, 31, 0 @ -1, -2, 0"));
114+
assert ! intersects(new Ray("19, 13, 0 @ -2, 1, 0"), new Ray("20, 19, 0 @ 1, -5, 0"));
115+
116+
def ray = new Ray(new Vec(24, 13, 10), new Vec(-3, 1, 2));
117+
assert intersects(ray, new Ray("19, 13, 30 @ -2, 1, -2"));
118+
assert intersects(ray, new Ray("18, 19, 22 @ -1, -1, -2"));
119+
assert intersects(ray, new Ray("20, 25, 34 @ -2, -2, -4"));
120+
assert intersects(ray, new Ray("12, 31, 28 @ -1, -2, -1"));
121+
assert intersects(ray, new Ray("20, 19, 15 @ 1, -5, -3"));
122+
}
123+
124+
boolean withinBounds(Vec pos, Vec min, Vec max) {
125+
return pos.x >= min.x && pos.x <= max.x
126+
&& pos.y >= min.y && pos.y <= max.y
127+
&& pos.z >= min.z && pos.z <= max.z;
128+
}
129+
130+
@EqualsAndHashCode
131+
class Ray {
132+
final Vec position;
133+
final Vec velocity;
134+
135+
Ray(String input) {
136+
def parts = input.split(" @ ");
137+
def ppos = parts[0].split(", ").collect { it.toDouble() as BigDecimal };
138+
def pvel = parts[1].split(", ").collect { it.toDouble() as BigDecimal };
139+
this.position = new Vec(ppos[0], ppos[1], ppos[2]);
140+
this.velocity = new Vec(pvel[0], pvel[1], pvel[2]);
141+
}
142+
143+
Ray(Vec pos, Vec vel) {
144+
this.position = pos;
145+
this.velocity = vel;
146+
}
147+
148+
String toString() {
149+
return "$position.x, $position.y, $position.z @ $velocity.x, $velocity.y, $velocity.z";
150+
}
151+
}
152+
153+
def countIntersections(def rays, def min, def max) {
154+
def result = 0;
155+
for (int i = 0; i < rays.size(); i++) {
156+
for (int j = i + 1; j < rays.size(); j++) {
157+
def a = rays[i];
158+
def b = rays[j];
159+
def abx = intersects(a, b);
160+
if (abx && withinBounds(abx[0], min, max) && withinBounds(abx[1], min, max)) {
161+
result += 1;
162+
}
163+
}
164+
}
165+
return result;
166+
}
167+
168+
def gaussianElimination(def matrix, def fc) {
169+
int ccount = matrix.length;
170+
for (int i = 0; i < ccount; i++) {
171+
double pivot = matrix[i][i];
172+
for (int j = 0; j < ccount; j++) {
173+
matrix[i][j] = matrix[i][j] / pivot;
174+
}
175+
fc[i] = fc[i] / pivot;
176+
for (int k = 0; k < ccount; k++) {
177+
if (k != i) {
178+
double factor = matrix[k][i];
179+
for (int j = 0; j < ccount; j++) {
180+
matrix[k][j] = matrix[k][j] - factor * matrix[i][j];
181+
}
182+
fc[k] = fc[k] - factor * fc[i];
183+
}
184+
}
185+
}
186+
}
187+
188+
def findIntersectingAll(def rays) {
189+
// Time for some more math magic?
190+
// This is essentially linear equation solving.
191+
192+
def matrix = new double[4][4];
193+
def sol = new double[4];
194+
195+
for (int i = 0; i < 4; i++) {
196+
Ray l1 = rays.get(i);
197+
Ray l2 = rays.get(i + 1);
198+
matrix[i][0] = l2.velocity.y - l1.velocity.y;
199+
matrix[i][1] = l1.velocity.x - l2.velocity.x;
200+
matrix[i][2] = l1.position.y - l2.position.y;
201+
matrix[i][3] = l2.position.x - l1.position.x;
202+
sol[i] = -l1.position.x * l1.velocity.y + l1.position.y * l1.velocity.x + l2.position.x * l2.velocity.y - l2.position.y * l2.velocity.x;
203+
}
204+
205+
gaussianElimination(matrix, sol);
206+
207+
long x = Math.round(sol[0]);
208+
long y = Math.round(sol[1]);
209+
long vx = Math.round(sol[2]);
210+
long vy = Math.round(sol[3]);
211+
212+
matrix = new double[2][2];
213+
sol = new double[2];
214+
for (int i = 0; i < 2; i++) {
215+
Ray l1 = rays.get(i);
216+
Ray l2 = rays.get(i + 1);
217+
matrix[i][0] = l1.velocity.x - l2.velocity.x;
218+
matrix[i][1] = l2.position.x - l1.position.x;
219+
sol[i] = -l1.position.x * l1.velocity.z + l1.position.z * l1.velocity.x + l2.position.x * l2.velocity.z -
220+
l2.position.z * l2.velocity.x - ((l2.velocity.z - l1.velocity.z) * x) - ((l1.position.z - l2.position.z) * vx);
221+
}
222+
223+
gaussianElimination(matrix, sol);
224+
225+
long z = Math.round(sol[0]);
226+
long vz = Math.round(sol[1]);
227+
228+
Ray rock = new Ray(new Vec(x, y, z), new Vec(vx, vy, vz));
229+
return rock.position.x + rock.position.y + rock.position.z;
230+
}
231+
232+
def input = new File("input/day24.txt").readLines();
233+
def rays = input.collect { new Ray(it) };
234+
// rays.each { println(it) };
235+
236+
def rays2d = rays.collect {
237+
new Ray(new Vec(it.position.x, it.position.y, 0), new Vec(it.velocity.x, it.velocity.y, 0))
238+
}
239+
240+
// Part 1
241+
// println(countIntersections(rays2d, new Vec(7, 7, 0), new Vec(27, 27, 0)));
242+
println(countIntersections(rays2d, new Vec(200000000000000, 200000000000000, 0), new Vec(400000000000000, 400000000000000, 0)));
243+
244+
// Part 2
245+
println(findIntersectingAll(rays));

0 commit comments

Comments
 (0)