aoc 2024 day 8 and 9
This commit is contained in:
parent
f8f0a9db2a
commit
e9dda17235
2 changed files with 385 additions and 0 deletions
231
elixir/livebook/2024/day8.livemd
Normal file
231
elixir/livebook/2024/day8.livemd
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
# AOC 2024 Day 8
|
||||||
|
|
||||||
|
## Section
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule Grid2D do
|
||||||
|
defstruct width: nil, height: nil
|
||||||
|
|
||||||
|
@directions %{
|
||||||
|
straight: [
|
||||||
|
{1, 0},
|
||||||
|
{-1, 0},
|
||||||
|
{0, 1},
|
||||||
|
{0, -1}
|
||||||
|
],
|
||||||
|
diagonal: [
|
||||||
|
{1, 1},
|
||||||
|
{1, -1},
|
||||||
|
{-1, 1},
|
||||||
|
{-1, -1}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def new(width, height) do
|
||||||
|
%Grid2D{width: width, height: height}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
## Examples
|
||||||
|
iex> Grid2D.index_to_coord(Grid2D.new(5,5), 0)
|
||||||
|
{0,0}
|
||||||
|
|
||||||
|
iex> Grid2D.index_to_coord(Grid2D.new(5,5), 5)
|
||||||
|
{0,1}
|
||||||
|
|
||||||
|
iex> Grid2D.index_to_coord(Grid2D.new(5,5), 6)
|
||||||
|
{1,1}
|
||||||
|
|
||||||
|
iex> Grid2D.index_to_coord(Grid2D.new(5,5), 3)
|
||||||
|
{3,0}
|
||||||
|
|
||||||
|
iex> Grid2D.index_to_coord(Grid2D.new(5,5), 24)
|
||||||
|
{4, 4}
|
||||||
|
"""
|
||||||
|
def index_to_coord(%Grid2D{width: w}, index) do
|
||||||
|
{rem(index, w), floor(index / w)}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
## Examples
|
||||||
|
iex> Grid2D.coord_to_index( Grid2D.new(5,5), {0,0})
|
||||||
|
0
|
||||||
|
|
||||||
|
iex> Grid2D.coord_to_index( Grid2D.new(5,5), {4,4})
|
||||||
|
24
|
||||||
|
|
||||||
|
iex> Grid2D.coord_to_index( Grid2D.new(5,5), {2,2})
|
||||||
|
12
|
||||||
|
"""
|
||||||
|
def coord_to_index(%Grid2D{width: w}, {x, y}) do
|
||||||
|
y * w + x
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
iex> Grid2D.neighbors({0, 0}, Grid2D.new(5,5)) |> MapSet.new()
|
||||||
|
MapSet.new([{0, 1}, {1, 1}, {1,0}])
|
||||||
|
|
||||||
|
iex> Grid2D.neighbors({2, 2}, Grid2D.new(5,5)) |> MapSet.new()
|
||||||
|
[{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 3}, {3, 1}, {3, 2}, {3, 3}] |> MapSet.new()
|
||||||
|
|
||||||
|
iex> Grid2D.neighbors({4, 4}, Grid2D.new(5,5)) |> MapSet.new
|
||||||
|
[{3, 3}, {3, 4}, {4, 3}] |> MapSet.new
|
||||||
|
"""
|
||||||
|
def neighbors(p, g, allowed_directions \\ :all) do
|
||||||
|
directions(allowed_directions)
|
||||||
|
|> Stream.map(&add(p, &1))
|
||||||
|
|> Stream.filter(&is_in_grid?(&1, g))
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Distance from two points in a grid
|
||||||
|
|
||||||
|
iex> Grid2D.distance(Grid2D.new(5,5), {0,0}, {0,1}, :radial)
|
||||||
|
1
|
||||||
|
|
||||||
|
iex> Grid2D.distance(Grid2D.new(5,5), {0,0}, {1,1}, :radial)
|
||||||
|
1
|
||||||
|
|
||||||
|
iex> Grid2D.distance(Grid2D.new(5,5), {0,0}, {1,2}, :radial)
|
||||||
|
2
|
||||||
|
"""
|
||||||
|
def distance(grid, p1, p2, type)
|
||||||
|
def distance(_, {x, y}, {a, b}, :radial), do: max(abs(x - a), abs(y - b))
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Add two points together pairwise
|
||||||
|
|
||||||
|
iex> Grid2D.add({0,0}, {0,1})
|
||||||
|
{0,1}
|
||||||
|
|
||||||
|
iex> Grid2D.add({2,3}, {3,2})
|
||||||
|
{5,5}
|
||||||
|
"""
|
||||||
|
def add({x, y}, {j, k}), do: {x + j, y + k}
|
||||||
|
def sub({x, y}, {j, k}), do: {x - j, y - k}
|
||||||
|
def mul({x, y}, n), do: {n*x, n*y}
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Test if a point is in a grid
|
||||||
|
|
||||||
|
iex> Grid2D.is_in_grid?({1, 2}, %Grid2D{width: 3, height: 4})
|
||||||
|
true
|
||||||
|
|
||||||
|
iex> Grid2D.is_in_grid?({2, 2}, %Grid2D{width: 2, height: 4})
|
||||||
|
false
|
||||||
|
"""
|
||||||
|
def is_in_grid?({x, y}, %Grid2D{width: w, height: h}), do: x < w and y < h and x >= 0 and y >= 0
|
||||||
|
|
||||||
|
def span(grid, origin, vector) do
|
||||||
|
Stream.iterate(1, &(&1+1))
|
||||||
|
|> Enum.reduce_while(
|
||||||
|
{[],false,false},
|
||||||
|
fn i, {acc,pos,neg} ->
|
||||||
|
sign = if rem(i, 2) == 1, do: 1, else: -1
|
||||||
|
dist = div(i, 2)
|
||||||
|
|
||||||
|
point = Grid2D.add(origin, Grid2D.mul(vector, sign*dist))
|
||||||
|
next = if Grid2D.is_in_grid?(point, grid) do
|
||||||
|
{[point | acc], pos, neg}
|
||||||
|
else
|
||||||
|
pos = if sign == 1, do: true, else: pos
|
||||||
|
neg = if sign == -1, do: true, else: neg
|
||||||
|
{acc, pos, neg}
|
||||||
|
end
|
||||||
|
case next do
|
||||||
|
{_, false, _} -> {:cont, next}
|
||||||
|
{_, _, false} -> {:cont, next}
|
||||||
|
{_, true, true} -> {:halt, next}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
) |> elem(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec directions(allowed :: :all | :straight | :diagonal) :: any()
|
||||||
|
def directions(allowed \\ :all)
|
||||||
|
def directions(:all), do: directions(:straight) ++ directions(:diagonal)
|
||||||
|
def directions(:straight), do: @directions |> Map.get(:straight)
|
||||||
|
def directions(:diagonal), do: @directions |> Map.get(:diagonal)
|
||||||
|
end
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
input = """
|
||||||
|
""" |> String.trim()
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
lines = input |> String.split("\n")
|
||||||
|
height = lines |> Enum.count()
|
||||||
|
width = lines |> Enum.at(0) |> String.trim() |> String.length()
|
||||||
|
grid = Grid2D.new(width, height)
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule EnumUtil do
|
||||||
|
def pick_n(enum, n)
|
||||||
|
def pick_n([], _), do: []
|
||||||
|
def pick_n([a | rest], n) do
|
||||||
|
pick_n(rest, n, n, []) ++ pick_n(rest, n, n - 1, [a])
|
||||||
|
end
|
||||||
|
defp pick_n([], n, _, acc) do
|
||||||
|
if acc |> Enum.count == n do
|
||||||
|
[acc]
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
defp pick_n(_, _, 0, acc), do: [acc]
|
||||||
|
defp pick_n([a | rest], n, r, acc) do
|
||||||
|
pick_n(rest, n, r, acc) ++ pick_n(rest, n, r - 1, [a | acc])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
input = input |> String.replace("\n", "")
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule Resonence do
|
||||||
|
def find_antinodes(input, grid, antenna) do
|
||||||
|
input |> String.to_charlist()
|
||||||
|
|> Stream.with_index()
|
||||||
|
|> Stream.filter(&(elem(&1, 0) == antenna))
|
||||||
|
|> Stream.map(&elem(&1, 1))
|
||||||
|
|> Enum.map(&Grid2D.index_to_coord(grid, &1))
|
||||||
|
|> EnumUtil.pick_n(2)
|
||||||
|
|> Stream.map(fn
|
||||||
|
[a, b] -> {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
a |> Grid2D.sub(b)
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|> Stream.flat_map(fn {a, b, dist} ->
|
||||||
|
Grid2D.span(grid, a, dist)
|
||||||
|
# uncomment this filter for pt 2
|
||||||
|
|> Enum.filter(fn point ->
|
||||||
|
x = Grid2D.sub(point, a)
|
||||||
|
y = Grid2D.sub(point, b)
|
||||||
|
Grid2D.mul(x, 2) == y or Grid2D.mul(y, 2) == x
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_antennae(input) do
|
||||||
|
input |> String.to_charlist() |> Enum.reject(&(&1 == ?.)) |> Enum.uniq()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Resonence.find_antennae(input)
|
||||||
|
|> Stream.flat_map(&Resonence.find_antinodes(input, grid, &1))
|
||||||
|
|> MapSet.new()
|
||||||
|
|> MapSet.size()
|
||||||
|
```
|
154
elixir/livebook/2024/day9.livemd
Normal file
154
elixir/livebook/2024/day9.livemd
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
# AOC 2024 Day 9
|
||||||
|
|
||||||
|
## Section
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
input = ""
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule Fragmenter do
|
||||||
|
def parse_disk_map(map) do
|
||||||
|
map
|
||||||
|
|> String.to_charlist()
|
||||||
|
|> Stream.map(&(&1 - 48))
|
||||||
|
|> Stream.chunk_every(2)
|
||||||
|
|> Stream.with_index()
|
||||||
|
|> Enum.flat_map(fn {[used, free], id} ->
|
||||||
|
(for _ <- Stream.cycle([0]) |> Enum.take(used) do id end) ++ (for _ <- Stream.cycle([0]) |> Enum.take(free) do nil end)
|
||||||
|
{[used], id} ->
|
||||||
|
(for _ <- Stream.cycle([0]) |> Enum.take(used) do id end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
## Examples
|
||||||
|
iex> "2333133121414131402" |> Fragmenter.parse_disk_map() |> Fragmenter.defrag() |> Fragmenter.checksum()
|
||||||
|
1928
|
||||||
|
"""
|
||||||
|
def defrag(disk) do
|
||||||
|
cl_disk = disk |> Stream.with_index()
|
||||||
|
bak = cl_disk |> Enum.reverse() |> Enum.reject(&(elem(&1,0) == nil))
|
||||||
|
|
||||||
|
cl_disk
|
||||||
|
|> Enum.reduce(
|
||||||
|
{[], bak },
|
||||||
|
fn
|
||||||
|
{c, i}, {fwd, [{_, j} | _] = bak} when c != nil and i <= j ->
|
||||||
|
{ [c | fwd], bak}
|
||||||
|
{c, i}, {fwd, [{_, j} | _] = bak} when c != nil and i > j ->
|
||||||
|
{ [nil | fwd], bak}
|
||||||
|
{nil, i}, {fwd, [{c, j} | rest]} when i <= j ->
|
||||||
|
{ [c | fwd], rest }
|
||||||
|
{nil, i}, {fwd, [{_, j} | rest]} when i > j ->
|
||||||
|
{ [nil | fwd], rest }
|
||||||
|
end
|
||||||
|
) |> elem(0) |> Enum.reverse()
|
||||||
|
end
|
||||||
|
|
||||||
|
def checksum(disk) do
|
||||||
|
disk
|
||||||
|
|> Stream.with_index()
|
||||||
|
|> Stream.reject(&(elem(&1, 0) == nil))
|
||||||
|
|> Enum.reduce(
|
||||||
|
0,
|
||||||
|
fn {c, i}, acc -> acc + c*i end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Fragmenter.parse_disk_map(input)
|
||||||
|
|> Fragmenter.defrag()
|
||||||
|
|> Fragmenter.checksum()
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule Fragmenter2 do
|
||||||
|
def parse_disk_map(map) do
|
||||||
|
map
|
||||||
|
|> String.to_charlist()
|
||||||
|
|> Stream.map(&(&1 - 48))
|
||||||
|
|> Stream.chunk_every(2)
|
||||||
|
|> Stream.with_index()
|
||||||
|
|> Stream.flat_map(fn {[used, free], id} ->
|
||||||
|
[Stream.cycle([id]) |> Enum.take(used), Stream.cycle([nil]) |> Enum.take(free)]
|
||||||
|
{[used], id} ->
|
||||||
|
[Stream.cycle([id]) |> Enum.take(used)]
|
||||||
|
end)
|
||||||
|
|> Stream.reject(&(&1 == []))
|
||||||
|
end
|
||||||
|
|
||||||
|
def defrag(disk) do
|
||||||
|
files = disk |> Enum.into([])
|
||||||
|
x = Stream.flat_map(disk, &(&1)) |> Stream.reject(&(&1 == nil)) |> Enum.into(MapSet.new())
|
||||||
|
defrag(files |> :queue.from_list(), :queue.new(), x)
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge_empty(file, empty) do
|
||||||
|
[file, empty |> Enum.take(Enum.count(empty) - Enum.count(file))]
|
||||||
|
end
|
||||||
|
|
||||||
|
def defrag(dq, fragged, x) do
|
||||||
|
if :queue.is_empty(dq) do
|
||||||
|
fragged |> :queue.to_list()
|
||||||
|
else
|
||||||
|
case :queue.get(dq) do
|
||||||
|
[] -> defrag(:queue.drop(dq), fragged, x)
|
||||||
|
[n | _] = file when n != nil ->
|
||||||
|
n_available? = MapSet.member?(x, n)
|
||||||
|
defrag(
|
||||||
|
:queue.drop(dq),
|
||||||
|
:queue.in(
|
||||||
|
if n_available? do
|
||||||
|
file
|
||||||
|
else
|
||||||
|
Stream.cycle([nil]) |> Enum.take(file |> Enum.count())
|
||||||
|
end,
|
||||||
|
fragged
|
||||||
|
),
|
||||||
|
x
|
||||||
|
)
|
||||||
|
[nil | _] = empty ->
|
||||||
|
fit_q = :queue.filter(
|
||||||
|
fn [n | _] = l -> MapSet.member?(x, n) and (l |> Enum.count()) <= (empty |> Enum.count())
|
||||||
|
and (l |> Enum.at(0) != nil)
|
||||||
|
end, dq)
|
||||||
|
cond do
|
||||||
|
fit_q |> :queue.is_empty() -> defrag(
|
||||||
|
dq |> :queue.drop(),
|
||||||
|
:queue.in(empty, fragged),
|
||||||
|
x
|
||||||
|
)
|
||||||
|
true ->
|
||||||
|
first_fit = fit_q |> :queue.get_r()
|
||||||
|
[first_fit, empty] = merge_empty(first_fit, empty)
|
||||||
|
defrag(
|
||||||
|
dq |> :queue.drop() |> then(&:queue.in_r(empty, &1)),
|
||||||
|
:queue.in(first_fit, fragged),
|
||||||
|
x |> MapSet.delete(first_fit |> Enum.at(0))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def checksum(disk) do
|
||||||
|
disk
|
||||||
|
|> Stream.flat_map(&(&1))
|
||||||
|
|> Stream.with_index()
|
||||||
|
|> Stream.reject(&(elem(&1, 0) == nil))
|
||||||
|
|> Enum.reduce(
|
||||||
|
0,
|
||||||
|
fn {c, i}, acc -> acc + c*i end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Fragmenter2.parse_disk_map(input)
|
||||||
|
|> Fragmenter2.defrag()
|
||||||
|
|> Fragmenter2.checksum()
|
||||||
|
```
|
Loading…
Add table
Reference in a new issue