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"
|
# 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
|
defmodule AdventOfCode do
|
||||||
@moduledoc """
|
@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
|
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 """
|
@doc """
|
||||||
Given the input as a stream, return the solution as a string
|
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
|
defmacro __using__(_) do
|
||||||
quote 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",
|
version: "0.1.0",
|
||||||
elixir: "~> 1.11",
|
elixir: "~> 1.11",
|
||||||
start_permanent: Mix.env() == :prod,
|
start_permanent: Mix.env() == :prod,
|
||||||
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
deps: deps()
|
deps: deps()
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
@ -18,11 +19,13 @@ defmodule AdventOfCode.MixProject do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp elixirc_paths(:test), do: ["lib", "test/support"]
|
||||||
|
defp elixirc_paths(_), do: ["lib"]
|
||||||
|
|
||||||
# Run "mix help deps" to learn about dependencies.
|
# Run "mix help deps" to learn about dependencies.
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
{:progress_bar, "~> 2.0"}
|
||||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
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
|
quote bind_quoted: [module: module] do
|
||||||
@module module
|
@module module
|
||||||
|
|
||||||
defp assert_solution(input, desired_output) do
|
defp assert_solution(input, desired_output) when is_binary(input) do
|
||||||
actual_output = @module.run(input)
|
{: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
|
assert actual_output == desired_output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue