From 12c5cc783b4eee57f2da6edced4293babf8b0b03 Mon Sep 17 00:00:00 2001 From: SOFe Date: Sun, 8 Dec 2024 14:20:36 +0800 Subject: [PATCH] d6p2 (very slow) --- src/all.rs | 4 ++ src/all/d4.rs | 4 +- src/all/d6.rs | 121 +++++++++++++++++++++++++++++++++++++++++++++++--- src/util.rs | 46 +++++++++---------- 4 files changed, 144 insertions(+), 31 deletions(-) diff --git a/src/all.rs b/src/all.rs index 81d793c..9d7cefc 100644 --- a/src/all.rs +++ b/src/all.rs @@ -141,7 +141,11 @@ macros::all! { part 1 { "ticked-fxhash-loc" => p1_ticked_fxhash_loc, "ticked-fxhash-index" => p1_ticked_fxhash_index, + "ticked-boolvec" => p1_ticked_boolvec, "ticked-bitvec" => p1_ticked_bitvec, } + part 2 { + "brute-fxhash-loc" => p2_brute_fxhash_loc, + } } } diff --git a/src/all/d4.rs b/src/all/d4.rs index 68f5c42..32da52c 100644 --- a/src/all/d4.rs +++ b/src/all/d4.rs @@ -10,7 +10,7 @@ pub fn p1_brute(input: String) -> u32 { for (index, _) in input.match_indices('X') { let loc = grid.index_to_loc(index).unwrap(); for &dir in DirectBoth::ALL { - let mut iter = loc.direct_iter(dir, grid).skip(1).take(3).map(|loc| grid.get(loc)); + let mut iter = loc.direct_iter(dir, &grid).skip(1).take(3).map(|loc| grid.get(loc)); let chars: [_; 3] = array::from_fn(|_| iter.next().flatten()); if chars == [Some(b'M'), Some(b'A'), Some(b'S')] { count += 1; @@ -33,7 +33,7 @@ pub fn p2_brute(input: String) -> u32 { [DirectDiagonal::RightUp, DirectDiagonal::LeftDown], ] .map(|ends| { - let values = ends.map(|direct| grid.get(loc.direct(direct, grid)?)); + let values = ends.map(|direct| grid.get(loc.direct(direct, &grid)?)); values == [Some(b'M'), Some(b'S')] || values == [Some(b'S'), Some(b'M')] }); if matched[0] && matched[1] { diff --git a/src/all/d6.rs b/src/all/d6.rs index 06a5f28..bdc4590 100644 --- a/src/all/d6.rs +++ b/src/all/d6.rs @@ -5,13 +5,13 @@ use rustc_hash::FxHashSet; use crate::util::{DirectTaxicab, GridLoc, GridView}; -trait Collector { +trait LocCounter { fn new(capacity: usize) -> Self; fn insert(&mut self, loc: impl FnOnce() -> GridLoc, index: impl FnOnce() -> u32); fn count(&self) -> u32; } -fn p1_ticked(input: String) -> u32 { +fn p1_ticked(input: String) -> u32 { let grid = GridView::new(&input); let mut loc = grid.index_to_loc(input.find('^').unwrap()).unwrap(); @@ -22,7 +22,7 @@ fn p1_ticked(input: String) -> u32 { collector.insert(|| loc, || grid.loc_to_index(loc)); 'directs: loop { - match loc.direct(direct, grid) { + match loc.direct(direct, &grid) { None => return collector.count(), // leave map Some(new_loc) => { match grid.get(new_loc).unwrap() { @@ -42,7 +42,7 @@ fn p1_ticked(input: String) -> u32 { } } -impl Collector for HashSet { +impl LocCounter for HashSet { fn new(capacity: usize) -> Self { Self::with_capacity_and_hasher(capacity, S::default()) } @@ -56,7 +56,7 @@ impl Collector for HashSet { } } -impl Collector for HashSet { +impl LocCounter for HashSet { fn new(capacity: usize) -> Self { Self::with_capacity_and_hasher(capacity, S::default()) } @@ -70,7 +70,28 @@ impl Collector for HashSet { } } -impl Collector for BitVec { +impl LocCounter for Vec { + fn new(capacity: usize) -> Self { + vec![false; capacity] + } + + fn insert(&mut self, _: impl FnOnce() -> GridLoc, index: impl FnOnce() -> u32) { + self[index() as usize] = true; + } + + fn count(&self) -> u32 { + let mut output = 0; + // we don't use Iterator::count() here because it uses usize instead of u32 + for &b in self { + if b { + output += 1; + } + } + output + } +} + +impl LocCounter for BitVec { fn new(capacity: usize) -> Self { Self::repeat(false, capacity) } @@ -86,4 +107,92 @@ impl Collector for BitVec { pub fn p1_ticked_fxhash_loc(input: String) -> u32 { p1_ticked::>(input) } pub fn p1_ticked_fxhash_index(input: String) -> u32 { p1_ticked::>(input) } +pub fn p1_ticked_boolvec(input: String) -> u32 { p1_ticked::>(input) } pub fn p1_ticked_bitvec(input: String) -> u32 { p1_ticked::(input) } + +trait LoopDetector { + fn new(capacity: usize) -> Self; + fn clear(&mut self); + + fn insert(&mut self, loc: impl FnOnce() -> GridLoc, index: impl FnOnce() -> u32, direct: DirectTaxicab) -> IsLooped; +} + +#[derive(PartialEq, Eq)] +enum IsLooped { + Repeating, + NewStep, +} + +fn is_looping(grid: &GridView>, det: &mut DetectorT, initial: GridLoc) -> bool { + det.clear(); + + let mut loc = initial; + let mut direct = DirectTaxicab::Up; + + 'ticks: loop { + if det.insert(|| loc, || grid.loc_to_index(loc), direct) == IsLooped::Repeating { + return true; + } + + 'directs: loop { + match loc.direct(direct, grid) { + None => return false, // leave map + Some(new_loc) => { + match grid.get(new_loc).unwrap() { + b'^' | b'.' => { + loc = new_loc; + continue 'ticks + } + b'#' => { + direct = direct.clockwise(); + continue 'directs + } + _ => unreachable!(), + } + } + } + } + } +} + +fn p2_brute(input: String) -> u32 { + let initial_index = input.find('^').unwrap(); + let size = input.len(); + let mut det = LoopDetectorT::new(size); + + let input = input.into_bytes(); + let mut grid = GridView::new(input); + let initial = grid.index_to_loc(initial_index).unwrap(); + + let mut count = 0; + for index in 0..size { + if grid.input[index] == b'.' { + grid.input[index] = b'#'; + if is_looping(&grid, &mut det, initial) { + count += 1; + } + grid.input[index] = b'.'; + } + } + count +} + +impl LoopDetector for HashSet<(GridLoc, DirectTaxicab), S> { + fn new(capacity: usize) -> Self { + HashSet::with_capacity_and_hasher(capacity, S::default()) + } + + fn clear(&mut self) { + HashSet::clear(self) + } + + fn insert(&mut self, loc: impl FnOnce() -> GridLoc, _: impl FnOnce() -> u32, direct: DirectTaxicab) -> IsLooped { + if HashSet::insert(self, (loc(), direct)) { + IsLooped::NewStep + } else { + IsLooped::Repeating + } + } +} + +pub fn p2_brute_fxhash_loc(input: String) -> u32 { p2_brute::>(input) } diff --git a/src/util.rs b/src/util.rs index c149b77..f134caa 100644 --- a/src/util.rs +++ b/src/util.rs @@ -3,21 +3,21 @@ use std::iter; #[derive(Clone, Copy)] -pub struct GridView<'a> { - input: &'a [u8], - width: u32, - height: u32, +pub struct GridView { + pub input: Input, + pub width: u32, + pub height: u32, } -impl<'a> GridView<'a> { - pub fn new(input: &'a impl AsRef<[u8]>) -> Self { - let input = input.as_ref(); - let width = input.iter().position(|&b| b == b'\n').unwrap() as u32 + 1; - Self { input, width, height: (input.len() as u32).div_ceil(width) } +impl> GridView { + pub fn new(input: Input) -> Self { + let width = input.as_ref().iter().position(|&b| b == b'\n').unwrap() as u32 + 1; + let height = (input.as_ref().len() as u32).div_ceil(width); + Self { input, width, height } } pub fn get(&self, loc: GridLoc) -> Option { - self.input.get(self.loc_to_index(loc) as usize).copied() + self.input.as_ref().get(self.loc_to_index(loc) as usize).copied() } pub fn loc_to_index(&self, loc: GridLoc) -> u32 { @@ -43,7 +43,7 @@ pub struct GridLoc { impl GridLoc { pub fn left(self) -> Option { Some(Self { x: self.x.checked_sub(1)?, y: self.y }) } - pub fn right(self, grid: GridView) -> Option { + pub fn right(self, grid: &GridView>) -> Option { Some(Self { x: match self.x.checked_add(1)? { x if x < grid.width - 1 => x, @@ -53,7 +53,7 @@ impl GridLoc { }) } pub fn up(self) -> Option { Some(Self { x: self.x, y: self.y.checked_sub(1)? }) } - pub fn down(self, grid: GridView) -> Option { + pub fn down(self, grid: &GridView>) -> Option { Some(Self { x: self.x, y: match self.y.checked_add(1)? { @@ -63,15 +63,15 @@ impl GridLoc { }) } - pub fn direct(self, direct: impl Direct, grid: GridView) -> Option { + pub fn direct(self, direct: impl Direct, grid: &GridView>) -> Option { direct.apply(self, grid) } - pub fn direct_iter( + pub fn direct_iter>( self, direct: D, - grid: GridView, - ) -> impl Iterator + use<'_, D> { + grid: &GridView, + ) -> impl Iterator + use<'_, D, Input> { let mut loc = Some(self); iter::from_fn(move || { let output = loc?; @@ -84,10 +84,10 @@ impl GridLoc { pub trait Direct: Copy + 'static { const ALL: &[Self]; - fn apply(self, loc: GridLoc, grid: GridView) -> Option; + fn apply(self, loc: GridLoc, grid: &GridView>) -> Option; } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum DirectTaxicab { Left, Right, @@ -109,7 +109,7 @@ impl DirectTaxicab { impl Direct for DirectTaxicab { const ALL: &[Self] = &[Self::Left, Self::Right, Self::Up, Self::Down]; - fn apply(self, loc: GridLoc, grid: GridView) -> Option { + fn apply(self, loc: GridLoc, grid: &GridView>) -> Option { match self { Self::Left => loc.left(), Self::Right => loc.right(grid), @@ -119,7 +119,7 @@ impl Direct for DirectTaxicab { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum DirectBoth { Left, Right, @@ -143,7 +143,7 @@ impl Direct for DirectBoth { Self::RightDown, ]; - fn apply(self, loc: GridLoc, grid: GridView) -> Option { + fn apply(self, loc: GridLoc, grid: &GridView>) -> Option { match self { Self::Left => loc.left(), Self::Right => loc.right(grid), @@ -157,7 +157,7 @@ impl Direct for DirectBoth { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum DirectDiagonal { LeftUp, RightUp, @@ -168,7 +168,7 @@ pub enum DirectDiagonal { impl Direct for DirectDiagonal { const ALL: &[Self] = &[Self::LeftUp, Self::RightUp, Self::LeftDown, Self::RightDown]; - fn apply(self, loc: GridLoc, grid: GridView) -> Option { + fn apply(self, loc: GridLoc, grid: &GridView>) -> Option { match self { Self::LeftUp => loc.left().and_then(GridLoc::up), Self::RightUp => loc.right(grid).and_then(GridLoc::up),