euler_ex/problems/18/solution.livemd

1.8 KiB

Project Euler 18 and 67

Section

defmodule PathSum do
  defstruct data: %{}, depth: 0
  def parse(input) do
    data = input
    |> String.trim()
    |> String.split("\n")
    |> Stream.with_index()
    |> Stream.flat_map(fn {line, j} -> 
      line
      |> String.split(" ")
      |> Stream.map(&String.to_integer/1)
      |> Stream.with_index()
      |> Stream.map(fn {n, i} -> {{j, i}, n} end)
      end)
    |> Enum.into(%{})

    %__MODULE__{
      data: data,
      depth: data |> Enum.max_by(fn {{x, _}, _} -> x end) |> elem(0) |> elem(0)
    }
  end

  defmacro with_val(coord, tree) do 
    quote do
      { unquote(coord), Map.get(unquote(tree).data, unquote(coord)) }
    end
  end

  def above({0, 0}, _), do: nil
  def above({1, _}, tree), do: {0, 0} |> with_val(tree)
  def above({n, 0}, tree), do: {n - 1, 0} |> with_val(tree)
  def above({n, n}, tree), do: {n - 1, n - 1} |> with_val(tree)
  def above({n, m}, tree), do: [{n - 1, m - 1} |> with_val(tree), {n - 1, m} |> with_val(tree)]
  
  def best_path_to(tree, loc) do
    case Process.get({tree, loc}) do
      nil -> 
        best = case above(loc, tree) do
          nil -> 0
          {c, n} -> n + best_path_to(tree, c)
          [{lloc, left}, {rloc, right}] ->
            ll = left + best_path_to(tree, lloc)
            rr = right + best_path_to(tree, rloc)
            if ll > rr do
              ll
            else
              rr
            end
        end
        Process.put({tree, loc}, best)
        best
      n -> n
    end   
  end

  @doc """
  
  ## Example
  iex> """
  ...> 3
  ...> 7 4
  ...> 2 4 6
  ...> 8 5 9 3
  ...> """ |>
  ...> PathSum.parse() |> PathSum.best_path()
  23
  """
  def best_path(tree) do
    for {{x, _} = coord, n} <- tree.data,
      x == tree.depth,
      reduce: 0 do
      best -> 
        max(best, n + best_path_to(tree, coord))
    end
  end
end