summaryrefslogtreecommitdiff
path: root/2020/src/bin/day_4.rs
diff options
context:
space:
mode:
Diffstat (limited to '2020/src/bin/day_4.rs')
-rw-r--r--2020/src/bin/day_4.rs222
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);
+}