use std::convert::TryInto; const INPUT: &str = include_str!("../../data/day2"); struct Policy { min: usize, max: usize, chr: char, } impl Policy { fn parse(text: &str) -> Self { let tokens: Vec<_> = text.trim().split_whitespace().collect(); let [range, chr]: [&str; 2] = tokens.try_into().unwrap(); let tokens: Vec<_> = range.split("-").collect(); let [min, max]: [&str; 2] = tokens.try_into().unwrap(); assert!(chr.len() == 1); let chr = chr.chars().next().unwrap(); let min = min.parse::().unwrap(); let max = max.parse::().unwrap(); Self { min, max, chr } } fn test(&self, input: &str) -> bool { let count = input.chars().filter(|&c| c == self.chr).count(); count >= self.min && count <= self.max } fn test_crazy(&self, input: &str) -> bool { let chars: Vec<_> = input.chars().collect(); assert!(self.min > 0 && self.max > 0); assert!(self.min <= chars.len() && self.max <= chars.len()); (chars[self.min-1] == self.chr) != (chars[self.max-1] == self.chr) } } struct Entry { policy: Policy, password: String, } impl Entry { fn parse(line: &str) -> Self { let tokens: Vec<_> = line.split(":").collect(); let [policy, password]: [&str; 2] = tokens.try_into().unwrap(); Self { policy: Policy::parse(policy), password: password.trim().into(), } } fn parse_list(input: &str) -> Vec { input.lines().map(Self::parse).collect() } fn valid(&self) -> bool { self.policy.test(&self.password) } fn valid_crazy(&self) -> bool { self.policy.test_crazy(&self.password) } } fn main() { let entries = Entry::parse_list(INPUT); println!("Valid entries: {}", entries.iter().filter(|e| e.valid()).count()); println!("Crazy valid entries: {}", entries.iter().filter(|e| e.valid_crazy()).count()); }