Added Solution for 2019 day 12

This commit is contained in:
Chris Alge 2023-03-12 18:29:00 +01:00
parent 7525dd7168
commit dd0de2a736
5 changed files with 425 additions and 0 deletions

View file

@ -0,0 +1,8 @@
[package]
name = "day12_the_n-body_problem"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,256 @@
The space near Jupiter is not a very safe place; you need to be careful of a [big distracting red spot](https://en.wikipedia.org/wiki/Great_Red_Spot), extreme [radiation](https://en.wikipedia.org/wiki/Magnetosphere_of_Jupiter), and a [whole lot of moons](https://en.wikipedia.org/wiki/Moons_of_Jupiter#List) swirling around. You decide to start by tracking the four largest moons: *Io*, *Europa*, *Ganymede*, and *Callisto*.
After a brief scan, you calculate the *position of each moon* (your puzzle input). You just need to *simulate their motion* so you can avoid them.
Each moon has a 3-dimensional position (`x`, `y`, and `z`) and a 3-dimensional velocity. The position of each moon is given in your scan; the `x`, `y`, and `z` velocity of each moon starts at `0`.
Simulate the motion of the moons in *time steps*. Within each time step, first update the velocity of every moon by applying *gravity*. Then, once all moons' velocities have been updated, update the position of every moon by applying *velocity*. Time progresses by one step once all of the positions are updated.
To apply *gravity*, consider every *pair* of moons. On each axis (`x`, `y`, and `z`), the velocity of each moon changes by *exactly +1 or -1* to pull the moons together. For example, if Ganymede has an `x` position of `3`, and Callisto has a `x` position of `5`, then Ganymede's `x` velocity *changes by +1* (because `5 > 3`) and Callisto's `x` velocity *changes by -1* (because `3 < 5`). However, if the positions on a given axis are the same, the velocity on that axis *does not change* for that pair of moons.
Once all gravity has been applied, apply *velocity*: simply add the velocity of each moon to its own position. For example, if Europa has a position of `x=1, y=2, z=3` and a velocity of `x=-2, y=0,z=3`, then its new position would be `x=-1, y=2, z=6`. This process does not modify the velocity of any moon.
For example, suppose your scan reveals the following positions:
```
<x=-1, y=0, z=2>
<x=2, y=-10, z=-7>
<x=4, y=-8, z=8>
<x=3, y=5, z=-1>
```
Simulating the motion of these moons would produce the following:
```
After 0 steps:
pos=<x=-1, y= 0, z= 2>, vel=<x= 0, y= 0, z= 0>
pos=<x= 2, y=-10, z=-7>, vel=<x= 0, y= 0, z= 0>
pos=<x= 4, y= -8, z= 8>, vel=<x= 0, y= 0, z= 0>
pos=<x= 3, y= 5, z=-1>, vel=<x= 0, y= 0, z= 0>
After 1 step:
pos=<x= 2, y=-1, z= 1>, vel=<x= 3, y=-1, z=-1>
pos=<x= 3, y=-7, z=-4>, vel=<x= 1, y= 3, z= 3>
pos=<x= 1, y=-7, z= 5>, vel=<x=-3, y= 1, z=-3>
pos=<x= 2, y= 2, z= 0>, vel=<x=-1, y=-3, z= 1>
After 2 steps:
pos=<x= 5, y=-3, z=-1>, vel=<x= 3, y=-2, z=-2>
pos=<x= 1, y=-2, z= 2>, vel=<x=-2, y= 5, z= 6>
pos=<x= 1, y=-4, z=-1>, vel=<x= 0, y= 3, z=-6>
pos=<x= 1, y=-4, z= 2>, vel=<x=-1, y=-6, z= 2>
After 3 steps:
pos=<x= 5, y=-6, z=-1>, vel=<x= 0, y=-3, z= 0>
pos=<x= 0, y= 0, z= 6>, vel=<x=-1, y= 2, z= 4>
pos=<x= 2, y= 1, z=-5>, vel=<x= 1, y= 5, z=-4>
pos=<x= 1, y=-8, z= 2>, vel=<x= 0, y=-4, z= 0>
After 4 steps:
pos=<x= 2, y=-8, z= 0>, vel=<x=-3, y=-2, z= 1>
pos=<x= 2, y= 1, z= 7>, vel=<x= 2, y= 1, z= 1>
pos=<x= 2, y= 3, z=-6>, vel=<x= 0, y= 2, z=-1>
pos=<x= 2, y=-9, z= 1>, vel=<x= 1, y=-1, z=-1>
After 5 steps:
pos=<x=-1, y=-9, z= 2>, vel=<x=-3, y=-1, z= 2>
pos=<x= 4, y= 1, z= 5>, vel=<x= 2, y= 0, z=-2>
pos=<x= 2, y= 2, z=-4>, vel=<x= 0, y=-1, z= 2>
pos=<x= 3, y=-7, z=-1>, vel=<x= 1, y= 2, z=-2>
After 6 steps:
pos=<x=-1, y=-7, z= 3>, vel=<x= 0, y= 2, z= 1>
pos=<x= 3, y= 0, z= 0>, vel=<x=-1, y=-1, z=-5>
pos=<x= 3, y=-2, z= 1>, vel=<x= 1, y=-4, z= 5>
pos=<x= 3, y=-4, z=-2>, vel=<x= 0, y= 3, z=-1>
After 7 steps:
pos=<x= 2, y=-2, z= 1>, vel=<x= 3, y= 5, z=-2>
pos=<x= 1, y=-4, z=-4>, vel=<x=-2, y=-4, z=-4>
pos=<x= 3, y=-7, z= 5>, vel=<x= 0, y=-5, z= 4>
pos=<x= 2, y= 0, z= 0>, vel=<x=-1, y= 4, z= 2>
After 8 steps:
pos=<x= 5, y= 2, z=-2>, vel=<x= 3, y= 4, z=-3>
pos=<x= 2, y=-7, z=-5>, vel=<x= 1, y=-3, z=-1>
pos=<x= 0, y=-9, z= 6>, vel=<x=-3, y=-2, z= 1>
pos=<x= 1, y= 1, z= 3>, vel=<x=-1, y= 1, z= 3>
After 9 steps:
pos=<x= 5, y= 3, z=-4>, vel=<x= 0, y= 1, z=-2>
pos=<x= 2, y=-9, z=-3>, vel=<x= 0, y=-2, z= 2>
pos=<x= 0, y=-8, z= 4>, vel=<x= 0, y= 1, z=-2>
pos=<x= 1, y= 1, z= 5>, vel=<x= 0, y= 0, z= 2>
After 10 steps:
pos=<x= 2, y= 1, z=-3>, vel=<x=-3, y=-2, z= 1>
pos=<x= 1, y=-8, z= 0>, vel=<x=-1, y= 1, z= 3>
pos=<x= 3, y=-6, z= 1>, vel=<x= 3, y= 2, z=-3>
pos=<x= 2, y= 0, z= 4>, vel=<x= 1, y=-1, z=-1>
```
Then, it might help to calculate the *total energy in the system*. The total energy for a single moon is its *potential energy* multiplied by its *kinetic energy*. A moon's *potential energy* is the sum of the [absolute values](https://en.wikipedia.org/wiki/Absolute_value) of its `x`, `y`, and `z` position coordinates. A moon's *kinetic energy* is the sum of the absolute values of its velocity coordinates. Below, each line shows the calculations for a moon's potential energy (`pot`), kinetic energy (`kin`), and total energy:
```
Energy after 10 steps:
pot: 2 + 1 + 3 = 6; kin: 3 + 2 + 1 = 6; total: 6 * 6 = 36
pot: 1 + 8 + 0 = 9; kin: 1 + 1 + 3 = 5; total: 9 * 5 = 45
pot: 3 + 6 + 1 = 10; kin: 3 + 2 + 3 = 8; total: 10 * 8 = 80
pot: 2 + 0 + 4 = 6; kin: 1 + 1 + 1 = 3; total: 6 * 3 = 18
Sum of total energy: 36 + 45 + 80 + 18 = 179
```
In the above example, adding together the total energy for all moons after 10 steps produces the total energy in the system, `*179*`.
Here's a second example:
```
<x=-8, y=-10, z=0>
<x=5, y=5, z=10>
<x=2, y=-7, z=3>
<x=9, y=-8, z=-3>
```
Every ten steps of simulation for 100 steps produces:
```
After 0 steps:
pos=<x= -8, y=-10, z= 0>, vel=<x= 0, y= 0, z= 0>
pos=<x= 5, y= 5, z= 10>, vel=<x= 0, y= 0, z= 0>
pos=<x= 2, y= -7, z= 3>, vel=<x= 0, y= 0, z= 0>
pos=<x= 9, y= -8, z= -3>, vel=<x= 0, y= 0, z= 0>
After 10 steps:
pos=<x= -9, y=-10, z= 1>, vel=<x= -2, y= -2, z= -1>
pos=<x= 4, y= 10, z= 9>, vel=<x= -3, y= 7, z= -2>
pos=<x= 8, y=-10, z= -3>, vel=<x= 5, y= -1, z= -2>
pos=<x= 5, y=-10, z= 3>, vel=<x= 0, y= -4, z= 5>
After 20 steps:
pos=<x=-10, y= 3, z= -4>, vel=<x= -5, y= 2, z= 0>
pos=<x= 5, y=-25, z= 6>, vel=<x= 1, y= 1, z= -4>
pos=<x= 13, y= 1, z= 1>, vel=<x= 5, y= -2, z= 2>
pos=<x= 0, y= 1, z= 7>, vel=<x= -1, y= -1, z= 2>
After 30 steps:
pos=<x= 15, y= -6, z= -9>, vel=<x= -5, y= 4, z= 0>
pos=<x= -4, y=-11, z= 3>, vel=<x= -3, y=-10, z= 0>
pos=<x= 0, y= -1, z= 11>, vel=<x= 7, y= 4, z= 3>
pos=<x= -3, y= -2, z= 5>, vel=<x= 1, y= 2, z= -3>
After 40 steps:
pos=<x= 14, y=-12, z= -4>, vel=<x= 11, y= 3, z= 0>
pos=<x= -1, y= 18, z= 8>, vel=<x= -5, y= 2, z= 3>
pos=<x= -5, y=-14, z= 8>, vel=<x= 1, y= -2, z= 0>
pos=<x= 0, y=-12, z= -2>, vel=<x= -7, y= -3, z= -3>
After 50 steps:
pos=<x=-23, y= 4, z= 1>, vel=<x= -7, y= -1, z= 2>
pos=<x= 20, y=-31, z= 13>, vel=<x= 5, y= 3, z= 4>
pos=<x= -4, y= 6, z= 1>, vel=<x= -1, y= 1, z= -3>
pos=<x= 15, y= 1, z= -5>, vel=<x= 3, y= -3, z= -3>
After 60 steps:
pos=<x= 36, y=-10, z= 6>, vel=<x= 5, y= 0, z= 3>
pos=<x=-18, y= 10, z= 9>, vel=<x= -3, y= -7, z= 5>
pos=<x= 8, y=-12, z= -3>, vel=<x= -2, y= 1, z= -7>
pos=<x=-18, y= -8, z= -2>, vel=<x= 0, y= 6, z= -1>
After 70 steps:
pos=<x=-33, y= -6, z= 5>, vel=<x= -5, y= -4, z= 7>
pos=<x= 13, y= -9, z= 2>, vel=<x= -2, y= 11, z= 3>
pos=<x= 11, y= -8, z= 2>, vel=<x= 8, y= -6, z= -7>
pos=<x= 17, y= 3, z= 1>, vel=<x= -1, y= -1, z= -3>
After 80 steps:
pos=<x= 30, y= -8, z= 3>, vel=<x= 3, y= 3, z= 0>
pos=<x= -2, y= -4, z= 0>, vel=<x= 4, y=-13, z= 2>
pos=<x=-18, y= -7, z= 15>, vel=<x= -8, y= 2, z= -2>
pos=<x= -2, y= -1, z= -8>, vel=<x= 1, y= 8, z= 0>
After 90 steps:
pos=<x=-25, y= -1, z= 4>, vel=<x= 1, y= -3, z= 4>
pos=<x= 2, y= -9, z= 0>, vel=<x= -3, y= 13, z= -1>
pos=<x= 32, y= -8, z= 14>, vel=<x= 5, y= -4, z= 6>
pos=<x= -1, y= -2, z= -8>, vel=<x= -3, y= -6, z= -9>
After 100 steps:
pos=<x= 8, y=-12, z= -9>, vel=<x= -7, y= 3, z= 0>
pos=<x= 13, y= 16, z= -3>, vel=<x= 3, y=-11, z= -5>
pos=<x=-29, y=-11, z= -1>, vel=<x= -3, y= 7, z= 4>
pos=<x= 16, y=-13, z= 23>, vel=<x= 7, y= 1, z= 1>
Energy after 100 steps:
pot: 8 + 12 + 9 = 29; kin: 7 + 3 + 0 = 10; total: 29 * 10 = 290
pot: 13 + 16 + 3 = 32; kin: 3 + 11 + 5 = 19; total: 32 * 19 = 608
pot: 29 + 11 + 1 = 41; kin: 3 + 7 + 4 = 14; total: 41 * 14 = 574
pot: 16 + 13 + 23 = 52; kin: 7 + 1 + 1 = 9; total: 52 * 9 = 468
Sum of total energy: 290 + 608 + 574 + 468 = 1940
```
*What is the total energy in the system* after simulating the moons given in your scan for `1000` steps?
Your puzzle answer was `7687`.
\--- Part Two ---
----------
All this drifting around in space makes you wonder about the nature of the universe. Does history really repeat itself? You're curious whether the moons will ever return to a previous state.
Determine *the number of steps* that must occur before all of the moons' *positions and velocities* exactly match a previous point in time.
For example, the first example above takes `2772` steps before they exactly match a previous point in time; it eventually returns to the initial state:
```
After 0 steps:
pos=<x= -1, y= 0, z= 2>, vel=<x= 0, y= 0, z= 0>
pos=<x= 2, y=-10, z= -7>, vel=<x= 0, y= 0, z= 0>
pos=<x= 4, y= -8, z= 8>, vel=<x= 0, y= 0, z= 0>
pos=<x= 3, y= 5, z= -1>, vel=<x= 0, y= 0, z= 0>
After 2770 steps:
pos=<x= 2, y= -1, z= 1>, vel=<x= -3, y= 2, z= 2>
pos=<x= 3, y= -7, z= -4>, vel=<x= 2, y= -5, z= -6>
pos=<x= 1, y= -7, z= 5>, vel=<x= 0, y= -3, z= 6>
pos=<x= 2, y= 2, z= 0>, vel=<x= 1, y= 6, z= -2>
After 2771 steps:
pos=<x= -1, y= 0, z= 2>, vel=<x= -3, y= 1, z= 1>
pos=<x= 2, y=-10, z= -7>, vel=<x= -1, y= -3, z= -3>
pos=<x= 4, y= -8, z= 8>, vel=<x= 3, y= -1, z= 3>
pos=<x= 3, y= 5, z= -1>, vel=<x= 1, y= 3, z= -1>
After 2772 steps:
pos=<x= -1, y= 0, z= 2>, vel=<x= 0, y= 0, z= 0>
pos=<x= 2, y=-10, z= -7>, vel=<x= 0, y= 0, z= 0>
pos=<x= 4, y= -8, z= 8>, vel=<x= 0, y= 0, z= 0>
pos=<x= 3, y= 5, z= -1>, vel=<x= 0, y= 0, z= 0>
```
Of course, the universe might last for a *very long time* before repeating. Here's a copy of the second example from above:
```
<x=-8, y=-10, z=0>
<x=5, y=5, z=10>
<x=2, y=-7, z=3>
<x=9, y=-8, z=-3>
```
This set of initial positions takes `4686774924` steps before it repeats a previous state! Clearly, you might need to *find a more efficient way to simulate the universe*.
*How many steps does it take* to reach the first state that exactly matches a previous state?
Your puzzle answer was `334945516288044`.
Both parts of this puzzle are complete! They provide two gold stars: \*\*
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
If you still want to see it, you can [get your puzzle input](12/input).

View file

@ -0,0 +1,153 @@
use std::{iter::Sum, ops::AddAssign, num::ParseIntError};
#[derive(Clone, Copy, PartialEq, Eq)]
struct Coordinate {
x: isize,
y: isize,
z: isize,
}
impl Sum for Coordinate {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
let mut x = 0;
let mut y = 0;
let mut z = 0;
iter.for_each(|coord| {
x += coord.x;
y += coord.y;
z += coord.z;
});
Self { x, y, z }
}
}
impl AddAssign for Coordinate {
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
impl Coordinate {
fn energy(&self) -> usize {
self.x.unsigned_abs() + self.y.unsigned_abs() + self.z.unsigned_abs()
}
}
#[derive(Clone, PartialEq, Eq)]
struct Moon {
position: Coordinate,
velocity: Coordinate,
}
impl TryFrom<&str> for Moon {
type Error = ParseIntError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let components: Vec<_> = value.split(&['=', ',', '>']).collect();
assert_eq!(components.len(), 7);
let x = components[1].parse()?;
let y = components[3].parse()?;
let z = components[5].parse()?;
Ok(Self {
position: Coordinate { x, y, z, },
velocity: Coordinate { x: 0, y: 0, z: 0, },
})
}
}
impl Moon {
fn get_energy(&self) -> usize {
self.position.energy() * self.velocity.energy()
}
}
struct LunarSystem {
moons: Vec<Moon>,
}
impl LunarSystem {
fn step_motion(&mut self) {
let positions: Vec<_> = self.moons.iter().map(|moon| moon.position).collect();
for moon in self.moons.iter_mut() {
let delta_v: Coordinate = positions.iter().map(|other| Coordinate { x: (other.x - moon.position.x).signum(), y: (other.y - moon.position.y).signum(), z: (other.z - moon.position.z).signum() }).sum();
moon.velocity += delta_v;
}
for moon in self.moons.iter_mut() {
moon.position += moon.velocity;
}
}
}
pub fn run(input: &str) -> (usize, usize) {
let moons: Vec<Moon> = input.lines().map(Moon::try_from).collect::<Result<Vec<Moon>, _>>().unwrap();
let mut system = LunarSystem { moons: moons.to_vec(), };
for _ in 0..1000 {
system.step_motion();
}
let first = system.moons.iter().map(|moon| moon.get_energy()).sum();
system.moons = moons.to_vec();
let mut periods = [0; 3];
for step in 1.. {
system.step_motion();
if system.moons.iter().enumerate().all(|(idx, moon)| periods[0] == 0 && moon.velocity.x == 0 && moon.position.x == moons[idx].position.x) {
periods[0] = step;
if periods[1] > 0 && periods[2] > 0 {
break;
}
}
if system.moons.iter().enumerate().all(|(idx, moon)| periods[1] == 0 && moon.velocity.y == 0 && moon.position.y == moons[idx].position.y) {
periods[1] = step;
if periods[0] > 0 && periods[2] > 0 {
break;
}
}
if system.moons.iter().enumerate().all(|(idx, moon)| periods[2] == 0 && moon.velocity.z == 0 && moon.position.z == moons[idx].position.z) {
periods[2] = step;
if periods[0] > 0 && periods[1] > 0 {
break;
}
}
}
let second = scm(periods[0], scm(periods[1], periods[2]));
(first, second)
}
fn scm(lhs: usize, rhs: usize) -> usize {
let l = lhs.max(rhs);
let s = lhs.min(rhs);
for i in 1.. {
if (i*l) % s == 0 {
return i*l;
}
}
unreachable!("The loop always runs");
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_sample() {
let sample_input = read_file("tests/sample_input");
assert_eq!(run(&sample_input), (14645, 4686774924));
}
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (7687, 334945516288044));
}
}

View file

@ -0,0 +1,4 @@
<x=16, y=-8, z=13>
<x=4, y=10, z=10>
<x=17, y=-5, z=6>
<x=13, y=-3, z=0>

View file

@ -0,0 +1,4 @@
<x=-8, y=-10, z=0>
<x=5, y=5, z=10>
<x=2, y=-7, z=3>
<x=9, y=-8, z=-3>