aoc/year2017/
day10.rs

1//! # Knot Hash
2//!
3//! Instead of reversing elements from the starting position then trying to handle wrap around,
4//! its easier use [`rotate_left`] to rotate the array by the same amount so that the starting
5//! position is always zero, then take advantage of the built in [`reverse`] method.
6//!
7//! [`rotate_left`]: slice::rotate_left
8//! [`reverse`]: slice::reverse
9use crate::util::parse::*;
10use std::fmt::Write as _;
11
12pub fn parse(input: &str) -> &str {
13    input
14}
15
16pub fn part1(input: &str) -> u32 {
17    let lengths: Vec<_> = input.iter_unsigned().collect();
18    let knot = hash(&lengths, 1);
19    knot.iter().take(2).map(|&b| b as u32).product()
20}
21
22pub fn part2(input: &str) -> String {
23    let mut lengths: Vec<_> = input.trim().bytes().map(|b| b as usize).collect();
24    lengths.extend([17, 31, 73, 47, 23]);
25
26    let knot = hash(&lengths, 64);
27    let mut result = String::new();
28
29    for chunk in knot.chunks_exact(16) {
30        let reduced = chunk.iter().fold(0, |acc, n| acc ^ n);
31        let _ = write!(&mut result, "{reduced:02x}");
32    }
33
34    result
35}
36
37fn hash(lengths: &[usize], rounds: usize) -> Vec<u8> {
38    let mut knot: Vec<_> = (0..=255).collect();
39    let mut position = 0;
40    let mut skip = 0;
41
42    for _ in 0..rounds {
43        for &length in lengths {
44            let next = length + skip;
45            knot[0..length].reverse();
46            knot.rotate_left(next % 256);
47            position += next;
48            skip += 1;
49        }
50    }
51
52    // Rotate the array the other direction so that the original starting position is restored.
53    knot.rotate_right(position % 256);
54    knot
55}