+41
-19
day5/src/main.rs
+41
-19
day5/src/main.rs
···
2
2
use std::fmt::Debug;
3
3
use std::{cmp, fs};
4
4
5
+
#[derive(Debug)]
5
6
struct Ingredient {
6
7
id: u64,
7
8
}
8
9
10
+
// Describes a range of ingredient ids
9
11
struct IngredientRange {
10
12
start: Ingredient,
11
13
end: Ingredient,
12
14
}
13
15
16
+
// Used to store and manage IngredientRanges
14
17
struct IngredientDB {
15
18
ranges: Vec<IngredientRange>,
16
19
}
17
20
18
-
#[derive(Debug, Clone, Copy, Eq)]
21
+
// #[derive(Debug, Clone, Copy, Eq)]
22
+
// don't think i need to do this deriving stuff
23
+
// i find it a bit scary tbh
19
24
enum RangeCap {
20
25
Start(u64),
21
26
End(u64),
22
27
}
23
28
29
+
// If both elements have the same type and value, they are equal
24
30
impl cmp::PartialEq for RangeCap {
25
31
fn eq(&self, other: &Self) -> bool {
26
32
match (self, other) {
···
31
37
}
32
38
}
33
39
40
+
// Implementing an ordering for these Range Caps (better name? terminal?)
34
41
impl cmp::PartialOrd for RangeCap {
35
42
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
36
43
match (self, other) {
44
+
// if both values are Starts or both are Ends, compare values normally
37
45
(RangeCap::Start(a), RangeCap::Start(b)) => a.partial_cmp(b),
46
+
(RangeCap::End(a), RangeCap::End(b)) => a.partial_cmp(b),
47
+
// If they are equal, make sure Starts always go before Ends
38
48
(RangeCap::Start(a), RangeCap::End(b)) => {
39
49
let result = a.partial_cmp(b);
40
50
if result == Some(Ordering::Equal) {
···
51
61
result
52
62
}
53
63
}
54
-
(RangeCap::End(a), RangeCap::End(b)) => a.partial_cmp(b),
55
64
}
56
65
}
57
66
}
58
67
59
68
impl IngredientDB {
69
+
// Initializing an IngredientDB is basically parsing an input string and initializing the ranges vector.
60
70
fn new(range_list: &str) -> IngredientDB {
61
71
let mut ranges = Vec::new();
62
72
for l in range_list.lines() {
···
73
83
IngredientDB { ranges }
74
84
}
75
85
86
+
// We can check if an ingredient is fresh by comparing it to our ranges and seeing if it falls within any of them.
76
87
fn is_fresh(&self, ingredient: Ingredient) -> bool {
77
88
self.ranges
78
89
.iter()
79
90
.any(|range| range.start.id <= ingredient.id && ingredient.id <= range.end.id)
80
91
}
81
92
93
+
// This function is called clean_ranges_stack bc i initially tried to do something else and it was a nightmare
94
+
// Basically we initialize a list of RangeCaps, then sort them by value
95
+
// Then we go through and push to the stack when we find a Start, pop when we find an End
96
+
// If the end we find leaves our stack empty, we push a new range to our ranges vector
97
+
// Finally, we replace the internal ranges of this IngredientDB with our new, optimized ranges.
82
98
fn clean_ranges_stack(&mut self) {
83
99
let mut range_list = Vec::<RangeCap>::new();
84
100
for range in &self.ranges {
85
101
range_list.push(RangeCap::Start(range.start.id));
86
102
range_list.push(RangeCap::End(range.end.id));
87
103
}
88
-
range_list.sort_by(|a, b| a.partial_cmp(b).unwrap());
104
+
// Sort range list by Partial Ordering
105
+
// this shouldn't fail or something's wrong
106
+
range_list.sort_by(|a, b| {
107
+
a.partial_cmp(b)
108
+
.expect("RangeCaps should be able to be compared")
109
+
});
89
110
111
+
// initialize our stack and output range vectors
90
112
let mut range_stack = Vec::<u64>::new();
91
113
let mut new_ranges = Vec::<IngredientRange>::new();
92
114
93
115
for range in &range_list {
94
116
match *range {
117
+
// always push Starts to the stack
95
118
RangeCap::Start(id) => range_stack.push(id),
96
119
RangeCap::End(id) => {
120
+
// if stack has more than 1 element, pop the top and discard
121
+
// otherwise construct an IngredientRange with the final element and push it to the new_ranges vector
97
122
if range_stack.len() > 1 {
98
123
range_stack.pop();
99
124
} else {
···
111
136
self.ranges = new_ranges;
112
137
}
113
138
139
+
// Simple math function that subtracts ends of all the ranges from the starts and adds 1 to be **inclusive**
114
140
fn count_fresh(&self) -> u64 {
115
141
let mut fresh_count = 0u64;
116
142
for range in &self.ranges {
117
-
if range.start.id == 0 && range.end.id == 0 {
118
-
continue;
119
-
}
120
143
fresh_count += range.end.id.saturating_sub(range.start.id) + 1;
121
144
}
122
145
fresh_count
123
146
}
124
147
}
125
148
126
-
impl Debug for Ingredient {
127
-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128
-
write!(f, "Ingredient {{ id: {} }}", self.id)
129
-
}
130
-
}
131
-
132
149
impl Debug for IngredientRange {
133
150
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134
151
write!(
···
149
166
150
167
fn main() {
151
168
let in_file = fs::read_to_string("input.txt").expect("Failed to read file");
152
-
let file_sections = in_file.split("\n\n").collect::<Vec<&str>>();
153
169
170
+
// Split our input file into two parts based on newlines and make sure there are exactly 2 parts
171
+
let file_sections = in_file.split("\n\n").collect::<Vec<&str>>();
154
172
assert!(file_sections.len() == 2);
155
173
174
+
// Parse first part of the file and initialize an IngredientDB
156
175
let mut ingredient_db = IngredientDB::new(file_sections[0]);
157
-
let mut fresh_count = 0u64;
158
176
177
+
// before range cleanup
178
+
// println!("Ingredient Database: {:?}", ingredient_db);
179
+
// cleaning our ranges up here gives us better performance for the first part! since there are less ranges to check in.
180
+
ingredient_db.clean_ranges_stack();
181
+
// after range cleanup
182
+
// println!("Ingredient Database: {:?}", ingredient_db);
183
+
184
+
// to get our first star, we check each ingredient in the second section against our initialized DB and increment fresh_count
185
+
let mut fresh_count = 0u64;
159
186
for test_ingredient in file_sections[1].lines() {
160
187
if ingredient_db.is_fresh(Ingredient {
161
188
id: test_ingredient
···
166
193
}
167
194
}
168
195
169
-
// before range cleanup
170
-
// println!("Ingredient Database: {:?}", ingredient_db);
171
-
ingredient_db.clean_ranges_stack();
172
-
// after range cleanup
173
-
// println!("Ingredient Database: {:?}", ingredient_db);
174
196
println!("Fresh Ingredients in Pantry: {}", fresh_count);
175
197
println!(
176
198
"Fresh Ingredients in Database: {}",