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

Day 9

2022/day_9.livemd

Day 9

Mix.install(
  [
    {:advent_of_code_utils, "~> 3.1"},
    {:kino, "~> 0.8.0"},
    {:aoc_utils, path: "."}
  ],
  config: [
    advent_of_code_utils: [session: System.fetch_env!("LB_AOC_TOKEN")]
  ]
)

Mix.Tasks.Aoc.Get.run(["--year", "2022", "--day", "9"])

Inputs

# Override example
example_input = "R 4\nU 4\nL 3\nD 1\nR 4\nD 1\nL 5\nR 2\n"
input = AOC.input_string(2022, 9)

Parser

defmodule Parser do
  def convert(input, :pass) do
    input
  end

  def convert(_input, :drop) do
    :to_be_dropped
  end

  def convert(input, :to_integer) do
    String.to_integer(input)
  end

  def convert(input, :to_atom) do
    String.to_atom(input)
  end

  def convert(inputs, converters) when is_list(inputs) and is_list(converters) do
    Enum.zip(inputs, converters)
    |> Enum.map(fn {input, converter} ->
      convert(input, converter)
    end)
    |> Enum.reject(&(&1 == :to_be_dropped))
    |> List.to_tuple()
  end

  def parse_line(line) do
    line
    |> String.split(" ", trim: true)
    |> then(&convert(&1, [:to_atom, :to_integer]))
  end

  def parse(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&parse_line/1)
  end
end
Parser.parse(example_input)

Simple SVG Draw

alias AOCUtils.SVGDraw
import AOCUtils.SVGDraw

SVGDraw.new(200, 200)
|> circle(10, 10, 5)
|> set_stroke("green")
|> set_fill("red")
|> circle(100, 100, 5)
|> square(20, 20, 10)
|> set_stroke("black")
|> set_stroke_width("2")
|> line(10, 10, 100, 100)
|> set_fill("white")
|> set_stroke_width(4)
|> square(10, 100, 25)
|> render()
|> then(&Kino.Image.new(&1, :svg))

Rope Grid

defmodule RopeGrid do
  def new() do
    %{
      head: {0, 0},
      tail: {0, 0}
    }
  end

  def move(grid, :R, 1) do
    grid
    |> move_head(&increment_x/1)
    |> move_tail()
  end

  def move(grid, :U, 1) do
    grid
    |> move_head(&increment_y/1)
    |> move_tail()
  end

  def move_head(grid, mover) do
    Map.update!(grid, :head, mover)
  end

  # Head moved horizontally
  # def move_tail(%{head: {hx, hy}, tail: {tx, ty}} = grid) when hx == tx do
  #   grid
  # end

  def move_tail(grid) do
    distance_between =
      grid
      |> distance()
      |> subtract_one_clamp_zero()

    %{grid | tail: add_locations(grid.tail, distance_between)}
  end

  def increment_x({x, y}), do: {x + 1, y}
  def decrement_x({x, y}), do: {x - 1, y}
  def increment_y({x, y}), do: {x, y + 1}
  def decrement_y({x, y}), do: {x, y - 1}

  def distance(%{head: {hx, hy}, tail: {tx, ty}}) do
    {hx - tx, hy - ty}
  end

  def add_locations({x1, y1}, {x2, y2}) do
    {x1 + x2, y1 + y2}
  end

  def subtract_one_clamp_zero({x, y}) do
    {
      max(0, x - 1),
      max(0, y - 1)
    }
  end

  def draw(%{head: {hx, hy}, tail: {tx, ty}}, box_size \\ 10) do
    alias AOCUtils.SVGDraw

    text_location = """
    Head Location: #{hx}, #{hy}
Tail Location:
#{tx}, #{ty} """ width = hx + 3 height = hy + 3 border_width = 1 svg_width = width * box_size + border_width * 2 svg_height = height * box_size + border_width * 2 svg = SVGDraw.new( svg_width, svg_height, global_style: %{ transform: "scaleY(-1)", transform_origin: "center" } ) # Draw one big box around the outside svg = svg |> SVGDraw.set_stroke_width(border_width) |> SVGDraw.set_stroke("black") |> SVGDraw.set_fill("white") |> SVGDraw.rectangle(0, 0, svg_width, svg_height) # Draw boxes svg = for x <- Range.new(0, width), y <- Range.new(0, height), reduce: svg do svg -> svg |> SVGDraw.set_stroke_width(border_width) |> SVGDraw.set_stroke("black") |> then(fn svg -> cond do {x, y} == {0, 0} -> SVGDraw.set_fill(svg, "blue") {x, y} == {hx, hy} -> SVGDraw.set_fill(svg, "green") {x, y} == {tx, ty} -> SVGDraw.set_fill(svg, "red") true -> SVGDraw.set_fill(svg, "white") end end) |> SVGDraw.square( x * box_size + border_width, y * box_size + border_width, box_size ) end Kino.Layout.grid([ Kino.Markdown.new(text_location), svg |> SVGDraw.render() |> then(&amp;Kino.Image.new(&amp;1, :svg)) ]) end end
RopeGrid.new()
|> RopeGrid.move(:R, 1)
|> RopeGrid.move(:R, 1)
|> RopeGrid.move(:R, 1)
|> RopeGrid.move(:R, 1)
|> RopeGrid.move(:U, 1)
# |> RopeGrid.move(:U, 1)

|> RopeGrid.draw(20)

Part 1

example_input
input

Part 2

example_input
input