Compare commits

..

1 commit

Author SHA1 Message Date
Tristan Daniël Maat 2f48b0e912
Complete day 14 2020-12-15 20:33:16 +00:00

View file

@ -1,6 +1,5 @@
use std::convert::TryFrom; use std::collections::HashMap;
use std::fs; use std::fs;
use std::iter;
use itertools::Itertools; use itertools::Itertools;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -10,22 +9,26 @@ const MASK_36_BITS: u64 = 0xFFFFFFFFF;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let input = fs::read_to_string("input")?; let input = fs::read_to_string("input")?;
let program = parse_program_chip1(&input)?; let program = parse_program(&input)?;
// Part 1 // Part 1
let memory = run_program_chip1(&program); let memory = run_program_chip1(&program);
println!("{}", memory.iter().map(|(_, value)| value).sum::<u64>()); println!("{}", memory.iter().map(|(_, value)| value).sum::<u64>());
// Part 2
let memory = run_program_chip2(&program);
println!("{}", memory.iter().map(|(_, value)| value).sum::<u64>());
Ok(()) Ok(())
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
enum Chip1Instruction { enum Instruction {
Mask(u64, u64), Mask(u64, u64, u64),
Mem(usize, u64), Mem(u64, u64),
} }
impl Chip1Instruction { impl Instruction {
fn new(instruction: &str) -> Result<Self, String> { fn new(instruction: &str) -> Result<Self, String> {
lazy_static! { lazy_static! {
static ref INSTRUCTION_RE: Regex = static ref INSTRUCTION_RE: Regex =
@ -44,7 +47,7 @@ impl Chip1Instruction {
let or = u64::from_str_radix(&caps[3].replace("X", "0"), 2) let or = u64::from_str_radix(&caps[3].replace("X", "0"), 2)
.map_err(|e| format!("Could not parse or: {}", e))?; .map_err(|e| format!("Could not parse or: {}", e))?;
Chip1Instruction::Mask(and, or) Instruction::Mask(and, or, and ^ or)
} }
"mem" => { "mem" => {
let location = caps[2] let location = caps[2]
@ -54,30 +57,28 @@ impl Chip1Instruction {
.parse() .parse()
.map_err(|e| format!("Could not parse value: {}", e))?; .map_err(|e| format!("Could not parse value: {}", e))?;
Chip1Instruction::Mem(location, value) Instruction::Mem(location, value)
} }
_ => Err("Invalid instruction")?, _ => Err("Invalid instruction")?,
}) })
} }
} }
fn parse_program_chip1(input: &str) -> Result<Vec<Chip1Instruction>, String> { fn parse_program(input: &str) -> Result<Vec<Instruction>, String> {
input.lines().map(Chip1Instruction::new).collect() input.lines().map(Instruction::new).collect()
} }
fn run_program_chip1(program: &Vec<Chip1Instruction>) -> Vec<(u64, u64)> { fn run_program_chip1(program: &Vec<Instruction>) -> Vec<(u64, u64)> {
let mut mask = (u64::max_value() & MASK_36_BITS, 0); let mut mask = (u64::max_value() & MASK_36_BITS, 0);
program program
.iter() .iter()
.filter_map(|instruction| match instruction { .filter_map(|instruction| match instruction {
Chip1Instruction::Mask(and, or) => { Instruction::Mask(and, or, _) => {
mask = (*and, *or); mask = (*and, *or);
None None
} }
Chip1Instruction::Mem(address, value) => { Instruction::Mem(address, value) => Some((*address as u64, value & mask.0 | mask.1)),
Some((*address as u64, value & mask.0 | mask.1))
}
}) })
.collect::<Vec<(u64, u64)>>() .collect::<Vec<(u64, u64)>>()
.into_iter() .into_iter()
@ -86,86 +87,36 @@ fn run_program_chip1(program: &Vec<Chip1Instruction>) -> Vec<(u64, u64)> {
.collect() .collect()
} }
#[derive(Clone, Debug)] fn run_program_chip2(program: &Vec<Instruction>) -> Vec<(u64, u64)> {
enum Chip2Instruction { let mut memory = HashMap::new();
Mask(u64, u64), let mut mask = (0, 0);
Mem(usize, u64),
}
impl Chip2Instruction { for instruction in program {
fn new(instruction: &str) -> Result<Self, String> { match instruction {
lazy_static! { Instruction::Mask(_, or, floating) => {
static ref INSTRUCTION_RE: Regex = mask = (*or, *floating);
Regex::new(r"([[:alpha:]]+?)\[?(\d+?)?\]? = ([0-9X]+)") }
.expect("Regex should compile"); Instruction::Mem(address, value) => {
// We set floating bits to 0 here, so that they can be
// toggled with addition
let base = (address | mask.0) & !mask.1 & MASK_36_BITS;
let mut floating = !mask.1 & MASK_36_BITS;
while floating <= MASK_36_BITS {
// For each unset bit in the floating mask, we
// toggle the bit in the address
memory.insert(base | (!floating & MASK_36_BITS), *value);
floating += 1;
// Since add does overflows, we reset 1s to 0s if
// they're unset in the original mask
floating |= !mask.1 & MASK_36_BITS;
}
}
} }
let caps = INSTRUCTION_RE
.captures(instruction)
.ok_or("Instruction did not match")?;
Ok(match &caps[1] {
"mask" => {
let or = u64::from_str_radix(&caps[3].replace("X", "0"), 2)
.map_err(|e| format!("Could not parse or: {}", e))?;
let floating = u64::from_str_radix(&caps[3].replace("X", "0"), 2)
.map_err(|e| format!("Could not parse floating: {}", e))?;
Chip2Instruction::Mask(or, floating)
}
"mem" => {
let location = caps[2]
.parse()
.map_err(|e| format!("Could not parse location: {}", e))?;
let value = caps[3]
.parse()
.map_err(|e| format!("Could not parse value: {}", e))?;
Chip2Instruction::Mem(location, value)
}
_ => Err("Invalid instruction")?,
})
} }
}
fn parse_program_chip2(input: &str) -> Result<Vec<Chip2Instruction>, String> { memory.drain().collect()
input.lines().map(Chip2Instruction::new).collect()
}
fn run_program_chip2(program: &Vec<Chip2Instruction>) -> Vec<(u64, u64)> {
let mut mask = (
u64::max_value() & MASK_36_BITS,
u64::max_value() & MASK_36_BITS,
);
program
.iter()
.filter_map(|instruction| match instruction {
Chip2Instruction::Mask(and, or) => {
mask = (*and, *or);
None
}
Chip2Instruction::Mem(address, value) => Some(
mask.1
.iter()
.flat_map(|index| {
let address = u64::try_from(*address).expect("<=64 bit usize required");
iter::once((address | 1 << index | mask.0) & MASK_36_BITS).chain(
iter::once((address & !(1 << index) | mask.0) & MASK_36_BITS),
)
})
.map(|address| (address, *value))
.collect::<Vec<(u64, u64)>>(),
),
})
.flatten()
.collect::<Vec<(u64, u64)>>()
.into_iter()
.rev()
.unique_by(|address| address.0)
.collect()
} }
#[cfg(test)] #[cfg(test)]
@ -184,7 +135,7 @@ mod tests {
" "
); );
let program = parse_program_chip1(input)?; let program = parse_program(input)?;
let memory = run_program_chip1(&program); let memory = run_program_chip1(&program);
assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 165); assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 165);
@ -202,7 +153,7 @@ mod tests {
" "
); );
let program = parse_program_chip2(input)?; let program = parse_program(input)?;
let memory = dbg!(run_program_chip2(&program)); let memory = dbg!(run_program_chip2(&program));
assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 208); assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 208);