Skip to main content

year2019/
day12.rs

1use utils::number::lcm;
2use utils::prelude::*;
3
4/// Simulating gravity between pairs.
5#[derive(Clone, Debug)]
6pub struct Day12 {
7    pos: [[i32; 4]; 3],
8    part1_steps: u16,
9}
10
11impl Day12 {
12    pub fn new(input: &str, input_type: InputType) -> Result<Self, InputError> {
13        let moon = parser::i32()
14            .with_prefix("<x=")
15            .then(parser::i32().with_prefix(", y="))
16            .then(parser::i32().with_prefix(", z=").with_suffix(">"));
17        let moons = moon.repeat_n::<4, _>(parser::eol()).parse_complete(input)?;
18
19        let mut pos = [[0; 4]; 3];
20        for (i, (x, y, z)) in moons.into_iter().enumerate() {
21            pos[0][i] = x;
22            pos[1][i] = y;
23            pos[2][i] = z;
24        }
25
26        Ok(Self {
27            pos,
28            part1_steps: match input_type {
29                InputType::Example => 10,
30                InputType::Real => 1000,
31            },
32        })
33    }
34
35    #[must_use]
36    pub fn part1(&self) -> u32 {
37        let mut pos = self.pos;
38        let mut vel = [[0; 4]; 3];
39
40        for _ in 0..self.part1_steps {
41            Self::step_axis(&mut pos[0], &mut vel[0]);
42            Self::step_axis(&mut pos[1], &mut vel[1]);
43            Self::step_axis(&mut pos[2], &mut vel[2]);
44        }
45
46        let mut total = 0;
47        for moon in 0..4 {
48            let potential = pos[0][moon].unsigned_abs()
49                + pos[1][moon].unsigned_abs()
50                + pos[2][moon].unsigned_abs();
51            let kinetic = vel[0][moon].unsigned_abs()
52                + vel[1][moon].unsigned_abs()
53                + vel[2][moon].unsigned_abs();
54            total += potential * kinetic;
55        }
56
57        total
58    }
59
60    #[must_use]
61    pub fn part2(&self) -> u64 {
62        let x = Self::axis_period(self.pos[0]);
63        let y = Self::axis_period(self.pos[1]);
64        let z = Self::axis_period(self.pos[2]);
65        lcm(lcm(x, y), z)
66    }
67
68    #[inline]
69    fn axis_period(mut pos: [i32; 4]) -> u64 {
70        let mut vel = [0; 4];
71        let mut steps = 0;
72
73        loop {
74            Self::step_axis(&mut pos, &mut vel);
75            steps += 1;
76
77            if vel == [0; 4] {
78                return 2 * steps;
79            }
80        }
81    }
82
83    #[inline]
84    fn step_axis(pos: &mut [i32; 4], vel: &mut [i32; 4]) {
85        let [mut p0, mut p1, mut p2, mut p3] = *pos;
86        let [mut v0, mut v1, mut v2, mut v3] = *vel;
87
88        let d01 = Self::gravity_delta(p0, p1);
89        let d02 = Self::gravity_delta(p0, p2);
90        let d03 = Self::gravity_delta(p0, p3);
91        let d12 = Self::gravity_delta(p1, p2);
92        let d13 = Self::gravity_delta(p1, p3);
93        let d23 = Self::gravity_delta(p2, p3);
94
95        v0 += d01 + d02 + d03;
96        v1 += d12 + d13 - d01;
97        v2 += d23 - d02 - d12;
98        v3 -= d03 + d13 + d23;
99
100        p0 += v0;
101        p1 += v1;
102        p2 += v2;
103        p3 += v3;
104
105        *pos = [p0, p1, p2, p3];
106        *vel = [v0, v1, v2, v3];
107    }
108
109    #[inline]
110    fn gravity_delta(a: i32, b: i32) -> i32 {
111        i32::from(a < b) - i32::from(a > b)
112    }
113}
114
115examples!(Day12 -> (u32, u64) [
116    {
117        input: "<x=-1, y=0, z=2>\n\
118            <x=2, y=-10, z=-7>\n\
119            <x=4, y=-8, z=8>\n\
120            <x=3, y=5, z=-1>",
121        part1: 179,
122        part2: 2772,
123    },
124    {
125        input: "<x=-8, y=-10, z=0>\n\
126            <x=5, y=5, z=10>\n\
127            <x=2, y=-7, z=3>\n\
128            <x=9, y=-8, z=-3>",
129        // part1 example using this input uses a different step count to the previous example
130        part2: 4686774924,
131    },
132]);