use aoc_runner_derive::{aoc, aoc_generator}; use cached::proc_macro::cached; use misc::POW10; #[aoc_generator(day3)] fn parse(input: &str) -> Vec> { input .lines() .map(|bank| bank.bytes().map(|c| c - b'0').collect()) .collect() } #[cached] fn max_joltage(bank: Vec, n: usize) -> u64 { if n == 1 { return *bank.iter().max().unwrap() as u64; } (0..bank.len() - n + 1) .map(|start| { bank[start] as u64 * POW10[n - 1] + max_joltage(bank[start + 1..].to_vec(), n - 1) }) .max() .unwrap() } fn max_joltage_2(bank: &[u8], n: usize) -> u64 { if n == 1 { return *bank.iter().max().unwrap() as u64; } let choices = &bank[..bank.len() - (n - 1)]; // Note: can't use position_max() here since it returns the _last_ instance and we need the _first_. // get the max, then find the first instance of that value. There is definitely a one-pass solution. let max = choices.iter().max().unwrap(); let choice = choices.iter().position(|n| max == n).unwrap(); let rest = &bank[choice + 1..]; bank[choice] as u64 * POW10[n - 1] + max_joltage_2(rest, n - 1) } #[aoc(day3, part1, Brute)] fn part1(input: &[Vec]) -> u64 { input.iter().map(|bank| max_joltage(bank.clone(), 2)).sum() } #[aoc(day3, part2, Brute)] fn part2(input: &[Vec]) -> u64 { input.iter().map(|bank| max_joltage(bank.clone(), 12)).sum() } #[aoc(day3, part1, Smart)] fn part1_smart(input: &[Vec]) -> u64 { input.iter().map(|bank| max_joltage_2(bank, 2)).sum() } #[aoc(day3, part2, Smart)] fn part2_smart(input: &[Vec]) -> u64 { input.iter().map(|bank| max_joltage_2(bank, 12)).sum() } #[cfg(test)] mod tests { use super::*; const EXAMPLE: &str = "987654321111111 811111111111119 234234234234278 818181911112111"; #[test] fn part1_example() { assert_eq!(part1(&parse(EXAMPLE)), 357); } #[test] fn part2_example() { assert_eq!(part2(&parse(EXAMPLE)), 3121910778619); } #[test] fn part1_smart_example() { assert_eq!(part1_smart(&parse(EXAMPLE)), 357); } #[test] fn part2_smart_example() { assert_eq!(part2_smart(&parse(EXAMPLE)), 3121910778619); } }