Advent of Code solutions in Rust

feat: day 14 of AoC 2020

+348 -2
+346
aoc_2020/src/day14.rs
··· 1 + use std::{collections::HashMap, fmt::Write}; 2 + 3 + use anyhow::Context as _; 4 + use aoc_companion::prelude::*; 5 + use itertools::Itertools as _; 6 + 7 + pub(crate) struct Door { 8 + program: Vec<Instruction>, 9 + } 10 + 11 + impl<'input> Solution<'input> for Door { 12 + fn parse(input: &'input str) -> Result<Door> { 13 + Ok(Door { 14 + program: input.lines().map(|instr| instr.parse()).try_collect()?, 15 + }) 16 + } 17 + 18 + fn part1(&self) -> u64 { 19 + total_memory(generate_writes::<DecoderV1>(self.program.iter().copied())) 20 + } 21 + 22 + fn part2(&self) -> u64 { 23 + total_memory(generate_writes::<DecoderV2>(self.program.iter().copied())) 24 + } 25 + } 26 + 27 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 28 + enum Instruction { 29 + SetMask(Mask), 30 + Write(MemWrite), 31 + } 32 + 33 + impl std::str::FromStr for Instruction { 34 + type Err = anyhow::Error; 35 + 36 + fn from_str(s: &str) -> Result<Self> { 37 + let Some((lhs, rhs)) = s.split_once('=') else { 38 + anyhow::bail!("instruction does not contain assignment operator '='"); 39 + }; 40 + let lhs = lhs.trim_end(); 41 + let rhs = rhs.trim_start(); 42 + let (introducer, rest) = lhs.split_at_checked(4).unwrap_or((lhs, "")); 43 + 44 + Ok(match introducer { 45 + "mask" => Instruction::SetMask(rhs.parse()?), 46 + "mem[" => Instruction::Write(MemWrite { 47 + addr: rest 48 + .strip_suffix(']') 49 + .with_context(|| "missing closing bracket")? 50 + .parse() 51 + .with_context(|| "invalid memory address")?, 52 + value: rhs.parse().with_context(|| "invalid value to write")?, 53 + }), 54 + _ => anyhow::bail!("illegal instruction, introduced by {introducer:?}"), 55 + }) 56 + } 57 + } 58 + 59 + #[derive(Clone, Copy, PartialEq, Eq)] 60 + struct Mask([Option<bool>; 36]); 61 + 62 + impl std::str::FromStr for Mask { 63 + type Err = anyhow::Error; 64 + 65 + fn from_str(s: &str) -> Result<Self> { 66 + // FIXME: This should use `try_from_iter_exact()` 67 + aoc_utils::array::try_from_iter(s.chars().rev().map(|c| match c { 68 + 'X' => Ok(None), 69 + '0' => Ok(Some(false)), 70 + '1' => Ok(Some(true)), 71 + _ => Err(anyhow::anyhow!("invalid mask character: {c:?}")), 72 + }))? 73 + .map_err(|v| anyhow::anyhow!("expected mask of length 36, got length {}", v.len())) 74 + .map(Mask) 75 + } 76 + } 77 + 78 + impl Default for Mask { 79 + fn default() -> Self { 80 + Self([None; 36]) 81 + } 82 + } 83 + 84 + impl std::fmt::Debug for Mask { 85 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 86 + struct MaskInner<'a>(&'a [Option<bool>]); 87 + 88 + impl std::fmt::Debug for MaskInner<'_> { 89 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 90 + for b in self.0.iter().rev() { 91 + f.write_char(match b { 92 + None => 'X', 93 + Some(false) => '0', 94 + Some(true) => '1', 95 + })?; 96 + } 97 + Ok(()) 98 + } 99 + } 100 + 101 + f.debug_tuple("Mask").field(&MaskInner(&self.0)).finish() 102 + } 103 + } 104 + 105 + impl Mask { 106 + fn bits(&self) -> impl Iterator<Item = (Option<bool>, u64)> { 107 + self.0.iter().copied().enumerate().map(|(i, b)| (b, 1 << i)) 108 + } 109 + } 110 + 111 + trait ApplyMask { 112 + fn apply(mask: Mask, mem_write: MemWrite) -> impl Iterator<Item = MemWrite>; 113 + } 114 + 115 + struct DecoderV1; 116 + 117 + impl ApplyMask for DecoderV1 { 118 + fn apply(mask: Mask, MemWrite { addr, value }: MemWrite) -> impl Iterator<Item = MemWrite> { 119 + std::iter::once_with(move || MemWrite { 120 + addr, 121 + value: mask 122 + .bits() 123 + .filter_map(|(b, i)| Some((b?, i))) 124 + .fold(value, |v, (b, i)| v ^ (v & i) ^ ((b as u64) * i)), 125 + }) 126 + } 127 + } 128 + 129 + struct DecoderV2; 130 + 131 + impl ApplyMask for DecoderV2 { 132 + fn apply(mask: Mask, MemWrite { addr, value }: MemWrite) -> impl Iterator<Item = MemWrite> { 133 + let addr = mask 134 + .bits() 135 + .filter_map(|(b, i)| (b == Some(true)).then_some(i)) 136 + .fold(addr, std::ops::BitOr::bitor); 137 + 138 + let floating_bits = mask.bits().filter_map(|(b, i)| b.is_none().then_some(i)); 139 + 140 + floating_bits 141 + .into_iter() 142 + .map(|i| [0, i]) 143 + .multi_cartesian_product() 144 + .map(move |bits| bits.iter().fold(addr, |a, b| a ^ b)) 145 + .map(move |addr| MemWrite { addr, value }) 146 + } 147 + } 148 + 149 + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 150 + struct MemWrite { 151 + addr: u64, 152 + value: u64, 153 + } 154 + 155 + fn generate_writes<D: ApplyMask>( 156 + program: impl IntoIterator<Item = Instruction>, 157 + ) -> impl Iterator<Item = MemWrite> { 158 + program 159 + .into_iter() 160 + .scan(Mask::default(), move |mask, instr| match instr { 161 + Instruction::SetMask(new_mask) => { 162 + *mask = new_mask; 163 + Some(None) 164 + } 165 + Instruction::Write(w) => Some(Some(D::apply(*mask, w))), 166 + }) 167 + .flatten() 168 + .flatten() 169 + } 170 + 171 + fn total_memory(mem_writes: impl IntoIterator<Item = MemWrite>) -> u64 { 172 + let memory: HashMap<u64, u64> = mem_writes 173 + .into_iter() 174 + .map(|MemWrite { addr, value }| (addr, value)) 175 + .collect(); 176 + memory.into_values().sum() 177 + } 178 + 179 + #[cfg(test)] 180 + mod tests { 181 + use std::collections::HashSet; 182 + 183 + use super::*; 184 + 185 + const EXAMPLE_INPUT: &str = "mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X 186 + mem[8] = 11 187 + mem[7] = 101 188 + mem[8] = 0"; 189 + 190 + const EXAMPLE_MASK: Mask = Mask([ 191 + None, 192 + Some(false), 193 + None, 194 + None, 195 + None, 196 + None, 197 + Some(true), 198 + None, 199 + None, 200 + None, 201 + None, 202 + None, 203 + None, 204 + None, 205 + None, 206 + None, 207 + None, 208 + None, 209 + None, 210 + None, 211 + None, 212 + None, 213 + None, 214 + None, 215 + None, 216 + None, 217 + None, 218 + None, 219 + None, 220 + None, 221 + None, 222 + None, 223 + None, 224 + None, 225 + None, 226 + None, 227 + ]); 228 + 229 + const EXAMPLE_PROGRAM: &[Instruction] = &[ 230 + Instruction::SetMask(EXAMPLE_MASK), 231 + Instruction::Write(MemWrite { addr: 8, value: 11 }), 232 + Instruction::Write(MemWrite { 233 + addr: 7, 234 + value: 101, 235 + }), 236 + Instruction::Write(MemWrite { addr: 8, value: 0 }), 237 + ]; 238 + 239 + const EXAMPLE_MEM_WRITES_V1: &[MemWrite] = &[ 240 + MemWrite { addr: 8, value: 73 }, 241 + MemWrite { 242 + addr: 7, 243 + value: 101, 244 + }, 245 + MemWrite { addr: 8, value: 64 }, 246 + ]; 247 + 248 + #[test] 249 + fn mask_is_parsed_and_printed_consistently() { 250 + const EXAMPLE_MASK_STR: &str = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X"; 251 + assert_eq!( 252 + format!("{:?}", EXAMPLE_MASK_STR.parse::<Mask>().unwrap()), 253 + format!("Mask({EXAMPLE_MASK_STR})") 254 + ); 255 + } 256 + 257 + #[test] 258 + fn parse_example_program() { 259 + itertools::assert_equal( 260 + Door::parse(EXAMPLE_INPUT).unwrap().program.iter(), 261 + EXAMPLE_PROGRAM, 262 + ); 263 + } 264 + 265 + #[test] 266 + fn apply_mask_v1() { 267 + itertools::assert_equal( 268 + DecoderV1::apply(EXAMPLE_MASK, MemWrite { addr: 8, value: 11 }), 269 + [MemWrite { addr: 8, value: 73 }], 270 + ); 271 + itertools::assert_equal( 272 + DecoderV1::apply( 273 + EXAMPLE_MASK, 274 + MemWrite { 275 + addr: 7, 276 + value: 101, 277 + }, 278 + ), 279 + [MemWrite { 280 + addr: 7, 281 + value: 101, 282 + }], 283 + ); 284 + itertools::assert_equal( 285 + DecoderV1::apply(EXAMPLE_MASK, MemWrite { addr: 8, value: 0 }), 286 + [MemWrite { addr: 8, value: 64 }], 287 + ); 288 + } 289 + 290 + #[test] 291 + fn generate_example_writes_v1() { 292 + itertools::equal( 293 + generate_writes::<DecoderV1>(EXAMPLE_PROGRAM.iter().copied()), 294 + EXAMPLE_MEM_WRITES_V1.iter().copied(), 295 + ); 296 + } 297 + 298 + #[test] 299 + fn find_total_memory_for_example_v1() { 300 + assert_eq!(total_memory(EXAMPLE_MEM_WRITES_V1.iter().copied()), 165); 301 + } 302 + 303 + #[test] 304 + fn apply_mask_v2() { 305 + let first_mask: Mask = "000000000000000000000000000000X1001X".parse().unwrap(); 306 + assert_eq!( 307 + HashSet::from_iter(DecoderV2::apply( 308 + first_mask, 309 + MemWrite { 310 + addr: 42, 311 + value: 100, 312 + }, 313 + )), 314 + HashSet::from([ 315 + MemWrite { 316 + addr: 26, 317 + value: 100, 318 + }, 319 + MemWrite { 320 + addr: 27, 321 + value: 100, 322 + }, 323 + MemWrite { 324 + addr: 58, 325 + value: 100, 326 + }, 327 + MemWrite { 328 + addr: 59, 329 + value: 100, 330 + }, 331 + ]), 332 + ); 333 + } 334 + 335 + #[test] 336 + fn find_total_memory_for_example_v2() { 337 + let Door { program } = Door::parse( 338 + "mask = 000000000000000000000000000000X1001X 339 + mem[42] = 100 340 + mask = 00000000000000000000000000000000X0XX 341 + mem[26] = 1", 342 + ) 343 + .unwrap(); 344 + assert_eq!(total_memory(generate_writes::<DecoderV2>(program)), 208); 345 + } 346 + }
+2 -2
aoc_2020/src/main.rs
··· 13 13 mod day11; 14 14 mod day12; 15 15 mod day13; 16 - // mod day14; 16 + mod day14; 17 17 // mod day15; 18 18 // mod day16; 19 19 // mod day17; ··· 44 44 door!(2020-12-11 ~> day11), 45 45 door!(2020-12-12 ~> day12), 46 46 door!(2020-12-13 ~> day13), 47 - // door!(2020-12-14 ~> day14), 47 + door!(2020-12-14 ~> day14), 48 48 // door!(2020-12-15 ~> day15), 49 49 // door!(2020-12-16 ~> day16), 50 50 // door!(2020-12-17 ~> day17),