Compare commits

..

25 Commits

Author SHA1 Message Date
bluepython508
aa1d6f5ca0 Day 17 2023-12-18 15:57:20 +00:00
bluepython508
a9519aebbf Day 18 2023-12-18 09:19:27 +00:00
bluepython508
3fc1300387 Day 16 2023-12-16 08:51:08 +00:00
bluepython508
c550c1850c Day 15 2023-12-15 09:13:57 +00:00
bluepython508
e24b45de16 Submission fixes 2023-12-14 09:56:12 +00:00
bluepython508
a7beb06a28 Day 14 2023-12-14 09:38:23 +00:00
bluepython508
03b92dd930 Day 13 2023-12-13 12:17:31 +00:00
bluepython508
131f501b91 Day 12 2023-12-12 11:26:57 +00:00
bluepython508
e9367fd784 Include timing information 2023-12-11 13:45:15 +00:00
bluepython508
753635b38e Day 11 2023-12-11 13:33:19 +00:00
bluepython508
5c04d2651e Day 10 2023-12-10 17:24:56 +00:00
bluepython508
1655ead021 Day 9 2023-12-09 12:13:46 +00:00
bluepython508
3361ad3a79 Fix jokers 2023-12-08 17:19:44 +00:00
bluepython508
20501c0819 Day 8 2023-12-08 14:08:39 +00:00
bluepython508
c500a9a280 Improvements to day 7 2023-12-07 12:14:45 +00:00
bluepython508
863e1a5cb0 Day 7 2023-12-07 11:51:22 +00:00
bluepython508
abbd8a263a Day 6 2023-12-06 08:21:43 +00:00
bluepython508
683522ddc0 Day 5 2023-12-05 21:23:38 +00:00
bluepython508
ce5e83baa4 Day 4; run watches in a seperate process to allow continuation after crashes 2023-12-04 18:04:49 +00:00
bluepython508
ee17acccae Day 3 2023-12-03 11:35:22 +00:00
bluepython508
2ae3b6cf7b Further output improvements 2023-12-03 10:00:10 +00:00
bluepython508
c086fc1773 Test result improvements: color 2023-12-02 19:25:19 +00:00
bluepython508
2784245b2d Native watch 2023-12-02 19:20:27 +00:00
bluepython508
c6c501af2a Day 2 2023-12-02 18:13:05 +00:00
bluepython508
70cf651767 Further mix improvements 2023-12-02 18:12:57 +00:00
84 changed files with 1590 additions and 34 deletions

View File

@@ -4,9 +4,11 @@ defmodule Aoc2023 do
import Aoc2023.Common
def main(input) do
parsed = parse(input)
IO.puts("Part 1: #{part1(parsed)}")
IO.puts("Part 2: #{part2(parsed)}")
{t_parse, parsed} = :timer.tc fn -> parse(input) end
{t_p1, _} = :timer.tc fn -> IO.puts("Part 1: #{part1(parsed)}") end
{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

View File

@@ -1,4 +1,66 @@
defmodule Aoc2023.Common do
def stringify(s) when is_binary(s) do s 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

91
lib/day10.ex Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View File

@@ -3,14 +3,34 @@ defmodule Mix.Tasks.Aoc do
import Aoc2023.Common
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]
defp base_dir(), do: System.get_env("AOC_BASE")
defp tests(day) do
case File.ls(tests_dir(day)) do
{:ok, paths} -> paths |> Enum.filter(&(not String.contains?(&1, ".")))
{:error, e} -> dbg(e)
{:ok, paths} ->
paths |> Enum.filter(&(not String.contains?(&1, "."))) |> Enum.sort()
{:error, e} ->
dbg(e)
[]
end
end
@@ -59,17 +79,17 @@ defmodule Mix.Tasks.Aoc do
defp run(day, ["new"]) do
create_file("#{base_dir()}/lib/day#{day}.ex", """
defmodule Aoc2023.Day#{day} do
use Aoc2023
use Aoc2023
def parse(input) do
input
end
def parse(input) do
input
end
def part1(input) do
end
def part1(_input) do
end
def part2(input) do
end
def part2(_input) do
end
end
""")
@@ -92,10 +112,40 @@ defmodule Mix.Tasks.Aoc do
create_file("#{tests_dir(day)}#{last + 1}", IO.read(:stdio, :eof))
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
create_file("#{tests_dir(day)}#{test}.#{part}", IO.read(:stdio, :eof))
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
mod = module(day)
tests = tests(day)
@@ -104,29 +154,27 @@ defmodule Mix.Tasks.Aoc do
|> Enum.map(&test(day, &1))
|> Enum.map(fn {test, input, p1e, p2e} ->
parsed = mod.parse(input)
p1 = mod.part1(parsed)
if p1e do
if stringify(p1) != p1e do
IO.puts("Failed at #{test}.1: expected #{p1e}, got:")
dbg(p1)
result = fn part, got, expected ->
cond do
stringify(got) == expected ->
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
else
IO.puts("Test #{test}.1")
dbg(p1)
end
p2 = mod.part2(parsed)
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
result.(1, mod.part1(parsed), p1e)
result.(2, mod.part2(parsed), p2e)
end)
end
@@ -135,6 +183,30 @@ defmodule Mix.Tasks.Aoc do
run_file(day, "#{base_dir()}/inputs/day#{day}")
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
run_file(day)
end
@@ -158,4 +230,30 @@ defmodule Mix.Tasks.Aoc do
defp run_file(day, file \\ nil) do
module(day).main(if file, do: read(file), else: IO.read(:stdio, :eof))
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

View File

@@ -19,7 +19,8 @@ defmodule Aoc2023.MixProject do
defp deps do
[
{:httpoison, "~> 2.0"}
httpoison: "~> 2.0",
file_system: "~> 1.0"
]
end
end

View File

@@ -1,5 +1,6 @@
%{
"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"},
"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"},

View File

@@ -5,10 +5,11 @@
elixir,
elixir-ls,
inotify-tools,
entr,
}:
mkShell {
packages =
[elixir elixir-ls]
[elixir elixir-ls entr]
++ lib.lists.optional (pkgs.system == "x86_64-linux") inotify-tools
++ lib.lists.optionals (pkgs.system == "aarch64-darwin") (with pkgs.darwin.apple_sdk.frameworks; [
CoreFoundation

5
tests/day10/1 Normal file
View File

@@ -0,0 +1,5 @@
-L|F7
7S-7|
L|7||
-L-J|
L|-JF

1
tests/day10/1.1 Normal file
View File

@@ -0,0 +1 @@
4

5
tests/day10/2 Normal file
View File

@@ -0,0 +1,5 @@
..F7.
.FJ|.
SJ.L7
|F--J
LJ...

1
tests/day10/2.1 Normal file
View File

@@ -0,0 +1 @@
8

9
tests/day10/3 Normal file
View File

@@ -0,0 +1,9 @@
...........
.S-------7.
.|F-----7|.
.||.....||.
.||.....||.
.|L-7.F-J|.
.|..|.|..|.
.L--J.L--J.
...........

1
tests/day10/3.2 Normal file
View File

@@ -0,0 +1 @@
4

10
tests/day10/4 Normal file
View 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
View File

@@ -0,0 +1 @@
8

10
tests/day10/5 Normal file
View 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
View File

@@ -0,0 +1 @@
10

10
tests/day11/1 Normal file
View File

@@ -0,0 +1,10 @@
...#......
.......#..
#.........
..........
......#...
.#........
.........#
..........
.......#..
#...#.....

1
tests/day11/1.1 Normal file
View File

@@ -0,0 +1 @@
374

6
tests/day12/1 Normal file
View 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
View File

@@ -0,0 +1 @@
21

1
tests/day12/1.2 Normal file
View File

@@ -0,0 +1 @@
525152

15
tests/day13/1 Normal file
View File

@@ -0,0 +1,15 @@
#.##..##.
..#.##.#.
##......#
##......#
..#.##.#.
..##..##.
#.#.##.#.
#...##..#
#....#..#
..##..###
#####.##.
#####.##.
..##..###
#....#..#

1
tests/day13/1.1 Normal file
View File

@@ -0,0 +1 @@
405

1
tests/day13/1.2 Normal file
View File

@@ -0,0 +1 @@
400

10
tests/day14/1 Normal file
View 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
View File

@@ -0,0 +1 @@
136

1
tests/day14/1.2 Normal file
View File

@@ -0,0 +1 @@
64

1
tests/day15/1 Normal file
View 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
View File

@@ -0,0 +1 @@
1320

1
tests/day15/1.2 Normal file
View File

@@ -0,0 +1 @@
145

10
tests/day16/1 Normal file
View File

@@ -0,0 +1,10 @@
.|...\....
|.-.\.....
.....|-...
........|.
..........
.........\
..../.\\..
.-.-/..|..
.|....-|.\
..//.|....

1
tests/day16/1.1 Normal file
View File

@@ -0,0 +1 @@
46

1
tests/day16/1.2 Normal file
View File

@@ -0,0 +1 @@
51

13
tests/day17/1 Normal file
View 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
View File

@@ -0,0 +1 @@
102

1
tests/day17/1.2 Normal file
View File

@@ -0,0 +1 @@
94

5
tests/day17/2 Normal file
View File

@@ -0,0 +1,5 @@
111111111111
999999999991
999999999991
999999999991
999999999991

1
tests/day17/2.2 Normal file
View File

@@ -0,0 +1 @@
71

14
tests/day18/1 Normal file
View 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
View File

@@ -0,0 +1 @@
62

1
tests/day18/1.2 Normal file
View File

@@ -0,0 +1 @@
952408144115

5
tests/day2/1 Normal file
View 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
View File

@@ -0,0 +1 @@
8

1
tests/day2/1.2 Normal file
View File

@@ -0,0 +1 @@
2286

10
tests/day3/1 Normal file
View File

@@ -0,0 +1,10 @@
467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..

1
tests/day3/1.1 Normal file
View File

@@ -0,0 +1 @@
4361

1
tests/day3/1.2 Normal file
View File

@@ -0,0 +1 @@
467835

6
tests/day4/1 Normal file
View 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
View File

@@ -0,0 +1 @@
13

1
tests/day4/1.2 Normal file
View File

@@ -0,0 +1 @@
30

33
tests/day5/1 Normal file
View 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
View File

@@ -0,0 +1 @@
35

1
tests/day5/1.2 Normal file
View File

@@ -0,0 +1 @@
46

2
tests/day6/1 Normal file
View File

@@ -0,0 +1,2 @@
Time: 7 15 30
Distance: 9 40 200

1
tests/day6/1.1 Normal file
View File

@@ -0,0 +1 @@
288

5
tests/day7/1 Normal file
View File

@@ -0,0 +1,5 @@
32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483

1
tests/day7/1.1 Normal file
View File

@@ -0,0 +1 @@
6440

1
tests/day7/1.2 Normal file
View File

@@ -0,0 +1 @@
5905

9
tests/day8/1 Normal file
View 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
View File

@@ -0,0 +1 @@
2

5
tests/day8/2 Normal file
View File

@@ -0,0 +1,5 @@
LLR
AAA = (BBB, BBB)
BBB = (AAA, ZZZ)
ZZZ = (ZZZ, ZZZ)

1
tests/day8/2.1 Normal file
View File

@@ -0,0 +1 @@
6

10
tests/day8/3 Normal file
View 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
View File

@@ -0,0 +1 @@
6

3
tests/day9/1 Normal file
View 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
View File

@@ -0,0 +1 @@
114

1
tests/day9/1.2 Normal file
View File

@@ -0,0 +1 @@
2