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