chore: use advent_of_code_utils
This commit is contained in:
parent
4e35ec07be
commit
dd9ded8d4e
23 changed files with 76 additions and 589 deletions
2
2021/.gitignore
vendored
2
2021/.gitignore
vendored
|
@ -28,4 +28,4 @@ advent_of_code-*.tar
|
||||||
|
|
||||||
.envrc
|
.envrc
|
||||||
|
|
||||||
priv/inputs/
|
input/
|
||||||
|
|
1
2021/.iex.exs
Normal file
1
2021/.iex.exs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import AOC.IEx
|
|
@ -19,26 +19,7 @@
|
||||||
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
|
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
|
||||||
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
|
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Mix Tasks
|
|
||||||
|
|
||||||
- `advent_of_code.gen.solution`
|
|
||||||
- `advent_of_code.solve`
|
|
||||||
- `advent_of_code.fetch_input`
|
|
||||||
|
|
||||||
Run `mix help <task>` for details.
|
|
||||||
|
|
||||||
## [`AdventOfCode.PuzzleSolver`](./lib/advent_of_code/puzzle_solver.ex)
|
|
||||||
|
|
||||||
A behaviour for a solution to a puzzle. Must define a `solve[2] callback.
|
|
||||||
|
|
||||||
## [`AdventOfCode.PuzzleCase`](./test/support/puzzle_case.ex)
|
|
||||||
|
|
||||||
Case template defining an `assert_solution[2] helper.
|
|
||||||
|
|
||||||
<!-- links -->
|
<!-- links -->
|
||||||
|
|
||||||
[1]: ./lib/advent_of_code/day01.ex
|
[1]: ./lib/2021/1.ex
|
||||||
|
[2]: ./lib/2021/2.ex
|
||||||
[2]: ./lib/advent_of_code/day02.ex
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
config :advent_of_code,
|
config :advent_of_code_utils,
|
||||||
event: "2021"
|
session: System.get_env("ADVENT_OF_CODE_SESSION"),
|
||||||
|
auto_compile?: true
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
import Config
|
|
||||||
|
|
||||||
config :advent_of_code, session: System.get_env("ADVENT_OF_CODE_SESSION")
|
|
35
2021/lib/2021/1.ex
Normal file
35
2021/lib/2021/1.ex
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import AOC
|
||||||
|
|
||||||
|
aoc 2021, 1 do
|
||||||
|
def input_list,
|
||||||
|
do:
|
||||||
|
input_stream()
|
||||||
|
|> Stream.map(&String.to_integer/1)
|
||||||
|
|> Enum.to_list()
|
||||||
|
|
||||||
|
def p1 do
|
||||||
|
input_list = input_list()
|
||||||
|
input_list_ = Enum.drop(input_list, 1)
|
||||||
|
|
||||||
|
Enum.zip_reduce(
|
||||||
|
input_list,
|
||||||
|
input_list_,
|
||||||
|
0,
|
||||||
|
&if(&1 < &2, do: &3 + 1, else: &3)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def p2 do
|
||||||
|
input_list()
|
||||||
|
|> window_increases()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp window_increases(list, acc \\ 0)
|
||||||
|
|
||||||
|
defp window_increases([a, b, c, d | _] = list, acc) do
|
||||||
|
acc = if a + b + c < b + c + d, do: acc + 1, else: acc
|
||||||
|
window_increases(Enum.drop(list, 1), acc)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp window_increases(_, acc), do: acc
|
||||||
|
end
|
32
2021/lib/2021/2.ex
Normal file
32
2021/lib/2021/2.ex
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import AOC
|
||||||
|
|
||||||
|
aoc 2021, 2 do
|
||||||
|
def input_stream(),
|
||||||
|
do:
|
||||||
|
super()
|
||||||
|
|> Stream.map(fn
|
||||||
|
"forward " <> v -> {:forward, String.to_integer(v)}
|
||||||
|
"down " <> v -> {:down, String.to_integer(v)}
|
||||||
|
"up " <> v -> {:up, String.to_integer(v)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
def p1 do
|
||||||
|
input_stream()
|
||||||
|
|> Enum.reduce({0, 0}, fn
|
||||||
|
{:forward, v}, {h, d} -> {h + v, d}
|
||||||
|
{:down, v}, {h, d} -> {h, d + v}
|
||||||
|
{:up, v}, {h, d} -> {h, d - v}
|
||||||
|
end)
|
||||||
|
|> then(fn {h, d} -> h * d end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def p2 do
|
||||||
|
input_stream()
|
||||||
|
|> Enum.reduce({0, 0, 0}, fn
|
||||||
|
{:forward, v}, {h, d, a} -> {h + v, d + a * v, a}
|
||||||
|
{:down, v}, {h, d, a} -> {h, d, a + v}
|
||||||
|
{:up, v}, {h, d, a} -> {h, d, a - v}
|
||||||
|
end)
|
||||||
|
|> then(fn {h, d, _v} -> h * d end)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,23 +0,0 @@
|
||||||
defmodule AdventOfCode do
|
|
||||||
@moduledoc """
|
|
||||||
Solutions to the 2021 Advent of Code puzzles
|
|
||||||
"""
|
|
||||||
|
|
||||||
def solver(selector) do
|
|
||||||
[day, part] = String.split(selector, ".")
|
|
||||||
part_module(day, part)
|
|
||||||
end
|
|
||||||
|
|
||||||
def part_module(day, part) do
|
|
||||||
Module.concat(day_module(day), Macro.camelize("Part#{part_number(part)}"))
|
|
||||||
end
|
|
||||||
|
|
||||||
def day_module(day) do
|
|
||||||
Module.concat(AdventOfCode, Macro.camelize("Day#{day_number(day)}"))
|
|
||||||
end
|
|
||||||
|
|
||||||
def day_input_path(day), do: "priv/inputs/#{day_number(day)}.input"
|
|
||||||
|
|
||||||
defp day_number(day), do: String.pad_leading(day, 2, "0")
|
|
||||||
defp part_number(part), do: String.trim_leading(part, "0")
|
|
||||||
end
|
|
|
@ -1,61 +0,0 @@
|
||||||
defmodule AdventOfCode.Day01 do
|
|
||||||
@moduledoc """
|
|
||||||
Day 1
|
|
||||||
"""
|
|
||||||
|
|
||||||
def parse_input_stream(input_stream) do
|
|
||||||
input_stream
|
|
||||||
|> Stream.map(&String.trim/1)
|
|
||||||
|> Stream.map(&String.to_integer/1)
|
|
||||||
|> Enum.to_list()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defmodule AdventOfCode.Day01.Part1 do
|
|
||||||
@moduledoc """
|
|
||||||
Day 1, Part 1
|
|
||||||
"""
|
|
||||||
|
|
||||||
alias AdventOfCode.PuzzleSolver
|
|
||||||
use PuzzleSolver
|
|
||||||
|
|
||||||
import AdventOfCode.Day01, warn: false
|
|
||||||
|
|
||||||
@impl PuzzleSolver
|
|
||||||
def solve(input_stream) do
|
|
||||||
depths = parse_input_stream(input_stream)
|
|
||||||
|
|
||||||
Enum.zip_reduce(
|
|
||||||
depths,
|
|
||||||
Enum.drop(depths, 1),
|
|
||||||
0,
|
|
||||||
&if(&1 < &2, do: &3 + 1, else: &3)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defmodule AdventOfCode.Day01.Part2 do
|
|
||||||
@moduledoc """
|
|
||||||
Day 1, Part 2
|
|
||||||
"""
|
|
||||||
|
|
||||||
alias AdventOfCode.PuzzleSolver
|
|
||||||
use PuzzleSolver
|
|
||||||
|
|
||||||
import AdventOfCode.Day01, warn: false
|
|
||||||
|
|
||||||
@impl PuzzleSolver
|
|
||||||
def solve(input_stream) do
|
|
||||||
parse_input_stream(input_stream)
|
|
||||||
|> window_increases()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp window_increases(list, acc \\ 0)
|
|
||||||
|
|
||||||
defp window_increases([a, b, c, d | _] = list, acc) do
|
|
||||||
acc = if a + b + c < b + c + d, do: acc + 1, else: acc
|
|
||||||
window_increases(Enum.drop(list, 1), acc)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp window_increases(_, acc), do: acc
|
|
||||||
end
|
|
|
@ -1,62 +0,0 @@
|
||||||
defmodule AdventOfCode.Day02 do
|
|
||||||
@moduledoc """
|
|
||||||
Day 2
|
|
||||||
"""
|
|
||||||
|
|
||||||
def parse_action("forward " <> v), do: {:forward, String.to_integer(v)}
|
|
||||||
def parse_action("down " <> v), do: {:down, String.to_integer(v)}
|
|
||||||
def parse_action("up " <> v), do: {:up, String.to_integer(v)}
|
|
||||||
end
|
|
||||||
|
|
||||||
defmodule AdventOfCode.Day02.Part1 do
|
|
||||||
@moduledoc """
|
|
||||||
Day 2, Part 1
|
|
||||||
"""
|
|
||||||
|
|
||||||
alias AdventOfCode.PuzzleSolver
|
|
||||||
use PuzzleSolver
|
|
||||||
|
|
||||||
import AdventOfCode.Day02, warn: false
|
|
||||||
|
|
||||||
@impl PuzzleSolver
|
|
||||||
def solve(input_stream) do
|
|
||||||
input_stream
|
|
||||||
|> Stream.map(&String.trim/1)
|
|
||||||
|> Stream.map(&parse_action/1)
|
|
||||||
|> Enum.reduce({0, 0}, &apply_action/2)
|
|
||||||
|> product()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp apply_action({:forward, v}, {horiz, depth}), do: {horiz + v, depth}
|
|
||||||
defp apply_action({:down, v}, {horiz, depth}), do: {horiz, depth + v}
|
|
||||||
defp apply_action({:up, v}, {horiz, depth}), do: {horiz, depth - v}
|
|
||||||
|
|
||||||
defp product({x, y}), do: x * y
|
|
||||||
end
|
|
||||||
|
|
||||||
defmodule AdventOfCode.Day02.Part2 do
|
|
||||||
@moduledoc """
|
|
||||||
Day 2, Part 2
|
|
||||||
"""
|
|
||||||
|
|
||||||
alias AdventOfCode.PuzzleSolver
|
|
||||||
use PuzzleSolver
|
|
||||||
|
|
||||||
import AdventOfCode.Day02, warn: false
|
|
||||||
|
|
||||||
@impl PuzzleSolver
|
|
||||||
def solve(input_stream) do
|
|
||||||
input_stream
|
|
||||||
|> Stream.map(&String.trim/1)
|
|
||||||
|> Stream.map(&parse_action/1)
|
|
||||||
|> Enum.reduce({0, 0, 0}, &apply_action/2)
|
|
||||||
|> product()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp apply_action({:forward, v}, {horiz, depth, aim}), do: {horiz + v, depth + aim * v, aim}
|
|
||||||
defp apply_action({:down, v}, {horiz, depth, aim}), do: {horiz, depth, aim + v}
|
|
||||||
defp apply_action({:up, v}, {horiz, depth, aim}), do: {horiz, depth, aim - v}
|
|
||||||
|
|
||||||
defp product({x, y, _z}), do: x * y
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
defmodule AdventOfCode.PuzzleSolver do
|
|
||||||
@moduledoc """
|
|
||||||
Behaviour for a puzzle solution.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Given the input as a stream, return the solution as a string
|
|
||||||
"""
|
|
||||||
@callback solve(Enumerable.t()) :: any()
|
|
||||||
|
|
||||||
def solve(mod, stream), do: apply(mod, :solve, [stream])
|
|
||||||
|
|
||||||
defmacro __using__(_) do
|
|
||||||
quote do
|
|
||||||
@behaviour AdventOfCode.PuzzleSolver
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,9 +0,0 @@
|
||||||
defmodule Mix.AdventOfCode do
|
|
||||||
@moduledoc """
|
|
||||||
Helpers for `AdventOfCode` mix tasks.
|
|
||||||
"""
|
|
||||||
|
|
||||||
defdelegate day_module(day), to: AdventOfCode
|
|
||||||
defdelegate part_module(day, part), to: AdventOfCode
|
|
||||||
defdelegate day_input_path(day), to: AdventOfCode
|
|
||||||
end
|
|
|
@ -1,63 +0,0 @@
|
||||||
defmodule Mix.Tasks.AdventOfCode.FetchInput do
|
|
||||||
use Mix.Task
|
|
||||||
|
|
||||||
import Mix.Generator
|
|
||||||
import Mix.AdventOfCode
|
|
||||||
|
|
||||||
@shortdoc "Fetches the inputs for day and saves them in `priv/inputs`"
|
|
||||||
|
|
||||||
@moduledoc """
|
|
||||||
#{@shortdoc}.
|
|
||||||
|
|
||||||
Expects `:event` and `:session` keys to be configured for the `:advent_of_code` application.
|
|
||||||
These values can be overridden.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
# Fetch day 1's inputs
|
|
||||||
$ mix advent_of_code.fetch_input 1
|
|
||||||
|
|
||||||
# Fetch Day 1 of 2020
|
|
||||||
$ mix advent_of_code.fetch_input --event 2020 1
|
|
||||||
|
|
||||||
# Pass your session cookie value
|
|
||||||
$ mix advent_of_code.fetch_input 13 --session foobar
|
|
||||||
"""
|
|
||||||
|
|
||||||
@finch Mix.AdventOfCode.FetchInput.Finch
|
|
||||||
|
|
||||||
@switches [event: :string, session: :string]
|
|
||||||
|
|
||||||
@impl Mix.Task
|
|
||||||
def run(args) do
|
|
||||||
Mix.Task.run("app.config")
|
|
||||||
:ok = Application.ensure_started(:telemetry)
|
|
||||||
{:ok, _pid} = Finch.start_link(name: @finch)
|
|
||||||
|
|
||||||
default_event = Application.get_env(:advent_of_code, :event)
|
|
||||||
default_session = Application.get_env(:advent_of_code, :session)
|
|
||||||
|
|
||||||
case OptionParser.parse!(args, strict: @switches) do
|
|
||||||
{opts, [day]} ->
|
|
||||||
input_file_path = day_input_path(day)
|
|
||||||
|
|
||||||
if overwrite?(input_file_path) do
|
|
||||||
event = Keyword.get(opts, :event, default_event)
|
|
||||||
session = Keyword.get(opts, :session, default_session)
|
|
||||||
|
|
||||||
{:ok, %{body: data}} =
|
|
||||||
Finch.build(
|
|
||||||
:get,
|
|
||||||
"https://adventofcode.com/#{event}/day/#{day}/input",
|
|
||||||
[{"Cookie", "session=#{session}"}]
|
|
||||||
)
|
|
||||||
|> Finch.request(@finch)
|
|
||||||
|
|
||||||
create_file(input_file_path, data, force: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Mix.raise("Unexpected arguments.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,119 +0,0 @@
|
||||||
defmodule Mix.Tasks.AdventOfCode.Gen.Solution do
|
|
||||||
use Mix.Task
|
|
||||||
import Mix.Generator
|
|
||||||
import Mix.AdventOfCode
|
|
||||||
|
|
||||||
@shortdoc "Generate a new solution module"
|
|
||||||
|
|
||||||
@moduledoc """
|
|
||||||
#{@shortdoc}.
|
|
||||||
|
|
||||||
Includes new solution module, test, and empty problem input.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
# Generate solution modules, tests, and empty input for Day 2
|
|
||||||
$ mix advent_of_code.gen.solution 2
|
|
||||||
"""
|
|
||||||
|
|
||||||
@switches []
|
|
||||||
|
|
||||||
@impl Mix.Task
|
|
||||||
def run(args) do
|
|
||||||
case OptionParser.parse!(args, switches: @switches) do
|
|
||||||
{_, [day]} ->
|
|
||||||
day_module = day_module(day)
|
|
||||||
day_contents = day_template(mod: day_module, day: day) |> Code.format_string!()
|
|
||||||
day_path = Path.join("lib", Macro.underscore(day_module))
|
|
||||||
day_tests_path = Path.join("test", Macro.underscore(day_module))
|
|
||||||
day_file = "#{day_path}.ex"
|
|
||||||
|
|
||||||
create_file(day_file, day_contents)
|
|
||||||
|
|
||||||
create_directory(day_tests_path)
|
|
||||||
|
|
||||||
for part <- 1..2 do
|
|
||||||
part = to_string(part)
|
|
||||||
part_module = part_module(day, part)
|
|
||||||
|
|
||||||
part_test_module = Module.concat(day_module, Macro.camelize("Part#{part}Test"))
|
|
||||||
|
|
||||||
part_test_contents =
|
|
||||||
part_test_template(mod: part_module, test_mod: part_test_module)
|
|
||||||
|> Code.format_string!()
|
|
||||||
|
|
||||||
part_test_file =
|
|
||||||
Path.join(day_tests_path, "#{Macro.underscore("part_#{part}_test")}.exs")
|
|
||||||
|
|
||||||
create_file(part_test_file, part_test_contents)
|
|
||||||
end
|
|
||||||
|
|
||||||
day_regex = ~r/\W(#{day})\W/
|
|
||||||
readme_file = "README.md"
|
|
||||||
readme_contents = File.read!("README.md")
|
|
||||||
|
|
||||||
readme_contents =
|
|
||||||
"#{String.replace(readme_contents, day_regex, "[\\1]")}\n[#{day}]: ./#{day_file}\n"
|
|
||||||
|
|
||||||
File.write!(readme_file, readme_contents)
|
|
||||||
Mix.shell().info([:green, "* updating ", :reset, "README.md"])
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Mix.raise("Unknown arguments.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
embed_template(:day, ~S[
|
|
||||||
defmodule <%= inspect(@mod) %> do
|
|
||||||
@moduledoc """
|
|
||||||
Day <%= @day %>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
defmodule <%= inspect(@mod) %>.Part1 do
|
|
||||||
@moduledoc """
|
|
||||||
Day <%= @day %>, Part 1
|
|
||||||
"""
|
|
||||||
|
|
||||||
alias AdventOfCode.PuzzleSolver
|
|
||||||
use PuzzleSolver
|
|
||||||
|
|
||||||
import <%= inspect(@mod) %>, warn: false
|
|
||||||
|
|
||||||
@impl PuzzleSolver
|
|
||||||
def solve(_input_stream) do
|
|
||||||
:ok |> to_string()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defmodule <%= inspect(@mod) %>.Part2 do
|
|
||||||
@moduledoc """
|
|
||||||
Day <%= @day %>, Part 2
|
|
||||||
"""
|
|
||||||
|
|
||||||
alias AdventOfCode.PuzzleSolver
|
|
||||||
use PuzzleSolver
|
|
||||||
|
|
||||||
import <%= inspect(@mod) %>, warn: false
|
|
||||||
|
|
||||||
@impl PuzzleSolver
|
|
||||||
def solve(_input_stream) do
|
|
||||||
:ok |> to_string()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
])
|
|
||||||
|
|
||||||
embed_template(:part_test, ~S[
|
|
||||||
defmodule <%= inspect(@test_mod) %> do
|
|
||||||
use AdventOfCode.PuzzleCase, module: <%= inspect(@mod) %>
|
|
||||||
|
|
||||||
test "returns :ok" do
|
|
||||||
input = ~S"""
|
|
||||||
input
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert_solution input, "ok"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
])
|
|
||||||
end
|
|
|
@ -1,91 +0,0 @@
|
||||||
defmodule Mix.Tasks.AdventOfCode.Solve do
|
|
||||||
use Mix.Task
|
|
||||||
import Mix.AdventOfCode
|
|
||||||
|
|
||||||
@shortdoc "Runs solution code with problem input"
|
|
||||||
|
|
||||||
@moduledoc """
|
|
||||||
#{@shortdoc}.
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
`--input`
|
|
||||||
- name of a `.input` file in `priv/inputs/` without the `.input` extension or `-` to read from stdin
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
# Run Day 2, Part 1 solution program against the `2.1.input` file
|
|
||||||
$ mix advent_of_code.solve 2.1
|
|
||||||
|
|
||||||
# Run Day 1, Part 2 with a special input
|
|
||||||
$ mix advent_of_code.solve 1.2 --input day-1-part-2-small # priv/inputs/day-1-part-2-small.input
|
|
||||||
|
|
||||||
# Run Day 17, Part 1 with input from stdin
|
|
||||||
# $ mix advent_of_code.solve 17.1 --input -
|
|
||||||
"""
|
|
||||||
|
|
||||||
@switches [input: :string]
|
|
||||||
|
|
||||||
@impl Mix.Task
|
|
||||||
def run(args) do
|
|
||||||
case OptionParser.parse!(args, strict: @switches) do
|
|
||||||
{opts, [selector]} ->
|
|
||||||
case stream_for_input(selector, opts) do
|
|
||||||
{:indeterminate, stream} ->
|
|
||||||
ProgressBar.render_spinner(
|
|
||||||
[text: "Processing indeterminate data...", done: "Done."],
|
|
||||||
fn -> solve(selector, stream) end
|
|
||||||
)
|
|
||||||
|> IO.puts()
|
|
||||||
|
|
||||||
{size, stream} ->
|
|
||||||
stream =
|
|
||||||
stream
|
|
||||||
|> Stream.with_index(1)
|
|
||||||
|> Stream.each(fn {_, i} -> ProgressBar.render(i, size) end)
|
|
||||||
|> Stream.map(fn {l, _} -> l end)
|
|
||||||
|
|
||||||
solve(selector, stream)
|
|
||||||
|> IO.puts()
|
|
||||||
end
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp solve(selector, stream) do
|
|
||||||
selector
|
|
||||||
|> AdventOfCode.solver()
|
|
||||||
|> AdventOfCode.PuzzleSolver.solve(stream)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp stream_for_input(selector, opts) do
|
|
||||||
case Keyword.fetch(opts, :input) do
|
|
||||||
{:ok, "-"} ->
|
|
||||||
{:indeterminate, IO.stream()}
|
|
||||||
|
|
||||||
{:ok, name} ->
|
|
||||||
Path.join("priv/inputs", "#{name}.input")
|
|
||||||
|> get_size_and_stream()
|
|
||||||
|
|
||||||
:error ->
|
|
||||||
[day | _] = String.split(selector, ".")
|
|
||||||
|
|
||||||
file = day_input_path(day)
|
|
||||||
|
|
||||||
unless File.exists?(file) or
|
|
||||||
not Mix.shell().yes?("Input missing. Fetch input for day #{day}?") do
|
|
||||||
Mix.Task.run("advent_of_code.fetch_input", [day])
|
|
||||||
end
|
|
||||||
|
|
||||||
day_input_path(day)
|
|
||||||
|> get_size_and_stream()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_size_and_stream(file) do
|
|
||||||
stream = File.stream!(file)
|
|
||||||
{Enum.count(stream), stream}
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -25,9 +25,7 @@ defmodule AdventOfCode.MixProject do
|
||||||
# Run "mix help deps" to learn about dependencies.
|
# Run "mix help deps" to learn about dependencies.
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:finch, "~> 0.9.0"},
|
{:advent_of_code_utils, "~> 1.0"}
|
||||||
{:mix_test_interactive, "~> 1.0", only: :dev, runtime: false},
|
|
||||||
{:progress_bar, "~> 2.0"}
|
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
%{
|
%{
|
||||||
|
"advent_of_code_utils": {:hex, :advent_of_code_utils, "1.0.0", "e9ce8be22988e095991168e9d2dde802afc213aaae64491e9fde84dd877db41c", [:mix], [], "hexpm", "1fa5217ccb95a38ce2312dd7ea6057b0d0002a3eba7490feab704df4b8a94038"},
|
||||||
"castore": {:hex, :castore, "0.1.13", "ccf3ab251ffaebc4319f41d788ce59a6ab3f42b6c27e598ad838ffecee0b04f9", [:mix], [], "hexpm", "a14a7eecfec7e20385493dbb92b0d12c5d77ecfd6307de10102d58c94e8c49c0"},
|
"castore": {:hex, :castore, "0.1.13", "ccf3ab251ffaebc4319f41d788ce59a6ab3f42b6c27e598ad838ffecee0b04f9", [:mix], [], "hexpm", "a14a7eecfec7e20385493dbb92b0d12c5d77ecfd6307de10102d58c94e8c49c0"},
|
||||||
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
||||||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
defmodule AdventOfCode.Day01.Part1Test do
|
|
||||||
use AdventOfCode.PuzzleCase, module: AdventOfCode.Day01.Part1
|
|
||||||
|
|
||||||
test "solves for a small input" do
|
|
||||||
input = ~S"""
|
|
||||||
199
|
|
||||||
200
|
|
||||||
208
|
|
||||||
210
|
|
||||||
200
|
|
||||||
207
|
|
||||||
240
|
|
||||||
269
|
|
||||||
260
|
|
||||||
263
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert_solution(input, 7)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,20 +0,0 @@
|
||||||
defmodule AdventOfCode.Day01.Part2Test do
|
|
||||||
use AdventOfCode.PuzzleCase, module: AdventOfCode.Day01.Part2
|
|
||||||
|
|
||||||
test "solves for a small input" do
|
|
||||||
input = ~S"""
|
|
||||||
199
|
|
||||||
200
|
|
||||||
208
|
|
||||||
210
|
|
||||||
200
|
|
||||||
207
|
|
||||||
240
|
|
||||||
269
|
|
||||||
260
|
|
||||||
263
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert_solution(input, 5)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,16 +0,0 @@
|
||||||
defmodule AdventOfCode.Day02.Part1Test do
|
|
||||||
use AdventOfCode.PuzzleCase, module: AdventOfCode.Day02.Part1
|
|
||||||
|
|
||||||
test "returns the product of horizontal position and depth" do
|
|
||||||
input = ~S"""
|
|
||||||
forward 5
|
|
||||||
down 5
|
|
||||||
forward 8
|
|
||||||
up 3
|
|
||||||
down 8
|
|
||||||
forward 2
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert_solution(input, 150)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,16 +0,0 @@
|
||||||
defmodule AdventOfCode.Day02.Part2Test do
|
|
||||||
use AdventOfCode.PuzzleCase, module: AdventOfCode.Day02.Part2
|
|
||||||
|
|
||||||
test "returns the product of horizontal position and depth" do
|
|
||||||
input = ~S"""
|
|
||||||
forward 5
|
|
||||||
down 5
|
|
||||||
forward 8
|
|
||||||
up 3
|
|
||||||
down 8
|
|
||||||
forward 2
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert_solution(input, 900)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,21 +0,0 @@
|
||||||
defmodule AdventOfCodeTest do
|
|
||||||
use ExUnit.Case
|
|
||||||
|
|
||||||
import AdventOfCode
|
|
||||||
|
|
||||||
describe "solver/1" do
|
|
||||||
test "returns a module for a solver" do
|
|
||||||
assert solver("1.1") == AdventOfCode.Day01.Part1
|
|
||||||
assert solver("2.2") == AdventOfCode.Day02.Part2
|
|
||||||
assert solver("3.1") == AdventOfCode.Day03.Part1
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns a solver module for zero padded numbers" do
|
|
||||||
assert solver("01.01") == AdventOfCode.Day01.Part1
|
|
||||||
assert solver("02.2") == AdventOfCode.Day02.Part2
|
|
||||||
assert solver("3.01") == AdventOfCode.Day03.Part1
|
|
||||||
assert solver("12.1") == AdventOfCode.Day12.Part1
|
|
||||||
assert solver("24.02") == AdventOfCode.Day24.Part2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,20 +0,0 @@
|
||||||
defmodule AdventOfCode.PuzzleCase do
|
|
||||||
@moduledoc """
|
|
||||||
Defines tests for an `AdventOfCode.PuzzleSolver` module.
|
|
||||||
"""
|
|
||||||
|
|
||||||
use ExUnit.CaseTemplate
|
|
||||||
|
|
||||||
using module: module do
|
|
||||||
quote bind_quoted: [module: module] do
|
|
||||||
@module module
|
|
||||||
|
|
||||||
defp assert_solution(input, desired_output) when is_binary(input) do
|
|
||||||
{:ok, stream_pid} = StringIO.open(input)
|
|
||||||
stream_input = IO.stream(stream_pid, :line)
|
|
||||||
actual_output = AdventOfCode.PuzzleSolver.solve(@module, stream_input)
|
|
||||||
assert actual_output == desired_output
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in a new issue