diff --git a/day-14/src/main.rs b/day-14/src/main.rs index 0b4a865..80d7c46 100644 --- a/day-14/src/main.rs +++ b/day-14/src/main.rs @@ -1,5 +1,6 @@ -use std::collections::HashMap; +use std::convert::TryFrom; use std::fs; +use std::iter; use itertools::Itertools; use lazy_static::lazy_static; @@ -9,26 +10,22 @@ const MASK_36_BITS: u64 = 0xFFFFFFFFF; fn main() -> Result<(), Box<dyn std::error::Error>> { let input = fs::read_to_string("input")?; - let program = parse_program(&input)?; + let program = parse_program_chip1(&input)?; // Part 1 let memory = run_program_chip1(&program); 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(()) } #[derive(Copy, Clone, Debug)] -enum Instruction { - Mask(u64, u64, u64), - Mem(u64, u64), +enum Chip1Instruction { + Mask(u64, u64), + Mem(usize, u64), } -impl Instruction { +impl Chip1Instruction { fn new(instruction: &str) -> Result<Self, String> { lazy_static! { static ref INSTRUCTION_RE: Regex = @@ -47,7 +44,7 @@ impl Instruction { let or = u64::from_str_radix(&caps[3].replace("X", "0"), 2) .map_err(|e| format!("Could not parse or: {}", e))?; - Instruction::Mask(and, or, and ^ or) + Chip1Instruction::Mask(and, or) } "mem" => { let location = caps[2] @@ -57,28 +54,30 @@ impl Instruction { .parse() .map_err(|e| format!("Could not parse value: {}", e))?; - Instruction::Mem(location, value) + Chip1Instruction::Mem(location, value) } _ => Err("Invalid instruction")?, }) } } -fn parse_program(input: &str) -> Result<Vec<Instruction>, String> { - input.lines().map(Instruction::new).collect() +fn parse_program_chip1(input: &str) -> Result<Vec<Chip1Instruction>, String> { + input.lines().map(Chip1Instruction::new).collect() } -fn run_program_chip1(program: &Vec<Instruction>) -> Vec<(u64, u64)> { +fn run_program_chip1(program: &Vec<Chip1Instruction>) -> Vec<(u64, u64)> { let mut mask = (u64::max_value() & MASK_36_BITS, 0); program .iter() .filter_map(|instruction| match instruction { - Instruction::Mask(and, or, _) => { + Chip1Instruction::Mask(and, or) => { mask = (*and, *or); None } - Instruction::Mem(address, value) => Some((*address as u64, value & mask.0 | mask.1)), + Chip1Instruction::Mem(address, value) => { + Some((*address as u64, value & mask.0 | mask.1)) + } }) .collect::<Vec<(u64, u64)>>() .into_iter() @@ -87,36 +86,86 @@ fn run_program_chip1(program: &Vec<Instruction>) -> Vec<(u64, u64)> { .collect() } -fn run_program_chip2(program: &Vec<Instruction>) -> Vec<(u64, u64)> { - let mut memory = HashMap::new(); - let mut mask = (0, 0); +#[derive(Clone, Debug)] +enum Chip2Instruction { + Mask(u64, u64), + Mem(usize, u64), +} - for instruction in program { - match instruction { - Instruction::Mask(_, or, floating) => { - mask = (*or, *floating); - } - 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; - } - } +impl Chip2Instruction { + fn new(instruction: &str) -> Result<Self, String> { + lazy_static! { + static ref INSTRUCTION_RE: Regex = + Regex::new(r"([[:alpha:]]+?)\[?(\d+?)?\]? = ([0-9X]+)") + .expect("Regex should compile"); } - } - memory.drain().collect() + 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> { + 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)] @@ -135,7 +184,7 @@ mod tests { " ); - let program = parse_program(input)?; + let program = parse_program_chip1(input)?; let memory = run_program_chip1(&program); assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 165); @@ -153,7 +202,7 @@ mod tests { " ); - let program = parse_program(input)?; + let program = parse_program_chip2(input)?; let memory = dbg!(run_program_chip2(&program)); assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 208);