1
+ //! # Sea Cucumber
2
+ //!
3
+ //! To speed things up we compute an entire row at a time for each direction, storing `across`
4
+ //! and `down` sea cucumbers as bits in a 256 bit wide variable.
5
+ //!
6
+ //! For example `...>>>>>...` is represented as `00011111000`.
7
+ //! To compute the movement the steps are:
8
+ //! * Rotate right by one `00001111100`
9
+ //! * Invert existing cucumbers `11100000111`
10
+ //! * Bitwise AND together to find cucumbers that can move `00000000100`
11
+ //! * Rotate `00000001000` left by one, invert `11111110111` then bitwise AND with original
12
+ //! cucumbers to remove moving cucumbers from static `00011110000`
13
+ //! * Finally bitwise OR static and moving cucumbers together for the final result
14
+ //! `00011110000 | 00000000100 = 00011110100`
15
+ //!
16
+ //! In the actual implementation `across` and `down` are stored separately so that we know
17
+ //! which cucumbers turn it is to move. We bitwise OR both together to calculate any blockers.
1
18
use std:: ops:: { BitAnd , BitOr , Not } ;
2
19
20
+ /// Duct tape two `u128` together to make a 256 bit wide integer.
3
21
#[ derive( Clone , Copy , Default ) ]
4
22
pub struct U256 {
5
23
left : u128 ,
@@ -19,6 +37,7 @@ impl U256 {
19
37
self . left != 0 || self . right != 0
20
38
}
21
39
40
+ /// Perform a `rotate_left` where the width can be different from 256 bits.
22
41
fn left_roll ( & self , width : usize ) -> U256 {
23
42
if width <= 128 {
24
43
let mask = !( 1 << width) ;
@@ -32,6 +51,7 @@ impl U256 {
32
51
}
33
52
}
34
53
54
+ /// Perform a `rotate_right` where the width can be different from 256 bits.
35
55
fn right_roll ( & self , width : usize ) -> U256 {
36
56
if width <= 128 {
37
57
let right = ( self . right >> 1 ) | ( ( self . right & 1 ) << ( width - 1 ) ) ;
@@ -44,6 +64,7 @@ impl U256 {
44
64
}
45
65
}
46
66
67
+ /// Implement operator bitswise logic so that we can use the regular `&`, `|` and `!` notation.
47
68
impl BitAnd for U256 {
48
69
type Output = U256 ;
49
70
@@ -76,6 +97,7 @@ pub struct State {
76
97
down : Vec < U256 > ,
77
98
}
78
99
100
+ /// Parse the sea cucumbers as individual bits into separate `across` and `down` arrays.
79
101
pub fn parse ( input : & str ) -> State {
80
102
let raw: Vec < & [ u8 ] > = input. lines ( ) . map ( str:: as_bytes) . collect ( ) ;
81
103
let width = raw[ 0 ] . len ( ) ;
@@ -111,6 +133,8 @@ pub fn part1(input: &State) -> usize {
111
133
changed = false ;
112
134
count += 1 ;
113
135
136
+ // Use the bitwise logic described above to process an entire row across at a time.
137
+ // Direction is reflected due to the parsing so we rotate left instead of right.
114
138
for i in 0 ..height {
115
139
let candidates = across[ i] . left_roll ( width) ;
116
140
let moved = candidates & !( across[ i] | down[ i] ) ;
@@ -119,6 +143,7 @@ pub fn part1(input: &State) -> usize {
119
143
across[ i] = moved | stay;
120
144
}
121
145
146
+ // Use a similar approach to handle an entire row down at a time.
122
147
let last_mask = across[ 0 ] | down[ 0 ] ;
123
148
let mut moved = down[ height - 1 ] & !last_mask;
124
149
0 commit comments