diff options
Diffstat (limited to '2020/src/bin/day_4.rs')
-rw-r--r-- | 2020/src/bin/day_4.rs | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/2020/src/bin/day_4.rs b/2020/src/bin/day_4.rs new file mode 100644 index 0000000..50b8eb5 --- /dev/null +++ b/2020/src/bin/day_4.rs @@ -0,0 +1,222 @@ +use bevy::prelude::*; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::str::FromStr; +use thiserror::Error; + +fn main() { + App::build() + .add_resource(WindowDescriptor { + title: "Advent of Code".to_string(), + width: 1920, + height: 1080, + ..Default::default() + }) + .add_resource(ClearColor(Color::rgb(0., 0., 0.))) + .add_startup_system(setup_camera.system()) + .add_startup_system(read_input_file.system()) + .add_system(count_valid.system()) + //.add_plugins(DefaultPlugins) + .run(); +} + +fn setup_camera(mut commands: Commands) { + commands.spawn(Camera2dComponents::default()); +} + +#[derive(Error, Debug)] +enum ValidationError { + #[error("{0}")] + FromIntError(#[from] std::num::ParseIntError), + #[error("value is out of range")] + OutOfRange, + #[error("value has an invalid unit")] + InvalidUnit, + #[error("value has an invalid characters")] + InvalidChars, +} + +struct BirthYear(u32); +impl FromStr for BirthYear { + type Err = ValidationError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let num: u32 = s.parse()?; + if num >= 1920 && num <= 2002 { + Ok(Self(num)) + } else { + Err(ValidationError::OutOfRange) + } + } +} + +struct IssueYear(u32); +impl FromStr for IssueYear { + type Err = ValidationError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let num: u32 = s.parse()?; + if num >= 2010 && num <= 2020 { + Ok(Self(num)) + } else { + Err(ValidationError::OutOfRange) + } + } +} + +struct ExpirationYear(u32); +impl FromStr for ExpirationYear { + type Err = ValidationError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let num: u32 = s.parse()?; + if num >= 2020 && num <= 2030 { + Ok(Self(num)) + } else { + Err(ValidationError::OutOfRange) + } + } +} + +struct Height(u32); +impl FromStr for Height { + type Err = ValidationError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let inches = s.ends_with("in"); + let centimeters = s.ends_with("cm"); + if !inches && !centimeters { + return Err(ValidationError::InvalidUnit); + } + + let num: u32 = s[0..s.len() - 2].parse()?; + let num_cm = if inches { + ((num as f32) * 2.54).round() as u32 + } else { + num + }; + + if num_cm >= 150 && num_cm <= 193 { + Ok(Self(num_cm)) + } else { + Err(ValidationError::OutOfRange) + } + } +} + +struct HairColor(String); +impl FromStr for HairColor { + type Err = ValidationError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + if !s.starts_with("#") { + return Err(ValidationError::InvalidChars); + } + if s.len() != 7 { + return Err(ValidationError::InvalidChars); + } + if !s + .chars() + .skip(1) + .all(|c| (c >= 'a' && c <= 'f') || c >= '0' && c <= '9') + { + return Err(ValidationError::InvalidChars); + } + + Ok(Self(s.to_string())) + } +} + +struct EyeColor(String); +impl FromStr for EyeColor { + type Err = ValidationError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let valid_vals = ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]; + if valid_vals.contains(&s) { + Ok(Self(s.to_string())) + } else { + Err(ValidationError::InvalidChars) + } + } +} + +struct PassportId(String); +impl FromStr for PassportId { + type Err = ValidationError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + if s.len() != 9 { + return Err(ValidationError::InvalidChars); + } + if !s.chars().all(|c| c.is_ascii_digit()) { + return Err(ValidationError::InvalidChars); + } + + Ok(Self(s.to_string())) + } +} + +struct CountryId(String); +impl FromStr for CountryId { + type Err = ValidationError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok(Self(s.to_string())) + } +} + +fn read_input_file(mut commands: Commands) { + let f = File::open("./inputs/day_4.txt").expect("Failed to read file"); + + commands.spawn(()); + for line in BufReader::new(f).lines() { + let line = line.unwrap(); + let line = line.trim(); + if line.is_empty() { + commands.spawn(()); + } else { + for section in line.split_whitespace() { + let mut section_parts = section.split(':'); + let name = section_parts.next().unwrap(); + let value = section_parts.next().unwrap(); + match name { + "byr" => { + BirthYear::from_str(value).ok().map(|c| commands.with(c)); + } + "iyr" => { + IssueYear::from_str(value).ok().map(|c| commands.with(c)); + } + "eyr" => { + ExpirationYear::from_str(value) + .ok() + .map(|c| commands.with(c)); + } + "hgt" => { + Height::from_str(value).ok().map(|c| commands.with(c)); + } + "hcl" => { + HairColor::from_str(value).ok().map(|c| commands.with(c)); + } + "ecl" => { + EyeColor::from_str(value).ok().map(|c| commands.with(c)); + } + "pid" => { + PassportId::from_str(value).ok().map(|c| commands.with(c)); + } + "cid" => { + CountryId::from_str(value).ok().map(|c| commands.with(c)); + } + _ => {} + } + } + } + } +} + +fn count_valid( + passports: Query<( + &BirthYear, + &IssueYear, + &ExpirationYear, + &Height, + &HairColor, + &EyeColor, + &PassportId, + )>, +) { + let count = passports.iter().count(); + println!("There are {} valid passports", count); +} |