AoCHelper
AoCHelper is a support library for solving Advent of Code puzzles, available for .NET and .NET Standard 2.x.
It provides a 'framework' so that you only have to worry about solving the problems, and measures the performance of your solutions.
Problem example:
using AoCHelper;
using System.Threading.Tasks;
namespace AdventOfCode
{
public class Day_01 : BaseDay
{
public override ValueTask<string> Solve_1() => new("Solution 1");
public override ValueTask<string> Solve_2() => new("Solution 2");
}
}
Output example:
AdventOfCode.Template
Creating your Advent of Code repository from AdventOfCode.Template is the quickest way to get up and running with AoCHelper
.
Simple usage
- Add AoCHelper NuGet package to your project.
- Create one class per day/problem, using one of the following approaches:
- Name them
DayXX
orDay_XX
and make them inheritBaseDay
. - Name them
ProblemXX
orProblem_XX
and make them inheritBaseProblem
.
- Name them
- Put your input files under
Inputs/
directory and followXX.txt
naming convention for dayXX
. Make sure to copy those files to your output folder. - Choose your solving strategy in your
Main()
method, adjusting it with your customSolverConfiguration
if needed:Solver.SolveAll();
Solver.SolveLast();
Solver.SolveLast(new SolverConfiguration { ClearConsole = false });
Solver.Solve<Day_05>();
Solver.Solve(new List<uint>{ 5, 6 });
Solver.Solve(new List<Type> { typeof(Day_05), typeof(Day_06) });
Customization
A custom SolverConfiguration
instance can be provided to any of the Solver
methods. These are the configurable parameters (false
or null
by default unless otherwise specified).
bool ClearConsole
: Clears previous runs information from the console. True by default.bool ShowOverallResults
: Shows a panel at the end of the run with aggregated stats of the solved problems. True by default when solving multiple problems, false otherwise.bool ShowConstructorElapsedTime
: Shows the time elapsed during the instantiation of aBaseProblem
. This normally reflects the elapsed time while parsing the input data.bool ShowTotalElapsedTimePerDay
: Shows total elapsed time per day. This includes constructor time + part 1 + part 2.string? ElapsedTimeFormatSpecifier
: Custom numeric format strings used for elapsed milliseconds. See Standard numeric format strings.
Advanced usage
You can also:
- Create your own abstract base class tha(t inherits
BaseProblem
, make all your problem classes inherit it and use this custom base class to:- Override
ClassPrefix
property, to be able to follow your own$(ClassPrefix)XX
or$(ClassPrefix)_XX
convention in each one of your problem classes. - Override
InputFileDirPath
to change the input files directory - Override
InputFileExtension
to change the input files extension. - Override
CalculateIndex()
to follow a differentXX
or_XX
convention in your class names. - Override
InputFilePath
to follow a different naming convention in your input files. Check the current implementation to understand how to reuse all the other properties and methods.
- Override
- [Not recommended] Override
InputFilePath
in any specific problem class to point to a concrete file. This will make the values ofClassPrefix
,InputFileDirPath
andInputFileExtension
and the implementation ofCalculateIndex()
irrelevant (see the current implementation).
Testing
- Example of simple AoC solutions testing: SampleTests
- Example of advanced AoC solutions testing by providing a custom input test filepath: ModifyInputFilePathTests_SampleTests
- Example of advanced AoC solutions testing by providing a custom input test dir path: ModifyInputFileDirPath_SampleTests
Usage examples
Example projects can be found at:
- AoC2021 (v1.x)
- AdventOfCode.Template
- AoCHelper.PoC
- AoCHelper.Test
- AoC2020 (v0.x)
- All these repositories
🆕 v0.x to v1.x migration
BaseProblem.Solve_1()
and BaseProblem.Solve_2()
signature has changed: they must return ValueTask<string>
now.
ValueTask<T>
has constructors that accept both T
and Task<T>
, so:
v0.x:
public class Day_01 : BaseDay
{
public override string Solve_1() => "Solution 2";
public override string Solve_2() => FooAsync().Result;
private async Task<string> FooAsync()
{
await Task.Delay(1000);
return "Solution 2";
}
}
becomes now in v1.x:
public class Day_01 : BaseDay
{
public override ValueTask<string> Solve_1() => new("Solution 2");
public override ValueTask<string> Solve_2() => new(FooAsync());
private async Task<string> FooAsync()
{
await Task.Delay(1000);
return "Solution 2";
}
}
or in case we prefer async
/await
over returning the task, as recommended here:
public class Day_01 : BaseDay
{
public override ValueTask<string> Solve_1() => new("Solution 2");
public override async ValueTask<string> Solve_2() => new(await FooAsync());
private async Task<string> FooAsync()
{
await Task.Delay(1000);
return "Solution 2";
}
}
Tips
Your problem classes are instantiated only once, so parsing the input file (InputFilePath
) in your class constructor allows you to:
- Avoid executing parsing logic twice per problem.
- Measure more accurately your part 1 and part 2 solutions performance*.
* Consider enabling ShowConstructorElapsedTime
and ShowTotalElapsedTimePerDay
in SolverConfiguration
.