This commit is contained in:
2023-12-27 14:55:54 -05:00
commit 0005f4caff
9 changed files with 600 additions and 0 deletions

46
src/day1.rs Normal file
View File

@@ -0,0 +1,46 @@
use aoc_runner_derive::{aoc, aoc_generator};
use itertools::Itertools;
#[aoc_generator(day1)]
fn parse(input: &str) -> Vec<u64> {
input.lines()
.map(str::parse::<u64>)
.map(Result::unwrap)
.collect_vec()
}
#[aoc(day1, part1)]
fn part1(input: &Vec<u64>) -> u64 {
input.iter().combinations(2)
.find(|x| x.iter().cloned().sum::<u64>() == 2020)
.map(|x| x.iter().cloned().product()).unwrap()
}
#[aoc(day1, part2)]
fn part2(input: &Vec<u64>) -> u64 {
input.iter().combinations(3)
.find(|x| x.iter().cloned().sum::<u64>() == 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);
}
}

75
src/day2.rs Normal file
View File

@@ -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::<Vec<_>>();
let policy = line[0].split(' ').collect::<Vec<_>>();
let min = policy[0][0..policy[0].find('-').unwrap()].parse::<u32>().unwrap();
let max = policy[0][policy[0].find('-').unwrap() + 1..].parse::<u32>().unwrap();
let match_char = policy[1].chars().next().unwrap();
(Policy {min, max, match_char}, line[1].to_string())
}).collect::<Vec<_>>();
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::<Vec<_>>()[(*min - 1) as usize] == *match_char;
let second = password.chars().collect::<Vec<_>>()[(*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);
}
}

64
src/day3.rs Normal file
View File

@@ -0,0 +1,64 @@
use aoc_runner_derive::{aoc, aoc_generator};
#[aoc_generator(day3)]
fn parse(input: &str) -> Vec<Vec<char>> {
let input = input.lines().map(|line| {
line.chars().collect::<Vec<_>>()
}).collect::<Vec<_>>();
input
}
#[aoc(day3, part1)]
fn part1(input: &Vec<Vec<char>>) -> u64 {
count_trees(1, 3, input)
}
#[aoc(day3, part2)]
fn part2(input: &Vec<Vec<char>>) -> 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<Vec<char>>) -> 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);
}
}

202
src/day4.rs Normal file
View File

@@ -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::<u32>() {
if num >= 1920 && num <= 2002 {
return true;
}
}
}
false
},
IDField::IssueYear => {
if val.len() == 4 {
if let Ok(num) = val.parse::<u32>() {
if num >= 2010 && num <= 2020 {
return true;
}
}
}
false
},
IDField::ExpirationYear => {
if val.len() == 4 {
if let Ok(num) = val.parse::<u32>() {
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::<u32>() {
if num >= 150 && num <= 193 {
return true;
}
}
} else if let Some(idx) = val.find("in") {
if let Ok(num) = val[0..idx].parse::<u32>() {
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<Vec<(IDField, String)>> {
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::<Vec<_>>()
}).collect::<Vec<_>>();
input
}
#[aoc(day4, part1)]
fn part1(input: &Vec<Vec<(IDField, String)>>) -> u32 {
input.iter()
.map(|passport| {
let passport = passport.iter().map(|(field, _)| *field).collect::<Vec<_>>();
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<Vec<(IDField, String)>>) -> 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);
}
}

10
src/lib.rs Normal file
View File

@@ -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 }

7
src/main.rs Normal file
View File

@@ -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 }