DEV Community

Discussion on: AoC Day 6: Chronal Coordinates

Collapse
 
yordiverkroost profile image
Yordi Verkroost

Here we go, this is my solution in Elixir.

The idea for part one is to first determine the maximum field, based on the min/max values of the x and y coordinates. Then, for each position in the field, determine the distance to each of the input coordinates, keeping track of the coordinate that is the closest to the position in the field. Once this information is calculated, we can determine the size of the largest area within the field.

Part two largely follows the same logic, but takes the sum of a position in the field to all input coordinates, instead of taking only the closest one.

Common:

defmodule AoC.DaySix.Common do
  alias AoC.DaySix.{Coordinate, Field}

  def read_input(path) do
    path
    |> File.stream!()
    |> Stream.map(&String.trim_trailing/1)
    |> Enum.with_index()
    |> Enum.map_reduce(%Field{}, fn {line, index}, field -> parse_input(line, index, field) end)
  end

  defp parse_input(line, index, field) do
    coordinate = get_coordinate(line, index)
    field = get_field(coordinate, field)
    {coordinate, field}
  end

  defp get_coordinate(line, index) do
    [x, y] = String.split(line, ",")
    x = get_integer(x)
    y = get_integer(y)
    %Coordinate{id: index, x: x, y: y}
  end

  defp get_integer(string) do
    string
    |> String.trim()
    |> String.to_integer()
  end

  defp get_field(coordinate, field) do
    %Field{
      min_x: min(coordinate.x, field.min_x),
      min_y: min(coordinate.y, field.min_y),
      max_x: max(coordinate.x, field.max_x),
      max_y: max(coordinate.y, field.max_y)
    }
  end
end

Part one:

defmodule AoC.DaySix.PartOne do
  alias AoC.DaySix.{Coordinate, Common}

  def main() do
    "lib/day6/input.txt"
    |> Common.read_input()
    |> get_distance()
    |> Enum.filter(fn {_k, v} -> v != nil end)
    |> Enum.reduce(%{}, fn {_, {[c], _}}, acc ->
      Map.update(acc, c.id, 1, &(&1 + 1))
    end)
    |> Enum.reduce(0, fn {_, v}, acc -> max(v, acc) end)
  end

  defp get_distance({coordinates, field}) do
    Enum.reduce(field.min_x..field.max_x, %{}, fn x, acc ->
      Enum.reduce(field.min_y..field.max_y, acc, fn y, acc ->
        get_field_distances(x, y, coordinates, field, acc)
      end)
    end)
  end

  defp get_field_distances(x, y, coordinates, field, acc) do
    field_coordinate = %Coordinate{x: x, y: y}
    max_distance = get_max_distance(field)

    {result, distance} =
      Enum.reduce(coordinates, {[], max_distance}, fn input_coordinate, {result, distance} ->
        get_distance_to_coordinate(input_coordinate, field_coordinate, result, distance)
      end)

    result_distance =
      case length(result) do
        1 -> {result, distance}
        _ -> nil
      end

    Map.put(acc, {x, y}, result_distance)
  end

  defp get_max_distance(field) do
    get_manhattan_distance(%Coordinate{x: field.min_x, y: field.min_y}, %Coordinate{
      x: field.max_x,
      y: field.max_y
    })
  end

  defp get_distance_to_coordinate(input_coordinate, field_coordinate, result, distance) do
    distance_total = get_manhattan_distance(input_coordinate, field_coordinate)

    cond do
      distance_total == distance -> {[input_coordinate | result], distance}
      distance_total < distance -> {[input_coordinate], distance_total}
      true -> {result, distance}
    end
  end

  defp get_manhattan_distance(c1, c2) do
    abs(c1.x - c2.x) + abs(c1.y - c2.y)
  end
end

Part two:

defmodule AoC.DaySix.PartTwo do
  alias AoC.DaySix.{Common}

  @max_region_size 10000

  def main() do
    "lib/day6/input.txt"
    |> Common.read_input()
    |> get_distance()
    |> Enum.filter(fn {_k, v} -> v < @max_region_size end)
    |> Enum.count()
  end

  defp get_distance({coordinates, field}) do
    Enum.reduce(field.min_x..field.max_x, %{}, fn x, acc ->
      Enum.reduce(field.min_y..field.max_y, acc, fn y, acc ->
        result =
          Enum.reduce(coordinates, 0, fn coordinate, result ->
            get_total_distance(coordinate, x, y, result)
          end)

        Map.put(acc, {x, y}, result)
      end)
    end)
  end

  defp get_total_distance(coordinate, x, y, result) do
    result + abs(coordinate.x - x) + abs(coordinate.y - y)
  end
end