-
Notifications
You must be signed in to change notification settings - Fork 15
/
day11.rs
117 lines (99 loc) · 3.15 KB
/
day11.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! # Seating System
//!
//! Cellular automata are hard to speed up due to the need to check all neighbors each iteration.
//! For both parts we minimize expensive memory allocation by creating only two temporary buffers
//! then swapping between them each turn, a similar approach to double buffering.
//!
//! For part two we can further optimize by precalculating the locations of the nearest visible
//! seats only once then reusing that information for each step.
use crate::util::grid::*;
use crate::util::point::*;
use std::mem::swap;
const FLOOR: u8 = b'.';
const DIRECTIONS: [Point; 8] = [
Point::new(-1, -1),
Point::new(0, -1),
Point::new(1, -1),
Point::new(-1, 0),
Point::new(1, 0),
Point::new(-1, 1),
Point::new(0, 1),
Point::new(1, 1),
];
struct Seat {
index: u16,
size: u8,
neighbors: [u16; 8],
}
impl Seat {
#[inline]
fn push(&mut self, index: u16) {
self.neighbors[self.size as usize] = index;
self.size += 1;
}
}
pub fn parse(input: &str) -> Grid<u8> {
Grid::parse(input)
}
pub fn part1(input: &Grid<u8>) -> u32 {
simulate(input, true, 4)
}
pub fn part2(input: &Grid<u8>) -> u32 {
simulate(input, false, 5)
}
pub fn simulate(input: &Grid<u8>, part_one: bool, limit: u8) -> u32 {
let width = input.width;
let height = input.height;
let mut seats = Vec::new();
for y in 0..height {
for x in 0..width {
let point = Point::new(x, y);
if input[point] == FLOOR {
continue;
}
let mut seat = Seat { index: (width * y + x) as u16, size: 0, neighbors: [0; 8] };
for direction in DIRECTIONS {
if part_one {
let next = point + direction;
if input.contains(next) && input[next] != FLOOR {
seat.push((width * next.y + next.x) as u16);
}
} else {
let mut next = point + direction;
while input.contains(next) {
if input[next] != FLOOR {
seat.push((width * next.y + next.x) as u16);
break;
}
next += direction;
}
}
}
seats.push(seat);
}
}
let mut current = vec![0; (width * height) as usize];
let mut next = vec![0; (width * height) as usize];
let mut change = true;
while change {
change = false;
for seat in &seats {
let index = seat.index as usize;
let mut total = 0;
for i in 0..seat.size {
total += current[seat.neighbors[i as usize] as usize];
}
if current[index] == 0 && total == 0 {
next[index] = 1;
change |= true;
} else if current[index] == 1 && total >= limit {
next[index] = 0;
change |= true;
} else {
next[index] = current[index];
}
}
swap(&mut current, &mut next);
}
current.iter().map(|&n| n as u32).sum()
}