Skip to content

Commit a413b32

Browse files
committed
Document Year 2021 Day 25
1 parent eabe207 commit a413b32

File tree

1 file changed

+25
-0
lines changed

1 file changed

+25
-0
lines changed

src/year2021/day25.rs

+25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
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.
118
use std::ops::{BitAnd, BitOr, Not};
219

20+
/// Duct tape two `u128` together to make a 256 bit wide integer.
321
#[derive(Clone, Copy, Default)]
422
pub struct U256 {
523
left: u128,
@@ -19,6 +37,7 @@ impl U256 {
1937
self.left != 0 || self.right != 0
2038
}
2139

40+
/// Perform a `rotate_left` where the width can be different from 256 bits.
2241
fn left_roll(&self, width: usize) -> U256 {
2342
if width <= 128 {
2443
let mask = !(1 << width);
@@ -32,6 +51,7 @@ impl U256 {
3251
}
3352
}
3453

54+
/// Perform a `rotate_right` where the width can be different from 256 bits.
3555
fn right_roll(&self, width: usize) -> U256 {
3656
if width <= 128 {
3757
let right = (self.right >> 1) | ((self.right & 1) << (width - 1));
@@ -44,6 +64,7 @@ impl U256 {
4464
}
4565
}
4666

67+
/// Implement operator bitswise logic so that we can use the regular `&`, `|` and `!` notation.
4768
impl BitAnd for U256 {
4869
type Output = U256;
4970

@@ -76,6 +97,7 @@ pub struct State {
7697
down: Vec<U256>,
7798
}
7899

100+
/// Parse the sea cucumbers as individual bits into separate `across` and `down` arrays.
79101
pub fn parse(input: &str) -> State {
80102
let raw: Vec<&[u8]> = input.lines().map(str::as_bytes).collect();
81103
let width = raw[0].len();
@@ -111,6 +133,8 @@ pub fn part1(input: &State) -> usize {
111133
changed = false;
112134
count += 1;
113135

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.
114138
for i in 0..height {
115139
let candidates = across[i].left_roll(width);
116140
let moved = candidates & !(across[i] | down[i]);
@@ -119,6 +143,7 @@ pub fn part1(input: &State) -> usize {
119143
across[i] = moved | stay;
120144
}
121145

146+
// Use a similar approach to handle an entire row down at a time.
122147
let last_mask = across[0] | down[0];
123148
let mut moved = down[height - 1] & !last_mask;
124149

0 commit comments

Comments
 (0)