day22: part 2 solution. 38s runtime but works.

This commit is contained in:
Keenan Tims 2024-12-21 21:57:52 -08:00
parent c681727fb3
commit 32937aaa0e
Signed by: ktims
GPG Key ID: 11230674D69038D4

View File

@ -1,9 +1,17 @@
use aoc_runner_derive::{aoc, aoc_generator}; 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 { fn evolve_secret(mut n: i64) -> i64 {
n = ((n * 64) ^ n) % 16777216; n = ((n * 64) ^ n) % 16777216;
n = ((n / 32) ^ n) % 16777216; n = ((n / 32) ^ n) % 16777216;
n = ((n * 2048) ^ n) % 16777216; n = ((n * 2048) ^ n) % 16777216;
n n
} }
@ -14,6 +22,54 @@ fn rounds(mut secret: i64, n: i64) -> i64 {
secret secret
} }
fn prices(mut secret: i64, n: i64) -> Vec<i8> {
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<i8>) -> Vec<Change> {
prices
.windows(2)
.map(|a| Change {
price: a[1],
delta: a[1] - a[0],
})
.collect()
}
fn profit_for_sequence(changes: &Vec<Vec<Change>>, 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<Vec<Change>>) -> [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<i64> { fn parse(input: &str) -> Vec<i64> {
input.lines().map(|l| l.parse().unwrap()).collect() input.lines().map(|l| l.parse().unwrap()).collect()
} }
@ -27,9 +83,14 @@ fn part1(input: &str) -> i64 {
#[aoc(day22, part2)] #[aoc(day22, part2)]
fn part2(input: &str) -> i64 { 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)] #[cfg(test)]
mod tests { mod tests {
@ -37,6 +98,11 @@ mod tests {
const EXAMPLE: &str = "1 const EXAMPLE: &str = "1
10 10
100 100
2024";
const EXAMPLE2: &str = "1
2
3
2024"; 2024";
#[test] #[test]
@ -44,21 +110,54 @@ mod tests {
assert_eq!(evolve_secret(123), 15887950); assert_eq!(evolve_secret(123), 15887950);
assert_eq!(evolve_secret(15887950), 16495136); assert_eq!(evolve_secret(15887950), 16495136);
assert_eq!(evolve_secret(16495136), 527345); assert_eq!(evolve_secret(16495136), 527345);
} }
#[test] #[test]
fn test_rounds() { fn test_rounds() {
assert_eq!(rounds(1, 2000), 8685429); assert_eq!(rounds(1, 2000), 8685429);
assert_eq!(rounds(10, 2000), 4700978); 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] #[test]
fn part1_example() { fn part1_example() {
assert_eq!(part1(EXAMPLE),37327623); assert_eq!(part1(EXAMPLE), 37327623);
} }
#[test] #[test]
fn part2_example() { fn part2_example() {
assert_eq!(part2(EXAMPLE), 0); assert_eq!(part2(EXAMPLE2), 23);
} }
} }