Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 8

day08.livemd

Day 8

Mix.install([
  {:kino, "~> 0.11.3"},
  {:math, "~> 0.7.0"}
])

Solutions

Note: Different sample input for Part 1 and Part 2.

input = Kino.Input.textarea("Please paste your input file:")
input2 = Kino.Input.textarea("Please paste your input file:")

Part 1

Premise: Desert map.

Sample data:

RL

AAA = (BBB, CCC)
BBB = (DDD, EEE)
CCC = (ZZZ, GGG)
DDD = (DDD, DDD)
EEE = (EEE, EEE)
GGG = (GGG, GGG)
ZZZ = (ZZZ, ZZZ)

Result: 2

Part 2

Premise: Desert ghost map.

Sample data:

LR

11A = (11B, XXX)
11B = (XXX, 11Z)
11Z = (11B, XXX)
22A = (22B, XXX)
22B = (22C, 22C)
22C = (22Z, 22Z)
22Z = (22B, 22B)
XXX = (XXX, XXX)

Result: 6

defmodule InputHelpers do
  def parse_input(input, trim \\ true) do
    input
    |> String.split("\n", trim: trim)
    |> Enum.map(&String.trim/1)
  end
end

defmodule RingList do
  def init(list) when is_list(list), do: {[], list}

  def next({[], []}), do: {[], []}

  def next({visited, []}) do
    [a | remain] = Enum.reverse(visited)
    {[a], remain}
  end

  def next({visited, [a | remain]}), do: {[a] ++ visited, remain}

  def value({[], []}, _), do: nil
  def value({visited, []}), do: List.last(visited)
  def value({_, remain}), do: hd(remain)
end
input = Kino.Input.read(input)
input2 = Kino.Input.read(input2)

import InputHelpers
import RingList
alias Math

defmodule Y2023.D8 do
  @moduledoc """
  https://adventofcode.com/2023/day/8
  """

  @start "AAA"
  @finish "ZZZ"

  def p1(input) do
    {map, route} =
      input
      |> parse_input()
      |> parse_map_and_route()

    travel(@start, map, route, 0)
  end

  def parse_map_and_route(input) do
    route = input |> hd() |> String.split("", trim: true) |> init()
    map = input |> tl() |> Enum.map(&parse_directions/1) |> Map.new()
    {map, route}
  end

  def parse_directions(line) do
    [_, source, left, right] = Regex.run(~r/^(\w+) = \((\w+), (\w+)\)/, line)

    {source, %{"L" => left, "R" => right}}
  end

  def travel(@finish, _map, _route, count), do: count

  def travel(location, map, route, count) do
    direction = value(route)
    next_location = map[location][direction]
    travel(next_location, map, next(route), count + 1)
  end

  def p2(input) do
    {map, route} =
      input
      |> parse_input()
      |> parse_map_and_route()

    map
    |> Map.keys()
    |> Enum.filter(&String.ends_with?(&1, "A"))
    |> Enum.map(fn start_location ->
      ghost_travel(start_location, map, route, 0)
    end)
    |> list_lcm()
  end

  def ghost_travel(location, map, route, count) do
    cond do
      String.ends_with?(location, "Z") ->
        count

      true ->
        direction = value(route)
        next_location = map[location][direction]
        ghost_travel(next_location, map, next(route), count + 1)
    end
  end

  def list_lcm([n]), do: n
  def list_lcm([n1, n2 | rest]), do: [Math.lcm(n1, n2) | rest] |> list_lcm()
end

Y2023.D8.p1(input) |> IO.puts()
Y2023.D8.p2(input2) |> IO.puts()

Observations