mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Very clunky implementation of 2023 day 14 part 2
This commit is contained in:
@@ -300,3 +300,98 @@ impl Display for Grid<'_> {
|
|||||||
write!(f, "{}", String::from_utf8_lossy(self.data))
|
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 {
|
||||||
|
self.width - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rows(&self) -> impl Iterator<Item = &[u8]> {
|
||||||
|
let width = self.width();
|
||||||
|
self.data
|
||||||
|
.chunks_exact(self.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)> {
|
||||||
|
let pos = self.data.iter().position(|&d| d == c)?;
|
||||||
|
|
||||||
|
Some((pos % self.width, pos / self.width))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<usize> for OwnedGrid {
|
||||||
|
type Output = [u8];
|
||||||
|
|
||||||
|
fn index(&self, y: usize) -> &Self::Output {
|
||||||
|
let offset = y * self.width;
|
||||||
|
&self.data[offset..(offset + self.width())]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<usize> 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 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", String::from_utf8_lossy(&self.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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`
|
||||||
|
impl Clone for OwnedGrid {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
width: self.width.clone(),
|
||||||
|
data: self.data.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_from(&mut self, source: &Self) {
|
||||||
|
self.width = source.width;
|
||||||
|
self.data.clone_from(&source.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
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)?;
|
||||||
@@ -22,8 +23,131 @@ pub fn part1(input: &[u8]) -> anyhow::Result<String> {
|
|||||||
Ok(load.to_string())
|
Ok(load.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn part2(_input: &[u8]) -> anyhow::Result<String> {
|
fn advance(grid: &mut OwnedGrid, stack_heights: &mut [usize]) {
|
||||||
anyhow::bail!("Not implemented")
|
// Tilt north
|
||||||
|
stack_heights.fill(0);
|
||||||
|
for y in 0..grid.height() {
|
||||||
|
for (x, stack_height) in stack_heights.iter_mut().enumerate() {
|
||||||
|
let c = grid[y][x];
|
||||||
|
match c {
|
||||||
|
b'#' => *stack_height = y + 1,
|
||||||
|
b'O' => {
|
||||||
|
grid[y][x] = b'.';
|
||||||
|
grid[*stack_height][x] = b'O';
|
||||||
|
|
||||||
|
*stack_height += 1;
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tilt west
|
||||||
|
for row in grid.rows_mut() {
|
||||||
|
let mut stack_height = 0;
|
||||||
|
for x in 0..row.len() {
|
||||||
|
let c = row[x];
|
||||||
|
match c {
|
||||||
|
b'#' => stack_height = x + 1,
|
||||||
|
b'O' => {
|
||||||
|
row[x] = b'.';
|
||||||
|
row[stack_height] = b'O';
|
||||||
|
|
||||||
|
stack_height += 1;
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tilt south
|
||||||
|
stack_heights.fill(grid.height() - 1);
|
||||||
|
for y in (0..grid.height()).rev() {
|
||||||
|
for (x, stack_height) in stack_heights.iter_mut().enumerate() {
|
||||||
|
let c = grid[y][x];
|
||||||
|
match c {
|
||||||
|
b'#' => *stack_height = y.saturating_sub(1),
|
||||||
|
b'O' => {
|
||||||
|
grid[y][x] = b'.';
|
||||||
|
grid[*stack_height][x] = b'O';
|
||||||
|
|
||||||
|
// Saturating because possible underflow
|
||||||
|
*stack_height = stack_height.saturating_sub(1);
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tilt east
|
||||||
|
for row in grid.rows_mut() {
|
||||||
|
let mut stack_height = row.len() - 1;
|
||||||
|
for x in (0..row.len()).rev() {
|
||||||
|
let c = row[x];
|
||||||
|
match c {
|
||||||
|
b'#' => stack_height = x.saturating_sub(1),
|
||||||
|
b'O' => {
|
||||||
|
row[x] = b'.';
|
||||||
|
row[stack_height] = b'O';
|
||||||
|
|
||||||
|
stack_height = stack_height.saturating_sub(1);
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_cycle(
|
||||||
|
it: impl Iterator<Item = usize>,
|
||||||
|
hare: &mut OwnedGrid,
|
||||||
|
stack_heights: &mut [usize],
|
||||||
|
) -> Option<usize> {
|
||||||
|
let mut tortoise = hare.clone();
|
||||||
|
let mut last_sync = 0;
|
||||||
|
|
||||||
|
for cycle in it {
|
||||||
|
advance(hare, stack_heights);
|
||||||
|
|
||||||
|
if tortoise == *hare {
|
||||||
|
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);
|
||||||
|
last_sync = cycle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part2(input: &[u8]) -> anyhow::Result<String> {
|
||||||
|
const GOAL: usize = 1000000000;
|
||||||
|
let mut hare = OwnedGrid::new(input.to_owned())?;
|
||||||
|
let mut stack_heights = vec![0; hare.width()];
|
||||||
|
|
||||||
|
let mut it = 1..=GOAL;
|
||||||
|
|
||||||
|
// If there is a cycle found, skip ahead
|
||||||
|
if let Some(len) = find_cycle(&mut it, &mut hare, &mut stack_heights) {
|
||||||
|
let remaining = it.size_hint().0;
|
||||||
|
let steps = remaining % len;
|
||||||
|
|
||||||
|
for _ in 0..steps {
|
||||||
|
advance(&mut hare, &mut stack_heights);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let height = hare.height();
|
||||||
|
let load = hare
|
||||||
|
.rows()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(y, row)| {
|
||||||
|
row.iter()
|
||||||
|
.filter_map(move |&c| (c == b'O').then_some(height - y))
|
||||||
|
})
|
||||||
|
.sum::<usize>();
|
||||||
|
|
||||||
|
Ok(load.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -36,4 +160,9 @@ mod tests {
|
|||||||
fn sample_part1() {
|
fn sample_part1() {
|
||||||
assert_eq!("136", part1(SAMPLE).unwrap());
|
assert_eq!("136", part1(SAMPLE).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sample_part2() {
|
||||||
|
assert_eq!("64", part2(SAMPLE).unwrap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user