1
+ //! # Regolith Reservoir
2
+ //!
3
+ //! We could simulate each grain of sand falling one at a time, for example:
4
+ //!
5
+ //! ```none
6
+ //! #o# # # # # # #
7
+ //! # # #o# # # # #
8
+ //! # # => # # => #o# => # #
9
+ //! # # # # # # #o#
10
+ //! ### ### ### ###
11
+ //! ```
12
+ //!
13
+ //! In this example it would take 4 steps to simulate the first grain, 3 steps for the second
14
+ //! and so on. More generally it would take `∑n` = `n * (n + 1) / 2` or `O(n²)` complexity for
15
+ //! the whole pile.
16
+ //!
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,
19
+ //! for example:
20
+ //!
21
+ //! ```none
22
+ //! #?# #?# #?# #?# #?# #?# #?# #o#
23
+ //! # # #?# #?# #?# #?# #?# #o# #o#
24
+ //! # # => # # => #?# => #?# => #?# => #o# => #o# => #o#
25
+ //! # # # # # # #?# #o# #o# #o# #o#
26
+ //! ### ### ### ### ### ### ### ###
27
+ //! ```
28
+ //!
29
+ //! We model the cave as a grid in the possible states:
30
+ //! * `Air` Empty blocks, treated as unknown status when checking underneath neighbors.
31
+ //! * `Falling` Grains of sand that will continue to fall continously forever.
32
+ //! * `Stopped` Both original rock walls and any grains of sand that have come to rest.
1
33
use crate :: util:: parse:: * ;
2
- use Kind :: * ;
3
34
4
35
#[ derive( Clone , Copy , PartialEq , Eq ) ]
5
36
enum Kind {
@@ -20,67 +51,81 @@ pub struct Cave {
20
51
21
52
impl Cave {
22
53
fn fall ( & mut self , index : usize ) -> Kind {
54
+ // Check in order: center, left then right
23
55
let result = self . check ( index + self . width )
24
56
&& self . check ( index + self . width - 1 )
25
57
&& self . check ( index + self . width + 1 ) ;
26
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.
27
61
if result {
28
62
self . count += 1 ;
29
- self . kind [ index] = Stopped ;
30
- Stopped
63
+ self . kind [ index] = Kind :: Stopped ;
64
+ Kind :: Stopped
31
65
} else {
32
- self . kind [ index] = Falling ;
33
- Falling
66
+ self . kind [ index] = Kind :: Falling ;
67
+ Kind :: Falling
34
68
}
35
69
}
36
70
71
+ // Returns `true` if cell is stopped.
37
72
fn check ( & mut self , index : usize ) -> bool {
38
73
let kind = if index >= self . size {
74
+ // If we've reached the "floor" then return that.
39
75
self . floor
40
- } else if self . kind [ index] == Air {
76
+ } else if self . kind [ index] == Kind :: Air {
77
+ // If we're unknown then recursively check our own underneath neighbors
41
78
self . fall ( index)
42
79
} else {
80
+ // Otherwise use the cached value.
43
81
self . kind [ index]
44
82
} ;
45
- kind == Stopped
83
+ kind == Kind :: Stopped
46
84
}
47
85
}
48
86
87
+ /// Creates a 2D grid cave exactly the maximum possible size.
49
88
pub fn parse ( input : & str ) -> Cave {
50
89
let unsigned = |line : & str | line. iter_unsigned ( ) . collect ( ) ;
51
90
let points: Vec < Vec < usize > > = input. lines ( ) . map ( unsigned) . collect ( ) ;
52
91
let max_y = points. iter ( ) . flat_map ( |row| row. iter ( ) . skip ( 1 ) . step_by ( 2 ) ) . max ( ) . unwrap ( ) ;
53
- let width = 2 * max_y + 5 ;
92
+ // Floor is 2 below the bottommost wall.
54
93
let height = max_y + 2 ;
94
+ // Allow enough horizontal room to spread out.
95
+ let width = 2 * height + 1 ;
55
96
let size = width * height;
56
- let mut kind = vec ! [ Air ; size] ;
97
+ let mut kind = vec ! [ Kind :: Air ; size] ;
57
98
99
+ // Draw each of the walls.
58
100
for row in points {
59
101
for window in row. windows ( 4 ) . step_by ( 2 ) {
60
102
if let & [ x1, y1, x2, y2] = window {
61
103
for x in x1. min ( x2) ..=x1. max ( x2) {
62
104
for y in y1. min ( y2) ..=y1. max ( y2) {
63
- kind[ ( width * y) + ( x + height - 500 ) ] = Stopped ;
105
+ kind[ ( width * y) + ( x + height - 500 ) ] = Kind :: Stopped ;
64
106
}
65
107
}
66
108
}
67
109
}
68
110
}
69
111
70
- Cave { width, height, size, kind, floor : Air , count : 0 }
112
+ Cave { width, height, size, kind, floor : Kind :: Air , count : 0 }
71
113
}
72
114
115
+ /// If a grain of sand reaches the floor it will fall forever.
73
116
pub fn part1 ( input : & Cave ) -> u32 {
74
- simulate ( input, Falling )
117
+ simulate ( input, Kind :: Falling )
75
118
}
76
119
120
+ /// The floor is solid rock.
77
121
pub fn part2 ( input : & Cave ) -> u32 {
78
- simulate ( input, Stopped )
122
+ simulate ( input, Kind :: Stopped )
79
123
}
80
124
81
125
fn simulate ( input : & Cave , floor : Kind ) -> u32 {
82
126
let mut cave = input. clone ( ) ;
83
127
cave. floor = floor;
128
+ // Height is also the x coordinate of the central starting location for grains.
84
129
cave. fall ( cave. height ) ;
85
130
cave. count
86
131
}
0 commit comments