diff --git a/data/examples/18.txt b/data/examples/18.txt new file mode 100644 index 0000000..79c8583 --- /dev/null +++ b/data/examples/18.txt @@ -0,0 +1,25 @@ +5,4 +4,2 +4,5 +3,0 +2,1 +6,3 +2,4 +1,5 +0,6 +3,3 +2,6 +5,1 +1,2 +5,5 +2,5 +6,5 +1,4 +0,4 +6,4 +1,1 +6,1 +1,0 +0,5 +1,6 +2,0 diff --git a/src/bin/18.rs b/src/bin/18.rs new file mode 100644 index 0000000..4662588 --- /dev/null +++ b/src/bin/18.rs @@ -0,0 +1,179 @@ +advent_of_code::solution!(18); + +use std::collections::HashMap; +use std::collections::HashSet; +use std::collections::VecDeque; + + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct Pos { + x: u8, + y: u8, +} + +impl Pos { + fn init(x: u8, y: u8) -> Self { + Self { x, y } + } + fn neighbors(&self, max: &Pos) -> Vec { + let mut res = Vec::new(); + if self.x > 0 { + res.push(Pos { x: self.x - 1, ..self.clone() }); + } + if self.y > 0 { + res.push(Pos { y: self.y - 1, ..self.clone() }); + } + if self.x < max.x { + res.push(Pos { x: self.x + 1, ..self.clone() }); + } + if self.y < max.y { + res.push(Pos { y: self.y + 1, ..self.clone() }); + } + res + } + // fn to_string(&self) -> String { + // format!("{},{}", self.x, self.y) + // } +} + +fn parse_input(input: &str) -> HashMap { + input + .lines() + .enumerate() + .filter_map(|(n, l)| + if l.is_empty() { + None + } else { + let mut v: Vec<_> = l.split(",").map(|n|n.parse::().unwrap()).collect(); + if v.len() < 2 { + return None + } + Some((Pos::init(v.remove(0), v.remove(0)), n)) + }) + .collect() +} + +fn bfs(walls: &HashMap, start: Pos, end: &Pos) -> Option> { + let mut queue: VecDeque = VecDeque::new(); + let mut visited: HashSet = HashSet::new(); + let mut parent_map: HashMap = HashMap::new(); + + let mut blockage_queue: Vec = Vec::new(); + + queue.push_back(start.clone()); + visited.insert(start); + while let Some(current) = queue.pop_front() { + if current == *end { + let mut path = vec![]; + let mut parent = current.clone(); + while let Some(&ref p) = parent_map.get(&parent) { + path.push(p.clone()); + parent = p.clone(); + } + path.reverse(); + return Some(path); + } + for n in current.neighbors(end) { + if visited.contains(&n) { + continue; + } + if walls.contains_key(&n) { + if !blockage_queue.contains(&n) { + blockage_queue.push(n); + } + continue; + } + queue.push_back(n.clone()); + visited.insert(n.clone()); + parent_map.insert(n, current.clone()); + } + } + blockage_queue.sort_by_cached_key(|p| walls.get(p).unwrap()); + + // Sort blockage queue by wall number ?! + for new_start in blockage_queue { + let mut queue: VecDeque = VecDeque::new(); + let mut visited = visited.clone(); + queue.push_back(new_start.clone()); + while let Some(current) = queue.pop_front() { + if current == *end { + println!("Found end by removing {:?}!", new_start); + println!("Wall at time: {}", walls.get(&new_start)?); + break; + } + for n in current.neighbors(end) { + if visited.contains(&n) { + continue; + } + if walls.contains_key(&n) { + continue; + } + queue.push_back(n.clone()); + visited.insert(n.clone()); + } + } + } + + None +} + +fn print_map(size: &Pos, walls: &HashMap, path: &Vec) { + for y in 0..=size.y { + for x in 0..=size.x { + let p = Pos::init(x, y); + if path.contains(&p) { + print!("O"); + } else if walls.contains_key(&p) { + print!("#"); + } else { + print!("."); + } + } + println!(); + } +} + +pub fn part_one(input: &str) -> Option { + let mut coords = parse_input(input); + let (limit, end) = if coords.len() < 100 { // Test case + (12, Pos::init(6, 6)) + } else { + (1024, Pos::init(70, 70)) + }; + coords.retain(|_, v| *v < limit); + + bfs(&coords, Pos::init(0, 0), &end) + .map(|r| r.len()) +} + +pub fn part_two(input: &str) -> Option { + let coords = parse_input(input); + let end = if coords.len() < 100 { + Pos::init(6, 6) + } else { + Pos::init(70, 70) + }; + + print_map(&end, &coords, &vec![]); + + let route = bfs(&coords, Pos::init(0, 0), &end); + println!("{:?}", route); + None +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(22)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some("6,1".into())); + } +}