AoCHelper 4.0.0

dotnet add package AoCHelper --version 4.0.0                
NuGet\Install-Package AoCHelper -Version 4.0.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AoCHelper" Version="4.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add AoCHelper --version 4.0.0                
#r "nuget: AoCHelper, 4.0.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install AoCHelper as a Cake Addin
#addin nuget:?package=AoCHelper&version=4.0.0

// Install AoCHelper as a Cake Tool
#tool nuget:?package=AoCHelper&version=4.0.0                

AoCHelper

GitHub Actions Nuget

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:

aochelper

AdventOfCode.Template

Creating your Advent of Code repository from AdventOfCode.Template is the quickest way to get up and running with AoCHelper.

There's also AdventOfCode.MultiYearTemplate available of you want to keep all the years in the same repository, but I'd recommend to use the former one if you're participating in Advent of Code or using AoCHelper for the first time.

Simple usage

  • Add AoCHelper NuGet package to your project.
  • Create one class per day/problem, using one of the following approaches:
    • Name them DayXX or Day_XX and make them inherit BaseDay.
    • Name them ProblemXX or Problem_XXand make them inherit BaseProblem.
  • Put your input files under Inputs/ directory and follow XX.txt naming convention for day XX. Make sure to copy those files to your output folder.
  • Choose your solving strategy in your Main() method, adjusting it with your custom Action<SolverConfiguration> if needed:
    • Solver.SolveAll();
    • Solver.SolveLast();
    • Solver.SolveLast(opt => opt.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 Action<SolverConfiguration> can be provided to any of the Solver methods. It has the following configurable options (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 a BaseProblem. 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.
  • List<Assembly>: Assembly/Assemblies where the problems are located. Defaults to Assembly.GetEntryAssembly(), which assumes the problems are in the same project where Solver is invoked.

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 different XX 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.
  • [Not recommended] Override InputFilePath in any specific problem class to point to a concrete file. This will make the values of ClassPrefix, InputFileDirPath and InputFileExtension and the implementation of CalculateIndex() irrelevant (see the current implementation).

Testing

Usage examples

Example projects can be found at:

Some cool repositories that add their own abstractions/customizations on top of AocHelper:

v1 to v2+ migration

Methods that accept an instance of SolverConfiguration were deprecated in v2 and removed in v3.

They have been replaced by methods that accept Action<SolverConfiguration>.

v1:

await Solver.SolveAll(new SolverConfiguration
{
    ShowConstructorElapsedTime = true,
    ShowOverallResults = true,
    ClearConsole = false
});

v2+:

await Solver.SolveAll(options =>
{
    options.ShowConstructorElapsedTime = true;
    options.ShowOverallResults = true;
    options.ClearConsole = false;
});

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/day 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 Action<SolverConfiguration>.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
4.0.0 4,069 11/24/2024
3.1.0 11,008 11/30/2023
3.1.0-alpha.1 87 11/28/2023
3.0.0 2,648 11/24/2023
3.0.0-alpha.1 80 11/20/2023
2.0.2 1,761 12/14/2022
2.0.2-alpha.1 130 12/11/2022
2.0.1 714 12/7/2022
2.0.0 1,429 11/12/2022
2.0.0-alpha.1 127 11/2/2022
1.0.2 1,932 11/29/2021
1.0.1 363 11/17/2021
1.0.0 359 11/16/2021
0.15.1 522 5/2/2021
0.15.0 800 12/16/2020
0.14.0 905 12/6/2020
0.13.0 471 12/4/2020
0.12.1 494 12/3/2020
0.11.0 516 12/2/2020
0.10.0 560 11/29/2020
0.9.0 457 11/29/2020
0.8.2 399 11/28/2020
0.8.1 414 11/28/2020
0.8.0 440 11/28/2020
0.7.0 407 11/26/2020
0.7.0-alpha.1 238 11/25/2020
0.6.0 425 11/24/2020
0.5.1 424 11/12/2020
0.5.0 499 5/3/2020
0.5.0-alpha-1 363 5/3/2020
0.4.0 514 2/29/2020
0.3.1 519 12/6/2019
0.3.0 514 12/6/2019
0.2.3 520 12/3/2019
0.2.2 533 12/3/2019
0.2.1 518 12/3/2019
0.2.0 502 12/2/2019
0.1.0 494 12/2/2019
0.1.0-alpha 400 11/29/2019