diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..2f99098
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,52 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "aoc-2024-01 Debug",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "dotnet: build aoc-2024-01",
+ "program": "${workspaceFolder}/artifacts/bin/aoc-2024-01/debug/aoc-2024-01.dll",
+ "args": ["part-one", "data/2024/01/input.txt"],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "aoc-2024-02 Debug",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "dotnet: build aoc-2024-02",
+ "program": "${workspaceFolder}/artifacts/bin/aoc-2024-02/debug/aoc-2024-02.dll",
+ "args": ["part-two", "data/2024/02/dev.txt"],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "aoc-2024-03 Debug",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "dotnet: build aoc-2024-03",
+ "program": "${workspaceFolder}/artifacts/bin/aoc-2024-03/debug/aoc-2024-03.dll",
+ "args": ["part-two", "data/2024/03/dev2.txt"],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": "aoc-2024-04 Debug",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "dotnet: build aoc-2024-04",
+ "program": "${workspaceFolder}/artifacts/bin/aoc-2024-04/debug/aoc-2024-04.dll",
+ "args": ["part-one", "data/2024/04/dev.txt"],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..ff6ee0c
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,39 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "type": "dotnet",
+ "task": "build aoc-2024-01.csproj",
+ "file": "src/2024/01/aoc-2024-01.csproj",
+ "group": "build",
+ "problemMatcher": [],
+ "label": "dotnet: build aoc-2024-01"
+ },
+ {
+ "type": "dotnet",
+ "task": "build aoc-2024-02.csproj",
+ "file": "src/2024/02/aoc-2024-02.csproj",
+ "group": "build",
+ "problemMatcher": [],
+ "label": "dotnet: build aoc-2024-02"
+ },
+ {
+ "type": "dotnet",
+ "task": "build aoc-2024-03.csproj",
+ "file": "src/2024/03/aoc-2024-03.csproj",
+ "group": "build",
+ "problemMatcher": [],
+ "label": "dotnet: build aoc-2024-03"
+ },
+ {
+ "type": "dotnet",
+ "task": "build aoc-2024-04.csproj",
+ "file": "src/2024/04/aoc-2024-04.csproj",
+ "group": "build",
+ "problemMatcher": [],
+ "label": "dotnet: build aoc-2024-04"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/AdventOfCode.sln b/AdventOfCode.sln
index f4f2125..674fbdd 100644
--- a/AdventOfCode.sln
+++ b/AdventOfCode.sln
@@ -1,7 +1,55 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2024", "2024", "{37CA8017-085A-4F6A-BADB-535F929C10B9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aoc-2024-01", "src\2024\01\aoc-2024-01.csproj", "{FAF70800-E2C3-4AD7-B433-86C1F20380F7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aoc-2024-02", "src\2024\02\aoc-2024-02.csproj", "{8B563C17-E24D-46F0-A98D-1533DC183C6B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aoc-2024-03", "src\2024\03\aoc-2024-03.csproj", "{D6DAA79C-6B82-46E7-90CD-73D0A3196B8A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aoc-2024-04", "src\2024\04\aoc-2024-04.csproj", "{49021FCE-1CA9-430F-99B2-8E5C14A48394}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aoc-2024-05", "src\2024\05\aoc-2024-05.csproj", "{D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}"
+EndProject
Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FAF70800-E2C3-4AD7-B433-86C1F20380F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FAF70800-E2C3-4AD7-B433-86C1F20380F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FAF70800-E2C3-4AD7-B433-86C1F20380F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FAF70800-E2C3-4AD7-B433-86C1F20380F7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8B563C17-E24D-46F0-A98D-1533DC183C6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8B563C17-E24D-46F0-A98D-1533DC183C6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8B563C17-E24D-46F0-A98D-1533DC183C6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8B563C17-E24D-46F0-A98D-1533DC183C6B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D6DAA79C-6B82-46E7-90CD-73D0A3196B8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6DAA79C-6B82-46E7-90CD-73D0A3196B8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6DAA79C-6B82-46E7-90CD-73D0A3196B8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6DAA79C-6B82-46E7-90CD-73D0A3196B8A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {49021FCE-1CA9-430F-99B2-8E5C14A48394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {49021FCE-1CA9-430F-99B2-8E5C14A48394}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {49021FCE-1CA9-430F-99B2-8E5C14A48394}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {49021FCE-1CA9-430F-99B2-8E5C14A48394}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D108A6AB-B974-4BD9-B97A-CB1D1A2F3920}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {FAF70800-E2C3-4AD7-B433-86C1F20380F7} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
+ {8B563C17-E24D-46F0-A98D-1533DC183C6B} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
+ {D6DAA79C-6B82-46E7-90CD-73D0A3196B8A} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
+ {49021FCE-1CA9-430F-99B2-8E5C14A48394} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
+ {D108A6AB-B974-4BD9-B97A-CB1D1A2F3920} = {37CA8017-085A-4F6A-BADB-535F929C10B9}
+ EndGlobalSection
EndGlobal
diff --git a/Directory.Build.props b/Directory.Build.props
index 2047dee..ff785ed 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,7 +1,7 @@
- true
+ $(MSBuildThisFileDirectory)artifacts
\ No newline at end of file
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 3e3b132..a81f256 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -7,6 +7,10 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
\ No newline at end of file
diff --git a/src/2024/01/Program.cs b/src/2024/01/Program.cs
new file mode 100644
index 0000000..d9bbb7e
--- /dev/null
+++ b/src/2024/01/Program.cs
@@ -0,0 +1,76 @@
+using System.ComponentModel.DataAnnotations;
+using System.Runtime.InteropServices;
+
+using ConsoleAppFramework;
+
+var builder = ConsoleApp.Create();
+builder.Add("part-one", static ([Argument, FileExists] string file) =>
+{
+ List left = [];
+ List right = [];
+
+ int entries = ReadFile(file, (l, r) =>
+ {
+ left.Add(l);
+ right.Add(r);
+ });
+
+ left.Sort();
+ right.Sort();
+ int rolling = 0;
+ for (int i = 0; i < entries; i++)
+ {
+ rolling += Math.Abs(left[i] - right[i]);
+ }
+
+ Console.WriteLine($"Total distance: {rolling}");
+});
+builder.Add("part-two", static ([Argument, FileExists] string file) =>
+{
+ List left = [];
+ Dictionary right = [];
+
+ int entries = ReadFile(file, (l, r) =>
+ {
+ left.Add(l);
+ CollectionsMarshal.GetValueRefOrAddDefault(right, r, out _)++;
+ });
+
+ int similarity = 0;
+ for (int i = 0; i < entries; i++)
+ {
+ if (right.TryGetValue(left[i], out var count))
+ {
+ similarity += left[i] * count;
+ }
+ }
+
+ Console.WriteLine($"Similarity: {similarity}");
+});
+builder.Run(args);
+
+static int ReadFile(string file, Action handler)
+{
+ int entries = 0;
+ using var fileReader = new StreamReader(file);
+ Span parse = stackalloc Range[3];
+ while (fileReader.ReadLine() is { } line)
+ {
+ if (line.AsSpan().Split(parse, ' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) != 2)
+ {
+ Console.WriteLine($"Skipping Line {line}");
+ continue;
+ }
+
+ entries += 1;
+ handler(
+ int.Parse(line.AsSpan(parse[0])),
+ int.Parse(line.AsSpan(parse[1])));
+ }
+ return entries;
+}
+
+class FileExistsAttribute : ValidationAttribute
+{
+ public override bool IsValid(object? value) => value is string path && File.Exists(path);
+}
diff --git a/src/2024/01/aoc-2024-01.csproj b/src/2024/01/aoc-2024-01.csproj
new file mode 100644
index 0000000..8aeb71a
--- /dev/null
+++ b/src/2024/01/aoc-2024-01.csproj
@@ -0,0 +1,11 @@
+
+
+
+ Exe
+ net8.0
+ aoc_2024_01
+ enable
+ enable
+
+
+
diff --git a/src/2024/02/Program.cs b/src/2024/02/Program.cs
new file mode 100644
index 0000000..1f7abf5
--- /dev/null
+++ b/src/2024/02/Program.cs
@@ -0,0 +1,94 @@
+using System.Buffers;
+using System.ComponentModel.DataAnnotations;
+
+using ConsoleAppFramework;
+
+var builder = ConsoleApp.Create();
+builder.Add("part-one", static ([Argument, FileExists] string file) =>
+{
+ Console.WriteLine($"Safe Routes: {ReadFile(file, false)}");
+});
+builder.Add("part-two", static ([Argument, FileExists] string file) =>
+{
+ Console.WriteLine($"Safe Routes: {ReadFile(file, true)}");
+});
+builder.Run(args);
+
+static int ReadFile(string file, bool dampening)
+{
+ int result = 0;
+ using var fileReader = new StreamReader(file);
+ ArrayBufferWriter distances = new(8);
+ while (fileReader.ReadLine() is { } line)
+ {
+ distances.ResetWrittenCount();
+ int? previous = null;
+ foreach (var valueRange in line.AsSpan().Split(' '))
+ {
+ var sep = line.AsSpan(valueRange);
+ if (sep.IsWhiteSpace())
+ {
+ continue;
+ }
+
+ var tmp = previous;
+ previous = int.Parse(sep);
+ if (tmp is not null)
+ {
+ distances.GetSpan(1)[0] = previous.Value - tmp.Value;
+ distances.Advance(1);
+ }
+ }
+
+ if (Check(distances.WrittenSpan))
+ {
+ result++;
+ }
+ else if (dampening)
+ {
+ // This is ugly.
+ // Should've used Vertex/Edge-data structure.
+ if (Check(distances.WrittenSpan[1..]) || Check(distances.WrittenSpan[..^1]))
+ {
+ Console.WriteLine($"Dropped one front/end for {line}");
+ result++;
+ continue;
+ }
+
+ for (int i = 0; i < distances.WrittenCount - 1; i++)
+ {
+ Span merge = [.. distances.WrittenSpan[(i + 1)..]];
+ merge[0] += distances.WrittenSpan[i];
+ int[] check = [.. distances.WrittenSpan[..i], .. merge];
+ if (Check(check))
+ {
+ result++;
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+
+ static bool Check(in ReadOnlySpan distances)
+ {
+ int sign = 0;
+ foreach (ref readonly var distance in distances)
+ {
+ if (Math.Abs(distance) is < 1 or > 3)
+ {
+ return false;
+ }
+
+ sign += Math.Sign(distance);
+ }
+
+ return Math.Abs(sign) == distances.Length;
+ }
+}
+
+class FileExistsAttribute : ValidationAttribute
+{
+ public override bool IsValid(object? value) => value is string path && File.Exists(path);
+}
diff --git a/src/2024/02/aoc-2024-02.csproj b/src/2024/02/aoc-2024-02.csproj
new file mode 100644
index 0000000..6d1cba6
--- /dev/null
+++ b/src/2024/02/aoc-2024-02.csproj
@@ -0,0 +1,11 @@
+
+
+
+ Exe
+ net8.0
+ aoc_2024_02
+ enable
+ enable
+
+
+
\ No newline at end of file
diff --git a/src/2024/03/Program.cs b/src/2024/03/Program.cs
new file mode 100644
index 0000000..b09b451
--- /dev/null
+++ b/src/2024/03/Program.cs
@@ -0,0 +1,61 @@
+using System.ComponentModel.DataAnnotations;
+using System.Globalization;
+using System.Text.RegularExpressions;
+
+using ConsoleAppFramework;
+
+partial class Program
+{
+ private static void Main(string[] args)
+ {
+ var builder = ConsoleApp.Create();
+ builder.Add("part-one", static ([Argument, FileExists] string file) =>
+ {
+ Console.WriteLine($"Result: {ReadFile(file, true)}");
+ });
+ builder.Add("part-two", static ([Argument, FileExists] string file) =>
+ {
+ Console.WriteLine($"Result: {ReadFile(file, false)}");
+ });
+ builder.Run(args);
+ }
+
+ static int ReadFile(string file, bool forceEnable)
+ {
+ var pattern = MulPattern();
+ int result = 0;
+
+ using (var fileReader = new StreamReader(file))
+ {
+ bool state = true;
+ while (fileReader.ReadLine() is { } line)
+ {
+ foreach (var matchRange in pattern.EnumerateMatches(line))
+ {
+ var match = line.AsSpan(matchRange.Index, matchRange.Length);
+ Console.Out.WriteLine(match);
+ if (match.StartsWith("do"))
+ {
+ state = matchRange.Length == 4;
+ }
+ else if (forceEnable || state) /* Definitely mul */
+ {
+ var r = pattern.Match(line, matchRange.Index, matchRange.Length);
+ result += int.Parse(r.Groups[1].ValueSpan, NumberStyles.None) * int.Parse(r.Groups[2].ValueSpan, NumberStyles.None);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ [GeneratedRegex(@"do(?:n't)?\(\)|mul\((?\d+),(?\d+)\)")]
+ private static partial Regex MulPattern();
+}
+
+
+class FileExistsAttribute : ValidationAttribute
+{
+ public override bool IsValid(object? value) => value is string path && File.Exists(path);
+}
diff --git a/src/2024/03/aoc-2024-03.csproj b/src/2024/03/aoc-2024-03.csproj
new file mode 100644
index 0000000..96ca86f
--- /dev/null
+++ b/src/2024/03/aoc-2024-03.csproj
@@ -0,0 +1,11 @@
+
+
+
+ Exe
+ net9.0
+ aoc_2024_03
+ enable
+ enable
+
+
+
\ No newline at end of file
diff --git a/src/2024/04/Program.cs b/src/2024/04/Program.cs
new file mode 100644
index 0000000..12d9f79
--- /dev/null
+++ b/src/2024/04/Program.cs
@@ -0,0 +1,177 @@
+using System.ComponentModel.DataAnnotations;
+using System.Diagnostics.CodeAnalysis;
+using System.IO.MemoryMappedFiles;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+using ConsoleAppFramework;
+
+partial class Program
+{
+ delegate int Handler(in FileGrid grid, Vector2I index);
+
+ static IndexBuffer s_directions = new()
+ {
+ [0] = new(1, 0),
+ [1] = new(0, 1),
+ [2] = new(0, -1),
+ [3] = new(-1, 0),
+ [4] = new(1, 1),
+ [5] = new(1, -1),
+ [6] = new(-1, 1),
+ [7] = new(-1, -1),
+ };
+
+ private static void Main(string[] args)
+ {
+ var builder = ConsoleApp.Create();
+ builder.Add("part-one", static ([Argument, FileExists] string file) =>
+ {
+ Console.WriteLine($"Result: {ReadFile(file, 'X', new(InnerHandler))}");
+
+ static int InnerHandler(in FileGrid grid, Vector2I index)
+ {
+ int result = 0;
+ foreach (ref readonly Vector2I direction in s_directions)
+ {
+ if (!grid.IsValid(direction * 3 + index))
+ {
+ continue;
+ }
+
+ if ("MAS".AsSpan().SequenceEqual([
+ grid[direction + index]!.Value,
+ grid[direction * 2 + index]!.Value,
+ grid[direction * 3 + index]!.Value]))
+ {
+ result++;
+ }
+ }
+
+ return result;
+ }
+ });
+ builder.Add("part-two", static ([Argument, FileExists] string file) =>
+ {
+ Console.WriteLine($"Result: {ReadFile(file, 'A', new(InnerHandler))}");
+
+ static int InnerHandler(in FileGrid grid, Vector2I index)
+ {
+ return (grid[index + s_directions[7]], grid[index + s_directions[4]]) is ('M', 'S') or ('S', 'M') &&
+ (grid[index + s_directions[6]], grid[index + s_directions[5]]) is ('M', 'S') or ('S', 'M')
+ ? 1
+ : 0;
+ }
+ });
+ builder.Run(args);
+ }
+
+ static int ReadFile(string file, char activator, Handler handler)
+ {
+ int result = 0;
+ using (var fileView = MemoryMappedFile.CreateFromFile(file, FileMode.Open))
+ using (var accessor = fileView.CreateViewAccessor())
+ {
+ FileGrid grid = new(accessor);
+ var length = grid.Width * grid.Height;
+ for (var ci = 0; ci < length; ci++)
+ {
+ Vector2I index = new(ci / grid.Width, ci % grid.Width);
+ if (grid[index] != activator)
+ {
+ continue;
+ }
+
+ result += handler(grid, index);
+ }
+ }
+
+ return result;
+ }
+}
+
+readonly record struct Vector2I(int X, int Y) : IAdditionOperators, IMultiplyOperators
+{
+ public Vector2I((int x, int y) index) : this(index.x, index.y)
+ {
+ }
+
+ public static Vector2I operator +(Vector2I left, Vector2I right)
+ {
+ return new(left.X + right.X, left.Y + right.Y);
+ }
+
+ public static Vector2I operator *(Vector2I left, int right)
+ {
+ return new(left.X * right, left.Y * right);
+ }
+}
+
+[InlineArray(8)]
+struct IndexBuffer
+{
+ private Vector2I _element0;
+
+ [UnscopedRef]
+ public ref Vector2I this[int i] => ref this[i];
+}
+
+record struct FileGrid
+{
+ private readonly MemoryMappedViewAccessor _accessor;
+ private readonly int _stride;
+
+ public int Width { get; }
+
+ public int Height { get; }
+
+ public readonly char? this[Vector2I index] => this[index.X, index.Y];
+
+ public readonly char? this[int x, int y]
+ {
+ get
+ {
+ return IsValid(x, y) ? (char)_accessor.ReadByte(y * _stride + x) : null;
+ }
+ }
+
+ public FileGrid(MemoryMappedViewAccessor accessor)
+ {
+ _accessor = accessor;
+ bool hasNewLine = false;
+ for (int i = 0; i < accessor.Capacity; i++)
+ {
+ if ((char)accessor.ReadByte(i) is '\r' or '\n')
+ {
+ if (!hasNewLine)
+ {
+ Width = i;
+ }
+
+ hasNewLine = true;
+ }
+ else if (hasNewLine)
+ {
+ _stride = i;
+ break;
+ }
+ }
+
+ Height = (int)((accessor.Capacity + Width) / _stride);
+ }
+
+ public readonly bool IsValid(int x, int y)
+ {
+ return !(x < 0 || x >= Width || y < 0 || y >= Height);
+ }
+
+ public readonly bool IsValid(Vector2I index)
+ {
+ return IsValid(index.X, index.Y);
+ }
+}
+
+class FileExistsAttribute : ValidationAttribute
+{
+ public override bool IsValid(object? value) => value is string path && File.Exists(path);
+}
diff --git a/src/2024/04/aoc-2024-04.csproj b/src/2024/04/aoc-2024-04.csproj
new file mode 100644
index 0000000..a47f4d3
--- /dev/null
+++ b/src/2024/04/aoc-2024-04.csproj
@@ -0,0 +1,11 @@
+
+
+
+ Exe
+ net8.0
+ aoc_2024_04
+ enable
+ enable
+
+
+
diff --git a/src/2024/05/Program.cs b/src/2024/05/Program.cs
new file mode 100644
index 0000000..521210f
--- /dev/null
+++ b/src/2024/05/Program.cs
@@ -0,0 +1,83 @@
+using System.ComponentModel.DataAnnotations;
+
+using ConsoleAppFramework;
+
+var builder = ConsoleApp.Create();
+builder.Add("part-one", static ([Argument, FileExists] string file) =>
+{
+ Console.WriteLine($"Result: {ReadFile(file, false)}");
+});
+builder.Add("part-two", static ([Argument, FileExists] string file) =>
+{
+ Console.WriteLine($"Result: {ReadFile(file, true)}");
+});
+builder.Run(args);
+
+static int ReadFile(string file, bool sort)
+{
+ int result = 0;
+
+ using (var fileReader = new StreamReader(file))
+ {
+ HashSet orderingRules = [];
+ while (fileReader.ReadLine() is { } line && !string.IsNullOrWhiteSpace(line))
+ {
+ var sep = line.IndexOf('|');
+ var left = short.Parse(line.AsSpan(0, sep));
+ var right = short.Parse(line.AsSpan(sep + 1));
+ orderingRules.Add(new(left, right));
+ }
+
+ var ruleComparer = Comparer.Create((x, y) =>
+ {
+ return orderingRules.Contains(new(x, y)) ? -1
+ : orderingRules.Contains(new(y, x)) ? 1
+ : 0;
+ });
+
+ List pages = [];
+ while (fileReader.ReadLine() is { } line)
+ {
+ pages.Clear();
+ foreach (var element in line.AsSpan().Split(','))
+ {
+ pages.Add(int.Parse(line.AsSpan(element)));
+ }
+
+ bool valid = true;
+ for (int i = 0; valid && i < pages.Count; i++)
+ {
+ for (int j = 0; valid && j < i; j++)
+ {
+ var left = pages[j];
+ var right = pages[i];
+ if (orderingRules.Contains(new(right, left)))
+ {
+ valid = false;
+ }
+ }
+ }
+
+ if ((valid && sort) || !(valid || sort))
+ {
+ continue;
+ }
+
+ if (sort)
+ {
+ pages.Sort(ruleComparer);
+ }
+ Console.WriteLine(string.Join(',', pages));
+ result += pages[pages.Count / 2];
+ }
+ }
+
+ return result;
+}
+
+readonly record struct OrderingRule(int Left, int Right);
+
+class FileExistsAttribute : ValidationAttribute
+{
+ public override bool IsValid(object? value) => value is string path && File.Exists(path);
+}
diff --git a/src/2024/05/aoc-2024-05.csproj b/src/2024/05/aoc-2024-05.csproj
new file mode 100644
index 0000000..9202d77
--- /dev/null
+++ b/src/2024/05/aoc-2024-05.csproj
@@ -0,0 +1,11 @@
+
+
+
+ Exe
+ net8.0
+ aoc_2024_05
+ enable
+ enable
+
+
+
diff --git a/src/2024/Directory.Build.props b/src/2024/Directory.Build.props
new file mode 100644
index 0000000..68d915b
--- /dev/null
+++ b/src/2024/Directory.Build.props
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
\ No newline at end of file