1515//! the whole pile.
1616//!
1717//! We instead simulate in `O(n)` complexity by recursively check each grain's underneath
18- //! neighbors until we have a conclusive result then propagating that back up the call stack,
18+ //! neighbors until we have a conclusive result then propagating that back up the stack,
1919//! for example:
2020//!
2121//! ```none
3131//! * `Falling` Grains of sand that will continue to fall continously forever.
3232//! * `Stopped` Both original rock walls and any grains of sand that have come to rest.
3333use crate :: util:: parse:: * ;
34+ use Kind :: * ;
3435
3536#[ derive( Clone , Copy , PartialEq , Eq ) ]
3637enum Kind {
@@ -43,89 +44,86 @@ enum Kind {
4344pub struct Cave {
4445 width : usize ,
4546 height : usize ,
46- size : usize ,
4747 kind : Vec < Kind > ,
48- floor : Kind ,
49- count : u32 ,
50- }
51-
52- impl Cave {
53- fn fall ( & mut self , index : usize ) -> Kind {
54- // Check in order: center, left then right
55- let result = self . check ( index + self . width )
56- && self . check ( index + self . width - 1 )
57- && self . check ( index + self . width + 1 ) ;
58-
59- // If all 3 bottom neighbors are stopped then so are we.
60- // Cache the result into the grid then propagate result back up the call stack.
61- if result {
62- self . count += 1 ;
63- self . kind [ index] = Kind :: Stopped ;
64- Kind :: Stopped
65- } else {
66- self . kind [ index] = Kind :: Falling ;
67- Kind :: Falling
68- }
69- }
70-
71- // Returns `true` if cell is stopped.
72- fn check ( & mut self , index : usize ) -> bool {
73- let kind = if index >= self . size {
74- // If we've reached the "floor" then return that.
75- self . floor
76- } else if self . kind [ index] == Kind :: Air {
77- // If we're unknown then recursively check our own underneath neighbors
78- self . fall ( index)
79- } else {
80- // Otherwise use the cached value.
81- self . kind [ index]
82- } ;
83- kind == Kind :: Stopped
84- }
8548}
8649
8750/// Creates a 2D grid cave exactly the maximum possible size.
8851pub fn parse ( input : & str ) -> Cave {
8952 let unsigned = |line : & str | line. iter_unsigned ( ) . collect ( ) ;
9053 let points: Vec < Vec < usize > > = input. lines ( ) . map ( unsigned) . collect ( ) ;
9154 let max_y = points. iter ( ) . flat_map ( |row| row. iter ( ) . skip ( 1 ) . step_by ( 2 ) ) . max ( ) . unwrap ( ) ;
55+
9256 // Floor is 2 below the bottommost wall.
9357 let height = max_y + 2 ;
9458 // Allow enough horizontal room to spread out.
9559 let width = 2 * height + 1 ;
96- let size = width * height;
97- let mut kind = vec ! [ Kind :: Air ; size] ;
9860
9961 // Draw each of the walls.
62+ let mut kind = vec ! [ Air ; width * height] ;
63+
10064 for row in points {
10165 for window in row. windows ( 4 ) . step_by ( 2 ) {
10266 if let & [ x1, y1, x2, y2] = window {
103- for x in x1 . min ( x2 ) ..=x1 . max ( x2 ) {
67+ if x1 == x2 {
10468 for y in y1. min ( y2) ..=y1. max ( y2) {
105- kind[ ( width * y) + ( x + height - 500 ) ] = Kind :: Stopped ;
69+ kind[ width * y + x1 + height - 500 ] = Stopped ;
70+ }
71+ } else {
72+ for x in x1. min ( x2) ..=x1. max ( x2) {
73+ kind[ width * y1 + x + height - 500 ] = Stopped ;
10674 }
10775 }
10876 }
10977 }
11078 }
11179
112- Cave { width, height, size , kind, floor : Kind :: Air , count : 0 }
80+ Cave { width, height, kind }
11381}
11482
11583/// If a grain of sand reaches the floor it will fall forever.
11684pub fn part1 ( input : & Cave ) -> u32 {
117- simulate ( input, Kind :: Falling )
85+ simulate ( input, Falling )
11886}
11987
12088/// The floor is solid rock.
12189pub fn part2 ( input : & Cave ) -> u32 {
122- simulate ( input, Kind :: Stopped )
90+ simulate ( input, Stopped )
12391}
12492
12593fn simulate ( input : & Cave , floor : Kind ) -> u32 {
126- let mut cave = input. clone ( ) ;
127- cave. floor = floor;
94+ let Cave { width, height, mut kind } = input. clone ( ) ;
95+ let mut count = 0 ;
96+
12897 // Height is also the x coordinate of the central starting location for grains.
129- cave. fall ( cave. height ) ;
130- cave. count
98+ let mut todo = Vec :: with_capacity ( 1_000 ) ;
99+ todo. push ( height) ;
100+
101+ ' outer: while let Some ( index) = todo. pop ( ) {
102+ // Check in order: center, left then right
103+ for next in [ index + width, index + width - 1 , index + width + 1 ] {
104+ // If we've reached the "floor" then return that.
105+ let tile = if next >= kind. len ( ) { floor } else { kind[ next] } ;
106+
107+ match tile {
108+ // If we're unknown then check underneath neighbors first then re-check this tile.
109+ Air => {
110+ todo. push ( index) ;
111+ todo. push ( next) ;
112+ continue ' outer;
113+ }
114+ // Any falling tile underneath means that this tile is also falling.
115+ Falling => {
116+ kind[ index] = Falling ;
117+ continue ' outer;
118+ }
119+ Stopped => ( ) ,
120+ }
121+ }
122+
123+ // If all 3 tiles underneath are stopped then this tile is also stopped.
124+ kind[ index] = Stopped ;
125+ count += 1 ;
126+ }
127+
128+ count
131129}
0 commit comments