commit 59264dfa916311ce8cc399febb3e0be3574fa817 Author: Sijmen Schoon Date: Sun Dec 6 18:07:08 2020 +0100 Rust solutions diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..3307b0f --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,56 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "aoc" +version = "0.1.0" +dependencies = [ + "regex", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "regex" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..fb6f99f --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "aoc" +version = "0.1.0" +authors = ["Sijmen Schoon "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +regex = "1.4.2" diff --git a/rust/src/main.rs b/rust/src/main.rs new file mode 100644 index 0000000..944366c --- /dev/null +++ b/rust/src/main.rs @@ -0,0 +1,286 @@ +use std::time::Duration; + +mod day01 { + use std::{collections::HashMap, time::Duration, time::Instant}; + + pub fn run(print: bool) -> Duration { + let file_string = std::fs::read_to_string("inputs/day01").unwrap(); + let instant = Instant::now(); + + let entries: Vec = file_string.lines().flat_map(|line| line.parse()).collect(); + let mut sums = HashMap::new(); + + let mut part1 = None; + for i in &entries { + for j in &entries { + if i + j < 2020 { + sums.insert(i + j, i * j); + } else if i + j == 2020 { + part1 = Some(i * j); + } + } + } + let part1 = part1.unwrap(); + + let part2 = entries + .iter() + .find_map(|i| sums.get(&(2020 - i)).map(|mul| i * mul)) + .unwrap(); + + let elapsed = instant.elapsed(); + if print { + dbg!(elapsed, part1, part2); + } + + elapsed + } +} + +mod day02 { + use std::time::{Duration, Instant}; + + pub fn run(print: bool) -> Duration { + let file_string = std::fs::read_to_string("inputs/day02").unwrap(); + let instant = Instant::now(); + + let rules: Vec<(usize, usize, char, &str)> = file_string + .lines() + .flat_map(|rule| { + let mut splitc = rule.splitn(2, ' '); + let mut split_range = splitc.next()?.split('-'); + + let min: usize = split_range.next()?.parse().ok()?; + let max: usize = split_range.next()?.parse().ok()?; + + let rest = splitc.next()?; + let c = rest.chars().next()?; + let pw = &rest[3..]; + + Some((min, max, c, pw)) + }) + .collect(); + + let part1 = rules + .iter() + .filter(|(min, max, c, pw)| { + let c = &pw.chars().filter(|n| n == c).count(); + min <= c && c <= max + }) + .count(); + + let part2 = rules + .iter() + .filter(|(min, max, c, pw)| { + let mut chars = pw.chars(); + let a = chars.nth(min - 1) == Some(*c); + let b = chars.nth(max - min - 1) == Some(*c); + a ^ b + }) + .count(); + + let elapsed = instant.elapsed(); + if print { + dbg!(elapsed, part1, part2); + } + elapsed + } +} + +mod day03 { + use std::time::{Duration, Instant}; + + fn count(rows: &[Vec], dx: usize, dy: usize) -> usize { + let mut x = 0; + let mut trees = 0; + for y in (0..rows.len()).step_by(dy) { + let row = &rows[y]; + + if row[x] == '#' { + trees += 1; + } + + x = (x + dx) % row.len(); + } + trees + } + + pub fn run(print: bool) -> Duration { + let file_string = std::fs::read_to_string("inputs/day3").unwrap(); + + let instant = Instant::now(); + + let rows: Vec> = file_string + .lines() + .map(|line| line.chars().collect()) + .collect(); + + let part1 = count(&rows, 3, 1); + + let part2 = part1 + * [(1, 1), (5, 1), (7, 1), (1, 2)] + .iter() + .map(|(dx, dy)| count(&rows, *dx, *dy)) + .fold(1, |acc, element| acc * element); + + let elapsed = instant.elapsed(); + if print { + dbg!(elapsed, part1, part2); + } + + elapsed + } +} + +mod day04 { + use std::time::{Duration, Instant}; + + fn between(field: &str, lower: i32, higher: i32) -> bool { + let n: i32 = field.parse().unwrap(); + n >= lower && n <= higher + } + + pub fn run(print: bool) -> Duration { + let input = std::fs::read_to_string("inputs/day04").unwrap(); + + let now = Instant::now(); + + let passports: Vec<_> = input + .split("\n\n") + .map(|p| p.replace('\n', " ").trim().to_string()) + .collect(); + + let valid_passports = passports.iter().filter(|p| { + p.contains("byr") + && p.contains("iyr") + && p.contains("eyr") + && p.contains("hgt") + && p.contains("hcl") + && p.contains("ecl") + && p.contains("pid") + }); + + let mut part1 = 0; + + let part2 = valid_passports + .filter(|p| { + part1 += 1; + + let mut valid = true; + for field in p.split(' ') { + let mut parts = field.split(':'); + let label = parts.next().unwrap(); + let value = parts.next().unwrap(); + + valid = valid + && match label { + "byr" => between(value, 1920, 2002), + "iyr" => between(value, 2010, 2020), + "eyr" => between(value, 2020, 2030), + "hgt" => { + let height = &value[..value.len() - 2]; + (value.ends_with("in") && between(height, 59, 76)) + || (value.ends_with("cm") && between(height, 150, 193)) + } + "hcl" => value.starts_with("#"), + "ecl" => { + value == "amb" + || value == "blu" + || value == "brn" + || value == "gry" + || value == "grn" + || value == "hzl" + || value == "oth" + } + "pid" => value.len() == 9, + "cid" => true, + _ => false, + } + } + valid + }) + .count(); + + let elapsed = now.elapsed(); + if print { + dbg!(elapsed, part1, part2); + } + + elapsed + } +} + +mod day05 { + use std::time::{Duration, Instant}; + + pub fn run(print: bool) -> Duration { + let input = std::fs::read_to_string("inputs/day05").unwrap(); + let instant = Instant::now(); + + let passes: Vec<_> = input.lines().collect(); + let mut ids: Vec<_> = passes + .iter() + .map(|pass| { + let mut row_min = 0; + let mut row_max = 128; + let mut column_min = 0; + let mut column_max = 8; + + for c in pass.chars() { + match c { + 'F' => row_max = (row_min + row_max) / 2, + 'B' => row_min = (row_min + row_max) / 2, + 'L' => column_max = (column_min + column_max) / 2, + 'R' => column_min = (column_min + column_max) / 2, + _ => (), + }; + } + + row_min * 8 + column_min + }) + .collect(); + + ids.sort(); + + let part1 = ids.iter().max().unwrap(); + + let part2 = ids + .windows(2) + .find_map(|window| { + if window[0] + 2 == window[1] { + Some(window[0] + 1) + } else { + None + } + }) + .unwrap(); + + let elapsed = instant.elapsed(); + if print { + dbg!(elapsed, part1, part2); + } + + elapsed + } +} + +fn main() { + let days = [day01::run, day02::run, day03::run, day04::run, day05::run]; + + if let Some(day) = std::env::args().nth(1) { + let day: usize = day.parse().expect("day not an integer"); + if day > 0 { + days[day - 1](true); + } + } else { + let repetitions = 2000; + let mut total = Duration::new(0, 0); + + for day in 0..days.len() { + for _ in 0..repetitions { + total += days[day](false) / repetitions; + } + } + + dbg!(total); + } +}