Skip to content

Commit

Permalink
Day 14: Parabolic Reflector Dish
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemient committed Dec 14, 2023
1 parent 1e644e1 commit 96fdecd
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Development occurs in language-specific directories:
|[Day11.hs](hs/src/Day11.hs)|[Day11.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day11.kt)|[day11.py](py/aoc2023/day11.py)|[day11.rs](rs/src/day11.rs)|
|[Day12.hs](hs/src/Day12.hs)|[Day12.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day12.kt)|[day12.py](py/aoc2023/day12.py)|[day12.rs](rs/src/day12.rs)|
|[Day13.hs](hs/src/Day13.hs)|[Day13.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day13.kt)|[day13.py](py/aoc2023/day13.py)|[day13.rs](rs/src/day13.rs)|
|[Day14.hs](hs/src/Day14.hs)|[Day14.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day14.kt)|[day14.py](py/aoc2023/day14.py)||
|[Day14.hs](hs/src/Day14.hs)|[Day14.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day14.kt)|[day14.py](py/aoc2023/day14.py)|[day14.rs](rs/src/day14.rs)|
10 changes: 9 additions & 1 deletion rs/benches/criterion.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use aoc2023::{day1, day10, day11, day12, day13, day2, day3, day4, day5, day6, day7, day8, day9};
use aoc2023::{
day1, day10, day11, day12, day13, day14, day2, day3, day4, day5, day6, day7, day8, day9,
};
use criterion::{black_box, Criterion};
use std::env;
use std::fs;
Expand Down Expand Up @@ -92,6 +94,12 @@ fn aoc2023_bench(c: &mut Criterion) -> io::Result<()> {
g.bench_function("part 2", |b| b.iter(|| day13::part2(black_box(&data))));
g.finish();

let data = get_day_input(14)?;
let mut g = c.benchmark_group("day 14");
g.bench_function("part 1", |b| b.iter(|| day14::part1(black_box(&data))));
g.bench_function("part 2", |b| b.iter(|| day14::part2(black_box(&data))));
g.finish();

Ok(())
}

Expand Down
121 changes: 121 additions & 0 deletions rs/src/day14.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use std::collections::{hash_map::Entry, HashMap};

fn parse(data: &str) -> (Vec<u8>, usize, usize) {
let lines = data.lines().map(|line| line.as_bytes()).collect::<Vec<_>>();
let width = lines
.iter()
.map(|line| line.len())
.max()
.unwrap_or_default();
(
(0..width)
.flat_map(|x| {
lines
.iter()
.map(move |line| if x < line.len() { line[x] } else { b'.' })
})
.collect(),
width,
lines.len(),
)
}

fn tilt(data: &mut [u8], height: usize) {
for col in data.chunks_mut(height) {
for group in col.split_mut(|&c| c == b'#') {
let n = group.iter().filter(|&&c| c == b'O').count();
group[..n].fill(b'O');
group[n..].fill(b'.');
}
}
}

fn rotate(src: &[u8], dst: &mut [u8], width: usize, height: usize) {
for (i, c) in (0..height)
.rev()
.flat_map(|y| (0..width).map(move |x| src[x * height + y]))
.enumerate()
{
dst[i] = c;
}
}

fn spin(data: &mut [u8], temp: &mut [u8], width: usize, height: usize) {
tilt(data, height);
rotate(data, temp, width, height);
tilt(temp, width);
rotate(temp, data, height, width);
tilt(data, height);
rotate(data, temp, width, height);
tilt(temp, width);
rotate(temp, data, height, width);
}

fn load(data: &[u8], height: usize) -> usize {
data.chunks(height)
.flat_map(|col| {
col.iter()
.enumerate()
.filter_map(|(y, &c)| if c == b'O' { Some(height - y) } else { None })
})
.sum()
}

pub fn part1(data: &str) -> usize {
let (mut data, _, height) = parse(data);
tilt(&mut data, height);
load(&data, height)
}

const N: usize = 1000000000;

pub fn part2(data: &str) -> usize {
let (mut data, width, height) = parse(data);
let mut temp = vec![0; data.len()];
let mut cache = [(data.clone(), 0)].into_iter().collect::<HashMap<_, _>>();
for i in 1..=N {
spin(&mut data, &mut temp, width, height);
match cache.entry(data.clone()) {
Entry::Vacant(entry) => {
entry.insert(i);
}
Entry::Occupied(entry) => {
for _ in 0..(N - i) % (i - *entry.get()) {
spin(&mut data, &mut temp, width, height);
}
break;
}
}
}
load(&data, height)
}

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

static EXAMPLE: &str = indoc! {"
O....#....
O.OO#....#
.....##...
OO.#O....O
.O.....O#.
O.#..O.#.#
..O..#O..O
.......O..
#....###..
#OO..#....
"};

#[test]
fn part1_examples() {
assert_eq!(136, part1(EXAMPLE));
}

#[test]
fn part2_examples() {
assert_eq!(64, part2(EXAMPLE));
}
}
1 change: 1 addition & 0 deletions rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod day10;
pub mod day11;
pub mod day12;
pub mod day13;
pub mod day14;
pub mod day2;
pub mod day3;
pub mod day4;
Expand Down
12 changes: 11 additions & 1 deletion rs/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use aoc2023::{day1, day10, day11, day12, day13, day2, day3, day4, day5, day6, day7, day8, day9};
use aoc2023::{
day1, day10, day11, day12, day13, day14, day2, day3, day4, day5, day6, day7, day8, day9,
};
use std::collections::HashSet;
use std::env;
use std::fs;
Expand Down Expand Up @@ -120,5 +122,13 @@ fn main() -> io::Result<()> {
println!();
}

if args.is_empty() || args.contains("14") {
println!("Day 14");
let data = get_day_input(14)?;
println!("{:?}", day14::part1(&data));
println!("{:?}", day14::part2(&data));
println!();
}

Ok(())
}

0 comments on commit 96fdecd

Please sign in to comment.