🎄
Advent of Code 2020 day04
part1
ecl:gry pid:860033327 eyr:2020 hcl:#fffffd
byr:1937 iyr:2017 cid:147 hgt:183cm
iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884
hcl:#cfa07d byr:1929
hcl:#ae17e1 iyr:2013
eyr:2024
ecl:brn pid:760753108 byr:1931
hgt:179cm
hcl:#cfa07d eyr:2025 pid:166559648
iyr:2011 ecl:brn hgt:59in
のような入力が与えられる。空行区切りでひとまとめのdataで、各dataはspaceまたは改行で区切られたfieldを持ち、各fieldは:
で区切られたkey-valueとなる。
まずは cid
だけがoptionalなので欠損していてもよく、それ以外はrequiredとしたときにvalidな件数は幾つか、という問題。
ただその条件の通りにparseしてcountするだけ。
入力を丸ごと読み込んでから \n\n
でsplit
してやればdata列挙しやすかったかもしれないけど、1行ずつ読み込んだものを扱う方針でやっていたので空行が来たら判定、とした。最後のdataを無視してしまわないように iter().chain([String::new()]).iter())
ってやったけどなんかカッコ悪いかな…
判定はkeyの存在だけ確認すれば良いので HashSet
に入れて「8つ揃っている」もしくは「7つだけ揃っていて cid
が含まれていない」で判定。
use std::collections::HashMap;
struct Solution {
inputs: Vec<String>,
}
impl Solution {
fn new(inputs: Vec<String>) -> Self {
Self { inputs }
}
fn solve_1(&self) -> usize {
let mut ret = 0;
let mut keys: HashSet<String> = HashSet::new();
for line in self.inputs.iter().chain([String::new()].iter()) {
if line.is_empty() {
if keys.len() == 8 || (keys.len() == 7 && !keys.contains("cid")) {
ret += 1;
}
keys.clear();
} else {
keys.extend(
line.split(' ')
.filter_map(|field| field.split(':').next())
.map(|key| key.to_string()),
);
}
}
ret
}
}
part2
今度はkeyの存在確認だけではなく、valueも正しい値であることを確認する必要がある。ひたすらその条件の通りに実装していくだけ。
part1ではHashSet
にkeyだけを入れていたが、今度はvalueも見る必要があるのでHashMap
に変更。valueを判定するかどうかで分岐を入れて、判定する場合はすべてのfieldについてそれぞれ条件に当てはまっているかを確認。iter().all(...)
でtrue
になれば良い。
lazy_static
って使ったことなくてよく分かっていない…
use lazy_static::lazy_static;
use regex::Regex;
use std::collections::HashMap;
impl Solution {
fn new(inputs: Vec<String>) -> Self {
Self { inputs }
}
fn solve_1(&self) -> usize {
self.count_valid(false)
}
fn solve_2(&self) -> usize {
self.count_valid(true)
}
fn count_valid(&self, validate_value: bool) -> usize {
let mut ret = 0;
let mut fields: HashMap<String, String> = HashMap::new();
for line in self.inputs.iter().chain([String::new()].iter()) {
if line.is_empty() {
if (fields.len() == 8 || (fields.len() == 7 && !fields.contains_key("cid")))
&& (!validate_value || self.validate_values(&fields))
{
ret += 1;
}
fields.clear();
} else {
fields.extend(line.split(' ').map(|field| {
let v: Vec<&str> = field.split(':').collect();
(v[0].to_string(), v[1].to_string())
}));
}
}
ret
}
fn validate_values(&self, fields: &HashMap<String, String>) -> bool {
fields.iter().all(|(key, value)| match key.as_str() {
"byr" => {
if let Ok(y) = value.parse::<i32>() {
1920 <= y && y <= 2002
} else {
false
}
}
"iyr" => {
if let Ok(y) = value.parse::<i32>() {
2010 <= y && y <= 2020
} else {
false
}
}
"eyr" => {
if let Ok(y) = value.parse::<i32>() {
2020 <= y && y <= 2030
} else {
false
}
}
"hgt" => {
lazy_static! {
static ref RE: Regex = Regex::new(r"^(\d+)(cm|in)$").unwrap();
}
if let Some(cap) = RE.captures_iter(value).next() {
let n: i32 = cap[1].parse::<i32>().unwrap();
match &cap[2] {
"cm" => 150 <= n && n <= 193,
"in" => 59 <= n && n <= 76,
_ => false,
}
} else {
false
}
}
"hcl" => {
lazy_static! {
static ref RE: Regex = Regex::new(r"^#[0-9a-f]{6}$").unwrap();
}
RE.is_match(value)
}
"ecl" => matches!(
value.as_str(),
"amb" | "blu" | "brn" | "gry" | "grn" | "hzl" | "oth"
),
"pid" => value.len() == 9 && value.chars().all(|c| c.is_numeric()),
"cid" => true,
_ => false,
})
}
}
Discussion