Skip to content

Instantly share code, notes, and snippets.

@ArdaXi
Created December 2, 2020 20:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ArdaXi/fbcd446fc1d0708a5bcd518b519b75ec to your computer and use it in GitHub Desktop.
Save ArdaXi/fbcd446fc1d0708a5bcd518b519b75ec to your computer and use it in GitHub Desktop.
use anyhow::{anyhow, bail, Context, Error, Result};
use itertools::Itertools;
use std::convert::TryFrom;
use std::io::{stdin, BufRead};
#[derive(Clone, Debug)]
struct Line {
min: usize,
max: usize,
letter: char,
pass: String,
}
impl TryFrom<String> for Line {
type Error = anyhow::Error;
fn try_from(value: String) -> Result<Self> {
let mut iter = value.split(&['-', ' ', ':'][..]).filter(|x| !x.is_empty());
let min = iter
.next()
.ok_or_else(|| anyhow!("Missing min"))?
.parse()
.context("Failed to parse min")?;
let max = iter
.next()
.ok_or_else(|| anyhow!("Missing max"))?
.parse()
.context("Failed to parse max")?;
let letter_slice = iter.next().ok_or_else(|| anyhow!("Missing letter"))?;
if letter_slice.len() != 1 {
bail!("Letter wrong length");
}
let letter = letter_slice
.chars()
.next()
.ok_or_else(|| anyhow!("Missing letter somehow"))?;
let pass = iter
.next()
.ok_or_else(|| anyhow!("Missing pass"))?
.to_string();
if pass.len() < max {
bail!("Password shorter than max value");
}
Ok(Line {
min,
max,
letter,
pass,
})
}
}
impl Line {
fn is_valid(&self) -> bool {
let count = self.pass.chars().filter(|c| c == &self.letter).count();
count >= self.min && count <= self.max
}
fn is_valid2(&self) -> bool {
let mut chars = self.pass.chars();
let first = chars.nth(self.min - 1).unwrap();
let second = chars.nth(self.max - self.min - 1).unwrap();
(first == self.letter) ^ (second == self.letter)
}
}
fn until_err<T, E>(err: &mut &mut Result<()>, item: Result<T, E>) -> Option<T>
where
E: Into<Error>,
{
match item {
Ok(item) => Some(item),
Err(e) => {
**err = Err(anyhow!(e));
None
}
}
}
fn main() -> Result<()> {
let stdin = stdin();
let mut err: Result<()> = Ok(());
let (lines1, lines2) = stdin
.lock()
.lines()
.map(|l| l.map_err(Error::new).and_then(Line::try_from))
.scan(&mut err, until_err)
.tee();
let part1 = lines1.filter(Line::is_valid).count();
let part2 = lines2.filter(Line::is_valid2).count();
err.context("Failed to parse all lines")?;
println!("part 1: {:?}", part1);
println!("part 2: {:?}", part2);
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment