diff --git a/aoc_2024/src/day21.rs b/aoc_2024/src/day21.rs index ea5f532..2412680 100644 --- a/aoc_2024/src/day21.rs +++ b/aoc_2024/src/day21.rs @@ -1,56 +1,29 @@ use aoc_runner_derive::{aoc, aoc_generator}; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] struct Path { steps: usize, path: [Option; 6], } impl Path { - fn new() -> Self { - Self { - steps: 0, - path: [None, None, None, None, None, None], + const fn from(buttons: &[DirButton]) -> Option { + let steps = buttons.len(); + let mut path = [None, None, None, None, None, None]; + let mut i = 0; + while i < steps { + path[i] = Some(match buttons[i] { + DirButton::Up => DirButton::Up, + DirButton::Down => DirButton::Down, + DirButton::Left => DirButton::Left, + DirButton::Right => DirButton::Right, + DirButton::A => DirButton::A, + }); + i += 1; } - } - - fn push(&mut self, button: DirButton) { - self.path[self.steps] = Some(button); - self.steps += 1; - } - - fn append(&mut self, other: &Self) { - for i in 0..other.steps { - self.push(other.path[i].unwrap()); - } - } - - fn reversed(&self) -> Self { - let mut path = [None; 6]; - (0..self.steps).for_each(|i| { - path[i] = match self.path[self.steps - i - 1] { - Some(DirButton::Up) => Some(DirButton::Down), - Some(DirButton::Down) => Some(DirButton::Up), - Some(DirButton::Left) => Some(DirButton::Right), - Some(DirButton::Right) => Some(DirButton::Left), - Some(DirButton::A) => Some(DirButton::A), - None => None, - }; - }); - - Self { - steps: self.steps, - path, - } - } -} - -impl IntoIterator for Path { - type Item = DirButton; - type IntoIter = std::iter::Flatten, 6>>; - - fn into_iter(self) -> Self::IntoIter { - self.path.into_iter().flatten() + let steps = steps + 1; + path[i] = Some(DirButton::A); + Some(Self { steps, path }) } } @@ -63,17 +36,93 @@ enum DirButton { A, } -impl DirButton { - fn pos(&self) -> (usize, usize) { - match self { - DirButton::Up => (1, 0), - DirButton::Down => (1, 1), - DirButton::Left => (0, 1), - DirButton::Right => (2, 1), - DirButton::A => (2, 0), - } - } +const DIRBUTTON_PATHS: [[[Option; 2]; 5]; 5] = [ + // Up + [ + // Up + [Path::from(&[]), None], + // Down + [Path::from(&[DirButton::Down]), None], + // Left + [Path::from(&[DirButton::Down, DirButton::Left]), None], + // Right + [ + Path::from(&[DirButton::Down, DirButton::Right]), + Path::from(&[DirButton::Right, DirButton::Down]), + ], + // A + [Path::from(&[DirButton::Right]), None], + ], + // Down + [ + // Up + [Path::from(&[DirButton::Up]), None], + // Down + [Path::from(&[]), None], + // Left + [Path::from(&[DirButton::Left]), None], + // Right + [Path::from(&[DirButton::Right]), None], + // A + [ + Path::from(&[DirButton::Up, DirButton::Right]), + Path::from(&[DirButton::Right, DirButton::Up]), + ], + ], + // Left + [ + // Up + [Path::from(&[DirButton::Right, DirButton::Up]), None], + // Down + [Path::from(&[DirButton::Right]), None], + // Left + [Path::from(&[]), None], + // Right + [Path::from(&[DirButton::Right, DirButton::Right]), None], + // A + [ + Path::from(&[DirButton::Right, DirButton::Right, DirButton::Up]), + None, + ], + ], + // Right + [ + // Up + [ + Path::from(&[DirButton::Up, DirButton::Left]), + Path::from(&[DirButton::Left, DirButton::Up]), + ], + // Down + [Path::from(&[DirButton::Left]), None], + // Left + [Path::from(&[DirButton::Left, DirButton::Left]), None], + // Right + [Path::from(&[]), None], + // A + [Path::from(&[DirButton::Up]), None], + ], + // A + [ + // Up + [Path::from(&[DirButton::Left]), None], + // Down + [ + Path::from(&[DirButton::Down, DirButton::Left]), + Path::from(&[DirButton::Left, DirButton::Down]), + ], + // Left + [ + Path::from(&[DirButton::Down, DirButton::Left, DirButton::Left]), + None, + ], + // Right + [Path::from(&[DirButton::Down]), None], + // A + [Path::from(&[]), None], + ], +]; +impl DirButton { fn index(&self) -> usize { match self { DirButton::Up => 0, @@ -84,40 +133,8 @@ impl DirButton { } } - fn to(&self, other: &DirButton) -> [Option; 2] { - let start = self.pos(); - let end = other.pos(); - - let mut x = Path::new(); - let mut y = Path::new(); - - if start.0 < end.0 { - (start.0..end.0).for_each(|_| x.push(DirButton::Right)); - } else { - (end.0..start.0).for_each(|_| x.push(DirButton::Left)); - }; - if start.1 < end.1 { - (start.1..end.1).for_each(|_| y.push(DirButton::Down)); - } else { - (end.1..start.1).for_each(|_| y.push(DirButton::Up)); - }; - - if start.0 == 0 && end.1 == 0 { - x.append(&y); - x.push(DirButton::A); - [Some(x), None] - } else if end.0 == 0 && start.1 == 0 { - y.append(&x); - y.push(DirButton::A); - [Some(y), None] - } else { - let x_copy = x; - x.append(&y); - x.push(DirButton::A); - y.append(&x_copy); - y.push(DirButton::A); - [Some(x), Some(y)] - } + fn to(&self, other: &DirButton) -> &[Option; 2] { + &DIRBUTTON_PATHS[self.index()][other.index()] } } @@ -153,40 +170,79 @@ impl NumButton { } } - #[inline(always)] fn to(&self, other: &NumButton) -> [Option; 2] { let start = self.pos(); let end = other.pos(); - let mut x = Path::new(); - let mut y = Path::new(); + let bx = if start.0 < end.0 { + DirButton::Right + } else { + DirButton::Left + }; + let dx = start.0.abs_diff(end.0); - if start.0 < end.0 { - (start.0..end.0).for_each(|_| x.push(DirButton::Right)); + let by = if start.1 < end.1 { + DirButton::Down } else { - (end.0..start.0).for_each(|_| x.push(DirButton::Left)); - }; - if start.1 < end.1 { - (start.1..end.1).for_each(|_| y.push(DirButton::Down)); - } else { - (end.1..start.1).for_each(|_| y.push(DirButton::Up)); + DirButton::Up }; + let dy = start.1.abs_diff(end.1); if start.0 == 0 && end.1 == 3 { - x.append(&y); - x.push(DirButton::A); - [Some(x), None] + let mut x = [None, None, None, None, None, None]; + (0..dx).for_each(|i| { + x[i] = Some(bx.clone()); + }); + (0..dy).for_each(|i| { + x[i + dx] = Some(by.clone()); + }); + x[dx + dy] = Some(DirButton::A); + [ + Some(Path { + steps: dx + dy + 1, + path: x, + }), + None, + ] } else if end.0 == 0 && start.1 == 3 { - y.append(&x); - y.push(DirButton::A); - [Some(y), None] + let mut y = [None, None, None, None, None, None]; + (0..dx).for_each(|i| { + y[i + dy] = Some(bx.clone()); + }); + (0..dy).for_each(|i| { + y[i] = Some(by.clone()); + }); + y[dx + dy] = Some(DirButton::A); + [ + Some(Path { + steps: dx + dy + 1, + path: y, + }), + None, + ] } else { - let x_copy = x; - x.append(&y); - x.push(DirButton::A); - y.append(&x_copy); - y.push(DirButton::A); - [Some(x), Some(y)] + let mut x = [None, None, None, None, None, None]; + let mut y = [None, None, None, None, None, None]; + (0..dx).for_each(|i| { + x[i] = Some(bx.clone()); + y[i + dy] = Some(bx.clone()); + }); + (0..dy).for_each(|i| { + x[i + dx] = Some(by.clone()); + y[i] = Some(by.clone()); + }); + x[dx + dy] = Some(DirButton::A); + y[dx + dy] = Some(DirButton::A); + [ + Some(Path { + steps: dx + dy + 1, + path: x, + }), + Some(Path { + steps: dx + dy + 1, + path: y, + }), + ] } } } @@ -238,47 +294,22 @@ fn precompute_steps(steps: &mut [DistanceMatrix]) { DirButton::A, ]; - buttons.iter().enumerate().for_each(|(i, start)| { - buttons[i + 1..].iter().for_each(|end| { - let (forward, reversed) = start + buttons.iter().for_each(|start| { + buttons.iter().for_each(|end| { + steps[0][start.index()][end.index()] = start .to(end) - .into_iter() + .iter() .flatten() - .map(|mut path| { - path.steps -= 1; - let mut reversed = path.reversed(); - let mut forward = path; - reversed.push(DirButton::A); - forward.push(DirButton::A); - - ( - forward - .path - .into_iter() - .flatten() - .fold((0, DirButton::A), |acc, next| { - let extension = steps[1][acc.1.index()][next.index()]; - - (acc.0 + extension, next) - }) - .0, - reversed - .path - .into_iter() - .flatten() - .fold((0, DirButton::A), |acc, next| { - let extension = steps[1][acc.1.index()][next.index()]; - - (acc.0 + extension, next) - }) - .0, - ) + .map(|path| { + path.path + .iter() + .flatten() + .fold((0, &DirButton::A), |acc, next| { + (acc.0 + steps[1][acc.1.index()][next.index()], next) + }) + .0 }) - .fold((usize::MAX, usize::MAX), |acc, next| { - (acc.0.min(next.0), acc.1.min(next.1)) - }); - steps[0][start.index()][end.index()] = forward; - steps[0][end.index()][start.index()] = reversed; + .fold(usize::MAX, |acc, next| acc.min(next)); }); }); }