mirror of
https://github.com/parnic/advent-of-code-2022.git
synced 2025-06-16 21:41:53 -05:00
Initial commit - framework
This commit is contained in:
62
src/01.cs
Normal file
62
src/01.cs
Normal 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
55
src/Day.cs
Normal 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
41
src/Extensions.cs
Normal 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
72
src/Logger.cs
Normal 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
25
src/Template.cs
Normal 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
48
src/Timer.cs
Normal 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
95
src/Util.cs
Normal 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
74
src/main.cs
Normal 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();
|
||||
}
|
Reference in New Issue
Block a user