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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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); }