Skip to content

Commit c928d37

Browse files
committed
2017 day 14
1 parent ab3d2e9 commit c928d37

File tree

4 files changed

+140
-35
lines changed

4 files changed

+140
-35
lines changed

crates/year2017/src/day10.rs

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use std::array;
2-
use utils::md5;
1+
use crate::knot_hash::{knot_hash_hex, knot_rounds};
32
use utils::prelude::*;
43

54
/// Implementing a custom hash function.
@@ -21,46 +20,16 @@ impl<'a> Day10<'a> {
2120
.parse_all(self.input)
2221
.expect("input invalid for part 1");
2322

24-
let list = Self::knot_hash(lengths.iter().copied(), 1);
23+
let list = knot_rounds(lengths.iter().copied(), 1);
2524

2625
list[0] as u32 * list[1] as u32
2726
}
2827

2928
#[must_use]
3029
pub fn part2(&self) -> String {
31-
let lengths = self.input.bytes().chain([17, 31, 73, 47, 23]);
30+
let hex = knot_hash_hex(self.input.bytes());
3231

33-
let sparse = Self::knot_hash(lengths, 64);
34-
35-
let dense: [u8; 16] = array::from_fn(|i| {
36-
sparse[16 * i..16 * (i + 1)]
37-
.iter()
38-
.fold(0, |acc, x| acc ^ x)
39-
});
40-
41-
let dense_hex = md5::to_hex(array::from_fn(|i| {
42-
u32::from_be_bytes(dense[4 * i..4 * (i + 1)].try_into().unwrap())
43-
}));
44-
45-
String::from_utf8(dense_hex.to_vec()).unwrap()
46-
}
47-
48-
fn knot_hash(lengths: impl Iterator<Item = u8> + Clone, rounds: u32) -> [u8; 256] {
49-
let mut list = array::from_fn(|i| i as u8);
50-
let mut position = 0;
51-
let mut skip = 0;
52-
53-
for _ in 0..rounds {
54-
for length in lengths.clone() {
55-
list[0..length as usize].reverse();
56-
list.rotate_left((length as usize + skip) % 256);
57-
position = (position + length as usize + skip) % 256;
58-
skip += 1;
59-
}
60-
}
61-
62-
list.rotate_right(position);
63-
list
32+
String::from_utf8(hex.to_vec()).unwrap()
6433
}
6534
}
6635

crates/year2017/src/day14.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use crate::knot_hash::knot_hash;
2+
use utils::bit::BitIterator;
3+
use utils::prelude::*;
4+
5+
/// Finding connected regions in a hash-derived grid.
6+
///
7+
/// This puzzle is a combination of [`Day10`](crate::Day10), which introduced the custom knot hash
8+
/// function used to create the grid, and [`Day12`](crate::Day12), which also involved finding
9+
/// connected components.
10+
#[derive(Clone, Debug)]
11+
pub struct Day14 {
12+
grid: [u128; 128],
13+
}
14+
15+
impl Day14 {
16+
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
17+
let mut grid = [0u128; 128];
18+
19+
let mut buf = Vec::with_capacity(input.len() + 4);
20+
buf.extend_from_slice(input.as_bytes());
21+
buf.push(b'-');
22+
23+
for (i, row) in grid.iter_mut().enumerate() {
24+
buf.truncate(input.len() + 1);
25+
if i < 10 {
26+
buf.push(b'0' + (i as u8));
27+
} else if i < 100 {
28+
buf.push(b'0' + ((i / 10) as u8));
29+
buf.push(b'0' + ((i % 10) as u8));
30+
} else {
31+
buf.push(b'0' + ((i / 100) as u8));
32+
buf.push(b'0' + (((i / 10) % 10) as u8));
33+
buf.push(b'0' + ((i % 10) as u8));
34+
}
35+
36+
let hash = knot_hash(buf.iter().copied());
37+
*row = u128::from_be_bytes(hash);
38+
}
39+
40+
Ok(Day14 { grid })
41+
}
42+
43+
#[must_use]
44+
pub fn part1(&self) -> u32 {
45+
self.grid.iter().map(|x| x.count_ones()).sum()
46+
}
47+
48+
#[must_use]
49+
pub fn part2(&self) -> u32 {
50+
let mut regions = 0;
51+
let mut visited = [0u128; 128];
52+
53+
for (r, &row) in self.grid.iter().enumerate() {
54+
for (_, bit) in BitIterator::ones(row) {
55+
if visited[r] & bit == 0 {
56+
regions += 1;
57+
self.visit(&mut visited, r, bit)
58+
}
59+
}
60+
}
61+
62+
regions
63+
}
64+
65+
fn visit(&self, visited: &mut [u128; 128], r: usize, bit: u128) {
66+
visited[r] |= bit;
67+
68+
if r > 0 && self.grid[r - 1] & bit != 0 && visited[r - 1] & bit == 0 {
69+
self.visit(visited, r - 1, bit);
70+
}
71+
if r < 127 && self.grid[r + 1] & bit != 0 && visited[r + 1] & bit == 0 {
72+
self.visit(visited, r + 1, bit);
73+
}
74+
75+
let left = bit << 1;
76+
if left != 0 && self.grid[r] & left != 0 && visited[r] & left == 0 {
77+
self.visit(visited, r, left);
78+
}
79+
let right = bit >> 1;
80+
if right != 0 && self.grid[r] & right != 0 && visited[r] & right == 0 {
81+
self.visit(visited, r, right);
82+
}
83+
}
84+
}
85+
86+
examples!(Day14 -> (u32, u32) [
87+
{input: "flqrgnkx", part1: 8108, part2: 1242},
88+
]);

crates/year2017/src/knot_hash.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//! Knot Hash implementation.
2+
//!
3+
//! See [`Day10`](crate::Day10) and [`Day14`](crate::Day14).
4+
5+
use std::array;
6+
use utils::md5;
7+
8+
#[inline]
9+
pub(crate) fn knot_rounds(lengths: impl Iterator<Item = u8> + Clone, rounds: u32) -> [u8; 256] {
10+
let mut list = array::from_fn(|i| i as u8);
11+
let mut position = 0;
12+
let mut skip = 0;
13+
14+
for _ in 0..rounds {
15+
for length in lengths.clone() {
16+
list[0..length as usize].reverse();
17+
list.rotate_left((length as usize + skip) % 256);
18+
position = (position + length as usize + skip) % 256;
19+
skip += 1;
20+
}
21+
}
22+
23+
list.rotate_right(position);
24+
list
25+
}
26+
27+
#[inline]
28+
pub(crate) fn knot_hash(lengths: impl Iterator<Item = u8> + Clone) -> [u8; 16] {
29+
let sparse = knot_rounds(lengths.chain([17, 31, 73, 47, 23]), 64);
30+
31+
array::from_fn(|i| {
32+
sparse[16 * i..16 * (i + 1)]
33+
.iter()
34+
.fold(0, |acc, x| acc ^ x)
35+
})
36+
}
37+
38+
#[inline]
39+
pub(crate) fn knot_hash_hex(lengths: impl Iterator<Item = u8> + Clone) -> [u8; 32] {
40+
let hash = knot_hash(lengths);
41+
42+
md5::to_hex(array::from_fn(|i| {
43+
u32::from_be_bytes(hash[4 * i..4 * (i + 1)].try_into().unwrap())
44+
}))
45+
}

crates/year2017/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#![doc = include_str!("../README.md")]
22
#![cfg_attr(not(feature = "unsafe"), forbid(unsafe_code))]
33

4+
mod knot_hash;
5+
46
utils::year!(2017 => year2017, ${
57
1 => day01::Day01<'_>,
68
2 => day02::Day02,
@@ -15,4 +17,5 @@ utils::year!(2017 => year2017, ${
1517
11 => day11::Day11,
1618
12 => day12::Day12,
1719
13 => day13::Day13,
20+
14 => day14::Day14,
1821
});

0 commit comments

Comments
 (0)