advent_of_code/year2019/
day12.rsuse crate::input::Input;
#[derive(Clone)]
struct Moons {
positions: [[i32; 3]; 4],
velocities: [[i32; 3]; 4],
}
pub const fn gcd(mut a: u64, mut b: u64) -> u64 {
while b != 0 {
let tmp = a;
a = b;
b = tmp % b;
}
a
}
pub const fn lcd(a: u64, b: u64) -> u64 {
a * b / gcd(a, b)
}
pub const fn lcd3(a: u64, b: u64, c: u64) -> u64 {
lcd(a, lcd(b, c))
}
impl Moons {
fn parse(input: &str) -> Result<Self, String> {
let mut positions = [[0; 3]; 4];
for (i, line) in input.lines().enumerate() {
let error_message = |_| format!("Invalid line: {}", i + 1);
let parts: Vec<&str> = line.split(['=', ' ', '>', ',']).collect();
if parts.len() != 9 {
return Err(format!("Invalid line: {}", i + 1));
}
positions[i][0] = parts[1].trim().parse::<i32>().map_err(error_message)?;
positions[i][1] = parts[4].trim().parse::<i32>().map_err(error_message)?;
positions[i][2] = parts[7].trim().parse::<i32>().map_err(error_message)?;
}
Ok(Self {
positions,
velocities: [[0; 3]; 4],
})
}
fn total_energy(&self) -> u64 {
let mut total_energy = 0_u64;
for i in 0..4 {
let potential_energy = self.positions[i].iter().map(|&x| x.abs()).sum::<i32>() as u64;
let kinetic_energy = self.velocities[i].iter().map(|&x| x.abs()).sum::<i32>() as u64;
total_energy += potential_energy * kinetic_energy;
}
total_energy
}
fn step(&mut self) {
for i in 0..4 {
for j in 0..4 {
if i != j {
for k in 0..3 {
self.velocities[i][k] +=
signum(self.positions[j][k] - self.positions[i][k]);
}
}
}
}
for (i, position) in self.positions.iter_mut().enumerate() {
for (k, p) in position.iter_mut().enumerate() {
*p += self.velocities[i][k];
}
}
}
}
const fn signum(value: i32) -> i32 {
match value {
_ if value < 0 => -1,
_ if value == 0 => 0,
_ => 1,
}
}
pub fn part1_nth(input_string: &str, n: usize) -> Result<u64, String> {
let mut moons = Moons::parse(input_string)?;
for _ in 0..n {
moons.step();
}
Ok(moons.total_energy())
}
pub fn solve(input: &Input) -> Result<u64, String> {
if input.is_part_one() {
part1_nth(input.text, 1000)
} else {
let mut moons = Moons::parse(input.text)?;
let initial_moons = moons.clone();
let mut cycles: [Option<u64>; 3] = [None; 3];
let mut step = 0;
while cycles.iter().any(Option::is_none) {
moons.step();
step += 1;
for (i, cycle) in cycles.iter_mut().enumerate() {
if cycle.is_none() {
let mut same = true;
for moon in 0..4 {
if initial_moons.positions[moon][i] != moons.positions[moon][i]
|| initial_moons.velocities[moon][i] != moons.velocities[moon][i]
{
same = false;
}
}
if same {
cycle.replace(step);
}
}
}
}
Ok(lcd3(
cycles[0].ok_or("Cycles not found")?,
cycles[1].ok_or("Cycles not found")?,
cycles[2].ok_or("Cycles not found")?,
))
}
}
#[test]
pub fn tests() {
use crate::input::{test_part_one, test_part_two};
assert_eq!(
part1_nth(
"<x=-1, y=0, z=2>
<x=2, y=-10, z=-7>
<x=4, y=-8, z=8>
<x=3, y=5, z=-1>",
10
),
Ok(179)
);
let input = include_str!("day12_input.txt");
test_part_one!(input => 6220);
test_part_two!(input => 548_525_804_273_976);
}