Compare commits
25 Commits
cf508c9396
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa1d6f5ca0 | ||
|
|
a9519aebbf | ||
|
|
3fc1300387 | ||
|
|
c550c1850c | ||
|
|
e24b45de16 | ||
|
|
a7beb06a28 | ||
|
|
03b92dd930 | ||
|
|
131f501b91 | ||
|
|
e9367fd784 | ||
|
|
753635b38e | ||
|
|
5c04d2651e | ||
|
|
1655ead021 | ||
|
|
3361ad3a79 | ||
|
|
20501c0819 | ||
|
|
c500a9a280 | ||
|
|
863e1a5cb0 | ||
|
|
abbd8a263a | ||
|
|
683522ddc0 | ||
|
|
ce5e83baa4 | ||
|
|
ee17acccae | ||
|
|
2ae3b6cf7b | ||
|
|
c086fc1773 | ||
|
|
2784245b2d | ||
|
|
c6c501af2a | ||
|
|
70cf651767 |
@@ -4,9 +4,11 @@ defmodule Aoc2023 do
|
|||||||
import Aoc2023.Common
|
import Aoc2023.Common
|
||||||
|
|
||||||
def main(input) do
|
def main(input) do
|
||||||
parsed = parse(input)
|
{t_parse, parsed} = :timer.tc fn -> parse(input) end
|
||||||
IO.puts("Part 1: #{part1(parsed)}")
|
{t_p1, _} = :timer.tc fn -> IO.puts("Part 1: #{part1(parsed)}") end
|
||||||
IO.puts("Part 2: #{part2(parsed)}")
|
|
||||||
|
{t_p2, _} = :timer.tc fn -> IO.puts("Part 2: #{part2(parsed)}") end
|
||||||
|
IO.puts("Took #{(t_parse + t_p1 + t_p2) / 1_000}ms (parse: #{t_parse / 1_000}ms, part1: #{t_p1 / 1_000}ms, part2: #{t_p2 / 1_000}ms)")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,66 @@
|
|||||||
defmodule Aoc2023.Common do
|
defmodule Aoc2023.Common do
|
||||||
def stringify(s) when is_binary(s) do s end
|
def stringify(s) when is_binary(s) do s end
|
||||||
def stringify(n) when is_integer(n) do inspect(n) end
|
def stringify(n) when is_integer(n) do inspect(n) end
|
||||||
|
def stringify(nil), do: "nil"
|
||||||
|
|
||||||
|
def map_nth(tup, n, fun) do
|
||||||
|
put_elem(tup, n, fun.(elem(tup, n)))
|
||||||
|
end
|
||||||
|
|
||||||
|
defmacro pipe(expr) do
|
||||||
|
quote do
|
||||||
|
fn (val) -> val |> unquote(expr) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defmacro l &&& r do
|
||||||
|
quote do
|
||||||
|
fn val ->
|
||||||
|
l = val |> unquote(l)
|
||||||
|
r = val |> unquote(r)
|
||||||
|
{l, r}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def runs(lst, f) do
|
||||||
|
lst
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.chunk_by(pipe(elem(0) |> f.()))
|
||||||
|
|> Enum.filter(pipe(Enum.at(0) |> elem(0) |> f.()))
|
||||||
|
|> Enum.map(Enum.at(0) |> elem(1) &&& length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def id(x), do: x
|
||||||
|
def const(_, x), do: x
|
||||||
|
|
||||||
|
def lines(x), do: String.split(x, "\n")
|
||||||
|
|
||||||
|
def head([head | _]), do: head
|
||||||
|
def head([]), do: nil
|
||||||
|
def tail([_ | tail]), do: tail
|
||||||
|
def tail([]), do: nil
|
||||||
|
|
||||||
|
def lcm(a, b), do: div(abs(a*b), Integer.gcd(a,b))
|
||||||
|
def lcm(ls), do: ls |> Enum.reduce(&lcm/2)
|
||||||
|
|
||||||
|
def list_map_at(l, n, f) do
|
||||||
|
List.replace_at(l, n, f.(Enum.at(l, n)))
|
||||||
|
end
|
||||||
|
|
||||||
|
def transpose(l), do: l |> Enum.zip |> Enum.map(&Tuple.to_list/1)
|
||||||
|
|
||||||
|
def collapse(enum, v) do
|
||||||
|
enum
|
||||||
|
|> Stream.zip(enum |> Stream.drop(1) |> Stream.concat([nil]))
|
||||||
|
|> Stream.filter(fn {a, b} -> a != v || b != v end)
|
||||||
|
|> Stream.map(pipe(elem(0)))
|
||||||
|
end
|
||||||
|
|
||||||
|
def repeat(_, 0), do: []
|
||||||
|
def repeat(lst, n), do: Enum.concat(lst, repeat(lst, n - 1))
|
||||||
|
|
||||||
|
def add_vec({y, x}, {y_, x_}) do
|
||||||
|
{y + y_, x + x_}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
91
lib/day10.ex
Normal file
91
lib/day10.ex
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
defmodule Aoc2023.Day10 do
|
||||||
|
use Aoc2023
|
||||||
|
require Integer
|
||||||
|
|
||||||
|
@connections %{
|
||||||
|
?| => [{-1, 0}, {1, 0}],
|
||||||
|
?- => [{0, -1}, {0, 1}],
|
||||||
|
?L => [{-1, 0}, {0, 1}],
|
||||||
|
?J => [{-1, 0}, {0, -1}],
|
||||||
|
?7 => [{1, 0}, {0, -1}],
|
||||||
|
?F => [{1, 0}, {0, 1}],
|
||||||
|
?. => [],
|
||||||
|
?S => []
|
||||||
|
}
|
||||||
|
|
||||||
|
defp path(s_conns, conns) do
|
||||||
|
loop = fn recur, checked, checking ->
|
||||||
|
checked_ = MapSet.union(checked, checking)
|
||||||
|
|
||||||
|
checking_ =
|
||||||
|
checking
|
||||||
|
|> Enum.flat_map(&Map.get(conns, &1, []))
|
||||||
|
|> MapSet.new()
|
||||||
|
|> MapSet.difference(checked_)
|
||||||
|
|
||||||
|
if MapSet.size(checking_) > 0, do: recur.(recur, checked_, checking_), else: checked_
|
||||||
|
end
|
||||||
|
|
||||||
|
loop.(loop, MapSet.new(), MapSet.new(s_conns))
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
grid =
|
||||||
|
input
|
||||||
|
|> lines
|
||||||
|
|> Enum.map(&String.to_charlist/1)
|
||||||
|
|> Enum.with_index()
|
||||||
|
|
||||||
|
s =
|
||||||
|
grid
|
||||||
|
|> Enum.find_value(fn {line, y} ->
|
||||||
|
x = line |> Enum.find_index(&(&1 == ?S))
|
||||||
|
if x, do: {y, x}
|
||||||
|
end)
|
||||||
|
|
||||||
|
conns =
|
||||||
|
grid
|
||||||
|
|> Enum.flat_map(fn {row, y} ->
|
||||||
|
row
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.map(fn {c, x} ->
|
||||||
|
{
|
||||||
|
{y, x},
|
||||||
|
Map.get(@connections, c) |> Enum.map(pipe(add_vec({y, x})))
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
|
||||||
|
s_conns = conns |> Enum.filter(&(s in elem(&1, 1))) |> Enum.map(pipe(elem(0)))
|
||||||
|
{s, s_conns, grid, path(s_conns, conns)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1({_, _, _, path}) do
|
||||||
|
path
|
||||||
|
|> MapSet.size()
|
||||||
|
|> then(&(&1 / 2))
|
||||||
|
|> trunc()
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2({{sy, sx}, s_conns, grid, path}) do
|
||||||
|
s_dirs = s_conns |> Enum.map(pipe(add_vec({-sy, -sx}))) |> Enum.sort()
|
||||||
|
s_char = @connections |> Enum.find(fn {_, conns} -> Enum.sort(conns) == s_dirs end) |> elem(0)
|
||||||
|
grid_ = grid |> list_map_at(sy, pipe(map_nth(0, pipe(List.replace_at(sx, s_char)))))
|
||||||
|
|
||||||
|
grid_
|
||||||
|
|> Stream.map(fn {line, y} ->
|
||||||
|
for x <- 0..length(line) - 1, reduce: {false, 0} do
|
||||||
|
{within, area} ->
|
||||||
|
cond do
|
||||||
|
{y, x} in path and Enum.at(line, x) in [?L, ?J, ?|] -> {not within, area}
|
||||||
|
{y, x} in path -> {within, area}
|
||||||
|
within -> {within, area + 1}
|
||||||
|
true -> {within, area}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|> elem(1)
|
||||||
|
end)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
end
|
||||||
48
lib/day11.ex
Normal file
48
lib/day11.ex
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
defmodule Aoc2023.Day11 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
coords =
|
||||||
|
input
|
||||||
|
|> lines
|
||||||
|
|> Enum.map(pipe(String.to_charlist() |> Enum.map(&(&1 == ?#))))
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.flat_map(fn {row, y} ->
|
||||||
|
row
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.filter(pipe(elem(0)))
|
||||||
|
|> Enum.map(fn {_, x} -> {y, x} end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
ys = coords |> Enum.map(pipe(elem(0))) |> MapSet.new()
|
||||||
|
empty_rows = MapSet.difference(MapSet.new(0..(ys |> Enum.max())), ys)
|
||||||
|
xs = coords |> Enum.map(pipe(elem(1))) |> MapSet.new()
|
||||||
|
empty_cols = MapSet.difference(MapSet.new(0..(xs |> Enum.max())), xs)
|
||||||
|
fn expansion ->
|
||||||
|
coords_ =
|
||||||
|
coords
|
||||||
|
|> Enum.map(fn {y, x} ->
|
||||||
|
{y + Enum.count(empty_rows, &(&1 < y)) * (expansion - 1),
|
||||||
|
x + Enum.count(empty_cols, &(&1 < x)) * (expansion - 1)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
coords_
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.flat_map(fn {c, i} -> coords_ |> Enum.drop(i + 1) |> Enum.map(&{c, &1}) end)
|
||||||
|
|> Enum.map(&dist/1)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp dist({{y1, x1}, {y2, x2}}) do
|
||||||
|
abs(x1 - x2) + abs(y1 - y2)
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
input.(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
input.(1_000_000)
|
||||||
|
end
|
||||||
|
end
|
||||||
93
lib/day12.ex
Normal file
93
lib/day12.ex
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
defmodule Aoc2023.Day12 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
defmodule Cache do
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
def start do
|
||||||
|
{:ok, pid} = GenServer.start_link(Cache, nil)
|
||||||
|
pid
|
||||||
|
end
|
||||||
|
|
||||||
|
def memoize(cache, key, fun) do
|
||||||
|
got = GenServer.call(cache, {:get, key})
|
||||||
|
if got == nil do
|
||||||
|
val = fun.(key)
|
||||||
|
GenServer.call(cache, {:set, key, val})
|
||||||
|
val
|
||||||
|
else
|
||||||
|
got
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(_) do
|
||||||
|
{:ok, %{}}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_call({:get, key}, _, state) do
|
||||||
|
case state do
|
||||||
|
%{^key => value} ->
|
||||||
|
{:reply, value, state}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:reply, nil, state}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_call({:set, key, value}, _, state) do
|
||||||
|
{:reply, nil, Map.put_new(state, key, value)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
input
|
||||||
|
|> lines
|
||||||
|
|> Enum.map(
|
||||||
|
pipe(
|
||||||
|
String.split()
|
||||||
|
|> then(fn [rec, blocks] ->
|
||||||
|
{rec |> String.to_charlist(),
|
||||||
|
blocks |> String.split(",") |> Enum.map(&String.to_integer/1)}
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp possibilities({rec, blocks}) do
|
||||||
|
loop = fn recur, arg ->
|
||||||
|
case arg do
|
||||||
|
{rec, []} -> if Enum.all?(rec, &(&1 in [??, ?.])) do 1 else 0 end
|
||||||
|
{rec, [block | blocks]} ->
|
||||||
|
# Expect rec to start with any number of ./?s up to the max allowing all remaining groups
|
||||||
|
# i.e. their lengths + an undamaged seperator
|
||||||
|
0..(length(rec) - Enum.sum(blocks) - length(blocks) - block)
|
||||||
|
|> Enum.map(fn i -> repeat('.', i) ++ repeat('#', block) ++ '.' end)
|
||||||
|
|> Enum.filter(pipe(Enum.zip_with(rec, &(&1 == &2 || &2 == ??)) |> Enum.all?))
|
||||||
|
|> Enum.map(&(recur.(recur, {Enum.drop(rec, length(&1)), blocks})))
|
||||||
|
|> Enum.sum
|
||||||
|
end
|
||||||
|
end
|
||||||
|
cache = Cache.start()
|
||||||
|
memoized = fn recur, arg -> Cache.memoize(cache, arg, fn arg -> loop.(recur, arg) end) end
|
||||||
|
memoized.(memoized, {rec, blocks})
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
input
|
||||||
|
|> Enum.map(pipe(possibilities()))
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
input
|
||||||
|
|> Enum.map(fn {rec, blocks} -> {
|
||||||
|
[rec] |> repeat(5) |> Enum.intersperse('?') |> Enum.flat_map(&id/1),
|
||||||
|
repeat(blocks, 5)
|
||||||
|
} end)
|
||||||
|
|> Enum.map(&possibilities/1)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
end
|
||||||
46
lib/day13.ex
Normal file
46
lib/day13.ex
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
defmodule Aoc2023.Day13 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
input
|
||||||
|
|> String.split("\n\n")
|
||||||
|
|> Enum.map(pipe(lines |> Enum.map(&String.to_charlist/1)))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp reflected_row(block, smudges) do
|
||||||
|
0..(length(block) - 2)
|
||||||
|
|> Enum.filter(fn i ->
|
||||||
|
Enum.zip_with(
|
||||||
|
Enum.take(block, i + 1) |> Enum.reverse() |> Enum.concat(),
|
||||||
|
Enum.drop(block, i + 1) |> Enum.concat(),
|
||||||
|
&Kernel.==/2
|
||||||
|
)
|
||||||
|
|> Enum.count(&(not &1))
|
||||||
|
|> Kernel.==(smudges)
|
||||||
|
end)
|
||||||
|
|> head
|
||||||
|
end
|
||||||
|
|
||||||
|
defp reflected_column(block, smudges) do
|
||||||
|
block |> transpose() |> reflected_row(smudges)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp reflection(block, smudges \\ 0) do
|
||||||
|
row = reflected_row(block, smudges)
|
||||||
|
if row != nil, do: {:h, row + 1}, else: {:v, reflected_column(block, smudges) + 1}
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
reflections = input |> Enum.map(&reflection/1)
|
||||||
|
|
||||||
|
(Keyword.get_values(reflections, :h) |> Enum.sum()) * 100 +
|
||||||
|
(Keyword.get_values(reflections, :v) |> Enum.sum())
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
reflections = input |> Enum.map(pipe(reflection(1)))
|
||||||
|
|
||||||
|
(Keyword.get_values(reflections, :h) |> Enum.sum()) * 100 +
|
||||||
|
(Keyword.get_values(reflections, :v) |> Enum.sum())
|
||||||
|
end
|
||||||
|
end
|
||||||
77
lib/day14.ex
Normal file
77
lib/day14.ex
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
defmodule Aoc2023.Day14 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
input
|
||||||
|
|> lines
|
||||||
|
|> Enum.map(&String.to_charlist/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp replace_many_at(lst, idxs, val) do
|
||||||
|
idxs |> Enum.reduce(lst, &List.replace_at(&2, &1, val))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp slide(grid) do
|
||||||
|
iter = fn recur, grid ->
|
||||||
|
case grid do
|
||||||
|
[last | [next | rest]] ->
|
||||||
|
idxs =
|
||||||
|
last
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.filter(pipe(elem(0) |> Kernel.==(?O)))
|
||||||
|
|> Enum.map(pipe(elem(1)))
|
||||||
|
|> Enum.filter(&(Enum.at(next, &1) == ?.))
|
||||||
|
|
||||||
|
[
|
||||||
|
last |> replace_many_at(idxs, ?.)
|
||||||
|
| recur.(recur, [next |> replace_many_at(idxs, ?O) | rest])
|
||||||
|
]
|
||||||
|
|
||||||
|
[last] ->
|
||||||
|
[last]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
loop = fn recur, grid ->
|
||||||
|
grid_ = iter.(iter, grid)
|
||||||
|
if grid_ == grid, do: grid, else: recur.(recur, grid_)
|
||||||
|
end
|
||||||
|
|
||||||
|
loop.(loop, grid |> Enum.reverse())
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cycle(grid) do
|
||||||
|
for _ <- 1..4, reduce: grid do
|
||||||
|
# reverse |> transpose is rotation clockwise, but slide already reverses
|
||||||
|
grid -> grid |> slide |> transpose
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cycles(grid, n \\ 1_000_000_000, {cache_to_n, cache_from_n} \\ {%{}, %{}}) when n > 0 do
|
||||||
|
rep_n = Map.get(cache_to_n, grid)
|
||||||
|
if rep_n do
|
||||||
|
n_ = rep_n - Integer.mod(n, rep_n - n)
|
||||||
|
cache_from_n |> Map.get(n_)
|
||||||
|
else
|
||||||
|
grid_ = cycle(grid)
|
||||||
|
cycles(grid_, n - 1, {Map.put(cache_to_n, grid, n), Map.put(cache_from_n, n, grid)})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
input
|
||||||
|
|> slide
|
||||||
|
|> Enum.with_index(1)
|
||||||
|
|> Enum.map(fn {row, i} -> Enum.count(row, &(&1 == ?O)) * i end)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
input
|
||||||
|
|> cycles()
|
||||||
|
|> Enum.reverse
|
||||||
|
|> Enum.with_index(1)
|
||||||
|
|> Enum.map(fn {row, i} -> Enum.count(row, &(&1 == ?O)) * i end)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
end
|
||||||
32
lib/day15.ex
Normal file
32
lib/day15.ex
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
defmodule Aoc2023.Day15 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
input |> String.split(",")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp hash(str) do
|
||||||
|
for <<char <- str>>, reduce: 0 do
|
||||||
|
acc -> Bitwise.band((acc + char) * 17, 255)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
input |> Enum.map(&hash/1) |> Enum.sum
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
for instr <- input, reduce: 0..255 |> Enum.map(&{&1, []}) |> Enum.into(%{}) do
|
||||||
|
boxes ->
|
||||||
|
[_, label, op, lens] = Regex.run(~r/([a-z]+)([-=])(\d*)/, instr)
|
||||||
|
box = hash(label)
|
||||||
|
label = String.to_atom(label)
|
||||||
|
case op do
|
||||||
|
"=" -> boxes |> Map.update!(box, &Keyword.update(&1, label, String.to_integer(lens), fn _ -> String.to_integer(lens) end))
|
||||||
|
"-" -> boxes |> Map.update!(box, &Keyword.delete(&1, label))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|> Enum.flat_map(fn {box, lenses} -> lenses |> Enum.with_index(1) |> Enum.map(fn {{_, focus}, i} -> focus * (box + 1) * i end) end)
|
||||||
|
|> Enum.sum
|
||||||
|
end
|
||||||
|
end
|
||||||
67
lib/day16.ex
Normal file
67
lib/day16.ex
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
defmodule Aoc2023.Day16 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
defp emissions(char, dir) do
|
||||||
|
case {char, dir} do
|
||||||
|
{?., dir} -> [dir]
|
||||||
|
{?-, {0, dir}} -> [{0, dir}]
|
||||||
|
{?-, {_, 0}} -> [{0, -1}, {0, 1}]
|
||||||
|
{?|, {dir, 0}} -> [{dir, 0}]
|
||||||
|
{?|, {0, _}} -> [{-1, 0}, {1, 0}]
|
||||||
|
{?/, {dir, 0}} -> [{0, -dir}]
|
||||||
|
{?/, {0, dir}} -> [{-dir, 0}]
|
||||||
|
{?\\, {dir, 0}} -> [{0, dir}]
|
||||||
|
{?\\, {0, dir}} -> [{dir, 0}]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
input
|
||||||
|
|> lines
|
||||||
|
|> Enum.map(&String.to_charlist/1)
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.flat_map(fn {row, y} ->
|
||||||
|
row |> Enum.with_index() |> Enum.map(fn {c, x} -> {{y, x}, c} end)
|
||||||
|
end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp run(input, heads) do
|
||||||
|
loop = fn recur, energized, heads ->
|
||||||
|
energized_ = MapSet.union(energized, heads |> MapSet.new())
|
||||||
|
|
||||||
|
heads_ =
|
||||||
|
heads
|
||||||
|
|> Enum.flat_map(fn {coord, dir} ->
|
||||||
|
char = Map.get(input, coord)
|
||||||
|
|
||||||
|
if char,
|
||||||
|
do:
|
||||||
|
emissions(char, dir)
|
||||||
|
|> Enum.map(&{add_vec(coord, &1), &1})
|
||||||
|
|> Enum.reject(&MapSet.member?(energized_, &1))
|
||||||
|
|> Enum.filter(&Map.has_key?(input, elem(&1, 0))),
|
||||||
|
else: []
|
||||||
|
end)
|
||||||
|
|
||||||
|
if length(heads_) > 0, do: recur.(recur, energized_, heads_), else: energized_
|
||||||
|
end
|
||||||
|
|
||||||
|
loop.(loop, MapSet.new(), heads) |> Enum.map(pipe(elem(0))) |> MapSet.new() |> MapSet.size()
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
run(input, [{{0, 0}, {0, 1}}])
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
{my, mx} = input |> Enum.map(pipe(elem(0))) |> Enum.max()
|
||||||
|
|
||||||
|
[
|
||||||
|
0..my |> Enum.map(&{{&1, 0}, {0, 1}}),
|
||||||
|
0..my |> Enum.map(&{{&1, mx}, {0, -1}}),
|
||||||
|
0..mx |> Enum.map(&{{0, &1}, {1, 0}}),
|
||||||
|
0..mx |> Enum.map(&{{my, &1}, {-1, 0}})
|
||||||
|
] |> Enum.concat |> Enum.map(&run(input, [&1])) |> Enum.max
|
||||||
|
end
|
||||||
|
end
|
||||||
167
lib/day17.ex
Normal file
167
lib/day17.ex
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
defmodule Aoc2023.Day17 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
defmodule PriorityQueue do
|
||||||
|
defstruct [:set]
|
||||||
|
def new(contents \\ []), do: %__MODULE__{set: :gb_sets.from_list(contents)}
|
||||||
|
|
||||||
|
def add(%__MODULE__{set: set}, elem, prio) do
|
||||||
|
%__MODULE__{set: :gb_sets.add({prio, elem}, set)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def size(%__MODULE__{set: set}) do
|
||||||
|
:gb_sets.size(set)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pop_min(%__MODULE__{set: set}) do
|
||||||
|
case :gb_sets.size(set) do
|
||||||
|
0 ->
|
||||||
|
{nil, new()}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{res, set} = :gb_sets.take_smallest(set)
|
||||||
|
{res, %__MODULE__{set: set}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defimpl Inspect do
|
||||||
|
import Inspect.Algebra
|
||||||
|
|
||||||
|
def inspect(%PriorityQueue{set: set}, opts) do
|
||||||
|
concat(["#PriorityQueue.new(", to_doc(:gb_sets.to_list(set), opts), ")"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
lines = input |> lines
|
||||||
|
|
||||||
|
{
|
||||||
|
{lines |> length() |> Kernel.-(1), lines |> head |> String.length() |> Kernel.-(1)},
|
||||||
|
lines
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.flat_map(fn {row, y} ->
|
||||||
|
row
|
||||||
|
|> String.to_charlist()
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.map(fn {cost, x} -> {{y, x}, String.to_integer(<<cost>>)} end)
|
||||||
|
end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp a_star(starts, neighbors, heuristic, is_goal) do
|
||||||
|
loop = fn recur, explored, frontier ->
|
||||||
|
{explored, frontier}
|
||||||
|
{{_, current}, frontier} = PriorityQueue.pop_min(frontier)
|
||||||
|
|
||||||
|
if is_goal.(current) do
|
||||||
|
{explored, current}
|
||||||
|
else
|
||||||
|
cost_to_here = Map.get(explored, current) |> elem(1)
|
||||||
|
|
||||||
|
{explored, frontier} =
|
||||||
|
for {neighbor, cost} <- neighbors.(current), reduce: {explored, frontier} do
|
||||||
|
{explored, frontier} ->
|
||||||
|
cost_ = cost_to_here + cost
|
||||||
|
|
||||||
|
if Map.get(explored, neighbor, {nil, cost_}) |> elem(1) < cost_ do
|
||||||
|
{explored, frontier}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Map.put(explored, neighbor, {current, cost_}),
|
||||||
|
PriorityQueue.add(frontier, neighbor, cost_ + heuristic.(neighbor))
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
recur.(recur, explored, frontier)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
{explored, goal} =
|
||||||
|
loop.(
|
||||||
|
loop,
|
||||||
|
starts |> Enum.map(&{&1, {nil, 0}}) |> Enum.into(%{}),
|
||||||
|
starts |> Enum.map(&{0, &1}) |> PriorityQueue.new()
|
||||||
|
)
|
||||||
|
|
||||||
|
# This doesn't include the starting point
|
||||||
|
path_find = fn recur, point, path ->
|
||||||
|
case Map.get(explored, point) do
|
||||||
|
nil -> path
|
||||||
|
{nil, _} -> path
|
||||||
|
{prev, _} -> recur.(recur, prev, [point | path])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
path_find.(path_find, goal, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1({{my, mx}, grid}) do
|
||||||
|
# Nodes are y, x, dir, straight length
|
||||||
|
a_star(
|
||||||
|
[{0, 0, :d, 0}, {0, 0, :r, 0}],
|
||||||
|
fn {y, x, dir, len} ->
|
||||||
|
case dir do
|
||||||
|
:u ->
|
||||||
|
[{y, x - 1, :l, 0}, {y, x + 1, :r, 0}] ++
|
||||||
|
if len < 2, do: [{y + 1, x, dir, len + 1}], else: []
|
||||||
|
|
||||||
|
:d ->
|
||||||
|
[{y, x - 1, :l, 0}, {y, x + 1, :r, 0}] ++
|
||||||
|
if len < 2, do: [{y - 1, x, dir, len + 1}], else: []
|
||||||
|
|
||||||
|
:l ->
|
||||||
|
[{y + 1, x, :u, 0}, {y - 1, x, :d, 0}] ++
|
||||||
|
if len < 2, do: [{y, x - 1, dir, len + 1}], else: []
|
||||||
|
|
||||||
|
:r ->
|
||||||
|
[{y + 1, x, :u, 0}, {y - 1, x, :d, 0}] ++
|
||||||
|
if len < 2, do: [{y, x + 1, dir, len + 1}], else: []
|
||||||
|
end
|
||||||
|
|> Enum.filter(fn {y, x, _, _} -> y in 0..my and x in 0..mx end)
|
||||||
|
|> Enum.map(fn {y, x, _, _} = pos -> {pos, Map.get(grid, {y, x})} end)
|
||||||
|
end,
|
||||||
|
fn {y, x, _, _} -> abs(y - my) + abs(x - mx) end,
|
||||||
|
fn {y, x, _, _} -> {y, x} == {my, mx} end
|
||||||
|
)
|
||||||
|
|> Enum.map(fn {y, x, _, _} -> Map.get(grid, {y, x}) end)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2({{my, mx}, grid}) do
|
||||||
|
# Nodes are y, x, dir, straight length
|
||||||
|
a_star(
|
||||||
|
[{0, 0, :r, 0}, {0, 0, :d, 0}, {0, 0, :u, 0}, {0, 0, :l, 0}],
|
||||||
|
fn {y, x, dir, len} ->
|
||||||
|
(if(len < 9,
|
||||||
|
do:
|
||||||
|
case dir do
|
||||||
|
:u -> [{y + 1, x, :u, len + 1}]
|
||||||
|
:d -> [{y - 1, x, :d, len + 1}]
|
||||||
|
:l -> [{y, x - 1, :l, len + 1}]
|
||||||
|
:r -> [{y, x + 1, :r, len + 1}]
|
||||||
|
end,
|
||||||
|
else: []
|
||||||
|
) ++
|
||||||
|
if(len > 2,
|
||||||
|
do:
|
||||||
|
case dir do
|
||||||
|
:u -> [{y, x - 1, :l, 0}, {y, x + 1, :r, 0}]
|
||||||
|
:d -> [{y, x - 1, :l, 0}, {y, x + 1, :r, 0}]
|
||||||
|
:l -> [{y + 1, x, :u, 0}, {y - 1, x, :d, 0}]
|
||||||
|
:r -> [{y + 1, x, :u, 0}, {y - 1, x, :d, 0}]
|
||||||
|
end,
|
||||||
|
else: []
|
||||||
|
))
|
||||||
|
|> Enum.filter(fn {y, x, _, _} -> y in 0..my and x in 0..mx end)
|
||||||
|
|> Enum.map(fn {y, x, _, _} = pos -> {pos, Map.get(grid, {y, x})} end)
|
||||||
|
end,
|
||||||
|
fn {y, x, _, _} -> abs(y - my) + abs(x - mx) end,
|
||||||
|
fn {y, x, _, len} -> {y, x} == {my, mx} and len > 2 end
|
||||||
|
)
|
||||||
|
|> Enum.map(fn {y, x, _, _} -> Map.get(grid, {y, x}) end)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
end
|
||||||
67
lib/day18.ex
Normal file
67
lib/day18.ex
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
defmodule Aoc2023.Day18 do
|
||||||
|
require Integer
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
input
|
||||||
|
|> lines
|
||||||
|
|> Enum.map(fn line ->
|
||||||
|
[dir, n, color] =
|
||||||
|
Regex.run(~r/^([RDLU]) (\d+) \(#([0-9a-f]{6})\)$/, line, capture: :all_but_first)
|
||||||
|
|
||||||
|
{
|
||||||
|
dir |> String.to_charlist() |> head,
|
||||||
|
n |> String.to_integer(),
|
||||||
|
color
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp simulate({pos, dist, points}, {dir, n}) do
|
||||||
|
p =
|
||||||
|
case dir do
|
||||||
|
:u -> {-n, 0}
|
||||||
|
:d -> {n, 0}
|
||||||
|
:r -> {0, n}
|
||||||
|
:l -> {0, -n}
|
||||||
|
end
|
||||||
|
|> add_vec(pos)
|
||||||
|
|
||||||
|
{p, dist + n, [p | points]}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp area(perim, points),
|
||||||
|
# Shoelace formula
|
||||||
|
do:
|
||||||
|
Stream.zip(points, points |> Stream.cycle() |> Stream.drop(1))
|
||||||
|
|> Stream.map(fn {{y, x}, {y1, x1}} -> x * y1 - x1 * y end)
|
||||||
|
|> Enum.sum()
|
||||||
|
|> abs
|
||||||
|
|> then(&((&1 + perim) / 2))
|
||||||
|
|> trunc
|
||||||
|
|
||||||
|
defp run(instrs) do
|
||||||
|
{_, dist, points} = instrs |> Enum.reduce({{0, 0}, 2, [{0, 0}]}, &simulate(&2, &1))
|
||||||
|
area(dist, points)
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
run(for {dir, n, _} <- input, do: {case dir do
|
||||||
|
?U -> :u
|
||||||
|
?D -> :d
|
||||||
|
?L -> :l
|
||||||
|
?R -> :r
|
||||||
|
end, n})
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
run(for {_, _, <<len::binary-size(5), dir::integer>>} <- input do
|
||||||
|
{case dir do
|
||||||
|
?0 -> :r
|
||||||
|
?1 -> :d
|
||||||
|
?2 -> :l
|
||||||
|
?3 -> :u
|
||||||
|
end, String.to_integer(len, 16)}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
52
lib/day2.ex
Normal file
52
lib/day2.ex
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
defmodule Aoc2023.Day2 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
input
|
||||||
|
|> String.split("\n")
|
||||||
|
|> Enum.map(pipe(
|
||||||
|
String.split(": ")
|
||||||
|
|> then(fn ["Game " <> id, rounds] ->
|
||||||
|
{String.to_integer(id),
|
||||||
|
rounds
|
||||||
|
|> String.split("; ")
|
||||||
|
|> Enum.map(pipe(
|
||||||
|
String.split(", ")
|
||||||
|
|> Enum.map(pipe(
|
||||||
|
String.split(" ")
|
||||||
|
|> then(fn [n, color] ->
|
||||||
|
{String.to_existing_atom(color), String.to_integer(n)}
|
||||||
|
end)
|
||||||
|
))
|
||||||
|
))}
|
||||||
|
end)
|
||||||
|
))
|
||||||
|
|> Enum.into(%{})
|
||||||
|
end
|
||||||
|
|
||||||
|
@part1_available [
|
||||||
|
red: 12, green: 13, blue: 14
|
||||||
|
]
|
||||||
|
def part1(input) do
|
||||||
|
input
|
||||||
|
|> Enum.filter(pipe(
|
||||||
|
elem(1)
|
||||||
|
|> Enum.all?(pipe(
|
||||||
|
Enum.all?(fn {color, num} -> Keyword.get(@part1_available, color, 0) >= num end)
|
||||||
|
))
|
||||||
|
))
|
||||||
|
|> Enum.map(pipe(elem(0)))
|
||||||
|
|> Enum.sum
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
input
|
||||||
|
|> Enum.map(pipe(
|
||||||
|
elem(1)
|
||||||
|
|> Enum.reduce(&(Keyword.merge(&1, &2, fn _, m1, m2 -> max(m1, m2) end)))
|
||||||
|
|> Enum.map(pipe(elem(1)))
|
||||||
|
|> Enum.product
|
||||||
|
))
|
||||||
|
|> Enum.sum
|
||||||
|
end
|
||||||
|
end
|
||||||
110
lib/day3.ex
Normal file
110
lib/day3.ex
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
defmodule Aoc2023.Day3 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
@symbols [
|
||||||
|
?$,
|
||||||
|
?%,
|
||||||
|
?&,
|
||||||
|
?*,
|
||||||
|
?+,
|
||||||
|
?-,
|
||||||
|
?/,
|
||||||
|
?=,
|
||||||
|
?@,
|
||||||
|
?#
|
||||||
|
]
|
||||||
|
|
||||||
|
defp adjacents({y, x, len}, grid) do
|
||||||
|
[
|
||||||
|
{y, x - 1},
|
||||||
|
{y, x + len}
|
||||||
|
| for(y <- [y - 1, y + 1], x <- (x - 1)..(x + len), do: {y, x})
|
||||||
|
]
|
||||||
|
|> Enum.filter(fn {y, x} ->
|
||||||
|
0 <= y and y < length(grid) and 0 <= x and x < length(Enum.at(grid, y))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp adjacents({y, x}, grid), do: adjacents({y, x, 1}, grid)
|
||||||
|
|
||||||
|
defp get({y, x}, grid) do
|
||||||
|
grid |> Enum.at(y) |> Enum.at(x)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get({y, x, len}, grid) do
|
||||||
|
x..(x + len - 1) |> Enum.map(&get({y, &1}, grid))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp contains({y, x}, numbers) do
|
||||||
|
numbers
|
||||||
|
|> Enum.filter(fn {ny, nx, nlen} -> ny == y and nx <= x and x < nx + nlen end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp number(num, grid) do
|
||||||
|
num
|
||||||
|
|> get(grid)
|
||||||
|
|> Enum.map(&(&1 - ?0))
|
||||||
|
|> Integer.undigits()
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
grid =
|
||||||
|
input
|
||||||
|
|> String.split("\n")
|
||||||
|
|> Enum.map(&String.to_charlist/1)
|
||||||
|
|
||||||
|
{
|
||||||
|
grid,
|
||||||
|
grid
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.flat_map(fn {row, y} ->
|
||||||
|
row
|
||||||
|
|> runs(&(?0 <= &1 && &1 <= ?9))
|
||||||
|
|> Enum.map(fn {x, len} -> {y, x, len} end)
|
||||||
|
end)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1({grid, numbers}) do
|
||||||
|
numbers
|
||||||
|
|> Enum.map(id &&& adjacents(grid))
|
||||||
|
|> Enum.filter(
|
||||||
|
pipe(
|
||||||
|
elem(1)
|
||||||
|
|> Enum.any?(&(get(&1, grid) in @symbols))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> Enum.map(
|
||||||
|
pipe(
|
||||||
|
elem(0)
|
||||||
|
|> number(grid)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2({grid, numbers}) do
|
||||||
|
grid
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.flat_map(fn {row, y} ->
|
||||||
|
row
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.filter(&(elem(&1, 0) == ?*))
|
||||||
|
|> Enum.map(&{y, elem(&1, 1)})
|
||||||
|
|> Enum.map(id &&& adjacents(grid))
|
||||||
|
|> Enum.map(pipe(
|
||||||
|
elem(1)
|
||||||
|
|> Enum.flat_map(pipe(contains(numbers)))
|
||||||
|
|> Enum.uniq()
|
||||||
|
))
|
||||||
|
|> Enum.filter(pipe(
|
||||||
|
length()
|
||||||
|
|> then(&(&1 == 2)))
|
||||||
|
)
|
||||||
|
|> Enum.map(pipe(
|
||||||
|
Enum.map(pipe(number(grid))) |> Enum.product
|
||||||
|
))
|
||||||
|
end)
|
||||||
|
|> Enum.sum
|
||||||
|
end
|
||||||
|
end
|
||||||
39
lib/day4.ex
Normal file
39
lib/day4.ex
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
defmodule Aoc2023.Day4 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
defp numbers(values) do
|
||||||
|
values |> String.split() |> Enum.map(&String.to_integer/1) |> MapSet.new()
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
input
|
||||||
|
|> lines
|
||||||
|
|> Enum.map(
|
||||||
|
&Regex.run(~r/Card\s+(?<id>\d+): (?<winning>(\s*\d+)+) \| (?<values>(\s*\d+)+)/, &1,
|
||||||
|
capture: ["id", "winning", "values"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> Enum.map(fn [id, winning, values] ->
|
||||||
|
{String.to_integer(id),
|
||||||
|
MapSet.intersection(numbers(winning), numbers(values)) |> MapSet.size()}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
for {_, count} <- input,
|
||||||
|
count > 0 do
|
||||||
|
2 ** (count - 1)
|
||||||
|
end
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
for {id, count} <- input,
|
||||||
|
id_ <- (id + 1)..(id + count)//1,
|
||||||
|
reduce: input |> Enum.map(elem(0) &&& const(1)) |> Enum.into(%{}) do
|
||||||
|
acc -> Map.update!(acc, id_, &(&1 + Map.get(acc, id)))
|
||||||
|
end
|
||||||
|
|> Enum.map(pipe(elem(1)))
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
end
|
||||||
84
lib/day5.ex
Normal file
84
lib/day5.ex
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
defmodule Aoc2023.Day5 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
defp numbers(str), do: str |> String.split() |> Enum.map(&String.to_integer/1)
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
[seeds | maps] =
|
||||||
|
input
|
||||||
|
|> String.split("\n\n")
|
||||||
|
|
||||||
|
{
|
||||||
|
seeds |> String.split(": ") |> Enum.at(1) |> numbers,
|
||||||
|
maps
|
||||||
|
|> Enum.map(pipe(
|
||||||
|
String.split("\n")
|
||||||
|
|> tail
|
||||||
|
|> Enum.map(pipe(
|
||||||
|
numbers
|
||||||
|
|> List.to_tuple()
|
||||||
|
|> then(fn {dst, src, len} -> {src .. (src + len - 1), dst - src} end)
|
||||||
|
))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp split_range_on(r1, r2) do
|
||||||
|
cond do
|
||||||
|
Range.disjoint?(r1, r2) -> {[r1], []}
|
||||||
|
r1.first < r2.first && r1.last < r2.last ->
|
||||||
|
{a, b} = Range.split(r1, r2.first - r1.first)
|
||||||
|
{[a], [b]}
|
||||||
|
r1.first > r2.first && r1.last > r2.last ->
|
||||||
|
{a, b} = Range.split(r1, r1.first - r2.first)
|
||||||
|
{[b], [a]}
|
||||||
|
r1.first >= r2.first && r1.last <= r2.last ->
|
||||||
|
{[], [r1]}
|
||||||
|
r1.first <= r2.first && r1.last >= r2.last ->
|
||||||
|
{pre, rest} = Range.split(r1, r2.first - r1.first)
|
||||||
|
{mid, last} = Range.split(rest, r2.last - rest.first)
|
||||||
|
{[pre, last], [mid]}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp map(loc, map) do
|
||||||
|
{unmapped, mapped} = for {src, offset} <- map,
|
||||||
|
reduce: {[loc], []} do
|
||||||
|
{unmapped, mapped} ->
|
||||||
|
for range <- unmapped,
|
||||||
|
reduce: {[], mapped} do
|
||||||
|
{unmapped_, mapped_} ->
|
||||||
|
{unmapped, mapped} = split_range_on(range, src)
|
||||||
|
{
|
||||||
|
unmapped_ ++ unmapped,
|
||||||
|
mapped_ ++ Enum.map(mapped, &(Range.shift(&1, offset)))
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
unmapped ++ mapped
|
||||||
|
end
|
||||||
|
|
||||||
|
defp run(seeds, maps) do
|
||||||
|
for seed <- seeds do
|
||||||
|
for map <- maps, reduce: [seed] do
|
||||||
|
loc ->
|
||||||
|
loc
|
||||||
|
|> Enum.flat_map(pipe(map(map)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|> Enum.flat_map(&id/1)
|
||||||
|
|> Enum.min_by(&(&1.first))
|
||||||
|
|> then(&(&1.first))
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1({seeds, maps}) do
|
||||||
|
run(seeds |> Enum.map(&(&1..&1)), maps)
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2({seeds, maps}) do
|
||||||
|
run(
|
||||||
|
seeds |> Enum.chunk_every(2) |> Enum.map(fn [s, len] -> s..(s + len - 1) end),
|
||||||
|
maps
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
37
lib/day6.ex
Normal file
37
lib/day6.ex
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
defmodule Aoc2023.Day6 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
input
|
||||||
|
|> lines
|
||||||
|
|> Enum.map(pipe(
|
||||||
|
String.split(":")
|
||||||
|
|> Enum.at(1)
|
||||||
|
))
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
input
|
||||||
|
|> Enum.map(pipe(
|
||||||
|
String.split()
|
||||||
|
|> Enum.map(&String.to_integer/1)
|
||||||
|
))
|
||||||
|
|> Enum.zip()
|
||||||
|
|> Enum.map(fn {time, dist} ->
|
||||||
|
1..time
|
||||||
|
|> Enum.map(&(&1 * (time - &1)))
|
||||||
|
|> Enum.filter(&(&1 > dist))
|
||||||
|
|> length
|
||||||
|
end)
|
||||||
|
|> Enum.product()
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
[time, dist] = input
|
||||||
|
|> Enum.map(pipe(String.replace(~r(\s+), "") |> String.to_integer()))
|
||||||
|
1..(time)
|
||||||
|
|> Stream.map(&(&1 * (time - &1)))
|
||||||
|
|> Stream.filter(&(&1 > dist))
|
||||||
|
|> Enum.count
|
||||||
|
end
|
||||||
|
end
|
||||||
66
lib/day7.ex
Normal file
66
lib/day7.ex
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
defmodule Aoc2023.Day7 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
def hand_type(values) do
|
||||||
|
values
|
||||||
|
|> Enum.frequencies()
|
||||||
|
|> then(fn freq ->
|
||||||
|
{jokers, freq_} = freq |> Map.pop(0, 0)
|
||||||
|
common = freq_ |> Enum.max_by(pipe(elem(1)), fn -> {1, nil} end) |> elem(0)
|
||||||
|
freq_ |> Map.update(common, jokers, &(&1 + jokers))
|
||||||
|
end)
|
||||||
|
|> Enum.map(pipe(elem(1)))
|
||||||
|
|> Enum.sort(:desc)
|
||||||
|
|> case do
|
||||||
|
[5] -> 6
|
||||||
|
[4, 1] -> 5
|
||||||
|
[3, 2] -> 4
|
||||||
|
[3, 1, 1] -> 3
|
||||||
|
[2, 2, 1] -> 2
|
||||||
|
[2, 1, 1, 1] -> 1
|
||||||
|
[1, 1, 1, 1, 1] -> 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
fn rankings ->
|
||||||
|
input
|
||||||
|
|> lines
|
||||||
|
|> Enum.map(&String.split/1)
|
||||||
|
|> Enum.map(fn [hand, bid] ->
|
||||||
|
{
|
||||||
|
hand |> String.to_charlist() |> Enum.map(&Map.get(rankings, &1)),
|
||||||
|
bid |> String.to_integer()
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|> Enum.sort_by(elem(0) |> hand_type &&& elem(0), :asc)
|
||||||
|
|> Enum.with_index(1)
|
||||||
|
|> Enum.map(fn {{_, bid}, index} -> bid * index end)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@ranking [
|
||||||
|
?2,
|
||||||
|
?3,
|
||||||
|
?4,
|
||||||
|
?5,
|
||||||
|
?6,
|
||||||
|
?7,
|
||||||
|
?8,
|
||||||
|
?9,
|
||||||
|
?T,
|
||||||
|
?J,
|
||||||
|
?Q,
|
||||||
|
?K,
|
||||||
|
?A,
|
||||||
|
] |> Enum.with_index(1) |> Map.new
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
input.(@ranking)
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
input.(@ranking |> Map.put(?J, 0))
|
||||||
|
end
|
||||||
|
end
|
||||||
39
lib/day8.ex
Normal file
39
lib/day8.ex
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
defmodule Aoc2023.Day8 do
|
||||||
|
require Integer
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
input
|
||||||
|
|> String.split("\n\n")
|
||||||
|
|> then(fn [directions, map] ->
|
||||||
|
{
|
||||||
|
directions |> String.to_charlist() |> Enum.map(&%{?L => 0, ?R => 1}[&1]),
|
||||||
|
map
|
||||||
|
|> lines
|
||||||
|
|> Enum.map(&Regex.run(~r/(\w+) = \((\w+), (\w+)\)/, &1, capture: :all_but_first))
|
||||||
|
|> Enum.map(fn [src, dst1, dst2] -> {src, {dst1, dst2}} end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp run({directions, map}, node, filter) do
|
||||||
|
directions
|
||||||
|
|> Stream.cycle()
|
||||||
|
|> Stream.scan(node, fn dir, pos -> map[pos] |> elem(dir) end)
|
||||||
|
|> Enum.find_index(filter)
|
||||||
|
|> Kernel.+(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1({directions, map}) do
|
||||||
|
if map["AAA"], do: run({directions, map}, "AAA", &(&1 == "ZZZ"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2({directions, map}) do
|
||||||
|
map
|
||||||
|
|> Enum.map(pipe(elem(0)))
|
||||||
|
|> Enum.filter(pipe(String.ends_with?("A")))
|
||||||
|
|> Enum.map(&run({directions, map}, &1, pipe(String.ends_with?("Z"))))
|
||||||
|
|> lcm
|
||||||
|
end
|
||||||
|
end
|
||||||
28
lib/day9.ex
Normal file
28
lib/day9.ex
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
defmodule Aoc2023.Day9 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
defp prediction(lst) do
|
||||||
|
case lst |> Enum.uniq() do
|
||||||
|
[val] -> val
|
||||||
|
_ -> List.last(lst) + prediction(Enum.zip_with(lst, Enum.drop(lst, 1), &(&2 - &1)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
input
|
||||||
|
|> lines
|
||||||
|
|> Enum.map(pipe(String.split() |> Enum.map(&String.to_integer/1)))
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1(input) do
|
||||||
|
input
|
||||||
|
|> Enum.map(&prediction/1)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2(input) do
|
||||||
|
input
|
||||||
|
|> Enum.map(pipe(Enum.reverse() |> prediction))
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
end
|
||||||
144
lib/mix_tasks.ex
144
lib/mix_tasks.ex
@@ -3,14 +3,34 @@ defmodule Mix.Tasks.Aoc do
|
|||||||
import Aoc2023.Common
|
import Aoc2023.Common
|
||||||
|
|
||||||
defp module(1), do: Aoc2023.Day1
|
defp module(1), do: Aoc2023.Day1
|
||||||
|
defp module(2), do: Aoc2023.Day2
|
||||||
|
defp module(3), do: Aoc2023.Day3
|
||||||
|
defp module(4), do: Aoc2023.Day4
|
||||||
|
defp module(5), do: Aoc2023.Day5
|
||||||
|
defp module(6), do: Aoc2023.Day6
|
||||||
|
defp module(7), do: Aoc2023.Day7
|
||||||
|
defp module(8), do: Aoc2023.Day8
|
||||||
|
defp module(9), do: Aoc2023.Day9
|
||||||
|
defp module(10), do: Aoc2023.Day10
|
||||||
|
defp module(11), do: Aoc2023.Day11
|
||||||
|
defp module(12), do: Aoc2023.Day12
|
||||||
|
defp module(13), do: Aoc2023.Day13
|
||||||
|
defp module(14), do: Aoc2023.Day14
|
||||||
|
defp module(15), do: Aoc2023.Day15
|
||||||
|
defp module(16), do: Aoc2023.Day16
|
||||||
|
defp module(17), do: Aoc2023.Day17
|
||||||
|
defp module(18), do: Aoc2023.Day18
|
||||||
# [MODULE INSERTION POINT]
|
# [MODULE INSERTION POINT]
|
||||||
|
|
||||||
defp base_dir(), do: System.get_env("AOC_BASE")
|
defp base_dir(), do: System.get_env("AOC_BASE")
|
||||||
|
|
||||||
defp tests(day) do
|
defp tests(day) do
|
||||||
case File.ls(tests_dir(day)) do
|
case File.ls(tests_dir(day)) do
|
||||||
{:ok, paths} -> paths |> Enum.filter(&(not String.contains?(&1, ".")))
|
{:ok, paths} ->
|
||||||
{:error, e} -> dbg(e)
|
paths |> Enum.filter(&(not String.contains?(&1, "."))) |> Enum.sort()
|
||||||
|
|
||||||
|
{:error, e} ->
|
||||||
|
dbg(e)
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -65,10 +85,10 @@ defmodule Mix.Tasks.Aoc do
|
|||||||
input
|
input
|
||||||
end
|
end
|
||||||
|
|
||||||
def part1(input) do
|
def part1(_input) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def part2(input) do
|
def part2(_input) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
""")
|
""")
|
||||||
@@ -92,10 +112,40 @@ defmodule Mix.Tasks.Aoc do
|
|||||||
create_file("#{tests_dir(day)}#{last + 1}", IO.read(:stdio, :eof))
|
create_file("#{tests_dir(day)}#{last + 1}", IO.read(:stdio, :eof))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp run(day, ["test", "expected", test, part, value]) do
|
||||||
|
create_file("#{tests_dir(day)}#{test}.#{part}", value)
|
||||||
|
end
|
||||||
|
|
||||||
defp run(day, ["test", "expected", test, part]) do
|
defp run(day, ["test", "expected", test, part]) do
|
||||||
create_file("#{tests_dir(day)}#{test}.#{part}", IO.read(:stdio, :eof))
|
create_file("#{tests_dir(day)}#{test}.#{part}", IO.read(:stdio, :eof))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp run(day, ["test", "watch"]) do
|
||||||
|
{:ok, pid} = Mix.Tasks.Aoc.Watcher.start_link({[dirs: ["lib/", "tests/day#{day}/"]], self()})
|
||||||
|
Process.monitor(pid)
|
||||||
|
|
||||||
|
loop = fn loop ->
|
||||||
|
IO.puts(IO.ANSI.clear() <> IO.ANSI.cursor(0, 0))
|
||||||
|
IO.puts("At #{DateTime.utc_now(:second)}:")
|
||||||
|
System.shell("mix compile")
|
||||||
|
:code.purge(module(day))
|
||||||
|
:code.purge(Aoc2023.Common)
|
||||||
|
:code.load_file(module(day))
|
||||||
|
:code.load_file(Aoc2023.Common)
|
||||||
|
|
||||||
|
spawn(fn ->
|
||||||
|
run(day, ["test"])
|
||||||
|
end)
|
||||||
|
|
||||||
|
receive do
|
||||||
|
:update -> loop.(loop)
|
||||||
|
{:DOWN, _, :process, ^pid, _} -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
loop.(loop)
|
||||||
|
end
|
||||||
|
|
||||||
defp run(day, ["test"]) do
|
defp run(day, ["test"]) do
|
||||||
mod = module(day)
|
mod = module(day)
|
||||||
tests = tests(day)
|
tests = tests(day)
|
||||||
@@ -104,29 +154,27 @@ defmodule Mix.Tasks.Aoc do
|
|||||||
|> Enum.map(&test(day, &1))
|
|> Enum.map(&test(day, &1))
|
||||||
|> Enum.map(fn {test, input, p1e, p2e} ->
|
|> Enum.map(fn {test, input, p1e, p2e} ->
|
||||||
parsed = mod.parse(input)
|
parsed = mod.parse(input)
|
||||||
p1 = mod.part1(parsed)
|
|
||||||
|
|
||||||
if p1e do
|
result = fn part, got, expected ->
|
||||||
if stringify(p1) != p1e do
|
cond do
|
||||||
IO.puts("Failed at #{test}.1: expected #{p1e}, got:")
|
stringify(got) == expected ->
|
||||||
dbg(p1)
|
IO.puts(IO.ANSI.format([:green, "Test #{test}.#{part} succeeded (#{expected})"]))
|
||||||
|
|
||||||
|
expected ->
|
||||||
|
IO.puts(
|
||||||
|
IO.ANSI.format([
|
||||||
|
:red,
|
||||||
|
"Test #{test}.#{part} failed: expected #{expected}, got #{stringify(got)}"
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
true ->
|
||||||
|
IO.puts("Test #{test}.#{part}: #{stringify(got)}")
|
||||||
end
|
end
|
||||||
else
|
|
||||||
IO.puts("Test #{test}.1")
|
|
||||||
dbg(p1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
p2 = mod.part2(parsed)
|
result.(1, mod.part1(parsed), p1e)
|
||||||
|
result.(2, mod.part2(parsed), p2e)
|
||||||
if p2e do
|
|
||||||
if stringify(p2) != p2e do
|
|
||||||
IO.puts("Failed at #{test}.2: expected #{p2e}, got:")
|
|
||||||
dbg(p2)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
IO.puts("Test #{test}.2")
|
|
||||||
dbg(p2)
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -135,6 +183,30 @@ defmodule Mix.Tasks.Aoc do
|
|||||||
run_file(day, "#{base_dir()}/inputs/day#{day}")
|
run_file(day, "#{base_dir()}/inputs/day#{day}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp run(day, ["run", "submit", part]) do
|
||||||
|
run(day, ["fetch"])
|
||||||
|
mod = module(day)
|
||||||
|
parsed = mod.parse(read("#{base_dir()}/inputs/day#{day}"))
|
||||||
|
|
||||||
|
val =
|
||||||
|
case part do
|
||||||
|
"1" -> mod.part1(parsed)
|
||||||
|
"2" -> mod.part2(parsed)
|
||||||
|
end
|
||||||
|
|
||||||
|
HTTPoison.start()
|
||||||
|
|
||||||
|
resp =
|
||||||
|
HTTPoison.post!(
|
||||||
|
"https://adventofcode.com/2023/day/#{day}/answer",
|
||||||
|
"level=#{part}&answer=#{stringify(val)}",
|
||||||
|
"user-agent": "aoc-ex by ben@soroos.net",
|
||||||
|
cookie: "session=#{System.get_env("AOC_SESSION")}",
|
||||||
|
"content-type": "application/x-www-form-urlencoded"
|
||||||
|
).body
|
||||||
|
Regex.named_captures(~r[<main>(?<main>.*)</main>]is, resp)["main"] |> IO.puts
|
||||||
|
end
|
||||||
|
|
||||||
defp run(day, ["run", "-"]) do
|
defp run(day, ["run", "-"]) do
|
||||||
run_file(day)
|
run_file(day)
|
||||||
end
|
end
|
||||||
@@ -158,4 +230,30 @@ defmodule Mix.Tasks.Aoc do
|
|||||||
defp run_file(day, file \\ nil) do
|
defp run_file(day, file \\ nil) do
|
||||||
module(day).main(if file, do: read(file), else: IO.read(:stdio, :eof))
|
module(day).main(if file, do: read(file), else: IO.read(:stdio, :eof))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defmodule Watcher do
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
def start_link(args) do
|
||||||
|
GenServer.start_link(__MODULE__, args)
|
||||||
|
end
|
||||||
|
|
||||||
|
def init({args, callback}) do
|
||||||
|
{:ok, watcher_pid} = FileSystem.start_link(args)
|
||||||
|
FileSystem.subscribe(watcher_pid)
|
||||||
|
{:ok, %{watcher_pid: watcher_pid, callback: callback}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info(
|
||||||
|
{:file_event, watcher_pid, {_path, _events}},
|
||||||
|
%{watcher_pid: watcher_pid, callback: callback} = state
|
||||||
|
) do
|
||||||
|
send(callback, :update)
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info({:file_event, _watcher_pid, :stop}, state) do
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
3
mix.exs
3
mix.exs
@@ -19,7 +19,8 @@ defmodule Aoc2023.MixProject do
|
|||||||
|
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:httpoison, "~> 2.0"}
|
httpoison: "~> 2.0",
|
||||||
|
file_system: "~> 1.0"
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
1
mix.lock
1
mix.lock
@@ -1,5 +1,6 @@
|
|||||||
%{
|
%{
|
||||||
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
||||||
|
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
|
||||||
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
|
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
|
||||||
"httpoison": {:hex, :httpoison, "2.2.1", "87b7ed6d95db0389f7df02779644171d7319d319178f6680438167d7b69b1f3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "51364e6d2f429d80e14fe4b5f8e39719cacd03eb3f9a9286e61e216feac2d2df"},
|
"httpoison": {:hex, :httpoison, "2.2.1", "87b7ed6d95db0389f7df02779644171d7319d319178f6680438167d7b69b1f3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "51364e6d2f429d80e14fe4b5f8e39719cacd03eb3f9a9286e61e216feac2d2df"},
|
||||||
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
||||||
|
|||||||
@@ -5,10 +5,11 @@
|
|||||||
elixir,
|
elixir,
|
||||||
elixir-ls,
|
elixir-ls,
|
||||||
inotify-tools,
|
inotify-tools,
|
||||||
|
entr,
|
||||||
}:
|
}:
|
||||||
mkShell {
|
mkShell {
|
||||||
packages =
|
packages =
|
||||||
[elixir elixir-ls]
|
[elixir elixir-ls entr]
|
||||||
++ lib.lists.optional (pkgs.system == "x86_64-linux") inotify-tools
|
++ lib.lists.optional (pkgs.system == "x86_64-linux") inotify-tools
|
||||||
++ lib.lists.optionals (pkgs.system == "aarch64-darwin") (with pkgs.darwin.apple_sdk.frameworks; [
|
++ lib.lists.optionals (pkgs.system == "aarch64-darwin") (with pkgs.darwin.apple_sdk.frameworks; [
|
||||||
CoreFoundation
|
CoreFoundation
|
||||||
|
|||||||
5
tests/day10/1
Normal file
5
tests/day10/1
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-L|F7
|
||||||
|
7S-7|
|
||||||
|
L|7||
|
||||||
|
-L-J|
|
||||||
|
L|-JF
|
||||||
1
tests/day10/1.1
Normal file
1
tests/day10/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
4
|
||||||
5
tests/day10/2
Normal file
5
tests/day10/2
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
..F7.
|
||||||
|
.FJ|.
|
||||||
|
SJ.L7
|
||||||
|
|F--J
|
||||||
|
LJ...
|
||||||
1
tests/day10/2.1
Normal file
1
tests/day10/2.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
8
|
||||||
9
tests/day10/3
Normal file
9
tests/day10/3
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
...........
|
||||||
|
.S-------7.
|
||||||
|
.|F-----7|.
|
||||||
|
.||.....||.
|
||||||
|
.||.....||.
|
||||||
|
.|L-7.F-J|.
|
||||||
|
.|..|.|..|.
|
||||||
|
.L--J.L--J.
|
||||||
|
...........
|
||||||
1
tests/day10/3.2
Normal file
1
tests/day10/3.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
4
|
||||||
10
tests/day10/4
Normal file
10
tests/day10/4
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.F----7F7F7F7F-7....
|
||||||
|
.|F--7||||||||FJ....
|
||||||
|
.||.FJ||||||||L7....
|
||||||
|
FJL7L7LJLJ||LJ.L-7..
|
||||||
|
L--J.L7...LJS7F-7L7.
|
||||||
|
....F-J..F7FJ|L7L7L7
|
||||||
|
....L7.F7||L7|.L7L7|
|
||||||
|
.....|FJLJ|FJ|F7|.LJ
|
||||||
|
....FJL-7.||.||||...
|
||||||
|
....L---J.LJ.LJLJ...
|
||||||
1
tests/day10/4.2
Normal file
1
tests/day10/4.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
8
|
||||||
10
tests/day10/5
Normal file
10
tests/day10/5
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
FF7FSF7F7F7F7F7F---7
|
||||||
|
L|LJ||||||||||||F--J
|
||||||
|
FL-7LJLJ||||||LJL-77
|
||||||
|
F--JF--7||LJLJ7F7FJ-
|
||||||
|
L---JF-JLJ.||-FJLJJ7
|
||||||
|
|F|F-JF---7F7-L7L|7|
|
||||||
|
|FFJF7L7F-JF7|JL---7
|
||||||
|
7-L-JL7||F7|L7F-7F7|
|
||||||
|
L.L7LFJ|||||FJL7||LJ
|
||||||
|
L7JLJL-JLJLJL--JLJ.L
|
||||||
1
tests/day10/5.2
Normal file
1
tests/day10/5.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
10
|
||||||
10
tests/day11/1
Normal file
10
tests/day11/1
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
...#......
|
||||||
|
.......#..
|
||||||
|
#.........
|
||||||
|
..........
|
||||||
|
......#...
|
||||||
|
.#........
|
||||||
|
.........#
|
||||||
|
..........
|
||||||
|
.......#..
|
||||||
|
#...#.....
|
||||||
1
tests/day11/1.1
Normal file
1
tests/day11/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
374
|
||||||
6
tests/day12/1
Normal file
6
tests/day12/1
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
???.### 1,1,3
|
||||||
|
.??..??...?##. 1,1,3
|
||||||
|
?#?#?#?#?#?#?#? 1,3,1,6
|
||||||
|
????.#...#... 4,1,1
|
||||||
|
????.######..#####. 1,6,5
|
||||||
|
?###???????? 3,2,1
|
||||||
1
tests/day12/1.1
Normal file
1
tests/day12/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
21
|
||||||
1
tests/day12/1.2
Normal file
1
tests/day12/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
525152
|
||||||
15
tests/day13/1
Normal file
15
tests/day13/1
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#.##..##.
|
||||||
|
..#.##.#.
|
||||||
|
##......#
|
||||||
|
##......#
|
||||||
|
..#.##.#.
|
||||||
|
..##..##.
|
||||||
|
#.#.##.#.
|
||||||
|
|
||||||
|
#...##..#
|
||||||
|
#....#..#
|
||||||
|
..##..###
|
||||||
|
#####.##.
|
||||||
|
#####.##.
|
||||||
|
..##..###
|
||||||
|
#....#..#
|
||||||
1
tests/day13/1.1
Normal file
1
tests/day13/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
405
|
||||||
1
tests/day13/1.2
Normal file
1
tests/day13/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
400
|
||||||
10
tests/day14/1
Normal file
10
tests/day14/1
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
O....#....
|
||||||
|
O.OO#....#
|
||||||
|
.....##...
|
||||||
|
OO.#O....O
|
||||||
|
.O.....O#.
|
||||||
|
O.#..O.#.#
|
||||||
|
..O..#O..O
|
||||||
|
.......O..
|
||||||
|
#....###..
|
||||||
|
#OO..#....
|
||||||
1
tests/day14/1.1
Normal file
1
tests/day14/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
136
|
||||||
1
tests/day14/1.2
Normal file
1
tests/day14/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
64
|
||||||
1
tests/day15/1
Normal file
1
tests/day15/1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
|
||||||
1
tests/day15/1.1
Normal file
1
tests/day15/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
1320
|
||||||
1
tests/day15/1.2
Normal file
1
tests/day15/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
145
|
||||||
10
tests/day16/1
Normal file
10
tests/day16/1
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.|...\....
|
||||||
|
|.-.\.....
|
||||||
|
.....|-...
|
||||||
|
........|.
|
||||||
|
..........
|
||||||
|
.........\
|
||||||
|
..../.\\..
|
||||||
|
.-.-/..|..
|
||||||
|
.|....-|.\
|
||||||
|
..//.|....
|
||||||
1
tests/day16/1.1
Normal file
1
tests/day16/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
46
|
||||||
1
tests/day16/1.2
Normal file
1
tests/day16/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
51
|
||||||
13
tests/day17/1
Normal file
13
tests/day17/1
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
2413432311323
|
||||||
|
3215453535623
|
||||||
|
3255245654254
|
||||||
|
3446585845452
|
||||||
|
4546657867536
|
||||||
|
1438598798454
|
||||||
|
4457876987766
|
||||||
|
3637877979653
|
||||||
|
4654967986887
|
||||||
|
4564679986453
|
||||||
|
1224686865563
|
||||||
|
2546548887735
|
||||||
|
4322674655533
|
||||||
1
tests/day17/1.1
Normal file
1
tests/day17/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
102
|
||||||
1
tests/day17/1.2
Normal file
1
tests/day17/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
94
|
||||||
5
tests/day17/2
Normal file
5
tests/day17/2
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
111111111111
|
||||||
|
999999999991
|
||||||
|
999999999991
|
||||||
|
999999999991
|
||||||
|
999999999991
|
||||||
1
tests/day17/2.2
Normal file
1
tests/day17/2.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
71
|
||||||
14
tests/day18/1
Normal file
14
tests/day18/1
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
R 6 (#70c710)
|
||||||
|
D 5 (#0dc571)
|
||||||
|
L 2 (#5713f0)
|
||||||
|
D 2 (#d2c081)
|
||||||
|
R 2 (#59c680)
|
||||||
|
D 2 (#411b91)
|
||||||
|
L 5 (#8ceee2)
|
||||||
|
U 2 (#caa173)
|
||||||
|
L 1 (#1b58a2)
|
||||||
|
U 2 (#caa171)
|
||||||
|
R 2 (#7807d2)
|
||||||
|
U 3 (#a77fa3)
|
||||||
|
L 2 (#015232)
|
||||||
|
U 2 (#7a21e3)
|
||||||
1
tests/day18/1.1
Normal file
1
tests/day18/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
62
|
||||||
1
tests/day18/1.2
Normal file
1
tests/day18/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
952408144115
|
||||||
5
tests/day2/1
Normal file
5
tests/day2/1
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
|
||||||
|
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
|
||||||
|
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
|
||||||
|
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
|
||||||
|
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
|
||||||
1
tests/day2/1.1
Normal file
1
tests/day2/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
8
|
||||||
1
tests/day2/1.2
Normal file
1
tests/day2/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2286
|
||||||
10
tests/day3/1
Normal file
10
tests/day3/1
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
467..114..
|
||||||
|
...*......
|
||||||
|
..35..633.
|
||||||
|
......#...
|
||||||
|
617*......
|
||||||
|
.....+.58.
|
||||||
|
..592.....
|
||||||
|
......755.
|
||||||
|
...$.*....
|
||||||
|
.664.598..
|
||||||
1
tests/day3/1.1
Normal file
1
tests/day3/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
4361
|
||||||
1
tests/day3/1.2
Normal file
1
tests/day3/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
467835
|
||||||
6
tests/day4/1
Normal file
6
tests/day4/1
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
||||||
|
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
||||||
|
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
|
||||||
|
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
|
||||||
|
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
|
||||||
|
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
|
||||||
1
tests/day4/1.1
Normal file
1
tests/day4/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
13
|
||||||
1
tests/day4/1.2
Normal file
1
tests/day4/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
30
|
||||||
33
tests/day5/1
Normal file
33
tests/day5/1
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
seeds: 79 14 55 13
|
||||||
|
|
||||||
|
seed-to-soil map:
|
||||||
|
50 98 2
|
||||||
|
52 50 48
|
||||||
|
|
||||||
|
soil-to-fertilizer map:
|
||||||
|
0 15 37
|
||||||
|
37 52 2
|
||||||
|
39 0 15
|
||||||
|
|
||||||
|
fertilizer-to-water map:
|
||||||
|
49 53 8
|
||||||
|
0 11 42
|
||||||
|
42 0 7
|
||||||
|
57 7 4
|
||||||
|
|
||||||
|
water-to-light map:
|
||||||
|
88 18 7
|
||||||
|
18 25 70
|
||||||
|
|
||||||
|
light-to-temperature map:
|
||||||
|
45 77 23
|
||||||
|
81 45 19
|
||||||
|
68 64 13
|
||||||
|
|
||||||
|
temperature-to-humidity map:
|
||||||
|
0 69 1
|
||||||
|
1 0 69
|
||||||
|
|
||||||
|
humidity-to-location map:
|
||||||
|
60 56 37
|
||||||
|
56 93 4
|
||||||
1
tests/day5/1.1
Normal file
1
tests/day5/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
35
|
||||||
1
tests/day5/1.2
Normal file
1
tests/day5/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
46
|
||||||
2
tests/day6/1
Normal file
2
tests/day6/1
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Time: 7 15 30
|
||||||
|
Distance: 9 40 200
|
||||||
1
tests/day6/1.1
Normal file
1
tests/day6/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
288
|
||||||
5
tests/day7/1
Normal file
5
tests/day7/1
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
32T3K 765
|
||||||
|
T55J5 684
|
||||||
|
KK677 28
|
||||||
|
KTJJT 220
|
||||||
|
QQQJA 483
|
||||||
1
tests/day7/1.1
Normal file
1
tests/day7/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
6440
|
||||||
1
tests/day7/1.2
Normal file
1
tests/day7/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
5905
|
||||||
9
tests/day8/1
Normal file
9
tests/day8/1
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
RL
|
||||||
|
|
||||||
|
AAA = (BBB, CCC)
|
||||||
|
BBB = (DDD, EEE)
|
||||||
|
CCC = (ZZZ, GGG)
|
||||||
|
DDD = (DDD, DDD)
|
||||||
|
EEE = (EEE, EEE)
|
||||||
|
GGG = (GGG, GGG)
|
||||||
|
ZZZ = (ZZZ, ZZZ)
|
||||||
1
tests/day8/1.1
Normal file
1
tests/day8/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2
|
||||||
5
tests/day8/2
Normal file
5
tests/day8/2
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
LLR
|
||||||
|
|
||||||
|
AAA = (BBB, BBB)
|
||||||
|
BBB = (AAA, ZZZ)
|
||||||
|
ZZZ = (ZZZ, ZZZ)
|
||||||
1
tests/day8/2.1
Normal file
1
tests/day8/2.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
6
|
||||||
10
tests/day8/3
Normal file
10
tests/day8/3
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
LR
|
||||||
|
|
||||||
|
11A = (11B, XXX)
|
||||||
|
11B = (XXX, 11Z)
|
||||||
|
11Z = (11B, XXX)
|
||||||
|
22A = (22B, XXX)
|
||||||
|
22B = (22C, 22C)
|
||||||
|
22C = (22Z, 22Z)
|
||||||
|
22Z = (22B, 22B)
|
||||||
|
XXX = (XXX, XXX)
|
||||||
1
tests/day8/3.2
Normal file
1
tests/day8/3.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
6
|
||||||
3
tests/day9/1
Normal file
3
tests/day9/1
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
0 3 6 9 12 15
|
||||||
|
1 3 6 10 15 21
|
||||||
|
10 13 16 21 30 45
|
||||||
1
tests/day9/1.1
Normal file
1
tests/day9/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
114
|
||||||
1
tests/day9/1.2
Normal file
1
tests/day9/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2
|
||||||
Reference in New Issue
Block a user