summaryrefslogtreecommitdiff
path: root/2020
diff options
context:
space:
mode:
Diffstat (limited to '2020')
-rw-r--r--2020/rust/Cargo.toml2
-rw-r--r--2020/rust/day02/Cargo.toml10
-rw-r--r--2020/rust/day02/src/main.rs100
3 files changed, 111 insertions, 1 deletions
diff --git a/2020/rust/Cargo.toml b/2020/rust/Cargo.toml
index ee71f9e..febab99 100644
--- a/2020/rust/Cargo.toml
+++ b/2020/rust/Cargo.toml
@@ -2,7 +2,7 @@
members = [
"aoc",
"day01",
-# "day02",
+ "day02",
# "day03",
# "day04",
# "day05",
diff --git a/2020/rust/day02/Cargo.toml b/2020/rust/day02/Cargo.toml
new file mode 100644
index 0000000..56dd982
--- /dev/null
+++ b/2020/rust/day02/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "day02"
+version = "0.1.0"
+authors = ["cos <cos>"]
+edition = "2018"
+
+[dependencies]
+aoc = { path = "../aoc" }
+anyhow = "1.0"
+itertools = "0.9"
diff --git a/2020/rust/day02/src/main.rs b/2020/rust/day02/src/main.rs
new file mode 100644
index 0000000..42e26e5
--- /dev/null
+++ b/2020/rust/day02/src/main.rs
@@ -0,0 +1,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),
+ }
+}