diff --git a/day2/src/main.rs b/day2/src/main.rs index 48a1a28..522311e 100644 --- a/day2/src/main.rs +++ b/day2/src/main.rs @@ -1,8 +1,7 @@ mod part1; -mod part1alt; +mod part2; fn main() { println!("Part1: {}", part1::main(include_str!("input.txt"))); - println!("Part1alt: {}", part1alt::main(include_str!("input.txt"))); - println!("Part2: TODO"); + println!("Part2: {}", part2::main(include_str!("input.txt"))); } diff --git a/day2/src/old1.rs b/day2/src/old1.rs new file mode 100644 index 0000000..35e0ccf --- /dev/null +++ b/day2/src/old1.rs @@ -0,0 +1,156 @@ +use regex::{Match, Regex}; + +pub struct Games { + games: Vec, +} + +impl Games { + fn new(input: &str, bag: Contents) -> Self { + Self { + games: input + .lines() + .map(|m| Game::new(m.to_owned(), bag.clone())) + .collect(), + } + } + fn valid_games(&self) -> Vec { + self.games + .iter() + .filter(|m| m.valid()) + .map(|m| m.id) + .collect() + } + fn valid_sum(&self) -> i32 { + self.valid_games().iter().sum() + } +} + +#[derive(Debug, Clone)] +pub struct Game { + bag: Contents, + id: i32, + draws: Vec, +} + +impl Game { + fn new(line: String, bag: Contents) -> Self { + Self { + bag: bag.clone(), + id: line + .split(':') + .next() + .unwrap() + .split(' ') + .last() + .expect("Must have game ID") + .parse::() + .expect("Game ID must be integer value"), + draws: Game::get_draws(line.split(':').last().unwrap()), + } + } + + fn get_draws(line: &str) -> Vec { + line.split(':') + .next() + .unwrap() + .split(';') + .map(|m| -> Contents { Contents::new(m) }) + .collect() + } + fn valid(&self) -> bool { + self.draws.iter().all(|m| self.bag.valid(m)) + } +} + +#[derive(Debug, Clone)] +pub struct Contents { + blue: i32, + green: i32, + red: i32, +} + +impl Contents { + fn new(line: &str) -> Self { + Self { + red: Self::get_red(line), + blue: Self::get_blue(line), + green: Self::get_green(line), + } + } + fn valid(self: &Contents, hand: &Contents) -> bool { + self.red.ge(&hand.red) & self.blue.ge(&hand.blue) & self.green.ge(&hand.green) + } + fn get_value(slice: &str) -> i32 { + Regex::new(r"(\d)+") + .unwrap() + .find(slice) + .unwrap() + .as_str() + .parse() + .unwrap() + } + fn get_slice<'a>(line: &'a str, rstring: &'a str) -> Option> { + Regex::new(rstring).unwrap().find(line) + } + fn get_red(line: &str) -> i32 { + let rstring = r"((\d)+ red)"; + match Self::get_slice(line, rstring) { + Some(slice) => Self::get_value(slice.as_str()), + None => 0, + } + } + fn get_blue(line: &str) -> i32 { + let rstring = r"((\d)+ blue)"; + match Self::get_slice(line, rstring) { + Some(slice) => Self::get_value(slice.as_str()), + None => 0, + } + } + fn get_green(line: &str) -> i32 { + let rstring = r"((\d)+ green)"; + match Self::get_slice(line, rstring) { + Some(slice) => Self::get_value(slice.as_str()), + None => 0, + } + } +} + +pub fn main(input: &str) -> i32 { + let bag = Contents { + red: 12, + green: 13, + blue: 14, + }; + let games = Games::new(input, bag); + games.valid_sum() +} + +#[cfg(test)] +mod tests { + use super::*; + const BAG: Contents = Contents { + red: 12, + green: 13, + blue: 14, + }; + const INPUT: &str = "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] + fn example() { + assert_eq!(main(INPUT), 8) + } + + #[test] + fn by_line() { + assert_eq!(Games::new(INPUT, BAG).valid_games(), vec![1, 2, 5]) + } + + #[test] + fn solution() { + assert_eq!(main(include_str!("input.txt")), 2476) + } +} diff --git a/day2/src/part1.rs b/day2/src/part1.rs index 19119c1..dcabb1a 100644 --- a/day2/src/part1.rs +++ b/day2/src/part1.rs @@ -1,152 +1,92 @@ -use regex::{Match, Regex}; +use regex::Regex; -struct Games { - games: Vec, +type Games = Vec; + +#[derive(Debug)] +pub struct Bag { + pub red: usize, + pub green: usize, + pub blue: usize, } -impl Games { - fn new(input: &str, bag: Contents) -> Self { - Self { - games: input - .lines() - .map(|m| Game::new(m.to_owned(), bag.clone())) - .collect(), - } - } - fn valid_games(&self) -> Vec { - self.games - .iter() - .filter(|m| m.valid()) - .map(|m| m.id) - .collect() - } - fn valid_sum(&self) -> i32 { - self.valid_games().iter().sum() - } -} - -#[derive(Debug, Clone)] -struct Game { - bag: Contents, - id: i32, - draws: Vec, +#[derive(Debug)] +pub struct Game { + pub id: usize, + pub rounds: Vec, } impl Game { - fn new(line: String, bag: Contents) -> Self { + pub fn new(input: &str) -> Self { + let (id, games) = input + .split_once(':') + .expect("All input lines follow this format"); Self { - bag: bag.clone(), - id: line - .split(':') - .next() - .unwrap() - .split(' ') - .last() - .expect("Must have game ID") - .parse::() - .expect("Game ID must be integer value"), - draws: Game::get_draws(line.split(':').last().unwrap()), + id: id.split(' ').last().unwrap().parse().unwrap(), + rounds: games.split(';').map(Round::new).collect(), } } - fn get_draws(line: &str) -> Vec { - line.split(':') - .next() - .unwrap() - .split(';') - .map(|m| -> Contents { Contents::new(m) }) - .collect() - } - fn valid(&self) -> bool { - self.draws.iter().all(|m| self.bag.valid(m)) + pub fn is_valid(&self, bag: &Bag) -> bool { + self.rounds.iter().all(|m| m.is_valid(bag)) } } -#[derive(Debug, Clone)] -struct Contents { - blue: i32, - green: i32, - red: i32, +#[derive(Debug)] +pub struct Round { + pub red: usize, + pub green: usize, + pub blue: usize, } -impl Contents { - fn new(line: &str) -> Self { +impl Round { + pub fn new(input: &str) -> Self { + fn get_colour(rstring: &str, input: &str) -> usize { + match Regex::new(rstring).unwrap().find(input) { + Some(val) => val.as_str().split(' ').next().unwrap().parse().unwrap(), + None => 0, + } + } + Self { - red: Self::get_red(line), - blue: Self::get_blue(line), - green: Self::get_green(line), + red: get_colour(r"\d+ (red)", input), + green: get_colour(r"\d+ (green)", input), + blue: get_colour(r"\d+ (blue)", input), } } - fn valid(self: &Contents, hand: &Contents) -> bool { - self.red.ge(&hand.red) & self.blue.ge(&hand.blue) & self.green.ge(&hand.green) - } - fn get_value(slice: &str) -> i32 { - Regex::new(r"(\d)+") - .unwrap() - .find(slice) - .unwrap() - .as_str() - .parse() - .unwrap() - } - fn get_slice<'a>(line: &'a str, rstring: &'a str) -> Option> { - Regex::new(rstring).unwrap().find(line) - } - fn get_red(line: &str) -> i32 { - let rstring = r"((\d)+ red)"; - match Self::get_slice(line, rstring) { - Some(slice) => Self::get_value(slice.as_str()), - None => 0, - } - } - fn get_blue(line: &str) -> i32 { - let rstring = r"((\d)+ blue)"; - match Self::get_slice(line, rstring) { - Some(slice) => Self::get_value(slice.as_str()), - None => 0, - } - } - fn get_green(line: &str) -> i32 { - let rstring = r"((\d)+ green)"; - match Self::get_slice(line, rstring) { - Some(slice) => Self::get_value(slice.as_str()), - None => 0, - } + + pub fn is_valid(&self, bag: &Bag) -> bool { + self.red <= bag.red && self.green <= bag.green && self.blue <= bag.blue } } -pub fn main(input: &str) -> i32 { - let bag = Contents { +pub fn games(input: &str) -> Games { + input.lines().map(Game::new).collect() +} + +pub fn main(input: &str) -> usize { + let bag = Bag { red: 12, green: 13, blue: 14, }; - let games = Games::new(input, bag); - games.valid_sum() + games(input) + .iter() + .filter_map(|m| if m.is_valid(&bag) { Some(m.id) } else { None }) + .sum() } #[cfg(test)] mod tests { use super::*; - const BAG: Contents = Contents { - red: 12, - green: 13, - blue: 14, - }; - const INPUT: &str = "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green + + #[test] + fn example() { + let input = "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] - fn example() { - assert_eq!(main(INPUT), 8) - } - - #[test] - fn by_line() { - assert_eq!(Games::new(INPUT, BAG).valid_games(), vec![1, 2, 5]) + assert_eq!(main(input), 8); } #[test] diff --git a/day2/src/part1alt.rs b/day2/src/part1alt.rs deleted file mode 100644 index 2a1df79..0000000 --- a/day2/src/part1alt.rs +++ /dev/null @@ -1,96 +0,0 @@ -use regex::Regex; - -// type Round<'a> = &'a str; -// type Game<'a> = Vec; - -#[derive(Debug)] -struct Bag { - red: usize, - green: usize, - blue: usize, -} - -#[derive(Debug)] -struct Game { - id: usize, - rounds: Vec, -} - -impl Game { - fn new(input: &str) -> Self { - let (id, games) = input - .split_once(':') - .expect("All input lines follow this format"); - Self { - id: id.split(' ').last().unwrap().parse().unwrap(), - rounds: games.split(';').map(Round::new).collect(), - } - } - - fn is_valid(&self, bag: &Bag) -> bool { - self.rounds.iter().all(|m| m.is_valid(bag)) - } -} - -#[derive(Debug)] -struct Round { - red: usize, - green: usize, - blue: usize, -} - -impl Round { - fn new(input: &str) -> Self { - fn get_colour(rstring: &str, input: &str) -> usize { - match Regex::new(rstring).unwrap().find(input) { - Some(val) => val.as_str().split(' ').next().unwrap().parse().unwrap(), - None => 0, - } - } - - Self { - red: get_colour(r"\d+ (red)", input), - green: get_colour(r"\d+ (green)", input), - blue: get_colour(r"\d+ (blue)", input), - } - } - - fn is_valid(&self, bag: &Bag) -> bool { - self.red <= bag.red && self.green <= bag.green && self.blue <= bag.blue - } -} - -pub fn main(input: &str) -> usize { - let bag = Bag { - red: 12, - green: 13, - blue: 14, - }; - let games: Vec = input.lines().map(Game::new).collect(); - // dbg!(&games); - // dbg!(&games.iter().map(|m| m.is_valid(&bag))); - games - .iter() - .filter_map(|m| if m.is_valid(&bag) { Some(m.id) } else { None }) - .sum() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn example() { - let input = "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"; - assert_eq!(main(input), 8); - } - - #[test] - fn solution() { - assert_eq!(main(include_str!("input.txt")), 2476) - } -} diff --git a/day2/src/part2.rs b/day2/src/part2.rs new file mode 100644 index 0000000..2f7579d --- /dev/null +++ b/day2/src/part2.rs @@ -0,0 +1,53 @@ +use crate::part1::{games, Bag, Game}; + +impl Game { + pub fn min_bag(&self) -> Bag { + Bag { + red: self.rounds.iter().map(|m| m.red).max().unwrap(), + green: self.rounds.iter().map(|m| m.green).max().unwrap(), + blue: self.rounds.iter().map(|m| m.blue).max().unwrap(), + } + } + + pub fn product(&self) -> usize { + let bag = self.min_bag(); + bag.red * bag.green * bag.blue + } +} + +pub fn products(input: &str) -> Vec { + games(input) + .iter() + .map(Game::product) + .collect::>() +} + +pub fn main(input: &str) -> usize { + products(input).iter().sum() +} + +#[cfg(test)] +mod tests { + use super::*; + + const INPUT: &str = "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] + fn example() { + assert_eq!(main(INPUT), 2286); + } + + #[test] + fn by_line() { + assert_eq!(products(INPUT), vec![48, 12, 1560, 630, 36]) + } + + #[test] + fn solution() { + assert_eq!(main(include_str!("input.txt")), 54911) + } +}