diff --git a/2023/src/day21.rs b/2023/src/day21.rs new file mode 100644 index 0000000..c2b07c4 --- /dev/null +++ b/2023/src/day21.rs @@ -0,0 +1,128 @@ +use aoc_runner_derive::{aoc, aoc_generator}; +use std::collections::{HashMap, VecDeque}; + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +struct Point(usize, usize); + +type Map = [[char; 131]; 131]; + +#[aoc_generator(day21)] +fn parse(input: &str) -> (Point, Map) { + let mut m = [['#'; 131]; 131]; + let mut start: Point = Point(0, 0); + for (x, line) in input.lines().enumerate() { + if line.is_empty() { + continue; + } + for (y, t) in line.chars().enumerate() { + match t { + '.' => m[x][y] = '.', + '#' => {} + 'S' => { + start.0 = x; + start.1 = y; + m[x][y] = '.'; + } + n @ _ => panic!("bad map char {}", n), + } + } + } + (start, m) +} + +fn infinite_step(start: &Point, map: &Map) -> HashMap { + let mut visited: HashMap = HashMap::new(); + let mut frontier: VecDeque<(Point, usize)> = VecDeque::new(); + frontier.push_back((*start, 0)); + + while let Some((cur, dist)) = frontier.pop_front() { + if visited.contains_key(&cur) { + continue; + } + + visited.insert(cur, dist); + + let x = cur.0; + let y = cur.1; + if x > 0 && map[x - 1][y] == '.' { + let p = Point(x - 1, y); + if !visited.contains_key(&p) { + frontier.push_back((p, dist + 1)); + } + } + if x < map.len() - 1 && map[x + 1][y as usize] == '.' { + let p = Point(x + 1, y); + if !visited.contains_key(&p) { + frontier.push_back((p, dist + 1)); + } + } + if y > 0 && map[x][y - 1] == '.' { + let p = Point(x, y - 1); + if !visited.contains_key(&p) { + frontier.push_back((p, dist + 1)); + } + } + if y < map[x].len() - 1 && map[x][y + 1] == '.' { + let p = Point(x, y + 1); + if !visited.contains_key(&p) { + frontier.push_back((p, dist + 1)); + } + } + } + + visited +} + +#[aoc(day21, part1)] +fn part1(input: &(Point, Map)) -> usize { + let visited = infinite_step(&input.0, &input.1); + visited + .values() + .filter(|t| **t <= 64 && **t % 2 == 0) + .count() +} + +#[aoc(day21, part2)] +fn part2(input: &(Point, Map)) -> usize { + let visited = infinite_step(&input.0, &input.1); + + let even_corners = visited + .values() + .filter(|v| **v % 2 == 0 && **v > 65) + .count(); + let odd_corners = visited + .values() + .filter(|v| **v % 2 == 1 && **v > 65) + .count(); + + let even_full = visited.values().filter(|v| **v % 2 == 0).count(); + let odd_full = visited.values().filter(|v| **v % 2 == 1).count(); + + let n = ((26501365 - (input.1.len() / 2)) / input.1.len()) as usize; + assert_eq!(n, 202300); + + ((n + 1) * (n + 1)) * odd_full + (n * n) * even_full - (n + 1) * odd_corners + n * even_corners +} + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE_DATA: &'static str = r#"........... +.....###.#. +.###.##..#. +..#.#...#.. +....#.#.... +.##..S####. +.##..#...#. +.......##.. +.##.#.####. +.##..##.##. +..........."#; + + #[test] + fn sample_data() { + let input = parse(&SAMPLE_DATA); + assert_eq!(part1(&input), 42); + } +} diff --git a/2023/src/lib.rs b/2023/src/lib.rs index 1add4d4..492c1a0 100644 --- a/2023/src/lib.rs +++ b/2023/src/lib.rs @@ -20,5 +20,6 @@ mod day17; mod day18; mod day19; mod day20; +mod day21; aoc_lib! { year = 2023 }