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

Day10

2024/elixir/day10.livemd

Day10

Mix.install([
  {:kino_aoc, git: "https://github.com/ljgago/kino_aoc"}
  # {:queue, "~> 0.1.0"}
  # {:heap, "~> 3.0"}
])

Setup

{:ok, data} = KinoAOC.download_puzzle("2024", "10", System.fetch_env!("LB_AOC_SECRET"))

Solve

defmodule Day10 do
  @up {-1, 0}
  @down {1, 0}
  @left {0, -1}
  @right {0, 1}
  @moves [@up, @down, @left, @right]

  def parse(data) do
    data
    |> String.trim()
    |> split_with("\n", fn row ->
      row |> String.split("", trim: true) |> Enum.map(&String.to_integer/1)
    end)
  end

  def split_with(str, sep, fun) do
    str
    |> String.split(sep, trim: true)
    |> Enum.map(&fun.(&1))
  end

  def to_grid(data) do
    my = length(data)
    mx = data |> hd() |> length()

    g = data
      |> Enum.with_index()
      |> Enum.reduce(%{}, fn {row, ri}, g ->
        row
        |> Enum.with_index()
        |> Enum.reduce(g, fn {v, ci}, g -> Map.put(g, {ri,ci}, v) end)
      end)
    %{g: g, my: my-1, mx: mx-1}
  end

  def starts(aoc) do
    aoc.g
    |> Enum.filter(fn {_p, h} -> h == 0 end)
    |> Enum.map(fn {k, _} -> k end)
  end

  def solve(data) do
    aoc = data |> parse() |> to_grid()

    starts(aoc)
    |> Enum.map(fn st -> dfs({[st], %{}}, aoc) end)
    |> calc_res()
  end

  def calc_res(maps) do
    maps
    |> Enum.map(fn m ->
      t1 = Enum.count(m)
      t2 = Map.values(m) |> Enum.sum()
      {t1, t2}
    end)
    |> Enum.reduce({0, 0}, fn {t1, t2}, {r1, r2} ->
      {r1+t1, r2+t2}
    end)
  end

  def dfs({[], res}, _), do: res

  def dfs({q, res}, aoc) do
    [h | q] = q

    next_moves(h, 1, aoc)
    |> Enum.reduce({q, res}, fn p, {q, res} ->
      if aoc.g[p] == 9 do
        res = Map.update(res, p, 1, fn v -> v+1 end)
        {q, res}
      else
        {[p | q], res}
      end
    end)
    |> dfs(aoc)
  end

  def next_moves({y, x}, step, aoc) do
    @moves
    |> Enum.map(fn {dy, dx} -> {y + dy, x + dx} end)
    |> Enum.filter(fn {y2, x2} ->
      in_grid?({y2, x2}, aoc) and
        (aoc.g[{y2, x2}] - aoc.g[{y,x}] == step)
    end)
  end

  def in_grid?({y, x}, aoc) do
    y >= 0 and y <= aoc.my and x >= 0 and x <= aoc.mx
  end
end

tdata = """
0123
1234
8765
9876
"""

tdata2 = """
89010123
78121874
87430965
96549874
45678903
32019012
01329801
10456732
"""

tdata3 = """
012345
123456
234567
345678
416789
567891
"""

Day10.solve(tdata)  |> IO.inspect(label: "t1")
Day10.solve(tdata2) |> IO.inspect(label: "t2")
Day10.solve(tdata3) |> IO.inspect(label: "t3")
Day10.solve(data) # {682, 1511}