diff --git a/y2024/resources/21_input.txt b/y2024/resources/21_input.txt new file mode 100644 index 0000000..dddcb20 --- /dev/null +++ b/y2024/resources/21_input.txt @@ -0,0 +1,5 @@ +340A +149A +582A +780A +463A diff --git a/y2024/src/bin/d21.rs b/y2024/src/bin/d21.rs new file mode 100644 index 0000000..fe9a2c1 --- /dev/null +++ b/y2024/src/bin/d21.rs @@ -0,0 +1,27 @@ +use std::{fs, time::Instant}; + +use utils::time::get_elapsed_string; +use y2024::days::d21; + +fn main() { + let now = Instant::now(); + println!("Part 1:"); + part1(); + println!("Ran in {}", get_elapsed_string(now.elapsed())); + let now = Instant::now(); + println!("Part 2:"); + part2(); + println!("Ran in {}", get_elapsed_string(now.elapsed())); +} + +fn part1() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/21_input.txt")).unwrap(); + println!("{}", d21::process_part1(&content)); +} + +fn part2() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/21_input.txt")).unwrap(); + println!("{}", d21::process_part2(&content)); +} diff --git a/y2024/src/days/d21.rs b/y2024/src/days/d21.rs new file mode 100644 index 0000000..a2baabe --- /dev/null +++ b/y2024/src/days/d21.rs @@ -0,0 +1,523 @@ +use core::panic; +use std::{char, error::Error}; + +use itertools::Itertools; + +pub fn process_part1(input: &str) -> usize { + input + .lines() + .map(|line| { + let numpad = Numpad::from(line); + println!("first robot {}", numpad.sequence); + let _ = Numpad::revert_from(&numpad.sequence); + let keypad_1 = Keypad::from(numpad.sequence.as_str()); + println!("second robot {}", keypad_1.sequence); + let _ = Keypad::revert_from(&keypad_1.sequence); + let keypad_2 = Keypad::from(keypad_1.sequence.as_str()); + println!("me {}", keypad_2.sequence); + let _ = Keypad::revert_from(&keypad_2.sequence); + keypad_2.sequence.len() * numeric_part(line) + }) + .sum() +} + +pub fn process_part2(input: &str) -> usize { + 0 +} + +fn numeric_part(code: &str) -> usize { + let code = &code[..code.len() - 1]; + code.parse().unwrap() +} + +#[derive(Debug, Clone, Copy)] +enum NumpadState { + Seven, + Eight, + Nine, + Four, + Five, + Six, + One, + Two, + Three, + Zero, + A, +} + +impl NumpadState { + fn get_col(&self) -> i32 { + match self { + NumpadState::Seven | NumpadState::Four | NumpadState::One => 0, + NumpadState::Eight | NumpadState::Five | NumpadState::Two | NumpadState::Zero => 1, + NumpadState::Nine | NumpadState::Six | NumpadState::Three | NumpadState::A => 2, + } + } + + fn get_row(&self) -> i32 { + match self { + NumpadState::Seven | NumpadState::Eight | NumpadState::Nine => 0, + NumpadState::Four | NumpadState::Five | NumpadState::Six => 1, + NumpadState::One | NumpadState::Two | NumpadState::Three => 2, + NumpadState::Zero | NumpadState::A => 3, + } + } + + fn get_sequence(&self, goto: NumpadState) -> String { + let row = self.get_row(); + let col = self.get_col(); + let goto_row = goto.get_row(); + let goto_col = goto.get_col(); + if row == goto_row && col == goto_col { + return "A".to_string(); + } + let row_diff = row.abs_diff(goto_row); + let col_diff = col.abs_diff(goto_col); + let mut sequence = String::new(); + let row_seq = match goto_row.cmp(&row) { + std::cmp::Ordering::Less => vec!["^"; row_diff as usize].join(""), + std::cmp::Ordering::Equal => "".to_string(), + std::cmp::Ordering::Greater => vec!["v"; row_diff as usize].join(""), + }; + let col_seq = match goto_col.cmp(&col) { + std::cmp::Ordering::Less => vec!["<"; col_diff as usize].join(""), + std::cmp::Ordering::Equal => "".to_string(), + std::cmp::Ordering::Greater => vec![">"; col_diff as usize].join(""), + }; + if (goto_col < col && row < 3) || col == 0 { + sequence.push_str(&col_seq); + sequence.push_str(&row_seq); + } else { + sequence.push_str(&row_seq); + sequence.push_str(&col_seq); + } + sequence.push('A'); + sequence + } +} + +impl TryFrom for NumpadState { + type Error = Box; + + fn try_from(value: char) -> Result { + match value { + 'A' => Ok(NumpadState::A), + '0' => Ok(NumpadState::Zero), + '1' => Ok(NumpadState::One), + '2' => Ok(NumpadState::Two), + '3' => Ok(NumpadState::Three), + '4' => Ok(NumpadState::Four), + '5' => Ok(NumpadState::Five), + '6' => Ok(NumpadState::Six), + '7' => Ok(NumpadState::Seven), + '8' => Ok(NumpadState::Eight), + '9' => Ok(NumpadState::Nine), + _ => Err(Box::from("Unrecognised keystate")), + } + } +} + +impl TryFrom<(usize, usize)> for NumpadState { + type Error = Box; + + fn try_from(value: (usize, usize)) -> Result { + match value { + (2, 3) => Ok(NumpadState::A), + (1, 3) => Ok(NumpadState::Zero), + (0, 2) => Ok(NumpadState::One), + (1, 2) => Ok(NumpadState::Two), + (2, 2) => Ok(NumpadState::Three), + (0, 1) => Ok(NumpadState::Four), + (1, 1) => Ok(NumpadState::Five), + (2, 1) => Ok(NumpadState::Six), + (0, 0) => Ok(NumpadState::Seven), + (1, 0) => Ok(NumpadState::Eight), + (2, 0) => Ok(NumpadState::Nine), + (0, 3) => Err(Box::from("Robot arm hovering over numpad gap")), + (x, y) => { + let err = format!("Unrecognised numpad key coord ({x}, {y})"); + Err(err.into()) + } + } + } +} + +impl From for char { + fn from(value: NumpadState) -> Self { + match value { + NumpadState::Seven => '7', + NumpadState::Eight => '8', + NumpadState::Nine => '9', + NumpadState::Four => '4', + NumpadState::Five => '5', + NumpadState::Six => '6', + NumpadState::One => '1', + NumpadState::Two => '2', + NumpadState::Three => '3', + NumpadState::Zero => '0', + NumpadState::A => 'A', + } + } +} + +struct Numpad { + state: NumpadState, + sequence: String, +} + +impl Numpad { + fn press_key(&mut self, goto: NumpadState) { + let sequence = self.state.get_sequence(goto); + self.state = goto; + self.sequence.push_str(&sequence); + } + + #[allow(dead_code)] + fn revert_from(sequence: &str) -> String { + let mut state = NumpadState::A; + let mut col_idx = 2; + let mut row_idx = 3; + let mut reverted = String::new(); + for movements in sequence.split_inclusive("A") { + for movement in movements.chars() { + match movement { + '<' => { + col_idx -= 1; + state = NumpadState::try_from((col_idx, row_idx)).unwrap() + } + '>' => { + col_idx += 1; + state = NumpadState::try_from((col_idx, row_idx)).unwrap() + } + '^' => { + row_idx -= 1; + state = NumpadState::try_from((col_idx, row_idx)).unwrap() + } + 'v' => { + row_idx += 1; + state = NumpadState::try_from((col_idx, row_idx)).unwrap() + } + 'A' => { + reverted.push(state.into()); + } + _ => panic!("Impossible"), + } + } + } + reverted + } +} + +impl From<&str> for Numpad { + fn from(value: &str) -> Self { + let mut numpad = Numpad { + state: NumpadState::A, + sequence: String::new(), + }; + let keys = value + .chars() + .map(|numkey| NumpadState::try_from(numkey).unwrap()) + .collect_vec(); + for key in keys { + numpad.press_key(key); + } + numpad + } +} + +#[derive(Debug, Clone, Copy)] +enum KeypadState { + Up, + A, + Left, + Down, + Right, +} + +impl KeypadState { + fn get_col(&self) -> i32 { + match self { + KeypadState::Left => 0, + KeypadState::Up | KeypadState::Down => 1, + KeypadState::A | KeypadState::Right => 2, + } + } + + fn get_row(&self) -> i32 { + match self { + KeypadState::Up | KeypadState::A => 0, + KeypadState::Left | KeypadState::Down | KeypadState::Right => 1, + } + } + + fn get_sequence(&self, goto: KeypadState) -> String { + let row = self.get_row(); + let col = self.get_col(); + let goto_row = goto.get_row(); + let goto_col = goto.get_col(); + if row == goto_row && col == goto_col { + return "A".to_string(); + } + let row_diff = row.abs_diff(goto_row); + let col_diff = col.abs_diff(goto_col); + let mut sequence = String::new(); + let row_seq = match goto_row.cmp(&row) { + std::cmp::Ordering::Less => vec!["^"; row_diff as usize].join(""), + std::cmp::Ordering::Equal => "".to_string(), + std::cmp::Ordering::Greater => vec!["v"; row_diff as usize].join(""), + }; + let col_seq = match goto_col.cmp(&col) { + std::cmp::Ordering::Less => vec!["<"; col_diff as usize].join(""), + std::cmp::Ordering::Equal => "".to_string(), + std::cmp::Ordering::Greater => vec![">"; col_diff as usize].join(""), + }; + if (goto_col < col && row == 1) || col == 0 { + sequence.push_str(&col_seq); + sequence.push_str(&row_seq); + } else { + sequence.push_str(&row_seq); + sequence.push_str(&col_seq); + } + sequence.push('A'); + sequence + } +} + +impl TryFrom for KeypadState { + type Error = Box; + + fn try_from(value: char) -> Result { + match value { + 'A' => Ok(KeypadState::A), + '<' => Ok(KeypadState::Left), + '>' => Ok(KeypadState::Right), + 'v' => Ok(KeypadState::Down), + '^' => Ok(KeypadState::Up), + _ => Err(Box::from("Unrecognised keystate")), + } + } +} + +impl TryFrom<(usize, usize)> for KeypadState { + type Error = Box; + + fn try_from(value: (usize, usize)) -> Result { + match value { + (2, 0) => Ok(KeypadState::A), + (0, 1) => Ok(KeypadState::Left), + (2, 1) => Ok(KeypadState::Right), + (1, 1) => Ok(KeypadState::Down), + (1, 0) => Ok(KeypadState::Up), + (0, 0) => Err(Box::from("Robot arm hovering over keypad gap")), + (x, y) => { + let err = format!("Unrecognised key coord ({x}, {y})"); + Err(err.into()) + } + } + } +} + +impl From for char { + fn from(value: KeypadState) -> Self { + match value { + KeypadState::A => 'A', + KeypadState::Up => '^', + KeypadState::Left => '<', + KeypadState::Down => 'v', + KeypadState::Right => '>', + } + } +} + +struct Keypad { + state: KeypadState, + sequence: String, +} + +impl Keypad { + fn press_key(&mut self, goto: KeypadState) { + let sequence = self.state.get_sequence(goto); + self.state = goto; + self.sequence.push_str(&sequence); + } + + #[allow(dead_code)] + fn revert_from(sequence: &str) -> String { + let mut state = KeypadState::A; + let mut col_idx = 2; + let mut row_idx = 0; + let mut reverted = String::new(); + for movements in sequence.split_inclusive("A") { + for movement in movements.chars() { + match movement { + '<' => { + col_idx -= 1; + state = KeypadState::try_from((col_idx, row_idx)).unwrap() + } + '>' => { + col_idx += 1; + state = KeypadState::try_from((col_idx, row_idx)).unwrap() + } + '^' => { + row_idx -= 1; + state = KeypadState::try_from((col_idx, row_idx)).unwrap() + } + 'v' => { + row_idx += 1; + state = KeypadState::try_from((col_idx, row_idx)).unwrap() + } + 'A' => { + reverted.push(state.into()); + } + _ => panic!("Impossible"), + } + } + } + reverted + } +} + +impl From<&str> for Keypad { + fn from(value: &str) -> Self { + let mut numpad = Keypad { + state: KeypadState::A, + sequence: String::new(), + }; + let keys = value + .chars() + .map(|numkey| KeypadState::try_from(numkey).unwrap()) + .collect_vec(); + for key in keys { + numpad.press_key(key); + } + numpad + } +} + +#[cfg(test)] +mod tests { + use rayon::result; + + use super::*; + + const INPUT_1: &str = "029A"; + const INPUT_2: &str = "980A"; + const INPUT_3: &str = "179A"; + const INPUT_4: &str = "456A"; + const INPUT_5: &str = "379A"; + + //const INPUT_1_BACK: &str = + // ">^AvAA<^A>A>^AvA^A^A^A>AAvA^AA>^AAAvA<^A>A"; + //const INPUT_2_BACK: &str = ">^AAAvA^A>^AvAA<^A>AA>^AAAvA<^A>A^AA"; + //const INPUT_3_BACK: &str = + // ">^A>^AAvAA<^A>A>^AAvA^A^AAAA>^AAAvA<^A>A"; + //const INPUT_4_BACK: &str = ">^AA>^AAvAA<^A>A^AA^AAA>^AAvA<^A>A"; + const INPUT_5_BACK: &str = ">^AvA^A>^AAvA<^A>AAvA^A^AAAA>^AAAvA<^A>A"; + + #[test] + fn part1_full() { + let result = process_part1(&[INPUT_1, INPUT_2, INPUT_3, INPUT_4, INPUT_5].join("\n")); + assert_eq!(result, 126384); + } + + #[test] + fn part1_1() { + let result = process_part1(INPUT_1); + assert_eq!(result, 29 * 68); + } + + #[test] + fn part1_2() { + let result = process_part1(INPUT_2); + assert_eq!(result, 60 * 980); + } + + #[test] + fn part1_3() { + let result = process_part1(INPUT_3); + assert_eq!(result, 68 * 179); + } + + #[test] + fn part1_4() { + let result = process_part1(INPUT_4); + assert_eq!(result, 64 * 456); + } + + #[test] + fn part1_5() { + let result = process_part1(INPUT_5); + assert_eq!(result, 64 * 379); + } + + #[test] + fn part1_edge() { + let code = "1"; + let numpad = Numpad::from(code); + let keypad_1 = Keypad::from(numpad.sequence.as_str()); + assert_eq!(numpad.sequence, "^<>^A"); + } + + //#[test] + //fn part1_1_back() { + // let result = Keypad::revert_from(INPUT_1_BACK); + // let result = Keypad::revert_from(&result); + // let result = Numpad::revert_from(&result); + // assert_eq!(result, "029A"); + //} + + //#[test] + //fn part1_2_back() { + // let result = Keypad::revert_from(INPUT_2_BACK); + // let result = Keypad::revert_from(&result); + // let result = Numpad::revert_from(&result); + // assert_eq!(result, "980A"); + //} + + //#[test] + //fn part1_3_back() { + // let result = Keypad::revert_from(INPUT_3_BACK); + // let result = Keypad::revert_from(&result); + // let result = Numpad::revert_from(&result); + // assert_eq!(result, "179A"); + //} + + //#[test] + //fn part1_4_back() { + // let result = Keypad::revert_from(INPUT_4_BACK); + // let result = Keypad::revert_from(&result); + // let result = Numpad::revert_from(&result); + // assert_eq!(result, "456A"); + //} + + #[test] + fn part1_5_back() { + let result = Keypad::revert_from(INPUT_5_BACK); + println!("back 1 {result}"); + let result = Keypad::revert_from(&result); + println!("back 2 {result}"); + let result = Numpad::revert_from(&result); + assert_eq!(result, "379A"); + } + + //#[test] + //fn part1_5_backnforth() { + // let numpad = Numpad::from(INPUT_5); + // let keypad_1 = Keypad::from(numpad.sequence.as_str()); + // let keypad_2 = Keypad::from(keypad_1.sequence.as_str()); + // let keypad_1_back = Keypad::revert_from(INPUT_5_BACK); + // let numpad_back = Keypad::revert_from(&keypad_1_back); + // let code = Numpad::revert_from(&numpad_back); + // //assert_eq!(code, INPUT_5); + // assert_eq!(numpad_back, numpad.sequence); + // //assert_eq!(keypad_1_back, keypad_1.sequence); + // //assert_eq!(INPUT_5_BACK, keypad_2.sequence); + //} + + #[test] + fn part2() { + let result = process_part2(INPUT_1); + assert_eq!(result, 0); + } +} diff --git a/y2024/src/days/mod.rs b/y2024/src/days/mod.rs index bf81c6c..4dcf084 100644 --- a/y2024/src/days/mod.rs +++ b/y2024/src/days/mod.rs @@ -29,3 +29,5 @@ pub mod d18; pub mod d19; pub mod d20; + +pub mod d21;