Compare commits
2 Commits
9f602bb947
...
a1dceb6ff1
Author | SHA1 | Date | |
---|---|---|---|
a1dceb6ff1 | |||
3712b32634 |
57
src/day18.rs
57
src/day18.rs
@ -1,7 +1,7 @@
|
|||||||
use aoc_runner_derive::aoc;
|
use aoc_runner_derive::aoc;
|
||||||
use grid::Grid;
|
use grid::Grid;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::{cmp::Reverse, collections::BinaryHeap};
|
use std::{cmp::Reverse, collections::{BinaryHeap, VecDeque}};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct MemoryMap {
|
struct MemoryMap {
|
||||||
@ -80,6 +80,53 @@ impl MemoryMap {
|
|||||||
.map(|ofs| (pos.0 + ofs.0, pos.1 + ofs.1))
|
.map(|ofs| (pos.0 + ofs.0, pos.1 + ofs.1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bfs<T: PathTrack>(&self, start: (i64, i64)) -> Option<T> {
|
||||||
|
let goal = (self.map.width() as i64 - 1, self.map.height() as i64 - 1);
|
||||||
|
|
||||||
|
let mut visited = self.map.same_shape(false);
|
||||||
|
let mut prev = self.map.same_shape((i64::MAX, i64::MAX));
|
||||||
|
let mut queue = VecDeque::new();
|
||||||
|
|
||||||
|
visited.set(&start, true);
|
||||||
|
queue.push_back((0, start));
|
||||||
|
|
||||||
|
while let Some((depth, pos)) = queue.pop_front() {
|
||||||
|
if pos == goal {
|
||||||
|
if T::DOES_WORK {
|
||||||
|
let mut visited_pos = goal;
|
||||||
|
let mut path = T::new();
|
||||||
|
path.push(pos);
|
||||||
|
while let Some(next) = prev.get(&visited_pos) {
|
||||||
|
visited_pos = *next;
|
||||||
|
path.push(*next);
|
||||||
|
if *next == start {
|
||||||
|
path.finalize();
|
||||||
|
return Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Some(T::new());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if visited.get(&pos).is_some_and(|v| *v) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
let moves = self.valid_moves(&pos);
|
||||||
|
for new_pos in moves {
|
||||||
|
if visited.get(&new_pos).is_none_or(|v| !v) {
|
||||||
|
visited.set(&new_pos, true);
|
||||||
|
if T::DOES_WORK {
|
||||||
|
prev.set(&new_pos, pos);
|
||||||
|
}
|
||||||
|
queue.push_back((depth + 1, new_pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn dijkstra<T: PathTrack>(&self, start: (i64, i64)) -> Option<T> {
|
fn dijkstra<T: PathTrack>(&self, start: (i64, i64)) -> Option<T> {
|
||||||
let goal = (self.map.width() as i64 - 1, self.map.height() as i64 - 1);
|
let goal = (self.map.width() as i64 - 1, self.map.height() as i64 - 1);
|
||||||
|
|
||||||
@ -131,7 +178,7 @@ impl MemoryMap {
|
|||||||
pub fn part1_impl(input: &str, width: usize, height: usize, initial_safe_byte_count: usize) -> usize {
|
pub fn part1_impl(input: &str, width: usize, height: usize, initial_safe_byte_count: usize) -> usize {
|
||||||
let mut map = MemoryMap::from_str(input, width, height);
|
let mut map = MemoryMap::from_str(input, width, height);
|
||||||
map.place_bytes(0, initial_safe_byte_count - 1);
|
map.place_bytes(0, initial_safe_byte_count - 1);
|
||||||
let path = map.dijkstra::<LengthPath>((0, 0)).expect("no path found");
|
let path = map.bfs::<LengthPath>((0, 0)).expect("no path found");
|
||||||
|
|
||||||
path.0 - 1 // count edges, not visited nodes (start doesn't count)
|
path.0 - 1 // count edges, not visited nodes (start doesn't count)
|
||||||
}
|
}
|
||||||
@ -141,7 +188,7 @@ pub fn part2_impl_brute(input: &str, width: usize, height: usize, initial_safe_b
|
|||||||
let mut input_map = MemoryMap::from_str(input, width, height);
|
let mut input_map = MemoryMap::from_str(input, width, height);
|
||||||
input_map.place_bytes(0, initial_safe_byte_count - 1);
|
input_map.place_bytes(0, initial_safe_byte_count - 1);
|
||||||
|
|
||||||
let mut path = input_map.dijkstra::<Vec<(i64, i64)>>((0, 0)).expect("no path found");
|
let mut path = input_map.bfs::<Vec<(i64, i64)>>((0, 0)).expect("no path found");
|
||||||
|
|
||||||
for byte in initial_safe_byte_count..input_map.byte_stream.len() {
|
for byte in initial_safe_byte_count..input_map.byte_stream.len() {
|
||||||
input_map.place_byte(byte);
|
input_map.place_byte(byte);
|
||||||
@ -149,7 +196,7 @@ pub fn part2_impl_brute(input: &str, width: usize, height: usize, initial_safe_b
|
|||||||
if let Some((obs_at, _)) = path.iter().find_position(|v| *v == &input_map.byte_stream[byte]) {
|
if let Some((obs_at, _)) = path.iter().find_position(|v| *v == &input_map.byte_stream[byte]) {
|
||||||
let (before, _) = path.split_at(obs_at);
|
let (before, _) = path.split_at(obs_at);
|
||||||
|
|
||||||
if let Some(new_path) = input_map.dijkstra::<Vec<(i64, i64)>>(path[obs_at - 1]) {
|
if let Some(new_path) = input_map.bfs::<Vec<(i64, i64)>>(path[obs_at - 1]) {
|
||||||
path = [before, &new_path].concat();
|
path = [before, &new_path].concat();
|
||||||
} else {
|
} else {
|
||||||
return input_map.byte_stream[byte];
|
return input_map.byte_stream[byte];
|
||||||
@ -172,7 +219,7 @@ pub fn part2_impl(input: &str, width: usize, height: usize, initial_safe_byte_co
|
|||||||
// avoiding this clone by rolling back the byte placements instead is slower
|
// avoiding this clone by rolling back the byte placements instead is slower
|
||||||
let mut local_map = input_map.clone();
|
let mut local_map = input_map.clone();
|
||||||
local_map.place_bytes(initial_safe_byte_count, *byte);
|
local_map.place_bytes(initial_safe_byte_count, *byte);
|
||||||
local_map.dijkstra::<NoopTrack>((0, 0)).is_some()
|
local_map.bfs::<NoopTrack>((0, 0)).is_some()
|
||||||
}) + initial_safe_byte_count;
|
}) + initial_safe_byte_count;
|
||||||
|
|
||||||
return input_map.byte_stream[solution];
|
return input_map.byte_stream[solution];
|
||||||
|
@ -99,8 +99,7 @@ pub struct Map {
|
|||||||
impl<T: BufRead> From<T> for Map {
|
impl<T: BufRead> From<T> for Map {
|
||||||
fn from(input: T) -> Self {
|
fn from(input: T) -> Self {
|
||||||
let grid = Grid::from(input);
|
let grid = Grid::from(input);
|
||||||
let mut visited_from: Grid<DirectionSet> = Grid::new(grid.width() as i64);
|
let visited_from = grid.same_shape(DirectionSet::empty());
|
||||||
visited_from.data.resize(grid.data.len(), DirectionSet::empty());
|
|
||||||
let guard_pos = grid.find(&b'^').expect("Guard not found");
|
let guard_pos = grid.find(&b'^').expect("Guard not found");
|
||||||
let guard_facing = FacingDirection::Up;
|
let guard_facing = FacingDirection::Up;
|
||||||
Self {
|
Self {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fmt::{Debug, Display, Formatter, Write},
|
fmt::{Debug, Display, Formatter, Write},
|
||||||
io::{BufRead, Cursor},
|
io::{BufRead, Cursor},
|
||||||
iter::repeat,
|
iter::repeat_n,
|
||||||
mem::swap,
|
mem::swap,
|
||||||
ops::{Add, AddAssign, Sub},
|
ops::{Add, AddAssign, Sub},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
@ -82,29 +82,76 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Debug)]
|
||||||
|
pub struct GridRowIter<'a, T> {
|
||||||
|
iter: std::slice::Iter<'a, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Clone + Eq + PartialEq + Debug> GridRowIter<'a, T> {
|
||||||
|
fn new(grid: &'a Grid<T>, y: i64) -> Self {
|
||||||
|
let iter = grid.data[y as usize * grid.width()..(y as usize + 1) * grid.width()].iter();
|
||||||
|
Self { iter }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for GridRowIter<'a, T> {
|
||||||
|
type Item = &'a T;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.iter.next()
|
||||||
|
}
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
self.iter.size_hint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GridColIter<'a, T> {
|
||||||
|
grid: &'a Grid<T>,
|
||||||
|
stride: usize,
|
||||||
|
cur: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Clone + Eq + PartialEq + Debug> GridColIter<'a, T> {
|
||||||
|
fn new(grid: &'a Grid<T>, x: i64) -> Self {
|
||||||
|
Self {
|
||||||
|
grid,
|
||||||
|
stride: grid.width(),
|
||||||
|
cur: x as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Clone + Eq + PartialEq + Debug> Iterator for GridColIter<'a, T> {
|
||||||
|
type Item = &'a T;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let cur = self.cur;
|
||||||
|
self.cur += self.stride;
|
||||||
|
if cur < self.grid.data.len() {
|
||||||
|
Some(&self.grid.data[cur])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
(self.grid.height() - 1, Some(self.grid.height() - 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
pub struct Grid<T> {
|
pub struct Grid<T> {
|
||||||
pub data: Vec<T>,
|
pub data: Vec<T>,
|
||||||
width: i64,
|
width: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + Eq + PartialEq + Debug> Grid<T> {
|
impl<T: Clone + Eq + PartialEq + Debug> Grid<T> {
|
||||||
pub fn new(width: i64) -> Self {
|
|
||||||
Self {
|
|
||||||
data: Vec::new(),
|
|
||||||
width,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Returns a new [Grid] with the same shape (width x height) as `self`, filled with `fill`
|
/// Returns a new [Grid] with the same shape (width x height) as `self`, filled with `fill`
|
||||||
pub fn same_shape<NT: Clone + Eq + PartialEq + Debug>(&self, fill: NT) -> Grid<NT> {
|
pub fn same_shape<NT: Clone + Eq + PartialEq + Debug>(&self, fill: NT) -> Grid<NT> {
|
||||||
Grid {
|
Grid::with_shape(self.width(), self.height(), fill)
|
||||||
data: Vec::from_iter(repeat(fill).take(self.width() * self.height())),
|
|
||||||
width: self.width,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
/// Returns a new [Grid] with the given shape (width x height), filled with `fill`
|
||||||
pub fn with_shape(width: usize, height: usize, fill: T) -> Self {
|
pub fn with_shape(width: usize, height: usize, fill: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: Vec::from_iter(repeat(fill).take(width * height)),
|
data: Vec::from_iter(repeat_n(fill, width * height)),
|
||||||
width: width as i64,
|
width: width as i64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,9 +225,25 @@ impl<T: Clone + Eq + PartialEq + Debug> Grid<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn row_iter(&self, y: i64) -> Option<GridRowIter<T>> {
|
||||||
|
if (y as usize) < self.height() {
|
||||||
|
Some(GridRowIter::new(self, y))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn col(&self, x: i64) -> Option<Vec<&T>> {
|
pub fn col(&self, x: i64) -> Option<Vec<&T>> {
|
||||||
if x < self.width() as i64 && x >= 0 {
|
if let Some(iter) = self.col_iter(x) {
|
||||||
Some((0..self.height()).map(|y| self.get(&(x, y as i64)).unwrap()).collect())
|
Some(iter.collect())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn col_iter(&self, x: i64) -> Option<GridColIter<T>> {
|
||||||
|
if (x as usize) < self.width() {
|
||||||
|
Some(GridColIter::new(self, x))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -350,6 +413,28 @@ FBCG";
|
|||||||
assert_eq!(grid.forward_slice(&(0, 2), 4), Some(b"IJKL".as_slice()));
|
assert_eq!(grid.forward_slice(&(0, 2), 4), Some(b"IJKL".as_slice()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn row_iter() {
|
||||||
|
let grid = unchecked_load();
|
||||||
|
assert_eq!(
|
||||||
|
grid.row_iter(2).unwrap().collect::<Vec<_>>(),
|
||||||
|
[&b'I', &b'J', &b'K', &b'L']
|
||||||
|
);
|
||||||
|
assert!(grid.row_iter(-1).is_none());
|
||||||
|
assert!(grid.row_iter(4).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn col_iter() {
|
||||||
|
let grid = unchecked_load();
|
||||||
|
assert_eq!(
|
||||||
|
grid.col_iter(2).unwrap().collect::<Vec<_>>(),
|
||||||
|
[&b'C', &b'G', &b'K', &b'C']
|
||||||
|
);
|
||||||
|
assert!(grid.col_iter(-1).is_none());
|
||||||
|
assert!(grid.col_iter(4).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn window_compare() {
|
// fn window_compare() {
|
||||||
// let grid = unchecked_load();
|
// let grid = unchecked_load();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user