use itertools::Itertools; pub fn run() { let input = include_str!("../input/day15.txt"); dbg!(sum_hashes(input)); let ops = parse_ops(input); let mut boxes: Vec> = vec![vec![]; 256]; run_ops(ops, &mut boxes); dbg!(focusing_power(&boxes)); } fn sum_hashes(s: &str) -> usize { s.trim().split(",").map(hash).sum() } fn hash(s: &str) -> usize { assert!(s.is_ascii()); let mut res = 0; for c in s.chars() { let cur = c as usize; res += cur; res *= 17; res %= 256; } res } fn parse_ops(s: &str) -> Vec { let mut v = vec![]; let mut cs = s.trim().chars().peekable(); while cs.peek().is_some() { let label = cs .take_while_ref(|&c| c != '-' && c != '=') .collect::(); let ty = match cs.next() { Some('-') => OpType::Dash, Some('=') => { let focal_length = cs .take_while_ref(|c| c.is_ascii_digit()) .collect::() .parse::() .unwrap(); OpType::Equals(focal_length) } x => panic!("unexpected char {:?}", x), }; v.push(Op { label, ty }); if cs.peek().is_some() { assert_eq!(cs.next(), Some(',')); } } v } #[derive(Debug, Clone, PartialEq, Eq)] struct Op { label: String, ty: OpType, } impl Op { fn hash(&self) -> usize { hash(&self.label) } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum OpType { Dash, Equals(u8), } #[derive(Debug, Clone, PartialEq, Eq)] struct Lens { label: String, focal_length: u8, } fn run_ops(ops: Vec, boxes: &mut Vec>) { for op in ops.into_iter() { let hash = op.hash(); let existing = boxes[hash] .iter() .find_position(|lens| lens.label == op.label); match op.ty { OpType::Dash => { if let Some((idx, _)) = existing { boxes[hash].remove(idx); } } OpType::Equals(focal_length) => { let new_lens = Lens { label: op.label, focal_length, }; if let Some((idx, _)) = existing { boxes[hash][idx] = new_lens; } else { boxes[hash].push(new_lens); } } } } } fn focusing_power(boxes: &[Vec]) -> usize { let mut res = 0; for (i, lenses) in boxes.iter().enumerate() { for (j, lens) in lenses.iter().enumerate() { res += (i + 1) * (j + 1) * lens.focal_length as usize; } } res } #[cfg(test)] mod tests { use std::io::BufRead; use super::*; #[test] fn test_hash() { assert_eq!(hash("HASH"), 52); } #[test] fn test_sum_hashes() { assert_eq!( sum_hashes("rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7"), 1320 ); } #[test] fn test_parse_ops() { assert_eq!( parse_ops("rn=1,cm-"), vec![ Op { label: "rn".into(), ty: OpType::Equals(1) }, Op { label: "cm".into(), ty: OpType::Dash }, ] ); } #[test] fn test_run_ops() { let ops = parse_ops("rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7"); let mut boxes: Vec> = vec![vec![]; 256]; run_ops(ops, &mut boxes); assert_eq!( boxes[0], vec![ Lens { label: "rn".into(), focal_length: 1 }, Lens { label: "cm".into(), focal_length: 2 }, ] ); assert_eq!( boxes[3], vec![ Lens { label: "ot".into(), focal_length: 7 }, Lens { label: "ab".into(), focal_length: 5 }, Lens { label: "pc".into(), focal_length: 6 }, ] ); for i in 0..256 { if i == 0 || i == 3 { continue; } assert!(boxes[i].is_empty()); } } #[test] fn test_focusing_power() { let ops = parse_ops("rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7"); let mut boxes: Vec> = vec![vec![]; 256]; run_ops(ops, &mut boxes); assert_eq!(focusing_power(&boxes), 145); } }