Skip to content

Commit bee2c3c

Browse files
committed
docs(README): rework the docs to include the new fluent API
1 parent 183b089 commit bee2c3c

File tree

2 files changed

+131
-97
lines changed

2 files changed

+131
-97
lines changed

README.md

Lines changed: 130 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,50 @@
1-
# Dotnet Advent of Code
1+
# 🎄 .NET Advent of Code Template
22

3-
[Advent of Code](https://adventofcode.com) template for .NET contenders, focus on the puzzle, let it take care the rest
3+
**A ready-to-go **Advent of Code** template for .NET contenders.**
4+
**Focus on solving the puzzle; we handle the boilerplate, input, and testing.**
45

5-
> After gaining yours, consider leaving a small ⭐ to this project too!
6+
This template's core strength is its foundation in **xUnit test projects**, which
7+
provides two massive advantages for complex AoC puzzles: **seamless debugging** and native support for **Test-Driven Development (TDD)**.
68

7-
---
9+
Here are the features offered by the template:
810

9-
## Advantages
11+
* **Automatic Input Retrieval** (local file or remotely fetches your input for a given day)
12+
* **Modeling/Parsing Tests** to allow you to model the problem as you want
13+
* **Example Tests** using provided examples and expected results to ensure your solution satisfies it
14+
* **Conditional Test Skipping** focus only on the parts you're currently working on
15+
* Pre-configured **[.NET CI](.github/workflows/dotnet.yml)** to ensure all puzzles are valid
1016

11-
In order to solve your puzzle, you might need the debugger from time to time, especially as the puzzles become harder.
17+
> If this template helps you earn your stars, consider leaving one on this repository
18+
> too! ⭐
1219
13-
This template is built around test projects, in order to take advatage of the debugging possibilities and TDD if you want to.
20+
## Usage & Setup
1421

15-
Here are also a bunch of features offered by the template:
22+
This template provides two core components: the **`Solver`** (for logic) and the
23+
**`TestEngine`** (for verification).
1624

17-
- Retrieval of the puzzle input localy or remotely
18-
- Testing of your modelization if you would like to
19-
- Testing of the puzzle examples
20-
- Conditionnaly skip the tests of a puzzle if wanted
25+
**Setup:** To begin a new year, simply create a new **`xUnit`** project and add a
26+
reference to the **`AdventOfCode.Commons`** library.
2127

22-
## Usage
28+
> If you want to jump straight to the code, check out the
29+
> [demo project](src/AdventOfCode.Usage).
2330
24-
This template exposes two classes: a `Solver` and a `TestEngine`.
25-
The [.NET CI](.github/workflows/dotnet.yml) is also preconfigured to ensure that all your tests are valid.
31+
### Solving a New Puzzle
2632

27-
To get started with a new year, just create a new `xUnit` project and add a reference to the `AdventOfCode.Commons` project to it.
33+
Create a new solver by inheriting the generic `Solver<TInput, TResult>` class,
34+
where `TInput` is your parsed data model and `TResult` is the expected result type.
2835

29-
> If you want to jump straight to the code for the usage, check the [demo project](src/AdventOfCode.Usage)
36+
Your `Solver` requires you to implement three core methods:
3037

31-
### Solving a new puzzle
38+
1. **`ParseInput`**: Convert raw `IEnumerable<string>` input lines into your strongly-typed `TInput` model.
39+
2. **`PartOne`**: Implement the logic for the first part of the puzzle.
40+
3. **`PartTwo`**: Implement the logic for the second part of the puzzle.
3241

33-
When working a new day, create a new `Solver` by inheriting the `Solver<TInput, TResult>` class,
34-
with `TInput` being the type of the input you will be working with and `TResult` the type of the result you are expecting.
35-
36-
You will then have to implement three different logics:
37-
38-
1. **The parsing of the input** in which you will have to convert the raw data from the file you are reading into the `TInput`
39-
you will be expecting afterwards
40-
2. **The logic for the first part of the puzzle**
41-
3. **The logic for the second part of the puzzle**
42-
43-
For example, if the first part of the puzzle is "Given a list of integers, find the greatest one" and the second one "Now find the sum of them", we can do the following:
42+
**Example Solver (Local Input):**
4443

4544
```csharp
46-
public class Solver : Solver<int[], int>
45+
public class Solver()
46+
: Solver<int[], int>(inputPath: "WithLocalInput/input.txt")
4747
{
48-
public Solver() : base(inputPath: "WithLocalInput/input.txt") { }
49-
5048
public override int PartOne(int[] input)
5149
=> input.Max();
5250

@@ -58,53 +56,36 @@ public class Solver : Solver<int[], int>
5856
}
5957
```
6058

61-
If you prefer fetching your input from the server, you can instead specify the date:
59+
**Example Solver (Remote Input):**
6260

6361
```csharp
64-
public class Solver : Solver<int[], int>
65-
{
66-
public Solver() : base(year: 0000, day: 00) { }
67-
68-
public override int PartOne(int[] input)
69-
=> input.Max();
70-
71-
public override int PartTwo(int[] input)
72-
=> input.Sum();
73-
74-
public override int[] ParseInput(IEnumerable<string> input)
75-
=> input.Select(int.Parse).ToArray();
76-
}
62+
public class Solver()
63+
: Solver<int[], int>(year: 2025, day: 01) { ... }
7764
```
7865

79-
### Testing your solution
80-
81-
Once that your have coded your `Solver`, you may want to test it.
82-
83-
To do so, create a new class inheriting from `TestEngine<TSolver, TInput, TResult>`, with `TSolver` the type of your solver and
84-
`TInput` and `TResult` the same as it.
85-
86-
You will then have to implement two `Puzzle` properties.
87-
Those represent the input of each part of the puzzle of the day.
66+
### Testing with the Fluent API
8867

89-
For each part you will have to specify what the example is (its input and solution) and the solution you are expecting.
90-
Specifying the example allows the engine to test your solution against a predictible result in order to help you to debug it.
68+
Once you have implemented your `Solver`, create your test class by inheriting from
69+
`TestEngine<TSolver, TInput, TResult>`.
9170

92-
Keeping our example in mind, the associated `TestEngine` might be:
71+
You will define the expected inputs and solutions via the `PartOne` and `PartTwo`
72+
properties.
9373

9474
```csharp
95-
public class SolverTest : TestEngine<Solver, int[], int>
75+
public sealed class SolverTest : TestEngine<Solver, int[], int>
9676
{
97-
public override Puzzle PartOne => new()
98-
{
99-
ShouldSkipTests = false, // Default to false
100-
Example = new()
101-
{
102-
Input = new[] { 1, 2, 3 },
103-
Result = 3,
104-
},
105-
Solution = 5,
106-
};
107-
77+
// Define Part One using the fluent builder
78+
public override Puzzle PartOne => PuzzleBuilder
79+
// 1. The raw input lines for the example
80+
.FromInput(["1", "2", "3"])
81+
// 2. The expected result of the ParseInput method (TInput)
82+
.ParsedAs([1, 2, 3])
83+
// 3. The expected result for the example input
84+
.ExpectsResult(3)
85+
// 4. The correct answer for the main puzzle input
86+
.WithTheActualSolutionBeing(5);
87+
88+
// You can also directly create the Puzzle object
10889
public override Puzzle PartTwo => new()
10990
{
11091
Example = new()
@@ -117,44 +98,86 @@ public class SolverTest : TestEngine<Solver, int[], int>
11798
}
11899
```
119100

120-
### Testing your parsing
121-
122-
This testing feature allows you to verify the correctness of the `ParseInput` method by
123-
comparing its output with a manually provided `RawInput`. It's completely optional, so
124-
you can choose to use it based on your preferences.
101+
### Skipping Tests
125102

126-
If you would like to, define the `RawInput` property along with your `Input` in your `Example`.
127-
If the `RawInput` is not set or empty, this part won't be tested.
103+
If you are stuck on a puzzle part, you can conditionally **skip all tests** for
104+
that part by using the builder, or setting `ShouldSkipTests = true` on the `Puzzle`
105+
definition:
128106

129-
```cs
130-
public override Puzzle PartOne => new()
107+
```csharp
108+
public sealed class SolverTest
109+
: TestEngine<Solver, int[], int>
131110
{
132-
Example = new()
111+
public override Puzzle PartOne => PuzzleBuilder
112+
// ...
113+
.WithTheActualSolutionBeing(5, skipTests: true); // Builder shortcut
114+
115+
public override Puzzle PartTwo => new()
133116
{
134-
// 👇 Since this is defined, a test will run to check if `ParseInput(RawInput)` is equal to `Input`
135-
RawInput = ["1", "2", "3"],
136-
Input = [1, 2, 3],
137-
Result = 3,
138-
},
139-
Solution = 5,
140-
};
117+
// ...
118+
ShouldSkipTests = true, // Direct property
119+
};
120+
}
141121
```
142122

143-
> Since the example might change from one part to the other, the `RawInput` is defined in each part instead of on the puzzle-level
123+
Alternatively, you can **skip only the parsing step** (if you prefer to define the
124+
input as `TInput` directly) by using `PuzzleBuilder.FromParsedInput` or leaving
125+
`Example.RawInput` empty:
144126

145-
## Troubleshooting
127+
```csharp
128+
public sealed class SolverTest
129+
: TestEngine<Solver, int[], int>
130+
{
131+
public override Puzzle PartOne => PuzzleBuilder
132+
.FromParsedInput([1, 2, 3])
133+
.ExpectsResult(3)
134+
.WithTheActualSolutionBeing(5);
146135

147-
### How can I find my cookie?
136+
// ...
137+
}
138+
```
148139

149-
Under the website of the [Advent of Code](https://adventofcode.com), open the dev tools and find the Advent of Code cookie in your storage:
140+
## Troubleshooting & FAQ
141+
142+
<details>
143+
<summary><b>Why isn't this a NuGet package?</b></summary>
144+
145+
This project is primarily intended to be used as a local template or library reference
146+
rather than a published NuGet package. The main reason for this is related to input
147+
retrieval from the official Advent of Code site.
148+
149+
To automatically fetch your puzzle input remotely, the template requires your personal
150+
AoC session cookie.
151+
152+
- If this library were published on NuGet, distributing it would require either
153+
hardcoding the input retrieval logic that depends on a local file or environment
154+
variable, or potentially handling cookie storage in a way that might raise
155+
security/privacy concerns for some users.
156+
- By keeping the common components as an `AdventOfCode.Commons` project reference
157+
within your repository, you retain full control over how your session cookie is
158+
managed, preventing any information from being unintentionally included or
159+
exposed.
160+
161+
</details>
162+
163+
<details>
164+
<summary><b>How can I find my AoC session cookie?</b></summary>
165+
166+
Under the website of the [Advent of Code](https://adventofcode.com/), open the
167+
developer tools (usually `F12`) and inspect the application/storage tab to find
168+
the session cookie:
150169

151170
![Example](https://user-images.githubusercontent.com/22640284/205501479-31e2e5ef-d50e-43f8-8a45-4741a473861c.png)
152171

153172
You can then copy its value.
154173

155-
### Where can I set my cookie?
174+
</details>
175+
176+
<details>
177+
<summary><b>Where can I set my cookie?</b></summary>
156178

157-
You can either provide the cookie as an environment variable named `AOC_COOKIE`, or directly set it in the `Configuration.cs` file:
179+
You can either provide the cookie as an environment variable named `AOC_COOKIE`, or
180+
directly set it in the `Configuration.cs` file:
158181

159182
```csharp
160183
public static class Configuration
@@ -163,12 +186,22 @@ public static class Configuration
163186
}
164187
```
165188

166-
### My input is missing!
189+
</details>
190+
191+
<details>
192+
<summary><b>My input is missing!</b></summary>
167193

168-
Be sure to have your input data accessible, you can do so by indicating how VS should take care of your file:
194+
Be sure to have your input data accessible, you can do so by indicating how VS should
195+
take care of your file:
169196

170197
![image](https://user-images.githubusercontent.com/22640284/205364254-5e1b7995-d267-4809-8ffa-5e68efe84b84.png)
171198

172-
### No tests are showing up!
199+
</details>
200+
201+
<details>
202+
<summary><b>No tests are showing up!</b></summary>
203+
204+
Verify if your `TestEngine` implementation is `public` or `xUnit` might have some
205+
trouble finding it.
173206

174-
Verify if your `TestEngine` implementation is `public` or `xUnit` might have some trouble finding it.
207+
</details>

src/advent-of-code.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ EndProject
1010
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E9D5A0EF-004F-4163-8F74-31F89FA01DD0}"
1111
ProjectSection(SolutionItems) = preProject
1212
..\.github\workflows\dotnet.yml = ..\.github\workflows\dotnet.yml
13+
..\README.md = ..\README.md
1314
EndProjectSection
1415
EndProject
1516
Global

0 commit comments

Comments
 (0)