mod model; mod parsing; use aoc_runner_derive::{aoc, aoc_generator}; pub use model::CrabSub; pub use parsing::parse_crab_sub_swarm; use yap::IntoTokens; #[aoc_generator(day7)] pub fn parse_input(input: &str) -> Vec { parse_crab_sub_swarm(&mut input.into_tokens()) } #[aoc(day7, part1)] pub fn part1(input: &[CrabSub]) -> usize { solve(input, &|x| x) } fn bounds(input: &[CrabSub]) -> (usize, usize) { let locations: Vec = input.iter().map(|sub| sub.location).collect(); ( *locations.iter().min().unwrap(), *locations.iter().max().unwrap(), ) } pub fn solve(input: &[CrabSub], fuel_calc: &dyn Fn(usize) -> usize) -> usize { let mut req_fuel = usize::MAX; let (min, max) = bounds(input); for i in min..=max { let mut new_req_fuel = 0; for sub in input.iter() { new_req_fuel += fuel_calc((i as isize - sub.location as isize).abs() as usize); } if req_fuel > new_req_fuel { req_fuel = new_req_fuel; } } req_fuel } #[aoc(day7, part2)] pub fn part2(input: &[CrabSub]) -> usize { solve(input, &|distance| (1..=distance).sum()) } #[cfg(test)] mod test { const EXAMPLE_INPUT: &str = "16,1,2,0,4,2,7,1,2,14"; const RESULT_PART_1: usize = 37; const RESULT_PART_2: usize = 168; #[test] fn part1_example() { let result = super::part1(&super::parse_input(EXAMPLE_INPUT)); assert_eq!(result, RESULT_PART_1); } #[test] fn part2_example() { let result = super::part2(&super::parse_input(EXAMPLE_INPUT)); assert_eq!(result, RESULT_PART_2); } #[test] fn parse_example() { let example = super::parse_input(EXAMPLE_INPUT); assert_eq!(example.len(), 10); } }