Initial commit - framework

This commit is contained in:
2022-11-30 17:19:53 -06:00
commit 23945154d4
39 changed files with 9161 additions and 0 deletions

62
src/01.cs Normal file
View File

@ -0,0 +1,62 @@
namespace aoc2022;
internal class Day01 : Day
{
IEnumerable<string>? lines;
internal override void Parse()
{
lines = Util.ReadAllLines("inputs/01.txt");
}
internal override string Part1()
{
int lastDepth = 0;
int numIncreased = -1;
foreach (var line in lines!)
{
var depth = Convert.ToInt32(line);
if (depth > lastDepth)
{
numIncreased++;
}
lastDepth = depth;
}
return $"<+white>{numIncreased}";
}
internal override string Part2()
{
int lastTotal = 0;
int numIncreased = -1;
int num1 = -1;
int num2 = -1;
int num3 = -1;
foreach (var line in lines!)
{
var depth = Convert.ToInt32(line);
num1 = num2;
num2 = num3;
num3 = depth;
if (num1 < 0 || num2 < 0 || num3 < 0)
{
continue;
}
var total = num1 + num2 + num3;
if (total > lastTotal)
{
numIncreased++;
}
lastTotal = total;
}
return $"<+white>{numIncreased}";
}
}

55
src/Day.cs Normal file
View File

@ -0,0 +1,55 @@
using System.Diagnostics;
namespace aoc2022;
internal abstract class Day : IDisposable
{
public void Dispose()
{
Logger.Log("");
}
internal void Go(bool runPart1, bool runPart2)
{
Logger.Log($"<reverse>{GetType().Name}<r>");
using (var parseStopwatch = new Timer("Parsing"))
{
Parse();
}
if (runPart1)
{
using var stopwatch = new Timer();
var response = Part1();
stopwatch.Stop();
if (!string.IsNullOrEmpty(response))
{
Logger.Log($"<+black>> part1: {response}<r>");
}
else
{
stopwatch.Disabled = true;
}
}
if (runPart2)
{
using var stopwatch = new Timer();
var response = Part2();
stopwatch.Stop();
if (!string.IsNullOrEmpty(response))
{
Logger.Log($"<+black>> part2: {response}<r>");
}
else
{
stopwatch.Disabled = true;
}
}
}
internal virtual void Parse() { }
internal virtual string Part1() { return string.Empty; }
internal virtual string Part2() { return string.Empty; }
}

41
src/Extensions.cs Normal file
View File

@ -0,0 +1,41 @@
using System.Diagnostics;
namespace aoc2022;
internal static class Extensions
{
public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach (T item in enumeration)
{
action(item);
}
}
public static (double elapsed, string unit) ConvertToHumanReadable(this Stopwatch stopwatch)
{
var elapsed = 1.0d * stopwatch.ElapsedTicks / Stopwatch.Frequency;
var unit = "s";
if (elapsed < 0.001)
{
elapsed *= 1e+6;
unit = "us";
}
else if (elapsed < 1)
{
elapsed *= 1000;
unit = "ms";
}
else if (elapsed < 60)
{
unit = "s";
}
else if (elapsed < 60 * 60)
{
elapsed /= 60;
unit = "m";
}
return (elapsed, unit);
}
}

72
src/Logger.cs Normal file
View File

@ -0,0 +1,72 @@
using System.Diagnostics;
namespace aoc2022;
internal class Logger
{
private static readonly Dictionary<string, string> colorCodes = new()
{
{ "r", "\u001b[0m" },
{ "black", "\u001b[30m" },
{ "red", "\u001b[31m" },
{ "green", "\u001b[32m" },
{ "yellow", "\u001b[33m" },
{ "blue", "\u001b[34m" },
{ "magenta", "\u001b[35m" },
{ "cyan", "\u001b[36m" },
{ "white", "\u001b[37m" },
{ "+black", "\u001b[30;1m" },
{ "+red", "\u001b[31;1m" },
{ "+green", "\u001b[32;1m" },
{ "+yellow", "\u001b[33;1m" },
{ "+blue", "\u001b[34;1m" },
{ "+magenta", "\u001b[35;1m" },
{ "+cyan", "\u001b[36;1m" },
{ "+white", "\u001b[37;1m" },
{ "bgBlack", "\u001b[40m" },
{ "bgRed", "\u001b[41m" },
{ "bgGreen", "\u001b[42m" },
{ "bgYellow", "\u001b[43m" },
{ "bgBlue", "\u001b[44m" },
{ "bgMagenta", "\u001b[45m" },
{ "bgCyan", "\u001b[46m" },
{ "bgWhite", "\u001b[47m" },
{ "+bgBlack", "\u001b[40;1m" },
{ "+bgRed", "\u001b[41;1m" },
{ "+bgGreen", "\u001b[42;1m" },
{ "+bgYellow", "\u001b[43;1m" },
{ "+bgBlue", "\u001b[44;1m" },
{ "+bgMagenta", "\u001b[45;1m" },
{ "+bgCyan", "\u001b[46;1m" },
{ "+bgWhite", "\u001b[47;1m" },
{ "bold", "\u001b[1m" },
{ "underline", "\u001b[4m" },
{ "reverse", "\u001b[7m" },
};
public static void Log(string msg)
{
Console.WriteLine(InsertColorCodes(msg));
Debug.WriteLine(StripColorCodes(msg));
}
private static string InsertColorCodes(string msg)
{
foreach (var code in colorCodes)
{
msg = msg.Replace($"<{code.Key}>", code.Value, StringComparison.CurrentCultureIgnoreCase);
}
return msg;
}
private static string StripColorCodes(string msg)
{
foreach (var code in colorCodes)
{
msg = msg.Replace($"<{code.Key}>", string.Empty, StringComparison.CurrentCultureIgnoreCase);
}
return msg;
}
}

25
src/Template.cs Normal file
View File

@ -0,0 +1,25 @@
namespace aoc2022;
internal class DayTemplate : Day
{
IEnumerable<string>? lines;
internal override void Parse()
{
lines = Util.ReadAllLines("inputs/##.txt");
}
internal override string Part1()
{
return $"<+white>";
}
internal override string Part2()
{
return $"<+white>";
}
}

48
src/Timer.cs Normal file
View File

@ -0,0 +1,48 @@
using System.Diagnostics;
namespace aoc2022;
internal class Timer : IDisposable
{
private readonly Stopwatch stopwatch = Stopwatch.StartNew();
private readonly string? name;
private bool stopped = false;
public bool Disabled { get; set; }
public Timer(string? inName = null)
{
name = inName;
}
public void Stop()
{
if (stopped)
{
return;
}
stopwatch.Stop();
stopped = true;
}
public void Dispose()
{
Stop();
if (Disabled)
{
return;
}
var (elapsed, unit) = stopwatch.ConvertToHumanReadable();
var color = "<red>";
if (unit == "us" || (unit == "ms" && elapsed < 10))
{
color = "<green>";
}
else if (unit == "ms" && elapsed < 250)
{
color = "<yellow>";
}
Logger.Log($"<cyan>{name}{(!string.IsNullOrEmpty(name) ? " t" : "T")}ook {color}{elapsed:N1}{unit}<r>");
}
}

95
src/Util.cs Normal file
View File

@ -0,0 +1,95 @@
using System.Diagnostics;
using System.Text;
namespace aoc2022;
internal static class Util
{
private static readonly char[] StripPreamble = new char[] { (char)8745, (char)9559, (char)9488, };
internal static void ReadData(string filename, Action<string> processor)
{
if (Console.IsInputRedirected)
{
string? line;
bool processedSomething = false;
for (int i = 0; (line = Console.In.ReadLine()) != null; i++)
{
if (i == 0)
{
var preamble = Encoding.UTF8.GetPreamble();
if (Enumerable.SequenceEqual(line[0..preamble.Length], preamble.Select(x => (char)x)))
{
line = line[preamble.Length..];
}
else if (Enumerable.SequenceEqual(line[0..StripPreamble.Length].ToCharArray(), StripPreamble))
{
line = line[StripPreamble.Length..];
}
}
processor(line);
if (!string.IsNullOrEmpty(line))
{
processedSomething = true;
}
}
if (processedSomething)
{
return;
}
}
foreach (var line in File.ReadLines(filename))
{
processor(line);
}
}
internal static string ReadAllText(string filename)
{
string contents = string.Empty;
ReadData(filename, (line) => contents = line);
return contents;
}
internal static IEnumerable<string> ReadAllLines(string filename)
{
List<string> lines = new();
ReadData(filename, (line) => lines.Add(line));
return lines;
}
internal static IEnumerable<long> ReadAllLinesAsInts(string filename)
{
return ReadAllLines(filename).Select(long.Parse);
}
internal static void StartTestSet(string name)
{
Logger.Log($"<underline>test: {name}<r>");
}
internal static void StartTest(string label)
{
Logger.Log($"<magenta>{label}<r>");
}
internal static void TestCondition(Func<bool> a, bool printResult = true)
{
if (a?.Invoke() == false)
{
Debug.Assert(false);
if (printResult)
{
Logger.Log("<red>x<r>");
}
}
else
{
if (printResult)
{
Logger.Log("<green>✓<r>");
}
}
}
}

74
src/main.cs Normal file
View File

@ -0,0 +1,74 @@
using aoc2022;
var types = System.Reflection.Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(t => t.IsSubclassOf(typeof(Day)) && !t.IsAbstract && t.Name != "DayTemplate")
.OrderBy(t => t.Name);
bool runAll = false;
bool? runPart1 = null;
bool? runPart2 = null;
string? desiredDay = null;
foreach (var arg in args)
{
if (arg.Equals("-part1", StringComparison.CurrentCultureIgnoreCase))
{
runPart1 = true;
}
else if (arg.Equals("-part2", StringComparison.CurrentCultureIgnoreCase))
{
runPart2 = true;
}
else if (arg.Equals("all", StringComparison.CurrentCultureIgnoreCase))
{
runAll = true;
}
else
{
desiredDay = arg;
}
}
if (runPart1 != null || runPart2 != null)
{
if (runPart1 == null)
{
runPart1 = false;
}
if (runPart2 == null)
{
runPart2 = false;
}
}
if (runAll)
{
foreach (var type in types)
{
using var day = (Day)Activator.CreateInstance(type)!;
day.Go(runPart1 ?? true, runPart2 ?? true);
}
}
else
{
Day? day = null;
if (string.IsNullOrEmpty(desiredDay))
{
day = new Day01();
}
else
{
var type = types.FirstOrDefault(x => x.Name == $"Day{desiredDay?.PadLeft(2, '0')}");
if (type == null)
{
Logger.Log($"Unknown day <cyan>{desiredDay}<r>");
}
else
{
day = (Day?)Activator.CreateInstance(type);
}
}
day?.Go(runPart1 ?? true, runPart2 ?? true);
day?.Dispose();
}