Skip to content

Commit f6a1ef3

Browse files
authored
Merge pull request #2 from pBouillon/handle-for-remote-input
Handle the remote input
2 parents 43e4d4e + 3f0754b commit f6a1ef3

File tree

13 files changed

+267
-44
lines changed

13 files changed

+267
-44
lines changed

src/AdventOfCode.Commons/AdventOfCode.Commons.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>net7.0</TargetFramework>
@@ -8,11 +8,11 @@
88
<IsPackable>false</IsPackable>
99
</PropertyGroup>
1010

11-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
11+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
1212
<LangVersion>preview</LangVersion>
1313
</PropertyGroup>
1414

15-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
15+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
1616
<LangVersion>preview</LangVersion>
1717
</PropertyGroup>
1818

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace AdventOfCode.Commons;
2+
3+
/// <summary>
4+
/// Configure the constants and values required for the application to run
5+
/// </summary>
6+
public static class Configuration
7+
{
8+
/// <summary>
9+
/// Your session cookie, if you want to retrieve your input from the internet
10+
/// </summary>
11+
///
12+
/// <remarks>
13+
/// To get your cookie, head to https://adventofcode.com/, then look into the cookies
14+
/// the website has set and paste its value here, it be something like <c>session:"[value]"</c>
15+
/// </remarks>
16+
public readonly static string? CookieValue = Environment.GetEnvironmentVariable("AOC_COOKIE");
17+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace AdventOfCode.Commons.PuzzleInputReaderStrategy;
2+
3+
/// <summary>
4+
/// Defines the strategy to be used to read the puzzle input
5+
/// </summary>
6+
public interface IPuzzleInputReaderStrategy
7+
{
8+
/// <summary>
9+
/// Read the lines composing puzzle input
10+
/// </summary>
11+
///
12+
/// <returns>
13+
/// The puzzle input as an enumeration of its lines
14+
/// </returns>
15+
///
16+
/// <remarks>
17+
/// The trailing blank line is omitted
18+
/// </remarks>
19+
IEnumerable<string> ReadInput();
20+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace AdventOfCode.Commons.PuzzleInputReaderStrategy;
2+
3+
/// <summary>
4+
/// Strategy to retrieve the input from a local file at a given path
5+
/// </summary>
6+
public class LocalPuzzleInputReaderStrategy : IPuzzleInputReaderStrategy
7+
{
8+
/// <summary>
9+
/// The path where the puzzle input is located
10+
/// </summary>
11+
public required string InputPath { get; init; }
12+
13+
/// <inheritdoc />
14+
public IEnumerable<string> ReadInput()
15+
=> File.ReadAllLines(InputPath);
16+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System.Net;
2+
3+
namespace AdventOfCode.Commons.PuzzleInputReaderStrategy;
4+
5+
/// <summary>
6+
/// Strategy to retrieve the input from the advent of code server
7+
/// </summary>
8+
public class RemotePuzzleInputReaderStrategy : IPuzzleInputReaderStrategy
9+
{
10+
private const string AdventOfCodeBaseUrl = "https://adventofcode.com";
11+
12+
/// <summary>
13+
/// The year targeted by this reader
14+
/// </summary>
15+
public required int Year { get; init; }
16+
17+
/// <summary>
18+
/// The day of the puzzle whose entry we want to retrieve
19+
/// </summary>
20+
public required int Day { get; init; }
21+
22+
/// <summary>
23+
/// The inner HTTP client handler, preconfigured to use cookies and provide the value set in
24+
/// <see cref="Configuration.CookieValue"/>
25+
/// </summary>
26+
private static HttpClientHandler Handler => new()
27+
{
28+
CookieContainer = GetCookieContainer(),
29+
UseCookies = true,
30+
};
31+
32+
/// <summary>
33+
/// The inner HTTP client, preconfigured to make requests to the advent of code URI
34+
/// </summary>
35+
private static HttpClient Client => new(Handler)
36+
{
37+
BaseAddress = new Uri(AdventOfCodeBaseUrl),
38+
};
39+
40+
/// <summary>
41+
/// Create a new cookie container with the one set in <see cref="Configuration.CookieValue"/>
42+
/// </summary>
43+
///
44+
/// <returns>
45+
/// The cookie container for the <see cref="HttpClientHandler"/> to use
46+
/// </returns>
47+
///
48+
/// <exception cref="Exception">
49+
/// Thrown if it is called but <see cref="Configuration.CookieValue"/> is not set
50+
/// </exception>
51+
private static CookieContainer GetCookieContainer()
52+
{
53+
var container = new CookieContainer();
54+
55+
container.Add(new Cookie
56+
{
57+
Name = "session",
58+
Domain = ".adventofcode.com",
59+
Value = string.IsNullOrEmpty(Configuration.CookieValue)
60+
? throw new Exception("You need to specify your cookie in order to get your input puzzle")
61+
: Configuration.CookieValue,
62+
});
63+
64+
return container;
65+
}
66+
67+
/// <inheritdoc />
68+
public IEnumerable<string> ReadInput()
69+
{
70+
var task = Task.Run(async () =>
71+
{
72+
var response = await Client.GetAsync($"/{Year}/day/{Day}/input");
73+
74+
return await response.EnsureSuccessStatusCode()
75+
.Content
76+
.ReadAsStringAsync();
77+
});
78+
79+
task.Wait();
80+
81+
var content = task.Result;
82+
return content.Split("\n")[..^1];
83+
}
84+
}

src/AdventOfCode.Commons/Solver.cs

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace AdventOfCode.Commons;
1+
using AdventOfCode.Commons.PuzzleInputReaderStrategy;
2+
3+
namespace AdventOfCode.Commons;
24

35
/// <summary>
46
/// Represent the solver used for a given day
@@ -14,30 +16,56 @@
1416
public abstract class Solver<TInput, TResult>
1517
{
1618
/// <summary>
17-
/// The path to the input file (ex: <c>"day1/input.txt"</c>)
19+
/// The puzzle input
1820
/// </summary>
19-
protected abstract string InputPath { get; }
21+
public readonly Lazy<TInput> PuzzleInput;
2022

21-
public readonly TInput Input;
23+
/// <summary>
24+
/// Create a new solver and initialize its puzzle input based on the provided <see cref="IPuzzleInputReaderStrategy"/>
25+
/// </summary>
26+
/// <param name="puzzleInputReader">The strategy to use to retriever the puzzle input</param>
27+
private Solver(IPuzzleInputReaderStrategy puzzleInputReader)
28+
{
29+
PuzzleInput = new Lazy<TInput>(() =>
30+
{
31+
var content = puzzleInputReader.ReadInput();
32+
return ParseInput(content);
33+
});
34+
}
2235

23-
protected Solver()
24-
=> Input = ReadInput(InputPath);
36+
/// <summary>
37+
/// Create a new solver with a puzzle input located in a local file
38+
/// </summary>
39+
/// <param name="inputPath">The path to the file containing the input</param>
40+
protected Solver(string inputPath)
41+
: this(new LocalPuzzleInputReaderStrategy { InputPath = inputPath }) { }
2542

2643
/// <summary>
27-
/// Logic for the solution of the first part of the puzzle
44+
/// Create a new solver that will retrieve the puzzle input from the server
45+
/// </summary>
46+
/// <param name="year">The year of the current challenge</param>
47+
/// <param name="day">The day for which the puzzle input is needed</param>
48+
protected Solver(int year, int day)
49+
: this(new RemotePuzzleInputReaderStrategy { Year = year, Day = day }) { }
50+
51+
52+
/// <summary>
53+
/// Parse the input file and convert it to <typeparamref name="TInput"/>
54+
/// for it to be used as the input of the <see cref="PartOne(TInput)"/>
55+
/// and <see cref="PartTwo(TInput)"/>
2856
/// </summary>
2957
///
30-
/// <param name="input">
31-
/// The digested puzzle input
58+
/// <param name="inputPath">
59+
/// The path of your puzzle input
3260
/// </param>
3361
///
3462
/// <returns>
35-
/// The puzzle's solution
63+
/// The digested input
3664
/// </returns>
37-
public abstract TResult PartOne(TInput input);
65+
public abstract TInput ParseInput(IEnumerable<string> input);
3866

3967
/// <summary>
40-
/// Logic for the solution of the second part of the puzzle
68+
/// Logic for the solution of the first part of the puzzle
4169
/// </summary>
4270
///
4371
/// <param name="input">
@@ -47,20 +75,18 @@ protected Solver()
4775
/// <returns>
4876
/// The puzzle's solution
4977
/// </returns>
50-
public abstract TResult PartTwo(TInput input);
78+
public abstract TResult PartOne(TInput input);
5179

5280
/// <summary>
53-
/// Parse the input file and convert it to <typeparamref name="TInput"/>
54-
/// for it to be used as the input of the <see cref="PartOne(TInput)"/>
55-
/// and <see cref="PartTwo(TInput)"/>
81+
/// Logic for the solution of the second part of the puzzle
5682
/// </summary>
5783
///
58-
/// <param name="inputPath">
59-
/// The path of your puzzle input
84+
/// <param name="input">
85+
/// The digested puzzle input
6086
/// </param>
6187
///
6288
/// <returns>
63-
/// The digested input
89+
/// The puzzle's solution
6490
/// </returns>
65-
public abstract TInput ReadInput(string inputPath);
91+
public abstract TResult PartTwo(TInput input);
6692
}

src/AdventOfCode.Commons/TestEngine.cs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ namespace AdventOfCode.Commons;
77
/// <summary>
88
/// Test engine for the implemented <typeparamref name="TSolver"/>
99
/// </summary>
10-
///
10+
///
1111
/// <typeparam name="TSolver">
1212
/// The <see cref="Solver{TInput, TResult}"/> to use
1313
/// </typeparam>
14-
///
14+
///
1515
/// <typeparam name="TInput">
1616
/// The type of the data used to solve the puzzle
1717
/// </typeparam>
18-
///
18+
///
1919
/// <typeparam name="TResult">
2020
/// The type of the result of the puzzle
2121
/// </typeparam>
@@ -32,7 +32,7 @@ public record Example
3232
{
3333
/// <summary>
3434
/// The data of the input mentionned in the example, in the same form as the
35-
/// <see cref="Solver{TInput, TResult}.Input"/>
35+
/// <see cref="Solver{TInput, TResult}.PuzzleInput"/>
3636
/// </summary>
3737
public TInput Input { get; init; } = default!;
3838

@@ -71,8 +71,7 @@ public record Puzzle
7171
/// </summary>
7272
private readonly TSolver _solver;
7373

74-
protected TestEngine()
75-
=> _solver = new TSolver();
74+
protected TestEngine() => _solver = new TSolver();
7675

7776
#region Part #1
7877

@@ -82,7 +81,7 @@ protected TestEngine()
8281
public void PartOneExampleTest()
8382
{
8483
Skip.If(PartOne.ShouldSkipTests, "Puzzle.ShouldSkipTests has been set to true, test skipped");
85-
84+
8685
// Arrange
8786
var input = PartOne.Example.Input;
8887

@@ -99,10 +98,10 @@ public void PartOneSolutionTest()
9998
Skip.If(PartOne.ShouldSkipTests, "Puzzle.ShouldSkipTests has been set to true, test skipped");
10099

101100
// Arrange
102-
var input = _solver.Input;
103-
101+
var input = _solver.PuzzleInput;
102+
104103
// Act
105-
var result = _solver.PartOne(input);
104+
var result = _solver.PartOne(input.Value);
106105

107106
// Assert
108107
result.Should().Be(PartOne.Solution);
@@ -135,10 +134,10 @@ public void PartTwoSolutionTest()
135134
Skip.If(PartTwo.ShouldSkipTests, "Puzzle.ShouldSkipTests has been set to true, test skipped");
136135

137136
// Arrange
138-
var input = _solver.Input;
137+
var input = _solver.PuzzleInput;
139138

140139
// Act
141-
var result = _solver.PartTwo(input);
140+
var result = _solver.PartTwo(input.Value);
142141

143142
// Assert
144143
result.Should().Be(PartTwo.Solution);

src/AdventOfCode.Usage/AdventOfCode.Usage.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
</ItemGroup>
2727

2828
<ItemGroup>
29-
<None Update="input.txt">
29+
<None Update="WithLocalInput\input.txt">
3030
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
3131
</None>
3232
</ItemGroup>
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using AdventOfCode.Commons;
22

3-
namespace AdventOfCode.Usage;
3+
namespace AdventOfCode.Usage.WithLocalInput;
44

55
/// <summary>
66
/// Example of how to use the <see cref="Solver{TInput, TResult}"/> for a simple puzzle
@@ -13,17 +13,14 @@ namespace AdventOfCode.Usage;
1313
/// </summary>
1414
public class Solver : Solver<int[], int>
1515
{
16-
protected override string InputPath => "input.txt";
16+
public Solver() : base(inputPath: "WithLocalInput/input.txt") { }
1717

1818
public override int PartOne(int[] input)
1919
=> input.Max();
2020

2121
public override int PartTwo(int[] input)
2222
=> input.Sum();
2323

24-
public override int[] ReadInput(string inputPath)
25-
=> File
26-
.ReadAllLines(inputPath)
27-
.Select(int.Parse)
28-
.ToArray();
24+
public override int[] ParseInput(IEnumerable<string> input)
25+
=> input.Select(int.Parse).ToArray();
2926
}

src/AdventOfCode.Usage/SolverTest.cs renamed to src/AdventOfCode.Usage/WithLocalInput/SolverTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using AdventOfCode.Commons;
22

3-
namespace AdventOfCode.Usage;
3+
namespace AdventOfCode.Usage.WithLocalInput;
44

55
/// <summary>
66
/// Example of how to test the <see cref="Solver{TInput, TResult}"/>

0 commit comments

Comments
 (0)