|
| 1 | +namespace AdventOfCode.Puzzles._2023; |
| 2 | + |
| 3 | +[Puzzle(2023, 24, CodeType.Original)] |
| 4 | +public partial class Day_24_Original : IPuzzle |
| 5 | +{ |
| 6 | + //private const double Low = 7; |
| 7 | + //private const double High = 27; |
| 8 | + private const double Low = 200_000_000_000_000; |
| 9 | + private const double High = 400_000_000_000_000; |
| 10 | + |
| 11 | + private record struct Hailstone( |
| 12 | + double Px, double Py, double Pz, |
| 13 | + int Vx, int Vy, int Vz) |
| 14 | + { |
| 15 | + private readonly double _c2d = (Py * Vx) - (Px * Vy); |
| 16 | + |
| 17 | + public readonly bool IsValid2dIntersection(Hailstone other) |
| 18 | + { |
| 19 | + var det = (Vx * other.Vy) - (Vy * other.Vx); |
| 20 | + if (det == 0) |
| 21 | + return false; |
| 22 | + |
| 23 | + var x = ((_c2d * other.Vx) - (Vx * other._c2d)) / det; |
| 24 | + if (!x.Between(Low, High)) |
| 25 | + return false; |
| 26 | + |
| 27 | + var y = ((_c2d * other.Vy) - (Vy * other._c2d)) / det; |
| 28 | + if (!y.Between(Low, High)) |
| 29 | + return false; |
| 30 | + |
| 31 | + return (x - Px) / Vx > 0 |
| 32 | + && (x - other.Px) / other.Vx > 0; |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | + private static long FindIntersection(Hailstone a, Hailstone b, Hailstone c) |
| 37 | + { |
| 38 | + // for any given stone, A = A0 + Av*t, our ray (P + Qt) should have a matching t value. |
| 39 | + // With three stones, we have nine equations and nine unknowns (t, u, v, Px, Py, Pz, Qx, Qy, Qz), assuming that |
| 40 | + // any solution for three will work for all: |
| 41 | + // A0x + Avx*t = Px + Qx*t |
| 42 | + // A0y + Avy*t = Py + Qy*t |
| 43 | + // A0z + Avz*t = Pz + Qz*t |
| 44 | + // |
| 45 | + // B0x + Bvx*u = Px + Qx*u |
| 46 | + // B0y + Bvy*u = Py + Qy*u |
| 47 | + // B0z + Bvz*u = Pz + Qz*u |
| 48 | + // |
| 49 | + // C0x + Cvx*v = Px + Qx*v |
| 50 | + // C0y + Cvy*v = Py + Qy*v |
| 51 | + // C0z + Cvz*v = Pz + Qz*v |
| 52 | + // |
| 53 | + // We can eliminate t, u, and v and end up with 6 equations with 6 unknowns (Px, Py, Pz, Qx, Qy, Qz): |
| 54 | + // (Px - A0x) / (Avx - Qx) = (Py - A0y) / (Avy - Qy) = (Pz - A0z) / (Avz - Qz) |
| 55 | + // (Px - B0x) / (Bvx - Qx) = (Py - B0y) / (Bvy - Qy) = (Pz - B0z) / (Bvz - Qz) |
| 56 | + // (Px - C0x) / (Cvx - Qx) = (Py - C0y) / (Cvy - Qy) = (Pz - C0z) / (Cvz - Qz) |
| 57 | + |
| 58 | + // Rearranging the Px/Py pairing: |
| 59 | + |
| 60 | + // Px * Avy - Px * Qy - A0x * Avy + A0x * Qy = Py * Avx - Py * Qx - A0y * Avx + A0y * Qx |
| 61 | + // (Px * Qy - Py * Qx) = (Px * Avy - Py * Avx) + (A0y * Avx - A0x * Avy) + (A0x * Qy - A0y * Qx) |
| 62 | + // (Px * Qy - Py * Qx) = (Px * Bvy - Py * Bvx) + (B0y * Bvx - B0x * Bvy) + (B0x * Qy - B0y * Qx) |
| 63 | + // (Px * Qy - Py * Qx) = (Px * Cvy - Py * Cvx) + (C0y * Cvx - C0x * Cvy) + (C0x * Qy - C0y * Qx) |
| 64 | + // |
| 65 | + // Note that this gets a common (Px * Qy - Py * Qx) on the left side of everything, and the right side of each is |
| 66 | + // now just a linear equation. |
| 67 | + // Do the same for the Pz/Px and Py/Pz pairints: |
| 68 | + // |
| 69 | + // (Pz * Qx - Px * Qz) = (Pz * Avx - Px * Avz) + (A0x * Avz - A0z * Avx) + (A0z * Qx - A0x * Qz) |
| 70 | + // (Pz * Qx - Px * Qz) = (Pz * Bvx - Px * Bvz) + (B0x * Bvz - B0z * Bvx) + (B0z * Qx - B0x * Qz) |
| 71 | + // (Pz * Qx - Px * Qz) = (Pz * Cvx - Px * Cvz) + (C0x * Cvz - C0z * Cvx) + (C0z * Qx - C0x * Qz) |
| 72 | + // |
| 73 | + // (Py * Qz - Pz * Qy) = (Py * Avz - Pz * Avy) + (A0z * Avy - A0y * Avz) + (A0y * Qz - A0z * Qy) |
| 74 | + // (Py * Qz - Pz * Qy) = (Py * Bvz - Pz * Bvy) + (B0z * Bvy - B0y * Bvz) + (B0y * Qz - B0z * Qy) |
| 75 | + // (Py * Qz - Pz * Qy) = (Py * Cvz - Pz * Cvy) + (C0z * Cvy - C0y * Cvz) + (C0y * Qz - C0z * Qy) |
| 76 | + // |
| 77 | + // This now turns into a series of 6 straight-up linear equations, which we can solve in, you know, the normal way. |
| 78 | + // [Avy - Bvy]Px - [Avx - Bvx]Py - [A0y - B0y]Qx + [A0x - B0x]Qy = (B0y * Bvx - B0x * Bvy) - (A0y * Avx - A0x * Avy) |
| 79 | + // [Avy - Cvy]Px - [Avx - Cvx]Py - [A0y - C0y]Qx + [A0x - C0x]Qy = (C0y * Cvx - C0x * Cvy) - (A0y * Avx - A0x * Avy) |
| 80 | + // [Avx - Bvx]Pz - [Avz - Bvz]Px - [A0x - B0x]Qz + [A0z - B0z]Qx = (B0x * Bvz - B0z * Bvx) - (A0x * Avz - A0z * Avx) |
| 81 | + // [Avx - Cvx]Pz - [Avz - Cvz]Px - [A0x - C0x]Qz + [A0z - C0z]Qx = (C0x * Cvz - C0z * Cvx) - (A0x * Avz - A0z * Avx) |
| 82 | + // [Avz - Bvz]Py - [Avy - Bvy]Pz - [A0z - B0z]Qy + [A0y - B0y]Qz = (B0z * Bvy - B0y * Bvz) - (A0z * Avy - A0y * Avz) |
| 83 | + // [Avz - Cvz]Py - [Avy - Cvy]Pz - [A0z - C0z]Qy + [A0y - C0y]Qz = (C0z * Cvy - C0y * Cvz) - (A0z * Avy - A0y * Avz) |
| 84 | + // |
| 85 | + // Combine some terms to get: |
| 86 | + double abvx = a.Vx - b.Vx; |
| 87 | + double abvy = a.Vy - b.Vy; |
| 88 | + double abvz = a.Vz - b.Vz; |
| 89 | + |
| 90 | + double acvx = a.Vx - c.Vx; |
| 91 | + double acvy = a.Vy - c.Vy; |
| 92 | + double acvz = a.Vz - c.Vz; |
| 93 | + |
| 94 | + var ab0x = a.Px - b.Px; |
| 95 | + var ab0y = a.Py - b.Py; |
| 96 | + var ab0z = a.Pz - b.Pz; |
| 97 | + |
| 98 | + var ac0x = a.Px - c.Px; |
| 99 | + var ac0y = a.Py - c.Py; |
| 100 | + var ac0z = a.Pz - c.Pz; |
| 101 | + |
| 102 | + var h0 = (b.Py * b.Vx) - (b.Px * b.Vy) - ((a.Py * a.Vx) - (a.Px * a.Vy)); |
| 103 | + var h1 = (c.Py * c.Vx) - (c.Px * c.Vy) - ((a.Py * a.Vx) - (a.Px * a.Vy)); |
| 104 | + var h2 = (b.Px * b.Vz) - (b.Pz * b.Vx) - ((a.Px * a.Vz) - (a.Pz * a.Vx)); |
| 105 | + var h3 = (c.Px * c.Vz) - (c.Pz * c.Vx) - ((a.Px * a.Vz) - (a.Pz * a.Vx)); |
| 106 | + var h4 = (b.Pz * b.Vy) - (b.Py * b.Vz) - ((a.Pz * a.Vy) - (a.Py * a.Vz)); |
| 107 | + var h5 = (c.Pz * c.Vy) - (c.Py * c.Vz) - ((a.Pz * a.Vy) - (a.Py * a.Vz)); |
| 108 | + |
| 109 | + // abvy*Px - abvx*Py - ab0y*Qx + ab0x*Qy = h0 |
| 110 | + // acvy*Px - acvx*Py - ac0y*Qx + ac0x*Qy = h1 |
| 111 | + // abvx*Pz - abvz*Px - ab0x*Qz + ab0z*Qx = h2 |
| 112 | + // acvx*Pz - acvz*Px - ac0x*Qz + ac0z*Qx = h3 |
| 113 | + // abvz*Py - abvy*Pz - ab0z*Qy + ab0y*Qz = h4 |
| 114 | + // acvz*Py - acvy*Pz - ac0z*Qy + ac0y*Qz = h5 |
| 115 | + |
| 116 | + // Now we're going to take each pair of those eliminate its initial P variable (leaving just the other) |
| 117 | + // Okay now that's 6 linear equations and 6 variable, right? |
| 118 | + // |
| 119 | + // Px = [h0 + abvx*Py + ab0y*Qx - ab0x*Qy]/abvy |
| 120 | + // Px = [h1 + acvx*Py + ac0y*Qx - ac0x*Qy]/acvy |
| 121 | + // |
| 122 | + // [h0 + abvx*Py + ab0y*Qx - ab0x*Qy]/abvy = [h1 + acvx*Py + ac0y*Qx - ac0x*Qy]/acvy |
| 123 | + // (acvy*abvx - abvy*acvx)*Py = [abvy*ac0y - acvy*ab0y]*Qx + [acvy*ab0x - abvy*ac0x]*Qy + [abvy*h1 - acvy*h0] |
| 124 | + // |
| 125 | + // ----------- |
| 126 | + // Py = ([abvy*ac0y - acvy*ab0y]*Qx + [acvy*ab0x - abvy*ac0x]*Qy + [abvy*h1 - acvy*h0])/(acvy*abvx - abvy*acvx) |
| 127 | + // ----------- |
| 128 | + // |
| 129 | + // [h4 + abvy*Pz + ab0z*Qy - ab0y*Qz]/abvz = [h5 + acvy*Pz + ac0z*Qy - ac0y*Qz]/acvz |
| 130 | + // (acvz*abvy - abvz*acvy)*Pz = [abvz*ac0z - acvz*ab0z]*Qy + [acvz*ab0y - abvz*ac0y)*Qz + [abvz*h5 - acvz*h4] |
| 131 | + // |
| 132 | + // |
| 133 | + // ----------- |
| 134 | + // Pz = ([abvz*ac0z - acvz*ab0z]*Qy + [acvz*ab0y - abvz*ac0y)*Qz + [abvz*h5 - acvz*h4])/(acvz*abvy - abvz*acvy) |
| 135 | + // ----------- |
| 136 | + // |
| 137 | + // [h2 + abvz*Px + ab0x*Qz - ab0z*Qx]/abvx = [h3 + acvz*Px + ac0x*Qz - ac0z*Qx]/acvx |
| 138 | + // (acvx*abvz - abvx*acvz)*Px = [abvx*ac0x - acvx*ab0x]*Qz + [acvx*ab0z - abvx*ac0z]*Qx + [abvx*h3 - acvx*h2] |
| 139 | + // |
| 140 | + // ----------- |
| 141 | + // Px = ([abvx*ac0x - acvx*ab0x]*Qz + [acvx*ab0z - abvx*ac0z]*Qx + [abvx*h3 - acvx*h2])/(acvx*abvz - abvx*acvz) |
| 142 | + // Py = ([abvy*ac0y - acvy*ab0y]*Qx + [acvy*ab0x - abvy*ac0x]*Qy + [abvy*h1 - acvy*h0])/(acvy*abvx - abvy*acvx) |
| 143 | + // Pz = ([abvz*ac0z - acvz*ab0z]*Qy + [acvz*ab0y - abvz*ac0y)*Qz + [abvz*h5 - acvz*h4])/(acvz*abvy - abvz*acvy) |
| 144 | + // ----------- |
| 145 | + // |
| 146 | + // Alright, now we can sub these into (half of) our original linear equations and rearrange in terms of Qx, Qy, |
| 147 | + // and Qz, leaving us with three equations and three variables. |
| 148 | + // abvy*Px - abvx*Py - ab0y*Qx + ab0x*Qy = h0 |
| 149 | + // abvx*Pz - abvz*Px - ab0x*Qz + ab0z*Qx = h2 |
| 150 | + // abvz*Py - abvy*Pz - ab0z*Qy + ab0y*Qz = h4 |
| 151 | + // |
| 152 | + // Make some more variables to make this easier |
| 153 | + // Px = (Pxz*Qz + Pxx*Qx + Pxc)/Pxd |
| 154 | + // Py = (Pyx*Qx + Pyy*Qy + Pyc)/Pyd |
| 155 | + // Pz = (Pzy*Qy + Pzz*Qz + Pzc)/Pzd |
| 156 | + var Pxx = (acvx * ab0z) - (abvx * ac0z); |
| 157 | + var Pyy = (acvy * ab0x) - (abvy * ac0x); |
| 158 | + var Pzz = (acvz * ab0y) - (abvz * ac0y); |
| 159 | + |
| 160 | + var Pxz = (abvx * ac0x) - (acvx * ab0x); |
| 161 | + var Pzy = (abvz * ac0z) - (acvz * ab0z); |
| 162 | + var Pyx = (abvy * ac0y) - (acvy * ab0y); |
| 163 | + |
| 164 | + var Pxc = (abvx * h3) - (acvx * h2); |
| 165 | + var Pyc = (abvy * h1) - (acvy * h0); |
| 166 | + var Pzc = (abvz * h5) - (acvz * h4); |
| 167 | + |
| 168 | + var Pxd = (acvx * abvz) - (abvx * acvz); |
| 169 | + var Pyd = (acvy * abvx) - (abvy * acvx); |
| 170 | + var Pzd = (acvz * abvy) - (abvz * acvy); |
| 171 | + |
| 172 | + // abvy*[(Pxz*Qz + Pxx*Qx + Pxc)/Pxd] - abvx*[(Pyx*Qx + Pyy*Qy + Pyc)/Pyd] - ab0y*Qx + ab0x*Qy = h0 |
| 173 | + // abvx*[(Pzy*Qy + Pzz*Qz + Pzc)/Pzd] - abvz*[(Pxz*Qz + Pxx*Qx + Pxc)/Pxd] - ab0x*Qz + ab0z*Qx = h2 |
| 174 | + // abvz*[(Pyx*Qx + Pyy*Qy + Pyc)/Pyd] - abvy*[(Pzy*Qy + Pzz*Qz + Pzc)/Pzd] - ab0z*Qy + ab0y*Qz = h4 |
| 175 | + // |
| 176 | + // okay this is unintelligible garbage now but we're almost there: |
| 177 | + // |
| 178 | + // ([abvy/Pxd]*Pxz)*Qz + ([abvy/Pxd]*Pxx - [abvx/Pyd]*Pyx - ab0y)*Qx + (ab0x - [abvx/Pyd]*Pyy)*Qy |
| 179 | + // = h0 - [abvy/Pxd]*Pxc + [abvx/Pyd]*Pyc |
| 180 | + // ([abvx/Pzd]*Pzy)*Qy + ([abvx/Pzd]*Pzz - [abvz/Pxd]*Pxz - ab0x)*Qz + (ab0z - [abvz/Pxd]*Pxx)*Qx |
| 181 | + // = h2 - [abvx/Pzd]*Pzc + [abvz/Pxd]*Pxc |
| 182 | + // ([abvz/Pyd]*Pyx)*Qx + ([abvz/Pyd]*Pyy - [abvy/Pzd]*Pzy - ab0z)*Qy + (ab0y - [abvy/Pzd]*Pzz)*Qz |
| 183 | + // = h4 - [abvz/Pyd]*Pyc + [abvy/Pzd]*Pzc |
| 184 | + // |
| 185 | + // MOAR VARIABLES |
| 186 | + var Qz0 = abvy / Pxd * Pxz; |
| 187 | + var Qx0 = (abvy / Pxd * Pxx) - (abvx / Pyd * Pyx) - ab0y; |
| 188 | + var Qy0 = ab0x - (abvx / Pyd * Pyy); |
| 189 | + var r0 = h0 - (abvy / Pxd * Pxc) + (abvx / Pyd * Pyc); |
| 190 | + var Qy1 = abvx / Pzd * Pzy; |
| 191 | + var Qz1 = (abvx / Pzd * Pzz) - (abvz / Pxd * Pxz) - ab0x; |
| 192 | + var Qx1 = ab0z - (abvz / Pxd * Pxx); |
| 193 | + var r1 = h2 - (abvx / Pzd * Pzc) + (abvz / Pxd * Pxc); |
| 194 | + var Qx2 = abvz / Pyd * Pyx; |
| 195 | + var Qy2 = (abvz / Pyd * Pyy) - (abvy / Pzd * Pzy) - ab0z; |
| 196 | + var Qz2 = ab0y - (abvy / Pzd * Pzz); |
| 197 | + var r2 = h4 - (abvz / Pyd * Pyc) + (abvy / Pzd * Pzc); |
| 198 | + |
| 199 | + // Qz0*Qz + Qx0*Qx + Qy0*Qy = r0 |
| 200 | + // Qy1*Qy + Qz1*Qz + Qx1*Qx = r1 |
| 201 | + // Qx2*Qx + Qy2*Qy + Qz2*Qz = r2 |
| 202 | + // |
| 203 | + // Qx = [r0 - Qy0*Qy - Qz0*Qz]/Qx0 |
| 204 | + // = [r1 - Qy1*Qy - Qz1*Qz]/Qx1 |
| 205 | + // = [r2 - Qy2*Qy - Qz2*Qz]/Qx2 |
| 206 | + // |
| 207 | + // Qx1*r0 - Qx1*Qy0*Qy - Qx1*Qz0*Qz = Qx0*r1 - Qx0*Qy1*Qy - Qx0*Qz1*Qz |
| 208 | + // Qy = ([Qx0*Qz1 - Qx1*Qz0]Qz + [Qx1*r0 - Qx0*r1])/[Qx1*Qy0 - Qx0*Qy1] |
| 209 | + // = ([Qx0*Qz2 - Qx2*Qz0]Qz + [Qx2*r0 - Qx0*r2])/[Qx2*Qy0 - Qx0*Qy2] |
| 210 | + // |
| 211 | + // ([Qx2*Qy0 - Qx0*Qy2][Qx0*Qz1 - Qx1*Qz0] - [Qx1*Qy0 - Qx0*Qy1][Qx0*Qz2 - Qx2*Qz0])Qz |
| 212 | + // = [Qx1*Qy0 - Qx0*Qy1][Qx2*r0 - Qx0*r2] - [Qx2*Qy0 - Qx0*Qy2][Qx1*r0 - Qx0*r1] |
| 213 | + |
| 214 | + // Alright after alllll that we can now solve for Qz, and then backsolve for everything else up the chain. |
| 215 | + var Qz = ((((Qx1 * Qy0) - (Qx0 * Qy1)) * ((Qx2 * r0) - (Qx0 * r2))) - (((Qx2 * Qy0) - (Qx0 * Qy2)) * ((Qx1 * r0) - (Qx0 * r1)))) |
| 216 | + / ((((Qx2 * Qy0) - (Qx0 * Qy2)) * ((Qx0 * Qz1) - (Qx1 * Qz0))) - (((Qx1 * Qy0) - (Qx0 * Qy1)) * ((Qx0 * Qz2) - (Qx2 * Qz0)))); |
| 217 | + |
| 218 | + var Qy = ((((Qx0 * Qz1) - (Qx1 * Qz0)) * Qz) + ((Qx1 * r0) - (Qx0 * r1))) / ((Qx1 * Qy0) - (Qx0 * Qy1)); |
| 219 | + |
| 220 | + var Qx = (r0 - (Qy0 * Qy) - (Qz0 * Qz)) / Qx0; |
| 221 | + |
| 222 | + var Px = ((Pxz * Qz) + (Pxx * Qx) + Pxc) / Pxd; |
| 223 | + var Py = ((Pyx * Qx) + (Pyy * Qy) + Pyc) / Pyd; |
| 224 | + var Pz = ((Pzy * Qy) + (Pzz * Qz) + Pzc) / Pzd; |
| 225 | + |
| 226 | + // Now, sum the (rounded, to deal with float precision issues) coordinates of the starting position together. |
| 227 | + // The end. FINALLY. |
| 228 | + return (long)Math.Round(Px) + (long)Math.Round(Py) + (long)Math.Round(Pz); |
| 229 | + } |
| 230 | + |
| 231 | + public (string, string) Solve(PuzzleInput input) |
| 232 | + { |
| 233 | + var regex = HailstoneRegex(); |
| 234 | + var hailstones = input.Lines |
| 235 | + .Select(l => regex.Match(l)) |
| 236 | + .Select(m => new Hailstone( |
| 237 | + Px: double.Parse(m.Groups[1].Value), |
| 238 | + Py: double.Parse(m.Groups[2].Value), |
| 239 | + Pz: double.Parse(m.Groups[3].Value), |
| 240 | + Vx: int.Parse(m.Groups[4].Value), |
| 241 | + Vy: int.Parse(m.Groups[5].Value), |
| 242 | + Vz: int.Parse(m.Groups[6].Value))) |
| 243 | + .ToList(); |
| 244 | + |
| 245 | + var part1 = 0; |
| 246 | + for (var i = 0; i < hailstones.Count; i++) |
| 247 | + { |
| 248 | + for (var j = i + 1; j < hailstones.Count; j++) |
| 249 | + { |
| 250 | + if (hailstones[i].IsValid2dIntersection(hailstones[j])) |
| 251 | + part1++; |
| 252 | + } |
| 253 | + } |
| 254 | + |
| 255 | + var part2 = FindIntersection(hailstones[0], hailstones[1], hailstones[2]); |
| 256 | + |
| 257 | + return (part1.ToString(), part2.ToString()); |
| 258 | + } |
| 259 | + |
| 260 | + [GeneratedRegex(@"^(-?\d+),\s+(-?\d+),\s+(-?\d+)\s+@\s+(-?\d+),\s+(-?\d+),\s+(-?\d+)$")] |
| 261 | + private static partial Regex HailstoneRegex(); |
| 262 | +} |
0 commit comments