mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
137 lines
3.3 KiB
Rust
137 lines
3.3 KiB
Rust
use anyhow::Context;
|
|
use anyhow::Result;
|
|
|
|
#[inline]
|
|
fn stripe<'a>(
|
|
values: impl IntoIterator<Item = &'a u8>,
|
|
visible: impl IntoIterator<Item = &'a mut bool>,
|
|
) {
|
|
let mut max = 0;
|
|
|
|
for (&val, visible) in values.into_iter().zip(visible) {
|
|
if val > max {
|
|
max = val;
|
|
*visible = true;
|
|
|
|
if val == b'9' {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn part1(input: &[u8]) -> Result<String> {
|
|
let width = input
|
|
.iter()
|
|
.position(|&b| b == b'\n')
|
|
.context("Single row field")?;
|
|
let height = input.len() / (width + 1); // Include newlines
|
|
|
|
let mut visible = vec![false; width * height];
|
|
|
|
// Horizontal striping
|
|
for (y, row) in input.chunks_exact(width + 1).enumerate() {
|
|
// First, left to right
|
|
stripe(&row[..width], &mut visible[(y * width)..]);
|
|
|
|
// Then right to left
|
|
stripe(
|
|
row[..width].iter().rev(),
|
|
visible[(y * width)..(y * width + width)].iter_mut().rev(),
|
|
);
|
|
}
|
|
|
|
// Vertical striping
|
|
for x in 0..width {
|
|
// Top to bottom
|
|
stripe(
|
|
input[x..].iter().step_by(width + 1),
|
|
visible[x..].iter_mut().step_by(width),
|
|
);
|
|
|
|
// Bottom to top
|
|
stripe(
|
|
input[x..].iter().step_by(width + 1).rev(),
|
|
visible[x..].iter_mut().step_by(width).rev(),
|
|
)
|
|
}
|
|
|
|
Ok(visible.into_iter().filter(|&b| b).count().to_string())
|
|
}
|
|
|
|
#[inline]
|
|
fn scenery<'a>(
|
|
values: impl IntoIterator<Item = &'a u8>,
|
|
visible: impl IntoIterator<Item = &'a mut usize>,
|
|
) {
|
|
let mut last_seen = [0; 10];
|
|
|
|
for (i, (&val, score)) in values.into_iter().zip(visible).enumerate() {
|
|
let val = val - b'0';
|
|
let visible = i - last_seen[val as usize];
|
|
|
|
if i > 0 {
|
|
*score *= visible;
|
|
last_seen[..=(val as usize)].fill(i);
|
|
} else {
|
|
*score = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn part2(input: &[u8]) -> Result<String> {
|
|
let width = input
|
|
.iter()
|
|
.position(|&b| b == b'\n')
|
|
.context("Single row field")?;
|
|
let height = input.len() / (width + 1); // Include newlines
|
|
|
|
let mut score = vec![1; width * height];
|
|
|
|
// Horizontal striping
|
|
for (y, row) in input.chunks_exact(width + 1).enumerate() {
|
|
// First, left to right
|
|
scenery(&row[..width], &mut score[(y * width)..]);
|
|
|
|
// Then right to left
|
|
scenery(
|
|
row[..width].iter().rev(),
|
|
score[(y * width)..(y * width + width)].iter_mut().rev(),
|
|
);
|
|
}
|
|
|
|
// Vertical striping
|
|
for x in 0..width {
|
|
// Top to bottom
|
|
scenery(
|
|
input[x..].iter().step_by(width + 1),
|
|
score[x..].iter_mut().step_by(width),
|
|
);
|
|
|
|
// Bottom to top
|
|
scenery(
|
|
input[x..].iter().step_by(width + 1).rev(),
|
|
score[x..].iter_mut().step_by(width).rev(),
|
|
)
|
|
}
|
|
|
|
Ok(score.into_iter().max().context("empty field")?.to_string())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
const SAMPLE: &[u8] = include_bytes!("samples/08.txt");
|
|
|
|
#[test]
|
|
fn sample_part1() {
|
|
assert_eq!(part1(SAMPLE).unwrap(), "21");
|
|
}
|
|
|
|
#[test]
|
|
fn sample_part2() {
|
|
assert_eq!(part2(SAMPLE).unwrap(), "8");
|
|
}
|
|
}
|