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
//! # Sporifica Virus
//!
//! Brute force solution using a fixed size grid, relying on the properties of the input to never
//! exceed the bounds. Some bit manipulation tricks are used to speeds things up slightly.
use crate::util::grid::*;
use crate::util::point::*;
pub fn parse(input: &str) -> Grid<u8> {
Grid::parse(input)
}
pub fn part1(input: &Grid<u8>) -> usize {
simulate(input, 10_000, 2)
}
pub fn part2(input: &Grid<u8>) -> usize {
simulate(input, 10_000_000, 1)
}
fn simulate(input: &Grid<u8>, bursts: usize, delta: usize) -> usize {
// Assume that the carrier will never go outside the range [0, 512] in both x and y axis.
// starting at the center (256, 256).
let full = 512;
let half = 256;
// Right, Down, Left, Up
let offsets = [1, full, 0_usize.wrapping_sub(1), 0_usize.wrapping_sub(full)];
// Copy input
let mut grid = vec![1; full * full];
let offset = half - (input.width / 2) as usize;
for x in 0..input.width {
for y in 0..input.height {
if input[Point::new(x, y)] == b'#' {
let index = full * (offset + y as usize) + (offset + x as usize);
grid[index] = 3; // Infected
}
}
}
let mut index = full * half + half; // Center
let mut direction = 3; // Up
let mut result = 0;
for _ in 0..bursts {
// Change state by adding either `2` for part one (flipping between clean and infected)
// or `1` for part two (using all four states).
// Clean => 1
// Weakened => 2
// Infected => 3
// Flagged => 0
let current = grid[index] as usize;
let next = (current + delta) & 0x3;
grid[index] = next as u8;
// Infected nodes result in a value of 4 >> 2 = 1, all other nodes result in 0.
result += (next + 1) >> 2;
// Change direction by adding an index modulo 4 depending on node type.
// Clean => 1 + 2 => 3 (left)
// Weakened => 2 + 2 => 0 (straight)
// Infected => 3 + 2 => 1 (right)
// Flagged => 0 + 2 => 2 (reverse)
direction = (direction + current + 2) & 0x3;
index = index.wrapping_add(offsets[direction]);
}
result
}