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

--- Day 8: Haunted Wasteland ---

2023/day_8.livemd

— Day 8: Haunted Wasteland —

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

Inputs

input = Kino.Input.textarea("input")
test_input2 = Kino.Input.textarea("test_input2")
test_input3 = Kino.Input.textarea("test_input3")
directions_input = Kino.Input.textarea("directions")

Code

defmodule HauntedWasteland do
  def part_one({[first_step | indexes], nodes} = input, starting_node, count) do
    indexes
    |> Enum.reduce_while(
      {elem(nodes[starting_node], first_step), count},
      fn i, {node, count} ->
        nodes[node]
        |> elem(i)
        |> halt_or_cont(count)
      end
    )
    |> then(fn
      {"ZZZ", count} -> count
      {node, count} -> part_one(input, node, count + 1)
    end)
  end

  defp acc(simul_nodes, index, nodes), do: Enum.map(simul_nodes, &elem(nodes[&1], index))

  def part_two(starting_nodes, {[first | indexes], nodes} = input, count) do
    Enum.reduce_while(
      indexes,
      {acc(starting_nodes, first, nodes), count},
      fn i, {simul_nodes, count} ->
        next_nodes = acc(simul_nodes, i, nodes)

        if Enum.all?(next_nodes, &String.ends_with?(&1, "Z")) do
          IO.inspect("hit Z")
          {:halt, {next_nodes, count + 1}}
        else
          {:cont, {next_nodes, count + 1}}
        end
      end
    )
    |> then(fn {simul_nodes, count} ->
      if Enum.all?(simul_nodes, &String.ends_with?(&1, "Z")) do
        count
      else
        peek(simul_nodes, count)
        part_two(simul_nodes, input, count + 1)
      end
    end)
  end

  def peek(simul_nodes, count) do
    l =
      simul_nodes
      |> Enum.filter(&String.ends_with?(&1, "Z"))
      |> length()

    if l >= 4, do: IO.inspect(simul_nodes, label: "count: #{count + 1}")
  end

  # def peek(simul_nodes, count, i) do
  #   if String.ends_with?(Enum.at(simul_nodes, i), "Z") do
  #     IO.inspect(simul_nodes, label: "index #{i}, count: #{count + 1}")
  #   end
  # end

  def parse(input) do
    [lr | nodes] =
      input
      |> Kino.Input.read()
      |> String.split("\n", trim: true)

    {parse_lr(lr), parse_nodes(nodes)}
  end

  def parse_lr(lr) do
    lr
    |> String.graphemes()
    |> Enum.map(fn
      "L" -> 0
      "R" -> 1
    end)
  end

  def parse_nodes(nodes) do
    nodes
    |> Enum.map(&String.split(&1, [" = ", "(", ", ", ")"], trim: true))
    |> Enum.reduce(%{}, fn [key, l, r], acc -> Map.put(acc, key, {l, r}) end)
  end

  def starting_nodes(nodes) do
    nodes
    |> Map.keys()
    |> Enum.filter(&String.ends_with?(&1, "A"))
  end

  def halt_or_cont("ZZZ", count), do: {:halt, {"ZZZ", count + 1}}
  def halt_or_cont(next_node, count), do: {:cont, {next_node, count + 1}}

  def traverse_multiple(current_nodes, all_nodes, directions, step \\ 0) do
    IO.inspect(current_nodes)

    if Enum.all?(current_nodes, &String.ends_with?(&1, "Z")) do
      step
    else
      nodes =
        for key <- current_nodes do
          {left, right} = all_nodes[key]

          if Enum.at(directions, rem(step, length(directions))) == 1,
            do: elem(all_nodes[right], 1),
            else: elem(all_nodes[left], 0)
        end

      traverse_multiple(nodes, all_nodes, directions, step + 1)
    end
  end
end
import HauntedWasteland

{lr, nodes} = input = parse(directions_input)

nodes
|> starting_nodes()
|> part_two(input, 1)