Day 20 (refactor)
This commit is contained in:
parent
79c8ed5744
commit
9ca870d1bb
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
.env
|
82
src/day20.rs
82
src/day20.rs
|
@ -1,74 +1,34 @@
|
||||||
use crate::util::maps::*;
|
use crate::util::maps::*;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
const CHEAT_DURATION: u64 = 20;
|
const CHEAT_DURATION: usize = 20;
|
||||||
|
|
||||||
pub fn day20() {
|
pub fn day20() {
|
||||||
let input = World::from_string(&crate::input(20));
|
let input: World<char> = World::from_string(&crate::input(20));
|
||||||
let maze = input.map(|&c| c == '#');
|
let maze: World<bool> = input.map(|&c| c == '#');
|
||||||
let (ex, ey, _) = input.enumerate().find(|(_, _, &c)| c == 'E').unwrap();
|
|
||||||
|
|
||||||
let costs = path_costs(&maze, ex, ey);
|
let (end_x, end_y, _) = input.enumerate().find(|(_, _, &c)| c == 'E').unwrap();
|
||||||
|
let distance_map: World<Option<u64>> = maze.tile_distances(end_x, end_y);
|
||||||
|
|
||||||
let free_spaces: Vec<(usize, usize)> = maze
|
let walkable_tiles: Vec<(usize, usize)> = distance_map
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(x, y, &b)| if !b { Some((x, y)) } else { None })
|
.filter_map(|(x, y, &d)| if d.is_some() { Some((x, y)) } else { None })
|
||||||
.collect();
|
.collect();
|
||||||
let all_cheats: Vec<u64> = free_spaces
|
let tile_pairs = walkable_tiles
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|&(ax, ay)| {
|
.cartesian_product(walkable_tiles.iter())
|
||||||
free_spaces
|
.filter(|(&(ax, ay), &(bx, by))| !(ax == bx && ay == by));
|
||||||
.iter()
|
let all_cheats = tile_pairs
|
||||||
.filter_map(|&(bx, by)| {
|
.filter(|(&(ax, ay), &(bx, by))| taxicab_distance(ax, ay, bx, by) <= CHEAT_DURATION)
|
||||||
if ax == bx && ay == by {
|
.map(|(&(ax, ay), &(bx, by))| {
|
||||||
return None;
|
let taxi_distance = taxicab_distance(ax, ay, bx, by) as i64;
|
||||||
}
|
let a_distance_to_end = distance_map.get(ax, ay).unwrap().unwrap() as i64;
|
||||||
let taxi_cost = taxicab_distance(ax, ay, bx, by) as u64;
|
let b_distance_to_end = distance_map.get(bx, by).unwrap().unwrap() as i64;
|
||||||
if taxi_cost > CHEAT_DURATION {
|
a_distance_to_end - b_distance_to_end - taxi_distance
|
||||||
return None;
|
});
|
||||||
}
|
let good_cheats = all_cheats.filter(|&advantage| advantage >= 100).count();
|
||||||
let a_cost = costs.get(ax, ay).unwrap();
|
|
||||||
let b_cost = costs.get(bx, by).unwrap();
|
|
||||||
// a cheat's timesave is the cost of a - the cost of b, minus the time it takes to do the cheat
|
|
||||||
// and we return None for cheats that have negative timesave (why would you cheat to do worse?)
|
|
||||||
a_cost.checked_sub(b_cost)?.checked_sub(taxi_cost)
|
|
||||||
})
|
|
||||||
.collect::<Vec<u64>>()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let good_cheats = all_cheats
|
|
||||||
.iter()
|
|
||||||
.filter(|&&advantage| advantage >= 100)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
println!("{good_cheats}");
|
println!("{good_cheats} cheats save 100 or more picoseconds.");
|
||||||
}
|
|
||||||
|
|
||||||
fn path_costs(world: &World<bool>, x: usize, y: usize) -> World<u64> {
|
|
||||||
fn calculate_path_costs(
|
|
||||||
world: &World<bool>,
|
|
||||||
x: usize,
|
|
||||||
y: usize,
|
|
||||||
cost_so_far: u64,
|
|
||||||
costs: &mut World<u64>,
|
|
||||||
) {
|
|
||||||
let adjacents = world
|
|
||||||
.adjacent_locations(x, y)
|
|
||||||
.into_iter()
|
|
||||||
.filter(|&(nx, ny)| !world.get(nx, ny).unwrap());
|
|
||||||
for (nx, ny) in adjacents {
|
|
||||||
let cost = cost_so_far + 1;
|
|
||||||
let old_cost = costs.get(nx, ny).unwrap();
|
|
||||||
if cost < old_cost {
|
|
||||||
costs.set(nx, ny, cost);
|
|
||||||
calculate_path_costs(world, nx, ny, cost, costs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut costs = World::from_dimensions(world.width(), world.height(), u64::MAX);
|
|
||||||
costs.set(x, y, 0);
|
|
||||||
calculate_path_costs(world, x, y, 0, &mut costs);
|
|
||||||
costs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn taxicab_distance(ax: usize, ay: usize, bx: usize, by: usize) -> usize {
|
fn taxicab_distance(ax: usize, ay: usize, bx: usize, by: usize) -> usize {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::fmt::Display;
|
use std::{fmt::Display, intrinsics::atomic_cxchgweak_acquire_acquire};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
@ -187,3 +187,27 @@ where
|
||||||
f.write_str(&final_string)
|
f.write_str(&final_string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl World<bool> {
|
||||||
|
pub fn tile_distances(&self, x: usize, y: usize) -> World<Option<u64>> {
|
||||||
|
let mut costs = World::from_dimensions(self.width(), self.height(), None);
|
||||||
|
let mut tile_stack: Vec<(usize, usize, u64)> = vec![];
|
||||||
|
tile_stack.push((x, y, 0));
|
||||||
|
while let Some((x, y, cost_so_far)) = tile_stack.pop() {
|
||||||
|
let adjacents = self
|
||||||
|
.adjacent_locations(x, y)
|
||||||
|
.into_iter()
|
||||||
|
.filter(|&(nx, ny)| !self.get(nx, ny).unwrap());
|
||||||
|
for (nx, ny) in adjacents {
|
||||||
|
let cost = cost_so_far + 1;
|
||||||
|
let old_cost = costs.get(nx, ny).unwrap().unwrap_or(u64::MAX);
|
||||||
|
if cost < old_cost {
|
||||||
|
costs.set(nx, ny, Some(cost));
|
||||||
|
tile_stack.push((nx, ny, cost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
costs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue