diff --git a/src/day22.rs b/src/day22.rs index b6ff2e8..6b044b1 100644 --- a/src/day22.rs +++ b/src/day22.rs @@ -1,9 +1,17 @@ use aoc_runner_derive::{aoc, aoc_generator}; +use itertools::Itertools; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; + +#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)] +struct Change { + price: i8, + delta: i8, +} fn evolve_secret(mut n: i64) -> i64 { n = ((n * 64) ^ n) % 16777216; n = ((n / 32) ^ n) % 16777216; - n = ((n * 2048) ^ n) % 16777216; + n = ((n * 2048) ^ n) % 16777216; n } @@ -14,6 +22,54 @@ fn rounds(mut secret: i64, n: i64) -> i64 { secret } +fn prices(mut secret: i64, n: i64) -> Vec { + let mut prices = vec![(secret % 10) as i8]; + for _ in 1..n { + secret = evolve_secret(secret); + prices.push((secret % 10) as i8); + } + prices +} + +fn changes(prices: &Vec) -> Vec { + prices + .windows(2) + .map(|a| Change { + price: a[1], + delta: a[1] - a[0], + }) + .collect() +} + +fn profit_for_sequence(changes: &Vec>, seq: &[i8]) -> i64 { + changes + .par_iter() + .filter_map(|inner| { + if let Some(buy) = inner + .windows(seq.len()) + .find(|window| window.iter().zip(seq).all(|(w, z)| w.delta == *z)) + { + Some(buy[seq.len() - 1].price as i64) + } else { + None + } + }) + .sum() +} + +fn find_best_sequence(changes: &Vec>) -> [i8; 4] { + let mut best_seq = [0, 0, 0, 0]; + let mut best_profit = 0; + for seq in (0..4).map(|_| (-9..=9 as i8)).multi_cartesian_product() { + let profit = profit_for_sequence(changes, &seq); + if profit > best_profit { + best_seq = seq.try_into().unwrap(); + best_profit = profit; + } + } + best_seq +} + fn parse(input: &str) -> Vec { input.lines().map(|l| l.parse().unwrap()).collect() } @@ -27,9 +83,14 @@ fn part1(input: &str) -> i64 { #[aoc(day22, part2)] fn part2(input: &str) -> i64 { - todo!() -} + let secrets = parse(input); + let price_changes = secrets.iter().map(|s| changes(&prices(*s, 2000))).collect_vec(); + + let seq = find_best_sequence(&price_changes); + println!("found best seq: {:?}", seq); + profit_for_sequence(&price_changes, &seq) +} #[cfg(test)] mod tests { @@ -37,6 +98,11 @@ mod tests { const EXAMPLE: &str = "1 10 100 +2024"; + + const EXAMPLE2: &str = "1 +2 +3 2024"; #[test] @@ -44,21 +110,54 @@ mod tests { assert_eq!(evolve_secret(123), 15887950); assert_eq!(evolve_secret(15887950), 16495136); assert_eq!(evolve_secret(16495136), 527345); - } + } #[test] fn test_rounds() { assert_eq!(rounds(1, 2000), 8685429); assert_eq!(rounds(10, 2000), 4700978); - } + } + + #[test] + fn test_prices() { + assert_eq!(prices(123, 10), vec![3, 0, 6, 5, 4, 4, 6, 4, 4, 2]); + } + + #[test] + fn test_profit() { + assert_eq!( + profit_for_sequence(&vec![changes(&prices(123, 10))], &[-1, -1, 0, 2]), + 6 + ); + let secrets = parse(EXAMPLE2); + + let price_changes = secrets.iter().map(|s| changes(&prices(*s, 2000))).collect_vec(); + assert_eq!( + profit_for_sequence(&price_changes, &[-2, 1, -1, 3]), + 23 + ); + } + + #[test] + fn test_changes() { + let changes = changes(&prices(123, 10)); + assert_eq!( + changes.iter().map(|c| c.delta).collect_vec(), + vec![-3, 6, -1, -1, 0, 2, -2, 0, -2] + ); + assert_eq!( + changes.iter().map(|c| c.price).collect_vec(), + vec![0, 6, 5, 4, 4, 6, 4, 4, 2] + ); + } #[test] fn part1_example() { - assert_eq!(part1(EXAMPLE),37327623); + assert_eq!(part1(EXAMPLE), 37327623); } #[test] fn part2_example() { - assert_eq!(part2(EXAMPLE), 0); + assert_eq!(part2(EXAMPLE2), 23); } -} \ No newline at end of file +}