AoCHelper
AoCHelper copied to clipboard
Helper .NET library for solving Advent of Code puzzles
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
DayXXorDay_XXand make them inheritBaseDay. - Name them
ProblemXXorProblem_XXand make them inheritBaseProblem.
- Name them
- Put your input files under
Inputs/directory and followXX.txtnaming 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 customSolverConfigurationif 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
ClassPrefixproperty, to be able to follow your own$(ClassPrefix)XXor$(ClassPrefix)_XXconvention in each one of your problem classes. - Override
InputFileDirPathto change the input files directory - Override
InputFileExtensionto change the input files extension. - Override
CalculateIndex()to follow a differentXXor_XXconvention in your class names. - Override
InputFilePathto 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
InputFilePathin any specific problem class to point to a concrete file. This will make the values ofClassPrefix,InputFileDirPathandInputFileExtensionand 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.