generated from bravit/advent-of-code-rust-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday_19.rs
151 lines (117 loc) · 4.11 KB
/
day_19.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
use std::collections::HashMap;
use anyhow::*;
use crate::Solution;
const TEST: &str = "\
r, wr, b, g, bwu, rb, gb, br
brwrr
bggr
gbbr
rrbgbr
ubwu
bwurrg
brgr
bbrgwb";
/// A pattern as a sequence of ASCII bytes
type Pattern<'a> = &'a [u8];
/// Collection of patterns
type Patterns<'a> = Vec<Pattern<'a>>;
/// A design as a sequence of ASCII bytes
type Design<'a> = &'a [u8];
/// Collection of designs
type Designs<'a> = Vec<Design<'a>>;
/// Memoization table to avoid resolving the same sub-problems again and again.
/// For each design, save the number of possibilities
type Memo<'a> = HashMap<Design<'a>, usize>;
fn split (content: &str) -> Vec<&str> {
content.lines().collect()
}
/// Get all the patterns from the puzzle file `content`
fn get_patterns<'a> (content: &'a[&'a str]) -> Result<Patterns<'a>> {
Ok(
content [0].split(", ")
.map(|s| s.as_bytes())
.collect()
)
}
/// Get all the designs from the puzzle file `content`
fn get_designs<'a> (content: &'a[&'a str]) -> Result<Designs<'a>> {
Ok (
content.iter()
.skip(2)
.map(|s| s.as_bytes())
.collect()
)
}
/// Generate a shorter design by removing the provided `pattern` from
/// the beginning of the given `design`
fn stripped_design<'a> (design: Design<'a>, pattern: Pattern<'a>) -> Design<'a> {
&design [pattern.len()..]
}
/// Return `true` if the provided `design` is solvable given the available `patterns`.
///
/// **This function is recursive**
fn can_solve (design: Design, patterns: &Patterns) -> bool {
// An empty design is solvable by definition
if design.is_empty() { return true }
// Try all the patterns matching the beginning of the design,
// then check if the stripped design is solvable
patterns.iter()
.filter(|pattern| design.starts_with(pattern))
.any (|pattern| {
can_solve (stripped_design(design, pattern), patterns)
})
}
/// Count the number of ways a `design` can be made given the available `patterns`.
/// Parameter `memo` is used to save solutions of intermediate sub-problems
///
/// **This function is recursive**
fn count_possibilities<'a> (memo: &mut Memo<'a>, design: Design<'a>, patterns: &Patterns<'a>) -> usize {
// Check if we already know the answer
if design.is_empty() { return 1 }
if let Some (count) = memo.get(design) {
return *count;
}
// Try all the patterns matching the beginning of the design.
// For each of them, get the number of possibilities for the design remaining part
let mut tot_count = 0;
for pattern in patterns.iter ().filter(|pat| design.starts_with(pat)) {
let count = count_possibilities(memo, stripped_design(design, pattern), patterns);
tot_count += count;
}
// Save the result
memo.insert(design, tot_count);
tot_count
}
/// Solve first part of the puzzle
fn part_a (_content: &[&str]) -> Result<usize> {
// Load the patterns and the design to reproduce
let patterns = get_patterns(_content)?;
let designs = get_designs(_content)?;
// Filter and count the number of solvable designs
let count = designs.iter()
.filter(
|design| can_solve(design, &patterns)
).count();
Ok(count)
}
/// Solve second part of the puzzle
fn part_b (content: &[&str]) -> Result<usize> {
// Load the patterns and the design to reproduce
let patterns = get_patterns(content)?;
let designs = get_designs(content)?;
// Use the memoization technique to avoid resolving sub-problems we have already seen
let mut memo = Memo::new();
// Count and sum the number of possibilities for each pattern
let count:usize = designs.iter()
.map(
|design| count_possibilities(&mut memo, design, &patterns)
).sum();
Ok(count)
}
pub fn day_19 (content: &[&str]) -> Result <(Solution, Solution)> {
debug_assert!(part_a (&split(TEST)).unwrap_or_default() == 6);
debug_assert!(part_b (&split(TEST)).unwrap_or_default() == 16);
let ra = part_a(content)?;
let rb = part_b(content)?;
Ok((Solution::Unsigned(ra), Solution::Unsigned(rb)))
}