Skip to content

Commit

Permalink
d24 pt2 done
Browse files Browse the repository at this point in the history
  • Loading branch information
derailed-dash committed Dec 28, 2023
1 parent e5db577 commit affcf36
Showing 1 changed file with 72 additions and 7 deletions.
79 changes: 72 additions & 7 deletions src/AoC_2023/Dazbo's_Advent_of_Code_2023.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -9427,10 +9427,18 @@
"source": [
"### Day 24 Part 2\n",
"\n",
"We can throw a rock that can reach any integer location and with any integer velocity. We want the rock to collide with EVERY hailstone. The rock itself won't change position or velocity when it hits a hailstone.\n",
"Whilst I could just about write the math here, the idea to use the [SymPy](https://www.sympy.org/en/index.html) library came from the amazing [Hyperneutrino](https://hyper-neutrino.xyz/). I'm not sure I would have been able to solve this one without that bit of magic.\n",
"\n",
"Anyhoo...\n",
"\n",
"We want to throw a rock that will collide with EVERY hailstone at some point in time. (In Part 1, we were only checking that the trajectories intersected.) We can throw our rock from any starting position, and with any arbitrary velocity. The rock itself won't change position or velocity when it hits a hailstone.\n",
"\n",
"**Determine the exact position and velocity the rock needs to have at time 0 so that it perfectly collides with every hailstone. What do you get if you add up the X, Y, and Z coordinates of that initial position?**\n",
"\n",
"**My solution:**\n",
"\n",
"First, let's understand the math:\n",
"\n",
"We can determine the time `t` when the rock (`r`) will hit a hailstone (`h`). Recall that location is given by initial location, plus `time * velocity`.\n",
"\n",
"First, in the `x` direction only:\n",
Expand All @@ -9448,11 +9456,32 @@
"\\end{align}\n",
"$$\n",
"\n",
"But this final equation must be true for all three vectors:\n",
"So at this time `t`, we have a collision, if only considering `x`. But this equation must be true for all three vectors. I.e. the rock and hailstone must collide at the same time in all three dimensions:\n",
"\n",
"$$\n",
"t = \\frac{x_{r} - x_{h}}{v_{x_{h}} - v_{x_{r}}} = \\frac{y_{r} - y_{h}}{v_{y_{h}} - v_{y_{r}}} = \\frac{z_{r} - z_{h}}{v_{z_{h}} - v_{z_{r}}} \n",
"$$\n"
"t = \\frac{x_{r} - x_{h}}{v_{x_{h}} - v_{x_{r}}} = \\frac{y_{r} - y_{h}}{v_{y_{h}} - v_{y_{r}}} = \\frac{z_{r} - z_{h}}{v_{z_{h}} - v_{z_{r}}}\n",
"$$\n",
"\n",
"So now we can rearrange such that we can relate the rock's position and velocity to each hailstone's known position and velocity.\n",
"\n",
"$$\n",
"\\begin{align}\n",
"(x_{r} - x_{h})(v_{y_{h}} - v_{y_{r}}) &= (y_{r} - y_{h})(v_{x_{h}} - v_{x_{r}}) \\\\\n",
"(y_{r} - y_{h})(v_{z_{h}} - v_{z_{r}}) &= (z_{r} - z_{h})(v_{y_{h}} - v_{y_{r}}) \\\\\n",
"(z_{r} - z_{h})(v_{x_{h}} - v_{x_{r}}) &= (x_{r} - x_{h})(v_{z_{h}} - v_{z_{r}}) \\\\\n",
"\\end{align}\n",
"$$\n",
"\n",
"Now we have a set of equations that are true for any given hailstone, we can loop through hailstones and build up a set of equestions that must be true.\n",
"\n",
"Here I'm using the awesome SymPy library, which allows us to [solve equations](https://docs.sympy.org/latest/guides/solving/index.html#solving-guide). We can use the `solve()` function to [find the solution to a system of equations](https://docs.sympy.org/latest/guides/solving/solve-system-of-equations-algebraically.html). \n",
"\n",
"Here's how we use it:\n",
"\n",
"- Define the mathematical (unknown) symbols we want to find solutions for.\n",
"- Build a set of equations, using hailstone positions and velocities to fill in the known variables.\n",
"- Then use the `solve()` function to obtain all solutions. With a sufficient number of hailstones, there will be only one solution. (Note that we don't need many hailstones to get a unique solution. If we don't limit the number of hailstones, the solution still runs fairly quickly. But it much much faster if we limit the number of hailstones.)\n",
"- Retrieve the one-and-only solution, and extract the rock location values from it. (We don't care about the velocities.)"
]
},
{
Expand All @@ -9461,8 +9490,37 @@
"metadata": {},
"outputs": [],
"source": [
"def solve_part2(data):\n",
" pass"
"def solve_part2(data: list[str]):\n",
" \"\"\" \n",
" Determine the sum of the rock's (x,y,z) coordinate at t=0, for a rock that will hit every hailstone\n",
" in our input data. The rock has constant velocity and is not affected by collisions.\n",
" \"\"\"\n",
" stones = parse_stones(data)\n",
" logger.debug(f\"We have {len(stones)} stones.\")\n",
" \n",
" # define SymPy rock symbols - these are our unknowns representing:\n",
" # initial rock location (xr, yr, zr)\n",
" # rock velocity (vxr, vyr, vzr)\n",
" xr, yr, zr, vxr, vyr, vzr = sympy.symbols(\"xr yr zr vxr vyr vzr\")\n",
" \n",
" equations = [] # we assemble a set of equations that must be true\n",
" for stone in stones[:10]: # we don't need ALL the stones to find a solution. We need just enough.\n",
" x, y, z = stone.posn\n",
" vx, vy, vz = stone.velocity\n",
" equations.append(sympy.Eq((xr-x)*(vy-vyr), (yr-y)*(vx-vxr)))\n",
" equations.append(sympy.Eq((yr-y)*(vz-vzr), (zr-z)*(vy-vyr)))\n",
"\n",
" try:\n",
" solutions = sympy.solve(equations)[0] # SymPy does the hard work\n",
" except sympy.core.sympify.SympifyError as e:\n",
" logger.error(f\"Could not find a solution: {e}\")\n",
" return None\n",
" \n",
" logger.debug(solutions)\n",
" x, y, z = solutions[xr], solutions[yr], solutions[zr]\n",
" logger.info(f\"{x=},{y=},{z=}\")\n",
" \n",
" return sum([x, y, z])\n"
]
},
{
Expand All @@ -9472,10 +9530,17 @@
"outputs": [],
"source": [
"%%time\n",
"sample_inputs = []\n",
"sample_inputs.append(\"\"\"\\\n",
"19, 13, 30 @ -2, 1, -2\n",
"18, 19, 22 @ -1, -1, -2\n",
"20, 25, 34 @ -2, -2, -4\n",
"12, 31, 28 @ -1, -2, -1\n",
"20, 19, 15 @ 1, -5, -3\"\"\")\n",
"sample_answers = [47]\n",
"\n",
"for curr_input, curr_ans in zip(sample_inputs, sample_answers):\n",
" validate(solve_part2(curr_input), curr_ans) # test with sample data\n",
" validate(solve_part2(curr_input.splitlines()), curr_ans) # test with sample data\n",
"\n",
"logger.info(\"Tests passed!\")\n",
"\n",
Expand Down

0 comments on commit affcf36

Please sign in to comment.