generated from bravit/advent-of-code-rust-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday_10.rs
154 lines (117 loc) · 4.58 KB
/
day_10.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use anyhow::*;
use crate::{Solution};
use crate::tools::{Coo, Direction};
const TEST: &str = "\
89010123
78121874
87430965
96549874
45678903
32019012
01329801
10456732";
/// Sample the `map` altitude at some given `coo`
fn try_sample_map(map: &[&str], coo: Coo) -> Option<u8> {
if coo.x < 0 || coo.x >= map.len() as isize { return None }
if coo.y < 0 || coo.y >= map[0].len() as isize { return None }
let row = map [coo.y as usize].as_bytes();
Some (row [coo.x as usize])
}
/// Draw a trail on the `path_map` by following it back to the start,
/// incrementing the counter along the way.
/// `trail_coo` gives the end of the trail
fn create_path (path_map: &mut PathMap, trail_coo: Coo) {
// Start from the end of the trail
let mut coo = trail_coo;
// Step back to the head
for altitude in ('0'..= '9' as char).rev() {
// Increment the trail counter for this path location
let path_item = &mut path_map [coo.x as usize][coo.y as usize];
path_item.num_trails += 1;
if altitude != '0' { coo = path_item.back.unwrap() }
else { assert!(path_item.back.is_none()); }
}
}
/// Score a trail's head starting at `head_coo`. If `with_rating` is true, the score counts
/// all the possibly different trails leading to height `9`
fn score_head (map: &[&str], path_map: &mut PathMap, head_coo: Coo, with_rating: bool) -> u32 {
let height = map.len();
let width = map[0].len();
// DFS queue
let mut jobs = Vec::<Job>::with_capacity(height*width);
let first_job = Job { coo: head_coo };
jobs.push(first_job);
// Process the jobs
while let Some (job) = jobs.pop() {
// Check if we have reached a goal. In this case, trace the path
let Some (altitude) = try_sample_map(&map, job.coo) else { unreachable!() };
if altitude == '9' as u8 {
create_path(path_map, job.coo);
}
// Test all the directions around
for dir in Direction::iter() {
let Some(n_coo) = job.coo.try_next(dir, width, height) else { continue };
// If we don't care about rating, ignore the locations that have already been visited
// (they are already explored)
if !with_rating {
if path_map[n_coo.x as usize][n_coo.y as usize].back.is_some() { continue }
}
// If we have an unexplored location with the required elevation step ...
if try_sample_map(map, n_coo.into ()) == Some (altitude+1) {
// ... we must explore it
let next_job = Job { coo: n_coo };
jobs.push(next_job);
// and update our path map
let path_item = PathItem { num_trails: 0, back: Some(job.coo) };
path_map[n_coo.x as usize][n_coo.y as usize] = path_item;
}
}
}
// The score is equal to the number of trails starting from the trail's head
path_map[head_coo.x as usize][head_coo.y as usize].num_trails
}
/// Element to process next while searching for trails
struct Job {
/// Coordinate to process
pub coo: Coo,
}
/// Location in a [PathMap], keeping track of the number of trails going through
#[derive(Default, Debug, Copy, Clone)]
struct PathItem {
/// Number of trails
pub num_trails: u32,
/// Direction to the trail's head
pub back: Option<Coo>,
}
/// Enables to trace paths along the map
type PathMap = Vec<Vec<PathItem>>;
/// Solve the puzzle. Parameter `with_rating` enables the rated score of part b.
fn solve (map: &[&str], with_rating: bool) -> Result<usize> {
let height = map.len();
let width = map[0].len();
// Look for trail heads
let mut sum_score = 0;
for y in 0..map.len() {
let row = map [y].as_bytes();
for x in 0..row.len() {
// For each trail head ...
if row [x] as char == '0' {
let mut path_map: Vec<Vec<PathItem>> = vec![vec![Default::default(); height]; width];
// ... compute score and collect sum
let score = score_head(map, &mut path_map,(x, y).into (), with_rating);
sum_score += score;
}
}
}
Ok (sum_score as usize)
}
fn split (content: &str) -> Vec<&str> {
content.lines().collect()
}
pub fn day_10 (content: &[&str]) -> Result <(Solution, Solution)> {
debug_assert!(solve (&split (TEST), false).unwrap_or_default() == 36);
debug_assert!(solve (&split (TEST), true).unwrap_or_default() == 81);
let ra = solve(content, false)?;
let rb = solve(content, true)?;
Ok((Solution::Unsigned(ra), Solution::Unsigned(rb)))
}