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

Day 14

day14.livemd

Day 14

Mix.install([
  {:tucan, "~> 0.4.1"},
  {:kino_vega_lite, "~> 0.1.13"},
])

Setup

input =
  System.fetch_env!("LB_AOC_DIR")
  |> Path.join("data/day14.txt")
  |> File.read!()

nil
nil

Solve

defmodule Day14 do
  def parse(input) do
    for row <- String.split(input, "\n"), row != "" do
      [<<"p=", position::binary>>, <<"v=", velocity::binary>>] = String.split(row, " ")
      [x, y] = String.split(position, ",")
      [vx, vy] = String.split(velocity, ",")

      {{String.to_integer(x), String.to_integer(y)},
       {String.to_integer(vx), String.to_integer(vy)}}
    end
  end

  def(teleport(position, _velocity, _width, _height, 0), do: position)

  def teleport({x, y}, {dx, dy}, width, height, iter) do
    x2 = x + dx
    y2 = y + dy

    x2 =
      cond do
        x2 < 0 ->
          width + x2

        x2 >= width ->
          abs(width - x2)

        true ->
          x2
      end

    y2 =
      cond do
        y2 < 0 ->
          height + y2

        y2 >= height ->
          abs(height - y2)

        true ->
          y2
      end

    teleport({x2, y2}, {dx, dy}, width, height, iter - 1)
  end

  defp quadrant({x, y}, width, height) do
    mid_x = floor(width / 2)
    mid_y = floor(height / 2)

    cond do
      x < mid_x and y < mid_y ->
        1

      x < mid_x and y > mid_y ->
        2

      x > mid_x and y < mid_y ->
        3

      x > mid_x and y > mid_y ->
        4

      true ->
        nil
    end
  end

  def run(input, width, height, iters) do
    parse(input)
    |> Task.async_stream(
      fn {position, velocity} ->
        teleport(position, velocity, width, height, iters)
      end,
      ordered: false
    )
    |> Enum.map(&amp;elem(&amp;1, 1))
  end

  @doc ~S"""
  iex> Day14.part1("p=0,4 v=3,-3\np=6,3 v=-1,-3\np=10,3 v=-1,2\np=2,0 v=2,-1\np=0,0 v=1,3\np=3,0 v=-2,-2\np=7,6 v=-1,-3\np=3,0 v=-1,-2\np=9,3 v=2,3\np=7,3 v=-1,2\np=2,4 v=2,-3\np=9,5 v=-3,-3\n")
  12
  """
  def part1(input, sample \\ true) do
    {width, height} = if sample, do: {11, 7}, else: {101, 103}

    run(input, width, height, 100)
    |> Enum.group_by(&amp;quadrant(&amp;1, width, height))
    |> Enum.reduce(1, fn
      {nil, _g}, acc -> acc
      {_g, values}, acc -> acc * Enum.count(values)
    end)
  end

  def part2(input) do
    {width, height} = {101, 103}

    Enum.find_value(0..15_000, fn i ->
      soln = run(input, width, height, i)
      limit = 33
      group_by_row = Enum.group_by(soln, &amp;elem(&amp;1, 1))
      group_by_col = Enum.group_by(soln, &amp;elem(&amp;1, 0))

      if Enum.any?(group_by_row, fn {_row, group} -> Enum.count(group) >= limit end) and
           Enum.any?(group_by_col, fn {_col, group} -> Enum.count(group) >= limit end) do
        soln
      end
    end)
    |> Enum.reduce(%{x: [], y: []}, fn {x, y}, acc ->
      acc
      |> Map.update!(:x, fn x1 -> [x * 10 | x1] end)
      |> Map.update!(:y, fn y1 -> [y * 10 | y1] end)
    end)
    |> Tucan.scatter("x", "y", width: 101 * 10, height: 103 * 10)
  end
end
{:module, Day14, <<70, 79, 82, 49, 0, 0, 30, ...>>, {:part2, 1}}
Day14.part2(input)
{"$schema":"https://vega.github.io/schema/vega-lite/v5.json","data":{"values":[{"x":320,"y":500},{"x":380,"y":440},{"x":440,"y":600},{"x":120,"y":390},{"x":360,"y":500},{"x":210,"y":610},{"x":380,"y":560},{"x":950,"y":0},{"x":110,"y":680},{"x":350,"y":450},{"x":410,"y":540},{"x":550,"y":250},{"x":710,"y":0},{"x":380,"y":510},{"x":360,"y":520},{"x":510,"y":490},{"x":150,"y":250},{"x":510,"y":690},{"x":360,"y":510},{"x":510,"y":550},{"x":130,"y":470},{"x":320,"y":690},{"x":470,"y":180},{"x":510,"y":410},{"x":210,"y":590},{"x":560,"y":960},{"x":210,"y":640},{"x":860,"y":560},{"x":360,"y":590},{"x":390,"y":480},{"x":330,"y":530},{"x":350,"y":180},{"x":350,"y":370},{"x":760,"y":690},{"x":560,"y":870},{"x":350,"y":690},{"x":310,"y":10},{"x":350,"y":510},{"x":830,"y":110},{"x":80,"y":320},{"x":370,"y":860},{"x":430,"y":560},{"x":570,"y":1020},{"x":430,"y":580},{"x":330,"y":590},{"x":510,"y":10},{"x":100,"y":720},{"x":370,"y":600},{"x":300,"y":580},{"x":830,"y":720},{"x":500,"y":370},{"x":510,"y":510},{"x":290,"y":580},{"x":40,"y":680},{"x":320,"y":610},{"x":340,"y":580},{"x":440,"y":370},{"x":310,"y":550},{"x":210,"y":630},{"x":280,"y":590},{"x":770,"y":410},{"x":420,"y":580},{"x":360,"y":530},{"x":370,"y":500},{"x":250,"y":370},{"x":340,"y":550},{"x":320,"y":580},{"x":490,"y":20},{"x":400,"y":540},{"x":510,"y":500},{"x":730,"y":520},{"x":800,"y":40},{"x":900,"y":580},{"x":450,"y":600},{"x":590,"y":990},{"x":400,"y":600},{"x":510,"y":540},{"x":410,"y":580},{"x":400,"y":510},{"x":400,"y":610},{"x":390,"y":500},{"x":400,"y":490},{"x":390,"y":550},{"x":300,"y":370},{"x":110,"y":410},{"x":250,"y":830},{"x":320,"y":490},{"x":390,"y":600},{"x":480,"y":690},{"x":80,"y":190},{"x":290,"y":120},{"x":330,"y":610},{"x":370,"y":590},{"x":420,"y":370},{"x":750,"y":170},{"x":460,"y":370},{"x":290,"y":600},{"x":510,"y":640},{"x":390,"y":510},{"x":750,"y":970},{"x":820,"y":320},{"x":330,"y":280},{"x":510,"y":650},{"x":360,"y":460},{"x":280,"y":610},{"x":350,"y":550},{"x":430,"y":370},{"x":340,"y":450},{"x":120,"y":550},{"x":330,"y":600},{"x":360,"y":580},{"x":370,"y":690},{"x":300,"y":550},{"x":300,"y":610},{"x":380,"y":690},{"x":360,"y":470},{"x":310,"y":560},{"x":330,"y":370},{"x":340,"y":460},{"x":360,"y":640},{"x":580,"y":580},{"x":360,"y":610},{"x":350,"y":600},{"x":410,"y":600},{"x":740,"y":50},{"x":680,"y":470},{"x":210,"y":620},{"x":540,"y":650},{"x":340,"y":370},{"x":510,"y":430},{"x":340,"y":690},{"x":450,"y":610},{"x":370,"y":480},{"x":390,"y":490},{"x":330,"y":500},{"x":510,"y":380},{"x":960,"y":920},{"x":210,"y":560},{"x":230,"y":370},{"x":40,"y":1000},{"x":310,"y":510},{"x":800,"y":60},{"x":510,"y":370},{"x":330,"y":690},{"x":940,"y":150},{"x":480,"y":370},{"x":900,"y":870},{"x":80,"y":980},{"x":340,"y":570},{"x":170,"y":290},{"x":390,"y":690},{"x":880,"y":300},{"x":390,"y":370},{"x":730,"y":210},{"x":170,"y":70},{"x":430,"y":610},{"x":390,"y":460},{"x":420,"y":790},{"x":760,"y":830},{"x":390,"y":540},{"x":210,"y":430},{"x":400,"y":550},{"x":210,"y":670},{"x":370,"y":570},{"x":330,"y":580},{"x":300,"y":590},{"x":380,"y":370},{"x":180,"y":780},{"x":410,"y":550},{"x":400,"y":370},{"x":340,"y":510},{"x":510,"y":590},{"x":350,"y":520},{"x":400,"y":590},{"x":210,"y":200},{"x":370,"y":460},{"x":360,"y":370},{"x":340,"y":500},{"x":340,"y":520},{"x":660,"y":890},{"x":220,"y":790},{"x":330,"y":490},{"x":740,"y":880},{"x":930,"y":420},{"x":210,"y":420},{"x":210,"y":450},{"x":190,"y":300},{"x":310,"y":500},{"x":0,"y":640},{"x":340,"y":530},{"x":210,"y":390},{"x":340,"y":540},{"x":390,"y":590},{"x":380,"y":450},{"x":320,"y":520},{"x":640,"y":440},{"x":370,"y":580},{"x":770,"y":670},{"x":280,"y":600},{"x":900,"y":750},{"x":460,"y":690},{"x":420,"y":550},{"x":210,"y":540},{"x":620,"y":0},{"x":270,"y":610},{"x":40,"y":730},{"x":320,"y":570},{"x":510,"y":910},{"x":400,"y":820},{"x":340,"y":590},{"x":210,"y":550},{"x":360,"y":420},{"x":160,"y":400},{"x":290,"y":560},{"x":400,"y":580},{"x":310,"y":370},{"x":370,"y":490},{"x":840,"y":390},{"x":210,"y":410},{"x":240,"y":690},{"x":320,"y":460},{"x":220,"y":120},{"x":200,"y":780},{"x":350,"y":580},{"x":410,"y":370},{"x":250,"y":0},{"x":380,"y":610},{"x":330,"y":460},{"x":330,"y":510},{"x":350,"y":630},{"x":510,"y":680},{"x":370,"y":130},{"x":360,"y":490},{"x":330,"y":570},{"x":210,"y":570},{"x":410,"y":500},{"x":600,"y":500},{"x":210,"y":500},{"x":400,"y":530},{"x":250,"y":690},{"x":210,"y":380},{"x":350,"y":430},{"x":510,"y":670},{"x":500,"y":690},{"x":300,"y":1020},{"x":210,"y":690},{"x":310,"y":590},{"x":520,"y":170},{"x":390,"y":580},{"x":370,"y":510},{"x":280,"y":560},{"x":320,"y":550},{"x":440,"y":690},{"x":410,"y":610},{"x":840,"y":830},{"x":260,"y":610},{"x":370,"y":530},{"x":380,"y":540},{"x":380,"y":490},{"x":400,"y":690},{"x":670,"y":230},{"x":510,"y":600},{"x":350,"y":570},{"x":510,"y":520},{"x":320,"y":510},{"x":600,"y":40},{"x":470,"y":370},{"x":310,"y":600},{"x":450,"y":690},{"x":370,"y":550},{"x":370,"y":610},{"x":0,"y":900},{"x":260,"y":1020},{"x":510,"y":460},{"x":40,"y":150},{"x":230,"y":690},{"x":510,"y":530},{"x":710,"y":190},{"x":210,"y":440},{"x":420,"y":510},{"x":290,"y":690},{"x":330,"y":480},{"x":380,"y":500},{"x":390,"y":530},{"x":430,"y":590},{"x":230,"y":310},{"x":150,"y":1020},{"x":420,"y":590},{"x":60,"y":860},{"x":940,"y":960},{"x":360,"y":600},{"x":420,"y":570},{"x":350,"y":560},{"x":380,"y":590},{"x":540,"y":420},{"x":920,"y":730},{"x":510,"y":580},{"x":490,"y":370},{"x":410,"y":690},{"x":350,"y":610},{"x":360,"y":570},{"x":680,"y":50},{"x":340,"y":600},{"x":290,"y":610},{"x":370,"y":620},{"x":850,"y":670},{"x":210,"y":520},{"x":210,"y":680},{"x":510,"y":620},{"x":120,"y":590},{"x":310,"y":540},{"x":330,"y":540},{"x":660,"y":560},{"x":540,"y":780},{"x":510,"y":470},{"x":420,"y":610},{"x":360,"y":440},{"x":490,"y":840},{"x":370,"y":540},{"x":370,"y":560},{"x":310,"y":530},{"x":960,"y":820},{"x":390,"y":450},{"x":340,"y":480},{"x":360,"y":630},{"x":730,"y":770},{"x":670,"y":520},{"x":210,"y":490},{"x":760,"y":960},{"x":220,"y":690},{"x":350,"y":440},{"x":350,"y":620},{"x":520,"y":910},{"x":440,"y":610},{"x":310,"y":690},{"x":290,"y":370},{"x":510,"y":390},{"x":360,"y":560},{"x":320,"y":370},{"x":370,"y":640},{"x":360,"y":450},{"x":320,"y":600},{"x":480,"y":940},{"x":220,"y":970},{"x":770,"y":560},{"x":370,"y":170},{"x":800,"y":320},{"x":360,"y":690},{"x":510,"y":570},{"x":410,"y":510},{"x":470,"y":690},{"x":510,"y":450},{"x":390,"y":610},{"x":300,"y":600},{"x":880,"y":0},{"x":310,"y":580},{"x":260,"y":370},{"x":350,"y":640},{"x":350,"y":500},{"x":220,"y":370},{"x":980,"y":510},{"x":380,"y":550},{"x":690,"y":210},{"x":350,"y":480},{"x":270,"y":370},{"x":380,"y":460},{"x":360,"y":550},{"x":510,"y":400},{"x":360,"y":540},{"x":160,"y":360},{"x":490,"y":690},{"x":240,"y":370},{"x":510,"y":480},{"x":310,"y":570},{"x":350,"y":490},{"x":310,"y":610},{"x":210,"y":660},{"x":350,"y":470},{"x":320,"y":540},{"x":370,"y":630},{"x":810,"y":660},{"x":300,"y":510},{"x":600,"y":690},{"x":370,"y":440},{"x":440,"y":10},{"x":640,"y":70},{"x":350,"y":590},{"x":210,"y":530},{"x":420,"y":690},{"x":180,"y":560},{"x":460,"y":610},{"x":400,"y":460},{"x":340,"y":440},{"x":390,"y":520},{"x":450,"y":370},{"x":910,"y":490},{"x":460,"y":900},{"x":130,"y":500},{"x":370,"y":370},{"x":330,"y":450},{"x":110,"y":310},{"x":430,"y":600},{"x":410,"y":590},{"x":320,"y":560},{"x":510,"y":420},{"x":400,"y":500},{"x":210,"y":480},{"x":20,"y":280},{"x":420,"y":540},{"x":550,"y":750},{"x":330,"y":550},{"x":510,"y":630},{"x":210,"y":600},{"x":320,"y":590},{"x":320,"y":220},{"x":380,"y":470},{"x":290,"y":550},{"x":120,"y":80},{"x":990,"y":560},{"x":30,"y":560},{"x":510,"y":660},{"x":280,"y":370},{"x":200,"y":260},{"x":100,"y":880},{"x":380,"y":580},{"x":260,"y":690},{"x":350,"y":110},{"x":190,"y":570},{"x":270,"y":600},{"x":500,"y":940},{"x":310,"y":1010},{"x":280,"y":690},{"x":210,"y":370},{"x":820,"y":210},{"x":390,"y":560},{"x":210,"y":510},{"x":340,"y":560},{"x":410,"y":530},{"x":510,"y":440},{"x":350,"y":460},{"x":370,"y":470},{"x":350,"y":530},{"x":410,"y":570},{"x":270,"y":690},{"x":380,"y":530},{"x":510,"y":610},{"x":380,"y":600},{"x":340,"y":610},{"x":360,"y":430},{"x":440,"y":590},{"x":310,"y":740},{"x":770,"y":530},{"x":370,"y":430},{"x":490,"y":950},{"x":300,"y":540},{"x":210,"y":470},{"x":380,"y":480},{"x":360,"y":480},{"x":470,"y":310},{"x":940,"y":140},{"x":40,"y":270},{"x":390,"y":570},{"x":440,"y":560},{"x":430,"y":1010},{"x":330,"y":520},{"x":400,"y":570},{"x":420,"y":560},{"x":290,"y":10},{"x":320,"y":530},{"x":370,"y":520},{"x":340,"y":490},{"x":380,"y":570},{"x":630,"y":520},{"x":400,"y":560},{"x":210,"y":580},{"x":470,"y":190},{"x":370,"y":450},{"x":400,"y":520},{"x":340,"y":470},{"x":890,"y":1000},{"x":510,"y":560},{"x":350,"y":540},{"x":290,"y":590},{"x":210,"y":460},{"x":310,"y":300},{"x":300,"y":560},{"x":300,"y":570},{"x":480,"y":270},{"x":330,"y":560},{"x":210,"y":400},{"x":210,"y":650},{"x":360,"y":620},{"x":430,"y":690},{"x":420,"y":600},{"x":40,"y":750},{"x":380,"y":520},{"x":300,"y":690},{"x":410,"y":560},{"x":610,"y":780},{"x":430,"y":550}]},"encoding":{"x":{"field":"x","scale":{"zero":false},"type":"quantitative"},"y":{"field":"y","scale":{"zero":false},"type":"quantitative"}},"height":1030,"mark":{"fillOpacity":1,"type":"point"},"width":1010}