aoc/src/day14.rs
2020-12-16 16:52:43 -08:00

175 lines
4.5 KiB
Rust

use aoc_runner_derive::aoc;
use std::collections::HashMap;
#[derive(Debug)]
enum Instr<'a> {
Mask(&'a str),
UpdateMem { offset: u64, value: u64 },
}
impl<'a> Instr<'a> {
fn parse(instr: &'a str) -> Option<Self> {
let eq_idx = instr.find('=')?;
let lhs = &instr[..eq_idx - 1];
let rhs = &instr[eq_idx + 2..];
if lhs == "mask" {
Some(Instr::Mask(rhs))
} else {
Self::parse_mem(lhs, rhs)
}
}
fn parse_mem(lhs: &str, rhs: &str) -> Option<Self> {
let offset: u64 = lhs[4..lhs.len() - 1].parse().ok()?;
let value: u64 = rhs.parse().ok()?;
Some(Instr::UpdateMem { offset, value })
}
}
#[derive(Debug)]
struct Mask {
or_mask: u64,
and_mask: u64,
}
impl Mask {
fn parse(mask: &[u8]) -> Option<Mask> {
if mask.len() != 36 {
return None;
}
let mut and_mask = !0;
let mut or_mask = 0;
for (idx, b) in mask.iter().enumerate() {
match b {
b'0' => {
and_mask &= !(1 << 35 - idx);
}
b'1' => {
or_mask |= 1 << 35 - idx;
}
b'X' => {}
_ => return None,
}
}
Some(Mask { and_mask, or_mask })
}
fn mask(&self, value: u64) -> u64 {
value & self.and_mask | self.or_mask
}
}
#[aoc(day14, part1)]
fn solve_d14_p1(input: &str) -> u64 {
// Assume initialization doesn't write to any memory offset multiple times.
let mut iter = input.split('\n').map(|x| Instr::parse(x).unwrap());
let mut mask = match iter.next() {
Some(Instr::Mask(mask)) => Mask::parse(mask.as_bytes()).unwrap(),
_ => panic!("first line is not valid bitmask"),
};
let mut memory = std::collections::HashMap::new();
for instr in iter {
match instr {
Instr::Mask(new_mask) => mask = Mask::parse(new_mask.as_bytes()).unwrap(),
Instr::UpdateMem { offset, value } => {
memory.insert(offset, mask.mask(value));
}
}
}
memory.values().sum()
}
struct Mask2 {
floating: u64,
or_mask: u64,
}
impl Mask2 {
fn parse(mask: &[u8]) -> Option<Mask2> {
if mask.len() != 36 {
return None;
}
let mut floating = 0;
let mut or_mask = 0;
for (idx, b) in mask.iter().enumerate() {
match b {
b'0' => {}
b'1' => {
or_mask |= 1 << 35 - idx;
}
b'X' => {
floating |= 1 << 35 - idx;
}
_ => return None,
}
}
Some(Mask2 { floating, or_mask })
}
fn set_memory(&self, mem: &mut HashMap<u64, u64>, address: u64, value: u64) {
let offset = address | self.or_mask;
for mut floating_value in 0..2 << self.floating.count_ones() {
let mut addr = offset;
for bit in BitIndexes::new(self.floating) {
addr = set_bit(addr, bit, (floating_value & 1) == 1);
floating_value >>= 1;
}
mem.insert(addr, value);
}
}
}
fn set_bit(value: u64, bit_idx: u64, enabled: bool) -> u64 {
if enabled {
value | (1 << bit_idx)
} else {
value & !(1 << bit_idx)
}
}
struct BitIndexes {
value: u64,
current_idx: u64,
}
impl BitIndexes {
fn new(value: u64) -> Self {
BitIndexes {
value,
current_idx: 0,
}
}
}
impl Iterator for BitIndexes {
type Item = u64;
fn next(&mut self) -> Option<u64> {
while self.value > 0 {
let set = self.value & 1 == 1;
self.value >>= 1;
self.current_idx += 1;
if set {
return Some(self.current_idx - 1);
}
}
return None;
}
}
#[aoc(day14, part2)]
fn solve_d14_p2(input: &str) -> u64 {
let mut iter = input.split('\n').map(|x| Instr::parse(x).unwrap());
let mut mask = match iter.next() {
Some(Instr::Mask(mask)) => Mask2::parse(mask.as_bytes()).unwrap(),
_ => panic!("first line is not valid bitmask"),
};
let mut memory = std::collections::HashMap::new();
for instr in iter {
match instr {
Instr::Mask(new_mask) => mask = Mask2::parse(new_mask.as_bytes()).unwrap(),
Instr::UpdateMem { offset, value } => {
mask.set_memory(&mut memory, offset, value);
}
}
}
memory.values().sum()
}