diff --git a/src/day14.rs b/src/day14.rs new file mode 100644 index 0000000..15163c2 --- /dev/null +++ b/src/day14.rs @@ -0,0 +1,149 @@ +use std::{collections::HashMap, iter::zip}; + +use aoc_runner_derive::{aoc, aoc_generator}; +use itertools::{Itertools, iproduct}; + +#[derive(Debug)] +enum ProgramStep { + Mask(String), + Mem(u64, u64) +} + +impl From<&str> for ProgramStep { + fn from(value: &str) -> Self { + let (name, value) = value.split(" = ").collect_tuple().unwrap(); + + return match name { + "mask" => Self::Mask(value.to_string()), + name if name.starts_with("mem") => { + Self::Mem(name[4..name.len() - 1].parse::().unwrap(), value.parse::().unwrap()) + } + _ => unreachable!() + }; + } +} + +#[aoc_generator(day14)] +fn parse(input: &str) -> Vec { + input.lines().map(ProgramStep::from).collect_vec() +} + +fn apply_mask(mask: &str, val: u64) -> u64 { + let mut val = val; + for (i, char) in mask.chars().enumerate() { + match char { + 'X' => continue, + '1' => val = val | (2_u64.pow((36 - i - 1).try_into().unwrap())), + '0' => val = val & !(2_u64.pow((36 - i - 1).try_into().unwrap())), + _ => unreachable!() + }; + } + + val +} + +#[aoc(day14, part1)] +fn part1(program: &Vec) -> u64 { + let mut mem: HashMap = HashMap::new(); + let mut mask = ""; + + for step in program { + match step { + ProgramStep::Mask(val) => mask = val, + ProgramStep::Mem(addr, val) => { + let val = apply_mask(mask, *val); + mem.insert(*addr, val); + }, + } + } + mem.values().sum() +} + +fn permute_masks(mask: &str) -> Vec { + let mut masks: Vec = vec![]; + let bits = mask.chars().enumerate().filter(|(_, v)| *v == 'X').map(|(i, _)| i).collect_vec(); + let perms = kproduct("21".to_string(), bits.len() as u32); + + let mut new_mask = mask.to_string(); + for perm in perms { + let bit_vals = zip(bits.clone(), perm.chars()).collect::>(); + for (k, val) in bit_vals { + new_mask = new_mask[0..k].to_string() + &val.to_string() + &new_mask[k+1..]; + } + masks.push(new_mask.clone()); + } + masks +} + +fn kproduct(seq: String, k: u32) -> Vec { + match k { + 0 => vec![], + 1 => seq.chars().map(|c| c.to_string()).collect(), + 2 => iproduct!(seq.chars(), seq.chars()).map(|(a, b)| format!("{}{}", a, b)).collect(), + _ => iproduct!(kproduct(seq.clone(), k - 1), seq.chars()).map(|(a, b)| format!("{}{}", a, b)).collect(), + } +} + +fn get_addrs(addr: u64, mask: &str) -> Vec { + let mut addrs = vec![]; + let masks = permute_masks(mask); + for mask in masks { + let mut addr = addr; + for (i, char) in mask.chars().enumerate() { + match char { + '1' => addr = addr | (2_u64.pow((36 - i - 1).try_into().unwrap())), + '2' => addr = addr & !(2_u64.pow((36 - i - 1).try_into().unwrap())), + '0' => continue, + _ => unreachable!() + } + } + addrs.push(addr) + } + addrs +} + +#[aoc(day14, part2)] +fn part2(program: &Vec) -> u64 { + let mut mem: HashMap = HashMap::new(); + let mut mask = ""; + + for step in program { + match step { + ProgramStep::Mask(val) => mask = val, + ProgramStep::Mem(addr, val) => { + let addrs = get_addrs(*addr, mask); + for addr in addrs { + mem.insert(addr, *val); + } + }, + } + } + + mem.values().sum() +} + + +#[cfg(test)] +mod tests { + use super::*; + +const EX: &str = r"mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X +mem[8] = 11 +mem[7] = 101 +mem[8] = 0"; + + #[test] + fn part1_example() { + assert_eq!(part1(&parse(EX)), 165); + } + +const EX_2: &str = r"mask = 000000000000000000000000000000X1001X +mem[42] = 100 +mask = 00000000000000000000000000000000X0XX +mem[26] = 1"; + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX_2)), 208); + } +} \ No newline at end of file diff --git a/src/day9.rs b/src/day9.rs index e6d5e81..66a1bb2 100644 --- a/src/day9.rs +++ b/src/day9.rs @@ -1,7 +1,7 @@ use aoc_runner_derive::{aoc, aoc_generator}; use itertools::Itertools; -const PREAMBLE_LEN: usize = 25; +const PREAMBLE_LEN: usize = 5; #[aoc_generator(day9)] fn parse(input: &str) -> Vec { diff --git a/src/lib.rs b/src/lib.rs index c08e87f..695ff83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod day14; mod day13; mod day12; mod day10;