diff --git a/Cargo.lock b/Cargo.lock index 87d1537..3b949fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 6ae2742..3cc1112 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "day_11", "day_12", "day_13", + "day_14", "day_15", "day_16", "day_17", diff --git a/day_10/src/lib.rs b/day_10/src/lib.rs index 5dc7093..16367bd 100644 --- a/day_10/src/lib.rs +++ b/day_10/src/lib.rs @@ -69,28 +69,31 @@ pub mod day_10 { } } - pub fn knot_hash_unsalted(bytes: &[u8]) -> String { + pub fn knot_hash_unsalted(bytes: &[u8]) -> Vec { 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 { + let mut copy: Vec = bytes.to_vec(); + copy.extend(vec![17, 31, 73, 47, 23]); + knot_hash_unsalted(©) + } + + 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 = bytes.to_vec(); - copy.extend(vec![17, 31, 73, 47, 23]); - knot_hash_unsalted(©) - } - 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" ); } diff --git a/day_14/Cargo.toml b/day_14/Cargo.toml new file mode 100644 index 0000000..907f95c --- /dev/null +++ b/day_14/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "day_14" +version = "0.1.0" +authors = ["Smaug123 "] +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 diff --git a/day_14/benches/day_14.rs b/day_14/benches/day_14.rs new file mode 100644 index 0000000..fd9da03 --- /dev/null +++ b/day_14/benches/day_14.rs @@ -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); diff --git a/day_14/input.txt b/day_14/input.txt new file mode 100644 index 0000000..f2fdb36 --- /dev/null +++ b/day_14/input.txt @@ -0,0 +1 @@ +ffayrhll diff --git a/day_14/src/lib.rs b/day_14/src/lib.rs new file mode 100644 index 0000000..29070db --- /dev/null +++ b/day_14/src/lib.rs @@ -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 = 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::(); + } + + 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::(); + } + } + + 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::(); + } + } + + count + } + + fn value_at(i: &[T], width: usize, row: usize, col: usize) -> &T { + &i[row * width + col] + } + + fn set_value_at(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, 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 { + let mut output: Vec = vec![0; 128 * 128]; + let mut buffer: Vec = 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); + } +} diff --git a/day_14/src/main.rs b/day_14/src/main.rs new file mode 100644 index 0000000..7172ed1 --- /dev/null +++ b/day_14/src/main.rs @@ -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)); +}