diff --git a/2024/days/10/day10/Cargo.toml b/2024/days/10/day10/Cargo.toml new file mode 100644 index 0000000..3c916ef --- /dev/null +++ b/2024/days/10/day10/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day10" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/2024/days/10/day10/src/main.rs b/2024/days/10/day10/src/main.rs new file mode 100644 index 0000000..d6daefe --- /dev/null +++ b/2024/days/10/day10/src/main.rs @@ -0,0 +1,96 @@ +use std::collections::BTreeSet; + +fn parse_map(string: String) -> Vec> { + let mut map: Vec> = Vec::new(); + for line in string.lines() { + let mut map_slice: Vec = Vec::new(); + for letter in line.chars() { + let height: u32 = letter.to_digit(10).unwrap(); + map_slice.push(height); + } + map.push(map_slice); + } + map +} + +fn is_in_bound(x: isize, y: isize, width: usize, height: usize) -> bool { + x >= 0 && y >= 0 && y < height as isize && x < width as isize +} + +fn trailhead_score(start_x: usize, start_y: usize, map: &Vec>) -> u32 { + let mut visited: BTreeSet<(usize, usize)> = BTreeSet::new(); + let mut score: u32 = 0; + let mut queue: Vec<(usize, usize)> = Vec::new(); + queue.push((start_x, start_y)); + let moves: [(isize, isize); 4] = [(-1, 0), (0, -1), (1, 0), (0, 1)]; + while !queue.is_empty() { + if let Some((x, y)) = queue.pop() { + if map[y][x] == 9 { + score += 1; + } + for (dx, dy) in moves { + let new_x = x as isize + dx; + let new_y = y as isize + dy; + if is_in_bound(new_x, new_y, map[0].len(), map.len()) + && map[new_y as usize][new_x as usize] > map[y][x] + && map[new_y as usize][new_x as usize] - map[y][x] == 1 + && !visited.contains(&(new_x as usize, new_y as usize)) { + queue.push((new_x as usize, new_y as usize)); + } + } + visited.insert((x as usize, y as usize)); + } + } + score +} + +fn trailhead_paths(x: usize, y: usize, map: &Vec>) -> u32 { + if map[y][x] == 9 { + return 1; + } else { + let moves: [(isize, isize); 4] = [(-1, 0), (0, -1), (1, 0), (0, 1)]; + let mut paths: u32 = 0; + for (dx, dy) in moves { + let new_x = x as isize + dx; + let new_y = y as isize + dy; + if is_in_bound(new_x, new_y, map[0].len(), map.len()) + && map[new_y as usize][new_x as usize] > map[y][x] + && map[new_y as usize][new_x as usize] - map[y][x] == 1 { + let p = trailhead_paths(new_x as usize, new_y as usize, map); + paths += p; + } + } + return paths; + } +} + +fn find_trailheads(map: &Vec>) -> Vec<(usize, usize)> { + let mut trailheads: Vec<(usize, usize)> = Vec::new(); + for y in 0..map.len() { + for x in 0..map[y].len() { + if map[y][x] == 0 { + trailheads.push((x, y)); + } + } + } + trailheads +} + +fn main() -> std::io::Result<()> { + let input = std::io::read_to_string(std::io::stdin())?; + let map = parse_map(input); + let trailheads = find_trailheads(&map); + // Part 1. + let mut score = 0; + for (start_x, start_y) in &trailheads { + score += trailhead_score(*start_x, *start_y, &map); + } + println!("{}", score); + // Part 2. + let mut paths = 0; + for (start_x, start_y) in &trailheads { + paths += trailhead_paths(*start_x, *start_y, &map); + } + println!("{}", paths); + Ok(()) +}