aoc/year2015/
day06.rs

1//! # Probably a Fire Hazard
2//!
3//! Brute force approach that calculates each row independently, parallelizing the work across
4//! multiple threads.
5use crate::util::iter::*;
6use crate::util::parse::*;
7use crate::util::thread::*;
8
9#[derive(Clone, Copy)]
10enum Command {
11    On,
12    Off,
13    Toggle,
14}
15
16impl Command {
17    fn from(bytes: &[u8]) -> Command {
18        match bytes[6] {
19            b'n' => Command::On,
20            b'f' => Command::Off,
21            b' ' => Command::Toggle,
22            _ => unreachable!(),
23        }
24    }
25}
26
27#[derive(Clone, Copy)]
28struct Rectangle {
29    x1: usize,
30    x2: usize,
31    y1: usize,
32    y2: usize,
33}
34
35impl Rectangle {
36    // Add one to both x2 and y2 to make ranges easier.
37    fn from([x1, y1, x2, y2]: [usize; 4]) -> Rectangle {
38        Rectangle { x1, y1, x2: x2 + 1, y2: y2 + 1 }
39    }
40}
41
42#[derive(Clone, Copy)]
43pub struct Instruction {
44    command: Command,
45    rectangle: Rectangle,
46}
47
48impl Instruction {
49    fn from((bytes, points): (&[u8], [usize; 4])) -> Instruction {
50        let command = Command::from(bytes);
51        let rectangle = Rectangle::from(points);
52        Instruction { command, rectangle }
53    }
54}
55
56pub fn parse(input: &str) -> Vec<Instruction> {
57    let first = input.lines().map(str::as_bytes);
58    let second = input.iter_unsigned().chunk::<4>();
59    first.zip(second).map(Instruction::from).collect()
60}
61
62pub fn part1(input: &[Instruction]) -> u32 {
63    let items: Vec<_> = (0..1000).collect();
64    let result = spawn_parallel_iterator(&items, |iter| worker_one(input, iter));
65    result.into_iter().sum()
66}
67
68pub fn part2(input: &[Instruction]) -> u32 {
69    let items: Vec<_> = (0..1000).collect();
70    let result = spawn_parallel_iterator(&items, |iter| worker_two(input, iter));
71    result.into_iter().sum()
72}
73
74fn worker_one(input: &[Instruction], iter: ParIter<'_, usize>) -> u32 {
75    iter.map(|row| {
76        let mut grid = [0_u8; 1_024];
77
78        for &Instruction { command, rectangle: Rectangle { x1, y1, x2, y2 } } in input {
79            if (y1..y2).contains(row) {
80                let iter = grid[x1..x2].iter_mut();
81                match command {
82                    Command::On => iter.for_each(|b| *b = 1),
83                    Command::Off => iter.for_each(|b| *b = 0),
84                    Command::Toggle => iter.for_each(|b| *b ^= 1),
85                }
86            }
87        }
88
89        grid.into_iter().map(|b| b as u32).sum::<u32>()
90    })
91    .sum()
92}
93
94fn worker_two(input: &[Instruction], iter: ParIter<'_, usize>) -> u32 {
95    iter.map(|row| {
96        let mut grid = [0_u8; 1_024];
97
98        for &Instruction { command, rectangle: Rectangle { x1, y1, x2, y2 } } in input {
99            if (y1..y2).contains(row) {
100                let iter = grid[x1..x2].iter_mut();
101                match command {
102                    Command::On => iter.for_each(|b| *b += 1),
103                    Command::Off => iter.for_each(|b| *b = b.saturating_sub(1)),
104                    Command::Toggle => iter.for_each(|b| *b += 2),
105                }
106            }
107        }
108
109        grid.into_iter().map(|b| b as u32).sum::<u32>()
110    })
111    .sum()
112}