Powered by AppSignal & Oban Pro

Advent of code day 05

2021/livebooks/day-05.livemd

Advent of code day 05

Mix.install([
  {:kino, "~> 0.5.0"}
])

Setup input

example = Kino.Input.textarea("Please paste your input example:")
input = Kino.Input.textarea("Please paste your real input:")

Helpers

# is there a better way to match diagonal values? 
defmodule Coord do
  # vertical
  def extrapolate({x, y1}, {x, y2}) do
    y1..y2
    |> Enum.map(fn y ->
      {x, y}
    end)
  end

  # horizontal
  def extrapolate({x1, y}, {x2, y}) do
    x1..x2
    |> Enum.map(fn x ->
      {x, y}
    end)
  end

  # 45 degree diagonal

  def extrapolate({x1, y1} = from, {x2, y2}) when x1 > x2 and y1 > y2 do
    (x1 - 1)..x2
    |> Enum.reduce([from], fn _el, [{from_x, from_y} | _rest] = acc ->
      new_point = {from_x - 1, from_y - 1}

      [new_point | acc]
    end)
    |> Enum.reverse()
  end

  def extrapolate({x1, y1} = from, {x2, y2}) when x1 < x2 and y1 < y2 do
    x1..(x2 - 1)
    |> Enum.reduce([from], fn _el, [{from_x, from_y} | _rest] = acc ->
      new_point = {from_x + 1, from_y + 1}

      [new_point | acc]
    end)
    |> Enum.reverse()
  end

  def extrapolate({x1, y1} = from, {x2, y2}) when x1 >= x2 and y1 <= y2 do
    (x1 - 1)..x2
    |> Enum.reduce([from], fn _el, [{from_x, from_y} | _rest] = acc ->
      new_point = {from_x - 1, from_y + 1}

      [new_point | acc]
    end)
    |> Enum.reverse()
  end

  def extrapolate({x1, y1} = from, {x2, y2}) when x1 <= x2 and y1 >= y2 do
    x1..(x2 - 1)
    |> Enum.reduce([from], fn _el, [{from_x, from_y} | _rest] = acc ->
      new_point = {from_x + 1, from_y - 1}

      [new_point | acc]
    end)
    |> Enum.reverse()
  end
end

Part 01

coords =
  input
  |> Kino.Input.read()
  |> String.split(["\n", " -> ", ","], trim: true)
  |> Enum.map(&String.to_integer/1)
  |> Enum.chunk_every(4)
  |> Enum.map(fn [x1, y1, x2, y2] ->
    {{x1, y1}, {x2, y2}}
  end)
  |> Enum.filter(fn {{x1, y1}, {x2, y2}} ->
    x1 == x2 or y1 == y2
  end)
  |> Enum.map(fn {from, to} ->
    Coord.extrapolate(from, to)
  end)
  |> List.flatten()
  |> Enum.frequencies()
  |> Enum.filter(fn {_, value} -> value >= 2 end)
  |> Enum.count()

Part 02

# consider diagonal 

coords =
  example
  |> Kino.Input.read()
  |> String.split(["\n", " -> ", ","], trim: true)
  |> Enum.map(&String.to_integer/1)
  |> Enum.chunk_every(4)
  |> Enum.map(fn [x1, y1, x2, y2] ->
    {{x1, y1}, {x2, y2}}
  end)
  |> Enum.filter(fn {{x1, y1}, {x2, y2}} ->
    horizontal_or_vertical = x1 == x2 or y1 == y2
    diagonal_aligned = abs(x1 - x2) == abs(y1 - y2)

    diagonal_aligned or horizontal_or_vertical
  end)
  |> Enum.map(fn {from, to} ->
    Coord.extrapolate(from, to)
  end)
  |> List.flatten()
  |> Enum.frequencies()
  |> Enum.filter(fn {_, value} -> value >= 2 end)
  |> Enum.count()