mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-27 05:40:32 +01:00
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
This commit is contained in:
@@ -244,20 +244,22 @@ impl IndexMut<usize> for Vec2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Grid<'a> {
|
#[derive(PartialEq)]
|
||||||
|
pub struct Grid<T: AsRef<[u8]>> {
|
||||||
width: usize,
|
width: usize,
|
||||||
data: &'a [u8],
|
data: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Grid<'a> {
|
impl<T: AsRef<[u8]>> Grid<T> {
|
||||||
pub fn new(data: &'a [u8]) -> anyhow::Result<Self> {
|
pub fn new(data: T) -> anyhow::Result<Self> {
|
||||||
let width = 1 + data
|
let width = 1 + data
|
||||||
|
.as_ref()
|
||||||
.iter()
|
.iter()
|
||||||
.position(|&c| c == b'\n')
|
.position(|&c| c == b'\n')
|
||||||
.context("Failed to find end of line in grid")?;
|
.context("Failed to find end of line in grid")?;
|
||||||
|
|
||||||
anyhow::ensure!(
|
anyhow::ensure!(
|
||||||
data.len() % width == 0,
|
data.as_ref().len() % width == 0,
|
||||||
"Grid should divide equally into rows"
|
"Grid should divide equally into rows"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -265,65 +267,7 @@ impl<'a> Grid<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn height(&self) -> usize {
|
pub fn height(&self) -> usize {
|
||||||
self.data.len() / self.width
|
self.data.as_ref().len() / self.width
|
||||||
}
|
|
||||||
|
|
||||||
pub fn width(&self) -> usize {
|
|
||||||
self.width - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rows(&self) -> impl Iterator<Item = &'a [u8]> {
|
|
||||||
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<usize> 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<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OwnedGrid {
|
|
||||||
pub fn new(data: Vec<u8>) -> anyhow::Result<Self> {
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width(&self) -> usize {
|
pub fn width(&self) -> usize {
|
||||||
@@ -333,59 +277,48 @@ impl OwnedGrid {
|
|||||||
pub fn rows(&self) -> impl Iterator<Item = &[u8]> {
|
pub fn rows(&self) -> impl Iterator<Item = &[u8]> {
|
||||||
let width = self.width();
|
let width = self.width();
|
||||||
self.data
|
self.data
|
||||||
|
.as_ref()
|
||||||
.chunks_exact(self.width)
|
.chunks_exact(self.width)
|
||||||
.map(move |row| &row[..width])
|
.map(move |row| &row[..width])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rows_mut(&mut self) -> impl Iterator<Item = &mut [u8]> {
|
|
||||||
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)> {
|
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))
|
Some((pos % self.width, pos / self.width))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<usize> for OwnedGrid {
|
impl<T: AsMut<[u8]> + AsRef<[u8]>> Grid<T> {
|
||||||
|
pub fn rows_mut(&mut self) -> impl Iterator<Item = &mut [u8]> {
|
||||||
|
let width = self.width();
|
||||||
|
self.data
|
||||||
|
.as_mut()
|
||||||
|
.chunks_exact_mut(self.width)
|
||||||
|
.map(move |row| &mut row[..width])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<[u8]>> Index<usize> for Grid<T> {
|
||||||
type Output = [u8];
|
type Output = [u8];
|
||||||
|
|
||||||
fn index(&self, y: usize) -> &Self::Output {
|
fn index(&self, y: usize) -> &Self::Output {
|
||||||
let offset = y * self.width;
|
let offset = y * self.width;
|
||||||
&self.data[offset..(offset + self.width())]
|
&self.data.as_ref()[offset..(offset + self.width())]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexMut<usize> for OwnedGrid {
|
impl<T: AsRef<[u8]>> Display for Grid<T> {
|
||||||
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 {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", String::from_utf8_lossy(&self.data))
|
write!(f, "{}", String::from_utf8_lossy(self.data.as_ref()))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<OwnedGrid> for OwnedGrid {
|
|
||||||
fn eq(&self, other: &OwnedGrid) -> bool {
|
|
||||||
// No need to compare width as width is a function of data
|
|
||||||
self.data == other.data
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom Clone impl so we don't allocate in `clone_from`
|
// Custom Clone impl so we don't allocate in `clone_from`
|
||||||
impl Clone for OwnedGrid {
|
impl<T: AsRef<[u8]> + Clone> Clone for Grid<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
width: self.width.clone(),
|
width: self.width,
|
||||||
data: self.data.clone(),
|
data: self.data.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,3 +328,11 @@ impl Clone for OwnedGrid {
|
|||||||
self.data.clone_from(&source.data);
|
self.data.clone_from(&source.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: AsMut<[u8]> + AsRef<[u8]>> IndexMut<usize> for Grid<T> {
|
||||||
|
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)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use crate::common::Grid;
|
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 {
|
fn is_symbol(c: u8) -> bool {
|
||||||
!matches!(c, b'0'..=b'9' | b'.' | b'\n')
|
!matches!(c, b'0'..=b'9' | b'.' | b'\n')
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ pub fn part1(input: &[u8]) -> anyhow::Result<String> {
|
|||||||
Ok(sum.to_string())
|
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_min = start.saturating_sub(1);
|
||||||
let x_max = Ord::min(grid.width(), end + 2);
|
let x_max = Ord::min(grid.width(), end + 2);
|
||||||
|
|
||||||
|
|||||||
@@ -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 (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 visited = IndexSet::with_capacity(map.width() * map.height());
|
||||||
let mut todo = VecDeque::new();
|
let mut todo = VecDeque::new();
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ impl Symmetry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_symmetry(grid: Grid<'_>, differences: usize) -> Option<Symmetry> {
|
fn find_symmetry(grid: Grid<&[u8]>, differences: usize) -> Option<Symmetry> {
|
||||||
// Attempt to find a vertical line of reflection first
|
// Attempt to find a vertical line of reflection first
|
||||||
for c in 1..grid.width() {
|
for c in 1..grid.width() {
|
||||||
if grid
|
if grid
|
||||||
@@ -48,7 +48,7 @@ fn find_symmetry(grid: Grid<'_>, differences: usize) -> Option<Symmetry> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_grids(input: &[u8]) -> anyhow::Result<Vec<Grid<'_>>> {
|
fn parse_grids(input: &[u8]) -> anyhow::Result<Vec<Grid<&[u8]>>> {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
let mut last_newline = 0;
|
let mut last_newline = 0;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::common::Grid;
|
use crate::common::Grid;
|
||||||
use crate::common::OwnedGrid;
|
|
||||||
|
|
||||||
pub fn part1(input: &[u8]) -> anyhow::Result<String> {
|
pub fn part1(input: &[u8]) -> anyhow::Result<String> {
|
||||||
let grid = Grid::new(input)?;
|
let grid = Grid::new(input)?;
|
||||||
@@ -23,7 +22,7 @@ pub fn part1(input: &[u8]) -> anyhow::Result<String> {
|
|||||||
Ok(load.to_string())
|
Ok(load.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn advance(grid: &mut OwnedGrid, stack_heights: &mut [usize]) {
|
fn advance(grid: &mut Grid<Vec<u8>>, stack_heights: &mut [usize]) {
|
||||||
// Tilt north
|
// Tilt north
|
||||||
stack_heights.fill(0);
|
stack_heights.fill(0);
|
||||||
for y in 0..grid.height() {
|
for y in 0..grid.height() {
|
||||||
@@ -100,7 +99,7 @@ fn advance(grid: &mut OwnedGrid, stack_heights: &mut [usize]) {
|
|||||||
|
|
||||||
fn find_cycle(
|
fn find_cycle(
|
||||||
it: impl Iterator<Item = usize>,
|
it: impl Iterator<Item = usize>,
|
||||||
hare: &mut OwnedGrid,
|
hare: &mut Grid<Vec<u8>>,
|
||||||
stack_heights: &mut [usize],
|
stack_heights: &mut [usize],
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
let mut tortoise = hare.clone();
|
let mut tortoise = hare.clone();
|
||||||
@@ -113,7 +112,7 @@ fn find_cycle(
|
|||||||
return Some(cycle - last_sync);
|
return Some(cycle - last_sync);
|
||||||
} else if cycle.count_ones() == 1 {
|
} else if cycle.count_ones() == 1 {
|
||||||
// New power of two, sync up the tortoise and the hare
|
// New power of two, sync up the tortoise and the hare
|
||||||
tortoise.clone_from(&hare);
|
tortoise.clone_from(hare);
|
||||||
last_sync = cycle;
|
last_sync = cycle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,7 +121,7 @@ fn find_cycle(
|
|||||||
|
|
||||||
pub fn part2(input: &[u8]) -> anyhow::Result<String> {
|
pub fn part2(input: &[u8]) -> anyhow::Result<String> {
|
||||||
const GOAL: usize = 1000000000;
|
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 stack_heights = vec![0; hare.width()];
|
||||||
|
|
||||||
let mut it = 1..=GOAL;
|
let mut it = 1..=GOAL;
|
||||||
|
|||||||
Reference in New Issue
Block a user