Skip to content

Commit 0c3d92f

Browse files
committed
sub 100ms again (just need 6 more days lol)
1 parent 70a0022 commit 0c3d92f

File tree

1 file changed

+76
-71
lines changed

1 file changed

+76
-71
lines changed

2019/src/challenges/day12.rs

Lines changed: 76 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,86 @@
11
use std::num::ParseIntError;
22

33
use anyhow::Result;
4-
use itertools::Itertools;
54
use num::Integer;
6-
use rustc_hash::FxHashSet;
75

86
#[derive(Debug, Clone, Copy)]
97
struct Moon {
10-
x: i32,
11-
y: i32,
12-
z: i32,
13-
vx: i32,
14-
vy: i32,
15-
vz: i32,
8+
p: [i32; 3],
9+
v: [i32; 3],
1610
}
1711

1812
impl Moon {
19-
pub fn new(x: i32, y: i32, z: i32) -> Self {
13+
fn new(x: i32, y: i32, z: i32) -> Self {
2014
Self {
21-
x,
22-
y,
23-
z,
24-
vx: 0,
25-
vy: 0,
26-
vz: 0,
15+
p: [x, y, z],
16+
v: [0; 3],
2717
}
2818
}
19+
#[inline(always)]
20+
fn kinetic_energy(&self) -> i32 {
21+
self.v[0].abs() + self.v[1].abs() + self.v[2].abs()
22+
}
23+
#[inline(always)]
24+
fn potential_energy(&self) -> i32 {
25+
self.p[0].abs() + self.p[1].abs() + self.p[2].abs()
26+
}
27+
#[inline(always)]
28+
fn energy(&self) -> i32 {
29+
self.kinetic_energy() * self.potential_energy()
30+
}
31+
}
32+
33+
#[derive(Clone, Copy, Debug)]
34+
struct State {
35+
moons: [Moon; 4],
36+
}
37+
38+
impl State {
39+
#[inline]
40+
fn step_coord<const C: usize>(&mut self) {
41+
for a in 0..4 {
42+
for b in (a + 1)..4 {
43+
self.moons[a].v[C] += (self.moons[b].p[C] - self.moons[a].p[C]).signum();
44+
self.moons[b].v[C] += (self.moons[a].p[C] - self.moons[b].p[C]).signum();
45+
}
46+
}
47+
for a in 0..4 {
48+
self.moons[a].p[C] += self.moons[a].v[C];
49+
}
50+
}
51+
#[inline(always)]
52+
fn eq_coord<const C: usize>(&self, other: &Self) -> bool {
53+
self.moons[0].p[C] == other.moons[0].p[C]
54+
&& self.moons[1].p[C] == other.moons[1].p[C]
55+
&& self.moons[2].p[C] == other.moons[2].p[C]
56+
&& self.moons[3].p[C] == other.moons[3].p[C]
57+
&& self.moons[0].v[C] == other.moons[0].v[C]
58+
&& self.moons[1].v[C] == other.moons[1].v[C]
59+
&& self.moons[2].v[C] == other.moons[2].v[C]
60+
&& self.moons[3].v[C] == other.moons[3].v[C]
61+
}
62+
#[inline]
63+
fn cycle<const C: usize>(&self) -> usize {
64+
let mut length = 1;
65+
let mut state = *self;
66+
state.step_coord::<C>();
67+
while !self.eq_coord::<C>(&state) {
68+
state.step_coord::<C>();
69+
length += 1;
70+
}
71+
72+
length
73+
}
74+
#[inline(always)]
75+
fn step(&mut self) {
76+
self.step_coord::<0>();
77+
self.step_coord::<1>();
78+
self.step_coord::<2>();
79+
}
2980
}
3081

3182
pub async fn day12(input: String) -> Result<(String, String)> {
32-
let moons = input
83+
let state = input
3384
.trim()
3485
.chars()
3586
.filter(|c| c.is_ascii_digit() || matches!(c, '-' | ',' | '\n'))
@@ -43,72 +94,26 @@ pub async fn day12(input: String) -> Result<(String, String)> {
4394
Ok(Moon::new(components[0], components[1], components[2]))
4495
})
4596
.collect::<Result<Vec<Moon>, ParseIntError>>()?;
97+
let state = State {
98+
moons: [state[0], state[1], state[2], state[3]],
99+
};
46100

47101
let part1 = {
48-
let mut moons = moons.clone();
102+
let mut state = state.clone();
49103
for _ in 0..1000 {
50-
// Gravity
51-
for (a, b) in (0..moons.len()).tuple_combinations() {
52-
moons[a].vx += (moons[b].x - moons[a].x).signum();
53-
moons[a].vy += (moons[b].y - moons[a].y).signum();
54-
moons[a].vz += (moons[b].z - moons[a].z).signum();
55-
moons[b].vx += (moons[a].x - moons[b].x).signum();
56-
moons[b].vy += (moons[a].y - moons[b].y).signum();
57-
moons[b].vz += (moons[a].z - moons[b].z).signum();
58-
}
59-
60-
// Velocity
61-
for m in &mut moons {
62-
m.x += m.vx;
63-
m.y += m.vy;
64-
m.z += m.vz;
65-
}
104+
state.step();
66105
}
67-
68-
moons
69-
.iter()
70-
.map(|&m| (m.x.abs() + m.y.abs() + m.z.abs()) * (m.vx.abs() + m.vy.abs() + m.vz.abs()))
71-
.sum::<i32>()
106+
state.moons.iter().map(Moon::energy).sum::<i32>()
72107
};
73108

74109
let part2 = {
75-
// Make sure the size is known at compile time so the compiler has a better shot at
76-
// optimizing
77-
let mut m = [moons[0], moons[1], moons[2], moons[3]];
78-
macro_rules! find_cycle {
79-
($pos:ident, $vel:ident) => {
80-
'block: {
81-
let mut states = FxHashSet::<([i32; 4], [i32; 4])>::default();
82-
83-
for i in 0.. {
84-
if !states.insert(([m[0].$pos, m[1].$pos, m[2].$pos, m[3].$pos], [m[0].$vel, m[1].$vel, m[2].$vel, m[3].$vel])) {
85-
break 'block i as u128
86-
}
87-
88-
for a in 0..4 {
89-
for b in (a+1)..4 {
90-
m[a].$vel += (m[b].$pos - m[a].$pos).signum();
91-
m[b].$vel += (m[a].$pos - m[b].$pos).signum();
92-
}
93-
}
94-
95-
for a in 0..4 {
96-
m[a].$pos += m[a].$vel;
97-
}
98-
}
99-
100-
unreachable!()
101-
}
102-
};
103-
}
104-
let x_cycle = find_cycle!(x, vx);
105-
let y_cycle = find_cycle!(y, vy);
106-
let z_cycle = find_cycle!(z, vz);
110+
let x_cycle = state.cycle::<0>();
111+
let y_cycle = state.cycle::<1>();
112+
let z_cycle = state.cycle::<2>();
107113

108114
let xy_cycle = x_cycle.lcm(&y_cycle);
109115
z_cycle * xy_cycle / z_cycle.gcd(&xy_cycle)
110116
};
111117

112118
Ok((part1.to_string(), part2.to_string()))
113119
}
114-

0 commit comments

Comments
 (0)