Skip to content

Instantly share code, notes, and snippets.

@JCBurnside
Created December 5, 2020 00:44
Show Gist options
  • Save JCBurnside/e262f08a843956db09e4766b7e8f4b49 to your computer and use it in GitHub Desktop.
Save JCBurnside/e262f08a843956db09e4766b7e8f4b49 to your computer and use it in GitHub Desktop.
day 4 of AoC2020
use lazy_static::lazy_static;
use parse_display::{Display, FromStr};
use std::str::FromStr;
#[derive(FromStr, Debug, Copy, Clone)]
#[from_str(regex = "(?P<0>\\d+)(in|cm){1}")]
pub enum Height {
#[from_str(regex = "(?P<0>\\d+)cm")]
cm(u8),
#[from_str(regex = "(?P<0>\\d+)in")]
inch(u8),
}
#[derive(Debug)]
pub struct DataVec(pub Vec<DataP1>);
#[derive(Debug, Display)]
#[display("")]
pub struct VectorParseErr;
impl std::error::Error for VectorParseErr {}
impl FromStr for DataVec {
type Err = VectorParseErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut output = DataVec(Vec::new());
let mut current_data = DataP1::default();
for line in s.lines() {
if line.is_empty() {
output.0.push(current_data);
current_data = DataP1::default();
continue;
}
for word in line.split(' ') {
for cap in regex::Regex::new("(?P<name>[a-z]{3}):(?P<value>.+)")
.unwrap()
.captures_iter(word)
{
match &cap["name"] {
"byr" => {
if cap["value"].is_empty() {
return Err(VectorParseErr);
}
current_data.byr = Some(String::from(&cap["value"]));
}
"iyr" => {
if cap["value"].is_empty() {
return Err(VectorParseErr);
}
current_data.iyr = Some(String::from(&cap["value"]));
}
"eyr" => {
if cap["value"].is_empty() {
return Err(VectorParseErr);
}
current_data.eyr = Some(String::from(&cap["value"]));
}
"hgt" => {
if cap["value"].is_empty() {
return Err(VectorParseErr);
}
current_data.hgt = Some(String::from(&cap["value"]));
}
"hcl" => {
if cap["value"].is_empty() {
return Err(VectorParseErr);
}
current_data.hcl = Some(String::from(&cap["value"]));
}
"ecl" => {
if cap["value"].is_empty() {
return Err(VectorParseErr);
}
current_data.ecl = Some(String::from(&cap["value"]));
}
"pid" => {
if cap["value"].is_empty() {
return Err(VectorParseErr);
}
current_data.pid = Some(String::from(&cap["value"]));
}
"cid" => {
if cap["value"].is_empty() {
return Err(VectorParseErr);
}
current_data.cid = Some(String::from(&cap["value"]));
}
_ => return Err(VectorParseErr),
}
}
}
}
output.0.push(current_data);
Ok(output)
}
}
#[derive(Default, Debug)]
pub struct DataP1 {
pub byr: Option<String>,
pub iyr: Option<String>,
pub eyr: Option<String>,
pub hgt: Option<String>,
pub hcl: Option<String>,
pub ecl: Option<String>,
pub pid: Option<String>,
pub cid: Option<String>,
}
impl DataP1 {
pub fn is_valid(&self) -> bool {
self.byr.is_some()
&& self.iyr.is_some()
&& self.eyr.is_some()
&& self.hgt.is_some()
&& self.hcl.is_some()
&& self.ecl.is_some()
&& self.pid.is_some()
// && self.cid.is_some()
}
}
#[derive(Display, FromStr, Debug, Clone, Copy)]
#[display(style = "snake_case")]
pub enum EyeColor {
amb,
blu,
brn,
gry,
grn,
hzl,
oth,
}
#[derive(Default, Debug)]
pub struct DataP2 {
pub byr: Option<u16>,
pub iyr: Option<u16>,
pub eyr: Option<u16>,
pub hgt: Option<Height>,
pub hcl: Option<String>,
pub ecl: Option<EyeColor>,
pub pid: Option<String>,
pub cid: Option<String>,
}
impl From<&DataP1> for DataP2 {
fn from(data: &DataP1) -> Self {
lazy_static! {
static ref HCL_RE: regex::Regex = regex::Regex::new("#[0-9a-f]{6}").unwrap();
}
let mut output = DataP2::default();
output.byr = match &data.byr {
Some(s) => {
if let Ok(u) = s.parse() {
if u >= 1920 && u <= 2002 {
Some(u)
} else {
None
}
} else {
None
}
}
_ => None,
};
output.iyr = match &data.iyr {
Some(s) => {
if let Ok(u) = s.parse() {
if u >= 2010 && u <= 2020 {
Some(u)
} else {
None
}
} else {
None
}
}
_ => None,
};
output.eyr = match &data.eyr {
Some(s) => {
if let Ok(u) = s.parse() {
if u >= 2020 && u <= 2030 {
Some(u)
} else {
None
}
} else {
None
}
}
_ => None,
};
output.hgt = match &data.hgt {
Some(s) => match s.parse() {
Ok(Height::inch(u)) => {
if u >= 59 && u <= 76 {
Some(Height::inch(u))
} else {
None
}
}
Ok(Height::cm(u)) => {
if u >= 150 && u <= 193 {
Some(Height::cm(u))
} else {
None
}
}
_ => None,
},
_ => None,
};
output.hcl = match &data.hcl {
Some(s) => {
if HCL_RE.is_match(s) {
Some(s.clone())
} else {
None
}
}
_ => None,
};
output.ecl = match &data.ecl {
Some(s) => match s.parse() {
Ok(c) => Some(c),
_ => None,
},
_ => None,
};
output.pid = match &data.pid {
Some(s) => {
if s.len() == 9 {
Some(s.clone())
} else {
None
}
}
_ => None,
};
output
}
}
impl DataP2 {
pub fn is_valid(&self) -> bool {
self.byr.is_some()
&& self.iyr.is_some()
&& self.eyr.is_some()
&& self.hgt.is_some()
&& self.hcl.is_some()
&& self.ecl.is_some()
&& self.pid.is_some()
}
}
mod data;
use data::{DataP1, DataP2, DataVec};
use std::str::FromStr;
fn part1(data: &[DataP1]) -> usize {
let count = data.iter().filter(|it| it.is_valid()).count();
println!("correct lenght {}", count);
count
}
fn part2(data: &[DataP2]) -> usize {
let count = data.iter().filter(|it| it.is_valid()).count();
println!("validated :{}", count);
count
}
fn main() -> anyhow::Result<()> {
let data: DataVec = DataVec::from_str(std::fs::read_to_string("data.txt")?.as_str())?;
println!("{:?}", data.0.first());
part1(&data.0);
let data: Vec<DataP2> = data.0.iter().map(|it| it.into()).collect();
part2(&data);
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment