init
This commit is contained in:
46
src/day1.rs
Normal file
46
src/day1.rs
Normal 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
75
src/day2.rs
Normal 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
64
src/day3.rs
Normal 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
202
src/day4.rs
Normal 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
10
src/lib.rs
Normal 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
7
src/main.rs
Normal 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 }
|
Reference in New Issue
Block a user