diff --git a/2023/src/common.rs b/2023/src/common.rs index 8653c2b..1ee763d 100644 --- a/2023/src/common.rs +++ b/2023/src/common.rs @@ -146,11 +146,9 @@ pub fn get_both(slice: &mut [T], first: usize, second: usize) -> (&mut T, &mu } } -#[allow(unused)] #[derive(Debug, Default)] pub struct IndexSet(Vec); -#[allow(unused)] impl IndexSet { pub fn with_capacity(capacity: usize) -> Self { Self(Vec::with_capacity( @@ -278,6 +276,12 @@ impl<'a> Grid<'a> { .chunks_exact(self.width) .map(move |row| &row[..width]) } + + pub fn find(&self, c: u8) -> Option<(usize, usize)> { + let pos = self.data.iter().position(|&d| d == c)?; + + Some((pos % self.width, pos / self.width)) + } } impl<'a> Index for Grid<'a> { diff --git a/2023/src/day10.rs b/2023/src/day10.rs index 7c1760f..faabb87 100644 --- a/2023/src/day10.rs +++ b/2023/src/day10.rs @@ -1,7 +1,100 @@ -pub fn part1(_input: &[u8]) -> anyhow::Result { - anyhow::bail!("Not implemented") +use std::collections::VecDeque; + +use anyhow::Context; + +use crate::common::Grid; +use crate::common::IndexSet; + +const LEFT: u8 = 1; +const RIGHT: u8 = 2; +const UP: u8 = 4; +const DOWN: u8 = 8; + +fn get_connections(c: u8) -> u8 { + match c { + b'|' => UP | DOWN, + b'-' => LEFT | RIGHT, + b'F' => DOWN | RIGHT, + b'J' => LEFT | UP, + b'L' => RIGHT | UP, + b'7' => LEFT | DOWN, + _ => 0, + } +} + +pub fn part1(input: &[u8]) -> anyhow::Result { + let map = Grid::new(input)?; + let (start_x, start_y) = map.find(b'S').context("Couldn't find starting point")?; + let mut visited = IndexSet::with_capacity(map.width() * map.height()); + let mut todo = VecDeque::new(); + + visited.insert(start_y * map.width() + start_x); + + if start_x > 0 && (get_connections(map[start_y][start_x - 1]) & RIGHT) != 0 { + todo.push_back((1, (start_x - 1, start_y))); + visited.insert(start_y * map.width() + start_x - 1); + } + + if start_x + 1 < map.width() && (get_connections(map[start_y][start_x + 1]) & LEFT) != 0 { + todo.push_back((1, (start_x + 1, start_y))); + visited.insert(start_y * map.width() + start_x + 1); + } + + if start_y > 0 && (get_connections(map[start_y - 1][start_x]) & DOWN) != 0 { + todo.push_back((1, (start_x, start_y - 1))); + visited.insert((start_y - 1) * map.width() + start_x); + } + + if start_y + 1 < map.height() && (get_connections(map[start_y + 1][start_x]) & DOWN) != 0 { + todo.push_back((1, (start_x, start_y + 1))); + visited.insert((start_y + 1) * map.width() + start_x); + } + + let mut max_dist = 1; + + while let Some((dist, (x, y))) = todo.pop_front() { + let mut enqueue = |x, y| { + if visited.insert(y * map.width() + x) { + todo.push_back((dist + 1, (x, y))); + // Can elide comparison because we do a BFS and length is strictly increasing + max_dist = dist + 1; + } + }; + + let connections = get_connections(map[y][x]); + + if (connections & LEFT) != 0 { + enqueue(x - 1, y); + } + + if (connections & RIGHT) != 0 { + enqueue(x + 1, y); + } + + if (connections & UP) != 0 { + enqueue(x, y - 1); + } + + if (connections & DOWN) != 0 { + enqueue(x, y + 1); + } + } + + Ok(max_dist.to_string()) } pub fn part2(_input: &[u8]) -> anyhow::Result { anyhow::bail!("Not implemented") } + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE: &[u8] = include_bytes!("samples/10.txt"); + + #[test] + fn sample_part1() { + assert_eq!("8", part1(SAMPLE).unwrap()); + } +} diff --git a/2023/src/samples/10.txt b/2023/src/samples/10.txt new file mode 100644 index 0000000..682499d --- /dev/null +++ b/2023/src/samples/10.txt @@ -0,0 +1,5 @@ +..F7. +.FJ|. +SJ.L7 +|F--J +LJ...