2020: day02: part 1

This commit is contained in:
Antoine Martin 2020-12-02 12:50:50 +01:00
parent b2214c7195
commit e5a5c638f6
5 changed files with 1114 additions and 1 deletions

View file

@ -1,9 +1,11 @@
use criterion::{criterion_group, criterion_main, Criterion}; use criterion::{criterion_group, criterion_main, Criterion};
use aoc2020::day01; use aoc2020::day01;
use aoc2020::day02;
fn aoc2020_all(c: &mut Criterion) { fn aoc2020_all(c: &mut Criterion) {
c.bench_function("day01", |b| b.iter(|| day01::run().unwrap())); c.bench_function("day01", |b| b.iter(|| day01::run().unwrap()));
c.bench_function("day02", |b| b.iter(|| day02::run().unwrap()));
} }
criterion_group! { criterion_group! {

1000
aoc2020/input/day02.txt Normal file

File diff suppressed because it is too large Load diff

109
aoc2020/src/day02.rs Normal file
View file

@ -0,0 +1,109 @@
use std::fmt::Write;
use std::ops::RangeInclusive;
use std::str::FromStr;
use aoc::{err, Result};
const INPUT: &str = include_str!("../input/day02.txt");
#[derive(Debug)]
struct PassPolicy {
repetitions: RangeInclusive<usize>,
letter: u8,
password: String,
}
impl PassPolicy {
fn new(repetitions: RangeInclusive<usize>, letter: u8, password: String) -> Self {
Self {
repetitions,
letter,
password,
}
}
#[allow(clippy::naive_bytecount)] // let's not pull a crate just for that
fn is_valid(&self) -> bool {
let occurrences = self
.password
.as_bytes()
.iter()
.filter(|&&b| b == self.letter)
.count();
self.repetitions.contains(&occurrences)
}
}
impl FromStr for PassPolicy {
type Err = aoc::Error;
fn from_str(s: &str) -> Result<Self> {
let s = s.trim_end();
let space = s
.find(' ')
.ok_or_else(|| err!("couldn't parse password policy: didn't find space"))?;
let dash = s
.find('-')
.ok_or_else(|| err!("couldn't parse password policy: didn't find dash"))?;
let min_bound = s[..dash]
.parse::<usize>()
.map_err(|e| err!("couldn't parse range: {}", e))?;
let max_bound = s[(dash + 1)..space]
.parse::<usize>()
.map_err(|e| err!("couldn't parse range: {}", e))?;
let colon = s
.find(':')
.ok_or_else(|| err!("couldn't parse password policy: didn't find colon"))?;
let letter = s.as_bytes()[colon - 1];
let password = &s[(colon + 2)..];
Ok(Self::new(
min_bound..=max_bound,
letter,
password.to_string(),
))
}
}
pub fn run() -> Result<String> {
let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?;
Ok(res)
}
fn part1(input: &str) -> Result<usize> {
let policies = input
.lines()
.map(|line| line.parse::<PassPolicy>().map_err(|e| err!("{}", e)))
.collect::<Result<Vec<PassPolicy>>>()?;
Ok(policies.iter().filter(|policy| policy.is_valid()).count())
}
#[cfg(test)]
mod tests {
use super::*;
static PROVIDED: &'static str = "1-3 a: abcde
1-3 b: cdefg
2-9 c: ccccccccc
";
#[test]
fn part1_provided() {
assert_eq!(part1(PROVIDED).unwrap(), 2);
}
#[test]
fn part1_real() {
assert_eq!(part1(INPUT).unwrap(), 556);
}
}

View file

@ -1 +1,2 @@
pub mod day01; pub mod day01;
pub mod day02;

View file

@ -2,9 +2,10 @@ use aoc::DayFunc;
use aoc::Result; use aoc::Result;
use aoc2020::day01; use aoc2020::day01;
use aoc2020::day02;
fn main() -> Result<()> { fn main() -> Result<()> {
let days: &[DayFunc] = &[day01::run]; let days: &[DayFunc] = &[day01::run, day02::run];
aoc::run(days) aoc::run(days)
} }