From b965f4171115d08781cf667dab948bffcbc703b7 Mon Sep 17 00:00:00 2001 From: Patrick Stevens Date: Mon, 6 Dec 2021 09:29:23 +0000 Subject: [PATCH] Day 6, in Rust and Mathematica (#4) --- Cargo.lock | 7 +++ Cargo.toml | 1 + day_6/Cargo.toml | 15 ++++++ day_6/benches/bench.rs | 19 ++++++++ day_6/day_6.m | 14 ++++++ day_6/input.txt | 1 + day_6/src/lib.rs | 102 +++++++++++++++++++++++++++++++++++++++++ day_6/src/main.rs | 7 +++ 8 files changed, 166 insertions(+) create mode 100644 day_6/Cargo.toml create mode 100644 day_6/benches/bench.rs create mode 100644 day_6/day_6.m create mode 100644 day_6/input.txt create mode 100644 day_6/src/lib.rs create mode 100644 day_6/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index caf9e74..ceb798b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,6 +206,13 @@ dependencies = [ "criterion", ] +[[package]] +name = "day_6" +version = "0.1.0" +dependencies = [ + "criterion", +] + [[package]] name = "either" version = "1.6.1" diff --git a/Cargo.toml b/Cargo.toml index 9853fed..b433b71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,5 @@ members = [ "day_3", "day_4", "day_5", + "day_6", ] diff --git a/day_6/Cargo.toml b/day_6/Cargo.toml new file mode 100644 index 0000000..ead55f9 --- /dev/null +++ b/day_6/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "day_6" +version = "0.1.0" +authors = ["Smaug123 "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +[dev-dependencies] +criterion = "0.3" + +[[bench]] +name = "bench" +harness = false diff --git a/day_6/benches/bench.rs b/day_6/benches/bench.rs new file mode 100644 index 0000000..1af5ea4 --- /dev/null +++ b/day_6/benches/bench.rs @@ -0,0 +1,19 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use day_6::day_6::{input, part_1, part_2}; + +fn criterion_benchmark(c: &mut Criterion) { + let input = input(); + c.bench_function("day 6 part 1", |b| { + b.iter(|| { + black_box(part_1(&input)); + }) + }); + c.bench_function("day 6 part 2", |b| { + b.iter(|| { + black_box(part_2(&input)); + }) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/day_6/day_6.m b/day_6/day_6.m new file mode 100644 index 0000000..eedbbf5 --- /dev/null +++ b/day_6/day_6.m @@ -0,0 +1,14 @@ +(* ::Package:: *) + +nums=FromDigits/@StringSplit[StringTrim@ReadString[StringJoin[NotebookDirectory[], "/input.txt"]],","]; + +Clear[f]; +f[n_,days_]:=f[n,days]=If[days>n,f[0,days-n],1] +f[0,days_]:=f[0,days]=f[6,days-1]+f[8,days-1] +f[k_,0]:=1 + + +f[#,80]&/@nums//Total + + +f[#,256]&/@nums//Total diff --git a/day_6/input.txt b/day_6/input.txt new file mode 100644 index 0000000..acad901 --- /dev/null +++ b/day_6/input.txt @@ -0,0 +1 @@ +3,4,1,2,1,2,5,1,2,1,5,4,3,2,5,1,5,1,2,2,2,3,4,5,2,5,1,3,3,1,3,4,1,5,3,2,2,1,3,2,5,1,1,4,1,4,5,1,3,1,1,5,3,1,1,4,2,2,5,1,5,5,1,5,4,1,5,3,5,1,1,4,1,2,2,1,1,1,4,2,1,3,1,1,4,5,1,1,1,1,1,5,1,1,4,1,1,1,1,2,1,4,2,1,2,4,1,3,1,2,3,2,4,1,1,5,1,1,1,2,5,5,1,1,4,1,2,2,3,5,1,4,5,4,1,3,1,4,1,4,3,2,4,3,2,4,5,1,4,5,2,1,1,1,1,1,3,1,5,1,3,1,1,2,1,4,1,3,1,5,2,4,2,1,1,1,2,1,1,4,1,1,1,1,1,5,4,1,3,3,5,3,2,5,5,2,1,5,2,4,4,1,5,2,3,1,5,3,4,1,5,1,5,3,1,1,1,4,4,5,1,1,1,3,1,4,5,1,2,3,1,3,2,3,1,3,5,4,3,1,3,4,3,1,2,1,1,3,1,1,3,1,1,4,1,2,1,2,5,1,1,3,5,3,3,3,1,1,1,1,1,5,3,3,1,1,3,4,1,1,4,1,1,2,4,4,1,1,3,1,3,2,2,1,2,5,3,3,1,1 diff --git a/day_6/src/lib.rs b/day_6/src/lib.rs new file mode 100644 index 0000000..0840b1b --- /dev/null +++ b/day_6/src/lib.rs @@ -0,0 +1,102 @@ +pub mod day_6 { + + pub(crate) fn parse(s: &str) -> Vec { + s.trim() + .split(',') + .map(str::parse::) + .map(|i| i.unwrap()) + .collect() + } + + pub fn input() -> Vec { + parse(include_str!("../input.txt")) + } + + fn tick(data: &mut Vec) { + let mut new_fish = Vec::new(); + for fish in data.iter_mut() { + if *fish == 0 { + *fish = 6; + new_fish.push(8); + } else { + *fish -= 1; + } + } + data.extend(new_fish); + } + + pub(crate) fn execute(data: &[u8], count: u8) -> u64 { + let mut data = data.to_vec(); + for _ in 0..count { + tick(&mut data); + } + + data.len() as u64 + } + + pub fn part_1(data: &[u8]) -> u64 { + execute(data, 80) + } + + fn fish_created_given_spawning(fish_cache: &mut [u64], days: u16) -> u64 { + let possible_result = fish_cache[days as usize]; + if possible_result != 0 { + return possible_result; + } + let result = if days > 9 { + // We start spawning later... + fish_created_given_spawning(fish_cache, days - 9) + // and we create a fish now, which spawns eventually... + + fish_created_given_spawning(fish_cache, days - 7) + } else if days > 7 { + // In principle this could be tail-called, but I can't be bothered. + fish_created_given_spawning(fish_cache, days - 7) + 1 + } else { + 2 + }; + fish_cache[days as usize] = result; + result + } + + fn fish_created(fish_cache: &mut [u64], start: u8, days: u16) -> u64 { + if days == 0 { + return 1; + } + fish_created_given_spawning(fish_cache, days - start as u16) + } + + pub fn part_2(data: &[u8]) -> u64 { + let mut fish_cache = vec![0; 257]; + data.iter() + .map(|i| fish_created(&mut fish_cache, *i, 256)) + .sum() + } +} + +#[cfg(test)] +mod tests { + use super::day_6::*; + + static TEST_INPUT: &str = "3,4,3,1,2"; + + #[test] + fn part1_known() { + let data = parse(TEST_INPUT); + + assert_eq!(execute(&data, 18), 26); + } + + #[test] + fn part2_known() { + let data = parse(TEST_INPUT); + + assert_eq!(part_2(&data), 26984457539); + } + + #[test] + fn test_day_6() { + let input = input(); + assert_eq!(part_1(&input), 365131); + assert_eq!(part_2(&input), 1650309278600); + } +} diff --git a/day_6/src/main.rs b/day_6/src/main.rs new file mode 100644 index 0000000..41c95d7 --- /dev/null +++ b/day_6/src/main.rs @@ -0,0 +1,7 @@ +use day_6::day_6; + +fn main() { + let input = day_6::input(); + println!("part 1 => {}", day_6::part_1(&input)); + println!("part 2 => {}", day_6::part_2(&input)); +}