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

Advent of Code 2023

2023/day11.livemd

Advent of Code 2023

Mix.install([
  {:req, "~> 0.3.2"}
])

Day 11

input =
  "https://adventofcode.com/2023/day/11/input"
  |> Req.get!(headers: [cookie: "session=#{System.get_env("AOC_COOKIE")}"])
  |> Map.get(:body)
sample = """
...#......
.......#..
#.........
..........
......#...
.#........
.........#
..........
.......#..
#...#.....
"""
defmodule A do
  def parse(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&String.split(&1, "", trim: true))
    |> then(fn rows ->
      h = length(rows)
      w = length(hd(rows))

      grid =
        for {row, y} <- Enum.with_index(rows),
            {c, x} <- Enum.with_index(row),
            c == "#",
            into: MapSet.new(),
            do: {x, y}

      %{
        grid: grid,
        h: h,
        w: w
      }
    end)
  end

  def part1(input) do
    input
    |> parse()
    |> expand_grid(2)
    |> calc_distances()
    |> Enum.sum()
  end

  def part2(input) do
    input
    |> parse()
    |> expand_grid(1_000_000)
    |> calc_distances()
    |> Enum.sum()
  end

  def expand_grid(data, n) do
    [xs, ys] =
      data.grid
      |> MapSet.to_list()
      |> Enum.unzip()
      |> Tuple.to_list()
      |> Enum.map(&amp;Enum.into(&amp;1, MapSet.new()))

    {x_map, _} =
      0..data.h
      |> Enum.reduce({%{}, 0}, fn x, {x_mod, offset} ->
        if MapSet.member?(xs, x) do
          {Map.put(x_mod, x, offset), offset + 1}
        else
          {x_mod, offset + n}
        end
      end)

    {y_map, _} =
      0..data.w
      |> Enum.reduce({%{}, 0}, fn y, {y_mod, offset} ->
        if MapSet.member?(ys, y) do
          {Map.put(y_mod, y, offset), offset + 1}
        else
          {y_mod, offset + n}
        end
      end)

    data.grid
    |> Enum.map(fn {x, y} -> {Map.get(x_map, x), Map.get(y_map, y)} end)
    |> Enum.into(MapSet.new())
    |> then(fn new_grid ->
      Map.put(data, :grid, new_grid)
    end)
  end

  def calc_distances(data) do
    for(a <- data.grid, b <- data.grid, do: [a, b])
    |> Enum.map(&amp;Enum.sort/1)
    |> Enum.uniq()
    |> Enum.filter(fn [a, b] -> a != b end)
    |> Enum.map(fn [a, b] -> manhattan_distance(a, b) end)
  end

  def manhattan_distance({x1, y1}, {x2, y2}) do
    abs(x1 - x2) + abs(y1 - y2)
  end
end

Part 1

input
|> A.part1()

Part 2

input
|> A.part2()