1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
use anyhow::{Result, anyhow};
use itertools::Itertools;
use std::env::args;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::str::FromStr;
#[derive(Debug)]
struct PasswordPolicy {
character: char,
min: usize,
max: usize,
}
impl FromStr for PasswordPolicy {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(' ');
let range = parts.next().ok_or_else(|| anyhow!("Could not find range: {}", s))?;
let character = parts.next().ok_or_else(|| anyhow!("Could not find char: {}", s))?
.chars().next().ok_or_else(|| anyhow!("Coult not parse char: {}", s))?;
let ( min, max ) = match range.split('-')
.map(|v| v.parse::<usize>()).collect::<Result<Vec<usize>, _>>()
{
Ok(vals) => {
vals.into_iter().collect_tuple().ok_or_else(|| anyhow!("Invalid range"))?
},
_ => return Err(anyhow!("Couldn't parse {}", s)),
};
Ok(PasswordPolicy { min, max, character })
}
}
impl From<&str> for PasswordPolicy {
fn from(s: &str) -> Self {
PasswordPolicy::from_str(s).expect(s)
}
}
fn read_input<T: AsRef<Path>>(filename: T) -> Result<Vec<(PasswordPolicy, String)>> {
let f = File::open(filename)?;
let reader = BufReader::new(f);
reader.lines().map(|l| {
let line = l?;
let mut s = line.split(':');
let policy = From::from(s.next().ok_or_else(|| anyhow!("Could not parse policy"))?.trim());
let password = From::from(s.next()
.ok_or_else(|| anyhow!("Could not parse password"))?.trim());
Ok(( policy, password ))
}).collect()
}
fn part1(input: &[(PasswordPolicy, String)]) -> usize {
input.iter().filter(|row| {
let ( policy, password ) = row;
let occurances = password.chars().filter(|c| *c == policy.character).count();
occurances >= policy.min && occurances <= policy.max
}).count()
}
fn part2(input: &[(PasswordPolicy, String)]) -> usize {
input.iter().filter(|row| {
let ( policy, password ) = row;
let first = password.chars().nth(policy.min - 1);
let second = password.chars().nth(policy.max - 1);
first != second && (first == Some(policy.character) || second == Some(policy.character))
}).count()
}
fn main() {
let ( do_part_1, do_part_2 ) = aoc::do_parts();
let filename = match args().nth(1) {
Some(f) => f,
None => {
eprintln!("Missing input filename");
std::process::exit(1);
},
};
match read_input(filename) {
Ok(input) => {
if do_part_1 {
let solution = part1(&input);
println!("Part1, product found to be: {}", solution);
}
if do_part_2 {
let solution = part2(&input);
println!("Part2, product found to be: {}", solution);
}
},
Err(err) => eprintln!("Could not read input: {}", err),
}
}
|