This commit is contained in:
Smaug123
2021-05-19 20:37:15 +01:00
parent 5045202a33
commit 451b946fd1
8 changed files with 286 additions and 15 deletions

8
Cargo.lock generated
View File

@@ -199,6 +199,14 @@ version = "0.1.0"
name = "day_13"
version = "0.1.0"
[[package]]
name = "day_14"
version = "0.1.0"
dependencies = [
"criterion",
"day_10",
]
[[package]]
name = "day_15"
version = "0.1.0"

View File

@@ -13,6 +13,7 @@ members = [
"day_11",
"day_12",
"day_13",
"day_14",
"day_15",
"day_16",
"day_17",

View File

@@ -69,28 +69,31 @@ pub mod day_10 {
}
}
pub fn knot_hash_unsalted(bytes: &[u8]) -> String {
pub fn knot_hash_unsalted(bytes: &[u8]) -> Vec<u8> {
let mut state = new_state(256);
for _ in 0..64 {
execute_round(&mut state, &bytes);
}
let dense = densify(&state.v);
let mut answer = vec![0u8; 2 * dense.len()];
for (i, b) in dense.iter().enumerate() {
densify(&state.v)
}
pub fn knot_hash(bytes: &[u8]) -> Vec<u8> {
let mut copy: Vec<u8> = bytes.to_vec();
copy.extend(vec![17, 31, 73, 47, 23]);
knot_hash_unsalted(&copy)
}
pub fn to_hex_str(bytes: &[u8]) -> String {
let mut answer = vec![0u8; 2 * bytes.len()];
for (i, b) in bytes.iter().enumerate() {
answer[2 * i] = to_hex(b / 16);
answer[2 * i + 1] = to_hex(b % 16);
}
String::from_utf8(answer).unwrap()
}
pub fn knot_hash(bytes: &[u8]) -> String {
let mut copy: Vec<u8> = bytes.to_vec();
copy.extend(vec![17, 31, 73, 47, 23]);
knot_hash_unsalted(&copy)
}
pub fn part_2(input: &[u8]) -> String {
knot_hash(input)
to_hex_str(&knot_hash(input))
}
}
@@ -109,17 +112,20 @@ mod tests {
#[test]
fn part2_known() {
assert_eq!(knot_hash("".as_bytes()), "a2582a3a0e66e6e86e3812dcb672a272");
assert_eq!(
knot_hash("AoC 2017".as_bytes()),
to_hex_str(&knot_hash("".as_bytes())),
"a2582a3a0e66e6e86e3812dcb672a272"
);
assert_eq!(
to_hex_str(&knot_hash("AoC 2017".as_bytes())),
"33efeb34ea91902bb2f59c9920caa6cd"
);
assert_eq!(
knot_hash("1,2,3".as_bytes()),
to_hex_str(&knot_hash("1,2,3".as_bytes())),
"3efbe78a8d82f29979031a4aa0b16a9d"
);
assert_eq!(
knot_hash("1,2,4".as_bytes()),
to_hex_str(&knot_hash("1,2,4".as_bytes())),
"63960835bcdc130f0b66d7ff4f6a5a8e"
);
}

17
day_14/Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "day_14"
version = "0.1.0"
authors = ["Smaug123 <patrick+github@patrickstevens.co.uk>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
day_10 = { path = "../day_10" }
[dev-dependencies]
criterion = "0.3"
[[bench]]
name = "day_14"
harness = false

24
day_14/benches/day_14.rs Normal file
View File

@@ -0,0 +1,24 @@
use criterion::{criterion_group, criterion_main, Criterion};
use day_14::day_14::{input, part_1, part_1_longhand, part_2};
fn criterion_benchmark(c: &mut Criterion) {
let input = input();
c.bench_function("day 14 part 1", |b| {
b.iter(|| {
part_1(&input);
})
});
c.bench_function("day 14 part 1 longhand", |b| {
b.iter(|| {
part_1_longhand(&input);
})
});
c.bench_function("day 14 part 2", |b| {
b.iter(|| {
part_2(&input);
})
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

1
day_14/input.txt Normal file
View File

@@ -0,0 +1 @@
ffayrhll

207
day_14/src/lib.rs Normal file
View File

@@ -0,0 +1,207 @@
pub mod day_14 {
use day_10::day_10::knot_hash;
pub fn input() -> &'static str {
include_str!("../input.txt").trim()
}
fn count_ones(i: u8) -> u8 {
let mut i = i;
let mut ans = 0;
while i > 0 {
if i % 2 == 1 {
ans += 1;
}
i /= 2;
}
ans
}
pub fn part_1_longhand(key: &str) -> u32 {
let mut buffer: Vec<u8> = key.chars().map(|i| i as u8).collect();
buffer.extend(&[b'-', b'0']);
let len = buffer.len();
let mut count = 0u32;
for i in 0u8..=9 {
buffer[len - 1] = i + b'0';
let hash = knot_hash(&buffer);
count += hash
.iter()
.cloned()
.map(|i| count_ones(i) as u32)
.sum::<u32>();
}
buffer.push(0);
for i in 1u8..=9 {
buffer[len - 1] = i + b'0';
for j in 0u8..=9 {
buffer[len] = j + b'0';
count += knot_hash(&buffer)
.iter()
.cloned()
.map(|i| count_ones(i) as u32)
.sum::<u32>();
}
}
buffer.push(0);
buffer[len - 1] = b'1';
for i in 0u8..=2 {
buffer[len] = i + b'0';
for j in 0u8..=(if i == 2 { 7 } else { 9 }) {
buffer[len + 1] = j + b'0';
count += knot_hash(&buffer)
.iter()
.cloned()
.map(|i| count_ones(i) as u32)
.sum::<u32>();
}
}
count
}
fn value_at<T>(i: &[T], width: usize, row: usize, col: usize) -> &T {
&i[row * width + col]
}
fn set_value_at<T>(i: &mut [T], width: usize, row: usize, col: usize, value: T) {
i[row * width + col] = value;
}
fn flood_fill(i: &mut [u32], width: usize, value: u32, row: usize, col: usize) {
let current = *value_at(i, width, row, col);
if current != 1 {
return;
}
set_value_at(i, width, row, col, value);
if row < (i.len() / width) - 1 {
flood_fill(i, width, value, row + 1, col);
}
if row > 0 {
flood_fill(i, width, value, row - 1, col);
}
if col > 0 {
flood_fill(i, width, value, row, col - 1);
}
if col < width - 1 {
flood_fill(i, width, value, row, col + 1);
}
}
fn count_contiguous(mut i: Vec<u32>, width: usize) -> u32 {
// 0 means empty, 1 means "full and not assigned a group".
let height = i.len() / width;
let mut next_group_number = 2;
for row in 0..height {
for col in 0..width {
if *value_at(&i, width, row, col) == 1 {
flood_fill(&mut i, width, next_group_number, row, col);
next_group_number += 1;
}
}
}
next_group_number - 2
}
pub(crate) fn render_vec(key: &str) -> Vec<u32> {
let mut output: Vec<u32> = vec![0; 128 * 128];
let mut buffer: Vec<u8> = key.chars().map(|i| i as u8).collect();
buffer.extend(&[b'-', b'0']);
let len = buffer.len();
let mut row = 0;
for i in 0u8..=9 {
buffer[len - 1] = i + b'0';
let hash = knot_hash(&buffer);
let mut col = 0;
for byte in hash {
let mut byte = byte;
for i in (0..8).rev() {
set_value_at(&mut output, 128, row, col + i, (byte % 2) as u32);
byte /= 2;
}
col += 8;
}
row += 1;
}
buffer.push(0);
for i in 1u8..=9 {
buffer[len - 1] = i + b'0';
for j in 0u8..=9 {
buffer[len] = j + b'0';
let hash = knot_hash(&buffer);
let mut col = 0;
for byte in hash {
let mut byte = byte;
for i in (0..8).rev() {
set_value_at(&mut output, 128, row, col + i, (byte % 2) as u32);
byte /= 2;
}
col += 8;
}
row += 1;
}
}
buffer.push(0);
buffer[len - 1] = b'1';
for i in 0u8..=2 {
buffer[len] = i + b'0';
for j in 0u8..=(if i == 2 { 7 } else { 9 }) {
buffer[len + 1] = j + b'0';
let hash = knot_hash(&buffer);
let mut col = 0;
for byte in hash {
let mut byte = byte;
for i in (0..8).rev() {
set_value_at(&mut output, 128, row, col + i, (byte % 2) as u32);
byte /= 2;
}
col += 8;
}
row += 1;
}
}
output
}
pub fn part_1(key: &str) -> usize {
render_vec(key).iter().cloned().filter(|i| *i == 1).count()
}
pub fn part_2(key: &str) -> u32 {
count_contiguous(render_vec(key), 128)
}
}
#[cfg(test)]
mod tests {
use super::day_14::*;
#[test]
fn part1_known() {
assert_eq!(part_1(&"flqrgnkx"), 8108);
assert_eq!(part_1_longhand(&"flqrgnkx"), 8108);
}
#[test]
fn part2_known() {
assert_eq!(part_2(&"flqrgnkx"), 1242);
}
#[test]
fn test_day_14() {
let input = input();
assert_eq!(part_1(input), 8190);
assert_eq!(part_1_longhand(input), 8190);
assert_eq!(part_2(&input), 1134);
}
}

7
day_14/src/main.rs Normal file
View File

@@ -0,0 +1,7 @@
use day_14::day_14;
fn main() {
let input = day_14::input();
println!("part 1 => {}", day_14::part_1(&input));
println!("part 2 => {}", day_14::part_2(&input));
}