-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday2.rs
156 lines (137 loc) · 4.28 KB
/
day2.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
155
156
use aoc_runner_derive::aoc;
fn parse(input: &str) -> Vec<utils::Game> {
use std::str::FromStr;
input
.lines()
.map(|line| {
let (game_id, game_content) = line.split_once(": ").unwrap();
// Parse game ID
let game_id = game_id
.split_ascii_whitespace()
.last()
.unwrap()
.parse()
.unwrap();
// Parse game content into cube sets
let cube_sets = game_content
.split(';')
.map(|cubes| {
let mut cube_set = utils::CubeSet::default();
cubes.trim().split(',').for_each(|cube| {
let mut cube = cube.trim().split(' ');
let n = cube.next().unwrap().parse().unwrap();
let color = utils::Color::from_str(cube.next().unwrap()).unwrap();
cube_set.set_n_color(&color, n);
});
cube_set
})
.collect();
// Form game from game ID and cube sets
utils::Game { game_id, cube_sets }
})
.collect()
}
#[aoc(day2, part1)]
#[must_use]
pub fn part1(input: &str) -> u32 {
const MAX_N_RED: u8 = 12;
const MAX_N_GREEN: u8 = 13;
const MAX_N_BLUE: u8 = 14;
let input = parse(input);
input
.iter()
.filter_map(|game| {
// Determine if game is valid
let is_game_valid = game.cube_sets.iter().all(|cube_set| {
cube_set.n_red <= MAX_N_RED
&& cube_set.n_green <= MAX_N_GREEN
&& cube_set.n_blue <= MAX_N_BLUE
});
// Return the ID of the game if valid to be summed
if is_game_valid {
Some(game.game_id)
} else {
None
}
})
.sum()
}
#[aoc(day2, part2)]
#[must_use]
pub fn part2(input: &str) -> u32 {
let input = parse(input);
input
.iter()
.map(|game| {
// Determine the minimum number of cubes of each color
let mut min_n_red = u8::MIN;
let mut min_n_green = u8::MIN;
let mut min_n_blue = u8::MIN;
game.cube_sets.iter().for_each(|cube_set| {
min_n_red = min_n_red.max(cube_set.n_red);
min_n_green = min_n_green.max(cube_set.n_green);
min_n_blue = min_n_blue.max(cube_set.n_blue);
});
// Return the product of the minimum number of cubes of each color
u32::from(min_n_red) * u32::from(min_n_green) * u32::from(min_n_blue)
})
.sum()
}
mod utils {
pub struct Game {
pub game_id: u32,
pub cube_sets: Vec<CubeSet>,
}
#[derive(Default)]
#[allow(clippy::struct_field_names)]
pub struct CubeSet {
pub n_red: u8,
pub n_green: u8,
pub n_blue: u8,
}
impl CubeSet {
pub fn set_n_color(&mut self, color: &Color, n: u8) {
match color {
Color::Red => self.n_red = n,
Color::Green => self.n_green = n,
Color::Blue => self.n_blue = n,
}
}
}
pub enum Color {
Red,
Green,
Blue,
}
impl std::str::FromStr for Color {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"red" => Ok(Self::Red),
"green" => Ok(Self::Green),
"blue" => Ok(Self::Blue),
_ => Err("Invalid color"),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
const SAMPLE: &str = indoc! {"
Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
"};
#[test]
pub fn part1_example() {
assert_eq!(part1(SAMPLE), 8);
}
#[test]
pub fn part2_example() {
assert_eq!(part2(SAMPLE), 2286);
}
}