Initial commit, prep

This commit is contained in:
2023-11-22 15:06:15 -06:00
commit 4547803969
28 changed files with 3375 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
/target
/.vs/
*.user
/bin/
/obj/
*.exe

10
.idea/.idea.advent-of-code-2023/.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/.idea.advent-of-code-2023.iml
/projectSettingsUpdater.xml
/modules.xml
/contentModel.xml
# Editor-based HTTP Client requests
/httpRequests/

View File

@ -0,0 +1 @@
advent-of-code-2023

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

8
GlobalSuppressions.cs Normal file
View File

@ -0,0 +1,8 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "<Pending>", Scope = "member", Target = "~F:aoc2023.DayTemplate.lines")]

View File

@ -0,0 +1,32 @@
{
"profiles": {
"all days": {
"commandName": "Project",
"commandLineArgs": "all"
},
"current day": {
"commandName": "Project",
"commandLineArgs": ""
},
"current day part 1": {
"commandName": "Project",
"commandLineArgs": "-part1"
},
"current day part 2": {
"commandName": "Project",
"commandLineArgs": "-part2"
},
"all days part 1": {
"commandName": "Project",
"commandLineArgs": "all -part1"
},
"all days part 2": {
"commandName": "Project",
"commandLineArgs": "all -part2"
},
"specific day": {
"commandName": "Project",
"commandLineArgs": "1"
}
}
}

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# Advent of Code 2023
My solutions to [Advent of Code 2023](https://adventofcode.com/2023).

View File

@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>aoc2023</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<ProduceReferenceAssembly>False</ProduceReferenceAssembly>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<WarningLevel>9999</WarningLevel>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<NoWarn>1701;1702;8981</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<WarningLevel>9999</WarningLevel>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<NoWarn>1701;1702;8981</NoWarn>
</PropertyGroup>
<ItemGroup>
<None Remove="inputs\01.txt" />
<None Remove="inputs\01a.txt" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="inputs\01.txt" />
<EmbeddedResource Include="inputs\01a.txt" />
</ItemGroup>
</Project>

25
advent-of-code-2023.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "advent-of-code-2023", "advent-of-code-2023.csproj", "{1B54D933-507B-4F44-9BE3-F1794B593AF7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1B54D933-507B-4F44-9BE3-F1794B593AF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B54D933-507B-4F44-9BE3-F1794B593AF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B54D933-507B-4F44-9BE3-F1794B593AF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B54D933-507B-4F44-9BE3-F1794B593AF7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {17D280F0-AD9F-481E-8687-28AAB4A54437}
EndGlobalSection
EndGlobal

7
global.json Normal file
View File

@ -0,0 +1,7 @@
{
"sdk": {
"version": "8.0.0",
"rollForward": "latestMajor",
"allowPrerelease": true
}
}

2253
inputs/01.txt Normal file

File diff suppressed because it is too large Load Diff

14
inputs/01a.txt Normal file
View File

@ -0,0 +1,14 @@
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000

23
src/01.cs Normal file
View File

@ -0,0 +1,23 @@
namespace aoc2023;
internal class Day01 : Day
{
internal override void Parse()
{
var lines = Util.Parsing.ReadAllLines($"{GetDay()}");
}
internal override string Part1()
{
return $"<+white>";
}
internal override string Part2()
{
return $"<+white>";
}
}

65
src/Day.cs Normal file
View File

@ -0,0 +1,65 @@
namespace aoc2023;
internal abstract class Day : IDisposable
{
public void Dispose()
{
Logger.Log("");
}
internal void Go(bool runPart1, bool runPart2)
{
Logger.Log($"<reverse>{GetType().Name}<r>");
using (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 int GetDayNum()
{
if (int.TryParse(GetType().Name["Day".Length..], out int dayNum))
{
return dayNum;
}
return -1;
}
internal string GetDay() => $"{GetDayNum():00}";
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 aoc2023;
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 aoc2023;
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;
}
}

23
src/Template.cs Normal file
View File

@ -0,0 +1,23 @@
namespace aoc2023;
internal class DayTemplate : Day
{
internal override void Parse()
{
var lines = Util.Parsing.ReadAllLines($"{GetDay()}");
}
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 aoc2023;
internal class Timer : IDisposable
{
private readonly Stopwatch stopwatch = Stopwatch.StartNew();
private readonly string? name;
private bool stopped;
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>");
}
}

49
src/Util/Bisect.cs Normal file
View File

@ -0,0 +1,49 @@
namespace aoc2023.Util;
public static class Bisect
{
// Bisect takes a known-good low and known-bad high value as the bounds
// to bisect, and a function to test each value for success or failure.
// If the function succeeds, the value is adjusted toward the maximum,
// and if the function fails, the value is adjusted toward the minimum.
// The final value is returned when the difference between the success
// and the failure is less than or equal to the acceptance threshold
// (usually 1, for integers).
public static double Find(double low, double high, double threshold, Func<double, bool> tryFunc)
{
while (System.Math.Abs(high - low) > threshold)
{
var currVal = low + ((high - low) / 2);
var success = tryFunc(currVal);
if (success)
{
low = currVal;
}
else
{
high = currVal;
}
}
return low;
}
public static double Find(long low, long high, long threshold, Func<long, bool> tryFunc)
{
while (System.Math.Abs(high - low) > threshold)
{
var currVal = low + ((high - low) / 2);
var success = tryFunc(currVal);
if (success)
{
low = currVal;
}
else
{
high = currVal;
}
}
return low;
}
}

39
src/Util/Combinatorics.cs Normal file
View File

@ -0,0 +1,39 @@
namespace aoc2023.Util;
public static class Combinatorics
{
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IList<T> list)
{
Action<IList<T>, int>? helper = null;
List<IEnumerable<T>> res = new();
helper = (arr, n) =>
{
if (n == 1)
{
var tmp = new T[arr.Count];
arr.CopyTo(tmp, 0);
res.Add(tmp);
}
else
{
for (var i = 0; i < n; i++)
{
// ReSharper disable once AccessToModifiedClosure
helper!(arr, n - 1);
if (n % 2 == 1)
{
(arr[i], arr[n - 1]) = (arr[n - 1], arr[i]);
}
else
{
(arr[0], arr[n - 1]) = (arr[n - 1], arr[0]);
}
}
}
};
helper(list, list.Count);
return res;
}
}

65
src/Util/Math.cs Normal file
View File

@ -0,0 +1,65 @@
namespace aoc2023.Util;
public static class Math
{
public static ulong GCD(ulong a, ulong b)
{
while (true)
{
if (b == 0)
{
return a;
}
var a1 = a;
a = b;
b = a1 % b;
}
}
public static ulong LCM(params ulong[] nums)
{
var num = nums.Length;
switch (num)
{
case 0:
return 0;
case 1:
return nums[0];
}
var ret = lcm(nums[0], nums[1]);
for (var i = 2; i < num; i++)
{
ret = lcm(nums[i], ret);
}
return ret;
}
private static ulong lcm(ulong a, ulong b)
{
return (a * b) / GCD(a, b);
}
public static long Modulo(long numer, long denom)
{
// long q = numer / denom;
long r = numer % denom;
if (r < 0)
{
if (denom > 0)
{
// q = q - 1;
r = r + denom;
}
else
{
// q = q + 1;
r = r - denom;
}
}
return r;
}
}

91
src/Util/Parsing.cs Normal file
View File

@ -0,0 +1,91 @@
using System.Reflection;
using System.Text;
namespace aoc2023.Util;
public static class Parsing
{
private static readonly char[] StripPreamble = { (char)8745, (char)9559, (char)9488, };
private static readonly Encoding[] StripBOMsFromEncodings = { Encoding.UTF8, Encoding.Unicode, Encoding.BigEndianUnicode, };
private static void ReadData(string inputName, Action<string> processor)
{
if (Console.IsInputRedirected)
{
bool processedSomething = false;
for (int i = 0; Console.In.ReadLine() is { } line; i++)
{
if (i == 0)
{
if (line[0..StripPreamble.Length].SequenceEqual(StripPreamble))
{
line = line[StripPreamble.Length..];
}
else
{
foreach (var encoding in StripBOMsFromEncodings)
{
if (line.StartsWith(encoding.GetString(encoding.GetPreamble()), StringComparison.Ordinal))
{
line = line.Replace(encoding.GetString(encoding.GetPreamble()), "", StringComparison.Ordinal);
}
}
}
}
processor(line);
if (!string.IsNullOrEmpty(line))
{
processedSomething = true;
}
}
if (processedSomething)
{
return;
}
}
var filename = $"inputs/{inputName}.txt";
if (File.Exists(filename))
{
if (Directory.Exists(Path.GetDirectoryName(filename)!) && File.Exists(filename))
{
foreach (var line in File.ReadLines(filename))
{
processor(line);
}
return;
}
}
// typeof(Logger) is not technically correct since what we need is the "default namespace,"
// but "default namespace" is a Project File concept, not a C#/.NET concept, so it's not
// accessible at runtime. instead, we assume Logger is also part of the "default namespace"
var resourceName = $"{typeof(Logger).Namespace}.inputs.{inputName}.txt";
using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
using StreamReader reader = new(stream!);
while (reader.ReadLine() is { } readLine)
{
processor(readLine);
}
}
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);
}
}

35
src/Util/Testing.cs Normal file
View File

@ -0,0 +1,35 @@
using System.Diagnostics;
namespace aoc2023.Util;
public static class Testing
{
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>");
}
}
}
}

162
src/Util/Vec2.cs Normal file
View File

@ -0,0 +1,162 @@
namespace aoc2023.Util;
public readonly struct ivec2 : IEquatable<ivec2>, IComparable<ivec2>, IComparable
{
public readonly long x = 0;
public readonly long y = 0;
public static readonly ivec2 ZERO = new ivec2(0, 0);
public static readonly ivec2 ONE = new ivec2(1, 1);
public static readonly ivec2 LEFT = new ivec2(-1, 0);
public static readonly ivec2 RIGHT = new ivec2(1, 0);
public static readonly ivec2 UP = new ivec2(0, -1);
public static readonly ivec2 DOWN = new ivec2(0, 1);
public static readonly ivec2[] FOURWAY = {RIGHT, LEFT, UP, DOWN};
public static readonly ivec2[] EIGHTWAY = {UP, UP + RIGHT, RIGHT, RIGHT + DOWN, DOWN, DOWN + LEFT, LEFT, LEFT + UP};
public ivec2(long xv, long yv)
{
x = xv;
y = yv;
}
public bool IsZero() => x == 0 && y == 0;
public long Sum => x + y;
public long Product => x * y;
public long MaxElement => System.Math.Max(x, y);
public ivec2 GetRotatedLeft() => new ivec2(y, -x);
public ivec2 GetRotatedRight() => new ivec2(-y, x);
public long Dot(ivec2 v) => (x * v.x) + (y * v.y);
public long LengthSquared => (x * x) + (y * y);
public float Length => MathF.Sqrt(LengthSquared);
public long ManhattanDistance => Abs(this).Sum;
public long ManhattanDistanceTo(ivec2 other) => System.Math.Abs(x - other.x) + System.Math.Abs(y - other.y);
public ivec2 GetBestDirectionTo(ivec2 p)
{
ivec2 diff = p - this;
if (diff.IsZero())
{
return ZERO;
}
ivec2 dir = diff / Abs(diff).MaxElement;
return Sign(dir);
}
// get a point in the 8 cells around me closest to p
public ivec2 GetNearestNeighbor(ivec2 p)
{
ivec2 dir = GetBestDirectionTo(p);
return this + dir;
}
public IEnumerable<ivec2> GetOrthogonalNeighbors()
{
foreach (var dir in FOURWAY)
{
yield return this + dir;
}
}
public IEnumerable<ivec2> GetNeighbors()
{
foreach (var dir in EIGHTWAY)
{
yield return this + dir;
}
}
public long this[long i] => (i == 0) ? x : y;
public static ivec2 operator +(ivec2 v) => v;
public static ivec2 operator -(ivec2 v) => new ivec2(-v.x, -v.y);
public static ivec2 operator +(ivec2 a, ivec2 b) => new ivec2(a.x + b.x, a.y + b.y);
public static ivec2 operator -(ivec2 a, ivec2 b) => new ivec2(a.x - b.x, a.y - b.y);
public static ivec2 operator *(ivec2 a, ivec2 b) => new ivec2(a.x * b.x, a.y * b.y);
public static ivec2 operator *(long a, ivec2 v) => new ivec2(a * v.x, a * v.y);
public static ivec2 operator *(ivec2 v, long a) => new ivec2(a * v.x, a * v.y);
public static ivec2 operator /(ivec2 v, long a) => new ivec2(v.x / a, v.y / a);
public static bool operator ==(ivec2 a, ivec2 b) => (a.x == b.x) && (a.y == b.y);
public static bool operator !=(ivec2 a, ivec2 b) => (a.x != b.x) || (a.y != b.y);
public static bool operator <(ivec2 a, ivec2 b) => (a.x < b.x) && (a.y < b.y);
public static bool operator <=(ivec2 a, ivec2 b) => (a.x <= b.x) && (a.y <= b.y);
public static bool operator >(ivec2 a, ivec2 b) => (a.x > b.x) && (a.y > b.y);
public static bool operator >=(ivec2 a, ivec2 b) => (a.x >= b.x) && (a.y >= b.y);
public bool Equals(ivec2 other)
{
return x == other.x && y == other.y;
}
public override bool Equals(object? obj)
{
return obj is ivec2 other && Equals(other);
}
public int CompareTo(ivec2 other)
{
if (this < other)
{
return -1;
}
if (this > other)
{
return 1;
}
return 0;
}
public int CompareTo(object? obj)
{
if (ReferenceEquals(null, obj)) return 1;
return obj is ivec2 other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(ivec2)}");
}
public static ivec2 Sign(ivec2 v) => new ivec2(System.Math.Sign(v.x), System.Math.Sign(v.y));
public static ivec2 Min(ivec2 a, ivec2 b) => new ivec2(System.Math.Min(a.x, b.x), System.Math.Min(a.y, b.y));
public static ivec2 Max(ivec2 a, ivec2 b) => new ivec2(System.Math.Max(a.x, b.x), System.Math.Max(a.y, b.y));
public static ivec2 Clamp(ivec2 v, ivec2 lh, ivec2 rh) => Min(rh, Max(lh, v));
public static ivec2 Abs(ivec2 v) => new ivec2(System.Math.Abs(v.x), System.Math.Abs(v.y));
public static ivec2 Mod(ivec2 val, long den)
{
long x = val.x % den;
long y = val.y % den;
if (x < 0)
{
x += den;
}
if (y < 0)
{
y += den;
}
return new ivec2(x, y);
}
public static long Dot(ivec2 a, ivec2 b) => (a.x * b.x) + (a.y * b.y);
public static ivec2 Parse(string s)
{
string[] parts = s.Split(',', 2);
long x = long.Parse(parts[0]);
long y = long.Parse(parts[1]);
return new ivec2(x, y);
}
public override int GetHashCode() => HashCode.Combine(x, y);
public override string ToString() => $"{x},{y}";
}

171
src/Util/Vec3.cs Normal file
View File

@ -0,0 +1,171 @@
namespace aoc2023.Util;
public readonly struct ivec3 : IEquatable<ivec3>, IComparable<ivec3>, IComparable
{
public readonly long x = 0;
public readonly long y = 0;
public readonly long z = 0;
public static readonly ivec3 ZERO = new ivec3(0, 0, 0);
public static readonly ivec3 ONE = new ivec3(1, 1, 1);
public static readonly ivec3 LEFT = new ivec3(-1, 0, 0);
public static readonly ivec3 RIGHT = new ivec3(1, 0, 0);
public static readonly ivec3 UP = new ivec3(0, -1, 0);
public static readonly ivec3 DOWN = new ivec3(0, 1, 0);
public static readonly ivec3 FORWARD = new ivec3(0, 0, 1);
public static readonly ivec3 BACKWARD = new ivec3(0, 0, -1);
public static readonly ivec3[] DIRECTIONS = {LEFT, RIGHT, UP, DOWN, FORWARD, BACKWARD};
public ivec3(long xv, long yv, long zv)
{
x = xv;
y = yv;
z = zv;
}
public bool IsZero() => x == 0 && y == 0 && z == 0;
public long Sum => x + y + z;
public long Product => x * y * z;
public long MaxElement => System.Math.Max(x, System.Math.Max(y, z));
public long MinElement => System.Math.Min(x, System.Math.Min(y, z));
public ivec3 GetRotatedLeft() => new ivec3(y, -x, z);
public ivec3 GetRotatedRight() => new ivec3(-y, x, z);
public long Dot(ivec3 v) => (x * v.x) + (y * v.y) + (z * v.z);
public long LengthSquared => (x * x) + (y * y) + (z * z);
public float Length => MathF.Sqrt(LengthSquared);
public long ManhattanDistance => Abs(this).Sum;
public long ManhattanDistanceTo(ivec3 other) => System.Math.Abs(x - other.x) + System.Math.Abs(y - other.y) + System.Math.Abs(z - other.z);
public bool IsTouching(ivec3 other) => ManhattanDistanceTo(other) == 1;
public IEnumerable<ivec3> GetNeighbors(ivec3? min = null, ivec3? max = null)
{
foreach (var d in DIRECTIONS)
{
var n = this + d;
if ((min == null || n >= min) && (max == null || n <= max))
{
yield return n;
}
}
}
public ivec3 GetBestDirectionTo(ivec3 p)
{
ivec3 diff = p - this;
if (diff.IsZero())
{
return ZERO;
}
ivec3 dir = diff / Abs(diff).MaxElement;
return Sign(dir);
}
// get a point in the 8 cells around me closest to p
public ivec3 GetNearestNeighbor(ivec3 p)
{
ivec3 dir = GetBestDirectionTo(p);
return this + dir;
}
public long this[long i] => (i == 0) ? x : (i == 1) ? y : z;
public static ivec3 operator +(ivec3 v) => v;
public static ivec3 operator -(ivec3 v) => new ivec3(-v.x, -v.y, -v.z);
public static ivec3 operator +(ivec3 a, ivec3 b) => new ivec3(a.x + b.x, a.y + b.y, a.z + b.z);
public static ivec3 operator -(ivec3 a, ivec3 b) => new ivec3(a.x - b.x, a.y - b.y, a.z - b.z);
public static ivec3 operator *(ivec3 a, ivec3 b) => new ivec3(a.x * b.x, a.y * b.y, a.z * b.z);
public static ivec3 operator *(long a, ivec3 v) => new ivec3(a * v.x, a * v.y, a * v.z);
public static ivec3 operator *(ivec3 v, long a) => new ivec3(a * v.x, a * v.y, a * v.z);
public static ivec3 operator /(ivec3 v, long a) => new ivec3(v.x / a, v.y / a, v.z / a);
public static bool operator ==(ivec3 a, ivec3 b) => (a.x == b.x) && (a.y == b.y) && (a.z == b.z);
public static bool operator !=(ivec3 a, ivec3 b) => (a.x != b.x) || (a.y != b.y) || (a.z != b.z);
public static bool operator <(ivec3 a, ivec3 b) => (a.x < b.x) && (a.y < b.y) && (a.z < b.z);
public static bool operator <=(ivec3 a, ivec3 b) => (a.x <= b.x) && (a.y <= b.y) && (a.z <= b.z);
public static bool operator >(ivec3 a, ivec3 b) => (a.x > b.x) && (a.y > b.y) && (a.z > b.z);
public static bool operator >=(ivec3 a, ivec3 b) => (a.x >= b.x) && (a.y >= b.y) && (a.z >= b.z);
public bool Equals(ivec3 other)
{
return x == other.x && y == other.y && z == other.z;
}
public override bool Equals(object? obj)
{
return obj is ivec3 other && Equals(other);
}
public int CompareTo(ivec3 other)
{
if (this < other)
{
return -1;
}
if (this > other)
{
return 1;
}
return 0;
}
public int CompareTo(object? obj)
{
if (ReferenceEquals(null, obj)) return 1;
return obj is ivec3 other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(ivec3)}");
}
public static ivec3 Sign(ivec3 v) => new ivec3(System.Math.Sign(v.x), System.Math.Sign(v.y), System.Math.Sign(v.z));
public static ivec3 Min(ivec3 a, ivec3 b) => new ivec3(System.Math.Min(a.x, b.x), System.Math.Min(a.y, b.y), System.Math.Min(a.z, b.z));
public static ivec3 Max(ivec3 a, ivec3 b) => new ivec3(System.Math.Max(a.x, b.x), System.Math.Max(a.y, b.y), System.Math.Max(a.z, b.z));
public static ivec3 Clamp(ivec3 v, ivec3 lh, ivec3 rh) => Min(rh, Max(lh, v));
public static ivec3 Abs(ivec3 v) => new ivec3(System.Math.Abs(v.x), System.Math.Abs(v.y), System.Math.Abs(v.z));
public static ivec3 Mod(ivec3 val, long den)
{
long x = val.x % den;
long y = val.y % den;
long z = val.z % den;
if (x < 0)
{
x += den;
}
if (y < 0)
{
y += den;
}
if (z < 0)
{
z += den;
}
return new ivec3(x, y, z);
}
public static long Dot(ivec3 a, ivec3 b) => (a.x * b.x) + (a.y * b.y) + (a.z * b.z);
public static ivec3 Parse(string s)
{
string[] parts = s.Split(',', 3);
long x = long.Parse(parts[0]);
long y = long.Parse(parts[1]);
long z = long.Parse(parts[2]);
return new ivec3(x, y, z);
}
public override int GetHashCode() => HashCode.Combine(x, y, z);
public override string ToString() => $"{x},{y},{z}";
}

78
src/main.cs Normal file
View File

@ -0,0 +1,78 @@
using aoc2023;
using aoc2023.Timer t = new("Full program");
var types = System.Reflection.Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(ty => ty.IsSubclassOf(typeof(Day)) && !ty.IsAbstract && ty.Name != "DayTemplate")
.OrderBy(ty => ty.Name).ToList();
bool runAll = false;
bool? runPart1 = null;
bool? runPart2 = null;
List<string> desiredDays = new();
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
{
desiredDays.Add(arg);
}
}
if (runPart1 != null || runPart2 != null)
{
runPart1 ??= false;
runPart2 ??= false;
}
if (runAll)
{
foreach (var type in types)
{
using var day = (Day)Activator.CreateInstance(type)!;
day.Go(runPart1 ?? true, runPart2 ?? true);
}
}
else
{
if (desiredDays.Count == 0)
{
desiredDays.Add("");
}
foreach (var desiredDay in desiredDays)
{
Day? day = null;
if (string.IsNullOrEmpty(desiredDay))
{
day = (Day) Activator.CreateInstance(types.Last())!;
}
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();
}
}