day15: problem 1 & 2 solutions
This commit is contained in:
		
							
								
								
									
										7
									
								
								15/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								15/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					# This file is automatically @generated by Cargo.
 | 
				
			||||||
 | 
					# It is not intended for manual editing.
 | 
				
			||||||
 | 
					version = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "day15"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
							
								
								
									
										8
									
								
								15/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								15/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "day15"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
							
								
								
									
										165
									
								
								15/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								15/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
				
			|||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use std::fs::File;
 | 
				
			||||||
 | 
					use std::io::{BufRead, BufReader, Lines};
 | 
				
			||||||
 | 
					use std::time::{Duration, Instant};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BOILERPLATE
 | 
				
			||||||
 | 
					type InputIter = Lines<BufReader<File>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 {
 | 
				
			||||||
 | 
					    let mut val = 0u64;
 | 
				
			||||||
 | 
					    for c in s.bytes() {
 | 
				
			||||||
 | 
					        val += c as u64;
 | 
				
			||||||
 | 
					        val *= 17;
 | 
				
			||||||
 | 
					        val %= 256;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    val
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PROBLEM 1 solution
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn problem1<T: BufRead>(mut input: Lines<T>) -> 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<String, u8>,
 | 
				
			||||||
 | 
					    // order is the lens stack, values are labels into lenses
 | 
				
			||||||
 | 
					    order: Vec<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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<T: BufRead>(mut input: Lines<T>) -> 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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user