advent_of_code/year2019/
day12.rs1use crate::input::Input;
2
3#[derive(Clone)]
4struct Moons {
5 positions: [[i32; 3]; 4],
6 velocities: [[i32; 3]; 4],
7}
8
9pub const fn gcd(mut a: u64, mut b: u64) -> u64 {
10 while b != 0 {
11 let tmp = a;
12 a = b;
13 b = tmp % b;
14 }
15 a
16}
17
18pub const fn lcd(a: u64, b: u64) -> u64 {
19 a * b / gcd(a, b)
20}
21
22pub const fn lcd3(a: u64, b: u64, c: u64) -> u64 {
23 lcd(a, lcd(b, c))
24}
25
26impl Moons {
27 fn parse(input: &str) -> Result<Self, String> {
28 let mut positions = [[0; 3]; 4];
29 for (i, line) in input.lines().enumerate() {
30 let error_message = |_| format!("Invalid line: {}", i + 1);
31 let parts: Vec<&str> = line.split(['=', ' ', '>', ',']).collect();
32
33 if parts.len() != 9 {
34 return Err(format!("Invalid line: {}", i + 1));
35 }
36
37 positions[i][0] = parts[1].trim().parse::<i32>().map_err(error_message)?;
38 positions[i][1] = parts[4].trim().parse::<i32>().map_err(error_message)?;
39 positions[i][2] = parts[7].trim().parse::<i32>().map_err(error_message)?;
40 }
41
42 Ok(Self {
43 positions,
44 velocities: [[0; 3]; 4],
45 })
46 }
47
48 fn total_energy(&self) -> u64 {
49 let mut total_energy = 0_u64;
50 for i in 0..4 {
51 let potential_energy = self.positions[i].iter().map(|&x| x.abs()).sum::<i32>() as u64;
52 let kinetic_energy = self.velocities[i].iter().map(|&x| x.abs()).sum::<i32>() as u64;
53 total_energy += potential_energy * kinetic_energy;
54 }
55 total_energy
56 }
57
58 fn step(&mut self) {
59 for i in 0..4 {
60 for j in 0..4 {
61 if i != j {
62 for k in 0..3 {
63 self.velocities[i][k] +=
64 signum(self.positions[j][k] - self.positions[i][k]);
65 }
66 }
67 }
68 }
69
70 for (i, position) in self.positions.iter_mut().enumerate() {
71 for (k, p) in position.iter_mut().enumerate() {
72 *p += self.velocities[i][k];
73 }
74 }
75 }
76}
77
78const fn signum(value: i32) -> i32 {
79 match value {
80 _ if value < 0 => -1,
81 _ if value == 0 => 0,
82 _ => 1,
83 }
84}
85
86pub fn part1_nth(input_string: &str, n: usize) -> Result<u64, String> {
87 let mut moons = Moons::parse(input_string)?;
88 for _ in 0..n {
89 moons.step();
90 }
91 Ok(moons.total_energy())
92}
93
94pub fn solve(input: &Input) -> Result<u64, String> {
95 if input.is_part_one() {
96 part1_nth(input.text, 1000)
97 } else {
98 let mut moons = Moons::parse(input.text)?;
99 let initial_moons = moons.clone();
100 let mut cycles: [Option<u64>; 3] = [None; 3];
101
102 let mut step = 0;
103 while cycles.iter().any(Option::is_none) {
104 moons.step();
105 step += 1;
106
107 for (i, cycle) in cycles.iter_mut().enumerate() {
108 if cycle.is_none() {
109 let mut same = true;
110 for moon in 0..4 {
111 if initial_moons.positions[moon][i] != moons.positions[moon][i]
112 || initial_moons.velocities[moon][i] != moons.velocities[moon][i]
113 {
114 same = false;
115 }
116 }
117 if same {
118 cycle.replace(step);
119 }
120 }
121 }
122 }
123
124 Ok(lcd3(
125 cycles[0].ok_or("Cycles not found")?,
126 cycles[1].ok_or("Cycles not found")?,
127 cycles[2].ok_or("Cycles not found")?,
128 ))
129 }
130}
131
132#[test]
133pub fn tests() {
134 assert_eq!(
135 part1_nth(
136 "<x=-1, y=0, z=2>
137<x=2, y=-10, z=-7>
138<x=4, y=-8, z=8>
139<x=3, y=5, z=-1>",
140 10
141 ),
142 Ok(179)
143 );
144
145 let input = include_str!("day12_input.txt");
146 test_part_one!(input => 6220);
147 test_part_two!(input => 548_525_804_273_976);
148}