Day 17 part 2

This commit is contained in:
Shadowfacts 2023-12-17 16:15:52 -05:00
parent 5bc57e235f
commit 0c5498df01
1 changed files with 228 additions and 109 deletions

View File

@ -1,12 +1,28 @@
use std::{collections::BinaryHeap, fmt::Debug, fs::DirEntry}; use std::{
collections::{BinaryHeap, HashMap},
fmt::Debug,
fs::DirEntry,
};
use itertools::Itertools; use itertools::Itertools;
pub fn run() { pub fn run() {
let input = include_str!("../input/day17.txt"); let input = include_str!("../input/day17.txt");
let map: Map = input.into(); let map: Map = input.into();
let path = map.dijkstra_ish((0, 0), (map.width as isize - 1, map.height as isize - 1), 3);
let path = map.dijkstra_ish(
(0, 0),
(map.width as isize - 1, map.height as isize - 1),
Map::part1_edges,
);
dbg!(map.path_cost(&path.unwrap())); dbg!(map.path_cost(&path.unwrap()));
let path2 = map.dijkstra_ish(
(0, 0),
(map.width as isize - 1, map.height as isize - 1),
Map::part2_edges,
);
dbg!(map.part2_path_cost(&path2.unwrap()));
} }
#[derive(Clone)] #[derive(Clone)]
@ -50,7 +66,7 @@ impl Debug for Map {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Direction { enum Direction {
Up, Up,
Down, Down,
@ -96,7 +112,7 @@ struct Edge {
direction: Direction, direction: Direction,
dest: (isize, isize), dest: (isize, isize),
cost: u8, cost: u8,
steps: u8, steps: usize,
} }
impl Map { impl Map {
@ -104,11 +120,15 @@ impl Map {
col >= 0 && col < self.width as isize && row >= 0 && row < self.height as isize col >= 0 && col < self.width as isize && row >= 0 && row < self.height as isize
} }
fn edges_from(&self, pos: (isize, isize), incoming_dir: Option<Direction>) -> Vec<Edge> { fn part1_edges(
&self,
pos: (isize, isize),
incoming_dir: Option<(Direction, usize)>,
) -> Vec<Edge> {
Direction::all() Direction::all()
.iter() .iter()
.filter_map(|&d| { .filter_map(|&d| {
if incoming_dir.is_some_and(|x| x == d.opposite()) { if incoming_dir.is_some_and(|(x, c)| x == d.opposite() || (x == d && c >= 3)) {
return None; return None;
} }
let moved = d.move_pos(pos); let moved = d.move_pos(pos);
@ -125,45 +145,53 @@ impl Map {
.collect() .collect()
} }
// fn edges_from_ultra( fn part2_edges(
// &self, &self,
// pos: (isize, isize), pos: (isize, isize),
// incoming_dir: Option<(Direction, usize)>, incoming_dir: Option<(Direction, usize)>,
// ) -> Vec<Edge> { ) -> Vec<Edge> {
// let mut v = vec![]; let mut v = vec![];
// for d in Direction::all() { for d in Direction::all() {
// for dist in 4..=10 { if incoming_dir.is_some_and(|(x, _)| x == d.opposite()) {
// let moved = d.move_pos_count(pos, dist); continue;
// let costs_sum = (1..=dist) }
// .map(|c| { for dist in 4..=10 {
// let moved = d.move_pos_count(pos, c); if incoming_dir.is_some_and(|(x, c)| x == d && (c + dist) > 10) {
// self.costs[moved.1 as usize][moved.0 as usize] continue;
// }) }
// .sum();
// if self.in_bounds(moved) { let moved = d.move_pos_count(pos, dist as isize);
// v.push(Edge { if !self.in_bounds(moved) {
// direction: d, continue;
// dest: moved, }
// cost: costs_sum,
// steps: dist as u8, let costs_sum = (1..=dist)
// }); .map(|c| {
// } let moved = d.move_pos_count(pos, c as isize);
// } self.costs[moved.1 as usize][moved.0 as usize]
// } })
// v .sum();
// } v.push(Edge {
direction: d,
dest: moved,
cost: costs_sum,
steps: dist,
});
}
}
v
}
fn dijkstra_ish( fn dijkstra_ish(
&self, &self,
start: (isize, isize), start: (isize, isize),
end: (isize, isize), end: (isize, isize),
max_single_direction: usize, edges_fn: impl Fn(&Self, (isize, isize), Option<(Direction, usize)>) -> Vec<Edge>,
) -> Option<Vec<(isize, isize)>> { ) -> Option<Vec<(isize, isize)>> {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct QueueEntry { struct QueueEntry {
neg_cost: isize, neg_cost: isize,
inbound_dir: Option<Direction>, inbound: Option<(Direction, usize)>,
inbound_dir_count: usize,
pos: (isize, isize), pos: (isize, isize),
} }
impl Ord for QueueEntry { impl Ord for QueueEntry {
@ -178,92 +206,94 @@ impl Map {
} }
let mut queue = BinaryHeap::<QueueEntry>::new(); let mut queue = BinaryHeap::<QueueEntry>::new();
let mut distances = vec![vec![[[usize::MAX; 4]; 4]; self.width]; self.height];
let mut prev = vec![vec![[[None; 4]; 4]; self.width]; self.height];
for d in Direction::all() { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
distances[start.1 as usize][start.0 as usize][d as usize][0] = 0; struct HistoryKey {
pos: (isize, isize),
inbound: Option<(Direction, usize)>,
} }
let mut distances = HashMap::<HistoryKey, usize>::new();
let mut prev = HashMap::<HistoryKey, HistoryKey>::new();
distances.insert(
HistoryKey {
pos: start,
inbound: None,
},
0,
);
queue.push(QueueEntry { queue.push(QueueEntry {
neg_cost: 0, neg_cost: 0,
inbound_dir: None, inbound: None,
inbound_dir_count: 0,
pos: start, pos: start,
}); });
while let Some(entry) = queue.pop() { while let Some(entry) = queue.pop() {
for edge in self.edges_from(entry.pos, entry.inbound_dir) { let entry_key = HistoryKey {
if entry.inbound_dir.is_some_and(|d| d == edge.direction) pos: entry.pos,
&& entry.inbound_dir_count >= max_single_direction inbound: entry.inbound,
{ };
continue; for edge in edges_fn(self, entry.pos, entry.inbound) {
} let alt = distances.get(&entry_key).unwrap_or(&usize::MAX) + edge.cost as usize;
let alt = distances[entry.pos.1 as usize][entry.pos.0 as usize] let new_inbound_dir_count = if let Some((d, c)) = entry.inbound {
[entry.inbound_dir.unwrap_or(Direction::Right) as usize] if d == edge.direction {
[entry.inbound_dir_count] c + edge.steps
+ edge.cost as usize;
let new_inbound_dir_count =
if entry.inbound_dir.is_some_and(|d| d == edge.direction) {
entry.inbound_dir_count + 1
} else { } else {
1 edge.steps
}; }
if alt } else {
< distances[edge.dest.1 as usize][edge.dest.0 as usize][edge.direction as usize] edge.steps
[new_inbound_dir_count] };
{ let edge_key = HistoryKey {
distances[edge.dest.1 as usize][edge.dest.0 as usize] pos: edge.dest,
[edge.direction as usize][new_inbound_dir_count] = alt; inbound: Some((edge.direction, new_inbound_dir_count)),
prev[edge.dest.1 as usize][edge.dest.0 as usize][edge.direction as usize] };
[new_inbound_dir_count] = Some(( if alt < *distances.get(&edge_key).unwrap_or(&usize::MAX) {
entry.pos, distances.insert(edge_key, alt);
entry.inbound_dir.map(|x| (x, entry.inbound_dir_count)), prev.insert(edge_key, entry_key);
)); let new_entry = QueueEntry {
queue.push(QueueEntry {
neg_cost: -(alt as isize), neg_cost: -(alt as isize),
inbound_dir: Some(edge.direction), inbound: Some((edge.direction, new_inbound_dir_count)),
inbound_dir_count: new_inbound_dir_count,
pos: edge.dest, pos: edge.dest,
}); };
queue.push(new_entry);
} }
} }
} }
for row in 0..self.height { // for row in 0..self.height {
for col in 0..self.width { // for col in 0..self.width {
println!("({}, {}) ", col, row); // println!("({}, {}) ", col, row);
for d in Direction::all() { // for d in Direction::all() {
println!( // println!(
"\t{:?} 0={}/{:?}, 1={}/{:?}, 2={}/{:?}, 3={}/{:?}", // "\t{:?} 0={}/{:?}, 1={}/{:?}, 2={}/{:?}, 3={}/{:?}",
d, // d,
distances[row][col][d as usize][0], // distances[row][col][d as usize][0],
prev[row][col][d as usize][0], // prev[row][col][d as usize][0],
distances[row][col][d as usize][1], // distances[row][col][d as usize][1],
prev[row][col][d as usize][1], // prev[row][col][d as usize][1],
distances[row][col][d as usize][2], // distances[row][col][d as usize][2],
prev[row][col][d as usize][2], // prev[row][col][d as usize][2],
distances[row][col][d as usize][3], // distances[row][col][d as usize][3],
prev[row][col][d as usize][3], // prev[row][col][d as usize][3],
); // );
} // }
} // }
} // }
let mut path = vec![]; let mut path = vec![];
let mut min = None; let min = distances
for (d, d_count) in Direction::all().into_iter().cartesian_product(0..4) { .iter()
let cost = distances[end.1 as usize][end.0 as usize][d as usize][d_count]; .filter(|(k, _)| k.pos == end)
if min.is_none() || min.is_some_and(|(c, _, _)| c > cost) { .min_by_key(|(_, d)| **d)
min = Some((cost, d, d_count)); .unwrap()
} .0;
} let mut u = Some(min);
let mut u = Some((end, min.map(|(_, d, dc)| (d, dc)))); while let Some(key) = u {
while let Some((p, dir_and_count)) = u { println!("({}, {})", key.pos.0, key.pos.1);
if let Some((dir, dir_count)) = dir_and_count { path.insert(0, key.pos);
println!("dir={:?}, dir_count={}", dir, dir_count); u = prev.get(key);
}
path.insert(0, p);
u = dir_and_count.and_then(|(d, dc)| prev[p.1 as usize][p.0 as usize][d as usize][dc]);
} }
Some(path) Some(path)
} }
@ -274,6 +304,24 @@ impl Map {
.map(|&(col, row)| self.costs[row as usize][col as usize] as usize) .map(|&(col, row)| self.costs[row as usize][col as usize] as usize)
.sum() .sum()
} }
fn part2_path_cost(&self, path: &[(isize, isize)]) -> usize {
let mut cost = 0;
for (a, b) in path.iter().zip(path.iter().skip(1)) {
// a->b is always a cardinal direction, so one of these loops will only have one
// iteration
for row in a.1.min(b.1)..=a.1.max(b.1) {
for col in a.0.min(b.0)..=a.0.max(b.0) {
cost += self.costs[row as usize][col as usize] as usize;
}
}
// don't double count the beginning of each path segment
// also don't count the beginning of the first segment
cost -= self.costs[a.1 as usize][a.0 as usize] as usize;
}
cost
}
} }
#[cfg(test)] #[cfg(test)]
@ -288,7 +336,7 @@ mod tests {
"# "#
.into(); .into();
assert_eq!( assert_eq!(
map.dijkstra_ish((0, 0), (4, 1), 3), map.dijkstra_ish((0, 0), (4, 1), Map::part1_edges),
Some(vec![(0, 0), (1, 0), (2, 0), (3, 0), (3, 1), (4, 1)]) Some(vec![(0, 0), (1, 0), (2, 0), (3, 0), (3, 1), (4, 1)])
); );
} }
@ -301,7 +349,11 @@ mod tests {
"# "#
.into(); .into();
assert_eq!( assert_eq!(
map.dijkstra_ish((0, 0), (map.width as isize - 1, map.height as isize - 1), 3), map.dijkstra_ish(
(0, 0),
(map.width as isize - 1, map.height as isize - 1),
Map::part1_edges
),
Some(vec![(0, 0), (1, 0), (2, 0), (2, 1), (3, 1), (4, 1)]) Some(vec![(0, 0), (1, 0), (2, 0), (2, 1), (3, 1), (4, 1)])
); );
} }
@ -324,8 +376,75 @@ mod tests {
4322674655533 4322674655533
"# "#
.into(); .into();
let path = let path = dbg!(map.dijkstra_ish(
dbg!(map.dijkstra_ish((0, 0), (map.width as isize - 1, map.height as isize - 1), 3)); (0, 0),
(map.width as isize - 1, map.height as isize - 1),
Map::part1_edges
));
assert_eq!(map.path_cost(&path.unwrap()), 102); assert_eq!(map.path_cost(&path.unwrap()), 102);
} }
#[test]
fn test_part2_example1() {
let map: Map = r#"
2413432311323
3215453535623
3255245654254
3446585845452
4546657867536
1438598798454
4457876987766
3637877979653
4654967986887
4564679986453
1224686865563
2546548887735
4322674655533
"#
.into();
let path = dbg!(map.dijkstra_ish(
(0, 0),
(map.width as isize - 1, map.height as isize - 1),
Map::part2_edges
));
assert_eq!(map.part2_path_cost(&path.unwrap()), 94);
}
#[test]
fn test_part2_example2() {
let map: Map = r#"
111111111111
999999999991
999999999991
999999999991
999999999991
"#
.into();
let path = dbg!(map.dijkstra_ish(
(0, 0),
(map.width as isize - 1, map.height as isize - 1),
Map::part2_edges
));
assert_eq!(map.part2_path_cost(&path.unwrap()), 71);
}
#[test]
fn test_part2_input_subgrid() {
let map: Map = r#"
645655655636632423532352254234
353344463432453352632231125552
526342234456245465642135234421
346523323446362131122522334552
344544334245121341153154552255
545315342221553551233455324225
312521155444131243522241333531
153145243235542533153124432435
133522524342325151543155414215
"#
.into();
assert_eq!(
map.part2_path_cost(&[(0, 8), (9, 8), (9, 4), (19, 4), (19, 0), (29, 0),]),
110
);
}
} }