1use utils::number::lcm;
2use utils::prelude::*;
3
4#[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 part2: 4686774924,
131 },
132]);