Compare commits
No commits in common. "main" and "cd77059be38e3cbf32d17d44819af544a668815d" have entirely different histories.
main
...
cd77059be3
39 changed files with 42 additions and 3075 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +0,0 @@
|
||||||
input
|
|
||||||
sample
|
|
||||||
arena
|
|
|
@ -1,52 +0,0 @@
|
||||||
defmodule Day1 do
|
|
||||||
def part1(<<>>), do: 0
|
|
||||||
def part1(<<?(, rest::binary>>), do: 1 + part1(rest)
|
|
||||||
def part1(<<?), rest::binary>>), do: -1 + part1(rest)
|
|
||||||
|
|
||||||
def part2(input), do: find_basement(input, _floor = 0, _pos = 1)
|
|
||||||
|
|
||||||
def find_basement(<<?), _::binary>>, 0, pos), do: pos
|
|
||||||
def find_basement(<<?), rest::binary>>, floor, pos), do: find_basement(rest, floor - 1, pos + 1)
|
|
||||||
def find_basement(<<?(, rest::binary>>, floor, pos), do: find_basement(rest, floor + 1, pos + 1)
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day1.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day1.run()
|
|
|
@ -1,66 +0,0 @@
|
||||||
defmodule Day2 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(fn [x, y, z] ->
|
|
||||||
a = x * y
|
|
||||||
b = x * z
|
|
||||||
c = y * z
|
|
||||||
min = Enum.min([a, b, c])
|
|
||||||
2 * (a + b + c) + min
|
|
||||||
end)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(fn box ->
|
|
||||||
[x, y, z] = Enum.sort(box)
|
|
||||||
2 * (x + y) + x * y * z
|
|
||||||
end)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.split(["\n", "x"], trim: true)
|
|
||||||
|> Enum.map(&String.to_integer/1)
|
|
||||||
|> Enum.chunk_every(3)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day2.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day2.run()
|
|
|
@ -1,67 +0,0 @@
|
||||||
defmodule Day3 do
|
|
||||||
def part1(input), do: move(input, MapSet.new([{0, 0}]), {0, 0})
|
|
||||||
|
|
||||||
def move(<<?<, rest::binary>>, houses, {x, y}), do: deliver(rest, houses, {x - 1, y})
|
|
||||||
def move(<<?>, rest::binary>>, houses, {x, y}), do: deliver(rest, houses, {x + 1, y})
|
|
||||||
def move(<<?^, rest::binary>>, houses, {x, y}), do: deliver(rest, houses, {x, y - 1})
|
|
||||||
def move(<<?v, rest::binary>>, houses, {x, y}), do: deliver(rest, houses, {x, y + 1})
|
|
||||||
def move(<<>>, houses, _addr), do: MapSet.size(houses)
|
|
||||||
|
|
||||||
def deliver(moves, houses, addr), do: move(moves, MapSet.put(houses, addr), addr)
|
|
||||||
|
|
||||||
def part2(input), do: move2(input, MapSet.new([{0, 0}]), {0, 0}, {0, 0})
|
|
||||||
|
|
||||||
def move2(<<ms, mb, rest::binary>>, houses, santa, bot) do
|
|
||||||
santa = next(ms, santa)
|
|
||||||
bot = next(mb, bot)
|
|
||||||
move2(rest, MapSet.union(houses, MapSet.new([santa, bot])), santa, bot)
|
|
||||||
end
|
|
||||||
|
|
||||||
def move2(<<>>, houses, _santa, _bot), do: MapSet.size(houses)
|
|
||||||
|
|
||||||
def next(?<, {x, y}), do: {x - 1, y}
|
|
||||||
def next(?>, {x, y}), do: {x + 1, y}
|
|
||||||
def next(?^, {x, y}), do: {x, y - 1}
|
|
||||||
def next(?v, {x, y}), do: {x, y + 1}
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day3.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day3.run()
|
|
|
@ -1,50 +0,0 @@
|
||||||
defmodule Day4 do
|
|
||||||
def part1(input), do: mine(input, 1, "00000")
|
|
||||||
|
|
||||||
def mine(input, nonce, prefix) do
|
|
||||||
hash = :crypto.hash(:md5, input <> Integer.to_string(nonce)) |> Base.encode16()
|
|
||||||
if String.starts_with?(hash, prefix), do: nonce, else: mine(input, nonce + 1, prefix)
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input), do: mine(input, 1, "000000")
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input] <- System.argv() do
|
|
||||||
input
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day4.exs input")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day4.run()
|
|
|
@ -1,86 +0,0 @@
|
||||||
defmodule Day5 do
|
|
||||||
def part1(input) do
|
|
||||||
Enum.count(input, &nice?/1)
|
|
||||||
end
|
|
||||||
|
|
||||||
def nice?(str) do
|
|
||||||
at_least_three_vowels(str) and
|
|
||||||
twice_in_a_row(str) and
|
|
||||||
not String.contains?(str, ["ab", "cd", "pq", "xy"])
|
|
||||||
end
|
|
||||||
|
|
||||||
def at_least_three_vowels(str) do
|
|
||||||
str
|
|
||||||
|> String.graphemes()
|
|
||||||
|> Enum.frequencies()
|
|
||||||
|> Map.take(["a", "e", "i", "o", "u"])
|
|
||||||
|> Map.values()
|
|
||||||
|> Enum.sum()
|
|
||||||
|> Kernel.>=(3)
|
|
||||||
end
|
|
||||||
|
|
||||||
def twice_in_a_row(<<x, x, _::binary>>), do: true
|
|
||||||
def twice_in_a_row(<<_x, rest::binary>>), do: twice_in_a_row(rest)
|
|
||||||
def twice_in_a_row(<<>>), do: false
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
Enum.count(input, &nice2?/1)
|
|
||||||
end
|
|
||||||
|
|
||||||
def nice2?(str) do
|
|
||||||
pairs = pairs(str)
|
|
||||||
uniq = MapSet.new(pairs)
|
|
||||||
length(pairs) > MapSet.size(uniq) and sandwich(str)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pairs(str, prev \\ nil)
|
|
||||||
def pairs(<<x, x, rest::binary>>, <<x, x>>), do: pairs(<<x, rest::binary>>)
|
|
||||||
def pairs(<<x, y, rest::binary>>, _prev), do: [<<x, y>> | pairs(<<y, rest::binary>>, <<x, y>>)]
|
|
||||||
def pairs(<<_>>, _prev), do: []
|
|
||||||
|
|
||||||
def sandwich(<<x, y, x, _rest::binary>>), do: true
|
|
||||||
def sandwich(<<_x, y, z, rest::binary>>), do: sandwich(<<y, z, rest::binary>>)
|
|
||||||
def sandwich(_), do: false
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.split("\n", trim: true)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day5.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day5.run()
|
|
5
2021/README.md
Normal file
5
2021/README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Advent of Code 2021 in Zig!
|
||||||
|
|
||||||
|
I just started learning Zig on Christmas Day 2021, so after running through a few tutorials I'm planning on running through a few of the 2021 puzzles as a way to get to know the language, probably starting sometime in January 2022.
|
||||||
|
|
||||||
|
<img width="943" alt="image" src="https://user-images.githubusercontent.com/498229/147409293-6a855c9c-7d89-4440-9192-240eb47b3d85.png">
|
|
@ -1,95 +0,0 @@
|
||||||
defmodule Day10 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(&parse_line(&1, []))
|
|
||||||
|> Enum.filter(&match?({:corrupted, _}, &1))
|
|
||||||
|> Enum.map(fn {:corrupted, char} -> score(char) end)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp parse_line(_, {:corrupted, char}), do: {:corrupted, char}
|
|
||||||
defp parse_line("", stack), do: {:incomplete, stack}
|
|
||||||
defp parse_line(<<char::utf8, rest::binary>>, []), do: parse_line(rest, [char])
|
|
||||||
|
|
||||||
defp parse_line(<<char::utf8, next::binary>>, [prev | popped] = stack) do
|
|
||||||
stack =
|
|
||||||
case char do
|
|
||||||
char when char in '([{<' -> [char | stack]
|
|
||||||
?) when prev == ?( -> popped
|
|
||||||
?] when prev == ?[ -> popped
|
|
||||||
?} when prev == ?{ -> popped
|
|
||||||
?> when prev == ?< -> popped
|
|
||||||
char -> {:corrupted, char}
|
|
||||||
end
|
|
||||||
|
|
||||||
parse_line(next, stack)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp score(?)), do: 3
|
|
||||||
defp score(?]), do: 57
|
|
||||||
defp score(?}), do: 1197
|
|
||||||
defp score(?>), do: 25137
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
scores =
|
|
||||||
input
|
|
||||||
|> Enum.map(&parse_line(&1, []))
|
|
||||||
|> Enum.filter(&match?({:incomplete, _}, &1))
|
|
||||||
|> Enum.map(fn {_, chars} -> score2(chars) end)
|
|
||||||
|> Enum.sort()
|
|
||||||
|
|
||||||
Enum.at(scores, trunc(length(scores) / 2))
|
|
||||||
end
|
|
||||||
|
|
||||||
defp score2(chars) do
|
|
||||||
for char <- chars, reduce: 0 do
|
|
||||||
score -> score * 5 + points(char)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp points(?(), do: 1
|
|
||||||
defp points(?[), do: 2
|
|
||||||
defp points(?{), do: 3
|
|
||||||
defp points(?<), do: 4
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
String.split(input, "\n", trim: true)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day10.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day10.run()
|
|
106
2021/day13.exs
106
2021/day13.exs
|
@ -1,106 +0,0 @@
|
||||||
defmodule Day13 do
|
|
||||||
defmodule Paper do
|
|
||||||
defstruct dots: MapSet.new(), instructions: []
|
|
||||||
end
|
|
||||||
|
|
||||||
def part1(paper) do
|
|
||||||
folded = fold(paper)
|
|
||||||
MapSet.size(folded.dots)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp fold(%Paper{dots: dots, instructions: [{axis, pos} | rest]}) do
|
|
||||||
dots =
|
|
||||||
MapSet.new(dots, fn {x, y} ->
|
|
||||||
cond do
|
|
||||||
axis == "x" and x > pos -> {pos - (x - pos), y}
|
|
||||||
axis == "y" and y > pos -> {x, pos - (y - pos)}
|
|
||||||
true -> {x, y}
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
%Paper{dots: dots, instructions: rest}
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(paper) do
|
|
||||||
paper
|
|
||||||
|> fold_all
|
|
||||||
|> draw
|
|
||||||
end
|
|
||||||
|
|
||||||
defp fold_all(%Paper{instructions: []} = paper), do: paper
|
|
||||||
defp fold_all(%Paper{} = paper), do: paper |> fold |> fold_all
|
|
||||||
|
|
||||||
defp draw(paper) do
|
|
||||||
{max_x, max_y} =
|
|
||||||
for {x, y} <- paper.dots, reduce: {0, 0} do
|
|
||||||
{max_x, max_y} -> {max(x, max_x), max(y, max_y)}
|
|
||||||
end
|
|
||||||
|
|
||||||
for y <- 0..max_y do
|
|
||||||
row =
|
|
||||||
for x <- 0..max_x do
|
|
||||||
if MapSet.member?(paper.dots, {x, y}), do: ?#, else: ?\s
|
|
||||||
end
|
|
||||||
|
|
||||||
# could Enum.join("\n") here, but I want to play with iolists
|
|
||||||
[row | "\n"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
[dots, instructions] = String.split(input, "\n\n")
|
|
||||||
|
|
||||||
dots =
|
|
||||||
dots
|
|
||||||
|> String.split([",", "\n"])
|
|
||||||
|> Enum.map(&String.to_integer/1)
|
|
||||||
|> Enum.chunk_every(2)
|
|
||||||
|> MapSet.new(fn [x, y] -> {x, y} end)
|
|
||||||
|
|
||||||
instructions =
|
|
||||||
instructions
|
|
||||||
|> String.split("\n", trim: true)
|
|
||||||
|> Enum.map(fn <<"fold along ", axis::binary-1, "=", pos::binary>> ->
|
|
||||||
{axis, String.to_integer(pos)}
|
|
||||||
end)
|
|
||||||
|
|
||||||
%Paper{dots: dots, instructions: instructions}
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day13.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day13.run()
|
|
140
2021/day16.exs
140
2021/day16.exs
|
@ -1,140 +0,0 @@
|
||||||
defmodule Day16 do
|
|
||||||
def part1(input) do
|
|
||||||
{_type, _rest, _count, version_sum} = decode_packet(input, 0, 0)
|
|
||||||
version_sum
|
|
||||||
end
|
|
||||||
|
|
||||||
@literal_type 4
|
|
||||||
@literal_part 1
|
|
||||||
@literal_end 0
|
|
||||||
@total_length_type 0
|
|
||||||
@sub_packets_length_type 1
|
|
||||||
|
|
||||||
defp decode_packet(<<version::3, @literal_type::3, rest::bits>>, count, version_sum) do
|
|
||||||
decode_literal(rest, <<>>, count + 6, version_sum + version)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp decode_packet(<<version::3, operator_type::3, rest::bits>>, count, version_sum) do
|
|
||||||
{sub_packets, rest, count, version_sum} =
|
|
||||||
case rest do
|
|
||||||
<<@total_length_type::1, length::15, rest::bits>> ->
|
|
||||||
decode_sub_packets_by(
|
|
||||||
:length,
|
|
||||||
length,
|
|
||||||
[],
|
|
||||||
rest,
|
|
||||||
count + 3 + 3 + 1 + 15,
|
|
||||||
version_sum + version
|
|
||||||
)
|
|
||||||
|
|
||||||
<<@sub_packets_length_type::1, num_sub_packets::11, rest::bits>> ->
|
|
||||||
decode_sub_packets_by(
|
|
||||||
:quantity,
|
|
||||||
num_sub_packets,
|
|
||||||
[],
|
|
||||||
rest,
|
|
||||||
count + 3 + 3 + 1 + 11,
|
|
||||||
version_sum + version
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
{{:operator, operator_type, sub_packets}, rest, count, version_sum}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp decode_literal(<<@literal_part::1, group::bits-4, rest::bits>>, acc, count, version_sum) do
|
|
||||||
decode_literal(rest, <<acc::bits, group::bits>>, count + 5, version_sum)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp decode_literal(<<@literal_end::1, group::bits-4, rest::bits>>, acc, count, version_sum) do
|
|
||||||
literal_binary = <<acc::bits, group::bits>>
|
|
||||||
<<literal::size(bit_size(literal_binary))>> = literal_binary
|
|
||||||
{{:literal, literal}, rest, count + 5, version_sum}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp decode_sub_packets_by(_method, 0, decoded_packets, bits, count, version_sum) do
|
|
||||||
{Enum.reverse(decoded_packets), bits, count, version_sum}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp decode_sub_packets_by(method, remaining, decoded_packets, bits, count, version_sum) do
|
|
||||||
{decoded_packet, rest, packet_size, version_sum} = decode_packet(bits, 0, version_sum)
|
|
||||||
|
|
||||||
remaining =
|
|
||||||
case method do
|
|
||||||
:length -> remaining - packet_size
|
|
||||||
:quantity -> remaining - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
decode_sub_packets_by(
|
|
||||||
method,
|
|
||||||
remaining,
|
|
||||||
[decoded_packet | decoded_packets],
|
|
||||||
rest,
|
|
||||||
count + packet_size,
|
|
||||||
version_sum
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
{packet, _rest, _count, _version_sum} = decode_packet(input, 0, 0)
|
|
||||||
evaluate(packet)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp evaluate({:literal, literal}), do: literal
|
|
||||||
|
|
||||||
defp evaluate({:operator, op, args}) do
|
|
||||||
args
|
|
||||||
|> Enum.map(&evaluate/1)
|
|
||||||
|> calculate(op)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp calculate(args, 0), do: Enum.sum(args)
|
|
||||||
defp calculate(args, 1), do: Enum.reduce(args, &Kernel.*/2)
|
|
||||||
defp calculate(args, 2), do: Enum.min(args)
|
|
||||||
defp calculate(args, 3), do: Enum.max(args)
|
|
||||||
defp calculate([a, b], 5), do: if(a > b, do: 1, else: 0)
|
|
||||||
defp calculate([a, b], 6), do: if(a < b, do: 1, else: 0)
|
|
||||||
defp calculate([a, b], 7), do: if(a == b, do: 1, else: 0)
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.trim()
|
|
||||||
|> Base.decode16!()
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def(run) do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day16.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day16.run()
|
|
|
@ -1,98 +0,0 @@
|
||||||
defmodule Day3 do
|
|
||||||
def part1(input) do
|
|
||||||
gamma_rating = find_rating(input, 0, :most, <<>>)
|
|
||||||
epsillon_rating = find_rating(input, 0, :least, <<>>)
|
|
||||||
gamma_rating * epsillon_rating
|
|
||||||
end
|
|
||||||
|
|
||||||
defp find_rating([value | _], _, _, bits) when bit_size(value) == bit_size(bits) do
|
|
||||||
<<rating::size(bit_size(bits))>> = bits
|
|
||||||
rating
|
|
||||||
end
|
|
||||||
|
|
||||||
defp find_rating(values, bits_seen, mode, acc) do
|
|
||||||
bit = find_common(values, bits_seen, mode)
|
|
||||||
find_rating(values, bits_seen + 1, mode, <<acc::bits-size(bits_seen), bit::1>>)
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
oxygen_generator_rating = find_rating2(input, 0, :most)
|
|
||||||
co2_scrubber_rating = find_rating2(input, 0, :least)
|
|
||||||
oxygen_generator_rating * co2_scrubber_rating
|
|
||||||
end
|
|
||||||
|
|
||||||
defp find_rating2([bits], _bits_seen, _mode) do
|
|
||||||
<<value::size(bit_size(bits))>> = bits
|
|
||||||
value
|
|
||||||
end
|
|
||||||
|
|
||||||
defp find_rating2(values, bits_seen, mode) do
|
|
||||||
bit = find_common(values, bits_seen, mode)
|
|
||||||
|
|
||||||
values
|
|
||||||
|> filter_by_bit(bits_seen, bit)
|
|
||||||
|> find_rating2(bits_seen + 1, mode)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp find_common(values, bits_seen, mode) do
|
|
||||||
{zeros, ones} =
|
|
||||||
values
|
|
||||||
|> Enum.map(fn <<_::size(bits_seen), bit::1, _rest::bits>> -> bit end)
|
|
||||||
|> Enum.reduce({_zeros = 0, _ones = 0}, fn
|
|
||||||
0, {zeros, ones} -> {zeros + 1, ones}
|
|
||||||
1, {zeros, ones} -> {zeros, ones + 1}
|
|
||||||
end)
|
|
||||||
|
|
||||||
case mode do
|
|
||||||
:most -> if zeros <= ones, do: 1, else: 0
|
|
||||||
:least -> if zeros <= ones, do: 0, else: 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp filter_by_bit(values, num_prev_bits, bit) do
|
|
||||||
Enum.filter(values, &match?(<<_::size(num_prev_bits), ^bit::1, _rest::bits>>, &1))
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
strings = String.split(input, "\n", trim: true)
|
|
||||||
length = byte_size(hd(strings))
|
|
||||||
Enum.map(strings, &<<String.to_integer(&1, 2)::size(length)>>)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir dayREPLACE_ME.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day3.run()
|
|
133
2021/day4.exs
133
2021/day4.exs
|
@ -1,133 +0,0 @@
|
||||||
defmodule Day4 do
|
|
||||||
@rows for y <- 0..4, do: for(x <- 0..4, do: {x, y})
|
|
||||||
@cols for x <- 0..4, do: for(y <- 0..4, do: {x, y})
|
|
||||||
|
|
||||||
def part1(input) do
|
|
||||||
{current_number, winning_board} = play(input)
|
|
||||||
sum_unmarked(winning_board) * current_number
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
{current_number, last_board} = play_until_last(input)
|
|
||||||
sum_unmarked(last_board) * current_number
|
|
||||||
end
|
|
||||||
|
|
||||||
def play({[current_number | next_numbers], boards}) do
|
|
||||||
boards = for board <- boards, do: mark(board, current_number)
|
|
||||||
|
|
||||||
case check_for_winner(boards) do
|
|
||||||
nil -> play({next_numbers, boards})
|
|
||||||
board -> {current_number, board}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def play_until_last({_numbers, [_last_board]} = input), do: play(input)
|
|
||||||
|
|
||||||
def play_until_last({numbers, boards} = input) do
|
|
||||||
{_current_number, current_winner} = play(input)
|
|
||||||
|
|
||||||
current_winner_no_markers = Map.new(current_winner, fn {k, {v, _marker}} -> {k, v} end)
|
|
||||||
|
|
||||||
remaining_boards =
|
|
||||||
Enum.reject(boards, fn board ->
|
|
||||||
current_winner_no_markers == Map.new(board, fn {k, {v, _marker}} -> {k, v} end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
play_until_last({numbers, remaining_boards})
|
|
||||||
end
|
|
||||||
|
|
||||||
def sum_unmarked(board) do
|
|
||||||
board
|
|
||||||
|> Enum.filter(&match?({_key, {_number, false}}, &1))
|
|
||||||
|> Enum.map(fn {_key, {number, _marked}} -> number end)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def mark(board, current_number) do
|
|
||||||
case Enum.find(board, &match?({_key, {^current_number, false}}, &1)) do
|
|
||||||
nil -> board
|
|
||||||
{key, _value} -> Map.put(board, key, {current_number, true})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_for_winner([]), do: nil
|
|
||||||
|
|
||||||
def check_for_winner([board | boards]) do
|
|
||||||
row_complete? =
|
|
||||||
Enum.any?(@rows, fn row ->
|
|
||||||
values = for key <- row, do: board[key]
|
|
||||||
Enum.all?(values, &match?({_number, true}, &1))
|
|
||||||
end)
|
|
||||||
|
|
||||||
col_complete? =
|
|
||||||
Enum.any?(@cols, fn col ->
|
|
||||||
values = for key <- col, do: board[key]
|
|
||||||
Enum.all?(values, &match?({_number, true}, &1))
|
|
||||||
end)
|
|
||||||
|
|
||||||
if row_complete? or col_complete? do
|
|
||||||
board
|
|
||||||
else
|
|
||||||
check_for_winner(boards)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
[numbers | boards] = String.split(input, "\n", trim: true)
|
|
||||||
numbers = numbers |> String.split(",") |> Enum.map(&String.to_integer/1)
|
|
||||||
boards = parse_boards(boards)
|
|
||||||
{numbers, boards}
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp parse_boards([]), do: []
|
|
||||||
|
|
||||||
defp parse_boards(boards) do
|
|
||||||
{board, boards} = Enum.split(boards, 5)
|
|
||||||
|
|
||||||
board = Enum.map(board, &String.split/1)
|
|
||||||
|
|
||||||
board =
|
|
||||||
for {row, y} <- Enum.with_index(board), {value, x} <- Enum.with_index(row), into: %{} do
|
|
||||||
{{x, y}, {String.to_integer(value), false}}
|
|
||||||
end
|
|
||||||
|
|
||||||
[board | parse_boards(boards)]
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day4.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day4.run()
|
|
|
@ -1,83 +0,0 @@
|
||||||
defmodule Day5 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.reduce(_points = %{}, fn
|
|
||||||
{{x, y1}, {x, y2}}, points ->
|
|
||||||
for y <- y1..y2, reduce: points, do: (points -> plot(points, {x, y}))
|
|
||||||
|
|
||||||
{{x1, y}, {x2, y}}, points ->
|
|
||||||
for x <- x1..x2, reduce: points, do: (points -> plot(points, {x, y}))
|
|
||||||
|
|
||||||
_point, points ->
|
|
||||||
points
|
|
||||||
end)
|
|
||||||
|> Enum.count(fn {_point, count} -> count >= 2 end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
input
|
|
||||||
|> Enum.reduce(_points = %{}, fn
|
|
||||||
{{x, y1}, {x, y2}}, points ->
|
|
||||||
for y <- y1..y2, reduce: points, do: (points -> plot(points, {x, y}))
|
|
||||||
|
|
||||||
{{x1, y}, {x2, y}}, points ->
|
|
||||||
for x <- x1..x2, reduce: points, do: (points -> plot(points, {x, y}))
|
|
||||||
|
|
||||||
{{x1, x1}, {x2, x2}}, points ->
|
|
||||||
for x <- x1..x2, reduce: points, do: (points -> plot(points, {x, x}))
|
|
||||||
|
|
||||||
{{x1, y1}, {x2, y2}}, points ->
|
|
||||||
for point <- Enum.zip(x1..x2, y1..y2), reduce: points, do: (points -> plot(points, point))
|
|
||||||
end)
|
|
||||||
|> Enum.count(fn {_point, count} -> count >= 2 end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp plot(points, point) do
|
|
||||||
Map.put(points, point, (points[point] || 0) + 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.split(["\n", " -> ", ","], trim: true)
|
|
||||||
|> Enum.map(&String.to_integer/1)
|
|
||||||
|> Enum.chunk_every(4)
|
|
||||||
|> Enum.map(fn [x1, y1, x2, y2] -> {{x1, y1}, {x2, y2}} end)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day5.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day5.run()
|
|
|
@ -1,84 +0,0 @@
|
||||||
defmodule Day6 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> run_days(80)
|
|
||||||
|> length()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_days(school, 0), do: school
|
|
||||||
defp run_days(school, days), do: run_days(day(school), days - 1)
|
|
||||||
|
|
||||||
defp day(school), do: Enum.flat_map(school, &fish/1)
|
|
||||||
|
|
||||||
defp fish(0), do: [6, 8]
|
|
||||||
defp fish(age), do: [age - 1]
|
|
||||||
|
|
||||||
# Implementing part 2 completely differently to avoid exponential growth
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
counts = Map.new(0..8, fn age -> {age, 0} end)
|
|
||||||
|
|
||||||
input
|
|
||||||
|> Enum.frequencies()
|
|
||||||
|> Enum.into(counts)
|
|
||||||
|> run_days2(256)
|
|
||||||
|> Map.values()
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_days2(counts, 0), do: counts
|
|
||||||
defp run_days2(counts, days), do: run_days2(day2(counts), days - 1)
|
|
||||||
|
|
||||||
defp day2(counts) do
|
|
||||||
{breeders, counts} = Map.pop(counts, 0)
|
|
||||||
|
|
||||||
counts
|
|
||||||
|> Map.new(fn {age, count} -> {age - 1, count} end)
|
|
||||||
|> Map.update(6, 0, &(&1 + breeders))
|
|
||||||
|> Map.put(8, breeders)
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.trim()
|
|
||||||
|> String.split(",")
|
|
||||||
|> Enum.map(&String.to_integer/1)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day6.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day6.run()
|
|
|
@ -1,81 +0,0 @@
|
||||||
defmodule Day7 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.frequencies()
|
|
||||||
|> Enum.into(Map.new(Enum.min(input)..Enum.max(input), &{&1, 0}))
|
|
||||||
|> calculate_costs()
|
|
||||||
|> Enum.min()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp calculate_costs(crabs) do
|
|
||||||
for here <- Map.keys(crabs) do
|
|
||||||
for {there, crabs_there} <- Map.delete(crabs, here), reduce: 0 do
|
|
||||||
cost -> cost + abs(here - there) * crabs_there
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
input
|
|
||||||
|> Enum.frequencies()
|
|
||||||
|> Enum.into(Map.new(Enum.min(input)..Enum.max(input), &{&1, 0}))
|
|
||||||
|> calculate_sum_costs()
|
|
||||||
|> Enum.min()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp calculate_sum_costs(crabs) do
|
|
||||||
for here <- Map.keys(crabs) do
|
|
||||||
for {there, crabs_there} <- Map.delete(crabs, here), reduce: 0 do
|
|
||||||
cost ->
|
|
||||||
distance = abs(here - there)
|
|
||||||
# Mmm maths. Can get the sum of 1..n by doing (n * n+1)/2. Guess you have to know it.
|
|
||||||
sum = div(distance * (distance + 1), 2)
|
|
||||||
cost + sum * crabs_there
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.trim()
|
|
||||||
|> String.split(",")
|
|
||||||
|> Enum.map(&String.to_integer/1)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day7.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day7.run()
|
|
111
2021/day9.exs
111
2021/day9.exs
|
@ -1,111 +0,0 @@
|
||||||
defmodule Day9 do
|
|
||||||
defmodule Cave do
|
|
||||||
defstruct floor: %{}, max_x: 0, max_y: 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def part1(cave) do
|
|
||||||
for x <- 0..cave.max_x, y <- 0..cave.max_y, lowest_neighbour?(cave, x, y), reduce: 0 do
|
|
||||||
risk_level -> risk_level + cave.floor[{x, y}] + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp neighbours(cave, x, y) do
|
|
||||||
[{x - 1, y}, {x + 1, y}, {x, y - 1}, {x, y + 1}]
|
|
||||||
|> Enum.reject(fn {x, y} -> x < 0 or x > cave.max_x or y < 0 or y > cave.max_y end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp lowest_neighbour?(cave, x, y) do
|
|
||||||
here = cave.floor[{x, y}]
|
|
||||||
Enum.all?(neighbours(cave, x, y), fn {xn, yn} -> here < cave.floor[{xn, yn}] end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(cave) do
|
|
||||||
cave
|
|
||||||
|> remove_nines()
|
|
||||||
|> find_basins()
|
|
||||||
|> Enum.sort(:desc)
|
|
||||||
|> Enum.take(3)
|
|
||||||
|> Enum.reduce(&Kernel.*/2)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp find_basins(%Cave{floor: floor}) when floor == %{}, do: []
|
|
||||||
|
|
||||||
defp find_basins(cave) do
|
|
||||||
{cave, location} = get_start_location(cave)
|
|
||||||
{cave, basin} = find_basin(cave, [location], [location])
|
|
||||||
[basin | find_basins(cave)]
|
|
||||||
end
|
|
||||||
|
|
||||||
defp find_basin(cave, [], basin), do: {cave, length(basin)}
|
|
||||||
|
|
||||||
defp find_basin(cave, [{x, y} | locations], basin) do
|
|
||||||
neighbours = cave.floor |> Map.take(neighbours(cave, x, y)) |> Map.keys()
|
|
||||||
cave = %Cave{cave | floor: Map.drop(cave.floor, neighbours)}
|
|
||||||
find_basin(cave, locations ++ neighbours, basin ++ neighbours)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp remove_nines(cave) do
|
|
||||||
floor = Map.reject(cave.floor, &match?({_k, 9}, &1))
|
|
||||||
%Cave{cave | floor: floor}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp get_start_location(cave) do
|
|
||||||
[{location, _v}] = Enum.take(cave.floor, 1)
|
|
||||||
cave = %Cave{cave | floor: Map.delete(cave.floor, location)}
|
|
||||||
{cave, location}
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
rows =
|
|
||||||
input
|
|
||||||
|> String.split("\n", trim: true)
|
|
||||||
|> Enum.map(&String.split(&1, "", trim: true))
|
|
||||||
|
|
||||||
floor =
|
|
||||||
for {row, y} <- Enum.with_index(rows), {height, x} <- Enum.with_index(row), into: %{} do
|
|
||||||
{{x, y}, String.to_integer(height)}
|
|
||||||
end
|
|
||||||
|
|
||||||
max_x = length(hd(rows)) - 1
|
|
||||||
max_y = length(rows) - 1
|
|
||||||
|
|
||||||
%Cave{floor: floor, max_x: max_x, max_y: max_y}
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day9.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day9.run()
|
|
|
@ -1,11 +0,0 @@
|
||||||
# AdventOfCode2022
|
|
||||||
|
|
||||||
My (attempted) solutions to [Advent of Code 2022](https://adventofcode.com/2022) in Elixir.
|
|
||||||
|
|
||||||
<img width="869" alt="image" src="https://user-images.githubusercontent.com/498229/207063420-9e176055-d40c-4b33-a826-368c04a7b75d.png">
|
|
||||||
|
|
||||||
## Running:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
elixir dayN.exs input_filename
|
|
||||||
```
|
|
|
@ -1,58 +0,0 @@
|
||||||
defmodule Day1 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(&Enum.sum/1)
|
|
||||||
|> Enum.max()
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(&Enum.sum/1)
|
|
||||||
|> Enum.sort(:desc)
|
|
||||||
|> Enum.take(3)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.split("\n\n")
|
|
||||||
|> Enum.map(&for elf <- String.split(&1, "\n", trim: true), do: String.to_integer(elf))
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day1.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day1.run()
|
|
109
2022/day10.exs
109
2022/day10.exs
|
@ -1,109 +0,0 @@
|
||||||
defmodule Day10 do
|
|
||||||
defmodule Instr do
|
|
||||||
defstruct cmd: nil, cycles: 0, amt: nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def part1(input) do
|
|
||||||
process(input, _cycle = 1, _x = 1, _sample_at = 20, _sig = 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp process([], _cycle, _x, _sample_at, sig), do: sig
|
|
||||||
|
|
||||||
defp process(instrs, cycle, x, sample_at, sig) when cycle == sample_at do
|
|
||||||
process(instrs, cycle, x, sample_at + 40, sig + cycle * x)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp process([%Instr{cycles: 0} | rest], cycle, x, sample_at, sig) do
|
|
||||||
process(rest, cycle + 1, x, sample_at, sig)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp process([instr = %Instr{cycles: 1, cmd: :noop} | rest], cycle, x, sample_at, sig) do
|
|
||||||
process([%Instr{instr | cycles: 0} | rest], cycle, x, sample_at, sig)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp process([instr = %Instr{cycles: 1, cmd: :addx, amt: amt} | rest], cycle, x, sample_at, sig) do
|
|
||||||
process([%Instr{instr | cycles: 0} | rest], cycle, x + amt, sample_at, sig)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp process([instr = %Instr{cycles: cycles} | rest], cycle, x, sample_at, sig) do
|
|
||||||
process([%Instr{instr | cycles: cycles - 1} | rest], cycle + 1, x, sample_at, sig)
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
input
|
|
||||||
|> scan(_x = 1, _pixel = 0, _pixels = [[]])
|
|
||||||
|> Enum.map(&Enum.reverse/1)
|
|
||||||
|> Enum.reverse()
|
|
||||||
|> Enum.join("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp scan([], _x, _pixel, pixels), do: pixels
|
|
||||||
|
|
||||||
defp scan(instrs, x, _pixel = 40, pixels) do
|
|
||||||
scan(instrs, x, _pixel = 0, [[] | pixels])
|
|
||||||
end
|
|
||||||
|
|
||||||
defp scan([%Instr{cmd: :noop} | rest], x, pixel, [row | pixels]) do
|
|
||||||
scan(rest, x, pixel + 1, [draw_pixel(pixel, x, row) | pixels])
|
|
||||||
end
|
|
||||||
|
|
||||||
defp scan([%Instr{cycles: 1, cmd: :addx, amt: amt} | rest], x, pixel, [row | pixels]) do
|
|
||||||
scan(rest, x + amt, pixel + 1, [draw_pixel(pixel, x, row) | pixels])
|
|
||||||
end
|
|
||||||
|
|
||||||
defp scan([instr = %Instr{cycles: cycles} | rest], x, pixel, [row | pixels]) do
|
|
||||||
scan([%Instr{instr | cycles: cycles - 1} | rest], x, pixel + 1, [
|
|
||||||
draw_pixel(pixel, x, row) | pixels
|
|
||||||
])
|
|
||||||
end
|
|
||||||
|
|
||||||
defp draw_pixel(pixel, x, row) when pixel in (x - 1)..(x + 1), do: ["#" | row]
|
|
||||||
defp draw_pixel(_pixel, _x, row), do: [" " | row]
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.trim()
|
|
||||||
|> String.split("\n")
|
|
||||||
|> Enum.map(fn
|
|
||||||
"noop" -> %Instr{cmd: :noop, cycles: 1}
|
|
||||||
"addx " <> x -> %Instr{cmd: :addx, cycles: 2, amt: String.to_integer(x)}
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day10.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day10.run()
|
|
188
2022/day11.exs
188
2022/day11.exs
|
@ -1,188 +0,0 @@
|
||||||
defmodule Monkey do
|
|
||||||
use GenServer
|
|
||||||
|
|
||||||
defstruct [
|
|
||||||
:num,
|
|
||||||
:items_caught,
|
|
||||||
:operation,
|
|
||||||
:divisor,
|
|
||||||
:true_monkey,
|
|
||||||
:false_monkey,
|
|
||||||
:inspected_count
|
|
||||||
]
|
|
||||||
|
|
||||||
# Server
|
|
||||||
#########
|
|
||||||
|
|
||||||
def init(%Monkey{} = monkey) do
|
|
||||||
{:ok, {monkey, _monkeys = %{}}}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_cast({:catch, item}, {monkey, monkies}) do
|
|
||||||
monkey = catch_item(monkey, item)
|
|
||||||
{:noreply, {monkey, monkies}}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_call({:register, monkies}, _from, {monkey, _monkies}) do
|
|
||||||
{:reply, :ok, {monkey, monkies}}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_call(:turn, _from, {monkey, monkies}) do
|
|
||||||
monkey = do_business(monkey, monkies)
|
|
||||||
{:reply, :done, {%Monkey{monkey | items_caught: []}, monkies}}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_call(:get_count, _from, {monkey, monkies}) do
|
|
||||||
{:reply, monkey.inspected_count, {monkey, monkies}}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Client
|
|
||||||
#########
|
|
||||||
def throw(monkey, item), do: GenServer.cast(monkey, {:catch, item})
|
|
||||||
def register(monkey, monkeys), do: GenServer.call(monkey, {:register, monkeys})
|
|
||||||
def take_turn(monkey), do: GenServer.call(monkey, :turn)
|
|
||||||
def report_count(monkey), do: GenServer.call(monkey, :get_count)
|
|
||||||
|
|
||||||
# Monkey business
|
|
||||||
##################
|
|
||||||
|
|
||||||
defp catch_item(monkey, item), do: %Monkey{monkey | items_caught: [item | monkey.items_caught]}
|
|
||||||
|
|
||||||
defp do_business(monkey, monkies) do
|
|
||||||
count =
|
|
||||||
for item <- Enum.reverse(monkey.items_caught), reduce: 0 do
|
|
||||||
count ->
|
|
||||||
item = monkey.operation.(item) |> div(3)
|
|
||||||
|
|
||||||
to_monkey =
|
|
||||||
if rem(item, monkey.divisor) == 0 do
|
|
||||||
monkey.true_monkey
|
|
||||||
else
|
|
||||||
monkey.false_monkey
|
|
||||||
end
|
|
||||||
|
|
||||||
throw(monkies[to_monkey], item)
|
|
||||||
count + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
%Monkey{monkey | items_caught: [], inspected_count: monkey.inspected_count + count}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defmodule Day11 do
|
|
||||||
def part1(input) do
|
|
||||||
monkies = setup_monkies(input)
|
|
||||||
order = monkies |> Map.keys() |> Enum.sort()
|
|
||||||
|
|
||||||
run_rounds(20, monkies, order)
|
|
||||||
|
|
||||||
order
|
|
||||||
|> Enum.map(&Monkey.report_count(monkies[&1]))
|
|
||||||
|> Enum.sort(:desc)
|
|
||||||
|> Enum.take(2)
|
|
||||||
|> Enum.reduce(&Kernel.*/2)
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup_monkies(input) do
|
|
||||||
monkies =
|
|
||||||
input
|
|
||||||
|> Enum.with_index()
|
|
||||||
|> Map.new(fn {monkey, idx} ->
|
|
||||||
{:ok, pid} = GenServer.start_link(Monkey, monkey)
|
|
||||||
{idx, pid}
|
|
||||||
end)
|
|
||||||
|
|
||||||
Enum.each(monkies, fn {_k, monkey} -> Monkey.register(monkey, monkies) end)
|
|
||||||
|
|
||||||
monkies
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_rounds(0, _monkies, _order), do: :ok
|
|
||||||
|
|
||||||
defp run_rounds(rounds, monkies, order) do
|
|
||||||
Enum.each(order, &Monkey.take_turn(monkies[&1]))
|
|
||||||
run_rounds(rounds - 1, monkies, order)
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(_input) do
|
|
||||||
"not implemented"
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.split("\n\n")
|
|
||||||
|> Enum.map(fn monkey ->
|
|
||||||
[
|
|
||||||
<<"Monkey ", monkey_num::binary-1, ":">>,
|
|
||||||
"Starting items: " <> starting_items,
|
|
||||||
<<"Operation: new = old ", op::binary-1, " ", operand::binary>>,
|
|
||||||
"Test: divisible by " <> divisor,
|
|
||||||
"If true: throw to monkey " <> true_monkey,
|
|
||||||
"If false: throw to monkey " <> false_monkey
|
|
||||||
] = monkey |> String.trim() |> String.split("\n") |> Enum.map(&String.trim/1)
|
|
||||||
|
|
||||||
starting_items = String.split(starting_items, ", ")
|
|
||||||
|
|
||||||
[monkey_num, divisor, true_monkey, false_monkey | starting_items] =
|
|
||||||
Enum.map(
|
|
||||||
[monkey_num, divisor, true_monkey, false_monkey] ++ starting_items,
|
|
||||||
&String.to_integer/1
|
|
||||||
)
|
|
||||||
|
|
||||||
operation =
|
|
||||||
case {op, operand} do
|
|
||||||
{"+", "old"} -> fn old -> old + old end
|
|
||||||
{"*", "old"} -> fn old -> old * old end
|
|
||||||
{"+", operand} -> fn old -> old + String.to_integer(operand) end
|
|
||||||
{"*", operand} -> fn old -> old * String.to_integer(operand) end
|
|
||||||
end
|
|
||||||
|
|
||||||
%Monkey{
|
|
||||||
num: monkey_num,
|
|
||||||
items_caught: Enum.reverse(starting_items),
|
|
||||||
operation: operation,
|
|
||||||
divisor: divisor,
|
|
||||||
true_monkey: true_monkey,
|
|
||||||
false_monkey: false_monkey,
|
|
||||||
inspected_count: 0
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day11.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day11.run()
|
|
100
2022/day2.exs
100
2022/day2.exs
|
@ -1,100 +0,0 @@
|
||||||
defmodule Day2 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(fn
|
|
||||||
{opponent, "X"} -> {opponent, :rock}
|
|
||||||
{opponent, "Y"} -> {opponent, :paper}
|
|
||||||
{opponent, "Z"} -> {opponent, :scissors}
|
|
||||||
end)
|
|
||||||
|> Enum.map(&play/1)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
@rock 1
|
|
||||||
@paper 2
|
|
||||||
@scissors 3
|
|
||||||
@lose 0
|
|
||||||
@draw 3
|
|
||||||
@win 6
|
|
||||||
|
|
||||||
defp play({:rock, :rock}), do: @rock + @draw
|
|
||||||
defp play({:paper, :rock}), do: @rock + @lose
|
|
||||||
defp play({:scissors, :rock}), do: @rock + @win
|
|
||||||
defp play({:rock, :paper}), do: @paper + @win
|
|
||||||
defp play({:paper, :paper}), do: @paper + @draw
|
|
||||||
defp play({:scissors, :paper}), do: @paper + @lose
|
|
||||||
defp play({:rock, :scissors}), do: @scissors + @lose
|
|
||||||
defp play({:paper, :scissors}), do: @scissors + @win
|
|
||||||
defp play({:scissors, :scissors}), do: @scissors + @draw
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(fn
|
|
||||||
{opponent, "X"} -> {opponent, :lose}
|
|
||||||
{opponent, "Y"} -> {opponent, :draw}
|
|
||||||
{opponent, "Z"} -> {opponent, :win}
|
|
||||||
end)
|
|
||||||
|> Enum.map(&play2/1)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp play2({:rock, :win}), do: @win + @paper
|
|
||||||
defp play2({:paper, :win}), do: @win + @scissors
|
|
||||||
defp play2({:scissors, :win}), do: @win + @rock
|
|
||||||
defp play2({:rock, :draw}), do: @draw + @rock
|
|
||||||
defp play2({:paper, :draw}), do: @draw + @paper
|
|
||||||
defp play2({:scissors, :draw}), do: @draw + @scissors
|
|
||||||
defp play2({:rock, :lose}), do: @lose + @scissors
|
|
||||||
defp play2({:paper, :lose}), do: @lose + @rock
|
|
||||||
defp play2({:scissors, :lose}), do: @lose + @paper
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.split([" ", "\n"], trim: true)
|
|
||||||
|> Enum.map(fn
|
|
||||||
"A" -> :rock
|
|
||||||
"B" -> :paper
|
|
||||||
"C" -> :scissors
|
|
||||||
me -> me
|
|
||||||
end)
|
|
||||||
|> Enum.chunk_every(2)
|
|
||||||
|> Enum.map(&List.to_tuple/1)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day2.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day2.run()
|
|
|
@ -1,71 +0,0 @@
|
||||||
defmodule Day3 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(fn str ->
|
|
||||||
size = byte_size(str)
|
|
||||||
|
|
||||||
str
|
|
||||||
|> String.split("", trim: true)
|
|
||||||
|> Enum.split(div(size, 2))
|
|
||||||
end)
|
|
||||||
|> Enum.map(fn {a, b} -> hd(a -- a -- b) end)
|
|
||||||
|> Enum.map(&score/1)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp score(<<lower::utf8>>) when lower in ?a..?z, do: lower - ?a + 1
|
|
||||||
defp score(<<upper::utf8>>) when upper in ?A..?Z, do: upper - ?A + 27
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(&String.split(&1, "", trim: true))
|
|
||||||
|> Enum.chunk_every(3)
|
|
||||||
|> Enum.map(fn [a, b, c] ->
|
|
||||||
d = a -- a -- b
|
|
||||||
hd(d -- d -- c)
|
|
||||||
end)
|
|
||||||
|> Enum.map(&score/1)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
String.split(input, "\n", trim: true)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day3.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day3.run()
|
|
|
@ -1,55 +0,0 @@
|
||||||
defmodule Day4 do
|
|
||||||
def part1(input) do
|
|
||||||
Enum.count(input, fn [a, b] -> MapSet.subset?(a, b) or MapSet.subset?(b, a) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
Enum.count(input, fn [a, b] -> not MapSet.disjoint?(a, b) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.split(["-", ",", "\n"], trim: true)
|
|
||||||
|> Enum.map(&String.to_integer/1)
|
|
||||||
|> Enum.chunk_every(2)
|
|
||||||
|> Enum.map(fn [a, b] -> MapSet.new(a..b) end)
|
|
||||||
|> Enum.chunk_every(2)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day4.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day4.run()
|
|
|
@ -1,92 +0,0 @@
|
||||||
defmodule Day5 do
|
|
||||||
def part1(input), do: input |> move_with(&move_9000/3) |> top_crates()
|
|
||||||
def part2(input), do: input |> move_with(&move_9001/3) |> top_crates()
|
|
||||||
|
|
||||||
defp move_with({stacks, procedure}, move) do
|
|
||||||
for [number, from_index, to_index] <- procedure, reduce: stacks do
|
|
||||||
stacks ->
|
|
||||||
{moved_from, moved_to} = move.(number, stacks[from_index], stacks[to_index])
|
|
||||||
%{stacks | from_index => moved_from, to_index => moved_to}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp move_9000(0, from, to), do: {from, to}
|
|
||||||
defp move_9000(number, [crate | from], to), do: move_9000(number - 1, from, [crate | to])
|
|
||||||
|
|
||||||
defp move_9001(number, from, to) do
|
|
||||||
{moving, moved_from} = Enum.split(from, number)
|
|
||||||
{moved_from, moving ++ to}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp top_crates(stacks) do
|
|
||||||
stacks
|
|
||||||
|> Enum.sort_by(fn {label, _stack} -> label end)
|
|
||||||
|> Enum.reduce("", fn {_label, [top | _stack]}, acc -> acc <> top end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
[raw_stacks, raw_procedure] = String.split(input, "\n\n")
|
|
||||||
|
|
||||||
stacks =
|
|
||||||
raw_stacks
|
|
||||||
|> String.split("\n")
|
|
||||||
|> parse_stacks()
|
|
||||||
|> Enum.zip()
|
|
||||||
|> Enum.map(fn stack -> stack |> Tuple.to_list() |> Enum.drop_while(&is_nil/1) end)
|
|
||||||
|> Enum.with_index()
|
|
||||||
|> Map.new(fn {stack, index} -> {index + 1, stack} end)
|
|
||||||
|
|
||||||
procedure =
|
|
||||||
raw_procedure
|
|
||||||
|> String.split(["move ", " from ", " to ", "\n"], trim: true)
|
|
||||||
|> Enum.map(&String.to_integer/1)
|
|
||||||
|> Enum.chunk_every(3)
|
|
||||||
|
|
||||||
{stacks, procedure}
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp parse_stacks([_labels_]), do: []
|
|
||||||
defp parse_stacks([line | rest]), do: [parse_line(line) | parse_stacks(rest)]
|
|
||||||
|
|
||||||
defp parse_line(<<"">>), do: []
|
|
||||||
defp parse_line(<<" ", rest::binary>>), do: [nil | parse_line(rest)]
|
|
||||||
defp parse_line(<<"[", crate::binary-1, "]", rest::binary>>), do: [crate | parse_line(rest)]
|
|
||||||
defp parse_line(<<" ", rest::binary>>), do: parse_line(rest)
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day5.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day5.run()
|
|
|
@ -1,72 +0,0 @@
|
||||||
defmodule Day6 do
|
|
||||||
@packet_marker_size 4
|
|
||||||
@message_marker_size 14
|
|
||||||
|
|
||||||
def part1(input) do
|
|
||||||
find_packet_marker(input)
|
|
||||||
end
|
|
||||||
|
|
||||||
defguardp all_different(a, b, c, d)
|
|
||||||
when a != b and a != c and a != d and b != c and b != d and c != d
|
|
||||||
|
|
||||||
defp find_packet_marker(buffer, seen \\ @packet_marker_size)
|
|
||||||
defp find_packet_marker([a, b, c, d | _rest], seen) when all_different(a, b, c, d), do: seen
|
|
||||||
defp find_packet_marker([_skip | rest], seen), do: find_packet_marker(rest, seen + 1)
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
find_message_marker(input)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp find_message_marker([_skip | rest] = buffer, seen \\ @message_marker_size) do
|
|
||||||
window = Enum.take(buffer, @message_marker_size)
|
|
||||||
|
|
||||||
if Enum.uniq(window) == window do
|
|
||||||
seen
|
|
||||||
else
|
|
||||||
find_message_marker(rest, seen + 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.trim()
|
|
||||||
|> String.codepoints()
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day6.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day6.run()
|
|
145
2022/day7.exs
145
2022/day7.exs
|
@ -1,145 +0,0 @@
|
||||||
defmodule Day7 do
|
|
||||||
@root ["/"]
|
|
||||||
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> filetree_with_dir_sizes()
|
|
||||||
|> Enum.filter(fn
|
|
||||||
{_k, {:dir, size, _paths}} -> size <= 100_000
|
|
||||||
{_k, {:file, _size, _paths}} -> false
|
|
||||||
end)
|
|
||||||
|> Enum.map(fn {_k, {_type, size, _paths}} -> size end)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp filetree_with_dir_sizes(input) do
|
|
||||||
tree = build_tree(input)
|
|
||||||
|
|
||||||
tree
|
|
||||||
|> dirs_fewest_descendents_first()
|
|
||||||
|> Enum.reduce(tree, &put_dir_size/2)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp put_dir_size(path, tree) do
|
|
||||||
{:dir, nil, paths} = tree[path]
|
|
||||||
size = tree |> Map.take(paths) |> Enum.map(fn {_, {_, size, _}} -> size end) |> Enum.sum()
|
|
||||||
Map.put(tree, path, {:dir, size, paths})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp dirs_fewest_descendents_first(tree, to_check \\ [@root], found_dirs \\ [])
|
|
||||||
defp dirs_fewest_descendents_first(_tree, [], found_dirs), do: found_dirs
|
|
||||||
|
|
||||||
defp dirs_fewest_descendents_first(tree, [node | rest], dirs) do
|
|
||||||
case tree[node] do
|
|
||||||
{:file, _size, nil} -> dirs_fewest_descendents_first(tree, rest, dirs)
|
|
||||||
{:dir, _size, paths} -> dirs_fewest_descendents_first(tree, paths ++ rest, [node | dirs])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp build_tree(input, path \\ [], tree \\ %{})
|
|
||||||
defp build_tree([], _path, tree), do: tree
|
|
||||||
defp build_tree([{:cd, "/"} | rest], _path, tree), do: build_tree(rest, @root, tree)
|
|
||||||
defp build_tree([{:cd, ".."} | rest], [_here | path], tree), do: build_tree(rest, path, tree)
|
|
||||||
defp build_tree([{:cd, dir} | rest], path, tree), do: build_tree(rest, [dir | path], tree)
|
|
||||||
|
|
||||||
defp build_tree([{:ls, items} | rest], path, tree) do
|
|
||||||
{paths, tree} =
|
|
||||||
Enum.map_reduce(items, tree, fn
|
|
||||||
# Create an empty directory for each dir listed
|
|
||||||
# (needed if we we forget to ls inside a dir)
|
|
||||||
{:dir, nil, name}, tree ->
|
|
||||||
item_path = [name | path]
|
|
||||||
{item_path, Map.put(tree, item_path, {:dir, nil, []})}
|
|
||||||
|
|
||||||
# Create a file for each file listed
|
|
||||||
{:file, size, name}, tree ->
|
|
||||||
item_path = [name | path]
|
|
||||||
{item_path, Map.put(tree, item_path, {:file, size, nil})}
|
|
||||||
end)
|
|
||||||
|
|
||||||
# Create current directory with listed children
|
|
||||||
tree = Map.put(tree, path, {:dir, nil, paths})
|
|
||||||
|
|
||||||
build_tree(rest, path, tree)
|
|
||||||
end
|
|
||||||
|
|
||||||
@total_size 70_000_000
|
|
||||||
@space_needed 30_000_000
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
tree = filetree_with_dir_sizes(input)
|
|
||||||
|
|
||||||
{:dir, space_used, _} = tree[@root]
|
|
||||||
unused_space = @total_size - space_used
|
|
||||||
space_to_free = @space_needed - unused_space
|
|
||||||
|
|
||||||
tree
|
|
||||||
|> Enum.filter(&match?({_path, {:dir, _size, _paths}}, &1))
|
|
||||||
|> Enum.map(fn {_path, {:dir, size, _paths}} -> size end)
|
|
||||||
|> Enum.sort()
|
|
||||||
|> Enum.find(fn size -> size >= space_to_free end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.trim()
|
|
||||||
|> String.split("\n")
|
|
||||||
|> parse_input()
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp parse_input([]), do: []
|
|
||||||
defp parse_input(["$ cd " <> dir | rest]), do: [{:cd, dir} | parse_input(rest)]
|
|
||||||
|
|
||||||
defp parse_input(["$ ls" | rest]) do
|
|
||||||
{listings, rest} = Enum.split_while(rest, &(!match?("$" <> _, &1)))
|
|
||||||
|
|
||||||
listings =
|
|
||||||
Enum.map(listings, fn
|
|
||||||
"dir " <> dir ->
|
|
||||||
{:dir, nil, dir}
|
|
||||||
|
|
||||||
file ->
|
|
||||||
[size, name] = String.split(file)
|
|
||||||
{:file, String.to_integer(size), name}
|
|
||||||
end)
|
|
||||||
|
|
||||||
[{:ls, listings} | parse_input(rest)]
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day7.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day7.run()
|
|
130
2022/day8.exs
130
2022/day8.exs
|
@ -1,130 +0,0 @@
|
||||||
defmodule Day8 do
|
|
||||||
defmodule Tree do
|
|
||||||
defstruct height: nil, visible: false, score: []
|
|
||||||
end
|
|
||||||
|
|
||||||
def part1({{max_x, max_y}, trees}) do
|
|
||||||
trees
|
|
||||||
|> look_in_direction(0..max_y, 0..max_x, false)
|
|
||||||
|> look_in_direction(0..max_x, 0..max_y, true)
|
|
||||||
|> look_in_direction(0..max_y, max_x..0, false)
|
|
||||||
|> look_in_direction(0..max_x, max_y..0, true)
|
|
||||||
|> Enum.count(&match?({_coord, %Tree{visible: true}}, &1))
|
|
||||||
end
|
|
||||||
|
|
||||||
defp look_in_direction(trees, outer_range, inner_range, flip_xy?) do
|
|
||||||
Enum.reduce(outer_range, trees, fn y, trees ->
|
|
||||||
{_height, trees} =
|
|
||||||
Enum.reduce_while(inner_range, {_height = -1, trees}, fn
|
|
||||||
_x, {9, trees} ->
|
|
||||||
{:halt, {9, trees}}
|
|
||||||
|
|
||||||
x, {height, trees} ->
|
|
||||||
coord = if flip_xy?, do: {y, x}, else: {x, y}
|
|
||||||
tree = trees[coord]
|
|
||||||
|
|
||||||
if tree.height > height do
|
|
||||||
{:cont, {tree.height, Map.replace(trees, coord, %Tree{tree | visible: true})}}
|
|
||||||
else
|
|
||||||
{:cont, {height, trees}}
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
trees
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2({{max_x, max_y}, trees}) do
|
|
||||||
trees
|
|
||||||
|> count_viewing_distance(0..max_y, 0..max_x, false)
|
|
||||||
|> count_viewing_distance(0..max_x, 0..max_y, true)
|
|
||||||
|> count_viewing_distance(0..max_y, max_x..0, false)
|
|
||||||
|> count_viewing_distance(0..max_x, max_y..0, true)
|
|
||||||
|> Enum.map(fn {_coord, %Tree{score: score}} -> Enum.reduce(score, &Kernel.*/2) end)
|
|
||||||
|> Enum.max()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp count_viewing_distance(trees, outer_range, inner_range, flip_xy?) do
|
|
||||||
Enum.reduce(outer_range, trees, fn y, trees ->
|
|
||||||
{_scoring, trees} =
|
|
||||||
Enum.reduce(inner_range, {_scoring = [], trees}, fn
|
|
||||||
x, {scoring, trees} ->
|
|
||||||
coord = if flip_xy?, do: {y, x}, else: {x, y}
|
|
||||||
tree = trees[coord]
|
|
||||||
|
|
||||||
{scoring, trees} =
|
|
||||||
Enum.reduce(scoring, {[], trees}, fn {coord, scoring_tree}, {scoring, trees} ->
|
|
||||||
%Tree{score: [score | scores]} = scoring_tree
|
|
||||||
scoring_tree = %Tree{scoring_tree | score: [score + 1 | scores]}
|
|
||||||
trees = Map.replace(trees, coord, scoring_tree)
|
|
||||||
|
|
||||||
if scoring_tree.height <= tree.height do
|
|
||||||
{scoring, trees}
|
|
||||||
else
|
|
||||||
{[{coord, scoring_tree} | scoring], trees}
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
tree = %Tree{tree | score: [0 | tree.score]}
|
|
||||||
{[{coord, tree} | scoring], Map.replace(trees, coord, tree)}
|
|
||||||
end)
|
|
||||||
|
|
||||||
trees
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
grid =
|
|
||||||
input
|
|
||||||
|> String.trim()
|
|
||||||
|> String.split("\n")
|
|
||||||
|> Enum.map(&String.graphemes/1)
|
|
||||||
|
|
||||||
max_x = length(hd(grid)) - 1
|
|
||||||
max_y = length(grid) - 1
|
|
||||||
|
|
||||||
trees =
|
|
||||||
for {row, y} <- Enum.with_index(grid), {height, x} <- Enum.with_index(row), into: %{} do
|
|
||||||
{{x, y}, %Tree{height: String.to_integer(height)}}
|
|
||||||
end
|
|
||||||
|
|
||||||
{{max_x, max_y}, trees}
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day8.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day8.run()
|
|
109
2022/day9.exs
109
2022/day9.exs
|
@ -1,109 +0,0 @@
|
||||||
defmodule Day9 do
|
|
||||||
defmodule Rope do
|
|
||||||
defstruct head: {0, 0},
|
|
||||||
tail: {0, 0},
|
|
||||||
knots: for(_ <- 1..8, do: {0, 0}),
|
|
||||||
visited: MapSet.new([{0, 0}])
|
|
||||||
end
|
|
||||||
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.reduce(%Rope{}, &motion/2)
|
|
||||||
|> then(fn %Rope{visited: visited} -> MapSet.size(visited) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp motion({_dir, 0}, rope), do: rope
|
|
||||||
|
|
||||||
defp motion({dir, amount}, rope) do
|
|
||||||
head = step_head(rope.head, dir)
|
|
||||||
tail = step_tail(head, rope.tail)
|
|
||||||
visited = MapSet.put(rope.visited, tail)
|
|
||||||
motion({dir, amount - 1}, %Rope{head: head, tail: tail, visited: visited})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp step_head({x, y}, "R"), do: {x + 1, y}
|
|
||||||
defp step_head({x, y}, "L"), do: {x - 1, y}
|
|
||||||
defp step_head({x, y}, "U"), do: {x, y + 1}
|
|
||||||
defp step_head({x, y}, "D"), do: {x, y - 1}
|
|
||||||
|
|
||||||
defp step_tail({hx, hy}, {tx, ty}) when abs(hx - tx) <= 1 and abs(hy - ty) <= 1, do: {tx, ty}
|
|
||||||
defp step_tail({tx, hy}, {tx, ty}) when hy - ty > 0, do: {tx, ty + 1}
|
|
||||||
defp step_tail({tx, hy}, {tx, ty}) when hy - ty < 0, do: {tx, ty - 1}
|
|
||||||
defp step_tail({hx, ty}, {tx, ty}) when hx - tx > 0, do: {tx + 1, ty}
|
|
||||||
defp step_tail({hx, ty}, {tx, ty}) when hx - tx < 0, do: {tx - 1, ty}
|
|
||||||
|
|
||||||
defp step_tail({hx, hy}, {tx, ty}) do
|
|
||||||
dx = if hx - tx > 0, do: 1, else: -1
|
|
||||||
dy = if hy - ty > 0, do: 1, else: -1
|
|
||||||
{tx + dx, ty + dy}
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
input
|
|
||||||
|> Enum.reduce(%Rope{}, &motion2/2)
|
|
||||||
|> then(fn %Rope{visited: visited} -> MapSet.size(visited) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp motion2({_dir, 0}, rope), do: rope
|
|
||||||
|
|
||||||
defp motion2({dir, amount}, rope) do
|
|
||||||
head = step_head(rope.head, dir)
|
|
||||||
|
|
||||||
{knots, leader} =
|
|
||||||
Enum.map_reduce(rope.knots, head, fn knot, leader ->
|
|
||||||
new_knot = step_tail(leader, knot)
|
|
||||||
{new_knot, new_knot}
|
|
||||||
end)
|
|
||||||
|
|
||||||
tail = step_tail(leader, rope.tail)
|
|
||||||
|
|
||||||
visited = MapSet.put(rope.visited, tail)
|
|
||||||
motion2({dir, amount - 1}, %Rope{head: head, tail: tail, knots: knots, visited: visited})
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.trim()
|
|
||||||
|> String.split("\n")
|
|
||||||
|> Enum.map(fn <<dir::binary-1, " ", amount::binary>> ->
|
|
||||||
{dir, String.to_integer(amount)}
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day9.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day9.run()
|
|
|
@ -1,77 +0,0 @@
|
||||||
defmodule Day1 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(fn line ->
|
|
||||||
numbers = filter_digits(line)
|
|
||||||
String.to_integer(String.first(numbers) <> String.last(numbers))
|
|
||||||
end)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_digits(<<>>), do: <<>>
|
|
||||||
def filter_digits(<<x, rest::binary>>) when x in ?1..?9, do: <<x>> <> filter_digits(rest)
|
|
||||||
def filter_digits(<<_, rest::binary>>), do: filter_digits(rest)
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(fn line ->
|
|
||||||
numbers = filter_digits2(line)
|
|
||||||
String.to_integer(String.first(numbers) <> String.last(numbers))
|
|
||||||
end)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_digits2(<<>>), do: <<>>
|
|
||||||
def filter_digits2(<<x, rest::binary>>) when x in ?1..?9, do: <<x>> <> filter_digits2(rest)
|
|
||||||
def filter_digits2(<<"one", rest::binary>>), do: "1" <> filter_digits2("e" <> rest)
|
|
||||||
def filter_digits2(<<"two", rest::binary>>), do: "2" <> filter_digits2("o" <> rest)
|
|
||||||
def filter_digits2(<<"three", rest::binary>>), do: "3" <> filter_digits2("e" <> rest)
|
|
||||||
def filter_digits2(<<"four", rest::binary>>), do: "4" <> filter_digits2(rest)
|
|
||||||
def filter_digits2(<<"five", rest::binary>>), do: "5" <> filter_digits2("e" <> rest)
|
|
||||||
def filter_digits2(<<"six", rest::binary>>), do: "6" <> filter_digits2(rest)
|
|
||||||
def filter_digits2(<<"seven", rest::binary>>), do: "7" <> filter_digits2("n" <> rest)
|
|
||||||
def filter_digits2(<<"eight", rest::binary>>), do: "8" <> filter_digits2("t" <> rest)
|
|
||||||
def filter_digits2(<<"nine", rest::binary>>), do: "9" <> filter_digits2("e" <> rest)
|
|
||||||
def filter_digits2(<<_, rest::binary>>), do: filter_digits2(rest)
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
String.split(input, "\n", trim: true)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day1.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day1.run()
|
|
|
@ -1,79 +0,0 @@
|
||||||
#!/usr/bin/env elixir
|
|
||||||
defmodule Day2 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Map.filter(fn {_id, cubes} ->
|
|
||||||
cubes["red"] <= 12 and cubes["green"] <= 13 and cubes["blue"] <= 14
|
|
||||||
end)
|
|
||||||
|> Map.keys()
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(fn {_id, cubes} -> cubes |> Map.values() |> Enum.product() end)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.split("\n", trim: true)
|
|
||||||
|> Map.new(fn line ->
|
|
||||||
["Game " <> id | rounds] = String.split(line, [": ", "; "])
|
|
||||||
|
|
||||||
rounds =
|
|
||||||
rounds
|
|
||||||
|> Enum.map(&parse_round/1)
|
|
||||||
|> Enum.reduce(
|
|
||||||
%{"red" => 0, "green" => 0, "blue" => 0},
|
|
||||||
&Map.merge(&1, &2, fn _colour, count1, count2 -> max(count1, count2) end)
|
|
||||||
)
|
|
||||||
|
|
||||||
{String.to_integer(id), rounds}
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_round(round) do
|
|
||||||
round
|
|
||||||
|> String.split([", ", " "])
|
|
||||||
|> Enum.chunk_every(2)
|
|
||||||
|> Map.new(fn [number, colour] -> {colour, String.to_integer(number)} end)
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{inspect(result)}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day2.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day2.run()
|
|
|
@ -1,90 +0,0 @@
|
||||||
#!/usr/bin/env elixir
|
|
||||||
defmodule Day3 do
|
|
||||||
def part1(_input) do
|
|
||||||
# for each number, check around its edges to see if there's a symbol
|
|
||||||
fn {{x, y}, number, length}, acc ->
|
|
||||||
top = for x <- (x - 1)..(x + length), do: {x, y - 1}
|
|
||||||
bottom = for x <- (x - 1)..(x + length), do: {x, y + 1}
|
|
||||||
box = [{x - 1, y}, {x + length, y}] ++ top ++ bottom
|
|
||||||
if part_number?(box), do: [number | acc], else: acc
|
|
||||||
end
|
|
||||||
|> :ets.foldl(_acc = [], :numbers)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def part_number?([]), do: false
|
|
||||||
|
|
||||||
def part_number?([coord | rest]) do
|
|
||||||
case :ets.lookup(:symbols, coord) do
|
|
||||||
[_symbol] -> true
|
|
||||||
[] -> part_number?(rest)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(_input) do
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
:ets.new(:numbers, [:named_table])
|
|
||||||
:ets.new(:symbols, [:named_table])
|
|
||||||
parse_schematic(input, _x = 0, _y = 0)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_schematic("", _x, _y), do: :ok
|
|
||||||
def parse_schematic("." <> rest, x, y), do: parse_schematic(rest, x + 1, y)
|
|
||||||
def parse_schematic("\n" <> rest, _x, y), do: parse_schematic(rest, 0, y + 1)
|
|
||||||
|
|
||||||
def parse_schematic(<<digit::utf8>> <> rest, x, y) when digit in ?0..?9 do
|
|
||||||
{number, length, rest} = take_digits(<<digit::utf8>> <> rest, "", 0)
|
|
||||||
:ets.insert(:numbers, {{x, y}, number, length})
|
|
||||||
parse_schematic(rest, x + length, y)
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_schematic(<<symbol::utf8>> <> rest, x, y) do
|
|
||||||
:ets.insert(:symbols, {{x, y}, <<symbol::utf8>>})
|
|
||||||
parse_schematic(rest, x + 1, y)
|
|
||||||
end
|
|
||||||
|
|
||||||
def take_digits(<<digit::utf8>> <> rest, digits, count) when digit in ?0..?9,
|
|
||||||
do: take_digits(rest, digits <> <<digit::utf8>>, count + 1)
|
|
||||||
|
|
||||||
def take_digits(rest, digits, count), do: {String.to_integer(digits), count, rest}
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{inspect(result)}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day3.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Day3.run()
|
|
|
@ -1,78 +0,0 @@
|
||||||
#!/usr/bin/env elixir
|
|
||||||
defmodule Day4 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(fn 0 -> 0; num_winners -> 2 ** (num_winners - 1) end)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(fn num_winners -> {_num_copies = 1, num_winners} end)
|
|
||||||
|> copy()
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def copy([]), do: []
|
|
||||||
|
|
||||||
def copy([{num_copies, num_winners} | rest]) do
|
|
||||||
{to_copy, left_alone} = Enum.split(rest, num_winners)
|
|
||||||
|
|
||||||
copied =
|
|
||||||
Enum.map(to_copy, fn {child_num_copies, num_winners} ->
|
|
||||||
{num_copies + child_num_copies, num_winners}
|
|
||||||
end)
|
|
||||||
|
|
||||||
[num_copies | copy(copied ++ left_alone)]
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.split("\n", trim: true)
|
|
||||||
|> Enum.map(fn line ->
|
|
||||||
[_card_id, winning, have] = String.split(line, [": ", " | "])
|
|
||||||
|
|
||||||
winning = winning |> String.split(" ", trim: true) |> MapSet.new()
|
|
||||||
have = have |> String.split(" ", trim: true) |> MapSet.new()
|
|
||||||
|
|
||||||
MapSet.intersection(winning, have) |> MapSet.size()
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{inspect(result)}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day4.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day4.run()
|
|
110
2023/day5.exs
110
2023/day5.exs
|
@ -1,110 +0,0 @@
|
||||||
#!/usr/bin/env elixir
|
|
||||||
defmodule Day5 do
|
|
||||||
def part1({seeds, maps}) do
|
|
||||||
seeds
|
|
||||||
|> Enum.map(fn seed -> Enum.reduce(maps, seed, &lookup/2) end)
|
|
||||||
|> Enum.min()
|
|
||||||
end
|
|
||||||
|
|
||||||
def lookup([], item), do: item
|
|
||||||
|
|
||||||
def lookup([{source, offset} | rest], item) do
|
|
||||||
if item in source do
|
|
||||||
item + offset
|
|
||||||
else
|
|
||||||
lookup(rest, item)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2({seeds, maps}) do
|
|
||||||
seed_ranges =
|
|
||||||
seeds
|
|
||||||
|> Enum.chunk_every(2)
|
|
||||||
|> Enum.map(fn [start, len] -> start..(start + len - 1) end)
|
|
||||||
|
|
||||||
maps
|
|
||||||
|> Enum.reduce(seed_ranges, &process_ranges/2)
|
|
||||||
|> Enum.map(& &1.first)
|
|
||||||
|> Enum.min()
|
|
||||||
end
|
|
||||||
|
|
||||||
def process_ranges(_map_ranges, []), do: []
|
|
||||||
|
|
||||||
def process_ranges(map_ranges, [item_range | rest]) do
|
|
||||||
overlap =
|
|
||||||
Enum.find(map_ranges, fn {source, _offset} -> not Range.disjoint?(source, item_range) end)
|
|
||||||
|
|
||||||
case overlap do
|
|
||||||
{source, offset} ->
|
|
||||||
to_shift = max(source.first, item_range.first)..min(source.last, item_range.last)
|
|
||||||
|
|
||||||
remainders =
|
|
||||||
[
|
|
||||||
if(item_range.first < source.first, do: item_range.first..(source.first - 1)),
|
|
||||||
if(item_range.last > source.last, do: (source.last + 1)..item_range.last)
|
|
||||||
]
|
|
||||||
|> Enum.reject(&is_nil/1)
|
|
||||||
|
|
||||||
[Range.shift(to_shift, offset) | process_ranges(map_ranges, remainders ++ rest)]
|
|
||||||
|
|
||||||
nil ->
|
|
||||||
[item_range | process_ranges(map_ranges, rest)]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
["seeds: " <> seeds | maps] = String.split(input, "\n\n")
|
|
||||||
seeds = seeds |> String.split() |> Enum.map(&String.to_integer/1)
|
|
||||||
|
|
||||||
maps =
|
|
||||||
Enum.map(maps, fn line ->
|
|
||||||
[_header, _map | items] = String.split(line)
|
|
||||||
|
|
||||||
items
|
|
||||||
|> Enum.map(&String.to_integer/1)
|
|
||||||
|> Enum.chunk_every(3)
|
|
||||||
|> Enum.map(fn [dest, source, offset] ->
|
|
||||||
{source..(source + offset), dest - source}
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|
|
||||||
{seeds, maps}
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{inspect(result, charlists: :as_lists)}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day5.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day5.run()
|
|
|
@ -1,69 +0,0 @@
|
||||||
#!/usr/bin/env elixir
|
|
||||||
defmodule Day6 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.map(fn line -> Enum.map(line, &String.to_integer/1) end)
|
|
||||||
|> Enum.zip()
|
|
||||||
|> Enum.map(fn {race, record} -> count_winning(race, record) end)
|
|
||||||
|> Enum.product()
|
|
||||||
end
|
|
||||||
|
|
||||||
def count_winning(race, record) do
|
|
||||||
scores = for n <- 1..div(race, 2), do: n * (race - n)
|
|
||||||
[_middle | rev_no_middle] = reversed = Enum.reverse(scores)
|
|
||||||
scores = scores ++ if rem(race, 2) == 1, do: reversed, else: rev_no_middle
|
|
||||||
Enum.count(scores, fn score -> score > record end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(input) do
|
|
||||||
[race, record] = Enum.map(input, fn line -> line |> Enum.join() |> String.to_integer() end)
|
|
||||||
count_winning(race, record)
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.split("\n", trim: true)
|
|
||||||
|> Enum.map(fn line ->
|
|
||||||
line
|
|
||||||
|> String.split()
|
|
||||||
|> Enum.drop(1)
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{inspect(result)}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day6.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Day6.run()
|
|
123
2023/day7.exs
123
2023/day7.exs
|
@ -1,123 +0,0 @@
|
||||||
#!/usr/bin/env elixir
|
|
||||||
defmodule Day7 do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
|> Enum.sort_by(&detect_type/1, &rank/2)
|
|
||||||
|> Enum.with_index()
|
|
||||||
|> Enum.map(fn {{_hand, bid}, rank} -> bid * (rank + 1) end)
|
|
||||||
|> Enum.sum()
|
|
||||||
end
|
|
||||||
|
|
||||||
def detect_type({hand, _bid}) do
|
|
||||||
frequencies =
|
|
||||||
hand
|
|
||||||
|> Enum.frequencies()
|
|
||||||
|> Enum.group_by(fn {_card, freq} -> freq end)
|
|
||||||
|> Map.new(fn {freq, cards} -> {freq, length(cards)} end)
|
|
||||||
|
|
||||||
type =
|
|
||||||
cond do
|
|
||||||
frequencies[5] -> :five_kind
|
|
||||||
frequencies[4] -> :four_kind
|
|
||||||
frequencies[3] && frequencies[2] -> :full_house
|
|
||||||
frequencies[3] -> :three_kind
|
|
||||||
frequencies[2] == 2 -> :two_pair
|
|
||||||
frequencies[2] -> :one_pair
|
|
||||||
true -> :high_card
|
|
||||||
end
|
|
||||||
|
|
||||||
{type, hand}
|
|
||||||
end
|
|
||||||
|
|
||||||
def rank({same_type, hand1}, {same_type, hand2}), do: tiebreak(hand1, hand2)
|
|
||||||
def rank({:high_card, _}, _), do: true
|
|
||||||
def rank({:one_pair, _}, {:high_card, _}), do: false
|
|
||||||
def rank({:one_pair, _}, _), do: true
|
|
||||||
def rank({:two_pair, _}, {b, _}) when b in [:high_card, :one_pair], do: false
|
|
||||||
def rank({:two_pair, _}, _), do: true
|
|
||||||
def rank({:three_kind, _}, {b, _}) when b in [:high_card, :one_pair, :two_pair], do: false
|
|
||||||
def rank({:three_kind, _}, _), do: true
|
|
||||||
def rank({:full_house, _}, {b, _}) when b in [:five_kind, :four_kind], do: true
|
|
||||||
def rank({:full_house, _}, _), do: false
|
|
||||||
def rank({:four_kind, _}, {:five_kind, _}), do: true
|
|
||||||
def rank({:four_kind, _}, _), do: false
|
|
||||||
def rank({:five_kind, _}, _), do: false
|
|
||||||
|
|
||||||
def tiebreak([first | resta], [first | restb]), do: tiebreak(resta, restb)
|
|
||||||
def tiebreak(["1" | _], _), do: true
|
|
||||||
def tiebreak(["2" | _], ["1" | _]), do: false
|
|
||||||
def tiebreak(["2" | _], _), do: true
|
|
||||||
def tiebreak(["3" | _], [b | _]) when b in ["1", "2"], do: false
|
|
||||||
def tiebreak(["3" | _], _), do: true
|
|
||||||
def tiebreak(["4" | _], [b | _]) when b in ["1", "2", "3"], do: false
|
|
||||||
def tiebreak(["4" | _], _), do: true
|
|
||||||
def tiebreak(["5" | _], [b | _]) when b in ["1", "2", "3", "4"], do: false
|
|
||||||
def tiebreak(["5" | _], _), do: true
|
|
||||||
def tiebreak(["6" | _], [b | _]) when b in ["1", "2", "3", "4", "5"], do: false
|
|
||||||
def tiebreak(["6" | _], _), do: true
|
|
||||||
def tiebreak(["7" | _], [b | _]) when b in ["1", "2", "3", "4", "5", "6"], do: false
|
|
||||||
def tiebreak(["7" | _], _), do: true
|
|
||||||
def tiebreak(["8" | _], [b | _]) when b in ["A", "K", "Q", "J", "T", "9"], do: true
|
|
||||||
def tiebreak(["8" | _], _), do: false
|
|
||||||
def tiebreak(["9" | _], [b | _]) when b in ["A", "K", "Q", "J", "T"], do: true
|
|
||||||
def tiebreak(["9" | _], _), do: false
|
|
||||||
def tiebreak(["T" | _], [b | _]) when b in ["A", "K", "Q", "J"], do: true
|
|
||||||
def tiebreak(["T" | _], _), do: false
|
|
||||||
def tiebreak(["J" | _], [b | _]) when b in ["A", "K", "Q"], do: true
|
|
||||||
def tiebreak(["J" | _], _), do: false
|
|
||||||
def tiebreak(["Q" | _], [b | _]) when b in ["A", "K"], do: true
|
|
||||||
def tiebreak(["Q" | _], _), do: false
|
|
||||||
def tiebreak(["K" | _], ["A" | _]), do: true
|
|
||||||
def tiebreak(["K" | _], _), do: false
|
|
||||||
def tiebreak(["A" | _], _), do: false
|
|
||||||
|
|
||||||
def part2(_input) do
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
|> String.split("\n", trim: true)
|
|
||||||
|> Enum.map(fn line ->
|
|
||||||
[hands, bid] = String.split(line)
|
|
||||||
{String.graphemes(hands), String.to_integer(bid)}
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{inspect(result)}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir day7.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Day7.run()
|
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
My (attempted) solutions to [Advent of Code](https://adventofcode.com/).
|
My (attempted) solutions to [Advent of Code](https://adventofcode.com/).
|
||||||
|
|
||||||
- [2022: Elixir](https://git.adamu.jp/adam/AdventOfCode/src/branch/main/2022)
|
- [2021: Zig](https://github.com/adamu/AdventOfCode/tree/main/2021)
|
||||||
- [2020: Elixir](https://git.adamu.jp/adam/AdventOfCode/src/branch/main/2020)
|
- [2020: Elixir](https://github.com/adamu/AdventOfCode/tree/main/2020)
|
||||||
- [2018: Elixir](https://git.adamu.jp/adam/AdventOfCode/src/branch/main/2018)
|
- [2018: Elixir](https://github.com/adamu/AdventOfCode/tree/main/2018)
|
||||||
|
|
51
dayN.exs
51
dayN.exs
|
@ -1,51 +0,0 @@
|
||||||
#!/usr/bin/env elixir
|
|
||||||
defmodule DayREPLACE_ME do
|
|
||||||
def part1(input) do
|
|
||||||
input
|
|
||||||
end
|
|
||||||
|
|
||||||
def part2(_input) do
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
def input do
|
|
||||||
with [input_filename] <- System.argv(),
|
|
||||||
{:ok, input} <- File.read(input_filename) do
|
|
||||||
input
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#######################
|
|
||||||
# HERE BE BOILERPLATE #
|
|
||||||
#######################
|
|
||||||
|
|
||||||
def run do
|
|
||||||
case input() do
|
|
||||||
:error -> print_usage()
|
|
||||||
input -> run_parts_with_timer(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_parts_with_timer(input) do
|
|
||||||
run_with_timer(1, fn -> part1(input) end)
|
|
||||||
run_with_timer(2, fn -> part2(input) end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp run_with_timer(part, fun) do
|
|
||||||
{time, result} = :timer.tc(fun)
|
|
||||||
IO.puts("Part #{part} (completed in #{format_time(time)}):\n")
|
|
||||||
IO.puts("#{inspect result}\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_time(μsec) when μsec < 1_000, do: "#{μsec}μs"
|
|
||||||
defp format_time(μsec) when μsec < 1_000_000, do: "#{μsec / 1000}ms"
|
|
||||||
defp format_time(μsec), do: "#{μsec / 1_000_000}s"
|
|
||||||
|
|
||||||
defp print_usage do
|
|
||||||
IO.puts("Usage: elixir dayREPLACE_ME.exs input_filename")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# DayREPLACE_ME.run()
|
|
25
template/README
Normal file
25
template/README
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
Day REPLACE_ME Notes
|
||||||
|
|
||||||
|
+--------+
|
||||||
|
| Part 1 |
|
||||||
|
+--------+
|
||||||
|
|
||||||
|
$ elixir dayREPLACE_MEpart1.exs
|
||||||
|
|
||||||
|
Thoughts:
|
||||||
|
|
||||||
|
|
||||||
|
+--------+
|
||||||
|
| Part 2 |
|
||||||
|
+--------+
|
||||||
|
|
||||||
|
$ elixir dayREPLACE_MEpart2.exs
|
||||||
|
|
||||||
|
Thoughts:
|
||||||
|
|
||||||
|
|
||||||
|
+------------------+
|
||||||
|
| Overall Thoughts |
|
||||||
|
+------------------+
|
||||||
|
|
||||||
|
|
9
template/dayNpart1.exs
Normal file
9
template/dayNpart1.exs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule DayREPLACE_MEPart1 do
|
||||||
|
def run do
|
||||||
|
File.read!("input")
|
||||||
|
|> String.split("\n", trim: true)
|
||||||
|
|> IO.inspect()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
DayREPLACE_MEPart1.run()
|
Loading…
Reference in a new issue