diff --git a/2022/src/bin/day07.rs b/2022/src/bin/day07.rs index 95af490..e7b5a95 100644 --- a/2022/src/bin/day07.rs +++ b/2022/src/bin/day07.rs @@ -24,7 +24,7 @@ fn parse_input(raw: &str) -> Node<'_> { let mut fs = Node::Dir("/", Vec::new(), 0); for cmd in raw.trim_start_matches("$ cd /\n$ ").split("\n$ ") { match cmd.bytes().next() { - Some(b'c') => if cmd.ends_with(".") { drop(pwd.pop()) } else { pwd.push(&cmd[3..]) }, + Some(b'c') => if cmd.ends_with('.') { pwd.pop(); } else { pwd.push(&cmd[3..]) }, // ls _ if let Some(Node::Dir(_, contents, _)) = pwd.iter().try_fold(&mut fs, |cd, p| cd.subdir_mut(p)) => contents.extend( cmd.lines() @@ -45,7 +45,7 @@ fn compute_dir_sizes(node: &mut Node<'_>) -> usize { match node { Node::File(s) => *s, Node::Dir(_, c, size) => { - *size = c.iter_mut().map(|d| compute_dir_sizes(d)).sum(); + *size = c.iter_mut().map(compute_dir_sizes).sum(); *size } } diff --git a/2022/src/bin/day08.rs b/2022/src/bin/day08.rs index c2ec81c..0325fa7 100644 --- a/2022/src/bin/day08.rs +++ b/2022/src/bin/day08.rs @@ -5,43 +5,42 @@ use std::iter::repeat; use aoc2022::{boilerplate, common::*}; -const DAY: usize = 08; +const DAY: usize = 8; type Parsed = Vec>; fn parse_input(raw: &str) -> Parsed { raw.lines().map(|l| l.as_bytes().to_vec()).collect() } -fn is_visible_1d<'a>(iter: impl IntoIterator) -> Vec { - iter.into_iter() +fn is_visible_1d<'a>(iter: impl IntoIterator, rev: bool) -> Vec { + let mut v: Vec<_> = iter + .into_iter() .scan(0, |max, tree| { let visible = tree > max; *max = *tree.max(max); Some(visible) }) - .collect() + .collect(); + if rev { + v.reverse(); + } + v +} + +fn transpose(v: &Vec>) -> Vec> { + let len = v.len(); + let mut iters: Vec<_> = v.iter().map(|n| n.iter()).collect(); + (0..len).map(|_| iters.iter_mut().map(|i| i.next().unwrap()).copied().collect::>()).collect() +} + +fn horizontally_visible(v: &[Vec]) -> Vec> { + v.iter().map(|l| is_visible_1d(l, false).into_iter().zip(is_visible_1d(l.iter().rev(), true)).map(|(a, b)| a || b).collect()).collect() } fn part1(parsed: &Parsed) -> usize { - let size = parsed.len(); // input is always square - let a: Vec<_> = parsed.iter().map(is_visible_1d).collect(); - let b: Vec<_> = parsed - .iter() - .map(|l| { - let mut v = is_visible_1d(l.iter().rev()); - v.reverse(); - v - }) - .collect(); - let c: Vec<_> = (0..size).map(|i| is_visible_1d(parsed.iter().map(|row| &row[i]))).collect(); - let d: Vec<_> = (0..size) - .map(|i| { - let mut v = is_visible_1d(parsed.iter().rev().map(|row| &row[i])); - v.reverse(); - v - }) - .collect(); - (0..size).flat_map(|i| repeat(i).zip(0..size)).filter(|&(i, j)| a[i][j] || b[i][j] || c[j][i] || d[j][i]).count() + let horizontal = horizontally_visible(parsed); + let vertical = horizontally_visible(&transpose(parsed)); + (0..parsed.len()).flat_map(|i| repeat(i).zip(0..parsed.len())).filter(|&(i, j)| horizontal[i][j] || vertical[j][i]).count() } fn part2(parsed: &Parsed) -> usize { @@ -50,20 +49,22 @@ fn part2(parsed: &Parsed) -> usize { .flat_map(|i| repeat(i).zip(1..size - 1)) .map(|(i, j)| { let tree = parsed[i][j]; - let mut heights = ((i + 1)..size).map(|i| parsed[i][j]).peekable(); - let a = heights.peeking_take_while(|&t| t < tree).count() + heights.peek().map(|_| 1).unwrap_or(0); - let mut heights = (0..i).rev().map(|i| parsed[i][j]).peekable(); - let b = heights.peeking_take_while(|&t| t < tree).count() + heights.peek().map(|_| 1).unwrap_or(0); - let mut heights = ((j + 1)..size).map(|j| parsed[i][j]).peekable(); - let c = heights.peeking_take_while(|&t| t < tree).count() + heights.peek().map(|_| 1).unwrap_or(0); - let mut heights = (0..j).rev().map(|j| parsed[i][j]).peekable(); - let d = heights.peeking_take_while(|&t| t < tree).count() + heights.peek().map(|_| 1).unwrap_or(0); + let a = visible_trees(((i + 1)..size).map(|i| parsed[i][j]), tree); + let b = visible_trees((0..i).rev().map(|i| parsed[i][j]), tree); + let c = visible_trees(((j + 1)..size).map(|j| parsed[i][j]), tree); + let d = visible_trees((0..j).rev().map(|j| parsed[i][j]), tree); a * b * c * d }) .max() .unwrap() } +#[inline] // this inline actually saves ~40% runtime +fn visible_trees(heights: impl Iterator, tree: u8) -> usize { + let mut heights = heights.peekable(); + heights.peeking_take_while(|&t| t < tree).count() + heights.peek().map(|_| 1).unwrap_or(0) +} + #[cfg(test)] const TEST_OUTPUT: &[bool] = &[true, true, false, true, false]; @@ -78,7 +79,7 @@ boilerplate! { part2: { TEST_INPUT => 8 }, }, unittests: { - is_visible_1d: { [1, 3, 2, 4, 2] => TEST_OUTPUT, }, + is_visible_1d: { [1, 3, 2, 4, 2].iter(), false => TEST_OUTPUT, }, }, bench1 == 1543, bench2 == 595080, diff --git a/2022/src/teststuff.rs b/2022/src/teststuff.rs index 4d21312..53d5052 100644 --- a/2022/src/teststuff.rs +++ b/2022/src/teststuff.rs @@ -6,7 +6,7 @@ macro_rules! boilerplate { $($part: ident: { $($tpi: expr => $to: expr),+$(,)? }),*$(,)? }, $(unittests: { - $($unittest: ident: { $($utpi: expr => $uto: expr),+$(,)? }),*$(,)? + $($unittest: ident: { $($($utpi: expr),+ => $uto: expr),+$(,)? }),*$(,)? },)? bench1 == $b1: literal, bench2 == $b2: literal, @@ -29,7 +29,7 @@ macro_rules! boilerplate { $($($(paste::paste! { #[test] fn [<$unittest _test_ $uto:lower>]() { - assert_eq!($unittest(&$utpi), $uto); + assert_eq!($unittest($($utpi),+), $uto); } })+)*)? $($(paste::paste! {