advent-of-code/aoc2020/src/day02.rs

148 lines
3.6 KiB
Rust
Raw Normal View History

2020-12-02 12:50:50 +01:00
use std::fmt::Write;
use std::str::FromStr;
2020-12-14 18:08:16 +01:00
use anyhow::{Context, Result};
2020-12-02 12:50:50 +01:00
const INPUT: &str = include_str!("../input/day02.txt");
#[derive(Debug)]
struct PassPolicy {
2020-12-02 13:23:23 +01:00
min_bound: usize,
max_bound: usize,
2020-12-02 12:50:50 +01:00
letter: u8,
password: String,
}
impl PassPolicy {
2020-12-02 13:23:23 +01:00
fn new(min_bound: usize, max_bound: usize, letter: u8, password: String) -> Self {
2020-12-02 12:50:50 +01:00
Self {
2020-12-02 13:23:23 +01:00
min_bound,
max_bound,
2020-12-02 12:50:50 +01:00
letter,
password,
}
}
#[allow(clippy::naive_bytecount)] // let's not pull a crate just for that
2020-12-02 13:23:23 +01:00
fn is_valid_part1(&self) -> bool {
2020-12-02 12:50:50 +01:00
let occurrences = self
.password
.as_bytes()
.iter()
.filter(|&&b| b == self.letter)
.count();
2020-12-02 13:23:23 +01:00
let range = self.min_bound..=self.max_bound;
range.contains(&occurrences)
}
fn is_valid_part2(&self) -> bool {
let bytes = self.password.as_bytes();
(bytes[self.min_bound - 1] == self.letter) ^ (bytes[self.max_bound - 1] == self.letter)
2020-12-02 12:50:50 +01:00
}
}
impl FromStr for PassPolicy {
2020-12-14 18:08:16 +01:00
type Err = anyhow::Error;
2020-12-02 12:50:50 +01:00
fn from_str(s: &str) -> Result<Self> {
let s = s.trim_end();
let space = s
.find(' ')
2020-12-14 18:08:16 +01:00
.context("couldn't parse password policy: didn't find space")?;
2020-12-02 12:50:50 +01:00
let dash = s
.find('-')
2020-12-14 18:08:16 +01:00
.context("couldn't parse password policy: didn't find dash")?;
2020-12-02 12:50:50 +01:00
let min_bound = s[..dash]
.parse::<usize>()
2020-12-14 18:08:16 +01:00
.context("couldn't parse range")?;
2020-12-02 12:50:50 +01:00
let max_bound = s[(dash + 1)..space]
.parse::<usize>()
2020-12-14 18:08:16 +01:00
.context("couldn't parse range")?;
2020-12-02 12:50:50 +01:00
let colon = s
.find(':')
2020-12-14 18:08:16 +01:00
.context("couldn't parse password policy: didn't find colon")?;
2020-12-02 12:50:50 +01:00
let letter = s.as_bytes()[colon - 1];
let password = &s[(colon + 2)..];
Ok(Self::new(
2020-12-02 13:23:23 +01:00
min_bound,
max_bound,
2020-12-02 12:50:50 +01:00
letter,
password.to_string(),
))
}
}
pub fn run() -> Result<String> {
let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?;
2020-12-02 13:10:04 +01:00
writeln!(res, "part 2: {}", part2(INPUT)?)?;
2020-12-02 12:50:50 +01:00
Ok(res)
}
fn part1(input: &str) -> Result<usize> {
let policies = input
.lines()
2020-12-14 18:08:16 +01:00
.map(|line| line.parse::<PassPolicy>())
2020-12-02 12:50:50 +01:00
.collect::<Result<Vec<PassPolicy>>>()?;
2020-12-02 13:23:23 +01:00
Ok(policies
.iter()
.filter(|policy| policy.is_valid_part1())
.count())
2020-12-02 12:50:50 +01:00
}
2020-12-02 13:10:04 +01:00
fn part2(input: &str) -> Result<usize> {
let policies = input
.lines()
2020-12-14 18:08:16 +01:00
.map(|line| line.parse::<PassPolicy>())
2020-12-02 13:23:23 +01:00
.collect::<Result<Vec<PassPolicy>>>()?;
2020-12-02 13:10:04 +01:00
2020-12-02 13:23:23 +01:00
Ok(policies
.iter()
.filter(|policy| policy.is_valid_part2())
.count())
2020-12-02 13:10:04 +01:00
}
2020-12-02 12:50:50 +01:00
#[cfg(test)]
mod tests {
use super::*;
2020-12-02 13:10:04 +01:00
static PROVIDED1: &'static str = "1-3 a: abcde";
static PROVIDED2: &'static str = "1-3 b: cdefg";
static PROVIDED3: &'static str = "2-9 c: ccccccccc";
2020-12-02 12:50:50 +01:00
#[test]
fn part1_provided() {
2020-12-02 13:23:23 +01:00
assert!(PROVIDED1.parse::<PassPolicy>().unwrap().is_valid_part1());
assert!(!PROVIDED2.parse::<PassPolicy>().unwrap().is_valid_part1());
assert!(PROVIDED3.parse::<PassPolicy>().unwrap().is_valid_part1());
2020-12-02 12:50:50 +01:00
}
#[test]
fn part1_real() {
assert_eq!(part1(INPUT).unwrap(), 556);
}
2020-12-02 13:10:04 +01:00
#[test]
fn part2_provided() {
2020-12-02 13:23:23 +01:00
assert!(PROVIDED1.parse::<PassPolicy>().unwrap().is_valid_part2());
assert!(!PROVIDED2.parse::<PassPolicy>().unwrap().is_valid_part2());
assert!(!PROVIDED3.parse::<PassPolicy>().unwrap().is_valid_part2());
2020-12-02 13:10:04 +01:00
}
#[test]
fn part2_real() {
assert_eq!(part2(INPUT).unwrap(), 605);
}
2020-12-02 12:50:50 +01:00
}