diff --git a/2019/16/src/main.rs b/2019/16/src/main.rs index 16d6d99..9bf933e 100644 --- a/2019/16/src/main.rs +++ b/2019/16/src/main.rs @@ -2,19 +2,59 @@ use std::io::{stdin, BufRead}; use std::iter::*; #[rustfmt::skip] -fn read_input() -> Vec { +fn read_input() -> Vec { stdin().lock().lines().next().unwrap().unwrap().chars().map(|c| c.to_string().parse().unwrap()).collect() } #[rustfmt::skip] -fn main() { - let mut last_phase = read_input(); - //let mut last_phase: Vec = "80871224585914546619083218645595".chars().map(|c| c.to_string().parse().unwrap()).collect(); +fn part1(mut last_phase: Vec) -> String { for _ in 0..100 { last_phase = (1..=last_phase.len()).map(|i| { - let mut pattern = [0i16, 1, 0, -1].iter().flat_map(|x| repeat(x).take(i)).cycle().skip(1); - last_phase.iter().map(|x| x*pattern.next().unwrap()).sum::().abs() % 10 + let mut pattern = [0i32, 1, 0, -1].iter().flat_map(|x| repeat(x).take(i)).cycle().skip(1); + last_phase.iter().map(|x| x*pattern.next().unwrap()).sum::().abs() % 10 }).collect(); } - println!("Part 1: {}", last_phase.iter().take(8).map(|n| n.to_string()).collect::()); + last_phase.iter().take(8).map(|n| n.to_string()).collect::() +} + +/** + * The outputs after each phase are related in a way that I can’t really explain. + * They are essentially a summed-area table, but built starting with the last element. + * However, this only works for the second half of the output. The rest seem to be random? + * + * The examples show this quite clearly: + * Input signal: 12345678 (let’s call this `input`) + * After 1 phase: 48226158 (`output`) + * We can build the state after 1 phase right to left. + * ``` + * output[7] = input[7..8].iter().sum() % 10; // 8 + * output[6] = input[6..8].iter().sum() % 10; // 15 % 10 == 5 + * output[5] = input[5..8].iter().sum() % 10; // 21 % 10 == 1 + * output[4] = input[4..8].iter().sum() % 10; // 26 % 10 == 6 + * ``` + * Which is exactly the output sequence. + * This pattern only holds true for the second half of the array, + * but the offset always seems to be high enough for that. + * + * Because all input elements only affect outputs with lower indices, + * we can also drop all elements before the output starts. + */ +#[rustfmt::skip] +fn part2(input: Vec) -> String { + let offset: usize = input.iter().take(7).map(|n| n.to_string()).collect::().parse().unwrap(); + let mut p2 = input.repeat(10_000).split_off(offset); + for _ in 0..100 { + let mut s = 0; + for i in (0..p2.len()).rev() { + s += p2[i]; + p2[i] = s % 10; + } + } + p2.iter().take(8).map(|n| n.to_string()).collect::() +} + +fn main() { + let input = read_input(); + println!("Part 1: {}", part1(input.clone())); + println!("Part 2: {}", part2(input)); }