diff --git a/samples/day18.txt b/samples/day18.txt new file mode 120000 index 0000000..f43336b --- /dev/null +++ b/samples/day18.txt @@ -0,0 +1 @@ +day18_1.txt \ No newline at end of file diff --git a/samples/day18_1.txt b/samples/day18_1.txt new file mode 100644 index 0000000..fc7612e --- /dev/null +++ b/samples/day18_1.txt @@ -0,0 +1,14 @@ +R 6 (#70c710) +D 5 (#0dc571) +L 2 (#5713f0) +D 2 (#d2c081) +R 2 (#59c680) +D 2 (#411b91) +L 5 (#8ceee2) +U 2 (#caa173) +L 1 (#1b58a2) +U 2 (#caa171) +R 2 (#7807d2) +U 3 (#a77fa3) +L 2 (#015232) +U 2 (#7a21e3) diff --git a/samples/day18_2.txt b/samples/day18_2.txt new file mode 100644 index 0000000..3b8bac7 --- /dev/null +++ b/samples/day18_2.txt @@ -0,0 +1,6 @@ +R 3 (#70c710) +D 1 (#0dc571) +R 3 (#5713f0) +U 1 (#d2c081) +R 3 (#59c680) +D 2 (#411b91) diff --git a/src/bin/day18.rs b/src/bin/day18.rs new file mode 100644 index 0000000..8914973 --- /dev/null +++ b/src/bin/day18.rs @@ -0,0 +1,118 @@ +use std::{collections::VecDeque, str::FromStr}; + +use aoc_2023::*; +use itertools::iproduct; + +type Output1 = usize; +type Output2 = Output1; + +struct Step { + direction: Vector2D, + count: isize, + color: String, +} + +impl FromStr for Step { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + let mut parts = s.split_ascii_whitespace(); + + let direction = match parts.next().unwrap() { + "U" => Vector2D::new(0, -1), + "D" => Vector2D::new(0, 1), + "L" => Vector2D::new(-1, 0), + "R" => Vector2D::new(1, 0), + _ => unreachable!(), + }; + + let count = parts.next().unwrap().parse().unwrap(); + let color = parts + .next() + .unwrap() + .trim_matches(|c| c == '(' || c == ')') + .to_owned(); + + Ok(Step { + direction, + count, + color, + }) + } +} + +fn flood_fill(m: &mut [Vec], start: Vector2D, original: char, fill: char) -> usize { + let mut filled_in = 0; + + let mut q: VecDeque> = VecDeque::from([start]); + m[start] = fill; + filled_in += 1; + + while let Some(p) = q.pop_front() { + for (dx, dy) in [(0, 1), (0, -1), (1, 0), (-1, 0)] { + let neighbour = p + Vector2D::new(dx, dy); + if !in_range(m, &neighbour) || m[neighbour] != original { + continue; + } + + q.push_back(neighbour); + m[neighbour] = fill; + filled_in += 1; + } + } + + filled_in +} + +struct Day18 { + plan: Vec, +} +impl Solution for Day18 { + fn new>(pathname: P) -> Self { + Self { + plan: file_to_structs(pathname), + } + } + + fn part_1(&mut self) -> Output1 { + let n = self.plan.iter().map(|s| s.count).sum::(); + + let mut map = vec![vec!['.'; 2 * n as usize]; 2 * n as usize]; + let mut position = Vector2D::new(n, n); + + for s in &self.plan { + for _ in 0..s.count { + map[position] = '#'; + position = position + s.direction; + } + } + + flood_fill(&mut map, Vector2D::new(0, 0), '.', '+'); + + while let Some((y, x)) = + iproduct!(0..2 * n as usize, 0..2 * n as usize).find(|&(y, x)| map[y][x] == '.') + { + flood_fill(&mut map, Vector2D::new(x as isize, y as isize), '.', '#'); + } + + // debug!( + // "Map:\n{}", + // map.iter().map(|r| r.iter().collect::()).join("\n") + // ); + + map.iter() + .map(|r| r.iter().filter(|&&c| c == '#').count()) + .sum() + } + + fn part_2(&mut self) -> Output2 { + todo!() + } +} + +fn main() -> Result<()> { + // Day18::run("sample") + Day18::main() +} + +test_sample!(day_18, Day18, 62, 952408144115);