Skip to content

Commit 23b69d3

Browse files
committed
2018 day 18
1 parent df752e6 commit 23b69d3

File tree

3 files changed

+229
-0
lines changed

3 files changed

+229
-0
lines changed

crates/utils/src/bit.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,70 @@ impl<T: UnsignedInteger> Iterator for BitIterator<T> {
6464
}
6565
}
6666
}
67+
68+
/// Computes the population count for each bit position across 8 input masks.
69+
///
70+
/// Returns four masks `[bit0, bit1, bit2, bit3]` that encode, per bit position, the 4-bit count
71+
/// of how many of the 8 inputs have that bit set.
72+
///
73+
/// For example, if a given bit is set in 5 inputs, then that bit would be set in `bit0` and `bit2`.
74+
/// If a given bit is set in all 8 inputs, then that bit would only be set in `bit3`.
75+
///
76+
/// # Examples
77+
/// ```
78+
/// # use utils::bit::bitwise_count8;
79+
/// let [bit0, bit1, bit2, bit3] = bitwise_count8::<u16>(&[
80+
/// 0b100000000,
81+
/// 0b110000000,
82+
/// 0b111000000,
83+
/// 0b111100000,
84+
/// 0b111110000,
85+
/// 0b111111000,
86+
/// 0b111111100,
87+
/// 0b111111110,
88+
/// ]);
89+
/// assert_eq!(bit0, 0b010101010);
90+
/// assert_eq!(bit1, 0b011001100);
91+
/// assert_eq!(bit2, 0b011110000);
92+
/// assert_eq!(bit3, 0b100000000);
93+
/// ```
94+
///
95+
/// ```
96+
/// # use utils::bit::bitwise_count8;
97+
/// let [bit0, bit1, bit2, bit3] = bitwise_count8::<u64>(&[
98+
/// 0b00111000_01001000_10000111_11111111_10111000_11100010_00110010_01010011,
99+
/// 0b01110000_00100111_01011101_11011000_10001100_00011000_10100101_00110010,
100+
/// 0b00000101_11010011_10110011_10000000_00000000_11110110_00000101_11111010,
101+
/// 0b01101001_01001100_00111001_01101100_00110111_00101011_00010101_10011101,
102+
/// 0b00011100_11101111_00111111_01101011_00011101_01011110_11101101_10101101,
103+
/// 0b10111100_11101111_00001001_10100100_01010110_10101011_01011000_11111100,
104+
/// 0b11110110_00010001_11101111_00101101_01110111_10000011_11110110_10101011,
105+
/// 0b01110001_01100010_01111101_01001000_01011001_10110100_11100110_11100000,
106+
/// ]);
107+
/// assert_eq!(bit0, 0b00000011_10000011_11110100_01100001_01100110_11100101_00100010_00011100);
108+
/// assert_eq!(bit1, 0b10110001_11010000_11001000_00011011_11110010_01000111_00001110_10100100);
109+
/// assert_eq!(bit2, 0b01111100_01101111_00111110_11101100_00011101_10111010_11110101_11111011);
110+
/// assert_eq!(bit3, 0b00000000_00000000_00000001_00000000_00000000_00000000_00000000_00000000);
111+
/// ```
112+
#[inline]
113+
#[must_use]
114+
pub fn bitwise_count8<T: UnsignedInteger>(m: &[T; 8]) -> [T; 4] {
115+
let (s1, c1) = carry_save_adder(m[0], m[1], m[2]);
116+
let (s2, c2) = carry_save_adder(m[3], m[4], m[5]);
117+
let (s3, c3) = carry_save_adder(m[6], m[7], T::ZERO);
118+
let (s4, c4) = carry_save_adder(c1, c2, c3);
119+
let (bit0, c5) = carry_save_adder(s1, s2, s3);
120+
let (bit1, c6) = carry_save_adder(s4, c5, T::ZERO);
121+
let (bit2, bit3) = carry_save_adder(c4, c6, T::ZERO);
122+
[bit0, bit1, bit2, bit3]
123+
}
124+
125+
#[inline]
126+
#[must_use]
127+
fn carry_save_adder<T: UnsignedInteger>(a: T, b: T, c: T) -> (T, T) {
128+
let sum_ab = a ^ b;
129+
let carry_ab = a & b;
130+
let sum_abc = sum_ab ^ c;
131+
let carry_abc = carry_ab | (sum_ab & c);
132+
(sum_abc, carry_abc)
133+
}

crates/year2018/src/day18.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use utils::bit::bitwise_count8;
2+
use utils::prelude::*;
3+
4+
/// Simulating a cyclic forest cellular automaton.
5+
#[derive(Clone, Debug)]
6+
pub struct Day18 {
7+
masks: [[u64; 52]; 2],
8+
}
9+
10+
impl Day18 {
11+
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
12+
// 50x50 bitmasks, with 1 bit/row of padding on each side
13+
let mut tree_mask = [0u64; 52];
14+
let mut lumberyard_mask = [0u64; 52];
15+
16+
let mut row = 0;
17+
for line in input.lines() {
18+
if row >= 50 {
19+
return Err(InputError::new(input, line, "too many rows"));
20+
}
21+
if line.len() > 50 {
22+
return Err(InputError::new(input, line, "too many columns"));
23+
}
24+
for (col, b) in line.bytes().enumerate() {
25+
match b {
26+
b'.' => {}
27+
b'|' => tree_mask[row + 1] |= 1 << (col + 1),
28+
b'#' => lumberyard_mask[row + 1] |= 1 << (col + 1),
29+
_ => {
30+
return Err(InputError::new(input, b as char, "expected '.', '|', '#'"));
31+
}
32+
};
33+
}
34+
row += 1;
35+
}
36+
37+
if row != 50 {
38+
return Err(InputError::new(input, 0, "expected 50 rows"));
39+
}
40+
41+
Ok(Self {
42+
masks: [tree_mask, lumberyard_mask],
43+
})
44+
}
45+
46+
#[must_use]
47+
pub fn part1(&self) -> u32 {
48+
let mut masks = &mut self.masks.clone();
49+
let mut temp = &mut [[0u64; _]; _];
50+
51+
for _ in 0..10 {
52+
Self::advance(masks, temp);
53+
(masks, temp) = (temp, masks);
54+
}
55+
56+
Self::resource_value(masks)
57+
}
58+
59+
#[must_use]
60+
pub fn part2(&self) -> u32 {
61+
// Brent's algorithm for cycle detection, as used in 2017 day 6.
62+
let (mut power, mut lambda) = (1, 1);
63+
64+
let mut tortoise = &mut self.masks.clone();
65+
let mut hare = &mut self.masks.clone();
66+
let mut temp = &mut [[0u64; _]; _];
67+
68+
Self::advance(hare, temp);
69+
(hare, temp) = (temp, hare);
70+
71+
while tortoise != hare {
72+
if power == lambda {
73+
*tortoise = *hare;
74+
power *= 2;
75+
lambda = 0;
76+
}
77+
78+
Self::advance(hare, temp);
79+
(hare, temp) = (temp, hare);
80+
81+
lambda += 1;
82+
}
83+
84+
*tortoise = self.masks;
85+
*hare = self.masks;
86+
for _ in 0..lambda {
87+
Self::advance(hare, temp);
88+
(hare, temp) = (temp, hare);
89+
}
90+
91+
let mut mu = 0;
92+
while tortoise != hare {
93+
Self::advance(tortoise, temp);
94+
(tortoise, temp) = (temp, tortoise);
95+
96+
Self::advance(hare, temp);
97+
(hare, temp) = (temp, hare);
98+
99+
mu += 1;
100+
}
101+
102+
let mut minutes = 1_000_000_000 - mu;
103+
minutes -= (minutes / lambda) * lambda;
104+
105+
while minutes > 0 {
106+
Self::advance(hare, temp);
107+
(hare, temp) = (temp, hare);
108+
109+
minutes -= 1;
110+
}
111+
112+
Self::resource_value(hare)
113+
}
114+
115+
#[inline(never)]
116+
fn advance([trees, yards]: &[[u64; 52]; 2], [next_trees, next_yards]: &mut [[u64; 52]; 2]) {
117+
const COLUMNS_MASK: u64 = ((1 << 50) - 1) << 1;
118+
119+
// This loop should be vectorized by the compiler
120+
for row in 1..51 {
121+
let (gte1_trees, gte3_trees) = Self::adjacent_gte1_gte3(trees, row);
122+
let (gte1_yards, gte3_yards) = Self::adjacent_gte1_gte3(yards, row);
123+
124+
let open_to_trees = !trees[row] & !yards[row] & gte3_trees;
125+
let trees_to_yard = trees[row] & gte3_yards;
126+
let yard_to_open = yards[row] & !(gte1_trees & gte1_yards);
127+
128+
next_trees[row] = ((trees[row] & !trees_to_yard) | open_to_trees) & COLUMNS_MASK;
129+
next_yards[row] = ((yards[row] & !yard_to_open) | trees_to_yard) & COLUMNS_MASK;
130+
}
131+
}
132+
133+
#[inline]
134+
pub fn adjacent_gte1_gte3(mask: &[u64; 52], row: usize) -> (u64, u64) {
135+
let adjacent = [
136+
mask[row - 1] << 1,
137+
mask[row - 1],
138+
mask[row - 1] >> 1,
139+
mask[row] << 1,
140+
mask[row] >> 1,
141+
mask[row + 1] << 1,
142+
mask[row + 1],
143+
mask[row + 1] >> 1,
144+
];
145+
146+
let [bit0, bit1, bit2, bit3] = bitwise_count8(&adjacent);
147+
let gte1 = bit0 | bit1 | bit2 | bit3;
148+
let gte3 = (bit0 & bit1) | bit2 | bit3;
149+
150+
(gte1, gte3)
151+
}
152+
153+
#[inline]
154+
fn resource_value([trees, yards]: &[[u64; 52]; 2]) -> u32 {
155+
let tree_count = trees.iter().map(|m| m.count_ones()).sum::<u32>();
156+
let yard_count = yards.iter().map(|m| m.count_ones()).sum::<u32>();
157+
tree_count * yard_count
158+
}
159+
}
160+
161+
examples!(Day18 -> (u32, u32) []);

crates/year2018/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ utils::year!(2018 => year2018, ${
1919
15 => day15::Day15,
2020
16 => day16::Day16,
2121
17 => day17::Day17,
22+
18 => day18::Day18,
2223
});

0 commit comments

Comments
 (0)