From 0005f4caffc12a2905c0ba83c4256b1452c41080 Mon Sep 17 00:00:00 2001 From: Andrew Glaze Date: Wed, 27 Dec 2023 14:55:54 -0500 Subject: [PATCH] init --- .gitignore | 2 + Cargo.lock | 178 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 16 +++++ src/day1.rs | 46 ++++++++++++ src/day2.rs | 75 +++++++++++++++++++ src/day3.rs | 64 +++++++++++++++++ src/day4.rs | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 10 +++ src/main.rs | 7 ++ 9 files changed, 600 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/day1.rs create mode 100644 src/day2.rs create mode 100644 src/day3.rs create mode 100644 src/day4.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e3b8ed0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/input \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..222422e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,178 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "advent_of_code_2020" +version = "0.1.0" +dependencies = [ + "aoc-runner", + "aoc-runner-derive", + "itertools", + "strum", + "strum_macros", +] + +[[package]] +name = "aoc-runner" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d21ef9204ad206a5a3e918e9920da04e1118ad91ce4f23570be964b9d6b9dfcb" + +[[package]] +name = "aoc-runner-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8b944269d3fee645d281b1335e1797044db497bb02d0098cc3fdb8900069cc" +dependencies = [ + "aoc-runner-internal", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "aoc-runner-internal" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "274b0ba7f3669a45ec0aaacf94eb032a749de880ab776091576cca94037c9982" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "proc-macro2" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.42", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..de63573 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "advent_of_code_2020" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +bench = false + +[dependencies] +aoc-runner = "0.3.0" +aoc-runner-derive = "0.3.0" +itertools = "0.12.0" +strum = "0.25.0" +strum_macros = "0.25" diff --git a/src/day1.rs b/src/day1.rs new file mode 100644 index 0000000..7f0c35b --- /dev/null +++ b/src/day1.rs @@ -0,0 +1,46 @@ +use aoc_runner_derive::{aoc, aoc_generator}; +use itertools::Itertools; +#[aoc_generator(day1)] +fn parse(input: &str) -> Vec { + input.lines() + .map(str::parse::) + .map(Result::unwrap) + .collect_vec() +} + +#[aoc(day1, part1)] +fn part1(input: &Vec) -> u64 { + input.iter().combinations(2) + .find(|x| x.iter().cloned().sum::() == 2020) + .map(|x| x.iter().cloned().product()).unwrap() +} + +#[aoc(day1, part2)] +fn part2(input: &Vec) -> u64 { + input.iter().combinations(3) + .find(|x| x.iter().cloned().sum::() == 2020) + .map(|x| x.iter().cloned().product()).unwrap() +} + + +#[cfg(test)] +mod tests { + use super::*; + +const EX: &str = r"1721 +979 +366 +299 +675 +1456"; + + #[test] + fn part1_example() { + assert_eq!(part1(&parse(EX)), 514579); + } + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX)), 241861950); + } +} \ No newline at end of file diff --git a/src/day2.rs b/src/day2.rs new file mode 100644 index 0000000..a299cdc --- /dev/null +++ b/src/day2.rs @@ -0,0 +1,75 @@ +use aoc_runner_derive::{aoc, aoc_generator}; + +#[derive(Debug)] +struct Policy { + min: u32, + max: u32, + match_char: char, +} + +#[aoc_generator(day2)] +fn parse(input: &str) -> Vec<(Policy, String)> { + let input = input.lines().map(|line| { + let line = line.split(": ").collect::>(); + + let policy = line[0].split(' ').collect::>(); + let min = policy[0][0..policy[0].find('-').unwrap()].parse::().unwrap(); + let max = policy[0][policy[0].find('-').unwrap() + 1..].parse::().unwrap(); + let match_char = policy[1].chars().next().unwrap(); + (Policy {min, max, match_char}, line[1].to_string()) + }).collect::>(); + + input +} + +#[aoc(day2, part1)] +fn part1(passwords: &Vec<(Policy, String)>) -> u32 { + let mut valid_count = 0; + for (Policy {min, max, match_char}, password) in passwords { + let mut count = 0; + for char in password.chars() { + if char == *match_char { + count += 1; + } + } + if count >= *min && count <= *max { + valid_count += 1; + } + } + + valid_count +} + +#[aoc(day2, part2)] +fn part2(passwords: &Vec<(Policy, String)>) -> u32 { + let mut valid_count = 0; + for (Policy {min, max, match_char}, password) in passwords { + let first = password.chars().collect::>()[(*min - 1) as usize] == *match_char; + let second = password.chars().collect::>()[(*max - 1) as usize] == *match_char; + if first ^ second { + valid_count += 1; + } + } + + valid_count +} + + +#[cfg(test)] +mod tests { + use super::*; + + const EX: &str = r"1-3 a: abcde +1-3 b: cdefg +2-9 c: ccccccccc"; + + #[test] + fn part1_example() { + assert_eq!(part1(&parse(EX)), 2); + } + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX)), 1); + } +} \ No newline at end of file diff --git a/src/day3.rs b/src/day3.rs new file mode 100644 index 0000000..6a3cafc --- /dev/null +++ b/src/day3.rs @@ -0,0 +1,64 @@ +use aoc_runner_derive::{aoc, aoc_generator}; + +#[aoc_generator(day3)] +fn parse(input: &str) -> Vec> { + let input = input.lines().map(|line| { + line.chars().collect::>() + }).collect::>(); + + input +} + +#[aoc(day3, part1)] +fn part1(input: &Vec>) -> u64 { + count_trees(1, 3, input) +} + +#[aoc(day3, part2)] +fn part2(input: &Vec>) -> u64 { + vec![(1,1), (1,3), (1,5), (1,7), (2,1)].iter() + .map(|(rise, run)| { + count_trees(*rise, *run, input) + }).product() +} + +fn count_trees(rise: usize, run: usize, input: &Vec>) -> u64 { + let mut x = 0; + let mut tree_count = 0; + + for y in (rise..input.len()).step_by(rise) { + x += run; + if input[y][x % input[0].len()] == '#' { + tree_count += 1; + } + } + tree_count +} + + +#[cfg(test)] +mod tests { + use super::*; + +const EX: &str = r"..##....... +#...#...#.. +.#....#..#. +..#.#...#.# +.#...##..#. +..#.##..... +.#.#.#....# +.#........# +#.##...#... +#...##....# +.#..#...#.#"; + + #[test] + fn part1_example() { + assert_eq!(part1(&parse(EX)), 7); + } + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX)), 336); + } +} \ No newline at end of file diff --git a/src/day4.rs b/src/day4.rs new file mode 100644 index 0000000..ee00f7f --- /dev/null +++ b/src/day4.rs @@ -0,0 +1,202 @@ +use aoc_runner_derive::{aoc, aoc_generator}; +use strum::IntoEnumIterator; +use strum_macros::EnumIter; + +#[derive(Debug, EnumIter, PartialEq, Clone, Copy)] +enum IDField { + BirthYear, + IssueYear, + ExpirationYear, + Height, + HairColor, + EyeColor, + PassportID, + CountryID +} + +impl From<&str> for IDField { + fn from(value: &str) -> Self { + match &*value { + "byr" => Self::BirthYear, + "iyr" => Self::IssueYear, + "eyr" => Self::ExpirationYear, + "hgt" => Self::Height, + "hcl" => Self::HairColor, + "ecl" => Self::EyeColor, + "pid" => Self::PassportID, + "cid" => Self::CountryID, + _ => unreachable!() + } + } +} + +impl IDField { + fn validate(&self, val: &str) -> bool { + match self { + IDField::BirthYear => { + if val.len() == 4 { + if let Ok(num) = val.parse::() { + if num >= 1920 && num <= 2002 { + return true; + } + } + } + false + }, + IDField::IssueYear => { + if val.len() == 4 { + if let Ok(num) = val.parse::() { + if num >= 2010 && num <= 2020 { + return true; + } + } + } + false + }, + IDField::ExpirationYear => { + if val.len() == 4 { + if let Ok(num) = val.parse::() { + if num >= 2020 && num <= 2030 { + return true; + } + } + } + false + }, + IDField::Height => { + if let Some(idx) = val.find("cm") { + if let Ok(num) = val[0..idx].parse::() { + if num >= 150 && num <= 193 { + return true; + } + } + } else if let Some(idx) = val.find("in") { + if let Ok(num) = val[0..idx].parse::() { + if num >= 59 && num <= 76 { + return true; + } + } + } + false + }, + IDField::HairColor => { + if val.chars().next().unwrap() == '#' { + return val[1..].chars().all(|char| char.is_ascii_hexdigit()) + } + + false + }, + IDField::EyeColor => { + match val { + "amb" | "blu" | "brn" | "gry" | "grn" | "hzl" | "oth" => true, + _ => false + } + }, + IDField::PassportID => { + val.len() == 9 && val.chars().all(char::is_numeric) + }, + IDField::CountryID => true, + } + } +} + + +#[aoc_generator(day4)] +fn parse(input: &str) -> Vec> { + let input = input.split("\n\n") + .map(str::split_ascii_whitespace) + .map(|line| { + line.map(|field_str| { + let colon = field_str.find(':').unwrap(); + (field_str[0..colon].into(), field_str[colon + 1..].to_string()) + }).collect::>() + }).collect::>(); + + input +} + +#[aoc(day4, part1)] +fn part1(input: &Vec>) -> u32 { + input.iter() + .map(|passport| { + let passport = passport.iter().map(|(field, _)| *field).collect::>(); + IDField::iter().all(|x| { + if x == IDField::CountryID { + return true + } + passport.contains(&x) + }) + }).filter(|x| *x).count() as u32 +} + +#[aoc(day4, part2)] +fn part2(input: &Vec>) -> u32 { + input.iter().map(|passport| { + IDField::iter().all(|x| { + if x == IDField::CountryID { + return true + } + if let Some((_, val)) = passport.iter().find(|(field, _)| field == &x ) { + return x.validate(&val) + } + false + }) + }).filter(|x| *x).count() as u32 +} + + +#[cfg(test)] +mod tests { + use super::*; + +const EX: &str = r"ecl:gry pid:860033327 eyr:2020 hcl:#fffffd +byr:1937 iyr:2017 cid:147 hgt:183cm + +iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884 +hcl:#cfa07d byr:1929 + +hcl:#ae17e1 iyr:2013 +eyr:2024 +ecl:brn pid:760753108 byr:1931 +hgt:179cm + +hcl:#cfa07d eyr:2025 pid:166559648 +iyr:2011 ecl:brn hgt:59in"; + +const EX_2: &str = r"eyr:1972 cid:100 +hcl:#18171d ecl:amb hgt:170 pid:186cm iyr:2018 byr:1926 + +iyr:2019 +hcl:#602927 eyr:1967 hgt:170cm +ecl:grn pid:012533040 byr:1946 + +hcl:dab227 iyr:2012 +ecl:brn hgt:182cm pid:021572410 eyr:2020 byr:1992 cid:277 + +hgt:59cm ecl:zzz +eyr:2038 hcl:74454a iyr:2023 +pid:3556412378 byr:2007 + +pid:087499704 hgt:74in ecl:grn iyr:2012 eyr:2030 byr:1980 +hcl:#623a2f + +eyr:2029 ecl:blu cid:129 byr:1989 +iyr:2014 pid:896056539 hcl:#a97842 hgt:165cm + +hcl:#888785 +hgt:164cm byr:2001 iyr:2015 cid:88 +pid:545766238 ecl:hzl +eyr:2022 + +iyr:2010 hgt:158cm hcl:#b6652a ecl:blu byr:1944 eyr:2021 pid:093154719"; + + #[test] + fn part1_example() { + assert_eq!(part1(&parse(EX)), 2); + } + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX_2)), 4); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0ad7566 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,10 @@ +mod day1; +mod day4; +mod day3; +mod day2; +extern crate aoc_runner; + +#[macro_use] +extern crate aoc_runner_derive; + +aoc_lib!{ year = 2020 } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..63c3769 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,7 @@ +extern crate advent_of_code_2020; +extern crate aoc_runner_derive; +extern crate aoc_runner; + +use aoc_runner_derive::aoc_main; + +aoc_main! { lib = advent_of_code_2020 } \ No newline at end of file