69 lines
1.7 KiB
Rust
69 lines
1.7 KiB
Rust
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::<usize>().unwrap();
|
|
let max = max.parse::<usize>().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<Self> {
|
|
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());
|
|
} |