From de75a141f707498308c1b7384a39c9eb7cebca6b Mon Sep 17 00:00:00 2001 From: Nekuskus Date: Tue, 10 Dec 2024 17:43:08 +0100 Subject: [PATCH] Solved 10! --- Cargo.toml | 4 ++ inputs | 2 +- src/10.rs | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 src/10.rs diff --git a/Cargo.toml b/Cargo.toml index fde57aa..9fd86a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,3 +49,7 @@ path = "src/08.rs" [[bin]] name = "09" path = "src/09.rs" + +[[bin]] +name = "10" +path = "src/10.rs" diff --git a/inputs b/inputs index 23105d5..5a632ad 160000 --- a/inputs +++ b/inputs @@ -1 +1 @@ -Subproject commit 23105d502be6f0896d6e5277bdfc12a8d75b27c9 +Subproject commit 5a632adc4c9dd0e9bec5f8b2639680eeb28948f5 diff --git a/src/10.rs b/src/10.rs new file mode 100644 index 0000000..8a7b679 --- /dev/null +++ b/src/10.rs @@ -0,0 +1,200 @@ +use debug_print::{debug_print as debug, debug_println as debugln}; +use itertools::Itertools; +use setup_utils::*; +use std::{ + collections::{HashMap, HashSet}, + path::Path, +}; + +// Symbols to replace: 10 36 81 789 SOLVE2 + +#[cfg(test)] +mod tests { + use setup_utils::read_lines; + use std::path::Path; + + #[test] + fn part1() -> Result<(), String> { + let lines = read_lines(Path::new("./inputs/10-example.txt")); + let result = crate::part1(&lines); + if result == 36 { + Ok(()) + } else { + Err(format!( + "10: Bad result for Part 1 example, expected 36 got {}", + result + )) + } + } + + #[test] + fn part2() -> Result<(), String> { + let lines = read_lines(Path::new("./inputs/10-example.txt")); + let result = crate::part2(&lines); + if result == 81 { + Ok(()) + } else { + Err(format!( + "10: Bad result for Part 2 example, expected 81 got {}", + result + )) + } + } + + #[test] + fn full() -> Result<(), String> { + let lines = read_lines(Path::new("./inputs/10-full.txt")); + let result1 = crate::part1(&lines); + //let result2 = crate::part2(&lines); + + if result1 == 789 { + Ok(()) + } else { + Err(format!( + "10: Bad result for Part 1, expected 789 got {}", + result1 + )) + } + /* + match (result1, result2) { + (789, SOLVE2) => Ok(()), + (_, SOLVE2) => Err(format!( + "10: Bad result for Part 1, expected 789 got {}", + result1 + )), + (789, _) => Err(format!( + "10: Bad result for Part 2, expected SOLVE2 got {}", + result2 + )), + (_, _) => Err(format!( + "10: Bad result for Part 1 & 2, expected (789, SOLVE2) got ({}, {})", + result1, result2 + )), + }*/ + } +} + +fn main() { + let linesfull = read_lines(Path::new("./inputs/10-full.txt")); + let lines1 = read_lines(Path::new("./inputs/10-example.txt")); + + println!("10-full.txt"); + println!("{}", part1(&linesfull)); + println!("{}\n", part2(&linesfull)); + + println!("10-1-example.txt"); + println!("{}", part1(&lines1)); + println!("{}", part2(&lines1)); +} + +fn get_neighbors(matrix: &Vec>, p: &Point, step: u32) -> Vec { + let mut ret = vec![]; + + let xlen = matrix[0].len(); + let ylen = matrix.len(); + + if p.x > 0 && matrix[p.y][p.x] + step == matrix[p.y][p.x - 1] { + ret.push(Point::new(p.x - 1, p.y)); + } + + if p.x < xlen - 1 && matrix[p.y][p.x] + step == matrix[p.y][p.x + 1] { + ret.push(Point::new(p.x + 1, p.y)); + } + + if p.y > 0 && matrix[p.y][p.x] + step == matrix[p.y - 1][p.x] { + ret.push(Point::new(p.x, p.y - 1)); + } + + if p.y < ylen - 1 && matrix[p.y][p.x] + step == matrix[p.y + 1][p.x] { + ret.push(Point::new(p.x, p.y + 1)); + } + + ret +} + +fn search_trails(matrix: &Vec>, start: Point, step: u32) -> usize { + let mut endpoints = HashSet::new(); + + let mut queue = vec![(start, 0)]; + + while let Some((p, cost_so_far)) = queue.pop() { + if matrix[p.y][p.x] == 9 { + endpoints.insert(p); + } + + let neighbors = get_neighbors(matrix, &p, step) + .iter() + .map(|&p| (p, cost_so_far + 1)) + .collect_vec(); + + queue.extend(neighbors); + } + + endpoints.len() +} + +fn part1(lines: &Vec) -> usize { + let matrix = lines + .iter() + .map(|l| l.chars().map(|c| c.to_digit(10).unwrap()).collect_vec()) + .collect_vec(); + + let trailheads = matrix + .iter() + .enumerate() + .map(|(y, l)| { + l.iter() + .enumerate() + .filter_map(move |(x, &d)| (d == 0).then_some(Point::new(x, y))) + }) + .flatten(); + + trailheads.map(|p| search_trails(&matrix, p, 1)).sum() +} + +fn search_trails_p2(matrix: &Vec>, start: Point, step: u32) -> usize { + let mut endpoints = HashMap::new(); + + let mut queue = vec![(start, 0)]; + + while let Some((p, cost_so_far)) = queue.pop() { + if matrix[p.y][p.x] == 9 { + match endpoints.get_mut(&p) { + Some(v) => { + *v += 1; + } + None => { + endpoints.insert(p, 1); + } + } + } + + let neighbors = get_neighbors(matrix, &p, step) + .iter() + .map(|&p| (p, cost_so_far + 1)) + .collect_vec(); + + queue.extend(neighbors); + } + + endpoints.values().sum() +} + +fn part2(lines: &Vec) -> usize { + let matrix = lines + .iter() + .map(|l| l.chars().map(|c| c.to_digit(10).unwrap()).collect_vec()) + .collect_vec(); + + let trailheads = matrix + .iter() + .enumerate() + .map(|(y, l)| { + l.iter() + .enumerate() + .filter_map(move |(x, &d)| (d == 0).then_some(Point::new(x, y))) + }) + .flatten(); + + trailheads.map(|p| search_trails_p2(&matrix, p, 1)).sum() +}