Skip to content

Commit e98a4d6

Browse files
committed
Implement support for cheats
1 parent 45b27f0 commit e98a4d6

1 file changed

Lines changed: 30 additions & 9 deletions

File tree

day20/src/day20.rs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,23 @@ struct Racetrack {
1717
rows: Vec<Vec<char>>,
1818
}
1919

20+
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
21+
struct Cheat {
22+
start: Vec2<i32>,
23+
end: Option<Vec2<i32>>,
24+
picos_left: i32,
25+
}
26+
2027
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
2128
struct Node {
2229
pos: Vec2<i32>,
2330
picos: i32,
31+
cheat: Option<Cheat>,
2432
}
2533

2634
impl Ord for Node {
2735
fn cmp(&self, other: &Self) -> Ordering {
28-
self.picos.cmp(&other.picos)
36+
other.picos.cmp(&self.picos) // Intentionally reversed to make BinaryHeap behave like a min-heap
2937
}
3038
}
3139

@@ -54,27 +62,40 @@ impl Racetrack {
5462
.find_map(|(y, row)| row.iter().enumerate().find(|(_, &cell)| cell == c).map(|(x, _)| Vec2::new(x as i32, y as i32)))
5563
}
5664

57-
fn shortest_path(&self, start: Vec2<i32>, end: Vec2<i32>) -> Option<i32> {
65+
fn shortest_path(&self, start: Vec2<i32>, end: Vec2<i32>) -> Option<Node> {
5866
// Your run-of-the-mill Dijkstra implementation
5967

6068
let mut queue = BinaryHeap::new();
6169
let mut visited = HashSet::new();
6270

63-
queue.push(Node { pos: start, picos: 0 });
64-
visited.insert(start);
71+
queue.push(Node { pos: start, picos: 0, cheat: None });
72+
visited.insert((start, None));
6573

6674
while let Some(node) = queue.pop() {
6775
if node.pos == end {
68-
return Some(node.picos);
76+
return Some(node);
6977
}
7078

7179
for dy in -1..=1 {
7280
for dx in -1..=1 {
7381
if (dx != 0) ^ (dy != 0) {
7482
let neigh = Vec2::new(node.pos.x + dx, node.pos.y + dy);
75-
if !visited.contains(&neigh) && self.in_bounds(neigh) && self[neigh] != '#' {
76-
visited.insert(neigh);
77-
queue.push(Node { pos: neigh, picos: node.picos + 1 });
83+
if self.in_bounds(neigh) {
84+
let is_wall = self[neigh] == '#';
85+
86+
let can_cheat = node.cheat.map_or(true, |c| c.picos_left > 0);
87+
let new_cheat = if let Some(cheat) = node.cheat {
88+
Some(Cheat { start: cheat.start, end: if cheat.picos_left == 1 { Some(neigh) } else { cheat.end }, picos_left: (cheat.picos_left - 1).max(0) })
89+
} else if is_wall {
90+
Some(Cheat { start: neigh, end: None, picos_left: 1 })
91+
} else {
92+
node.cheat
93+
};
94+
95+
if !visited.contains(&(neigh, new_cheat)) && (!is_wall || can_cheat) {
96+
visited.insert((neigh, new_cheat));
97+
queue.push(Node { pos: neigh, picos: node.picos + 1, cheat: new_cheat });
98+
}
7899
}
79100
}
80101
}
@@ -106,5 +127,5 @@ fn main() {
106127
let start = track.locate('S').unwrap();
107128
let end = track.locate('E').unwrap();
108129

109-
println!("Shortest: {}", track.shortest_path(start, end).unwrap());
130+
println!("Shortest: {:?}", track.shortest_path(start, end).unwrap());
110131
}

0 commit comments

Comments
 (0)