summaryrefslogtreecommitdiffstats
path: root/src/day_15.rs
blob: c2b780652bd6bd65c897121a94c4920cc909fe53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use std::collections::HashMap;

use anyhow::Context;

use crate::{Problem, Solution};

pub struct Day15;

impl Problem for Day15 {
    const DAY: u8 = 15;

    const INPUT: &'static str = include_str!("../input/day_15.txt");
}

impl Solution for Day15 {
    type Answer1 = usize;

    type Answer2 = usize;

    fn part_1(input: &str) -> anyhow::Result<Self::Answer1> {
        Ok(input.trim().split(',').map(hash).sum())
        // 509784 ==
    }

    fn part_2(input: &str) -> anyhow::Result<Self::Answer2> {
        Ok(input
            .trim()
            .split(',')
            .try_fold(Boxes::new(), process_step)?
            .into_iter()
            .map(calculate_power)
            .sum())

        // 339464 >
        // 230197 ==
    }
}

type Lenses = Vec<(String, usize)>;
type Boxes = HashMap<usize, Lenses>;

fn hash(s: &str) -> usize {
    s.trim()
        .chars()
        .fold(0, |acc, c| (acc + c as usize) * 17 % 256)
}

fn process_step(mut acc: Boxes, s: &str) -> anyhow::Result<Boxes> {
    let (label, power) = s.split_once(['-', '=']).context("Failed to parse input")?;

    let key = hash(label);
    let lenses = acc.entry(key).or_default();

    match lenses.iter().position(|(s, _)| s == label) {
        pos if !power.is_empty() => {
            lenses.push((label.to_owned(), power.parse()?));
            if let Some(index) = pos {
                lenses.swap_remove(index);
            }
        }
        Some(index) => {
            lenses.remove(index);
            if lenses.is_empty() {
                acc.remove(&key);
            }
        }
        _ => {}
    };

    Ok(acc)
}

fn calculate_power((id, lenses): (usize, Lenses)) -> usize {
    lenses
        .into_iter()
        .enumerate()
        .map(|(i, (_, len))| (id + 1) * (i + 1) * len)
        .sum::<usize>()
}

#[cfg(test)]
mod tests {
    use super::*;

    const INPUT: &str = "rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7";

    #[test]
    fn test_part_1() -> anyhow::Result<()> {
        Ok(assert_eq!(1320, Day15::part_1(INPUT)?))
    }

    #[test]
    fn test_part_2() -> anyhow::Result<()> {
        Ok(assert_eq!(145, Day15::part_2(INPUT)?))
    }
}