AOC 23 - Day 15 - Lens Library

Link to Advent of Code day 15

Today was a easy day, just what I needed to catch up. The problem itself is about hashes, which is very easy with Python.

Part 1

Rules

For the part 1, we have to iter on the input sequence, and get the sum of each of their hashes. Each step of the sequence is separated by a coma.

The hash is calculated by these steps:

  1. Determine the ASCII code for the current character of the string.
  2. Increase the current value by the ASCII code you just determined.
  3. Set the current value to itself multiplied by 17.
  4. Set the current value to the remainder of dividing itself by 256

For example with

rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
rn=1 becomes 30.
cm- becomes 253.
qp=3 becomes 97.
cm=2 becomes 47.
qp- becomes 14.
pc=4 becomes 180.
ot=9 becomes 9.
ab=5 becomes 197.
pc- becomes 48.
pc=6 becomes 214.
ot=7 becomes 231.

For a total of 1320.

Code

Let's start with the parsing:

def get_steps(input_file: str) -> list[str]:
    with open(input_file) as f:
        return f.read().strip().split(",")

Then the logic is easy and self-explanatory enough:

def part_one(input_file: str):
    steps = get_steps(input_file)
    return sum(get_string_hash(step) for step in steps)


def get_string_hash(step: str) -> int:
    current_value = 0
    for c in step:
        current_value += ord(c)
        current_value *= 17
        current_value %= 256
    return current_value

And part 1 is done !

Part 2

Rules

Part 2 is a bit longer to explain, but not that complicated.

We now have a box of 256 empty boxes. Each instruction can be one of 2 types.

If the instruction has = in it, we will update or create a box. For this, we will do the following:

  1. split the string in 2. rn=1 would become rn (the label) and 1 (the focal length)
  2. get the hash of the label. Forrn it is 0.
  3. look in the box having an id matching the computed hash.
  4. if this box do not contains a lens with this label, create a lens with the label and the focal length.
  5. if this box contains a lens with this label, just update the focal length.

If the instruction ends with -, the steps are:

  1. remove the trailing - to get the label
  2. get the hash of the label.
  3. look in the box having an id matching the computed hash.
  4. if the box contains a lens with the same label, delete this box
  5. otherwise do nothing

We then have to return the sum of the focusing power. The focusing power of each step is the multiplication of the following values:

  • One plus the box number of the lens in question.
  • The slot number of the lens within the box: 1 for the first lens, 2 for the second lens, and so on.
  • The focal length of the lens.

Code

Nothing difficult here. I chose to use a dataclass to represent the len:

from dataclasses import dataclass


@dataclass
class Lens:
    label: str
    focal: int

We then have to write the functions for the 2 instructions.


def remove_lens(boxes: list[list[Lens]], label: str):
    box_idx = get_string_hash(label)
    boxes[box_idx] = [lens for lens in boxes[box_idx] if lens.label != label]


def update_or_create_lens(boxes: list[list[Lens]], label: str, focal_length: int):
    box_idx = get_string_hash(label)
    for lens in boxes[box_idx]:
        if lens.label == label:
            lens.focal = focal_length
            return

    boxes[box_idx].append(Lens(label, focal_length))

And then plug them into the main function, calculating the focusing power at the end

def part_two(input_file: str):
    steps = get_steps(input_file)
    boxes: list[list[Lens]] = [[] for _ in range(256)]

    for step in steps:
        if step.endswith("-"):
            remove_lens(boxes, step[:-1])
        else:
            label, focal_length = step.split("=")
            update_or_create_lens(boxes, label, int(focal_length))

    total = 0
    for box_nbr, box in enumerate(boxes):
        for idx, lens in enumerate(box):
            total += (box_nbr + 1) * (idx + 1) * lens.focal
    return total

And that's it for day 15.


All the code is available on github