Skip to content

Commit

Permalink
Year 2016: Day 16
Browse files Browse the repository at this point in the history
  • Loading branch information
joshleaves committed Mar 20, 2024
1 parent 0fb71f3 commit 707d37f
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Of note:
- The changelog 2015.5.2 has been rewritten from each commit content.
- This file may be amended entirely in the future to adhere to the [GNU Changelog style](https://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html#Style-of-Change-Logs)

## [2016.16.1]
### Added
- Solved [exercice for 2016, day 16](src/year_2016/day_16.rs).

## [2016.15.1]
### Added
- Solved [exercice for 2016, day 15](src/year_2016/day_15.rs).
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "advent-rs"
version = "2016.15.1"
version = "2016.16.1"
edition = "2021"
authors = ["Arnaud 'red' Rouyer"]
readme = "README.md"
Expand Down
6 changes: 6 additions & 0 deletions NOTES_2016.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,9 @@ Computing MD5 hashes is never interesting.
## Day 15: Timing is Everything

Finally, something funny to do.

## Day 16: Dragon Checksum

For once, I'm faster than my [rustaceans counterparts](https://docs.rs/advent-of-code/2022.0.66/src/advent_of_code/year2016/day16.rs.html). In that case, manipulating `Vec<bool>` instead of `String` worked better.

Another thing of note is that if you know how to optimize your loop-down, you can win a lot of time.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,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, up to day 15](NOTES_2016.md)
- [2016, up to day 16](NOTES_2016.md)

# Regarding style rules
I'm gonna use a mix of what `cargo fmt` does, with some stuff that feels more natural to me.
Expand Down
16 changes: 15 additions & 1 deletion benches/year_2016.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use advent_rs::year_2016::day_12;
use advent_rs::year_2016::day_13;
use advent_rs::year_2016::day_14;
use advent_rs::year_2016::day_15;
use advent_rs::year_2016::day_16;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

pub fn year_2016_day_01(c: &mut Criterion) {
Expand Down Expand Up @@ -195,6 +196,18 @@ pub fn year_2016_day_15(c: &mut Criterion) {
g2016_day_15.finish();
}

pub fn year_2016_day_16(c: &mut Criterion) {
let mut g2016_day_16 = c.benchmark_group("year_2016::day_16");
let input_year_2016_day_16 = include_str!("../inputs/year_2016/day_16_input");
g2016_day_16.bench_function("year_2016::day_16_v1", |b| {
b.iter(|| day_16::day_16_v1(black_box(input_year_2016_day_16)))
});
g2016_day_16.bench_function("year_2016::day_16_v2", |b| {
b.iter(|| day_16::day_16_v2(black_box(input_year_2016_day_16)))
});
g2016_day_16.finish();
}

criterion_group!(
benches,
year_2016_day_01,
Expand All @@ -211,6 +224,7 @@ criterion_group!(
year_2016_day_12,
year_2016_day_13,
year_2016_day_14,
year_2016_day_15
year_2016_day_15,
year_2016_day_16
);
criterion_main!(benches);
1 change: 1 addition & 0 deletions inputs/year_2016/day_16_input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
11100010111110100
9 changes: 9 additions & 0 deletions src/year_2016.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod day_12;
pub mod day_13;
pub mod day_14;
pub mod day_15;
pub mod day_16;

pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<String> {
if part != 1 && part != 2 {
Expand All @@ -40,6 +41,7 @@ pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<String> {
13 => Some(format!("{}", day_13::day_13(part, input))),
14 => Some(format!("{}", day_14::day_14(part, input))),
15 => Some(format!("{}", day_15::day_15(part, input))),
16 => Some(format!("{}", day_16::day_16(part, input))),
_ => None,
}
}
Expand Down Expand Up @@ -161,4 +163,11 @@ mod tests {
assert_eq!(day_15::day_15_v1(input), 203_660);
assert_eq!(day_15::day_15_v2(input), 2_408_135);
}

#[test]
fn day_16() {
let input = include_str!("../inputs/year_2016/day_16_input");
assert_eq!(day_16::day_16_v1(input), "10100011010101011");
assert_eq!(day_16::day_16_v2(input), "01010001101011001");
}
}
133 changes: 133 additions & 0 deletions src/year_2016/day_16.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use itertools::Itertools;

const DIVIDERS: [usize; 15] = [
65536,
32768,
16384,
8192,
4096,
2048,
1024,
512,
256,
128,
64,
32,
16,
8,
4,
];

#[inline]
fn string2bools(input: &str) -> Vec<bool> {
input
.trim_end()
.chars()
.map(|c| if c == '1' { true } else { false })
.collect_vec()
}

#[inline]
fn bools2string(input: Vec<bool>) -> String {
input
.into_iter()
.map(|b| if b { '1' } else { '0' })
.collect()
}

fn fill_until(mut input: Vec<bool>, disk_size: usize) -> Vec<bool> {
while input.len() < disk_size {
let mut reverse = input.iter().rev().map(|b| !b).collect_vec();
input.push(false);
input.append(&mut reverse);
}
input.truncate(disk_size);

input
}

#[inline]
fn fold16(input: &[bool]) -> bool {
fold8(&input[0..8]) == fold8(&input[8..16])
}

#[inline]
fn fold8(input: &[bool]) -> bool {
fold4(&input[0..4]) == fold4(&input[4..8])
}

#[inline]
fn fold4(input: &[bool]) -> bool {
(input[0] == input[1]) == (input[2] == input[3])
}

#[inline]
fn completely_reduce(mut input: Vec<bool>) -> bool {
while input.len() % 16 == 0 {
input = input.chunks(16).map(|chunk| fold16(chunk)).collect_vec();
}
while input.len() % 8 == 0 {
input = input.chunks(8).map(|chunk| fold8(chunk)).collect_vec();
}
while input.len() % 4 == 0 {
input = input.chunks(4).map(|chunk| fold4(chunk)).collect_vec();
}
if input.len() == 2 {
return input[0] == input[1];
}
input[0]
}

fn checksum(input: Vec<bool>) -> Vec<bool> {
let mut results = input;
for divider in DIVIDERS {
while results.len() % divider == 0 {
results = results
.chunks(divider)
.map(|chunk| completely_reduce(chunk.to_vec()))
.collect_vec()
}
}

while results.len() % 2 == 0 {
results = results
.chunks(2)
.map(|pair| pair[0] == pair[1])
.collect_vec()
}

results
}

pub fn calculate_checksum(input: &str, disk_size: usize) -> String {
let mut values = string2bools(&input);
values = fill_until(values, disk_size);
values = checksum(values);

bools2string(values)
}

const DISK_SIZE_V1: usize = 272;

pub fn day_16_v1(input: impl Into<String>) -> String {
calculate_checksum(&input.into(), DISK_SIZE_V1)
}

const DISK_SIZE_V2: usize = 35_651_584;

pub fn day_16_v2(input: impl Into<String>) -> String {
calculate_checksum(&input.into(), DISK_SIZE_V2)
}

solvable!(day_16, day_16_v1, day_16_v2, String);

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn works_with_samples() {
let sample_one = "10000";
assert_eq!(calculate_checksum(sample_one, 20), "01100");
}
}

0 comments on commit 707d37f

Please sign in to comment.