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

Day 15:

2021/15.livemd

Day 15:

Intro

https://adventofcode.com/2021/day/15

Input

Mix.install([
  {:priority_queue, "~> 1.0"}
])
input_test =
  """
  1163751742
  1381373672
  2136511328
  3694931569
  7463417111
  1319128137
  1359912421
  3125421639
  1293138521
  2311944581
  """
  |> String.split("\n", trim: true)
  |> Enum.map(fn r -> String.graphemes(r) |> Enum.map(&String.to_integer(&1)) end)
input =
  File.read!("input15.txt")
  |> String.split("\n", trim: true)
  |> Enum.map(fn r -> String.graphemes(r) |> Enum.map(&String.to_integer(&1)) end)

Part 1

defmodule Aoc2021.Day15 do
  def load(lol, r) do
    # lol = Enum.map(args, fn l -> String.graphemes(l) |> Enum.map(&String.to_integer/1) end)
    map =
      Stream.with_index(lol)
      |> Enum.map(fn {x, i} ->
        Stream.with_index(x) |> Enum.map(&{{i, elem(&1, 1)}, elem(&1, 0)})
      end)
      |> List.flatten()
      |> Map.new()

    {map, length(lol) * r - 1, length(hd(lol)) * r - 1, length(lol), length(hd(lol))}
  end

  def update(pq, {map, ly, lx, my, mx}, cost, dist, p = {y, x})
      when y >= 0 and y <= ly and x >= 0 and x <= lx do
    oldd = if is_map_key(cost, p), do: cost[p], else: 999_999_999
    newd = dist + rem(map[{rem(y, my), rem(x, mx)}] + div(y, my) + div(x, mx) - 1, 9) + 1
    if newd < oldd, do: PriorityQueue.put(pq, {newd, p}), else: pq
  end

  def update(pq, _map, _cost, _dist, _pos), do: pq

  def explore({_map, ly, lx, _, _}, _cost, _pq, {dist, {ly, lx}}), do: dist

  def explore(mp, cost, pq, {dist, {y, x}}) do
    pq = PriorityQueue.delete_min!(pq)

    if not is_map_key(cost, {y, x}) or cost[{y, x}] > dist do
      cost = Map.put(cost, {y, x}, dist)
      ms = Enum.map([{-1, 0}, {1, 0}, {0, -1}, {0, 1}], fn {a, b} -> {y + a, x + b} end)
      pq = Enum.reduce(ms, pq, &amp;update(&amp;2, mp, cost, dist, &amp;1))
      explore(mp, cost, pq, PriorityQueue.min!(pq))
    else
      explore(mp, cost, pq, PriorityQueue.min!(pq))
    end
  end

  def startq(), do: PriorityQueue.new() |> PriorityQueue.put({0, {0, 0}})
  def part1(args), do: load(args, 1) |> explore(%{}, startq(), {0, {0, 0}})
  def part2(args), do: load(args, 5) |> explore(%{}, startq(), {0, {0, 0}})
end
# TODO: Does not work

i = input_test
xmax = (Enum.at(i, 0) |> length) - 1
ymax = length(i) - 1
goal = {xmax, ymax}
start = {0, 0}

risk =
  for y <- 0..ymax do
    for x <- 0..xmax do
      {{x, y}, Enum.at(i, y) |> Enum.at(x)}
    end
  end
  |> List.flatten()
  |> Map.new()

defmodule M1 do
  def solve(start, goal, risk) do
    solve(start, goal, risk, [], 0, 0)
  end

  def solve(goal, goal, risk, _seen, score, _deep) do
    score + risk[goal]
  end

  def solve({x, y} = pos, goal, risk, seen, score, deep) do
    if deep >= 20 do
      IO.puts("LOOP!")
      :invalid
    else
      candidates =
        [{x - 1, y}, {x + 1, y}, {x, y - 1}, {x, y + 1}]
        |> Enum.filter(fn p -> p not in seen end)
        |> Enum.filter(fn p -> Map.has_key?(risk, p) end)
        |> Enum.map(fn p -> solve(p, goal, risk, seen ++ [pos], score + risk[goal], deep + 1) end)
        |> IO.inspect()
        |> Enum.filter(fn r -> r != :invalid end)

      if length(candidates) == 0 do
        :invalid
      else
        IO.puts("MIN")
        Enum.min(candidates)
      end
    end
  end
end

# M1.solve(start, goal, risk)
# M1.solve({3,0}, {3,0}, risk, [{0,0},{1,0},{1,1},{1,0},{2,0}],2)
# risk[{9,9}]
Aoc2021.Day15.part1(input)

Correct: 602

Part 2

Aoc2021.Day15.part2(input)

Correct: 2935