diff --git a/2021/src/day7.rs b/2021/src/day7.rs index 58817d4..76e18b9 100644 --- a/2021/src/day7.rs +++ b/2021/src/day7.rs @@ -30,7 +30,26 @@ //! //! Determine the horizontal position that the crabs can align to using the least fuel possible. How much fuel must they spend to align to that position? //! +//! --- Part Two --- +//! The crabs don't seem interested in your proposed solution. Perhaps you misunderstand crab engineering? //! +//! As it turns out, crab submarine engines don't burn fuel at a constant rate. Instead, each change of 1 step in horizontal position costs 1 more unit of fuel than the last: the first step costs 1, the second step costs 2, the third step costs 3, and so on. +//! +//! As each crab moves, moving further becomes more expensive. This changes the best horizontal position to align them all on; in the example above, this becomes 5: +//! +//! Move from 16 to 5: 66 fuel +//! Move from 1 to 5: 10 fuel +//! Move from 2 to 5: 6 fuel +//! Move from 0 to 5: 15 fuel +//! Move from 4 to 5: 1 fuel +//! Move from 2 to 5: 6 fuel +//! Move from 7 to 5: 3 fuel +//! Move from 1 to 5: 10 fuel +//! Move from 2 to 5: 6 fuel +//! Move from 14 to 5: 45 fuel +//! This costs a total of 168 fuel. This is the new cheapest possible outcome; the old alignment position (2) now costs 206 fuel instead. +//! +//! Determine the horizontal position that the crabs can align to using the least fuel possible so they can make you an escape route! How much fuel must they spend to align to that position? use std::num::ParseIntError; use anyhow::Result; @@ -44,7 +63,7 @@ fn parse(input: &str) -> Result, ParseIntError> { .collect::, ParseIntError>>() } -fn score(nums: &[u64], mid: u64) -> u64 { +fn score1(nums: &[u64], mid: u64) -> u64 { nums.iter() .map(|n| ((*n as i64) - (mid as i64)).abs()) .sum::() as u64 @@ -54,28 +73,48 @@ fn score(nums: &[u64], mid: u64) -> u64 { fn part1(input: &[u64]) -> Result { let mut input: Vec<_> = input.to_vec(); input.sort_unstable(); - Ok(score(&input, input[input.len() / 2])) + Ok(score1(&input, input[input.len() / 2])) } -/* -#[aoc(day7, part2)] -fn part2(depths: &[u64]) -> Result { -todo!("part2") -Ok(()) +fn score2(nums: &[u64], mid: u64) -> u64 { + nums.iter() + .map(|n| { + let d = ((*n as i64) - (mid as i64)).abs(); + (d * (d + 1)) / 2 + }) + .sum::() as u64 +} + +#[aoc(day7, part2)] +fn part2(input: &[u64]) -> Result { + let mut input: Vec<_> = input.to_vec(); + let avg = input.iter().sum::() / input.len() as u64; + + let s = if avg > 10 { avg - 10 } else { 0 }; + let num = input.len() as u64; + let e = if avg + 10 < num { avg + 10 } else { num }; + let answer = (s..e) + .map(|i| score2(&input, i)) + .min() + .expect("couldn't find min"); + if input.len() > 10 { + // The real data needs an answer lower than our first attempt. + assert!(answer < 94862126); + } + Ok(answer) } -*/ #[cfg(test)] mod tests { use super::*; #[test] - fn test_score() -> Result<()> { + fn test_score1() -> Result<()> { let nums: Vec = parse("16,1,2,0,4,2,7,1,2,14")?; - assert_eq!(score(&nums, 1), 41); - assert_eq!(score(&nums, 2), 37); - assert_eq!(score(&nums, 3), 39); - assert_eq!(score(&nums, 10), 71); + assert_eq!(score1(&nums, 1), 41); + assert_eq!(score1(&nums, 2), 37); + assert_eq!(score1(&nums, 3), 39); + assert_eq!(score1(&nums, 10), 71); Ok(()) } #[test] @@ -85,14 +124,17 @@ mod tests { Ok(()) } - /* #[test] - fn test_part2()->Result<()> { - let input = r#" - "# - .trim(); - assert_eq!(part2(&parse(input)?)?, u64::MAX); - Ok(()) + fn test_score2() -> Result<()> { + let nums: Vec = parse("16,1,2,0,4,2,7,1,2,14")?; + assert_eq!(score2(&nums, 5), 168); + assert_eq!(score2(&nums, 2), 206); + Ok(()) + } + #[test] + fn test_part2() -> Result<()> { + let input = r#"16,1,2,0,4,2,7,1,2,14"#.trim(); + assert_eq!(part2(&parse(input)?)?, 168); + Ok(()) } - */ } diff --git a/2021/src/template.rs b/2021/src/template.rs index d94e822..bdca6f1 100644 --- a/2021/src/template.rs +++ b/2021/src/template.rs @@ -23,9 +23,9 @@ fn part1(input: &[u64]) -> Result { /* #[aoc(dayX, part2)] -fn part2(depths: &[u64]) -> Result { - todo!("part2") - Ok(()) +fn part2(input: &[u64]) -> Result { + todo!("part2"); + Ok(0) } */