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

Day 21

2022/elixir/day21.livemd

Day 21

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

Puzzle Input

area = Kino.Input.textarea("Puzzle Input")
puzzle_input = Kino.Input.read(area)
example_input = """
root: pppw + sjmn
dbpl: 5
cczh: sllz + lgvd
zczc: 2
ptdq: humn - dvpt
dvpt: 3
lfqf: 4
humn: 5
ljgn: 2
sjmn: drzm * dbpl
sllz: 4
pppw: cczh / lfqf
lgvd: ljgn * ptdq
drzm: hmdt - zczc
hmdt: 32
"""
input = puzzle_input

Common

expressions =
  input
  |> String.split("\n", trim: true)
  |> Enum.map(fn line ->
    [variable, statement] = String.split(line, ": ", parts: 2)

    statement =
      case String.split(statement, " ") do
        [lhs, operator, rhs] -> {:operator, {operator, lhs, rhs}}
        [value] -> {:value, value |> Integer.parse() |> elem(0)}
      end

    {variable, statement}
  end)
  |> Map.new()
defmodule Runtime do
  def calculate(expressions, variable \\ "root") do
    case expressions[variable] do
      {:operator, {operator, lhs, rhs}} ->
        lhs = calculate(expressions, lhs)
        rhs = calculate(expressions, rhs)

        case operator do
          "+" -> lhs + rhs
          "-" -> lhs - rhs
          "/" -> lhs / rhs
          "*" -> lhs * rhs
        end

      {:value, value} ->
        value
    end
  end

  def derive(expressions, variable \\ "humn") do
    {:operator, {_, lhs, rhs}} = expressions["root"]

    lhs_dependent? = variable in dependencies(expressions, lhs)
    dependent_branch = if lhs_dependent?, do: lhs, else: rhs

    value =
      if lhs_dependent? do
        calculate(expressions, rhs)
      else
        calculate(expressions, lhs)
      end

    derive(expressions, value, dependent_branch, variable)
  end

  def derive(_expressions, value, variable, variable) do
    value
  end

  def derive(expressions, value, current_variable, variable) do
    {:operator, {operator, lhs, rhs}} = expressions[current_variable]

    lhs_dependent? = variable in dependencies(expressions, lhs) || lhs == variable
    dependent_branch = if lhs_dependent?, do: lhs, else: rhs
    independent_branch = if lhs_dependent?, do: rhs, else: lhs
    independent_value = calculate(expressions, independent_branch)

    value_to_derive =
      case operator do
        "+" ->
          # x + independent_value = value
          # independent_value + x = value
          value - independent_value

        "-" ->
          if lhs_dependent? do
            # x - independent_value = value
            value + independent_value
          else
            # independent_value - x = value
            independent_value - value
          end

        "/" ->
          if lhs_dependent? do
            # x / independent_value = value
            value * independent_value
          else
            # independent_value / x = value
            independent_value / value
          end

        "*" ->
          # x * independent_value = value
          # independent_value * x = value
          value / independent_value
      end

    derive(expressions, value_to_derive, dependent_branch, variable)
  end

  def dependencies(expressions, variable, deps \\ MapSet.new()) do
    case expressions[variable] do
      {:operator, {_operator, lhs, rhs}} ->
        deps
        |> MapSet.put(lhs)
        |> MapSet.put(rhs)
        |> MapSet.union(dependencies(expressions, lhs))
        |> MapSet.union(dependencies(expressions, rhs))

      {:value, _} ->
        deps
    end
  end
end

Part One

Runtime.calculate(expressions)

Part Two

Runtime.derive(expressions)