123 lines
2.8 KiB
Rust
123 lines
2.8 KiB
Rust
use std::io::BufRead;
|
|
use std::str::FromStr;
|
|
use std::num::ParseIntError;
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
struct Policy {
|
|
letter: char,
|
|
first: usize,
|
|
second: usize,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
struct Entry {
|
|
policy: Policy,
|
|
password: String
|
|
}
|
|
|
|
impl FromStr for Policy {
|
|
type Err = ParseIntError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let policy: Vec<_> = s.split(' ').collect();
|
|
let letter = policy[1].parse().unwrap();
|
|
let count: Vec<_> = policy[0].split('-').collect();
|
|
|
|
Ok(Policy {
|
|
letter,
|
|
first: count[0].parse()?,
|
|
second: count[1].parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
impl FromStr for Entry {
|
|
type Err = ParseIntError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let entry: Vec<_> = s.split(':').collect();
|
|
Ok(Entry {
|
|
policy: entry[0].parse()?,
|
|
password: entry[1].trim().into(),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Entry {
|
|
fn is_valid_first(&self) -> bool {
|
|
let count = self.password.chars()
|
|
.filter(|c| *c == self.policy.letter)
|
|
.count();
|
|
self.policy.first <= count && count <= self.policy.second
|
|
}
|
|
|
|
fn is_valid_second(&self) -> bool {
|
|
(
|
|
self.password.chars().nth(self.policy.first - 1).unwrap() == self.policy.letter
|
|
) ^ (
|
|
self.password.chars().nth(self.policy.second - 1).unwrap() == self.policy.letter
|
|
)
|
|
}
|
|
}
|
|
|
|
|
|
fn count_valid_first<F: BufRead> (input: F) -> usize {
|
|
input.lines()
|
|
.filter_map(|line| line.unwrap().parse::<Entry>().ok())
|
|
.filter(|entry| entry.is_valid_first())
|
|
.count()
|
|
}
|
|
|
|
fn count_valid_second<F: BufRead> (input: F) -> usize {
|
|
input.lines()
|
|
.filter_map(|line| line.unwrap().parse::<Entry>().ok())
|
|
.filter(|entry| entry.is_valid_second())
|
|
.count()
|
|
}
|
|
|
|
|
|
pub fn part1<F: BufRead> (input: F) {
|
|
println!("{}", count_valid_first(input))
|
|
}
|
|
|
|
pub fn part2<F: BufRead> (input: F) {
|
|
println!("{}", count_valid_second(input))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
#[test]
|
|
pub fn test_parse() {
|
|
let res: Entry = "10-16 c: ccccqccchcccccjlc".parse().unwrap();
|
|
let expected = Entry {
|
|
password: "ccccqccchcccccjlc".into(),
|
|
policy: Policy {
|
|
first: 10,
|
|
second: 16,
|
|
letter: 'c'
|
|
}
|
|
};
|
|
assert_eq!(res, expected)
|
|
}
|
|
|
|
#[test]
|
|
pub fn test_count_valid_first() {
|
|
let input = r#"1-3 a: abcde
|
|
1-3 b: cdefg
|
|
2-9 c: ccccccccc
|
|
"#.as_bytes();
|
|
assert_eq!(count_valid_first(input), 2)
|
|
}
|
|
|
|
#[test]
|
|
pub fn test_count_valid_second() {
|
|
let input = r#"1-3 a: abcde
|
|
1-3 b: cdefg
|
|
2-9 c: ccccccccc
|
|
"#.as_bytes();
|
|
assert_eq!(count_valid_second(input), 1)
|
|
}
|
|
}
|