Day 9
This commit is contained in:
parent
3e2e3eb9d4
commit
8a4c0ab8cc
1 changed files with 173 additions and 0 deletions
173
day9.livemd
Normal file
173
day9.livemd
Normal file
|
@ -0,0 +1,173 @@
|
|||
# AOC 2022 - Day 9
|
||||
|
||||
```elixir
|
||||
Mix.install([
|
||||
{:req, "~> 0.3.3"},
|
||||
{:vega_lite, "~> 0.1.6"},
|
||||
{:kino_vega_lite, "~> 0.1.7"}
|
||||
])
|
||||
|
||||
alias VegaLite, as: Vl
|
||||
```
|
||||
|
||||
## Puzzle description
|
||||
|
||||
[Day 9: Rope Bridge](https://adventofcode.com/2022/day/9).
|
||||
|
||||
## Input
|
||||
|
||||
```elixir
|
||||
defmodule Load do
|
||||
def input do
|
||||
aoc_session = System.fetch_env!("LB_AOC_SESSION")
|
||||
input_url = "https://adventofcode.com/2022/day/9/input"
|
||||
Req.get!(input_url, headers: [cookie: "session=#{aoc_session}"]).body
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
```elixir
|
||||
defmodule Util do
|
||||
def process(input) do
|
||||
input
|
||||
|> String.split()
|
||||
|> Enum.chunk_every(2)
|
||||
|> Enum.map(&parse_motion/1)
|
||||
end
|
||||
|
||||
defp parse_motion(["U", amount]), do: {{0, 1}, String.to_integer(amount)}
|
||||
defp parse_motion(["D", amount]), do: {{0, -1}, String.to_integer(amount)}
|
||||
defp parse_motion(["L", amount]), do: {{-1, 0}, String.to_integer(amount)}
|
||||
defp parse_motion(["R", amount]), do: {{1, 0}, String.to_integer(amount)}
|
||||
|
||||
def plot_locations(positions) do
|
||||
data =
|
||||
positions
|
||||
|> Enum.map(fn {x, y} -> %{x: x, y: y} end)
|
||||
|
||||
chart =
|
||||
Vl.new(width: 500, height: 400)
|
||||
|> Vl.mark(:square)
|
||||
|> Vl.encode_field(:x, "x", type: :ordinal, title: "X-coordinate", axis: [label_angle: 0])
|
||||
|> Vl.encode_field(:y, "y", type: :ordinal, title: "Y-coordinate", sort: :descending)
|
||||
|> Vl.config(view: [stroke: nil])
|
||||
|> Kino.VegaLite.new()
|
||||
|> Kino.render()
|
||||
|
||||
for position <- data do
|
||||
Kino.VegaLite.push(chart, position)
|
||||
Process.sleep(1)
|
||||
end
|
||||
|
||||
positions
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
```elixir
|
||||
defmodule RopeSimulator do
|
||||
def simulate(instructions, knots \\ 2) do
|
||||
{_final_pos, trail} =
|
||||
for i <- instructions, reduce: {{0, 0}, []} do
|
||||
{pos, trail} ->
|
||||
new_trail = move(pos, i, [])
|
||||
new_pos = hd(new_trail)
|
||||
{new_pos, new_trail ++ trail}
|
||||
end
|
||||
|
||||
trail = Enum.reverse(trail)
|
||||
|
||||
for _ <- 2..knots, reduce: trail do
|
||||
trail -> Enum.scan(trail, {0, 0}, fn head, tail -> move_tail(head, tail) end)
|
||||
end
|
||||
|> MapSet.new()
|
||||
end
|
||||
|
||||
def move({x, y}, {_dir, 0}, trail), do: [{x, y} | trail]
|
||||
|
||||
def move(pos, {dir, n}, trail) do
|
||||
new_pos = translate(pos, dir)
|
||||
move(new_pos, {dir, n - 1}, [new_pos | trail])
|
||||
end
|
||||
|
||||
# overlap, do nothing
|
||||
def move_tail({x, y}, {x, y}), do: {x, y}
|
||||
# on same row
|
||||
def move_tail({hx, y}, {tx, y}) when abs(hx - tx) == 1, do: {tx, y}
|
||||
def move_tail({hx, y}, {tx, y}) when hx > tx, do: {tx + 1, y}
|
||||
def move_tail({hx, y}, {tx, y}) when hx < tx, do: {tx - 1, y}
|
||||
# on same column
|
||||
def move_tail({x, hy}, {x, ty}) when abs(hy - ty) == 1, do: {x, ty}
|
||||
def move_tail({x, hy}, {x, ty}) when hy > ty, do: {x, ty + 1}
|
||||
def move_tail({x, hy}, {x, ty}) when hy < ty, do: {x, ty - 1}
|
||||
# somewhere diagonally => do nothing
|
||||
def move_tail({hx, hy}, {tx, ty}) when abs(hx - tx) + abs(hy - ty) == 2, do: {tx, ty}
|
||||
|
||||
# move tail closer diagonally
|
||||
def move_tail({hx, hy}, {tx, ty}) do
|
||||
{dx, dy} = {hx - tx, hy - ty}
|
||||
translate({tx, ty}, {round(dx / abs(dx)), round(dy / abs(dy))})
|
||||
end
|
||||
|
||||
defp translate({x, y}, {dx, dy}), do: {x + dx, y + dy}
|
||||
end
|
||||
```
|
||||
|
||||
```elixir
|
||||
defmodule Part1 do
|
||||
def run(input) do
|
||||
input
|
||||
|> Util.process()
|
||||
|> RopeSimulator.simulate()
|
||||
|> Enum.count()
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Part2 do
|
||||
def run(input) do
|
||||
input
|
||||
|> Util.process()
|
||||
|> RopeSimulator.simulate(10)
|
||||
|> Enum.count()
|
||||
end
|
||||
end
|
||||
|
||||
ExUnit.start(autorun: false)
|
||||
|
||||
defmodule Test do
|
||||
use ExUnit.Case, async: true
|
||||
@example_input ~s(R 5
|
||||
U 8
|
||||
L 8
|
||||
D 3
|
||||
R 17
|
||||
D 10
|
||||
L 25
|
||||
U 20)
|
||||
@input Load.input()
|
||||
|
||||
test "it loads the input" do
|
||||
assert String.length(@input) > 0
|
||||
end
|
||||
|
||||
test "part 1 example" do
|
||||
assert Part1.run(@example_input) === 88
|
||||
end
|
||||
|
||||
test "part 1" do
|
||||
assert Part1.run(@input) === 5683
|
||||
end
|
||||
|
||||
test "part 2 example" do
|
||||
assert Part2.run(@example_input) === 36
|
||||
end
|
||||
|
||||
test "part 2" do
|
||||
assert Part2.run(@input) === 2372
|
||||
end
|
||||
end
|
||||
|
||||
ExUnit.run()
|
||||
```
|
Loading…
Reference in a new issue