feat: solution runner
This commit is contained in:
parent
9726431599
commit
14ba910fa2
14 changed files with 134 additions and 25 deletions
2021
|
@ -1,4 +1,5 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
|
||||
locals_without_parens: [assert_solution: 2]
|
||||
]
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
defmodule AdventOfCode do
|
||||
@moduledoc """
|
||||
Documentation for `AdventOfCode`.
|
||||
Solutions to the 2021 Advent of Code puzzles
|
||||
"""
|
||||
|
||||
def solver(selector) do
|
||||
[day, part] = String.split(selector, ".")
|
||||
day_module = Macro.camelize("Day#{day}")
|
||||
part_module = Macro.camelize("Part#{part}")
|
||||
Module.concat([AdventOfCode, day_module, part_module])
|
||||
end
|
||||
end
|
||||
|
|
0
2021/lib/advent_of_code/day0.ex
Normal file
0
2021/lib/advent_of_code/day0.ex
Normal file
11
2021/lib/advent_of_code/day0/part0.ex
Normal file
11
2021/lib/advent_of_code/day0/part0.ex
Normal file
|
@ -0,0 +1,11 @@
|
|||
defmodule AdventOfCode.Day0.Part0 do
|
||||
alias AdventOfCode.PuzzleSolver
|
||||
|
||||
use PuzzleSolver
|
||||
|
||||
@impl PuzzleSolver
|
||||
def solve(stream) do
|
||||
Stream.run(stream)
|
||||
"42"
|
||||
end
|
||||
end
|
|
@ -6,7 +6,9 @@ defmodule AdventOfCode.PuzzleSolver do
|
|||
@doc """
|
||||
Given the input as a stream, return the solution as a string
|
||||
"""
|
||||
@callback solve(IO.Stream.t()) :: String.t()
|
||||
@callback solve(Enumerable.t()) :: String.t()
|
||||
|
||||
def solve(mod, stream), do: apply(mod, :solve, [stream])
|
||||
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
|
|
73
2021/lib/mix/tasks/advent_of_code.solve.ex
Normal file
73
2021/lib/mix/tasks/advent_of_code.solve.ex
Normal file
|
@ -0,0 +1,73 @@
|
|||
defmodule Mix.Tasks.AdventOfCode.Solve do
|
||||
use Mix.Task
|
||||
|
||||
@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
|
||||
input = Keyword.get(opts, :input, selector)
|
||||
|
||||
if input == "-" do
|
||||
{:indeterminate, IO.stream()}
|
||||
else
|
||||
file = Path.join("priv/inputs", "#{input}.input")
|
||||
stream = File.stream!(file)
|
||||
{Enum.count(stream), stream}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,18 +0,0 @@
|
|||
defmodule Mix.Tasks.AdventOfCode.Solve do
|
||||
use Mix.Task
|
||||
|
||||
@shortdoc "Runs solution code with problem input"
|
||||
|
||||
@moduledoc """
|
||||
#{@shortdoc}.
|
||||
|
||||
## Examples
|
||||
|
||||
# Run Day 2, Part 1 solution program
|
||||
$ mix advent_of_code.solve 2.1
|
||||
"""
|
||||
|
||||
@impl Mix.Task
|
||||
def run(_args) do
|
||||
end
|
||||
end
|
|
@ -7,6 +7,7 @@ defmodule AdventOfCode.MixProject do
|
|||
version: "0.1.0",
|
||||
elixir: "~> 1.11",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
@ -18,11 +19,13 @@ defmodule AdventOfCode.MixProject do
|
|||
]
|
||||
end
|
||||
|
||||
defp elixirc_paths(:test), do: ["lib", "test/support"]
|
||||
defp elixirc_paths(_), do: ["lib"]
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
{:progress_bar, "~> 2.0"}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
4
2021/mix.lock
Normal file
4
2021/mix.lock
Normal file
|
@ -0,0 +1,4 @@
|
|||
%{
|
||||
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
||||
"progress_bar": {:hex, :progress_bar, "2.0.1", "7b40200112ae533d5adceb80ff75fbe66dc753bca5f6c55c073bfc122d71896d", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "2519eb58a2f149a3a094e729378256d8cb6d96a259ec94841bd69fdc71f18f87"},
|
||||
}
|
3
2021/priv/inputs/0.0.input
Normal file
3
2021/priv/inputs/0.0.input
Normal file
|
@ -0,0 +1,3 @@
|
|||
foo
|
||||
bar
|
||||
baz
|
7
2021/test/advent_of_code/day0/part_0_test.exs
Normal file
7
2021/test/advent_of_code/day0/part_0_test.exs
Normal file
|
@ -0,0 +1,7 @@
|
|||
defmodule AdventOfCode.Day0.Part0Test do
|
||||
use AdventOfCode.PuzzleCase, module: AdventOfCode.Day0.Part0
|
||||
|
||||
test "returns the answer to live the universe and everything" do
|
||||
assert_solution "life the universe and everything", "42"
|
||||
end
|
||||
end
|
13
2021/test/advent_of_code_test.exs
Normal file
13
2021/test/advent_of_code_test.exs
Normal file
|
@ -0,0 +1,13 @@
|
|||
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.Day1.Part1
|
||||
assert solver("2.2") == AdventOfCode.Day2.Part2
|
||||
assert solver("3.1") == AdventOfCode.Day3.Part1
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,8 +9,11 @@ defmodule AdventOfCode.PuzzleCase do
|
|||
quote bind_quoted: [module: module] do
|
||||
@module module
|
||||
|
||||
defp assert_solution(input, desired_output) do
|
||||
actual_output = @module.run(input)
|
||||
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)
|
||||
@module.solve(stream_input)
|
||||
assert actual_output == desired_output
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue