This commit is contained in:
Shadowfacts 2023-12-15 11:31:19 -05:00
parent 1ac02b2ac7
commit d9435c84ff
3 changed files with 205 additions and 1 deletions

1
input/day15.txt Normal file

File diff suppressed because one or more lines are too long

202
src/day15.rs Normal file
View File

@ -0,0 +1,202 @@
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<Lens>> = 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<Op> {
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::<String>();
let ty = match cs.next() {
Some('-') => OpType::Dash,
Some('=') => {
let focal_length = cs
.take_while_ref(|c| c.is_ascii_digit())
.collect::<String>()
.parse::<u8>()
.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<Op>, boxes: &mut Vec<Vec<Lens>>) {
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<Lens>]) -> 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<Lens>> = 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<Lens>> = vec![vec![]; 256];
run_ops(ops, &mut boxes);
assert_eq!(focusing_power(&boxes), 145);
}
}

View File

@ -15,7 +15,8 @@ mod day11;
mod day12;
mod day13;
mod day14;
mod day15;
fn main() {
day14::run();
day15::run();
}