AoCAutomaton provides skeleton classes and interfaces that makes Advent of Code even more enjoyable by simplifying mundane tasks, so you can concentrate on solving the puzzles. It is available as a Nuget package, named AOC2.
AoCAutomaton implements an overall logic as well as some helpers.
The overall workflow is implemented by the Automaton
class.
It will:
- create an instance of your solving logic
- fetch your specific input data (and cache them)
- tests your logic against sample/test data you may have provided (usually by copy-pasting from the day's puzzle)
- if tests are not ok, it will stop there allowing you to fix your code.
- otherwise it runs your solving logic against your specific input
- submits the answer to the AoC site
- gets the result (and cache it) and provides you with the main message (success or failure kind)
- if this is not the good answer, it will stop. Otherwise
- it does the same for the second part of the question
The Automaton
caches data to prevent unnecessary hammering of the AoC
site. Also, it automatically deals with response delay and submit the answer as soon as possible. It will also
avoid submitting known wrong answers, such as null, zero or negatives values, previously tried answers or answers that
are outside the known range (when you got 'answer is too low' or 'too high' results).
AocAutomaton is designed to be used within your IDE of choice, focusing on reducing friction when solving AoC puzzles. Friction appears as the need to copy/paste your input data between the site and your code (either in-line or as dedicated data files), but also when copy/pasting the answer or inputting it manually, which leads to the occasional typo error. AocAutomaton also helps you verify that your solution works against the provided examples; and it also provides a minimal unit test logic so you can check methods in isolation
It does not provide standard algorithms and/or helpers to simplify the solving of the puzzles. I think this would spoil the fun. Of course, you are free to use any package you find suitable to help you find the solution if you are so inclined.
AocAutomation provides you an integration class that implements interaction between your solver and the AoC site. You just need to provide a class that contains the solving logic and implements a simple interface with only three methods.
AocAutomation use several Attributes for you to provide needed specifications. This is different from alpha
versions that required you to implement the abstract SetUp
method in order to provide these specifications via method
calls.
Other abstract classes are provided to offer alternative interaction approaches, so you can pick the design matching the puzzle characteristics and/or your preferences; and of course, you can even use your own design.
In order to interact with the AoC site, AoCAutomaton requires your AoC
session id. It is stored as a hexadecimal value in a session
cookie;
you must get this value (via your browser of choice).
Then you have two options:
- Safest: store it in an environment variable named
AOC_SESSION
. - Input it when prompted by the automaton. This is not recommended as it will be visible in the console and may be stored in your IDE history. The token will be stored in a json file (plain text). As a safety precaution, AoCAutomaton will add a .gitignore entry to prevent this file from being committed to a repo.
Bear in mind that these tokens have a lifetime of a month so expect to have to refresh it through the website. As of now, there is limited error handling, so you should except some exception if the token is invalid or expired.
Note: early alpha versions allowed to pass the session id as a setup parameter (in code). I reverted that design due to the risk of having session ids with public visibility on GitHub.
Just add AoC2 package to the project you (plan to) use for AdventOfCode.
In the main
method of your program, create an instance of the Automaton
class, such as.
var automaton = Automaton.WebsiteAutomaton(2025);
Write an ISolver
implementation for the day puzzle you plan to solve and
use the Automaton.RunDay
method that will:
- Fetch your data input from the AoC website
- Run your solver against every test data you have provided, if any
- Run your solver for question 1 (if tests are successful)
- Push the answer to AoC site
- Report the response (success or failure)
- Repeat with question 2 (test and submission) if question 1 was successful.
Here is a simple example of a program that uses the automaton to solve a puzzle.
internal class Program
{
// this is the entry point of your program
private void Main()
{
// create an automaton that interacts with the AoC website
var automaton = Automaton.WebsiteAutomaton();
// run a specific day solver
automaton.RunDay<TheSolver>();
// and voila, you're done. The automaton will take care of everything
}
}
...
// Your need to write a class to write your solving logic
// Specify the day number via the DayAttribute
[Day(12)]
internal class TheSolver : SolverWithParser
{
public void Parse(string data)
{
// parse the input data
// and store it in fields/properties as you see fit
...
}
// compute the answer to the first part
// provide example data via the example attribute (as many as you want)
[Example("test data", 12)]
[Example(1, "other test data", 25)] // this example can be used for both questions
public override object GetAnswer1()
{
// compute the answer
...
return answer;
}
// compute the answer to the second part
// reuse the example data via the ReuseExample attribute
[ReuseExample(1, 123)]
public override object GetAnswer2()
{
// compute the answer
...
return answer;
}
}
The following methods or properties can be used to modify the automaton behavior.
These methods or properties should be used during the setup phase, i.e. within your ISolver.SetupRun
method implementation. Using them at some other time is not documented
and may lead to surprising results
int Day {get;set}
Sets the day number. This is mandatory for interaction with the AoC website (if not set, an exception will stop the program).
void ResetBetweenQuestions()
When called, the automation engine will use separate (solver) instances for question 1 and 2. This can be helpful when the solver alter its initialization data. Note that it implies that data will be (automatically) parsed again.
The following methods can be used to provide test data that the automation will use to check your solver before trying to solve the exercise with your AoC provided input. AoCAutomaton support as many test examples as you may provide. It also supports providing example specific data for part one and two.
Automaton RegisterTestDataAndResult(string data, object expected, int question = 1);
where
data
: input data to be used for testing purposes
expected
: the expected answer.
question
: identifies for which question part the data must be used. 1
for the first part (default), 2
for the second part.
Provides input data for testing the solver as well as the expected answer, either for question 1 or 2. You can register as much test data as you wish (memory permitting, of course).
When you register test data and expected answer for question 1, you can provide the expected answer for question two via a subsequent call to RegisterTestResult
(or via AskVisualConfirmation
).
Automaton RegisterTestData(string data, int question = 3);
where
data
: input data to be used for testing purposes
question
: identifies for which question part the data must be used. 1
for the first part, 2
for the second part or 3
for both parts (default case).
Provides input data for testing the solver. By default this test data will be used for both part of the question, but you can specify which part this is for. You can register as much test data as you wish (memory permitting, of course).
Note that test data and test results must be registered in the same order, otherwise there will be mix-ups.
public Automaton RegisterTestResult(object expected, int question = 1)
where
expected
: the expected answer.
question
: identifies for which question part the data must be used. 1
for the first part or 2
for the second part.
Provides an expected result for testing the solver. Associated test data must have already been registered, otherwise an exception is raised, with one tolerance: you can register one (1) result for part two without providing test data for it. AoCAutomaton will automatically reuse the test data provided for part one.
Note that test data and test results must be registered in the same order, otherwise there will be mix ups.
public Automaton AskVisualConfirmation(int question = 1)
where
question
: identifies for which question part the data must be used. 1
for the first part or 2
for the second part.
Provides an expected result for testing the solver. Associated test data must have already been registered, otherwise an exception is raised, with one tolerance: you can register one (1) result for part two without providing test data for it. AoCAutomaton will automatically reuse the test data provided for part one.
Note that test data and test results must be registered in the same order, otherwise there will be mix ups.
You can choose any of the design of each of your solver.
This is the default interface, with only three methods. It is recommended when the data parsing is straightforward, which is usually the case for the initial puzzles of each year. This is also the interface than must be implemented by any custom design you may provide.
public interface ISolver
{
// provides the puzzle data
void SetupRun(Automaton automaton);
// compute the answer to the first part
object GetAnswer1(string data);
// compute the answer to the second part
object GetAnswer2(string data);
}
. SetupRun(Automaton automaton)
method: implement it to feed puzzle data (day and test values)
to the automaton
. GetAnswer[1|2](string data)
methods: implement the input parsing and solving logic within
each of these methods. Note than a solver instance will always receive the same data
value
for each method.
This abstract class offers a method to parse data outside of the solving logic. This design is perfect as soon as you want need to convert the input data to some specialized structure that will be stored as fields/properties. Parsing occurs only once per solver.
// this class definition is for documentation purpose
// actual definition is different
public class SolverWithParser
{
// provides the puzzle data
public abstract void SetupRun(Automaton automaton);
// compute the answer to the first part
public abstract object GetAnswer1();
// compute the answer to the second part
public abstract object GetAnswer2();
// parse the puzzle data
protected abstract void Parse(string data);
}
This is a variant from the previous one. Most AoC puzzles use a data set
consisting of one line records. Parsing will be implemented via the ParseLine
method.
It receives each line to parse, with its index and total number of lines.
// this class definition is for documentation purpose
// actual definition is different
public class SolverWithParser
{
// provides the puzzle data
public abstract void SetupRun(Automaton automaton);
// compute the answer to the first part
public abstract object GetAnswer1();
// compute the answer to the second part
public abstract object GetAnswer2();
// parse a single line
protected abstract void ParseLine(string line, int index, int lineCount);
}