y2024d21p1 this is hard
This commit is contained in:
parent
985b88f0aa
commit
911a60ab6c
5
y2024/resources/21_input.txt
Normal file
5
y2024/resources/21_input.txt
Normal file
@ -0,0 +1,5 @@
|
||||
340A
|
||||
149A
|
||||
582A
|
||||
780A
|
||||
463A
|
27
y2024/src/bin/d21.rs
Normal file
27
y2024/src/bin/d21.rs
Normal file
@ -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));
|
||||
}
|
523
y2024/src/days/d21.rs
Normal file
523
y2024/src/days/d21.rs
Normal file
@ -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<char> for NumpadState {
|
||||
type Error = Box<dyn Error>;
|
||||
|
||||
fn try_from(value: char) -> Result<Self, Self::Error> {
|
||||
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<dyn Error>;
|
||||
|
||||
fn try_from(value: (usize, usize)) -> Result<Self, Self::Error> {
|
||||
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<NumpadState> 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<char> for KeypadState {
|
||||
type Error = Box<dyn Error>;
|
||||
|
||||
fn try_from(value: char) -> Result<Self, Self::Error> {
|
||||
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<dyn Error>;
|
||||
|
||||
fn try_from(value: (usize, usize)) -> Result<Self, Self::Error> {
|
||||
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<KeypadState> 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 =
|
||||
// "<vA<AA>>^AvAA<^A>A<v<A>>^AvA^A<vA>^A<v<A>^A>AAvA^A<v<A>A>^AAAvA<^A>A";
|
||||
//const INPUT_2_BACK: &str = "<v<A>>^AAAvA^A<vA<AA>>^AvAA<^A>A<v<A>A>^AAAvA<^A>A<vA>^A<A>A";
|
||||
//const INPUT_3_BACK: &str =
|
||||
// "<v<A>>^A<vA<A>>^AAvAA<^A>A<v<A>>^AAvA^A<vA>^AA<A>A<v<A>A>^AAAvA<^A>A";
|
||||
//const INPUT_4_BACK: &str = "<v<A>>^AA<vA<A>>^AAvAA<^A>A<vA>^A<A>A<vA>^A<A>A<v<A>A>^AAvA<^A>A";
|
||||
const INPUT_5_BACK: &str = "<v<A>>^AvA^A<vA<AA>>^AAvA<^A>AAvA^A<vA>^AA<A>A<v<A>A>^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");
|
||||
assert_eq!(keypad_1.sequence, "<Av<AA>>^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);
|
||||
}
|
||||
}
|
@ -29,3 +29,5 @@ pub mod d18;
|
||||
pub mod d19;
|
||||
|
||||
pub mod d20;
|
||||
|
||||
pub mod d21;
|
||||
|
Loading…
Reference in New Issue
Block a user