From 97d52fbb404e6d07bec67d43dff723d3a9d4d3cc Mon Sep 17 00:00:00 2001 From: red Date: Thu, 28 Mar 2024 17:25:12 +0100 Subject: [PATCH] Year 2017: Day 14 --- Cargo.toml | 2 +- NOTES_2017.md | 4 ++ README.md | 2 +- benches/year_2017.rs | 67 +++++++++++++--------- inputs/year_2017/day_14_input | 1 + src/year_2016.rs | 1 + src/year_2017.rs | 11 ++++ src/year_2017/day_10.rs | 47 +++------------- src/year_2017/day_14.rs | 102 ++++++++++++++++++++++++++++++++++ src/year_2017/knot_hash.rs | 33 +++++++++++ 10 files changed, 205 insertions(+), 65 deletions(-) create mode 100644 inputs/year_2017/day_14_input create mode 100644 src/year_2017/day_14.rs create mode 100644 src/year_2017/knot_hash.rs diff --git a/Cargo.toml b/Cargo.toml index 9ebdb9d..1993e84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "advent-rs" -version = "2017.13.1" +version = "2017.14.1" edition = "2021" authors = ["Arnaud 'red' Rouyer"] readme = "README.md" diff --git a/NOTES_2017.md b/NOTES_2017.md index 4fc7f03..ebd306c 100644 --- a/NOTES_2017.md +++ b/NOTES_2017.md @@ -55,3 +55,7 @@ I hate traversing trees. But you don't always have to. ## Day 13: Packet Scanners The maths were a bit annoying on this one, and I was hoping for a specific mathematical formula that would prevent me from looping over all scenarios. I found something that felt promising and ported the code from Python, but to no avail, my original code was already faster... + +## Day 14: Disk Defragmentation + +I kinda liked this one. diff --git a/README.md b/README.md index 4183e26..be815cb 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ As I said, [Consistency is hard](https://github.com/joshleaves/advent-rb), and I I'm also adding notes that may be useful if you're (like me) discovering Rust: - [2015, complete!](NOTES_2015.md) - [2016, complete!](NOTES_2016.md) -- [2017, up to day 13](NOTES_2017.md) +- [2017, up to day 14](NOTES_2017.md) # Regarding style rules I'm gonna use a mix of what `cargo fmt` does, with some stuff that feels more natural to me. diff --git a/benches/year_2017.rs b/benches/year_2017.rs index d0f93cd..675ac03 100644 --- a/benches/year_2017.rs +++ b/benches/year_2017.rs @@ -11,12 +11,13 @@ use advent_rs::year_2017::day_10; use advent_rs::year_2017::day_11; use advent_rs::year_2017::day_12; use advent_rs::year_2017::day_13; +use advent_rs::year_2017::day_14; use criterion::{black_box, criterion_group, criterion_main, Criterion}; fn year_2017_day_01(c: &mut Criterion) { let input_day_01 = include_str!("../inputs/year_2017/day_01_input"); - assert_eq!(day_01::day_01_v1(black_box(input_day_01)), 1_069); - assert_eq!(day_01::day_01_v2(black_box(input_day_01)), 1_268); + assert_eq!(day_01::day_01_v1(input_day_01), 1_069); + assert_eq!(day_01::day_01_v2(input_day_01), 1_268); let mut g2017_day_01 = c.benchmark_group("year_2017::day_01"); g2017_day_01.bench_function("year_2017::day_01_v1", |b| { @@ -30,8 +31,8 @@ fn year_2017_day_01(c: &mut Criterion) { fn year_2017_day_02(c: &mut Criterion) { let input_day_02 = include_str!("../inputs/year_2017/day_02_input"); - assert_eq!(day_02::day_02_v1(black_box(input_day_02)), 53_978); - assert_eq!(day_02::day_02_v2(black_box(input_day_02)), 314); + assert_eq!(day_02::day_02_v1(input_day_02), 53_978); + assert_eq!(day_02::day_02_v2(input_day_02), 314); let mut g2017_day_02 = c.benchmark_group("year_2017::day_02"); g2017_day_02.bench_function("year_2017::day_02_v1", |b| { @@ -45,8 +46,8 @@ fn year_2017_day_02(c: &mut Criterion) { fn year_2017_day_03(c: &mut Criterion) { let input_day_03 = include_str!("../inputs/year_2017/day_03_input"); - assert_eq!(day_03::day_03_v1(black_box(input_day_03)), 552); - assert_eq!(day_03::day_03_v2(black_box(input_day_03)), 330_785); + assert_eq!(day_03::day_03_v1(input_day_03), 552); + assert_eq!(day_03::day_03_v2(input_day_03), 330_785); let mut g2017_day_03 = c.benchmark_group("year_2017::day_03"); g2017_day_03.bench_function("year_2017::day_03_v1", |b| { @@ -60,8 +61,8 @@ fn year_2017_day_03(c: &mut Criterion) { fn year_2017_day_04(c: &mut Criterion) { let input_day_04 = include_str!("../inputs/year_2017/day_04_input"); - assert_eq!(day_04::day_04_v1(black_box(input_day_04)), 466); - assert_eq!(day_04::day_04_v2(black_box(input_day_04)), 251); + assert_eq!(day_04::day_04_v1(input_day_04), 466); + assert_eq!(day_04::day_04_v2(input_day_04), 251); let mut g2017_day_04 = c.benchmark_group("year_2017::day_04"); g2017_day_04.bench_function("year_2017::day_04_v1", |b| { @@ -75,8 +76,8 @@ fn year_2017_day_04(c: &mut Criterion) { fn year_2017_day_05(c: &mut Criterion) { let input_day_05 = include_str!("../inputs/year_2017/day_05_input"); - assert_eq!(day_05::day_05_v1(black_box(input_day_05)), 373_160); - assert_eq!(day_05::day_05_v2(black_box(input_day_05)), 26_395_586); + assert_eq!(day_05::day_05_v1(input_day_05), 373_160); + assert_eq!(day_05::day_05_v2(input_day_05), 26_395_586); let mut g2017_day_05 = c.benchmark_group("year_2017::day_05"); g2017_day_05.bench_function("year_2017::day_05_v1", |b| { @@ -90,8 +91,8 @@ fn year_2017_day_05(c: &mut Criterion) { fn year_2017_day_06(c: &mut Criterion) { let input_day_06 = include_str!("../inputs/year_2017/day_06_input"); - assert_eq!(day_06::day_06_v1(black_box(input_day_06)), 11_137); - assert_eq!(day_06::day_06_v2(black_box(input_day_06)), 1_037); + assert_eq!(day_06::day_06_v1(input_day_06), 11_137); + assert_eq!(day_06::day_06_v2(input_day_06), 1_037); let mut g2017_day_06 = c.benchmark_group("year_2017::day_06"); g2017_day_06.bench_function("year_2017::day_06_v1", |b| { @@ -105,8 +106,8 @@ fn year_2017_day_06(c: &mut Criterion) { fn year_2017_day_07(c: &mut Criterion) { let input_day_07 = include_str!("../inputs/year_2017/day_07_input"); - assert_eq!(day_07::day_07_v1(black_box(input_day_07)), "ykpsek"); - assert_eq!(day_07::day_07_v2(black_box(input_day_07)), "1060"); + assert_eq!(day_07::day_07_v1(input_day_07), "ykpsek"); + assert_eq!(day_07::day_07_v2(input_day_07), "1060"); let mut g2017_day_07 = c.benchmark_group("year_2017::day_07"); g2017_day_07.bench_function("year_2017::day_07_v1", |b| { @@ -120,8 +121,8 @@ fn year_2017_day_07(c: &mut Criterion) { fn year_2017_day_08(c: &mut Criterion) { let input_day_08 = include_str!("../inputs/year_2017/day_08_input"); - assert_eq!(day_08::day_08_v1(black_box(input_day_08)), 4_163); - assert_eq!(day_08::day_08_v2(black_box(input_day_08)), 5_347); + assert_eq!(day_08::day_08_v1(input_day_08), 4_163); + assert_eq!(day_08::day_08_v2(input_day_08), 5_347); let mut g2017_day_08 = c.benchmark_group("year_2017::day_08"); g2017_day_08.bench_function("year_2017::day_08_v1", |b| { @@ -135,8 +136,8 @@ fn year_2017_day_08(c: &mut Criterion) { fn year_2017_day_09(c: &mut Criterion) { let input_day_09 = include_str!("../inputs/year_2017/day_09_input"); - assert_eq!(day_09::day_09_v1(black_box(input_day_09)), 17_537); - assert_eq!(day_09::day_09_v2(black_box(input_day_09)), 7_539); + assert_eq!(day_09::day_09_v1(input_day_09), 17_537); + assert_eq!(day_09::day_09_v2(input_day_09), 7_539); let mut g2017_day_09 = c.benchmark_group("year_2017::day_09"); g2017_day_09.bench_function("year_2017::day_09_v1", |b| { @@ -168,8 +169,8 @@ fn year_2017_day_10(c: &mut Criterion) { fn year_2017_day_11(c: &mut Criterion) { let input_day_11 = include_str!("../inputs/year_2017/day_11_input"); - assert_eq!(day_11::day_11_v1(black_box(input_day_11)), 682); - assert_eq!(day_11::day_11_v2(black_box(input_day_11)), 1_406); + assert_eq!(day_11::day_11_v1(input_day_11), 682); + assert_eq!(day_11::day_11_v2(input_day_11), 1_406); let mut g2017_day_11 = c.benchmark_group("year_2017::day_11"); g2017_day_11.bench_function("year_2017::day_11_v1", |b| { @@ -183,8 +184,8 @@ fn year_2017_day_11(c: &mut Criterion) { fn year_2017_day_12(c: &mut Criterion) { let input_day_12 = include_str!("../inputs/year_2017/day_12_input"); - assert_eq!(day_12::day_12_v1(black_box(input_day_12)), 130); - assert_eq!(day_12::day_12_v2(black_box(input_day_12)), 189); + assert_eq!(day_12::day_12_v1(input_day_12), 130); + assert_eq!(day_12::day_12_v2(input_day_12), 189); let mut g2017_day_12 = c.benchmark_group("year_2017::day_12"); g2017_day_12.bench_function("year_2017::day_12_v1", |b| { @@ -198,8 +199,8 @@ fn year_2017_day_12(c: &mut Criterion) { fn year_2017_day_13(c: &mut Criterion) { let input_day_13 = include_str!("../inputs/year_2017/day_13_input"); - assert_eq!(day_13::day_13_v1(black_box(input_day_13)), 2_264); - assert_eq!(day_13::day_13_v2(black_box(input_day_13)), 3_875_838); + assert_eq!(day_13::day_13_v1(input_day_13), 2_264); + assert_eq!(day_13::day_13_v2(input_day_13), 3_875_838); let mut g2017_day_13 = c.benchmark_group("year_2017::day_13"); g2017_day_13.bench_function("year_2017::day_13_v1", |b| { @@ -211,6 +212,21 @@ fn year_2017_day_13(c: &mut Criterion) { g2017_day_13.finish(); } +fn year_2017_day_14(c: &mut Criterion) { + let input_day_14 = include_str!("../inputs/year_2017/day_14_input"); + assert_eq!(day_14::day_14_v1(input_day_14), 8_230); + assert_eq!(day_14::day_14_v2(input_day_14), 1_103); + + let mut g2017_day_14 = c.benchmark_group("year_2017::day_14"); + g2017_day_14.bench_function("year_2017::day_14_v1", |b| { + b.iter(|| day_14::day_14_v1(black_box(input_day_14))) + }); + g2017_day_14.bench_function("year_2017::day_14_v2", |b| { + b.iter(|| day_14::day_14_v2(black_box(input_day_14))) + }); + g2017_day_14.finish(); +} + criterion_group!( benches, year_2017_day_01, @@ -225,6 +241,7 @@ criterion_group!( year_2017_day_10, year_2017_day_11, year_2017_day_12, - year_2017_day_13 + year_2017_day_13, + year_2017_day_14 ); criterion_main!(benches); diff --git a/inputs/year_2017/day_14_input b/inputs/year_2017/day_14_input new file mode 100644 index 0000000..754197e --- /dev/null +++ b/inputs/year_2017/day_14_input @@ -0,0 +1 @@ +hfdlxzhv diff --git a/src/year_2016.rs b/src/year_2016.rs index a3d14f5..9b3134d 100644 --- a/src/year_2016.rs +++ b/src/year_2016.rs @@ -3,6 +3,7 @@ #![doc = include_str!("../NOTES_2016.md")] mod assembunny; + pub mod day_01; pub mod day_02; pub mod day_03; diff --git a/src/year_2017.rs b/src/year_2017.rs index 8b64e78..b91d6fd 100644 --- a/src/year_2017.rs +++ b/src/year_2017.rs @@ -2,6 +2,8 @@ //! #![doc = include_str!("../NOTES_2017.md")] +mod knot_hash; + pub mod day_01; pub mod day_02; pub mod day_03; @@ -15,6 +17,7 @@ pub mod day_10; pub mod day_11; pub mod day_12; pub mod day_13; +pub mod day_14; pub fn solve(day: u8, part: u8, input: impl Into) -> Option { if part > 2 { @@ -35,6 +38,7 @@ pub fn solve(day: u8, part: u8, input: impl Into) -> Option { 11 => Some(day_11::day_11(part, input).to_string()), 12 => Some(day_12::day_12(part, input).to_string()), 13 => Some(day_13::day_13(part, input).to_string()), + 14 => Some(day_14::day_14(part, input).to_string()), _ => None, } } @@ -133,4 +137,11 @@ mod tests { assert_eq!(day_13::day_13_v1(input), 2_264); assert_eq!(day_13::day_13_v2(input), 3_875_838); } + + #[test] + fn day_14() { + let input = include_str!("../inputs/year_2017/day_14_input"); + assert_eq!(day_14::day_14_v1(input), 8_230); + assert_eq!(day_14::day_14_v2(input), 1_103); + } } diff --git a/src/year_2017/day_10.rs b/src/year_2017/day_10.rs index 7e307e2..afa1a99 100644 --- a/src/year_2017/day_10.rs +++ b/src/year_2017/day_10.rs @@ -1,7 +1,6 @@ -use itertools::Itertools; +use super::knot_hash; -#[inline] -fn parse_jumps_v1(input: &str) -> Vec { +fn parse_jumps(input: &str) -> Vec { input .trim_end() .split(',') @@ -9,8 +8,7 @@ fn parse_jumps_v1(input: &str) -> Vec { .collect() } -#[inline] -fn parse_jumps_v2(input: &str) -> Vec { +fn parse_input(input: &str) -> Vec { input .trim_end() .bytes() @@ -18,42 +16,15 @@ fn parse_jumps_v2(input: &str) -> Vec { .collect() } -fn knot_hash(size: u32, jumps: &[u32], rounds: u32) -> Vec { - let mut numbers: Vec<_> = (0..size).collect_vec(); - let num_lens = numbers.len(); - let mut jumps_done = 0; - let mut skip_size = 0; - - for _ in 0..rounds { - for jump in jumps.iter() { - let extract = numbers[0..*jump as usize] - .iter() - .cloned() - .rev() - .collect_vec(); - numbers.splice(0..*jump as usize, extract); - numbers.rotate_left(((jump + skip_size) % num_lens as u32) as usize); - jumps_done += (jump + skip_size) % num_lens as u32; - skip_size += 1; - } - } - numbers.rotate_right(jumps_done as usize % num_lens); - - numbers -} - pub fn day_10_v1(input: impl Into) -> String { - let numbers = knot_hash(256, &parse_jumps_v1(&input.into()), 1); + let numbers = knot_hash::_knot_hash(256, &parse_jumps(&input.into()), 1); (numbers[0] * numbers[1]).to_string() } pub fn day_10_v2(input: impl Into) -> String { - let mut jumps = parse_jumps_v2(&input.into()); - jumps.append(&mut vec![17, 31, 73, 47, 23]); - - knot_hash(256, &jumps, 64) - .chunks(16) - .map(|chunk| chunk.iter().fold(0, |acc, &val| acc ^ val) as u8) + let mut input = parse_input(&input.into()); + knot_hash::knot_hash(&mut input) + .iter() .map(|xor| format!("{:02x}", xor)) .collect::>() .join("") @@ -67,8 +38,8 @@ mod tests { #[test] fn works_with_samples_v1() { - let jumps = parse_jumps_v1("3, 4, 1, 5"); - let numbers = knot_hash(5, &jumps, 1); + let jumps = parse_jumps("3, 4, 1, 5"); + let numbers = knot_hash::_knot_hash(5, &jumps, 1); assert_eq!(numbers[0] * numbers[1], 12); } diff --git a/src/year_2017/day_14.rs b/src/year_2017/day_14.rs new file mode 100644 index 0000000..41b2191 --- /dev/null +++ b/src/year_2017/day_14.rs @@ -0,0 +1,102 @@ +use super::knot_hash::knot_hash; +use itertools::Itertools; +use std::collections::VecDeque; + +fn generate_rows(input: &str, rows: usize) -> Vec> { + let input = input.as_bytes().to_vec(); + (0..rows) + .map(|row| { + let mut value = input.to_owned(); + value.push(b'-'); + value.append(&mut row.to_string().as_bytes().to_owned()); + value.iter().map(|c| *c as u32).collect() + }) + .collect_vec() +} + +#[inline] +fn fill_map(map: &mut [Vec], starter: (usize, usize), value: i32) { + let mut positions = VecDeque::from([starter]); + while let Some(position) = positions.pop_front() { + if map[position.0][position.1] != -1 { + continue; + } + map[position.0][position.1] = value; + if position.0 > 0 { + let up = (position.0 - 1, position.1); + positions.push_back(up) + } + if position.0 + 1 < 128 { + let down = (position.0 + 1, position.1); + positions.push_back(down) + } + if position.1 > 0 { + let left = (position.0, position.1 - 1); + positions.push_back(left) + } + if position.1 + 1 < 128 { + let right = (position.0, position.1 + 1); + positions.push_back(right) + } + } +} + +pub fn day_14_v1(input: impl Into) -> u32 { + let mut rows = generate_rows(input.into().trim_end(), 128); + rows + .iter_mut() + .map(|row| { + knot_hash(&mut *row) + .iter() + .map(|hash| hash.count_ones()) + .sum::() + }) + .sum() +} + +pub fn day_14_v2(input: impl Into) -> u32 { + let mut rows = generate_rows(input.into().trim_end(), 128); + let mut map: Vec<_> = rows + .iter_mut() + .map(|row| { + knot_hash(&mut *row) + .iter() + .flat_map(|hash| { + (0..=7) + .map(|x| -(((*hash >> x) & 1) as i32)) + .rev() + .collect::>() + }) + .collect::>() + }) + .collect(); + let mut cnt_groups = 0; + for y in 0..128 { + for x in 0..128 { + if map[y][x] == -1 { + cnt_groups += 1; + fill_map(&mut map, (y, x), cnt_groups); + } + } + } + cnt_groups as u32 +} + +solvable!(day_14, day_14_v1, day_14_v2, u32); + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE: &str = "flqrgnkx"; + + #[test] + fn works_with_samples_v1() { + assert_eq!(day_14_v1(SAMPLE), 8108); + } + + #[test] + fn works_with_samples_v2() { + assert_eq!(day_14_v2(SAMPLE), 1242); + } +} diff --git a/src/year_2017/knot_hash.rs b/src/year_2017/knot_hash.rs new file mode 100644 index 0000000..2a57a2c --- /dev/null +++ b/src/year_2017/knot_hash.rs @@ -0,0 +1,33 @@ +use itertools::Itertools; + +pub fn knot_hash(jumps: &mut Vec) -> Vec { + jumps.append(&mut vec![17, 31, 73, 47, 23]); + _knot_hash(256, jumps, 64) + .chunks(16) + .map(|chunk| chunk.iter().fold(0, |acc, &val| acc ^ val) as u8) + .collect::>() +} + +pub(crate) fn _knot_hash(size: u32, jumps: &[u32], rounds: u32) -> Vec { + let mut numbers: Vec<_> = (0..size).collect_vec(); + let num_lens = numbers.len(); + let mut jumps_done = 0; + let mut skip_size = 0; + + for _ in 0..rounds { + for jump in jumps.iter() { + let extract = numbers[0..*jump as usize] + .iter() + .cloned() + .rev() + .collect_vec(); + numbers.splice(0..*jump as usize, extract); + numbers.rotate_left(((jump + skip_size) % num_lens as u32) as usize); + jumps_done += (jump + skip_size) % num_lens as u32; + skip_size += 1; + } + } + numbers.rotate_right(jumps_done as usize % num_lens); + + numbers +}