diff --git a/data/examples/18.txt b/data/examples/18.txt new file mode 100644 index 0000000..0371b23 --- /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 \ No newline at end of file diff --git a/src/bin/06.rs b/src/bin/06.rs index d479bcb..81306a2 100644 --- a/src/bin/06.rs +++ b/src/bin/06.rs @@ -88,7 +88,7 @@ fn simulate( } pub fn part_one(input: &str) -> Option { - let map = CoordMap::new(input); + let map = CoordMap::new_from_map(input); let binding = map.find_char('^'); let start_pos = binding.get(0).unwrap(); @@ -105,7 +105,7 @@ pub fn part_one(input: &str) -> Option { } pub fn part_two(input: &str) -> Option { - let map = CoordMap::new(input); + let map = CoordMap::new_from_map(input); let binding = map.find_char('^'); let start_pos = binding.get(0).unwrap(); diff --git a/src/bin/08.rs b/src/bin/08.rs index 4a2c28c..33e27f0 100644 --- a/src/bin/08.rs +++ b/src/bin/08.rs @@ -5,7 +5,7 @@ use advent_of_code::{CoordMap, Coords}; advent_of_code::solution!(8); pub fn part_one(input: &str) -> Option { - let map = CoordMap::new(input); + let map = CoordMap::new_from_map(input); let antennas: Vec<(&Coords, &char)> = map.iter().filter(|(_, v)| **v != '.').collect(); @@ -56,8 +56,8 @@ pub fn part_one(input: &str) -> Option { } pub fn part_two(input: &str) -> Option { - let map = CoordMap::new(input); - let mut vizmap = CoordMap::new(input); + let map = CoordMap::new_from_map(input); + let mut vizmap = CoordMap::new_from_map(input); let antennas: Vec<(&Coords, &char)> = map.iter().filter(|(_, v)| **v != '.').collect(); diff --git a/src/bin/10.rs b/src/bin/10.rs index 5eba7c5..d6c1fae 100644 --- a/src/bin/10.rs +++ b/src/bin/10.rs @@ -13,7 +13,7 @@ fn is_next_number(a: &char, b: &char) -> bool { } pub fn part_one(input: &str) -> Option { - let map = CoordMap::new(input); + let map = CoordMap::new_from_map(input); let count: usize = map .find_char('0') @@ -60,7 +60,7 @@ struct PathBuilder { } pub fn part_two(input: &str) -> Option { - let map = CoordMap::new(input); + let map = CoordMap::new_from_map(input); let count: usize = map .find_char('0') diff --git a/src/bin/12.rs b/src/bin/12.rs index 51a146c..89c3893 100644 --- a/src/bin/12.rs +++ b/src/bin/12.rs @@ -67,7 +67,7 @@ fn find_zones(map: &CoordMap) -> Vec { } pub fn part_one(input: &str) -> Option { - let map = CoordMap::new(input); + let map = CoordMap::new_from_map(input); let zones = find_zones(&map); let mut acc: u32 = 0; @@ -188,7 +188,7 @@ fn sort_borders_into_sides( } pub fn part_two(input: &str) -> Option { - let map = CoordMap::new(input); + let map = CoordMap::new_from_map(input); let zones = find_zones(&map); let mut acc: u32 = 0; diff --git a/src/bin/14.rs b/src/bin/14.rs index db12119..066a4ea 100644 --- a/src/bin/14.rs +++ b/src/bin/14.rs @@ -124,7 +124,7 @@ pub fn part_two(input: &str) -> Option { let percent_symm = x_symm_coeff as f64 / (robots.len() as f64); if percent_symm > 0.2 { - let mut map = CoordMap::new(""); + let mut map = CoordMap::new_from_map(""); for r in moved { map.set(&Coords { x: r.x, y: r.y }, 'R'); diff --git a/src/bin/15.rs b/src/bin/15.rs index d05d54f..2b721ca 100644 --- a/src/bin/15.rs +++ b/src/bin/15.rs @@ -15,7 +15,7 @@ enum Move { fn create_map(input: &str) -> (CoordMap, Vec) { let spl: Vec<&str> = input.split("\n\n").collect(); - let map = CoordMap::new(&spl[0]); + let map = CoordMap::new_from_map(&spl[0]); ( map, @@ -200,7 +200,7 @@ pub fn part_two(input: &str) -> Option { let (mut map, commands) = create_map(&inp); - map.viz(&'.'); + map.viz('.'); for mv in commands { let cloned_map = map.clone(); diff --git a/src/bin/16.rs b/src/bin/16.rs index 41c068d..cd06624 100644 --- a/src/bin/16.rs +++ b/src/bin/16.rs @@ -59,7 +59,7 @@ struct Position { } fn solver(input: &str, store_visited: bool) -> (u32, Vec) { - let map = CoordMap::new(input); + let map = CoordMap::new_from_map(input); let start = map.find_char('S'); diff --git a/src/bin/18.rs b/src/bin/18.rs new file mode 100644 index 0000000..b76975a --- /dev/null +++ b/src/bin/18.rs @@ -0,0 +1,91 @@ +use advent_of_code::{extract_numbers, CoordMap, Coords}; + +advent_of_code::solution!(18); + +pub fn part_one(input: &str) -> Option { + //let (max, b) = (6, 12); + let (max, b) = (70, 1024); + + let mut map = CoordMap::new_max(max, max); + for (i, line) in input.lines().enumerate() { + if i >= b { + break; + } + let c = extract_numbers(line); + + map.set( + &Coords { + x: c[0] as i32, + y: c[1] as i32, + }, + '#', + ); + } + + map.shortest_steps(&Coords { x: 0, y: 0 }, &Coords { x: max, y: max }, |c| { + c == None + }) +} + +pub fn part_two(input: &str) -> Option { + //let (max, _) = (6, 12); + let (max, _) = (70, 1024); + + let lines: Vec<&str> = input.lines().collect(); + let ll = lines.len(); + + let mut range = (0, ll); + + while range.1 - range.0 > 1 { + let mid = (range.1 - range.0) / 2; + + let mut map = CoordMap::new_max(max, max); + for (i, line) in input.lines().enumerate() { + if i >= range.0 + mid { + break; + } + let c = extract_numbers(line); + + map.set( + &Coords { + x: c[0] as i32, + y: c[1] as i32, + }, + '#', + ); + } + + map.viz('.'); + + let v = map.shortest_steps(&Coords { x: 0, y: 0 }, &Coords { x: max, y: max }, |c| { + c == None + }); + + if v.is_none() { + range = (range.0, mid + range.0); + } else { + range = (mid + range.0, range.1); + } + } + + let a = lines[range.0]; + + Some(a.to_string()) +} + +#[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".to_owned())); + } +} diff --git a/src/lib.rs b/src/lib.rs index 56692f2..9600fdb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,23 @@ pub mod template; // Use this file to add helper functions and additional modules. +fn divide_range(start: i64, end: i64, n: i64) -> Vec<(i64, i64)> { + if n <= 0 { + return vec![]; + } + + let range_size = (end - start) as f64; + let chunk_size = (range_size / n as f64).ceil() as i64; + + (0..n) + .map(|i| { + let chunk_start = start + (i * chunk_size); + let chunk_end = (start + ((i + 1) * chunk_size)).min(end); + (chunk_start, chunk_end) + }) + .collect() +} + pub fn extract_numbers(text: &str) -> Vec { let re = Regex::new(r"-?\d+(?:\.\d+)?").unwrap(); @@ -108,7 +125,23 @@ pub struct CoordMap { } impl CoordMap { - pub fn new(input: &str) -> CoordMap { + pub fn new_max(x_max: i32, y_max: i32) -> CoordMap { + return CoordMap { + map: HashMap::new(), + y_len: y_max + 1, + x_len: x_max + 1, + }; + } + + pub fn new_len(x_len: i32, y_len: i32) -> CoordMap { + return CoordMap { + map: HashMap::new(), + y_len: y_len, + x_len: x_len, + }; + } + + pub fn new_from_map(input: &str) -> CoordMap { let lines: Vec<&str> = input.lines().collect(); let mut c = CoordMap { map: HashMap::new(), @@ -131,6 +164,7 @@ impl CoordMap { } return c; } + pub fn set(&mut self, k: &Coords, v: char) { self.map.insert(k.clone(), v); } @@ -181,8 +215,8 @@ impl CoordMap { ); } - pub fn viz(&self, empty: &char) { - print!("{}", self.viz_to_string(empty)); + pub fn viz(&self, empty: char) { + print!("{}", self.viz_to_string(&empty)); } pub fn viz_to_string(&self, empty: &char) -> String { @@ -222,4 +256,57 @@ impl CoordMap { pub fn coord_exists(&self, c: &Coords) -> bool { self.get(c).is_some() } + + pub fn shortest_steps( + &self, + from: &Coords, + to: &Coords, + can_step_on: fn(Option<&char>) -> bool, + ) -> Option { + let mut best_steps_to_pos: HashMap = HashMap::new(); + + let mut q = vec![(from.clone(), 0)]; + + let mut min_to_end = 100000000; + + while q.len() > 0 { + let (position, steps) = q.pop().unwrap(); + + let best = best_steps_to_pos.get(&position); + + if best.is_some() && steps >= *best.unwrap() { + continue; + } else { + best_steps_to_pos.insert(position.clone(), steps); + } + + if position == *to { + continue; + } + + let adj = self.get_adjacent_xy(&position); + let possible: Vec<&Coords> = adj + .iter() + .filter(|c| { + if c.x < 0 || c.y < 0 || c.x >= self.x_len || c.y >= self.y_len { + return false; + } + + let best = best_steps_to_pos.get(c); + + if best.is_some() && steps + 1 >= *best.unwrap() { + return false; + } + let letter = self.get(c); + return can_step_on(letter); + }) + .collect(); + + for item in possible { + q.push((item.clone(), steps + 1)); + } + } + + best_steps_to_pos.get(to).copied() + } }