use std::collections::HashMap; use std::fs::File; use std::io::{BufRead, BufReader, Lines}; use std::time::{Duration, Instant}; // BOILERPLATE type InputIter = Lines>; fn get_input() -> InputIter { let f = File::open("input").unwrap(); let br = BufReader::new(f); br.lines() } fn main() { let start = Instant::now(); let ans1 = problem1(get_input()); let duration = start.elapsed(); println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64()); let start = Instant::now(); let ans2 = problem2(get_input()); let duration = start.elapsed(); println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64()); } // COMMON fn hash_string(s: &str) -> u64 { s.bytes().fold(0u64, |mut accum, val| { accum += val as u64; accum *= 17; accum %= 256; accum }) } // PROBLEM 1 solution fn problem1(mut input: Lines) -> u64 { let line = input.next().unwrap().unwrap(); line.split(',').map(|s| hash_string(s)).sum() } // PROBLEM 2 solution #[derive(Clone, Default)] struct LensBox { // lenses k: lens label v: focal length lenses: HashMap, // order is the lens stack, values are labels into lenses order: Vec, } impl LensBox { fn add_lens(&mut self, label: &str, focal_length: u8) { // If there is not already a lens in the box with the same label, add // the lens to the box immediately behind any lenses already in the box. // Don't move any of the other lenses when you do this. If there aren't // any lenses in the box, the new lens goes all the way to the front of // the box. if self.lenses.insert(label.to_owned(), focal_length).is_none() { self.order.push(label.to_owned()); } } //If the operation character is a dash (-), go to the relevant box and //remove the lens with the given label if it is present in the box. Then, //move any remaining lenses as far forward in the box as they can go without //changing their order, filling any space made by removing the indicated //lens. (If no lens in that box has the given label, nothing happens.) fn remove_lens_if_present(&mut self, label: &str) { if let Some(_) = self.lenses.remove(label) { self.order.remove( self.order .iter() .position(|s| s == label) .expect("We had the lens in lenses but not in order?!"), ); } } fn total_lens_power(&self) -> u64 { self.order .iter() .enumerate() .map(|(lens_idx, label)| (lens_idx + 1) as u64 * self.lenses[label] as u64) .sum() } } enum Instruction { Replace(String, u8), Remove(String), } impl From<&str> for Instruction { fn from(s: &str) -> Self { let op_pos = s.find(|c| c == '-' || c == '=').expect("No operation?!"); let op = s.chars().nth(op_pos).unwrap(); let (label, op_focal) = s.split_once(|c| c == '-' || c == '=').unwrap(); match op { '-' => Self::Remove(label.to_owned()), '=' => Self::Replace(label.to_owned(), op_focal.parse().unwrap()), _ => panic!("unexpected op {}", op), } } } impl Instruction { fn execute(&self, boxes: &mut [LensBox; 256]) { match self { Self::Remove(label) => boxes[hash_string(label) as usize].remove_lens_if_present(label), Self::Replace(label, focal) => { boxes[hash_string(label) as usize].add_lens(label, *focal) } } } } fn print_boxes(boxes: &[LensBox; 256]) { for (idx, b) in boxes.iter().enumerate() { if !b.order.is_empty() { print!("Box {}:", idx); for label in &b.order { print!(" [{} {}]", label, b.lenses[label]); } println!(); } } } fn problem2(mut input: Lines) -> u64 { let line = input.next().unwrap().unwrap(); let instructions: Vec<_> = line.split(',').map(|s| Instruction::from(s)).collect(); let mut boxes: [LensBox; 256] = std::array::from_fn(|_| LensBox::default()); for inst in instructions { inst.execute(&mut boxes); } boxes .iter() .enumerate() .map(|(box_idx, b)| (box_idx + 1) as u64 * b.total_lens_power()) .sum() } #[cfg(test)] mod tests { use crate::*; use std::io::Cursor; const EXAMPLE: &str = &"rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7"; #[test] fn problem1_test_hash() { assert_eq!(hash_string(&"HASH"), 52); } #[test] fn problem1_example() { let c = Cursor::new(EXAMPLE); assert_eq!(problem1(c.lines()), 1320); } #[test] fn problem2_example() { let c = Cursor::new(EXAMPLE); assert_eq!(problem2(c.lines()), 145); } }