mirror of
https://github.com/parnic/advent-of-code-2024.git
synced 2025-06-16 04:20:15 -05:00
Day 1
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/target
|
||||
/.vs/
|
||||
*.user
|
||||
/bin/
|
||||
/obj/
|
||||
*.exe
|
||||
/inputs/
|
10
.idea/.idea.advent-of-code-2024/.idea/.gitignore
generated
vendored
Normal file
10
.idea/.idea.advent-of-code-2024/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Rider ignored files
|
||||
/contentModel.xml
|
||||
/.idea.advent-of-code-2024.iml
|
||||
/modules.xml
|
||||
/projectSettingsUpdater.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
1
.idea/.idea.advent-of-code-2024/.idea/.name
generated
Normal file
1
.idea/.idea.advent-of-code-2024/.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
||||
advent-of-code-2024
|
4
.idea/.idea.advent-of-code-2024/.idea/encodings.xml
generated
Normal file
4
.idea/.idea.advent-of-code-2024/.idea/encodings.xml
generated
Normal 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>
|
8
.idea/.idea.advent-of-code-2024/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.advent-of-code-2024/.idea/indexLayout.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
6
.idea/.idea.advent-of-code-2024/.idea/vcs.xml
generated
Normal file
6
.idea/.idea.advent-of-code-2024/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
8
GlobalSuppressions.cs
Normal file
8
GlobalSuppressions.cs
Normal 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:aoc2024.DayTemplate.lines")]
|
32
Properties/launchSettings.json
Normal file
32
Properties/launchSettings.json
Normal 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
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Advent of Code 2024
|
||||
|
||||
My solutions to [Advent of Code 2024](https://adventofcode.com/2024).
|
40
advent-of-code-2024.csproj
Normal file
40
advent-of-code-2024.csproj
Normal file
@ -0,0 +1,40 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<RootNamespace>aoc2024</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>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Z3" Version="4.12.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
25
advent-of-code-2024.sln
Normal file
25
advent-of-code-2024.sln
Normal 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-2024", "advent-of-code-2024.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
7
global.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "9.0.0",
|
||||
"rollForward": "latestMajor",
|
||||
"allowPrerelease": true
|
||||
}
|
||||
}
|
31
src/01.cs
Normal file
31
src/01.cs
Normal file
@ -0,0 +1,31 @@
|
||||
namespace aoc2024;
|
||||
|
||||
internal class Day01 : Day
|
||||
{
|
||||
private readonly List<long> list1 = [];
|
||||
private readonly List<long> list2 = [];
|
||||
internal override void Parse()
|
||||
{
|
||||
var lines = Util.Parsing.ReadAllLines($"{GetDay()}");
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var vals = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
list1.Add(long.Parse(vals[0]));
|
||||
list2.Add(long.Parse(vals[1]));
|
||||
}
|
||||
}
|
||||
|
||||
internal override string Part1()
|
||||
{
|
||||
list1.Sort();
|
||||
list2.Sort();
|
||||
long totalDist = list1.Select((num1, idx) => Math.Abs(num1 - list2[idx])).Sum();
|
||||
return $"Total distance between lists: <+white>{totalDist}";
|
||||
}
|
||||
|
||||
internal override string Part2()
|
||||
{
|
||||
long score = list1.Aggregate(0L, (accum, num1) => accum + num1 * list2.Count(num2 => num2 == num1));
|
||||
return $"Lists similarity score: <+white>{score}";
|
||||
}
|
||||
}
|
65
src/Day.cs
Normal file
65
src/Day.cs
Normal file
@ -0,0 +1,65 @@
|
||||
namespace aoc2024;
|
||||
|
||||
internal abstract class Day : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
Logger.LogLine("");
|
||||
}
|
||||
|
||||
internal void Go(bool runPart1, bool runPart2)
|
||||
{
|
||||
Logger.LogLine($"<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.LogLine($"<+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.LogLine($"<+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
41
src/Extensions.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace aoc2024;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
78
src/Logger.cs
Normal file
78
src/Logger.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace aoc2024;
|
||||
|
||||
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 LogLine(string msg)
|
||||
{
|
||||
Console.WriteLine(InsertColorCodes(msg));
|
||||
Debug.WriteLine(StripColorCodes(msg));
|
||||
}
|
||||
|
||||
public static void Log(string msg)
|
||||
{
|
||||
Console.Write(InsertColorCodes(msg));
|
||||
Debug.Write(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
23
src/Template.cs
Normal file
@ -0,0 +1,23 @@
|
||||
namespace aoc2024;
|
||||
|
||||
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
48
src/Timer.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace aoc2024;
|
||||
|
||||
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.LogLine($"<cyan>{name}{(!string.IsNullOrEmpty(name) ? " t" : "T")}ook {color}{elapsed:N1}{unit}<r>");
|
||||
}
|
||||
}
|
49
src/Util/Bisect.cs
Normal file
49
src/Util/Bisect.cs
Normal file
@ -0,0 +1,49 @@
|
||||
namespace aoc2024.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
39
src/Util/Combinatorics.cs
Normal file
@ -0,0 +1,39 @@
|
||||
namespace aoc2024.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;
|
||||
}
|
||||
}
|
22
src/Util/Constants.cs
Normal file
22
src/Util/Constants.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace aoc2024.Util;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public const char SolidBlock = '█';
|
||||
public const char SolidSquare = '■';
|
||||
public const char SolidSmallSquare = '▪';
|
||||
|
||||
public const char BoxVert = '│';
|
||||
public const char BoxHorz = '─';
|
||||
public const char BoxCurveNE = '╰';
|
||||
public const char BoxCurveNW = '╯';
|
||||
public const char BoxCurveSW = '╮';
|
||||
public const char BoxCurveSE = '╭';
|
||||
|
||||
public const char BoxDoubleVert = '║';
|
||||
public const char BoxDoubleHorz = '═';
|
||||
public const char BoxDoubleNE = '╚';
|
||||
public const char BoxDoubleNW = '╝';
|
||||
public const char BoxDoubleSW = '╗';
|
||||
public const char BoxDoubleSE = '╔';
|
||||
}
|
49
src/Util/Extensions.cs
Normal file
49
src/Util/Extensions.cs
Normal file
@ -0,0 +1,49 @@
|
||||
namespace aoc2024.Util;
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public static bool IsDigit(this char c) => c >= '0' && c <= '9';
|
||||
|
||||
public static void AddUnique<T>(this ICollection<T> list, T elem)
|
||||
{
|
||||
if (!list.Contains(elem))
|
||||
{
|
||||
list.Add(elem);
|
||||
}
|
||||
}
|
||||
|
||||
public static int IndexOf<T>(this ICollection<T> list, T elem) where T : IEquatable<T>
|
||||
{
|
||||
for (int idx = 0; idx < list.Count; idx++)
|
||||
{
|
||||
if (list.ElementAt(idx).Equals(elem))
|
||||
{
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static string ReplaceFirst(this string str, char ch, char replace)
|
||||
{
|
||||
for (int i = 0; i < str.Length; i++)
|
||||
{
|
||||
if (str[i] == ch)
|
||||
{
|
||||
return str.ReplaceAt(i, replace);
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public static string ReplaceAt(this string str, int index, char replace)
|
||||
{
|
||||
return str[..index] + replace + str[(index + 1)..];
|
||||
}
|
||||
|
||||
public static bool SequenceEquals<T>(this T[,] a, T[,] b) => a.Rank == b.Rank
|
||||
&& Enumerable.Range(0, a.Rank).All(d=> a.GetLength(d) == b.GetLength(d))
|
||||
&& a.Cast<T>().SequenceEqual(b.Cast<T>());
|
||||
}
|
96
src/Util/Math.cs
Normal file
96
src/Util/Math.cs
Normal file
@ -0,0 +1,96 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace aoc2024.Util;
|
||||
|
||||
public static class Math
|
||||
{
|
||||
public static T GCD<T>(T a, T b) where T : IBinaryInteger<T>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (b == T.Zero)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
var a1 = a;
|
||||
a = b;
|
||||
b = a1 % b;
|
||||
}
|
||||
}
|
||||
|
||||
public static T LCM<T>(params T[] nums) where T : IBinaryInteger<T>
|
||||
{
|
||||
var num = nums.Length;
|
||||
switch (num)
|
||||
{
|
||||
case 0:
|
||||
return T.Zero;
|
||||
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 T lcm<T>(T a, T b) where T : IBinaryInteger<T>
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// taken from https://rosettacode.org/wiki/Chinese_remainder_theorem#C.23
|
||||
public static T CRT<T>(T[] n, T[] a) where T : IBinaryInteger<T>
|
||||
{
|
||||
T prod = n.Aggregate(T.One, (i, j) => i * j);
|
||||
T p;
|
||||
T sm = T.Zero;
|
||||
for (int i = 0; i < n.Length; i++)
|
||||
{
|
||||
p = prod / n[i];
|
||||
sm += a[i] * ModularMultiplicativeInverse(p, n[i]) * p;
|
||||
}
|
||||
|
||||
return sm % prod;
|
||||
}
|
||||
|
||||
public static T ModularMultiplicativeInverse<T>(T a, T mod) where T : IBinaryInteger<T>
|
||||
{
|
||||
T b = a % mod;
|
||||
for (T x = T.One; x < mod; x++)
|
||||
{
|
||||
if ((b * x) % mod == T.One)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
return T.One;
|
||||
}
|
||||
}
|
91
src/Util/Parsing.cs
Normal file
91
src/Util/Parsing.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace aoc2024.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)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
ReadData(filename, (line) => sb.AppendLine(line));
|
||||
return sb.ToString().Trim();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
76
src/Util/Pathfinding/Dijkstra.cs
Normal file
76
src/Util/Pathfinding/Dijkstra.cs
Normal file
@ -0,0 +1,76 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace aoc2024.Util.Pathfinding;
|
||||
|
||||
public class Dijkstra
|
||||
{
|
||||
/*
|
||||
* 1 function Dijkstra(Graph, source):
|
||||
2 dist[source] ← 0 // Initialization
|
||||
3
|
||||
4 create vertex priority queue Q
|
||||
5
|
||||
6 for each vertex v in Graph.Vertices:
|
||||
7 if v ≠ source
|
||||
8 dist[v] ← INFINITY // Unknown distance from source to v
|
||||
9 prev[v] ← UNDEFINED // Predecessor of v
|
||||
10
|
||||
11 Q.add_with_priority(v, dist[v])
|
||||
12
|
||||
13
|
||||
14 while Q is not empty: // The main loop
|
||||
15 u ← Q.extract_min() // Remove and return best vertex
|
||||
16 for each neighbor v of u: // Go through all v neighbors of u
|
||||
17 alt ← dist[u] + Graph.Edges(u, v)
|
||||
18 if alt < dist[v]:
|
||||
19 dist[v] ← alt
|
||||
20 prev[v] ← u
|
||||
21 Q.decrease_priority(v, alt)
|
||||
22
|
||||
23 return dist, prev
|
||||
*/
|
||||
public static (long[,], Dictionary<ivec2, ivec2>) Solve(long[,] graph, ivec2 start)
|
||||
{
|
||||
long width = graph.GetLength(0);
|
||||
long height = graph.GetLength(1);
|
||||
long[,] dist = new long[width, height];
|
||||
Dictionary<ivec2, ivec2> prev = new();
|
||||
PriorityQueue<ivec2, long> pq = new();
|
||||
for (long x = 0; x < width; x++)
|
||||
{
|
||||
for (long y = 0; y < height; y++)
|
||||
{
|
||||
ivec2 pt = new(x, y);
|
||||
if (start.x == x && start.y == y)
|
||||
{
|
||||
dist[x, y] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dist[x, y] = long.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (pq.Count > 0)
|
||||
{
|
||||
var u = pq.Dequeue();
|
||||
foreach (var neighbor in u.GetBoundedOrthogonalNeighbors(0, 0, (int)width - 1, (int)height - 1))
|
||||
{
|
||||
if (!prev.ContainsKey(neighbor))
|
||||
{
|
||||
pq.Enqueue(neighbor, dist[neighbor.x, neighbor.y]);
|
||||
}
|
||||
|
||||
long alt = dist[u.x, u.y] + graph[neighbor.x, neighbor.y];
|
||||
if (alt < dist[neighbor.x, neighbor.y])
|
||||
{
|
||||
dist[neighbor.x, neighbor.y] = alt;
|
||||
prev.TryAdd(neighbor, u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (dist, prev);
|
||||
}
|
||||
}
|
35
src/Util/Testing.cs
Normal file
35
src/Util/Testing.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace aoc2024.Util;
|
||||
|
||||
public static class Testing
|
||||
{
|
||||
internal static void StartTestSet(string name)
|
||||
{
|
||||
Logger.LogLine($"<underline>test: {name}<r>");
|
||||
}
|
||||
|
||||
internal static void StartTest(string label)
|
||||
{
|
||||
Logger.LogLine($"<magenta>{label}<r>");
|
||||
}
|
||||
|
||||
internal static void TestCondition(Func<bool> a, bool printResult = true)
|
||||
{
|
||||
if (a.Invoke() == false)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
if (printResult)
|
||||
{
|
||||
Logger.LogLine("<red>x<r>");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (printResult)
|
||||
{
|
||||
Logger.LogLine("<green>✓<r>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
192
src/Util/Vec2.cs
Normal file
192
src/Util/Vec2.cs
Normal file
@ -0,0 +1,192 @@
|
||||
namespace aoc2024.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> GetBoundedOrthogonalNeighbors(int minX, int minY, int maxX, int maxY)
|
||||
{
|
||||
foreach (var dir in FOURWAY)
|
||||
{
|
||||
var pt = this + dir;
|
||||
if (!pt.IsWithinRange(minX, minY, maxX, maxY))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return pt;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ivec2> GetNeighbors()
|
||||
{
|
||||
foreach (var dir in EIGHTWAY)
|
||||
{
|
||||
yield return this + dir;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ivec2> GetBoundedNeighbors(int minX, int minY, int maxX, int maxY)
|
||||
{
|
||||
foreach (var dir in EIGHTWAY)
|
||||
{
|
||||
var pt = this + dir;
|
||||
if (!pt.IsWithinRange(minX, minY, maxX, maxY))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return pt;
|
||||
}
|
||||
}
|
||||
|
||||
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 IsWithinRange(int minX, int minY, int maxX, int maxY) => x >= minX && y >= minY && x <= maxX && y <= maxY;
|
||||
|
||||
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}";
|
||||
}
|
153
src/Util/Vec3.cs
Normal file
153
src/Util/Vec3.cs
Normal file
@ -0,0 +1,153 @@
|
||||
namespace aoc2024.Util;
|
||||
|
||||
public record struct ivec3(long x, long y, long z) : IComparable<ivec3>, IComparable
|
||||
{
|
||||
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 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, ivec3 max)
|
||||
{
|
||||
foreach (var d in DIRECTIONS)
|
||||
{
|
||||
var n = this + d;
|
||||
if (n >= min && 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 bool Equals(ivec3 other)
|
||||
{
|
||||
return x == other.x && y == other.y && z == other.z;
|
||||
}
|
||||
|
||||
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
78
src/main.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using aoc2024;
|
||||
|
||||
using aoc2024.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.LogLine($"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