diff --git a/src/bin/day01.rs b/src/bin/day01.rs index f8281ff..43f7ddc 100644 --- a/src/bin/day01.rs +++ b/src/bin/day01.rs @@ -1,8 +1,4 @@ -use aoc_2022::file_to_lines; - -use color_eyre::eyre::Result; -use tracing::*; -use tracing_subscriber::EnvFilter; +use aoc_2022::*; type Input = Vec; type Output = i32; @@ -24,53 +20,26 @@ fn n_biggest(n: usize, input: &Input) -> Output { calories.iter().rev().take(n).sum() } -fn part_1(input: &Input) -> Output { - n_biggest(1_usize, input) -} +struct Day01; +impl Solution for Day01 { + fn parse_input>(pathname: P) -> Input { + file_to_lines(pathname) + .iter() + .map(|s| if s.is_empty() { -1 } else { s.parse().unwrap() }) + .collect() + } -fn part_2(input: &Input) -> Output { - n_biggest(3_usize, input) -} + fn part_1(input: &Input) -> Output { + n_biggest(1_usize, input) + } -fn parse_input(pathname: &str) -> Input { - file_to_lines(pathname) - .iter() - .map(|s| if s.is_empty() { -1 } else { s.parse().unwrap() }) - .collect() + fn part_2(input: &Input) -> Output { + n_biggest(3_usize, input) + } } fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .with_target(false) - .with_file(true) - .with_line_number(true) - .without_time() - .compact() - .init(); - color_eyre::install()?; - - let input = parse_input("inputs/day01.txt"); - - info!("Part 1: {}", part_1(&input)); - info!("Part 2: {}", part_2(&input)); - - Ok(()) + Day01::main() } -#[cfg(test)] -mod day_01 { - use super::*; - - #[test] - fn test_part_1() { - let sample = parse_input("samples/day01.txt"); - assert_eq!(part_1(&sample), 24000); - } - - #[test] - fn test_part_2() { - let sample = parse_input("samples/day01.txt"); - assert_eq!(part_2(&sample), 45000); - } -} +test_sample!(day_01, Day01, 24000, 45000); diff --git a/src/bin/day02.rs b/src/bin/day02.rs index dbde01b..479c8c5 100644 --- a/src/bin/day02.rs +++ b/src/bin/day02.rs @@ -2,10 +2,14 @@ use std::str::FromStr; use aoc_2022::*; -use color_eyre::{eyre::{eyre, Result}, Report}; +use color_eyre::{ + eyre::eyre, + Report, +}; use itertools::Itertools; -use tracing::*; -use tracing_subscriber::EnvFilter; + +type Input = Vec; +type Output = i32; #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Shape { @@ -133,53 +137,23 @@ impl Round { } } -type Input = Vec; -type Output = i32; +struct Day02; +impl Solution for Day02 { + fn parse_input>(pathname: P) -> Input { + file_to_structs(pathname) + } -fn part_1(input: &Input) -> Output { - input.iter().map(Round::score).sum() -} + fn part_1(input: &Input) -> Output { + input.iter().map(Round::score).sum() + } -fn part_2(input: &Input) -> Output { - input.iter().map(Round::expected_score).sum() -} - -fn parse_input(pathname: &str) -> Input { - file_to_structs(pathname) + fn part_2(input: &Input) -> Output { + input.iter().map(Round::expected_score).sum() + } } fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .with_target(false) - .with_file(true) - .with_line_number(true) - .without_time() - .compact() - .init(); - color_eyre::install()?; - - let input = parse_input("inputs/day02.txt"); - - info!("Part 1: {}", part_1(&input)); - info!("Part 2: {}", part_2(&input)); - - Ok(()) + Day02::main() } -#[cfg(test)] -mod day_02 { - use super::*; - - #[test] - fn test_part_1() { - let sample = parse_input("samples/day02.txt"); - assert_eq!(part_1(&sample), 15); - } - - #[test] - fn test_part_2() { - let sample = parse_input("samples/day02.txt"); - assert_eq!(part_2(&sample), 12); - } -} +test_sample!(day_02, Day02, 15, 12); diff --git a/src/bin/day03.rs b/src/bin/day03.rs index 1b52905..ce86366 100644 --- a/src/bin/day03.rs +++ b/src/bin/day03.rs @@ -3,15 +3,13 @@ use std::str::FromStr; use aoc_2022::*; -use color_eyre::eyre::{Report, Result}; -use tracing::*; -use tracing_subscriber::EnvFilter; - -struct Backpack(HashSet, HashSet); +use color_eyre::eyre::Report; type Input = Vec; type Output = i32; +struct Backpack(HashSet, HashSet); + impl FromStr for Backpack { type Err = Report; @@ -49,13 +47,6 @@ impl Backpack { } } -fn part_1(input: &Input) -> Output { - input - .iter() - .map(|b| b.common_items().iter().sum::()) - .sum() -} - fn common_items(backpacks: &[Backpack]) -> HashSet { backpacks .iter() @@ -65,49 +56,29 @@ fn common_items(backpacks: &[Backpack]) -> HashSet { }) } -fn part_2(input: &Input) -> Output { - input - .chunks(3) - .map(|backpacks| common_items(backpacks).iter().sum::()) - .sum() -} +struct Day03; +impl Solution for Day03 { + fn parse_input>(pathname: P) -> Input { + file_to_structs(pathname) + } -fn parse_input(pathname: &str) -> Input { - file_to_structs(pathname) + fn part_1(input: &Input) -> Output { + input + .iter() + .map(|b| b.common_items().iter().sum::()) + .sum() + } + + fn part_2(input: &Input) -> Output { + input + .chunks(3) + .map(|backpacks| common_items(backpacks).iter().sum::()) + .sum() + } } fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .with_target(false) - .with_file(true) - .with_line_number(true) - .without_time() - .compact() - .init(); - color_eyre::install()?; - - let input = parse_input("inputs/day03.txt"); - - info!("Part 1: {}", part_1(&input)); - info!("Part 2: {}", part_2(&input)); - - Ok(()) + Day03::main() } -#[cfg(test)] -mod day_03 { - use super::*; - - #[test] - fn test_part_1() { - let sample = parse_input("samples/day03.txt"); - assert_eq!(part_1(&sample), 157); - } - - #[test] - fn test_part_2() { - let sample = parse_input("samples/day03.txt"); - assert_eq!(part_2(&sample), 70); - } -} +test_sample!(day_03, Day03, 157, 70); diff --git a/src/bin/day04.rs b/src/bin/day04.rs index 6bd8ddb..81d3674 100644 --- a/src/bin/day04.rs +++ b/src/bin/day04.rs @@ -3,9 +3,10 @@ use std::str::FromStr; use aoc_2022::*; -use color_eyre::eyre::{Report, Result}; -use tracing::*; -use tracing_subscriber::EnvFilter; +use color_eyre::eyre::Report; + +type Input = Vec; +type Output = usize; struct Assignment(RangeInclusive, RangeInclusive); @@ -48,53 +49,23 @@ impl Assignment { } } -type Input = Vec; -type Output = usize; +struct Day04; +impl Solution for Day04 { + fn parse_input>(pathname: P) -> Input { + file_to_structs(pathname) + } -fn part_1(input: &Input) -> Output { - input.iter().filter(|a| a.fully_overlap()).count() -} + fn part_1(input: &Input) -> Output { + input.iter().filter(|a| a.fully_overlap()).count() + } -fn part_2(input: &Input) -> Output { - input.iter().filter(|a| a.overlap()).count() -} - -fn parse_input(pathname: &str) -> Input { - file_to_structs(pathname) + fn part_2(input: &Input) -> Output { + input.iter().filter(|a| a.overlap()).count() + } } fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .with_target(false) - .with_file(true) - .with_line_number(true) - .without_time() - .compact() - .init(); - color_eyre::install()?; - - let input = parse_input("inputs/day04.txt"); - - info!("Part 1: {}", part_1(&input)); - info!("Part 2: {}", part_2(&input)); - - Ok(()) + Day04::main() } -#[cfg(test)] -mod day_04 { - use super::*; - - #[test] - fn test_part_1() { - let sample = parse_input("samples/day04.txt"); - assert_eq!(part_1(&sample), 2); - } - - #[test] - fn test_part_2() { - let sample = parse_input("samples/day04.txt"); - assert_eq!(part_2(&sample), 4); - } -} +test_sample!(day_04, Day04, 2, 4); diff --git a/src/bin/day05.rs b/src/bin/day05.rs index 3752c67..45cc685 100644 --- a/src/bin/day05.rs +++ b/src/bin/day05.rs @@ -3,10 +3,11 @@ use std::str::FromStr; use aoc_2022::*; -use color_eyre::eyre::{Report, Result}; +use color_eyre::eyre::Report; use itertools::Itertools; -use tracing::*; -use tracing_subscriber::EnvFilter; + +type Input = Ship; +type Output = String; #[derive(Debug)] struct Move { @@ -65,9 +66,6 @@ impl FromStr for Ship { } } -type Input = Ship; -type Output = String; - fn stacks_to_string(stacks: &[Vec]) -> String { stacks.iter().fold(String::new(), |acc, stack| { if let Some(c) = stack.last() { @@ -96,50 +94,23 @@ fn move_crates(input: &Input, one_by_one: bool) -> Output { stacks_to_string(&stacks) } -fn part_1(input: &Input) -> Output { - move_crates(input, true) -} +struct Day05; +impl Solution for Day05 { + fn parse_input>(pathname: P) -> Input { + file_to_string(pathname).parse().unwrap() + } -fn part_2(input: &Input) -> Output { - move_crates(input, false) -} + fn part_1(input: &Input) -> Output { + move_crates(input, true) + } -fn parse_input(pathname: &str) -> Input { - file_to_string(pathname).parse::().unwrap() + fn part_2(input: &Input) -> Output { + move_crates(input, false) + } } fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .with_target(false) - .with_file(true) - .with_line_number(true) - .without_time() - .compact() - .init(); - color_eyre::install()?; - - let input = parse_input("inputs/day05.txt"); - - info!("Part 1: {}", part_1(&input)); - info!("Part 2: {}", part_2(&input)); - - Ok(()) + Day05::main() } -#[cfg(test)] -mod day_05 { - use super::*; - - #[test] - fn test_part_1() { - let sample = parse_input("samples/day05.txt"); - assert_eq!(part_1(&sample), "CMZ".to_string()); - } - - #[test] - fn test_part_2() { - let sample = parse_input("samples/day05.txt"); - assert_eq!(part_2(&sample), "MCD".to_string()); - } -} +test_sample!(day_05, Day05, "CMZ".to_string(), "MCD".to_string()); diff --git a/src/bin/day06.rs b/src/bin/day06.rs index bf26a1f..e9ae1f2 100644 --- a/src/bin/day06.rs +++ b/src/bin/day06.rs @@ -2,10 +2,7 @@ use std::collections::HashSet; use aoc_2022::*; -use color_eyre::eyre::Result; use itertools::Itertools; -use tracing::*; -use tracing_subscriber::EnvFilter; type Input = String; type Output = usize; @@ -23,90 +20,92 @@ fn unique_marker_index(buffer: &Input, n: usize) -> Output { + n } -fn part_1(input: &Input) -> Output { - unique_marker_index(input, 4) -} +struct Day06; +impl Solution for Day06 { + fn parse_input>(pathname: P) -> Input { + file_to_string(pathname) + } -fn part_2(input: &Input) -> Output { - unique_marker_index(input, 14) -} + fn part_1(input: &Input) -> Output { + unique_marker_index(input, 4) + } -fn parse_input(pathname: &str) -> Input { - file_to_string(pathname) + fn part_2(input: &Input) -> Output { + unique_marker_index(input, 14) + } } fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .with_target(false) - .with_file(true) - .with_line_number(true) - .without_time() - .compact() - .init(); - color_eyre::install()?; - - let input = parse_input("inputs/day06.txt"); - - info!("Part 1: {}", part_1(&input)); - info!("Part 2: {}", part_2(&input)); - - Ok(()) + Day06::main() } +test_sample!(day_06, Day06, 7, 19); + #[cfg(test)] -mod day_06 { +mod day_06_extended { use super::*; - #[test] - fn test_part_1() { - let sample = parse_input("samples/day06.txt"); - assert_eq!(part_1(&sample), 7); - } - #[test] fn test_part_1_example_1() { - assert_eq!(part_1(&"bvwbjplbgvbhsrlpgdmjqwftvncz".to_string()), 5); + assert_eq!( + Day06::part_1(&"bvwbjplbgvbhsrlpgdmjqwftvncz".to_string()), + 5 + ); } #[test] fn test_part_1_example_2() { - assert_eq!(part_1(&"nppdvjthqldpwncqszvftbrmjlhg".to_string()), 6); + assert_eq!( + Day06::part_1(&"nppdvjthqldpwncqszvftbrmjlhg".to_string()), + 6 + ); } #[test] fn test_part_1_example_3() { - assert_eq!(part_1(&"nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg".to_string()), 10); + assert_eq!( + Day06::part_1(&"nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg".to_string()), + 10 + ); } #[test] fn test_part_1_example_4() { - assert_eq!(part_1(&"zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw".to_string()), 11); - } - - #[test] - fn test_part_2() { - let sample = parse_input("samples/day06.txt"); - assert_eq!(part_2(&sample), 19); + assert_eq!( + Day06::part_1(&"zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw".to_string()), + 11 + ); } #[test] fn test_part_2_example_1() { - assert_eq!(part_2(&"bvwbjplbgvbhsrlpgdmjqwftvncz".to_string()), 23); + assert_eq!( + Day06::part_2(&"bvwbjplbgvbhsrlpgdmjqwftvncz".to_string()), + 23 + ); } #[test] fn test_part_2_example_2() { - assert_eq!(part_2(&"nppdvjthqldpwncqszvftbrmjlhg".to_string()), 23); + assert_eq!( + Day06::part_2(&"nppdvjthqldpwncqszvftbrmjlhg".to_string()), + 23 + ); } #[test] fn test_part_2_example_3() { - assert_eq!(part_2(&"nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg".to_string()), 29); + assert_eq!( + Day06::part_2(&"nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg".to_string()), + 29 + ); } #[test] fn test_part_2_example_4() { - assert_eq!(part_2(&"zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw".to_string()), 26); + assert_eq!( + Day06::part_2(&"zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw".to_string()), + 26 + ); } } diff --git a/src/bin/day07.rs b/src/bin/day07.rs index 731224d..274491c 100644 --- a/src/bin/day07.rs +++ b/src/bin/day07.rs @@ -2,10 +2,11 @@ use std::{cell::RefCell, cmp::min, collections::BTreeMap, rc::Rc, str::FromStr}; use aoc_2022::*; -use color_eyre::{eyre::Result, Report}; +use color_eyre::Report; use itertools::Itertools; -use tracing::*; -use tracing_subscriber::EnvFilter; + +type Input = Filesystem; +type Output = usize; type FileHandle = Rc>; @@ -47,15 +48,15 @@ impl AocFile { panic!("cannot cd in file") } - fn size_under_100000(&self) -> (bool, usize, usize) { + fn size_under(&self, max_size: usize) -> (bool, usize, usize) { match self { AocFile::File(s) => (false, 0, *s), AocFile::Directory(files) => { let (running_total, size) = files .values() - .map(|f| f.borrow().size_under_100000()) + .map(|f| f.borrow().size_under(max_size)) .fold((0_usize, 0_usize), |(mut running, size), (dir, r, s)| { - if dir && s <= 100000 { + if dir && s <= max_size { running += s; } @@ -112,6 +113,8 @@ impl Filesystem { } } + // [MARK] Helper functions for ‹FromStr› trait + fn cd(&mut self, dir: &str) { match dir { ".." => { @@ -143,15 +146,28 @@ impl Filesystem { for file in command.lines().skip(1) { let parts = file.split_ascii_whitespace().collect_vec(); - if parts[0] == "dir" { - /* no-op */ - } else { + if parts[0] != "dir" { let name = parts[1]; let size: usize = parts[0].parse().unwrap(); self.touch(name, size); } } } + + // [MARK] Helper functions for ‹FromStr› trait + + fn size_under(&self, max_size: usize) -> usize { + self.root.borrow().size_under(max_size).1 + } + + fn purge(&self, total: usize, needed: usize) -> usize { + let used = self.root.borrow().size_under(0).2; + + // to_be_freed >= needed - (total - used) + let to_be_freed = needed - (total - used); + + self.root.borrow().smallest_bigger(to_be_freed).1 + } } impl FromStr for Filesystem { @@ -168,59 +184,23 @@ impl FromStr for Filesystem { } } -type Input = Filesystem; -type Output = usize; +struct Day07; +impl Solution for Day07 { + fn parse_input>(pathname: P) -> Input { + file_to_string(pathname).parse().unwrap() + } -fn part_1(input: &Input) -> Output { - input.root.borrow().size_under_100000().1 -} + fn part_1(input: &Input) -> Output { + input.size_under(100000) + } -fn part_2(input: &Input) -> Output { - let (total, needed) = (70000000, 30000000); - let used = input.root.borrow().size_under_100000().2; - - // to_be_freed >= needed - (total - used) - let to_be_freed = needed - (total - used); - - input.root.borrow().smallest_bigger(to_be_freed).1 -} - -fn parse_input(pathname: &str) -> Input { - file_to_string(pathname).parse().unwrap() + fn part_2(input: &Input) -> Output { + input.purge(70000000, 30000000) + } } fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .with_target(false) - .with_file(true) - .with_line_number(true) - .without_time() - .compact() - .init(); - color_eyre::install()?; - - let input = parse_input("inputs/day07.txt"); - - info!("Part 1: {}", part_1(&input)); - info!("Part 2: {}", part_2(&input)); - - Ok(()) + Day07::main() } -#[cfg(test)] -mod day_07 { - use super::*; - - #[test] - fn test_part_1() { - let sample = parse_input("samples/day07.txt"); - assert_eq!(part_1(&sample), 95437); - } - - #[test] - fn test_part_2() { - let sample = parse_input("samples/day07.txt"); - assert_eq!(part_2(&sample), 24933642); - } -} +test_sample!(day_07, Day07, 95437, 24933642); diff --git a/src/bin/day08.rs b/src/bin/day08.rs index 5e6c131..d74c35a 100644 --- a/src/bin/day08.rs +++ b/src/bin/day08.rs @@ -2,10 +2,7 @@ use std::{cmp::max, collections::BTreeSet}; use aoc_2022::*; -use color_eyre::eyre::Result; use itertools::Itertools; -use tracing::*; -use tracing_subscriber::EnvFilter; type Input = Vec>; type Output = usize; @@ -51,15 +48,6 @@ fn count_in_columns(trees: &Input, counted: &mut Visited) { count_in(trees, counted, true) } -fn part_1(input: &Input) -> Output { - let mut counted = Visited::new(); - - count_in_rows(input, &mut counted); - count_in_columns(input, &mut counted); - - counted.len() -} - fn count_visible(trees: &Input, position: SignedPosition, diff: SignedPosition) -> usize { let max_height = *index(trees, &position); let mut visible = 0; @@ -91,17 +79,10 @@ fn compute_scenic_score(trees: &Input, x: isize, y: isize) -> usize { .product() } -fn part_2(input: &Input) -> Output { - (0..input.len()) - .flat_map(|y| { - (0..input[y].len()).map(move |x| compute_scenic_score(input, x as isize, y as isize)) - }) - .max() - .unwrap() -} - -fn parse_input(pathname: &str) -> Input { - file_to_string(pathname) +struct Day08; +impl Solution for Day08 { + fn parse_input>(pathname: P) -> Input { + file_to_string(pathname) .lines() .map(|line| { line.chars() @@ -109,40 +90,29 @@ fn parse_input(pathname: &str) -> Input { .collect_vec() }) .collect_vec() + } + + fn part_1(input: &Input) -> Output { + let mut counted = Visited::new(); + + count_in_rows(input, &mut counted); + count_in_columns(input, &mut counted); + + counted.len() + } + + fn part_2(input: &Input) -> Output { + (0..input.len()) + .flat_map(|y| { + (0..input[y].len()).map(move |x| compute_scenic_score(input, x as isize, y as isize)) + }) + .max() + .unwrap() + } } fn main() -> Result<()> { - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .with_target(false) - .with_file(true) - .with_line_number(true) - .without_time() - .compact() - .init(); - color_eyre::install()?; - - let input = parse_input("inputs/day08.txt"); - - info!("Part 1: {}", part_1(&input)); - info!("Part 2: {}", part_2(&input)); - - Ok(()) + Day08::main() } -#[cfg(test)] -mod day_08 { - use super::*; - - #[test] - fn test_part_1() { - let sample = parse_input("samples/day08.txt"); - assert_eq!(part_1(&sample), 21); - } - - #[test] - fn test_part_2() { - let sample = parse_input("samples/day08.txt"); - assert_eq!(part_2(&sample), 8); - } -} +test_sample!(day_08, Day08, 21, 8);