From 44a1bcdc6233385131394053507c93e9904e4aa5 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Thu, 14 Dec 2023 21:30:05 +0100 Subject: [PATCH] Merge owned and non-owned grids Through the power of generics. Should've been easier if it was possible to be generic over mutable and non-mutable, but AsRef/AsMut is close enough --- 2023/src/common.rs | 127 ++++++++++++--------------------------------- 2023/src/day03.rs | 4 +- 2023/src/day10.rs | 2 +- 2023/src/day13.rs | 4 +- 2023/src/day14.rs | 9 ++-- 5 files changed, 43 insertions(+), 103 deletions(-) diff --git a/2023/src/common.rs b/2023/src/common.rs index f93b425..3e848b8 100644 --- a/2023/src/common.rs +++ b/2023/src/common.rs @@ -244,20 +244,22 @@ impl IndexMut for Vec2 { } } -pub struct Grid<'a> { +#[derive(PartialEq)] +pub struct Grid> { width: usize, - data: &'a [u8], + data: T, } -impl<'a> Grid<'a> { - pub fn new(data: &'a [u8]) -> anyhow::Result { +impl> Grid { + pub fn new(data: T) -> anyhow::Result { let width = 1 + data + .as_ref() .iter() .position(|&c| c == b'\n') .context("Failed to find end of line in grid")?; anyhow::ensure!( - data.len() % width == 0, + data.as_ref().len() % width == 0, "Grid should divide equally into rows" ); @@ -265,65 +267,7 @@ impl<'a> Grid<'a> { } pub fn height(&self) -> usize { - self.data.len() / self.width - } - - pub fn width(&self) -> usize { - self.width - 1 - } - - pub fn rows(&self) -> impl Iterator { - let width = self.width(); - self.data - .chunks_exact(self.width) - .map(move |row| &row[..width]) - } - - pub fn find(&self, c: u8) -> Option<(usize, usize)> { - let pos = self.data.iter().position(|&d| d == c)?; - - Some((pos % self.width, pos / self.width)) - } -} - -impl<'a> Index for Grid<'a> { - type Output = [u8]; - - fn index(&self, y: usize) -> &Self::Output { - let offset = y * self.width; - &self.data[offset..(offset + self.width())] - } -} - -impl Display for Grid<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", String::from_utf8_lossy(self.data)) - } -} - -// TODO: merge OwnedGrid and Grid impls so I don't go insane -pub struct OwnedGrid { - width: usize, - data: Vec, -} - -impl OwnedGrid { - pub fn new(data: Vec) -> anyhow::Result { - let width = 1 + data - .iter() - .position(|&c| c == b'\n') - .context("Failed to find end of line in grid")?; - - anyhow::ensure!( - data.len() % width == 0, - "Grid should divide equally into rows" - ); - - Ok(Self { width, data }) - } - - pub fn height(&self) -> usize { - self.data.len() / self.width + self.data.as_ref().len() / self.width } pub fn width(&self) -> usize { @@ -333,59 +277,48 @@ impl OwnedGrid { pub fn rows(&self) -> impl Iterator { let width = self.width(); self.data + .as_ref() .chunks_exact(self.width) .map(move |row| &row[..width]) } - pub fn rows_mut(&mut self) -> impl Iterator { - let width = self.width(); - self.data - .chunks_exact_mut(self.width) - .map(move |row| &mut row[..width]) - } - pub fn find(&self, c: u8) -> Option<(usize, usize)> { - let pos = self.data.iter().position(|&d| d == c)?; + let pos = self.data.as_ref().iter().position(|&d| d == c)?; Some((pos % self.width, pos / self.width)) } } -impl Index for OwnedGrid { +impl + AsRef<[u8]>> Grid { + pub fn rows_mut(&mut self) -> impl Iterator { + let width = self.width(); + self.data + .as_mut() + .chunks_exact_mut(self.width) + .map(move |row| &mut row[..width]) + } +} + +impl> Index for Grid { type Output = [u8]; fn index(&self, y: usize) -> &Self::Output { let offset = y * self.width; - &self.data[offset..(offset + self.width())] + &self.data.as_ref()[offset..(offset + self.width())] } } -impl IndexMut for OwnedGrid { - fn index_mut(&mut self, y: usize) -> &mut Self::Output { - let offset = y * self.width; - let width = self.width; - &mut self.data[offset..(offset + width)] - } -} - -impl Display for OwnedGrid { +impl> Display for Grid { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", String::from_utf8_lossy(&self.data)) - } -} - -impl PartialEq for OwnedGrid { - fn eq(&self, other: &OwnedGrid) -> bool { - // No need to compare width as width is a function of data - self.data == other.data + write!(f, "{}", String::from_utf8_lossy(self.data.as_ref())) } } // Custom Clone impl so we don't allocate in `clone_from` -impl Clone for OwnedGrid { +impl + Clone> Clone for Grid { fn clone(&self) -> Self { Self { - width: self.width.clone(), + width: self.width, data: self.data.clone(), } } @@ -395,3 +328,11 @@ impl Clone for OwnedGrid { self.data.clone_from(&source.data); } } + +impl + AsRef<[u8]>> IndexMut for Grid { + fn index_mut(&mut self, y: usize) -> &mut Self::Output { + let offset = y * self.width; + let width = self.width; + &mut self.data.as_mut()[offset..(offset + width)] + } +} diff --git a/2023/src/day03.rs b/2023/src/day03.rs index 1c87082..be40968 100644 --- a/2023/src/day03.rs +++ b/2023/src/day03.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use crate::common::Grid; -fn is_surrounded(grid: &Grid<'_>, y: usize, start: usize, last: usize) -> bool { +fn is_surrounded(grid: &Grid<&[u8]>, y: usize, start: usize, last: usize) -> bool { fn is_symbol(c: u8) -> bool { !matches!(c, b'0'..=b'9' | b'.' | b'\n') } @@ -53,7 +53,7 @@ pub fn part1(input: &[u8]) -> anyhow::Result { Ok(sum.to_string()) } -fn find_gear(grid: &Grid<'_>, y: usize, start: usize, end: usize) -> Option<(usize, usize)> { +fn find_gear(grid: &Grid<&[u8]>, y: usize, start: usize, end: usize) -> Option<(usize, usize)> { let x_min = start.saturating_sub(1); let x_max = Ord::min(grid.width(), end + 2); diff --git a/2023/src/day10.rs b/2023/src/day10.rs index 99ab529..bc1c392 100644 --- a/2023/src/day10.rs +++ b/2023/src/day10.rs @@ -22,7 +22,7 @@ fn get_connections(c: u8) -> u8 { } } -fn find_cycle(map: &Grid<'_>) -> anyhow::Result<(usize, bool, IndexSet)> { +fn find_cycle(map: &Grid<&[u8]>) -> anyhow::Result<(usize, bool, IndexSet)> { let (start_x, start_y) = map.find(b'S').context("Couldn't find starting point")?; let mut visited = IndexSet::with_capacity(map.width() * map.height()); let mut todo = VecDeque::new(); diff --git a/2023/src/day13.rs b/2023/src/day13.rs index 24aef9b..f1c86ce 100644 --- a/2023/src/day13.rs +++ b/2023/src/day13.rs @@ -15,7 +15,7 @@ impl Symmetry { } } -fn find_symmetry(grid: Grid<'_>, differences: usize) -> Option { +fn find_symmetry(grid: Grid<&[u8]>, differences: usize) -> Option { // Attempt to find a vertical line of reflection first for c in 1..grid.width() { if grid @@ -48,7 +48,7 @@ fn find_symmetry(grid: Grid<'_>, differences: usize) -> Option { None } -fn parse_grids(input: &[u8]) -> anyhow::Result>> { +fn parse_grids(input: &[u8]) -> anyhow::Result>> { let mut result = Vec::new(); let mut start = 0; let mut last_newline = 0; diff --git a/2023/src/day14.rs b/2023/src/day14.rs index 7d2b797..d6938a4 100644 --- a/2023/src/day14.rs +++ b/2023/src/day14.rs @@ -1,5 +1,4 @@ use crate::common::Grid; -use crate::common::OwnedGrid; pub fn part1(input: &[u8]) -> anyhow::Result { let grid = Grid::new(input)?; @@ -23,7 +22,7 @@ pub fn part1(input: &[u8]) -> anyhow::Result { Ok(load.to_string()) } -fn advance(grid: &mut OwnedGrid, stack_heights: &mut [usize]) { +fn advance(grid: &mut Grid>, stack_heights: &mut [usize]) { // Tilt north stack_heights.fill(0); for y in 0..grid.height() { @@ -100,7 +99,7 @@ fn advance(grid: &mut OwnedGrid, stack_heights: &mut [usize]) { fn find_cycle( it: impl Iterator, - hare: &mut OwnedGrid, + hare: &mut Grid>, stack_heights: &mut [usize], ) -> Option { let mut tortoise = hare.clone(); @@ -113,7 +112,7 @@ fn find_cycle( return Some(cycle - last_sync); } else if cycle.count_ones() == 1 { // New power of two, sync up the tortoise and the hare - tortoise.clone_from(&hare); + tortoise.clone_from(hare); last_sync = cycle; } } @@ -122,7 +121,7 @@ fn find_cycle( pub fn part2(input: &[u8]) -> anyhow::Result { const GOAL: usize = 1000000000; - let mut hare = OwnedGrid::new(input.to_owned())?; + let mut hare = Grid::new(input.to_owned())?; let mut stack_heights = vec![0; hare.width()]; let mut it = 1..=GOAL;