diff --git a/y2024/src/days/d20.rs b/y2024/src/days/d20.rs index a47e149..fa0101b 100644 --- a/y2024/src/days/d20.rs +++ b/y2024/src/days/d20.rs @@ -1,14 +1,12 @@ use std::{ collections::{HashMap, HashSet}, error::Error, - sync::mpsc, }; use itertools::Itertools; -use rayon::prelude::*; pub fn process_part1(input: &str) -> u32 { - simulate_all_2(input, 100).values().copied().sum() + simulate_all(input, 100).values().copied().sum() //simulate_all(input) // .iter() // .map( @@ -23,147 +21,11 @@ pub fn process_part1(input: &str) -> u32 { // .sum() } -fn simulate_all(input: &str) -> HashMap { - let mut cheats = Vec::new(); - let mut start = (0, 0); - let grid = input - .lines() - .enumerate() - .map(|(yidx, row)| { - row.chars() - .enumerate() - .map(|(xidx, chara)| { - let tile = GridTile::try_from(chara).unwrap(); - if tile == GridTile::Start { - start = (xidx, yidx); - } - tile - }) - .collect_vec() - }) - .collect_vec(); - grid[1..grid.len() - 1] - .iter() - .enumerate() - .for_each(|(yidx, row)| { - row[1..row.len() - 1] - .iter() - .enumerate() - .for_each(|(xidx, tile)| { - if *tile == GridTile::Wall && cheatable(&grid, (xidx + 1, yidx + 1)) { - cheats.push((xidx + 1, yidx + 1)); - } - }); - }); - let mut saved = HashMap::new(); - - let no_cheat = simulate(grid.clone(), start, None).unwrap(); - let (tx, rx) = mpsc::channel(); - cheats.into_par_iter().for_each(|cheat| { - let cheat = simulate(grid.clone(), start, Some(cheat)).unwrap(); - let time_saved = no_cheat - cheat; - let _ = tx.send(time_saved); - }); - drop(tx); - while let Ok(time_saved) = rx.recv() { - saved - .entry(time_saved) - .and_modify(|count| *count += 1) - .or_insert(1); - } - saved -} - -fn cheatable(grid: &[Vec], wall_position: (usize, usize)) -> bool { - let above = grid - .get(wall_position.1 - 1) - .map(|above_row| above_row[wall_position.0]); - let below = grid - .get(wall_position.1 + 1) - .map(|below_row| below_row[wall_position.0]); - let left = grid[wall_position.1].get(wall_position.0 - 1).copied(); - let right = grid[wall_position.1].get(wall_position.0 + 1).copied(); - [above, below, left, right] - .iter() - .map(|tile| { - if let Some(tile) = tile { - match tile { - GridTile::Wall => 0, - GridTile::Path | GridTile::Start | GridTile::End => 1, - } - } else { - 0 - } - }) - .sum::() - >= 2 -} - -fn simulate( - mut grid: Vec>, - start: (usize, usize), - cheat: Option<(usize, usize)>, -) -> Option { - if let Some(cheat) = cheat { - grid[cheat.1][cheat.0] = GridTile::Path; - } - let mut visited = HashSet::new(); - visited.insert(start); - let mut next_paths = vec![MazeRunner { - coords: start, - visited: visited.clone(), - ..Default::default() - }]; - let mut arrived: Vec = Vec::new(); - while !next_paths.is_empty() { - next_paths = next_paths - .iter() - .map(|maze_runner| { - let mut paths = Vec::new(); - if let Some(path) = maze_runner.get_next(&grid, Direction::Up) { - paths.push(path); - } - if let Some(path) = maze_runner.get_next(&grid, Direction::Down) { - paths.push(path); - } - if let Some(path) = maze_runner.get_next(&grid, Direction::Left) { - paths.push(path); - } - if let Some(path) = maze_runner.get_next(&grid, Direction::Right) { - paths.push(path); - } - paths - }) - .collect_vec() - .concat(); - for (idx, maze_runner) in next_paths.clone().iter().enumerate().rev() { - if maze_runner.visited.contains(&maze_runner.coords) - || visited.contains(&maze_runner.coords) - { - next_paths.remove(idx); - continue; - } - visited.insert(maze_runner.coords); - next_paths[idx].visited.insert(maze_runner.coords); - if maze_runner.state == State::Arrived { - let arrived_reindeer = next_paths.remove(idx); - arrived.push(arrived_reindeer); - } - //log_maze(&grid, maze_runner); - } - } - arrived.sort_by(|a_runner, b_runner| a_runner.visited.len().cmp(&b_runner.visited.len())); - arrived - .first() - .map(|arrived| arrived.visited.len() as u32 - 1) -} - pub fn process_part2(input: &str) -> u32 { - simulate_all_2(input, 100).values().copied().sum() + simulate_all(input, 100).values().copied().sum() } -fn simulate_all_2(input: &str, time_to_saved: usize) -> HashMap { - let mut cheats = Vec::new(); +fn simulate_all(input: &str, time_to_saved: usize) -> HashMap { let mut start = (0, 0); let grid = input .lines() @@ -181,20 +43,7 @@ fn simulate_all_2(input: &str, time_to_saved: usize) -> HashMap { .collect_vec() }) .collect_vec(); - grid[1..grid.len() - 1] - .iter() - .enumerate() - .for_each(|(yidx, row)| { - row[1..row.len() - 1] - .iter() - .enumerate() - .for_each(|(xidx, tile)| { - if *tile == GridTile::Wall && cheatable(&grid, (xidx + 1, yidx + 1)) { - cheats.push((xidx + 1, yidx + 1)); - } - }); - }); - let no_cheat = simulate_2(&grid, start); + let no_cheat = simulate(&grid, start); let mut saved = HashMap::new(); for (tile_idx, tile) in no_cheat[..no_cheat.len() - time_to_saved] .iter() @@ -216,7 +65,7 @@ fn simulate_all_2(input: &str, time_to_saved: usize) -> HashMap { saved } -fn simulate_2(grid: &[Vec], start: (usize, usize)) -> Vec<(usize, usize)> { +fn simulate(grid: &[Vec], start: (usize, usize)) -> Vec<(usize, usize)> { let mut visited = HashSet::new(); visited.insert(start); let mut next_paths = vec![MazeRunner { @@ -394,23 +243,9 @@ mod tests { #[test] fn part1() { - let now = Instant::now(); - println!("Test 1:"); - let result = simulate_all(INPUT); - result - .iter() - .sorted_by(|a, b| a.0.cmp(b.0)) - .for_each(|(saved, count)| { - println!("There are {count} cheats that saved {saved} picoseconds"); - }); - println!("Ran in {}", get_elapsed_string(now.elapsed())); - } - - #[test] - fn part1_fast() { let now = Instant::now(); println!("Test 2:"); - let result = simulate_all_2(INPUT, 0); + let result = simulate_all(INPUT, 0); result .iter() .sorted_by(|a, b| a.0.cmp(b.0)) @@ -424,7 +259,7 @@ mod tests { fn part2() { let now = Instant::now(); println!("Test 2:"); - let result = simulate_all_2(INPUT, 2); + let result = simulate_all(INPUT, 2); result .iter() .sorted_by(|a, b| a.0.cmp(b.0))