1
0
Fork 0

Compare commits

...

2 Commits

Author SHA1 Message Date
chylex bac7d97b9a
Add 2020 - Day 4 - Part 2 2022-02-23 14:20:40 +01:00
chylex aaec7168fb
Add 2020 - Day 4 - Part 1 2022-02-23 14:20:32 +01:00
7 changed files with 1321 additions and 0 deletions

View File

@ -0,0 +1,18 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="2020 - Day 04" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="run --bin 04" />
<option name="workingDirectory" value="file://$PROJECT_DIR$/2020/04" />
<option name="channel" value="DEFAULT" />
<option name="requiredFeatures" value="true" />
<option name="allFeatures" value="false" />
<option name="emulateTerminal" value="false" />
<option name="withSudo" value="false" />
<option name="backtrace" value="SHORT" />
<envs />
<option name="isRedirectInput" value="false" />
<option name="redirectInputPath" value="" />
<method v="2">
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
</component>

1146
2020/04/input/1.txt Normal file

File diff suppressed because it is too large Load Diff

139
2020/04/main.rs Normal file
View File

@ -0,0 +1,139 @@
use std::collections::{HashMap, HashSet};
use std::error::Error;
use lazy_static::lazy_static;
use crate::utils::GenericError;
#[path = "../utils/mod.rs"]
mod utils;
fn main() -> Result<(), Box<dyn Error>> {
let lines = utils::read_input_lines()?;
let passports = load_passports(&lines)?;
let passports = passports.into_iter().filter(|p| p.is_valid_or_from_north_pole()).collect::<Vec<Passport>>();
println!("Valid passports with no field validation: {}", passports.len());
println!("Valid passports with field validation: {}", passports.iter().filter(|p| p.are_field_values_valid()).count());
Ok(())
}
fn load_passports(lines: &Vec<String>) -> Result<Vec<Passport>, GenericError> {
let mut passports = Vec::new();
let mut passport = Passport::new();
for line in lines {
if line.is_empty() {
passports.push(passport);
passport = Passport::new();
} else {
passport.load_fields_from_line(line.as_str())?;
}
}
passports.push(passport);
Ok(passports)
}
#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone)]
enum PassportField {
BirthYear,
IssueYear,
ExpirationYear,
Height,
HairColor,
EyeColor,
PassportId,
CountryId,
}
impl PassportField {
fn from(s: &str) -> Option<PassportField> {
match s {
"byr" => Some(PassportField::BirthYear),
"iyr" => Some(PassportField::IssueYear),
"eyr" => Some(PassportField::ExpirationYear),
"hgt" => Some(PassportField::Height),
"hcl" => Some(PassportField::HairColor),
"ecl" => Some(PassportField::EyeColor),
"pid" => Some(PassportField::PassportId),
"cid" => Some(PassportField::CountryId),
_ => None
}
}
fn is_value_valid(&self, value: &str) -> bool {
fn as_u32(value: &str) -> Option<u32> {
value.parse().ok()
}
fn as_u32_with_unit(value: &str, unit: &str) -> Option<u32> {
value.strip_suffix(unit).and_then(as_u32)
}
match self {
PassportField::BirthYear => as_u32(value).filter(|year| *year >= 1920 && *year <= 2002).is_some(),
PassportField::IssueYear => as_u32(value).filter(|year| *year >= 2010 && *year <= 2020).is_some(),
PassportField::ExpirationYear => as_u32(value).filter(|year| *year >= 2020 && *year <= 2030).is_some(),
PassportField::Height => {
if let Some(height) = as_u32_with_unit(value, "cm") {
height >= 150 && height <= 193
} else if let Some(height) = as_u32_with_unit(value, "in") {
height >= 59 && height <= 76
} else {
false
}
}
PassportField::HairColor => value.strip_prefix('#').filter(|hex| hex.chars().all(|c| c.is_digit(16))).is_some(),
PassportField::EyeColor => VALID_EYE_COLORS.contains(value),
PassportField::PassportId => value.len() == 9 && value.chars().all(|c| c.is_ascii_digit()),
PassportField::CountryId => true
}
}
}
struct Passport {
fields: HashMap<PassportField, String>,
}
impl Passport {
fn new() -> Passport {
Passport { fields: HashMap::new() }
}
fn load_fields_from_line(&mut self, line: &str) -> Result<(), GenericError> {
for field_entry in line.split(' ') {
let (field_name, field_value) = field_entry.split_once(':').ok_or_else(|| GenericError::new("Passport entry is missing a colon."))?;
let field = PassportField::from(field_name).ok_or_else(|| GenericError::new(format!("Passport field is invalid: {}", field_name)))?;
self.fields.insert(field, field_value.to_string());
}
Ok(())
}
fn is_valid_or_from_north_pole(&self) -> bool {
let fields = &self.fields.keys().map(|f| *f).collect::<HashSet<PassportField>>();
return fields.is_superset(&REQUIRED_FIELDS);
}
fn are_field_values_valid(&self) -> bool {
return self.fields.iter().all(|(field, value)| field.is_value_valid(value.as_str()));
}
}
lazy_static! {
static ref REQUIRED_FIELDS: HashSet<PassportField> = HashSet::from([
PassportField::BirthYear,
PassportField::IssueYear,
PassportField::ExpirationYear,
PassportField::Height,
PassportField::HairColor,
PassportField::EyeColor,
PassportField::PassportId
]);
static ref VALID_EYE_COLORS: HashSet<&'static str> = HashSet::from([
"amb", "blu", "brn", "gry", "grn", "hzl", "oth"
]);
}

9
2020/Cargo.lock generated
View File

@ -5,3 +5,12 @@ version = 3
[[package]]
name = "chylex-aoc-2020"
version = "0.1.0"
dependencies = [
"lazy_static",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

View File

@ -3,6 +3,9 @@ name = "chylex-aoc-2020"
version = "0.1.0"
edition = "2021"
[dependencies]
lazy_static = "1.4.0"
[[bin]]
name = "01"
path = "01/main.rs"
@ -14,3 +17,7 @@ path = "02/main.rs"
[[bin]]
name = "03"
path = "03/main.rs"
[[bin]]
name = "04"
path = "04/main.rs"

View File

@ -10,6 +10,7 @@ pub fn read_input_lines() -> Result<Vec<String>, io::Error> {
return BufReader::new(file).lines().collect();
}
#[allow(dead_code)]
pub fn parse_input_lines<T : FromStr>() -> Result<Vec<T>, Box<dyn Error>> where <T as FromStr>::Err : Into<Box<dyn Error>> {
return read_input_lines()?.iter().map(|line| line.parse::<T>()).collect::<Result<Vec<T>, T::Err>>().map_err(Into::into);
}

View File

@ -40,6 +40,7 @@ The versions should not matter, but I used Visual Studio 2019 with `MSVC v142 (1
| 2020 | 01 | Rust |
| 2020 | 02 | Rust |
| 2020 | 03 | Rust |
| 2020 | 04 | Rust |
| 2021 | 01 | Kotlin |
| 2021 | 02 | Kotlin |
| 2021 | 03 | Kotlin |