|
| 1 | +defmodule Day15 do |
| 2 | + def read_input(file_path) do |
| 3 | + case File.read(file_path) do |
| 4 | + {:ok, content} -> |
| 5 | + content |
| 6 | + |> String.split(",", trim: true) |
| 7 | + |
| 8 | + {:error, reason} -> |
| 9 | + raise "Oh no! #{reason}" |
| 10 | + end |
| 11 | + end |
| 12 | + |
| 13 | + def partA(file_path) do |
| 14 | + file_path |
| 15 | + |> read_input() |
| 16 | + |> Enum.map(&hash/1) |
| 17 | + |> Enum.sum() |
| 18 | + end |
| 19 | + |
| 20 | + def partB(file_path) do |
| 21 | + file_path |
| 22 | + |> read_input() |
| 23 | + |> Enum.reduce(Enum.take(Stream.cycle([[]]), 256), &apply_op(&2, extract_op(&1))) |
| 24 | + |> total_focusing_power() |
| 25 | + end |
| 26 | + |
| 27 | + def hash(l), do: hash(String.to_charlist(l), 0) |
| 28 | + def hash([], acc), do: acc |
| 29 | + def hash([h | t], acc), do: hash(t, Integer.mod((acc + h) * 17, 256)) |
| 30 | + |
| 31 | + def extract_op(str) do |
| 32 | + case Regex.named_captures(~r/(?<label>\w+)((=(?<number>\d+))|-)/, str) do |
| 33 | + %{"label" => label, "number" => number} when number != "" -> |
| 34 | + {label, {:add, String.to_integer(number)}} |
| 35 | + |
| 36 | + %{"label" => label} -> |
| 37 | + {label, {:remove}} |
| 38 | + end |
| 39 | + end |
| 40 | + |
| 41 | + def remove(box, label), do: Enum.reject(box, fn {l, _} -> l == label end) |
| 42 | + |
| 43 | + def add(box, label, value) do |
| 44 | + case Enum.find_index(box, fn {l, _} -> l == label end) do |
| 45 | + nil -> box ++ [{label, value}] |
| 46 | + idx -> List.replace_at(box, idx, {label, value}) |
| 47 | + end |
| 48 | + end |
| 49 | + |
| 50 | + def apply_op_to_box(box, label, {:add, value}), do: add(box, label, value) |
| 51 | + def apply_op_to_box(box, label, {:remove}), do: remove(box, label) |
| 52 | + |
| 53 | + def apply_op(l, {label, op}) do |
| 54 | + Enum.at(l, hash(label)) |
| 55 | + |> apply_op_to_box(label, op) |
| 56 | + |> (&List.replace_at(l, hash(label), &1)).() |
| 57 | + end |
| 58 | + |
| 59 | + def total_focusing_power(boxes) do |
| 60 | + Enum.with_index(boxes) |
| 61 | + |> Enum.flat_map(fn {box, box_index} -> |
| 62 | + Enum.with_index(box) |
| 63 | + |> Enum.map(fn {{_, focal_length}, lens_index} -> |
| 64 | + (box_index + 1) * (lens_index + 1) * focal_length |
| 65 | + end) |
| 66 | + end) |
| 67 | + |> Enum.sum() |
| 68 | + end |
| 69 | +end |
| 70 | + |
| 71 | +IO.puts(Day15.partA("./input")) |
| 72 | +IO.puts(Day15.partB("./input")) |
0 commit comments