diff --git a/day20/Cargo.toml b/day20/Cargo.toml new file mode 100644 index 0000000..156d38d --- /dev/null +++ b/day20/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day20" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/day20/src/main.rs b/day20/src/main.rs new file mode 100644 index 0000000..1ff3881 --- /dev/null +++ b/day20/src/main.rs @@ -0,0 +1,125 @@ +use std::collections::HashSet; +use std::{env, fs}; + +fn read_input(path: &str) -> String { + fs::read_to_string(path).expect("Cannot read file.") +} + +fn parse_input(input: &str) -> (Vec>, (usize, usize), (usize, usize)) { + let mut map = Vec::new(); + let (mut start, mut end) = ((0, 0), (0, 0)); + + for (row, line) in input.lines().filter(|s| !s.is_empty()).enumerate() { + map.push(Vec::new()); + for (col, char) in line.chars().enumerate() { + match char { + '#' => map[row].push(-1), + '.' => map[row].push(0), + 'E' => { + map[row].push(0); + end = (row, col); + } + 'S' => { + map[row].push(0); + start = (row, col); + } + _ => unreachable!(), + } + } + } + + (map, start, end) +} + +fn get_if_in_map( + map: &Vec>, + row: usize, + offset_r: i32, + col: usize, + offset_c: i32, +) -> Option<(usize, usize)> { + let (row, col) = (row as i32 + offset_r, col as i32 + offset_c); + if !(0..map.len() as i32).contains(&row) || !(0..map[row as usize].len() as i32).contains(&col) + { + None + } else { + Some((row as usize, col as usize)) + } +} + +fn race_without_cheating( + map: &mut Vec>, + start: &(usize, usize), + end: &(usize, usize), +) -> HashSet<(usize, usize)> { + let mut positions = HashSet::new(); + positions.insert(*start); + + let mut position = *start; + let mut cost = 0; + while position != *end { + for dir in [(0, 1), (-1, 0), (1, 0), (0, -1)] { + if let Some((row, col)) = get_if_in_map(map, position.0, dir.0, position.1, dir.1) { + if map[row][col] == 0 && (row, col) != *start { + (position, cost) = ((row, col), cost + 1); + map[row][col] = cost; + positions.insert(position); + break; + } + } + } + } + + positions +} + +fn get_cheating_shortcuts( + map: &Vec>, + path: &HashSet<(usize, usize)>, + radius: i32, +) -> usize { + let mut savings = 0; + for &position in path { + let source = map[position.0][position.1]; + for i in -radius..=radius { + for j in -radius..=radius { + let distance = i.abs() + j.abs(); + if distance > radius { + continue; + } + + if let Some((row, col)) = get_if_in_map(map, position.0, i, position.1, j) { + let destination = map[row][col]; + + if source >= destination || destination == -1 { + continue; + } + + let saving = destination - source - distance; + if distance <= radius && saving >= 100 { + savings += 1; + } + } + } + } + } + savings +} + +fn main() { + let args: Vec = env::args().collect(); + for arg in args.iter().skip(1) { + let input = read_input(&arg); + let (mut map, start, end) = parse_input(&input); + let race = race_without_cheating(&mut map, &start, &end); + println!("[{}]", &arg); + println!( + "\t[Part 1] => Answer is '{}'.", + get_cheating_shortcuts(&map, &race, 2) + ); + println!( + "\t[Part 2] => Answer is '{}'.", + get_cheating_shortcuts(&map, &race, 20) + ); + } +}