2020: day02: refacto
This commit is contained in:
parent
43cca9e93f
commit
6fe3ae40fb
|
@ -1,5 +1,4 @@
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::ops::RangeInclusive;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use aoc::{err, Result};
|
use aoc::{err, Result};
|
||||||
|
@ -8,22 +7,24 @@ const INPUT: &str = include_str!("../input/day02.txt");
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PassPolicy {
|
struct PassPolicy {
|
||||||
repetitions: RangeInclusive<usize>,
|
min_bound: usize,
|
||||||
|
max_bound: usize,
|
||||||
letter: u8,
|
letter: u8,
|
||||||
password: String,
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PassPolicy {
|
impl PassPolicy {
|
||||||
fn new(repetitions: RangeInclusive<usize>, letter: u8, password: String) -> Self {
|
fn new(min_bound: usize, max_bound: usize, letter: u8, password: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
repetitions,
|
min_bound,
|
||||||
|
max_bound,
|
||||||
letter,
|
letter,
|
||||||
password,
|
password,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::naive_bytecount)] // let's not pull a crate just for that
|
#[allow(clippy::naive_bytecount)] // let's not pull a crate just for that
|
||||||
fn is_valid(&self) -> bool {
|
fn is_valid_part1(&self) -> bool {
|
||||||
let occurrences = self
|
let occurrences = self
|
||||||
.password
|
.password
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
|
@ -31,7 +32,14 @@ impl PassPolicy {
|
||||||
.filter(|&&b| b == self.letter)
|
.filter(|&&b| b == self.letter)
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
self.repetitions.contains(&occurrences)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,69 +72,14 @@ impl FromStr for PassPolicy {
|
||||||
let password = &s[(colon + 2)..];
|
let password = &s[(colon + 2)..];
|
||||||
|
|
||||||
Ok(Self::new(
|
Ok(Self::new(
|
||||||
min_bound..=max_bound,
|
min_bound,
|
||||||
|
max_bound,
|
||||||
letter,
|
letter,
|
||||||
password.to_string(),
|
password.to_string(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct PassPolicyNew {
|
|
||||||
pos1: usize,
|
|
||||||
pos2: usize,
|
|
||||||
letter: u8,
|
|
||||||
password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PassPolicyNew {
|
|
||||||
fn new(pos1: usize, pos2: usize, letter: u8, password: String) -> Self {
|
|
||||||
Self {
|
|
||||||
pos1,
|
|
||||||
pos2,
|
|
||||||
letter,
|
|
||||||
password,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_valid(&self) -> bool {
|
|
||||||
let bytes = self.password.as_bytes();
|
|
||||||
(bytes[self.pos1 - 1] == self.letter) ^ (bytes[self.pos2 - 1] == self.letter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for PassPolicyNew {
|
|
||||||
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 pos1 = s[..dash]
|
|
||||||
.parse::<usize>()
|
|
||||||
.map_err(|e| err!("couldn't parse position: {}", e))?;
|
|
||||||
let pos2 = s[(dash + 1)..space]
|
|
||||||
.parse::<usize>()
|
|
||||||
.map_err(|e| err!("couldn't parse position: {}", 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(pos1, pos2, letter, password.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run() -> Result<String> {
|
pub fn run() -> Result<String> {
|
||||||
let mut res = String::with_capacity(128);
|
let mut res = String::with_capacity(128);
|
||||||
|
|
||||||
|
@ -142,16 +95,22 @@ fn part1(input: &str) -> Result<usize> {
|
||||||
.map(|line| line.parse::<PassPolicy>().map_err(|e| err!("{}", e)))
|
.map(|line| line.parse::<PassPolicy>().map_err(|e| err!("{}", e)))
|
||||||
.collect::<Result<Vec<PassPolicy>>>()?;
|
.collect::<Result<Vec<PassPolicy>>>()?;
|
||||||
|
|
||||||
Ok(policies.iter().filter(|policy| policy.is_valid()).count())
|
Ok(policies
|
||||||
|
.iter()
|
||||||
|
.filter(|policy| policy.is_valid_part1())
|
||||||
|
.count())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn part2(input: &str) -> Result<usize> {
|
fn part2(input: &str) -> Result<usize> {
|
||||||
let policies = input
|
let policies = input
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| line.parse::<PassPolicyNew>().map_err(|e| err!("{}", e)))
|
.map(|line| line.parse::<PassPolicy>().map_err(|e| err!("{}", e)))
|
||||||
.collect::<Result<Vec<PassPolicyNew>>>()?;
|
.collect::<Result<Vec<PassPolicy>>>()?;
|
||||||
|
|
||||||
Ok(policies.iter().filter(|policy| policy.is_valid()).count())
|
Ok(policies
|
||||||
|
.iter()
|
||||||
|
.filter(|policy| policy.is_valid_part2())
|
||||||
|
.count())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -164,9 +123,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_provided() {
|
fn part1_provided() {
|
||||||
assert!(PROVIDED1.parse::<PassPolicy>().unwrap().is_valid());
|
assert!(PROVIDED1.parse::<PassPolicy>().unwrap().is_valid_part1());
|
||||||
assert!(!PROVIDED2.parse::<PassPolicy>().unwrap().is_valid());
|
assert!(!PROVIDED2.parse::<PassPolicy>().unwrap().is_valid_part1());
|
||||||
assert!(PROVIDED3.parse::<PassPolicy>().unwrap().is_valid());
|
assert!(PROVIDED3.parse::<PassPolicy>().unwrap().is_valid_part1());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -176,9 +135,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_provided() {
|
fn part2_provided() {
|
||||||
assert!(PROVIDED1.parse::<PassPolicyNew>().unwrap().is_valid());
|
assert!(PROVIDED1.parse::<PassPolicy>().unwrap().is_valid_part2());
|
||||||
assert!(!PROVIDED2.parse::<PassPolicyNew>().unwrap().is_valid());
|
assert!(!PROVIDED2.parse::<PassPolicy>().unwrap().is_valid_part2());
|
||||||
assert!(!PROVIDED3.parse::<PassPolicyNew>().unwrap().is_valid());
|
assert!(!PROVIDED3.parse::<PassPolicy>().unwrap().is_valid_part2());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue