15
15
//! the whole pile.
16
16
//!
17
17
//! 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,
19
19
//! for example:
20
20
//!
21
21
//! ```none
31
31
//! * `Falling` Grains of sand that will continue to fall continously forever.
32
32
//! * `Stopped` Both original rock walls and any grains of sand that have come to rest.
33
33
use crate :: util:: parse:: * ;
34
+ use Kind :: * ;
34
35
35
36
#[ derive( Clone , Copy , PartialEq , Eq ) ]
36
37
enum Kind {
@@ -43,89 +44,86 @@ enum Kind {
43
44
pub struct Cave {
44
45
width : usize ,
45
46
height : usize ,
46
- size : usize ,
47
47
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
- }
85
48
}
86
49
87
50
/// Creates a 2D grid cave exactly the maximum possible size.
88
51
pub fn parse ( input : & str ) -> Cave {
89
52
let unsigned = |line : & str | line. iter_unsigned ( ) . collect ( ) ;
90
53
let points: Vec < Vec < usize > > = input. lines ( ) . map ( unsigned) . collect ( ) ;
91
54
let max_y = points. iter ( ) . flat_map ( |row| row. iter ( ) . skip ( 1 ) . step_by ( 2 ) ) . max ( ) . unwrap ( ) ;
55
+
92
56
// Floor is 2 below the bottommost wall.
93
57
let height = max_y + 2 ;
94
58
// Allow enough horizontal room to spread out.
95
59
let width = 2 * height + 1 ;
96
- let size = width * height;
97
- let mut kind = vec ! [ Kind :: Air ; size] ;
98
60
99
61
// Draw each of the walls.
62
+ let mut kind = vec ! [ Air ; width * height] ;
63
+
100
64
for row in points {
101
65
for window in row. windows ( 4 ) . step_by ( 2 ) {
102
66
if let & [ x1, y1, x2, y2] = window {
103
- for x in x1 . min ( x2 ) ..=x1 . max ( x2 ) {
67
+ if x1 == x2 {
104
68
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 ;
106
74
}
107
75
}
108
76
}
109
77
}
110
78
}
111
79
112
- Cave { width, height, size , kind, floor : Kind :: Air , count : 0 }
80
+ Cave { width, height, kind }
113
81
}
114
82
115
83
/// If a grain of sand reaches the floor it will fall forever.
116
84
pub fn part1 ( input : & Cave ) -> u32 {
117
- simulate ( input, Kind :: Falling )
85
+ simulate ( input, Falling )
118
86
}
119
87
120
88
/// The floor is solid rock.
121
89
pub fn part2 ( input : & Cave ) -> u32 {
122
- simulate ( input, Kind :: Stopped )
90
+ simulate ( input, Stopped )
123
91
}
124
92
125
93
fn 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
+
128
97
// 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
131
129
}
0 commit comments