1
0
Fork 0

refactor: use generated boilerplate from lib

Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
Matej Focko 2022-12-08 22:43:34 +01:00
parent f53be47327
commit cb82cbb433
Signed by: mfocko
GPG key ID: 7C47D46246790496
8 changed files with 204 additions and 399 deletions

View file

@ -1,8 +1,4 @@
use aoc_2022::file_to_lines; use aoc_2022::*;
use color_eyre::eyre::Result;
use tracing::*;
use tracing_subscriber::EnvFilter;
type Input = Vec<i32>; type Input = Vec<i32>;
type Output = i32; type Output = i32;
@ -24,6 +20,15 @@ fn n_biggest(n: usize, input: &Input) -> Output {
calories.iter().rev().take(n).sum() calories.iter().rev().take(n).sum()
} }
struct Day01;
impl Solution<Input, Output> for Day01 {
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
file_to_lines(pathname)
.iter()
.map(|s| if s.is_empty() { -1 } else { s.parse().unwrap() })
.collect()
}
fn part_1(input: &Input) -> Output { fn part_1(input: &Input) -> Output {
n_biggest(1_usize, input) n_biggest(1_usize, input)
} }
@ -31,46 +36,10 @@ fn part_1(input: &Input) -> Output {
fn part_2(input: &Input) -> Output { fn part_2(input: &Input) -> Output {
n_biggest(3_usize, input) n_biggest(3_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 main() -> Result<()> { fn main() -> Result<()> {
tracing_subscriber::fmt() Day01::main()
.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(())
} }
#[cfg(test)] test_sample!(day_01, Day01, 24000, 45000);
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);
}
}

View file

@ -2,10 +2,14 @@ use std::str::FromStr;
use aoc_2022::*; use aoc_2022::*;
use color_eyre::{eyre::{eyre, Result}, Report}; use color_eyre::{
eyre::eyre,
Report,
};
use itertools::Itertools; use itertools::Itertools;
use tracing::*;
use tracing_subscriber::EnvFilter; type Input = Vec<Round>;
type Output = i32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Shape { enum Shape {
@ -133,8 +137,11 @@ impl Round {
} }
} }
type Input = Vec<Round>; struct Day02;
type Output = i32; impl Solution<Input, Output> for Day02 {
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
file_to_structs(pathname)
}
fn part_1(input: &Input) -> Output { fn part_1(input: &Input) -> Output {
input.iter().map(Round::score).sum() input.iter().map(Round::score).sum()
@ -143,43 +150,10 @@ fn part_1(input: &Input) -> Output {
fn part_2(input: &Input) -> Output { fn part_2(input: &Input) -> Output {
input.iter().map(Round::expected_score).sum() input.iter().map(Round::expected_score).sum()
} }
fn parse_input(pathname: &str) -> Input {
file_to_structs(pathname)
} }
fn main() -> Result<()> { fn main() -> Result<()> {
tracing_subscriber::fmt() Day02::main()
.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(())
} }
#[cfg(test)] test_sample!(day_02, Day02, 15, 12);
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);
}
}

View file

@ -3,15 +3,13 @@ use std::str::FromStr;
use aoc_2022::*; use aoc_2022::*;
use color_eyre::eyre::{Report, Result}; use color_eyre::eyre::Report;
use tracing::*;
use tracing_subscriber::EnvFilter;
struct Backpack(HashSet<i32>, HashSet<i32>);
type Input = Vec<Backpack>; type Input = Vec<Backpack>;
type Output = i32; type Output = i32;
struct Backpack(HashSet<i32>, HashSet<i32>);
impl FromStr for Backpack { impl FromStr for Backpack {
type Err = Report; type Err = Report;
@ -49,13 +47,6 @@ impl Backpack {
} }
} }
fn part_1(input: &Input) -> Output {
input
.iter()
.map(|b| b.common_items().iter().sum::<i32>())
.sum()
}
fn common_items(backpacks: &[Backpack]) -> HashSet<i32> { fn common_items(backpacks: &[Backpack]) -> HashSet<i32> {
backpacks backpacks
.iter() .iter()
@ -65,49 +56,29 @@ fn common_items(backpacks: &[Backpack]) -> HashSet<i32> {
}) })
} }
struct Day03;
impl Solution<Input, Output> for Day03 {
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
file_to_structs(pathname)
}
fn part_1(input: &Input) -> Output {
input
.iter()
.map(|b| b.common_items().iter().sum::<i32>())
.sum()
}
fn part_2(input: &Input) -> Output { fn part_2(input: &Input) -> Output {
input input
.chunks(3) .chunks(3)
.map(|backpacks| common_items(backpacks).iter().sum::<i32>()) .map(|backpacks| common_items(backpacks).iter().sum::<i32>())
.sum() .sum()
} }
fn parse_input(pathname: &str) -> Input {
file_to_structs(pathname)
} }
fn main() -> Result<()> { fn main() -> Result<()> {
tracing_subscriber::fmt() Day03::main()
.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(())
} }
#[cfg(test)] test_sample!(day_03, Day03, 157, 70);
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);
}
}

View file

@ -3,9 +3,10 @@ use std::str::FromStr;
use aoc_2022::*; use aoc_2022::*;
use color_eyre::eyre::{Report, Result}; use color_eyre::eyre::Report;
use tracing::*;
use tracing_subscriber::EnvFilter; type Input = Vec<Assignment>;
type Output = usize;
struct Assignment(RangeInclusive<i32>, RangeInclusive<i32>); struct Assignment(RangeInclusive<i32>, RangeInclusive<i32>);
@ -48,8 +49,11 @@ impl Assignment {
} }
} }
type Input = Vec<Assignment>; struct Day04;
type Output = usize; impl Solution<Input, Output> for Day04 {
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
file_to_structs(pathname)
}
fn part_1(input: &Input) -> Output { fn part_1(input: &Input) -> Output {
input.iter().filter(|a| a.fully_overlap()).count() input.iter().filter(|a| a.fully_overlap()).count()
@ -58,43 +62,10 @@ fn part_1(input: &Input) -> Output {
fn part_2(input: &Input) -> Output { fn part_2(input: &Input) -> Output {
input.iter().filter(|a| a.overlap()).count() input.iter().filter(|a| a.overlap()).count()
} }
fn parse_input(pathname: &str) -> Input {
file_to_structs(pathname)
} }
fn main() -> Result<()> { fn main() -> Result<()> {
tracing_subscriber::fmt() Day04::main()
.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(())
} }
#[cfg(test)] test_sample!(day_04, Day04, 2, 4);
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);
}
}

View file

@ -3,10 +3,11 @@ use std::str::FromStr;
use aoc_2022::*; use aoc_2022::*;
use color_eyre::eyre::{Report, Result}; use color_eyre::eyre::Report;
use itertools::Itertools; use itertools::Itertools;
use tracing::*;
use tracing_subscriber::EnvFilter; type Input = Ship;
type Output = String;
#[derive(Debug)] #[derive(Debug)]
struct Move { struct Move {
@ -65,9 +66,6 @@ impl FromStr for Ship {
} }
} }
type Input = Ship;
type Output = String;
fn stacks_to_string(stacks: &[Vec<char>]) -> String { fn stacks_to_string(stacks: &[Vec<char>]) -> String {
stacks.iter().fold(String::new(), |acc, stack| { stacks.iter().fold(String::new(), |acc, stack| {
if let Some(c) = stack.last() { if let Some(c) = stack.last() {
@ -96,6 +94,12 @@ fn move_crates(input: &Input, one_by_one: bool) -> Output {
stacks_to_string(&stacks) stacks_to_string(&stacks)
} }
struct Day05;
impl Solution<Input, Output> for Day05 {
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
file_to_string(pathname).parse().unwrap()
}
fn part_1(input: &Input) -> Output { fn part_1(input: &Input) -> Output {
move_crates(input, true) move_crates(input, true)
} }
@ -103,43 +107,10 @@ fn part_1(input: &Input) -> Output {
fn part_2(input: &Input) -> Output { fn part_2(input: &Input) -> Output {
move_crates(input, false) move_crates(input, false)
} }
fn parse_input(pathname: &str) -> Input {
file_to_string(pathname).parse::<Input>().unwrap()
} }
fn main() -> Result<()> { fn main() -> Result<()> {
tracing_subscriber::fmt() Day05::main()
.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(())
} }
#[cfg(test)] test_sample!(day_05, Day05, "CMZ".to_string(), "MCD".to_string());
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());
}
}

View file

@ -2,10 +2,7 @@ use std::collections::HashSet;
use aoc_2022::*; use aoc_2022::*;
use color_eyre::eyre::Result;
use itertools::Itertools; use itertools::Itertools;
use tracing::*;
use tracing_subscriber::EnvFilter;
type Input = String; type Input = String;
type Output = usize; type Output = usize;
@ -23,6 +20,12 @@ fn unique_marker_index(buffer: &Input, n: usize) -> Output {
+ n + n
} }
struct Day06;
impl Solution<Input, Output> for Day06 {
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
file_to_string(pathname)
}
fn part_1(input: &Input) -> Output { fn part_1(input: &Input) -> Output {
unique_marker_index(input, 4) unique_marker_index(input, 4)
} }
@ -30,83 +33,79 @@ fn part_1(input: &Input) -> Output {
fn part_2(input: &Input) -> Output { fn part_2(input: &Input) -> Output {
unique_marker_index(input, 14) unique_marker_index(input, 14)
} }
fn parse_input(pathname: &str) -> Input {
file_to_string(pathname)
} }
fn main() -> Result<()> { fn main() -> Result<()> {
tracing_subscriber::fmt() Day06::main()
.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(())
} }
test_sample!(day_06, Day06, 7, 19);
#[cfg(test)] #[cfg(test)]
mod day_06 { mod day_06_extended {
use super::*; use super::*;
#[test]
fn test_part_1() {
let sample = parse_input("samples/day06.txt");
assert_eq!(part_1(&sample), 7);
}
#[test] #[test]
fn test_part_1_example_1() { fn test_part_1_example_1() {
assert_eq!(part_1(&"bvwbjplbgvbhsrlpgdmjqwftvncz".to_string()), 5); assert_eq!(
Day06::part_1(&"bvwbjplbgvbhsrlpgdmjqwftvncz".to_string()),
5
);
} }
#[test] #[test]
fn test_part_1_example_2() { fn test_part_1_example_2() {
assert_eq!(part_1(&"nppdvjthqldpwncqszvftbrmjlhg".to_string()), 6); assert_eq!(
Day06::part_1(&"nppdvjthqldpwncqszvftbrmjlhg".to_string()),
6
);
} }
#[test] #[test]
fn test_part_1_example_3() { fn test_part_1_example_3() {
assert_eq!(part_1(&"nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg".to_string()), 10); assert_eq!(
Day06::part_1(&"nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg".to_string()),
10
);
} }
#[test] #[test]
fn test_part_1_example_4() { fn test_part_1_example_4() {
assert_eq!(part_1(&"zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw".to_string()), 11); assert_eq!(
} Day06::part_1(&"zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw".to_string()),
11
#[test] );
fn test_part_2() {
let sample = parse_input("samples/day06.txt");
assert_eq!(part_2(&sample), 19);
} }
#[test] #[test]
fn test_part_2_example_1() { fn test_part_2_example_1() {
assert_eq!(part_2(&"bvwbjplbgvbhsrlpgdmjqwftvncz".to_string()), 23); assert_eq!(
Day06::part_2(&"bvwbjplbgvbhsrlpgdmjqwftvncz".to_string()),
23
);
} }
#[test] #[test]
fn test_part_2_example_2() { fn test_part_2_example_2() {
assert_eq!(part_2(&"nppdvjthqldpwncqszvftbrmjlhg".to_string()), 23); assert_eq!(
Day06::part_2(&"nppdvjthqldpwncqszvftbrmjlhg".to_string()),
23
);
} }
#[test] #[test]
fn test_part_2_example_3() { fn test_part_2_example_3() {
assert_eq!(part_2(&"nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg".to_string()), 29); assert_eq!(
Day06::part_2(&"nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg".to_string()),
29
);
} }
#[test] #[test]
fn test_part_2_example_4() { fn test_part_2_example_4() {
assert_eq!(part_2(&"zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw".to_string()), 26); assert_eq!(
Day06::part_2(&"zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw".to_string()),
26
);
} }
} }

View file

@ -2,10 +2,11 @@ use std::{cell::RefCell, cmp::min, collections::BTreeMap, rc::Rc, str::FromStr};
use aoc_2022::*; use aoc_2022::*;
use color_eyre::{eyre::Result, Report}; use color_eyre::Report;
use itertools::Itertools; use itertools::Itertools;
use tracing::*;
use tracing_subscriber::EnvFilter; type Input = Filesystem;
type Output = usize;
type FileHandle = Rc<RefCell<AocFile>>; type FileHandle = Rc<RefCell<AocFile>>;
@ -47,15 +48,15 @@ impl AocFile {
panic!("cannot cd in file") 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 { match self {
AocFile::File(s) => (false, 0, *s), AocFile::File(s) => (false, 0, *s),
AocFile::Directory(files) => { AocFile::Directory(files) => {
let (running_total, size) = files let (running_total, size) = files
.values() .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)| { .fold((0_usize, 0_usize), |(mut running, size), (dir, r, s)| {
if dir && s <= 100000 { if dir && s <= max_size {
running += s; running += s;
} }
@ -112,6 +113,8 @@ impl Filesystem {
} }
} }
// [MARK] Helper functions for FromStr trait
fn cd(&mut self, dir: &str) { fn cd(&mut self, dir: &str) {
match dir { match dir {
".." => { ".." => {
@ -143,15 +146,28 @@ impl Filesystem {
for file in command.lines().skip(1) { for file in command.lines().skip(1) {
let parts = file.split_ascii_whitespace().collect_vec(); let parts = file.split_ascii_whitespace().collect_vec();
if parts[0] == "dir" { if parts[0] != "dir" {
/* no-op */
} else {
let name = parts[1]; let name = parts[1];
let size: usize = parts[0].parse().unwrap(); let size: usize = parts[0].parse().unwrap();
self.touch(name, size); 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 { impl FromStr for Filesystem {
@ -168,59 +184,23 @@ impl FromStr for Filesystem {
} }
} }
type Input = Filesystem; struct Day07;
type Output = usize; impl Solution<Input, Output> for Day07 {
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
fn part_1(input: &Input) -> Output {
input.root.borrow().size_under_100000().1
}
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() file_to_string(pathname).parse().unwrap()
} }
fn part_1(input: &Input) -> Output {
input.size_under(100000)
}
fn part_2(input: &Input) -> Output {
input.purge(70000000, 30000000)
}
}
fn main() -> Result<()> { fn main() -> Result<()> {
tracing_subscriber::fmt() Day07::main()
.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(())
} }
#[cfg(test)] test_sample!(day_07, Day07, 95437, 24933642);
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);
}
}

View file

@ -2,10 +2,7 @@ use std::{cmp::max, collections::BTreeSet};
use aoc_2022::*; use aoc_2022::*;
use color_eyre::eyre::Result;
use itertools::Itertools; use itertools::Itertools;
use tracing::*;
use tracing_subscriber::EnvFilter;
type Input = Vec<Vec<i8>>; type Input = Vec<Vec<i8>>;
type Output = usize; type Output = usize;
@ -51,15 +48,6 @@ fn count_in_columns(trees: &Input, counted: &mut Visited) {
count_in(trees, counted, true) 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 { fn count_visible(trees: &Input, position: SignedPosition, diff: SignedPosition) -> usize {
let max_height = *index(trees, &position); let max_height = *index(trees, &position);
let mut visible = 0; let mut visible = 0;
@ -91,16 +79,9 @@ fn compute_scenic_score(trees: &Input, x: isize, y: isize) -> usize {
.product() .product()
} }
fn part_2(input: &Input) -> Output { struct Day08;
(0..input.len()) impl Solution<Input, Output> for Day08 {
.flat_map(|y| { fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
(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) file_to_string(pathname)
.lines() .lines()
.map(|line| { .map(|line| {
@ -111,38 +92,27 @@ 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<()> { fn main() -> Result<()> {
tracing_subscriber::fmt() Day08::main()
.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(())
} }
#[cfg(test)] test_sample!(day_08, Day08, 21, 8);
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);
}
}