day15: problem 1 & 2 solutions
This commit is contained in:
parent
24c534ff88
commit
eac460417c
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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user