use std::{ collections::{HashSet, VecDeque}, fmt::Display, }; #[cfg(debug_assertions)] // const PASSCODE: &str = "ihgpwlah"; const PASSCODE: &str = "ulqzkmiv"; #[cfg(not(debug_assertions))] const PASSCODE: &str = "hhhxzeay"; fn main() { let state = State::new(); let result = bfs(state); println!("{result}"); } #[derive(Clone, Debug, PartialEq, Eq, Hash)] struct State { route: Route, position: (usize, usize), } #[derive(Clone, Debug, PartialEq, Eq, Hash)] struct Route(Vec); impl Display for Route { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for dir in &self.0 { let the_char = match dir { Direction::Up => 'U', Direction::Down => 'D', Direction::Left => 'L', Direction::Right => 'R', }; write!(f, "{the_char}")?; } Ok(()) } } impl Route { fn new() -> Self { Self(vec![]) } fn push(&mut self, dir: Direction) { self.0.push(dir); } } #[derive(Clone, Debug, PartialEq, Eq, Hash)] enum Direction { Up, Down, Left, Right, } impl State { fn new() -> Self { Self { route: Route::new(), position: (0, 0), } } fn is_goal_state(&self) -> bool { self.position == (3, 3) } fn neighbor_states(&self, passcode: &str) -> impl Iterator { let input = format!("{}{}", passcode, self.route); let mut hash = format!("{:x}", md5::compute(input)); hash.truncate(4); let possible_directions = self.generate_directions_from_hash(&hash); let mut result = vec![]; for direction in possible_directions { let mut new_state = self.clone(); new_state.update_position(&direction); new_state.route.push(direction); result.push(new_state) } result.into_iter() } fn generate_directions_from_hash(&self, hash: &str) -> impl Iterator { hash.chars() .zip([ Direction::Up, Direction::Down, Direction::Left, Direction::Right, ]) .filter(|(c, _)| ('b'..='f').contains(c)) .map(|(_, dir)| dir) .filter(|dir| match dir { Direction::Up => self.position.1 > 0, Direction::Down => self.position.1 < 3, Direction::Left => self.position.0 > 0, Direction::Right => self.position.0 < 3, }) } fn update_position(&mut self, dir: &Direction) { match *dir { Direction::Up => { self.position.1 -= 1; } Direction::Down => { self.position.1 += 1; } Direction::Left => { self.position.0 -= 1; } Direction::Right => { self.position.0 += 1; } } } } fn bfs(start: State) -> Route { let mut queue = VecDeque::new(); let mut visited = HashSet::new(); visited.insert(start.clone()); queue.push_back(start); while let Some(current_state) = queue.pop_front() { if current_state.is_goal_state() { // return current_state.route; continue; } for neighbor in current_state.neighbor_states(PASSCODE) { println!("{} {}", neighbor.route.0.len(), neighbor.is_goal_state()); if !visited.contains(&neighbor) { visited.insert(neighbor.clone()); queue.push_back(neighbor); } } } panic!("goal state unreachable"); }