Skip to content

Commit

Permalink
Year 2017: Day 15
Browse files Browse the repository at this point in the history
  • Loading branch information
joshleaves committed Mar 29, 2024
1 parent f395727 commit 50cb032
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 4 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "advent-rs"
version = "2017.14.1"
version = "2017.15.1"
edition = "2021"
authors = ["Arnaud 'red' Rouyer"]
readme = "README.md"
Expand Down Expand Up @@ -32,10 +32,10 @@ clap = { version = "4.5.1", features = ["derive"] }
itertools = "0.12.1"
md-5 = "0.10.6"
mutants = "0.0.3"
regex = "1.10.3"

[dev-dependencies]
assert_cmd = "2.0.13"
regex = "1.10.3"
predicates = "3.1.0"
criterion = { version = "0.5.1", features = ["html_reports"] }

Expand Down
4 changes: 4 additions & 0 deletions NOTES_2017.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,7 @@ The maths were a bit annoying on this one, and I was hoping for a specific mathe
## Day 14: Disk Defragmentation

I kinda liked this one.

## Day 15: Dueling Generators

The most interesting part of these exercises is often [all the obscure maths you can leanr about](https://www.reddit.com/r/adventofcode/comments/7jyz5x/2017_day_15_opportunities_for_optimization/drasfzr/?context=3), in that case [Mersenne prime](https://en.wikipedia.org/wiki/Mersenne_prime). But the peculiarities of the number used for the remainder [should have been obvious to people who wrote a lot of C](https://doc.rust-lang.org/std/i32/constant.MAX.html).
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 14](NOTES_2017.md)
- [2017, up to day 15](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.
Expand Down
19 changes: 18 additions & 1 deletion benches/year_2017.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ 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 advent_rs::year_2017::day_15;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn year_2017_day_01(c: &mut Criterion) {
Expand Down Expand Up @@ -227,6 +228,21 @@ fn year_2017_day_14(c: &mut Criterion) {
g2017_day_14.finish();
}

fn year_2017_day_15(c: &mut Criterion) {
let input_day_15 = include_str!("../inputs/year_2017/day_15_input");
assert_eq!(day_15::day_15_v1(input_day_15), 567);
assert_eq!(day_15::day_15_v2(input_day_15), 323);

let mut g2017_day_15 = c.benchmark_group("year_2017::day_15");
g2017_day_15.bench_function("year_2017::day_15_v1", |b| {
b.iter(|| day_15::day_15_v1(black_box(input_day_15)))
});
g2017_day_15.bench_function("year_2017::day_15_v2", |b| {
b.iter(|| day_15::day_15_v2(black_box(input_day_15)))
});
g2017_day_15.finish();
}

criterion_group!(
benches,
year_2017_day_01,
Expand All @@ -242,6 +258,7 @@ criterion_group!(
year_2017_day_11,
year_2017_day_12,
year_2017_day_13,
year_2017_day_14
year_2017_day_14,
year_2017_day_15
);
criterion_main!(benches);
2 changes: 2 additions & 0 deletions inputs/year_2017/day_15_input
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Generator A starts with 512
Generator B starts with 191
9 changes: 9 additions & 0 deletions src/year_2017.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod day_11;
pub mod day_12;
pub mod day_13;
pub mod day_14;
pub mod day_15;

pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<String> {
if part > 2 {
Expand All @@ -39,6 +40,7 @@ pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<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()),
15 => Some(day_15::day_15(part, input).to_string()),
_ => None,
}
}
Expand Down Expand Up @@ -144,4 +146,11 @@ mod tests {
assert_eq!(day_14::day_14_v1(input), 8_230);
assert_eq!(day_14::day_14_v2(input), 1_103);
}

#[test]
fn day_15() {
let input = include_str!("../inputs/year_2017/day_15_input");
assert_eq!(day_15::day_15_v1(input), 567);
assert_eq!(day_15::day_15_v2(input), 323);
}
}
85 changes: 85 additions & 0 deletions src/year_2017/day_15.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
struct Generator {
factor: u64,
value: u64,
multiples: u64,
}

const FACTOR_A: u64 = 16_807;
const FACTOR_B: u64 = 48_271;

impl Generator {
fn new(value: u64, factor: u64, multiples: u64) -> Self {
Generator {
value,
factor,
multiples,
}
}
}

impl Iterator for Generator {
// We can refer to this type using Self::Item
type Item = u64;

fn next(&mut self) -> Option<Self::Item> {
loop {
let prod = self.value * self.factor;
let g = (prod & 0x7fff_ffff) + (prod >> 31);
self.value = if g >> 31 == 0 { g } else { g - 0x7fff_ffff };
if (self.value & self.multiples - 1) == 0 {
return Some(self.value);
}
}
}
}

fn parse_input(input: &str) -> (u64, u64) {
let mut input = input.lines();
let line_a = input.next().unwrap().split_whitespace().last().unwrap();
let line_b = input.next().unwrap().split_whitespace().last().unwrap();
(
line_a.parse::<u64>().unwrap(),
line_b.parse::<u64>().unwrap(),
)
}

pub fn day_15_v1(input: impl Into<String>) -> u64 {
let inputs = parse_input(&input.into());
let gen_a = Generator::new(inputs.0, FACTOR_A, 1);
let gen_b = Generator::new(inputs.1, FACTOR_B, 1);
gen_a
.zip(gen_b)
.take(40_000_000)
.filter(|&(a, b)| (a & 0xFFFF) == (b & 0xFFFF))
.count() as u64
}

pub fn day_15_v2(input: impl Into<String>) -> u64 {
let inputs = parse_input(&input.into());
let gen_a = Generator::new(inputs.0, FACTOR_A, 4);
let gen_b = Generator::new(inputs.1, FACTOR_B, 8);
gen_a
.zip(gen_b)
.take(5_000_000)
.filter(|&(a, b)| (a & 0xFFFF) == (b & 0xFFFF))
.count() as u64
}
solvable!(day_15, day_15_v1, day_15_v2, u64);

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

const SAMPLE: &str = "Generator A starts with 65\n\
Generator B starts with 8921";

#[test]
fn works_with_samples_v1() {
assert_eq!(day_15_v1(SAMPLE), 588);
}

#[test]
fn works_with_samples_v2() {
assert_eq!(day_15_v2(SAMPLE), 309);
}
}

0 comments on commit 50cb032

Please sign in to comment.