year2016/
day18.rs

1use utils::prelude::*;
2
3/// Counting safe tiles.
4#[derive(Clone, Debug)]
5pub struct Day18 {
6    input: u128,
7    columns: u32,
8    input_type: InputType,
9}
10
11impl Day18 {
12    pub fn new(input: &str, input_type: InputType) -> Result<Self, InputError> {
13        if input.len() > 128 {
14            Err(InputError::new(input, input, "too many columns"))
15        } else if let Some(index) = input.find(|c| c != '.' && c != '^') {
16            Err(InputError::new(input, index, "expected '.' or '^'"))
17        } else {
18            Ok(Self {
19                input: input
20                    .bytes()
21                    .fold(0, |acc, b| (acc << 1) | u128::from(b == b'^')),
22                columns: input.len() as u32,
23                input_type,
24            })
25        }
26    }
27
28    #[must_use]
29    pub fn part1(&self) -> u32 {
30        self.count_safe(match self.input_type {
31            InputType::Example => 10,
32            InputType::Real => 40,
33        })
34    }
35
36    #[must_use]
37    pub fn part2(&self) -> u32 {
38        self.count_safe(400000)
39    }
40
41    #[inline]
42    fn count_safe(&self, rows: u32) -> u32 {
43        let mask = u128::MAX.wrapping_shr(128 - self.columns);
44
45        let mut row = self.input;
46        let mut traps = 0;
47        for _ in 0..rows {
48            traps += row.count_ones();
49
50            // Tiles on the next row are traps if only one of the left and right tiles on the
51            // previous row are traps.
52            row = ((row << 1) ^ (row >> 1)) & mask;
53        }
54
55        (rows * self.columns) - traps
56    }
57}
58
59examples!(Day18 -> (u32, u32) [
60    {input: ".^^.^.^^^^", part1: 38},
61]);