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(&Kino.Image.new(&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