Skip to content

Commit 739077a

Browse files
committed
refactor: moved most of core functionality into appropriate classes in a new Infrastructure namespace, and added a couple new features
1 parent abd6dd1 commit 739077a

File tree

9 files changed

+202
-132
lines changed

9 files changed

+202
-132
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,4 +341,5 @@ ASALocalRun/
341341

342342
# Local configuration
343343
config.json
344-
AdventOfCode/Solutions/Year*/Day*/input
344+
AdventOfCode/Solutions/Year*/Day*/debug
345+
AdventOfCode/Solutions/Year*/Day*/input
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using System.Runtime.Serialization;
3+
4+
namespace AdventOfCode.Infrastructure.Exceptions
5+
{
6+
class InputException : Exception
7+
{
8+
public InputException(string message) : base(message) { }
9+
10+
protected InputException(SerializationInfo info, StreamingContext context) : base(info, context) { }
11+
}
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using AdventOfCode.Infrastructure.Models;
3+
using AdventOfCode.Solutions;
4+
5+
namespace AdventOfCode.Infrastructure.Helpers
6+
{
7+
static class FormatHelper
8+
{
9+
public static string FormatDay(ASolution solution) => solution.ToString();
10+
11+
public static string FormatTitle(int day, string title) => $"Day {day}: {title}";
12+
13+
public static string FormatDebug(string debugInput) => string.IsNullOrEmpty(debugInput)
14+
? "!! Debug mode active, but no DebugInput defined"
15+
: "!! Debug mode active, using DebugInput";
16+
17+
public static string FormatPart(int part, SolutionResult result)
18+
=> $" - Part{part} => " + (string.IsNullOrEmpty(result.Answer) ? "Unsolved" : $"{result.Answer} ({result.Time.TotalMilliseconds}ms)");
19+
}
20+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System;
2+
using System.IO;
3+
using System.Net;
4+
5+
namespace AdventOfCode.Infrastructure.Helpers
6+
{
7+
static class InputHelper
8+
{
9+
public static string LoadInput(int day, int year)
10+
{
11+
string INPUT_FILEPATH = GetDayPath(day, year) + "/input";
12+
string INPUT_URL = GetAocInputUrl(day, year);
13+
string input = "";
14+
15+
if (File.Exists(INPUT_FILEPATH) && new FileInfo(INPUT_FILEPATH).Length > 0)
16+
{
17+
input = File.ReadAllText(INPUT_FILEPATH);
18+
}
19+
else
20+
{
21+
try
22+
{
23+
DateTime CURRENT_EST = TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.Utc).AddHours(-5);
24+
if (CURRENT_EST < new DateTime(year, 12, day)) throw new InvalidOperationException();
25+
26+
using (var client = new WebClient())
27+
{
28+
client.Headers.Add(HttpRequestHeader.Cookie, Program.Config.Cookie);
29+
input = client.DownloadString(INPUT_URL).Trim();
30+
File.WriteAllText(INPUT_FILEPATH, input);
31+
}
32+
}
33+
catch (WebException e)
34+
{
35+
var statusCode = ((HttpWebResponse)e.Response).StatusCode;
36+
var colour = Console.ForegroundColor;
37+
Console.ForegroundColor = ConsoleColor.DarkRed;
38+
if (statusCode == HttpStatusCode.BadRequest)
39+
{
40+
Console.WriteLine($"Day {day}: Error code 400 when attempting to retrieve puzzle input through the web client. Your session cookie is probably not recognized.");
41+
42+
}
43+
else if (statusCode == HttpStatusCode.NotFound)
44+
{
45+
Console.WriteLine($"Day {day}: Error code 404 when attempting to retrieve puzzle input through the web client. The puzzle is probably not available yet.");
46+
}
47+
else
48+
{
49+
Console.ForegroundColor = colour;
50+
Console.WriteLine(e.ToString());
51+
}
52+
Console.ForegroundColor = colour;
53+
}
54+
catch (InvalidOperationException)
55+
{
56+
var colour = Console.ForegroundColor;
57+
Console.ForegroundColor = ConsoleColor.DarkYellow;
58+
Console.WriteLine($"Day {day}: Cannot fetch puzzle input before given date (Eastern Standard Time).");
59+
Console.ForegroundColor = colour;
60+
}
61+
}
62+
return input;
63+
}
64+
65+
public static string LoadDebugInput(int day, int year)
66+
{
67+
string INPUT_FILEPATH = GetDayPath(day, year) + "/debug";
68+
return (File.Exists(INPUT_FILEPATH) && new FileInfo(INPUT_FILEPATH).Length > 0)
69+
? File.ReadAllText(INPUT_FILEPATH)
70+
: "";
71+
}
72+
73+
static string GetDayPath(int day, int year)
74+
=> Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, $"../../../Solutions/Year{year}/Day{day.ToString("D2")}"));
75+
76+
static string GetAocInputUrl(int day, int year)
77+
=> $"https://adventofcode.com/{year}/day/{day}/input";
78+
}
79+
}

AdventOfCode/Config.cs renamed to AdventOfCode/Infrastructure/Models/Config.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@
66
using System.Text.Json.Serialization;
77
using System.Text.RegularExpressions;
88

9-
namespace AdventOfCode
9+
namespace AdventOfCode.Infrastructure.Models
1010
{
11-
1211
class Config
1312
{
14-
1513
string _c;
1614
int _y;
1715
int[] _d;
@@ -21,15 +19,15 @@ public string Cookie
2119
get => _c;
2220
set
2321
{
24-
if(Regex.IsMatch(value, "^session=[a-z0-9]+$")) _c = value;
22+
if (Regex.IsMatch(value, "^session=[a-z0-9]+$")) _c = value;
2523
}
2624
}
2725
public int Year
2826
{
2927
get => _y;
3028
set
3129
{
32-
if(value >= 2015 && value <= DateTime.Now.Year) _y = value;
30+
if (value >= 2015 && value <= DateTime.Now.Year) _y = value;
3331
}
3432
}
3533
[JsonConverter(typeof(DaysConverter))]
@@ -41,11 +39,11 @@ public int[] Days
4139
bool allDaysCovered = false;
4240
_d = value.Where(v =>
4341
{
44-
if(v == 0) allDaysCovered = true;
42+
if (v == 0) allDaysCovered = true;
4543
return v > 0 && v < 26;
4644
}).ToArray();
4745

48-
if(allDaysCovered)
46+
if (allDaysCovered)
4947
{
5048
_d = new int[] { 0 };
5149
}
@@ -61,8 +59,8 @@ void setDefaults()
6159
//Make sure we're looking at EST, or it might break for most of the US
6260
DateTime CURRENT_EST = TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.Utc).AddHours(-5);
6361
if (Cookie == default(string)) Cookie = "";
64-
if(Year == default(int)) Year = CURRENT_EST.Year;
65-
if(Days == default(int[])) Days = (CURRENT_EST.Month == 12 && CURRENT_EST.Day <= 25) ? new int[] { CURRENT_EST.Day } : new int[] { 0 };
62+
if (Year == default(int)) Year = CURRENT_EST.Year;
63+
if (Days == default(int[])) Days = (CURRENT_EST.Month == 12 && CURRENT_EST.Day <= 25) ? new int[] { CURRENT_EST.Day } : new int[] { 0 };
6664
}
6765

6866
public static Config Get(string path)
@@ -74,7 +72,7 @@ public static Config Get(string path)
7472
WriteIndented = true
7573
};
7674
Config config;
77-
if(File.Exists(path))
75+
if (File.Exists(path))
7876
{
7977
config = JsonSerializer.Deserialize<Config>(File.ReadAllText(path), options);
8078
config.setDefaults();
@@ -85,6 +83,7 @@ public static Config Get(string path)
8583
config.setDefaults();
8684
File.WriteAllText(path, JsonSerializer.Serialize<Config>(config, options));
8785
}
86+
8887
return config;
8988
}
9089
}
@@ -93,7 +92,7 @@ class DaysConverter : JsonConverter<int[]>
9392
{
9493
public override int[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9594
{
96-
if(reader.TokenType == JsonTokenType.Number) return new int[] { reader.GetInt16() };
95+
if (reader.TokenType == JsonTokenType.Number) return new int[] { reader.GetInt16() };
9796
var tokens = reader.TokenType == JsonTokenType.String
9897
? new string[] { reader.GetString() }
9998
: JsonSerializer.Deserialize<object[]>(ref reader).Select<object, string>(o => o.ToString());
@@ -104,25 +103,26 @@ private IEnumerable<int> ParseString(string str)
104103
{
105104
return str.Split(",").SelectMany<string, int>(str =>
106105
{
107-
if(str.Contains(".."))
106+
if (str.Contains(".."))
108107
{
109108
var split = str.Split("..");
110109
int start = int.Parse(split[0]);
111110
int stop = int.Parse(split[1]);
112111
return Enumerable.Range(start, stop - start + 1);
113112
}
114-
else if(int.TryParse(str, out int day))
113+
else if (int.TryParse(str, out int day))
115114
{
116115
return new int[] { day };
117116
}
117+
118118
return new int[0];
119119
});
120120
}
121121

122122
public override void Write(Utf8JsonWriter writer, int[] value, JsonSerializerOptions options)
123123
{
124124
writer.WriteStartArray();
125-
foreach(int val in value) writer.WriteNumberValue(val);
125+
foreach (int val in value) writer.WriteNumberValue(val);
126126
writer.WriteEndArray();
127127
}
128128
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace AdventOfCode.Infrastructure.Models
4+
{
5+
class SolutionResult
6+
{
7+
public string Answer { get; set; }
8+
public TimeSpan Time { get; set; }
9+
10+
public static SolutionResult Empty => new SolutionResult();
11+
}
12+
}

AdventOfCode/Solutions/SolutionCollector.cs renamed to AdventOfCode/Infrastructure/SolutionCollector.cs

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@
22
using System.Collections;
33
using System.Collections.Generic;
44
using System.Linq;
5-
using System.Reflection;
5+
using AdventOfCode.Solutions;
66

7-
namespace AdventOfCode.Solutions
7+
namespace AdventOfCode.Infrastructure
88
{
9-
109
class SolutionCollector : IEnumerable<ASolution>
1110
{
12-
1311
IEnumerable<ASolution> Solutions;
1412

1513
public SolutionCollector(int year, int[] days) => Solutions = LoadSolutions(year, days).ToArray();
@@ -20,33 +18,28 @@ public ASolution GetSolution(int day)
2018
{
2119
return Solutions.Single(s => s.Day == day);
2220
}
23-
catch(InvalidOperationException)
21+
catch (InvalidOperationException)
2422
{
2523
return null;
2624
}
2725
}
2826

29-
public IEnumerator<ASolution> GetEnumerator()
30-
{
31-
return Solutions.GetEnumerator();
32-
}
27+
public IEnumerator<ASolution> GetEnumerator() => Solutions.GetEnumerator();
3328

34-
IEnumerator IEnumerable.GetEnumerator()
35-
{
36-
return GetEnumerator();
37-
}
29+
30+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
3831

3932
IEnumerable<ASolution> LoadSolutions(int year, int[] days)
4033
{
41-
if(days.Sum() == 0)
34+
if (days.Sum() == 0)
4235
{
4336
days = Enumerable.Range(1, 25).ToArray();
4437
}
45-
46-
foreach(int day in days)
38+
39+
foreach (int day in days)
4740
{
4841
var solution = Type.GetType($"AdventOfCode.Solutions.Year{year}.Day{day.ToString("D2")}");
49-
if(solution != null)
42+
if (solution != null)
5043
{
5144
yield return (ASolution)Activator.CreateInstance(solution);
5245
}

AdventOfCode/Program.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
using AdventOfCode.Solutions;
1+
using System;
2+
using AdventOfCode.Infrastructure;
3+
using AdventOfCode.Infrastructure.Helpers;
4+
using AdventOfCode.Infrastructure.Models;
5+
using AdventOfCode.Solutions;
26

37
namespace AdventOfCode
48
{
5-
69
class Program
710
{
8-
911
public static Config Config = Config.Get("config.json");
1012
static SolutionCollector Solutions = new SolutionCollector(Config.Year, Config.Days);
1113

1214
static void Main(string[] args)
1315
{
14-
foreach(ASolution solution in Solutions)
16+
foreach (ASolution solution in Solutions)
1517
{
16-
solution.Solve();
18+
Console.WriteLine();
19+
Console.WriteLine(FormatHelper.FormatDay(solution));
1720
}
1821
}
1922
}

0 commit comments

Comments
 (0)