From 0be7ca236a1f201459ba45050ca337f9a7c199bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20C=C3=A1ceres?= Date: Sun, 10 Nov 2024 22:56:54 +0100 Subject: [PATCH] Move to file-scoped namespaces --- src/AoCHelper.PoC.Library/LibraryDay11.cs | 29 +- src/AoCHelper.PoC.Library/LibraryDay15.cs | 31 +- src/AoCHelper.PoC.Library/LibraryDay16.cs | 21 +- src/AoCHelper.PoC.Library/LibraryDay17.cs | 19 +- src/AoCHelper.PoC.Library/LibraryDay_12.cs | 33 +- src/AoCHelper.PoC.Library/Problem13.cs | 33 +- src/AoCHelper.PoC.Library/Problem14.cs | 37 +- src/AoCHelper.PoC.Library/RandomProblem.cs | 47 +- src/AoCHelper.PoC/Day01.cs | 29 +- src/AoCHelper.PoC/Day05.cs | 31 +- src/AoCHelper.PoC/Day06.cs | 21 +- src/AoCHelper.PoC/Day07.cs | 19 +- src/AoCHelper.PoC/Day_02.cs | 33 +- src/AoCHelper.PoC/Problem03.cs | 33 +- src/AoCHelper.PoC/Problem04.cs | 37 +- src/AoCHelper.PoC/RandomProblem.cs | 41 +- src/AoCHelper/AoCHelper - Backup.csproj | 52 ++ src/AoCHelper/BaseDay.cs | 17 +- src/AoCHelper/BaseProblem.cs | 89 +-- src/AoCHelper/Solver.cs | 749 +++++++++--------- src/AoCHelper/SolverConfiguration.cs | 103 ++- src/AoCHelper/SolvingException.cs | 21 +- .../ModifyInputFileDirPath_SampleTests.cs | 3 +- .../ModifyInputFilePathTests_SampleTests.cs | 5 +- .../OverrideClassPrefixTests.cs | 45 +- tests/AoCHelper.Test/OverrideIndexTests.cs | 41 +- .../OverrideInputFileDirPathTests.cs | 45 +- .../OverrideInputFileExtensionTests.cs | 43 +- .../OverrideInputFilePathTests.cs | 41 +- tests/AoCHelper.Test/SolverTest.cs | 11 +- 30 files changed, 892 insertions(+), 867 deletions(-) create mode 100644 src/AoCHelper/AoCHelper - Backup.csproj diff --git a/src/AoCHelper.PoC.Library/LibraryDay11.cs b/src/AoCHelper.PoC.Library/LibraryDay11.cs index 1c1b2da..1436869 100644 --- a/src/AoCHelper.PoC.Library/LibraryDay11.cs +++ b/src/AoCHelper.PoC.Library/LibraryDay11.cs @@ -1,23 +1,22 @@ -namespace AoCHelper.PoC.Library +namespace AoCHelper.PoC.Library; + +public class LibraryDay11 : BaseLibraryDay { - public class LibraryDay11 : BaseLibraryDay + public LibraryDay11() { - public LibraryDay11() + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); - } + throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); } + } - public override ValueTask Solve_1() - { - return new("Solution Library 1.1"); - } + public override ValueTask Solve_1() + { + return new("Solution Library 1.1"); + } - public override ValueTask Solve_2() - { - return new("Solution Library 1.2"); - } + public override ValueTask Solve_2() + { + return new("Solution Library 1.2"); } } diff --git a/src/AoCHelper.PoC.Library/LibraryDay15.cs b/src/AoCHelper.PoC.Library/LibraryDay15.cs index d3fe284..2d309cc 100644 --- a/src/AoCHelper.PoC.Library/LibraryDay15.cs +++ b/src/AoCHelper.PoC.Library/LibraryDay15.cs @@ -1,22 +1,21 @@ -namespace AoCHelper.PoC.Library +namespace AoCHelper.PoC.Library; + +public class LibraryDay15 : BaseLibraryDay { - public class LibraryDay15 : BaseLibraryDay + public LibraryDay15() { - public LibraryDay15() - { - throw new SolvingException("Exception in constructor"); - } + throw new SolvingException("Exception in constructor"); + } - public override ValueTask Solve_1() - { - Thread.Sleep(66); - return new("Solution Library 5.1"); - } + public override ValueTask Solve_1() + { + Thread.Sleep(66); + return new("Solution Library 5.1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(66); - return new("Solution Library 5.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(66); + return new("Solution Library 5.2"); } } diff --git a/src/AoCHelper.PoC.Library/LibraryDay16.cs b/src/AoCHelper.PoC.Library/LibraryDay16.cs index 8dc5ce5..f406b47 100644 --- a/src/AoCHelper.PoC.Library/LibraryDay16.cs +++ b/src/AoCHelper.PoC.Library/LibraryDay16.cs @@ -1,16 +1,15 @@ -namespace AoCHelper.PoC.Library +namespace AoCHelper.PoC.Library; + +public class LibraryDay16 : BaseLibraryDay { - public class LibraryDay16 : BaseLibraryDay + public override ValueTask Solve_1() { - public override ValueTask Solve_1() - { - throw new SolvingException("Exception in Library 6 part 1"); - } + throw new SolvingException("Exception in Library 6 part 1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(123); - return new("Solution Library 6.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(123); + return new("Solution Library 6.2"); } } diff --git a/src/AoCHelper.PoC.Library/LibraryDay17.cs b/src/AoCHelper.PoC.Library/LibraryDay17.cs index b5264b5..e388017 100644 --- a/src/AoCHelper.PoC.Library/LibraryDay17.cs +++ b/src/AoCHelper.PoC.Library/LibraryDay17.cs @@ -1,15 +1,14 @@ -namespace AoCHelper.PoC.Library +namespace AoCHelper.PoC.Library; + +public class LibraryDay17 : BaseLibraryDay { - public class LibraryDay17 : BaseLibraryDay + public override ValueTask Solve_1() { - public override ValueTask Solve_1() - { - return new("Solution Library 7.1"); - } + return new("Solution Library 7.1"); + } - public override ValueTask Solve_2() - { - throw new SolvingException("Exception in Library 7 part 2"); - } + public override ValueTask Solve_2() + { + throw new SolvingException("Exception in Library 7 part 2"); } } diff --git a/src/AoCHelper.PoC.Library/LibraryDay_12.cs b/src/AoCHelper.PoC.Library/LibraryDay_12.cs index 244ff61..2a261af 100644 --- a/src/AoCHelper.PoC.Library/LibraryDay_12.cs +++ b/src/AoCHelper.PoC.Library/LibraryDay_12.cs @@ -1,25 +1,24 @@ -namespace AoCHelper.PoC.Library +namespace AoCHelper.PoC.Library; + +public class LibraryDay_12 : BaseLibraryDay { - public class LibraryDay_12 : BaseLibraryDay + public LibraryDay_12() { - public LibraryDay_12() + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); - } + throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); } + } - public override ValueTask Solve_1() - { - Thread.Sleep(1); - return new("Solution Library 2.1"); - } + public override ValueTask Solve_1() + { + Thread.Sleep(1); + return new("Solution Library 2.1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(11); - return new("Solution Library 2.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(11); + return new("Solution Library 2.2"); } } diff --git a/src/AoCHelper.PoC.Library/Problem13.cs b/src/AoCHelper.PoC.Library/Problem13.cs index 045c644..bc3907d 100644 --- a/src/AoCHelper.PoC.Library/Problem13.cs +++ b/src/AoCHelper.PoC.Library/Problem13.cs @@ -1,25 +1,24 @@ -namespace AoCHelper.PoC.Library +namespace AoCHelper.PoC.Library; + +internal class Problem13 : BaseLibraryProblem { - internal class Problem13 : BaseLibraryProblem + public Problem13() { - public Problem13() + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); - } + throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); } + } - public override ValueTask Solve_1() - { - Thread.Sleep(101); - return new("Solution Library 3.1"); - } + public override ValueTask Solve_1() + { + Thread.Sleep(101); + return new("Solution Library 3.1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(501); - return new("Solution Library 3.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(501); + return new("Solution Library 3.2"); } } diff --git a/src/AoCHelper.PoC.Library/Problem14.cs b/src/AoCHelper.PoC.Library/Problem14.cs index fd6aa63..e3090af 100644 --- a/src/AoCHelper.PoC.Library/Problem14.cs +++ b/src/AoCHelper.PoC.Library/Problem14.cs @@ -1,27 +1,26 @@ -namespace AoCHelper.PoC.Library +namespace AoCHelper.PoC.Library; + +internal class Problem14 : BaseLibraryProblem { - internal class Problem14 : BaseLibraryProblem - { - protected override string InputFileExtension => ".in"; + protected override string InputFileExtension => ".in"; - public Problem14() + public Problem14() + { + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); - } + throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); } + } - public override ValueTask Solve_1() - { - Thread.Sleep(1500); - return new("Solution Library 4.1"); - } + public override ValueTask Solve_1() + { + Thread.Sleep(1500); + return new("Solution Library 4.1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(2000); - return new("Solution Library 4.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(2000); + return new("Solution Library 4.2"); } } diff --git a/src/AoCHelper.PoC.Library/RandomProblem.cs b/src/AoCHelper.PoC.Library/RandomProblem.cs index 29f3a71..662b4f9 100644 --- a/src/AoCHelper.PoC.Library/RandomProblem.cs +++ b/src/AoCHelper.PoC.Library/RandomProblem.cs @@ -1,34 +1,33 @@ using System.Reflection; -namespace AoCHelper.PoC.Library +namespace AoCHelper.PoC.Library; + +internal class RandomProblem : BaseLibraryProblem { - internal class RandomProblem : BaseLibraryProblem - { - /// - /// Overriding FilePath, due to problem not following any convention (not even index one). - /// - public override string InputFilePath => - Path.Combine( - Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!, - "Inputs/LibraryRandomInput.random"); + /// + /// Overriding FilePath, due to problem not following any convention (not even index one). + /// + public override string InputFilePath => + Path.Combine( + Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!, + "Inputs/LibraryRandomInput.random"); - public RandomProblem() + public RandomProblem() + { + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); - } + throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); } + } - public override ValueTask Solve_1() - { - return new("Solution Library Random.1"); - } + public override ValueTask Solve_1() + { + return new("Solution Library Random.1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(10_000); - return new("Solution Library Random.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(10_000); + return new("Solution Library Random.2"); } } diff --git a/src/AoCHelper.PoC/Day01.cs b/src/AoCHelper.PoC/Day01.cs index 3a75222..3e92909 100644 --- a/src/AoCHelper.PoC/Day01.cs +++ b/src/AoCHelper.PoC/Day01.cs @@ -1,23 +1,22 @@ -namespace AoCHelper.PoC +namespace AoCHelper.PoC; + +public class Day01 : BaseDay { - public class Day01 : BaseDay + public Day01() { - public Day01() + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); - } + throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); } + } - public override ValueTask Solve_1() - { - return new("Solution 1.1"); - } + public override ValueTask Solve_1() + { + return new("Solution 1.1"); + } - public override ValueTask Solve_2() - { - return new("Solution 1.2"); - } + public override ValueTask Solve_2() + { + return new("Solution 1.2"); } } diff --git a/src/AoCHelper.PoC/Day05.cs b/src/AoCHelper.PoC/Day05.cs index 3bb5ab7..d89aa47 100644 --- a/src/AoCHelper.PoC/Day05.cs +++ b/src/AoCHelper.PoC/Day05.cs @@ -1,22 +1,21 @@ -namespace AoCHelper.PoC +namespace AoCHelper.PoC; + +public class Day05 : BaseProblem { - public class Day05 : BaseProblem + public Day05() { - public Day05() - { - throw new SolvingException("Exception in constructor"); - } + throw new SolvingException("Exception in constructor"); + } - public override ValueTask Solve_1() - { - Thread.Sleep(66); - return new("Solution 5.1"); - } + public override ValueTask Solve_1() + { + Thread.Sleep(66); + return new("Solution 5.1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(66); - return new("Solution 5.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(66); + return new("Solution 5.2"); } } diff --git a/src/AoCHelper.PoC/Day06.cs b/src/AoCHelper.PoC/Day06.cs index 4f08399..5f337eb 100644 --- a/src/AoCHelper.PoC/Day06.cs +++ b/src/AoCHelper.PoC/Day06.cs @@ -1,16 +1,15 @@ -namespace AoCHelper.PoC +namespace AoCHelper.PoC; + +public class Day06 : BaseDay { - public class Day06 : BaseDay + public override ValueTask Solve_1() { - public override ValueTask Solve_1() - { - throw new SolvingException("Exception in 6 part 1"); - } + throw new SolvingException("Exception in 6 part 1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(123); - return new("Solution 6.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(123); + return new("Solution 6.2"); } } diff --git a/src/AoCHelper.PoC/Day07.cs b/src/AoCHelper.PoC/Day07.cs index bcfca7e..8b6f122 100644 --- a/src/AoCHelper.PoC/Day07.cs +++ b/src/AoCHelper.PoC/Day07.cs @@ -1,15 +1,14 @@ -namespace AoCHelper.PoC +namespace AoCHelper.PoC; + +public class Day07 : BaseDay { - public class Day07 : BaseDay + public override ValueTask Solve_1() { - public override ValueTask Solve_1() - { - return new("Solution 7.1"); - } + return new("Solution 7.1"); + } - public override ValueTask Solve_2() - { - throw new SolvingException("Exception in 7 part 2"); - } + public override ValueTask Solve_2() + { + throw new SolvingException("Exception in 7 part 2"); } } diff --git a/src/AoCHelper.PoC/Day_02.cs b/src/AoCHelper.PoC/Day_02.cs index bdbc669..adbf5ad 100644 --- a/src/AoCHelper.PoC/Day_02.cs +++ b/src/AoCHelper.PoC/Day_02.cs @@ -1,25 +1,24 @@ -namespace AoCHelper.PoC +namespace AoCHelper.PoC; + +public class Day_02 : BaseDay { - public class Day_02 : BaseDay + public Day_02() { - public Day_02() + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); - } + throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); } + } - public override ValueTask Solve_1() - { - Thread.Sleep(1); - return new("Solution 2.1"); - } + public override ValueTask Solve_1() + { + Thread.Sleep(1); + return new("Solution 2.1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(11); - return new("Solution 2.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(11); + return new("Solution 2.2"); } } diff --git a/src/AoCHelper.PoC/Problem03.cs b/src/AoCHelper.PoC/Problem03.cs index 0e68c2f..7f38440 100644 --- a/src/AoCHelper.PoC/Problem03.cs +++ b/src/AoCHelper.PoC/Problem03.cs @@ -1,25 +1,24 @@ -namespace AoCHelper.PoC +namespace AoCHelper.PoC; + +internal class Problem03 : BaseProblem { - internal class Problem03 : BaseProblem + public Problem03() { - public Problem03() + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); - } + throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); } + } - public override ValueTask Solve_1() - { - Thread.Sleep(101); - return new("Solution 3.1"); - } + public override ValueTask Solve_1() + { + Thread.Sleep(101); + return new("Solution 3.1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(501); - return new("Solution 3.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(501); + return new("Solution 3.2"); } } diff --git a/src/AoCHelper.PoC/Problem04.cs b/src/AoCHelper.PoC/Problem04.cs index 452e8fc..ea13d6b 100644 --- a/src/AoCHelper.PoC/Problem04.cs +++ b/src/AoCHelper.PoC/Problem04.cs @@ -1,27 +1,26 @@ -namespace AoCHelper.PoC +namespace AoCHelper.PoC; + +internal class Problem04 : BaseProblem { - internal class Problem04 : BaseProblem - { - protected override string InputFileExtension => ".in"; + protected override string InputFileExtension => ".in"; - public Problem04() + public Problem04() + { + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); - } + throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); } + } - public override ValueTask Solve_1() - { - Thread.Sleep(1500); - return new("Solution 4.1"); - } + public override ValueTask Solve_1() + { + Thread.Sleep(1500); + return new("Solution 4.1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(2000); - return new("Solution 4.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(2000); + return new("Solution 4.2"); } } diff --git a/src/AoCHelper.PoC/RandomProblem.cs b/src/AoCHelper.PoC/RandomProblem.cs index d2c4e6b..272da1b 100644 --- a/src/AoCHelper.PoC/RandomProblem.cs +++ b/src/AoCHelper.PoC/RandomProblem.cs @@ -1,29 +1,28 @@ -namespace AoCHelper.PoC +namespace AoCHelper.PoC; + +internal class RandomProblem : BaseProblem { - internal class RandomProblem : BaseProblem - { - /// - /// Overriding FilePath, due to problem not following any convention (not even index one). - /// - public override string InputFilePath => "Inputs/RandomInput.random"; + /// + /// Overriding FilePath, due to problem not following any convention (not even index one). + /// + public override string InputFilePath => "Inputs/RandomInput.random"; - public RandomProblem() + public RandomProblem() + { + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); - } + throw new SolvingException($"Path {InputFilePath} not found for {GetType().Name}"); } + } - public override ValueTask Solve_1() - { - return new("Solution Random.1"); - } + public override ValueTask Solve_1() + { + return new("Solution Random.1"); + } - public override ValueTask Solve_2() - { - Thread.Sleep(10_000); - return new("Solution Random.2"); - } + public override ValueTask Solve_2() + { + Thread.Sleep(10_000); + return new("Solution Random.2"); } } diff --git a/src/AoCHelper/AoCHelper - Backup.csproj b/src/AoCHelper/AoCHelper - Backup.csproj new file mode 100644 index 0000000..533d139 --- /dev/null +++ b/src/AoCHelper/AoCHelper - Backup.csproj @@ -0,0 +1,52 @@ + + + + net8.0;net7.0;net6.0;netstandard2.1;netstandard2.0 + true + snupkg + AoCHelper + AoCHelper + 3.1.0 + AoC, AdventOfCode, Advent, Code + $(PackageTags) + Eduardo Cáceres + Library that provides infrastructure for you to solve AoC problems, so that you don't have to prepare (almost) anything! + MIT + https://github.com/eduherminio/AoCHelper + https://github.com/eduherminio/AoCHelper + git + true + true + README.md + + + + + + + + + + + + + + + + + Properties\README.md + + + + + + <_Parameter1>$(AssemblyName).Test + + + + + bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml + 1591 + + + diff --git a/src/AoCHelper/BaseDay.cs b/src/AoCHelper/BaseDay.cs index a0eae59..92d6a70 100644 --- a/src/AoCHelper/BaseDay.cs +++ b/src/AoCHelper/BaseDay.cs @@ -1,13 +1,12 @@ -namespace AoCHelper +namespace AoCHelper; + +/// +/// with custom ("Day") +/// +public abstract class BaseDay : BaseProblem { /// - /// with custom ("Day") + /// /// - public abstract class BaseDay : BaseProblem - { - /// - /// - /// - protected override string ClassPrefix { get; } = "Day"; - } + protected override string ClassPrefix { get; } = "Day"; } diff --git a/src/AoCHelper/BaseProblem.cs b/src/AoCHelper/BaseProblem.cs index 7bd679a..b099bcc 100644 --- a/src/AoCHelper/BaseProblem.cs +++ b/src/AoCHelper/BaseProblem.cs @@ -1,55 +1,54 @@ -namespace AoCHelper +namespace AoCHelper; + +public abstract class BaseProblem { - public abstract class BaseProblem + protected virtual string ClassPrefix { get; } = "Problem"; + + /// + /// Expected input file dir path. + /// + protected virtual string InputFileDirPath { get; } = "Inputs"; + + /// + /// Expected input file extension. + /// + protected virtual string InputFileExtension { get; } = ".txt"; + + /// + /// Problem's index. + /// Two digit number, (expect a leading '0' when appropriated). + /// + /// + /// Extracts problem's index from the class name. + /// Supported formats: {Index}, _{Index}. + /// In case of unsupported class name format, needs to be overriden to point to the right input file. + /// + /// Problem's index or uint.MaxValue if unsupported class name. + public virtual uint CalculateIndex() { - protected virtual string ClassPrefix { get; } = "Problem"; - - /// - /// Expected input file dir path. - /// - protected virtual string InputFileDirPath { get; } = "Inputs"; - - /// - /// Expected input file extension. - /// - protected virtual string InputFileExtension { get; } = ".txt"; - - /// - /// Problem's index. - /// Two digit number, (expect a leading '0' when appropriated). - /// - /// - /// Extracts problem's index from the class name. - /// Supported formats: {Index}, _{Index}. - /// In case of unsupported class name format, needs to be overriden to point to the right input file. - /// - /// Problem's index or uint.MaxValue if unsupported class name. - public virtual uint CalculateIndex() - { - var typeName = GetType().Name; + var typeName = GetType().Name; - return uint.TryParse(typeName.Substring(typeName.IndexOf(ClassPrefix) + ClassPrefix.Length).TrimStart('_'), out var index) - ? index - : default; - } + return uint.TryParse(typeName.Substring(typeName.IndexOf(ClassPrefix) + ClassPrefix.Length).TrimStart('_'), out var index) + ? index + : default; + } - /// - /// Expected input file path. - /// By default, /.. - /// Overriding it makes , , and irrelevant - /// - public virtual string InputFilePath + /// + /// Expected input file path. + /// By default, /.. + /// Overriding it makes , , and irrelevant + /// + public virtual string InputFilePath + { + get { - get - { - var index = CalculateIndex().ToString("D2"); + var index = CalculateIndex().ToString("D2"); - return Path.Combine(InputFileDirPath, $"{index}.{InputFileExtension.TrimStart('.')}"); - } + return Path.Combine(InputFileDirPath, $"{index}.{InputFileExtension.TrimStart('.')}"); } + } - public abstract ValueTask Solve_1(); + public abstract ValueTask Solve_1(); - public abstract ValueTask Solve_2(); - } + public abstract ValueTask Solve_2(); } diff --git a/src/AoCHelper/Solver.cs b/src/AoCHelper/Solver.cs index 626b07e..f5b70a6 100644 --- a/src/AoCHelper/Solver.cs +++ b/src/AoCHelper/Solver.cs @@ -2,236 +2,144 @@ using System.Reflection; using Spectre.Console; -namespace AoCHelper -{ - public static class Solver - { - private static readonly bool IsInteractiveEnvironment = Environment.UserInteractive && !Console.IsOutputRedirected; - - private record ElapsedTime(double Constructor, double Part1, double Part2); +namespace AoCHelper; - /// - /// Solves last problem. - /// It also prints the elapsed time in and methods. - /// - /// - public static async Task SolveLast(Action? options = null) - { - var configuration = PopulateConfiguration(options); - - if (IsInteractiveEnvironment && configuration.ClearConsole) - { - AnsiConsole.Clear(); - } +public static class Solver +{ + private static readonly bool IsInteractiveEnvironment = Environment.UserInteractive && !Console.IsOutputRedirected; - var table = GetTable(); + private record ElapsedTime(double Constructor, double Part1, double Part2); - await AnsiConsole.Live(table) - .AutoClear(false) - .Overflow(configuration.VerticalOverflow) - .Cropping(configuration.VerticalOverflowCropping) - .StartAsync(async ctx => - { - var lastProblem = LoadAllProblems(configuration.ProblemAssemblies).LastOrDefault(); - if (lastProblem is not null) - { - var sw = new Stopwatch(); - sw.Start(); - var potentialProblem = InstantiateProblem(lastProblem); - sw.Stop(); + /// + /// Solves last problem. + /// It also prints the elapsed time in and methods. + /// + /// + public static async Task SolveLast(Action? options = null) + { + var configuration = PopulateConfiguration(options); - if (potentialProblem is BaseProblem problem) - { - await SolveProblem(problem, table, CalculateElapsedMilliseconds(sw), configuration); - ctx.Refresh(); - } - else - { - RenderEmptyProblem(lastProblem, potentialProblem as string, table, CalculateElapsedMilliseconds(sw), configuration); - } - } - }); + if (IsInteractiveEnvironment && configuration.ClearConsole) + { + AnsiConsole.Clear(); } - /// - /// Solves a problem. - /// It also prints the elapsed time in and methods. - /// - /// - /// - public static async Task Solve(Action? options = null) - where TProblem : BaseProblem, new() - { - var configuration = PopulateConfiguration(options); + var table = GetTable(); - if (IsInteractiveEnvironment && configuration.ClearConsole) + await AnsiConsole.Live(table) + .AutoClear(false) + .Overflow(configuration.VerticalOverflow) + .Cropping(configuration.VerticalOverflowCropping) + .StartAsync(async ctx => { - AnsiConsole.Clear(); - } - - var table = GetTable(); - - await AnsiConsole.Live(table) - .AutoClear(false) - .Overflow(configuration.VerticalOverflow) - .Cropping(configuration.VerticalOverflowCropping) - .StartAsync(async ctx => + var lastProblem = LoadAllProblems(configuration.ProblemAssemblies).LastOrDefault(); + if (lastProblem is not null) { var sw = new Stopwatch(); sw.Start(); - try - { - TProblem problem = new(); - sw.Stop(); + var potentialProblem = InstantiateProblem(lastProblem); + sw.Stop(); + if (potentialProblem is BaseProblem problem) + { await SolveProblem(problem, table, CalculateElapsedMilliseconds(sw), configuration); + ctx.Refresh(); } - catch (Exception e) + else { - RenderEmptyProblem(typeof(TProblem), e.Message + Environment.NewLine + e.StackTrace, table, CalculateElapsedMilliseconds(sw), configuration); + RenderEmptyProblem(lastProblem, potentialProblem as string, table, CalculateElapsedMilliseconds(sw), configuration); } - ctx.Refresh(); - }); - } - - /// - /// Solves those problems whose method matches one of the provided numbers. - /// 0 can be used for those problems whose returns the default value due to not being able to deduct the index. - /// - /// - /// - public static async Task Solve(Action? options = null, params uint[] problemNumbers) - => await Solve(problemNumbers.AsEnumerable(), options); - - /// - /// Solves the provided problems. - /// It also prints the elapsed time in and methods. - /// - /// - /// - public static async Task Solve(Action? options = null, params Type[] problems) - => await Solve(problems.AsEnumerable(), options); - - /// - /// Solves the provided problems. - /// It also prints the elapsed time in and methods. - /// - /// - /// - public static async Task Solve(IEnumerable problems, Action? options = null) - { - var configuration = PopulateConfiguration(options); - - if (IsInteractiveEnvironment && configuration.ClearConsole) - { - AnsiConsole.Clear(); - } - - var totalElapsedTime = new List(); - var table = GetTable(); + } + }); + } - await AnsiConsole.Live(table) - .AutoClear(false) - .Overflow(configuration.VerticalOverflow) - .Cropping(configuration.VerticalOverflowCropping) - .StartAsync(async ctx => - { - var sw = new Stopwatch(); - foreach (Type problemType in LoadAllProblems(configuration.ProblemAssemblies)) - { - if (problems.Contains(problemType)) - { - sw.Restart(); - var potentialProblem = InstantiateProblem(problemType); - sw.Stop(); - - if (potentialProblem is BaseProblem problem) - { - totalElapsedTime.Add(await SolveProblem(problem, table, CalculateElapsedMilliseconds(sw), configuration)); - ctx.Refresh(); - } - else - { - totalElapsedTime.Add(RenderEmptyProblem(problemType, potentialProblem as string, table, CalculateElapsedMilliseconds(sw), configuration)); - } - } - } - }); + /// + /// Solves a problem. + /// It also prints the elapsed time in and methods. + /// + /// + /// + public static async Task Solve(Action? options = null) + where TProblem : BaseProblem, new() + { + var configuration = PopulateConfiguration(options); - RenderOverallResultsPanel(totalElapsedTime, configuration); + if (IsInteractiveEnvironment && configuration.ClearConsole) + { + AnsiConsole.Clear(); } - /// - /// Solves those problems whose method matches one of the provided numbers. - /// 0 can be used for those problems whose returns the default value due to not being able to deduct the index. - /// This method might not work correctly if any of the problems in the assembly throws an exception in its constructor. - /// - /// - /// - public static async Task Solve(IEnumerable problemNumbers, Action? options = null) - { - var configuration = PopulateConfiguration(options); + var table = GetTable(); - if (IsInteractiveEnvironment && configuration.ClearConsole) + await AnsiConsole.Live(table) + .AutoClear(false) + .Overflow(configuration.VerticalOverflow) + .Cropping(configuration.VerticalOverflowCropping) + .StartAsync(async ctx => { - AnsiConsole.Clear(); - } - - var totalElapsedTime = new List(); - var table = GetTable(); + var sw = new Stopwatch(); + sw.Start(); + try + { + TProblem problem = new(); + sw.Stop(); - await AnsiConsole.Live(table) - .AutoClear(false) - .Overflow(configuration.VerticalOverflow) - .Cropping(configuration.VerticalOverflowCropping) - .StartAsync(async ctx => + await SolveProblem(problem, table, CalculateElapsedMilliseconds(sw), configuration); + } + catch (Exception e) { - var sw = new Stopwatch(); - foreach (Type problemType in LoadAllProblems(configuration.ProblemAssemblies)) - { - sw.Restart(); - // Since we're trying to instantiate them all, we don't want to show unrelated errors or render unrelated problem rows - // However, without the index, calculated once the constructor success, we can't separate those unrelated errors from - // our desired problems' ones. So there's a limitation when using this method if other constructors are failing - var potentialProblem = Activator.CreateInstance(problemType); - sw.Stop(); + RenderEmptyProblem(typeof(TProblem), e.Message + Environment.NewLine + e.StackTrace, table, CalculateElapsedMilliseconds(sw), configuration); + } + ctx.Refresh(); + }); + } - if (potentialProblem is BaseProblem problem && problemNumbers.Contains(problem.CalculateIndex())) - { - totalElapsedTime.Add(await SolveProblem(problem, table, CalculateElapsedMilliseconds(sw), configuration)); - ctx.Refresh(); - } - } - }); + /// + /// Solves those problems whose method matches one of the provided numbers. + /// 0 can be used for those problems whose returns the default value due to not being able to deduct the index. + /// + /// + /// + public static async Task Solve(Action? options = null, params uint[] problemNumbers) + => await Solve(problemNumbers.AsEnumerable(), options); + + /// + /// Solves the provided problems. + /// It also prints the elapsed time in and methods. + /// + /// + /// + public static async Task Solve(Action? options = null, params Type[] problems) + => await Solve(problems.AsEnumerable(), options); + + /// + /// Solves the provided problems. + /// It also prints the elapsed time in and methods. + /// + /// + /// + public static async Task Solve(IEnumerable problems, Action? options = null) + { + var configuration = PopulateConfiguration(options); - RenderOverallResultsPanel(totalElapsedTime, configuration); + if (IsInteractiveEnvironment && configuration.ClearConsole) + { + AnsiConsole.Clear(); } - /// - /// Solves all problems in the assembly. - /// It also prints the elapsed time in and methods. - /// - /// - public static async Task SolveAll(Action? options = null) - { - var configuration = PopulateConfiguration(options); + var totalElapsedTime = new List(); + var table = GetTable(); - if (IsInteractiveEnvironment && configuration.ClearConsole) + await AnsiConsole.Live(table) + .AutoClear(false) + .Overflow(configuration.VerticalOverflow) + .Cropping(configuration.VerticalOverflowCropping) + .StartAsync(async ctx => { - AnsiConsole.Clear(); - } - - var totalElapsedTime = new List(); - var table = GetTable(); - - await AnsiConsole.Live(table) - .AutoClear(false) - .Overflow(configuration.VerticalOverflow) - .Cropping(configuration.VerticalOverflowCropping) - .StartAsync(async ctx => + var sw = new Stopwatch(); + foreach (Type problemType in LoadAllProblems(configuration.ProblemAssemblies)) { - var sw = new Stopwatch(); - foreach (Type problemType in LoadAllProblems(configuration.ProblemAssemblies)) + if (problems.Contains(problemType)) { sw.Restart(); var potentialProblem = InstantiateProblem(problemType); @@ -247,235 +155,326 @@ await AnsiConsole.Live(table) totalElapsedTime.Add(RenderEmptyProblem(problemType, potentialProblem as string, table, CalculateElapsedMilliseconds(sw), configuration)); } } - }); + } + }); - RenderOverallResultsPanel(totalElapsedTime, configuration); - } + RenderOverallResultsPanel(totalElapsedTime, configuration); + } + + /// + /// Solves those problems whose method matches one of the provided numbers. + /// 0 can be used for those problems whose returns the default value due to not being able to deduct the index. + /// This method might not work correctly if any of the problems in the assembly throws an exception in its constructor. + /// + /// + /// + public static async Task Solve(IEnumerable problemNumbers, Action? options = null) + { + var configuration = PopulateConfiguration(options); - /// - /// Loads all in the given assemblies - /// - /// - /// - internal static IEnumerable LoadAllProblems(List assemblies) + if (IsInteractiveEnvironment && configuration.ClearConsole) { - return assemblies.SelectMany(a => a.GetTypes()) - .Where(type => typeof(BaseProblem).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) - .OrderBy(t => t.FullName); + AnsiConsole.Clear(); } - private static SolverConfiguration PopulateConfiguration(Action? options) - { - var configuration = new SolverConfiguration(); - options?.Invoke(configuration); + var totalElapsedTime = new List(); + var table = GetTable(); - return configuration; - } + await AnsiConsole.Live(table) + .AutoClear(false) + .Overflow(configuration.VerticalOverflow) + .Cropping(configuration.VerticalOverflowCropping) + .StartAsync(async ctx => + { + var sw = new Stopwatch(); + foreach (Type problemType in LoadAllProblems(configuration.ProblemAssemblies)) + { + sw.Restart(); + // Since we're trying to instantiate them all, we don't want to show unrelated errors or render unrelated problem rows + // However, without the index, calculated once the constructor success, we can't separate those unrelated errors from + // our desired problems' ones. So there's a limitation when using this method if other constructors are failing + var potentialProblem = Activator.CreateInstance(problemType); + sw.Stop(); + + if (potentialProblem is BaseProblem problem && problemNumbers.Contains(problem.CalculateIndex())) + { + totalElapsedTime.Add(await SolveProblem(problem, table, CalculateElapsedMilliseconds(sw), configuration)); + ctx.Refresh(); + } + } + }); + + RenderOverallResultsPanel(totalElapsedTime, configuration); + } + + /// + /// Solves all problems in the assembly. + /// It also prints the elapsed time in and methods. + /// + /// + public static async Task SolveAll(Action? options = null) + { + var configuration = PopulateConfiguration(options); - private static object? InstantiateProblem(Type problemType) + if (IsInteractiveEnvironment && configuration.ClearConsole) { - try - { - return Activator.CreateInstance(problemType); - } - catch (Exception e) - { - return e.InnerException?.Message + Environment.NewLine + e.InnerException?.StackTrace; - } + AnsiConsole.Clear(); } - private static async Task SolveProblem(BaseProblem problem, Table table, double constructorElapsedTime, SolverConfiguration configuration) - { - var problemIndex = problem.CalculateIndex(); - var problemTitle = problemIndex != default - ? $"Day {problemIndex}" - : $"{problem.GetType().Name}"; + var totalElapsedTime = new List(); + var table = GetTable(); - if (configuration.ShowConstructorElapsedTime) + await AnsiConsole.Live(table) + .AutoClear(false) + .Overflow(configuration.VerticalOverflow) + .Cropping(configuration.VerticalOverflowCropping) + .StartAsync(async ctx => { - RenderRow(table, problemTitle, $"{problem.GetType().Name}()", "-----------", constructorElapsedTime, configuration); - } + var sw = new Stopwatch(); + foreach (Type problemType in LoadAllProblems(configuration.ProblemAssemblies)) + { + sw.Restart(); + var potentialProblem = InstantiateProblem(problemType); + sw.Stop(); - (string solution1, double elapsedMillisecondsPart1) = await SolvePart(isPart1: true, problem); - RenderRow(table, problemTitle, "Part 1", solution1, elapsedMillisecondsPart1, configuration); + if (potentialProblem is BaseProblem problem) + { + totalElapsedTime.Add(await SolveProblem(problem, table, CalculateElapsedMilliseconds(sw), configuration)); + ctx.Refresh(); + } + else + { + totalElapsedTime.Add(RenderEmptyProblem(problemType, potentialProblem as string, table, CalculateElapsedMilliseconds(sw), configuration)); + } + } + }); - (string solution2, double elapsedMillisecondsPart2) = await SolvePart(isPart1: false, problem); - RenderRow(table, problemTitle, "Part 2", solution2, elapsedMillisecondsPart2, configuration); + RenderOverallResultsPanel(totalElapsedTime, configuration); + } - if (configuration.ShowTotalElapsedTimePerDay) - { - RenderRow(table, problemTitle, "[bold]Total[/]", "-----------", constructorElapsedTime + elapsedMillisecondsPart1 + elapsedMillisecondsPart2, configuration); - } + /// + /// Loads all in the given assemblies + /// + /// + /// + internal static IEnumerable LoadAllProblems(List assemblies) + { + return assemblies.SelectMany(a => a.GetTypes()) + .Where(type => typeof(BaseProblem).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) + .OrderBy(t => t.FullName); + } - table.AddEmptyRow(); + private static SolverConfiguration PopulateConfiguration(Action? options) + { + var configuration = new SolverConfiguration(); + options?.Invoke(configuration); - return new ElapsedTime(constructorElapsedTime, elapsedMillisecondsPart1, elapsedMillisecondsPart2); - } + return configuration; + } - private static ElapsedTime RenderEmptyProblem(Type problemType, string? exceptionString, Table table, double constructorElapsedTime, SolverConfiguration configuration) + private static object? InstantiateProblem(Type problemType) + { + try { - var problemTitle = problemType.Name; + return Activator.CreateInstance(problemType); + } + catch (Exception e) + { + return e.InnerException?.Message + Environment.NewLine + e.InnerException?.StackTrace; + } + } - RenderRow(table, problemTitle, $"{problemTitle}()", exceptionString ?? "Unhandled exception during constructor", constructorElapsedTime, configuration); + private static async Task SolveProblem(BaseProblem problem, Table table, double constructorElapsedTime, SolverConfiguration configuration) + { + var problemIndex = problem.CalculateIndex(); + var problemTitle = problemIndex != default + ? $"Day {problemIndex}" + : $"{problem.GetType().Name}"; - const double elapsedMillisecondsPart1 = 0; - const double elapsedMillisecondsPart2 = 0; - RenderRow(table, problemTitle, "Part 1", "-----------", elapsedMillisecondsPart1, configuration); - RenderRow(table, problemTitle, "Part 2", "-----------", elapsedMillisecondsPart2, configuration); + if (configuration.ShowConstructorElapsedTime) + { + RenderRow(table, problemTitle, $"{problem.GetType().Name}()", "-----------", constructorElapsedTime, configuration); + } - if (configuration.ShowTotalElapsedTimePerDay) - { - RenderRow(table, problemTitle, "[bold]Total[/]", "-----------", constructorElapsedTime + elapsedMillisecondsPart1 + elapsedMillisecondsPart2, configuration); - } + (string solution1, double elapsedMillisecondsPart1) = await SolvePart(isPart1: true, problem); + RenderRow(table, problemTitle, "Part 1", solution1, elapsedMillisecondsPart1, configuration); - table.AddEmptyRow(); + (string solution2, double elapsedMillisecondsPart2) = await SolvePart(isPart1: false, problem); + RenderRow(table, problemTitle, "Part 2", solution2, elapsedMillisecondsPart2, configuration); - return new ElapsedTime(constructorElapsedTime, elapsedMillisecondsPart1, elapsedMillisecondsPart2); + if (configuration.ShowTotalElapsedTimePerDay) + { + RenderRow(table, problemTitle, "[bold]Total[/]", "-----------", constructorElapsedTime + elapsedMillisecondsPart1 + elapsedMillisecondsPart2, configuration); } - private static async Task<(string solution, double elapsedTime)> SolvePart(bool isPart1, BaseProblem problem) - { - Stopwatch stopwatch = new(); - var solution = string.Empty; + table.AddEmptyRow(); - try - { - Func> solve = isPart1 - ? problem.Solve_1 - : problem.Solve_2; + return new ElapsedTime(constructorElapsedTime, elapsedMillisecondsPart1, elapsedMillisecondsPart2); + } - stopwatch.Start(); - solution = await solve(); - } - catch (NotImplementedException) - { - solution = "[[Not implemented]]"; - } - catch (Exception e) - { - solution = e.Message + Environment.NewLine + e.StackTrace; - } - finally - { - stopwatch.Stop(); - } + private static ElapsedTime RenderEmptyProblem(Type problemType, string? exceptionString, Table table, double constructorElapsedTime, SolverConfiguration configuration) + { + var problemTitle = problemType.Name; - var elapsedMilliseconds = CalculateElapsedMilliseconds(stopwatch); + RenderRow(table, problemTitle, $"{problemTitle}()", exceptionString ?? "Unhandled exception during constructor", constructorElapsedTime, configuration); - return (solution, elapsedMilliseconds); - } + const double elapsedMillisecondsPart1 = 0; + const double elapsedMillisecondsPart2 = 0; + RenderRow(table, problemTitle, "Part 1", "-----------", elapsedMillisecondsPart1, configuration); + RenderRow(table, problemTitle, "Part 2", "-----------", elapsedMillisecondsPart2, configuration); - /// - /// http://geekswithblogs.net/BlackRabbitCoder/archive/2012/01/12/c.net-little-pitfalls-stopwatch-ticks-are-not-timespan-ticks.aspx - /// - /// - /// - private static double CalculateElapsedMilliseconds(Stopwatch stopwatch) + if (configuration.ShowTotalElapsedTimePerDay) { - return 1000 * stopwatch.ElapsedTicks / (double)Stopwatch.Frequency; + RenderRow(table, problemTitle, "[bold]Total[/]", "-----------", constructorElapsedTime + elapsedMillisecondsPart1 + elapsedMillisecondsPart2, configuration); } - private static string FormatTime(double elapsedMilliseconds, SolverConfiguration configuration, bool useColor = true) + table.AddEmptyRow(); + + return new ElapsedTime(constructorElapsedTime, elapsedMillisecondsPart1, elapsedMillisecondsPart2); + } + + private static async Task<(string solution, double elapsedTime)> SolvePart(bool isPart1, BaseProblem problem) + { + Stopwatch stopwatch = new(); + var solution = string.Empty; + + try { - var customFormatSpecifier = configuration?.ElapsedTimeFormatSpecifier; + Func> solve = isPart1 + ? problem.Solve_1 + : problem.Solve_2; - var message = customFormatSpecifier is null - ? elapsedMilliseconds switch - { - < 1 => $"{elapsedMilliseconds:F} ms", - < 1_000 => $"{Math.Round(elapsedMilliseconds)} ms", - < 60_000 => $"{0.001 * elapsedMilliseconds:F} s", - _ => $"{Math.Floor(elapsedMilliseconds / 60_000)} min {Math.Round(0.001 * (elapsedMilliseconds % 60_000))} s", - } - : elapsedMilliseconds switch - { - < 1 => $"{elapsedMilliseconds.ToString(customFormatSpecifier)} ms", - < 1_000 => $"{elapsedMilliseconds.ToString(customFormatSpecifier)} ms", - < 60_000 => $"{(0.001 * elapsedMilliseconds).ToString(customFormatSpecifier)} s", - _ => $"{elapsedMilliseconds / 60_000} min {(0.001 * (elapsedMilliseconds % 60_000)).ToString(customFormatSpecifier)} s", - }; + stopwatch.Start(); + solution = await solve(); + } + catch (NotImplementedException) + { + solution = "[[Not implemented]]"; + } + catch (Exception e) + { + solution = e.Message + Environment.NewLine + e.StackTrace; + } + finally + { + stopwatch.Stop(); + } + + var elapsedMilliseconds = CalculateElapsedMilliseconds(stopwatch); + + return (solution, elapsedMilliseconds); + } + + /// + /// http://geekswithblogs.net/BlackRabbitCoder/archive/2012/01/12/c.net-little-pitfalls-stopwatch-ticks-are-not-timespan-ticks.aspx + /// + /// + /// + private static double CalculateElapsedMilliseconds(Stopwatch stopwatch) + { + return 1000 * stopwatch.ElapsedTicks / (double)Stopwatch.Frequency; + } + + private static string FormatTime(double elapsedMilliseconds, SolverConfiguration configuration, bool useColor = true) + { + var customFormatSpecifier = configuration?.ElapsedTimeFormatSpecifier; - if (useColor) + var message = customFormatSpecifier is null + ? elapsedMilliseconds switch { - var color = elapsedMilliseconds switch - { - < 1 => Color.Blue, - < 10 => Color.Green1, - < 100 => Color.Lime, - < 500 => Color.GreenYellow, - < 1_000 => Color.Yellow1, - < 10_000 => Color.OrangeRed1, - _ => Color.Red1 - }; - - return $"[{color}]{message}[/]"; + < 1 => $"{elapsedMilliseconds:F} ms", + < 1_000 => $"{Math.Round(elapsedMilliseconds)} ms", + < 60_000 => $"{0.001 * elapsedMilliseconds:F} s", + _ => $"{Math.Floor(elapsedMilliseconds / 60_000)} min {Math.Round(0.001 * (elapsedMilliseconds % 60_000))} s", } - else + : elapsedMilliseconds switch { - return message; - } - } + < 1 => $"{elapsedMilliseconds.ToString(customFormatSpecifier)} ms", + < 1_000 => $"{elapsedMilliseconds.ToString(customFormatSpecifier)} ms", + < 60_000 => $"{(0.001 * elapsedMilliseconds).ToString(customFormatSpecifier)} s", + _ => $"{elapsedMilliseconds / 60_000} min {(0.001 * (elapsedMilliseconds % 60_000)).ToString(customFormatSpecifier)} s", + }; - private static Table GetTable() + if (useColor) { - return new Table() - .AddColumns( - "[bold]Day[/]", - "[bold]Part[/]", - "[bold]Solution[/]", - "[bold]Elapsed time[/]") - .RoundedBorder() - .BorderColor(Color.Grey); + var color = elapsedMilliseconds switch + { + < 1 => Color.Blue, + < 10 => Color.Green1, + < 100 => Color.Lime, + < 500 => Color.GreenYellow, + < 1_000 => Color.Yellow1, + < 10_000 => Color.OrangeRed1, + _ => Color.Red1 + }; + + return $"[{color}]{message}[/]"; } - - private static void RenderRow(Table table, string problemTitle, string part, string solution, double elapsedMilliseconds, SolverConfiguration configuration) + else { - var formattedTime = FormatTime(elapsedMilliseconds, configuration); - - table.AddRow(problemTitle, part, solution.EscapeMarkup(), formattedTime); + return message; } + } - private static void RenderOverallResultsPanel(List totalElapsedTime, SolverConfiguration configuration) - { - if (configuration?.ShowOverallResults != true || totalElapsedTime.Count <= 1) - { - return; - } + private static Table GetTable() + { + return new Table() + .AddColumns( + "[bold]Day[/]", + "[bold]Part[/]", + "[bold]Solution[/]", + "[bold]Elapsed time[/]") + .RoundedBorder() + .BorderColor(Color.Grey); + } - var totalConstructors = totalElapsedTime.Select(t => t.Constructor).Sum(); - var totalPart1 = totalElapsedTime.Select(t => t.Part1).Sum(); - var totalPart2 = totalElapsedTime.Select(t => t.Part2).Sum(); - var total = totalPart1 + totalPart2 + (configuration.ShowConstructorElapsedTime ? totalConstructors : 0); + private static void RenderRow(Table table, string problemTitle, string part, string solution, double elapsedMilliseconds, SolverConfiguration configuration) + { + var formattedTime = FormatTime(elapsedMilliseconds, configuration); - var grid = new Grid() - .AddColumn(new GridColumn().NoWrap().PadRight(4)) - .AddColumn() - .AddRow() - .AddRow($"[bold]Total ({totalElapsedTime.Count} days[/])", FormatTime(total, configuration, useColor: false)); + table.AddRow(problemTitle, part, solution.EscapeMarkup(), formattedTime); + } - if (configuration.ShowConstructorElapsedTime) - { - grid.AddRow("Total constructors", FormatTime(totalConstructors, configuration, useColor: false)); - } + private static void RenderOverallResultsPanel(List totalElapsedTime, SolverConfiguration configuration) + { + if (configuration?.ShowOverallResults != true || totalElapsedTime.Count <= 1) + { + return; + } - grid - .AddRow("Total parts 1", FormatTime(totalPart1, configuration, useColor: false)) - .AddRow("Total parts 2", FormatTime(totalPart2, configuration, useColor: false)) - .AddRow() - .AddRow("[bold]Mean (per day)[/]", FormatTime(total / totalElapsedTime.Count, configuration)); + var totalConstructors = totalElapsedTime.Select(t => t.Constructor).Sum(); + var totalPart1 = totalElapsedTime.Select(t => t.Part1).Sum(); + var totalPart2 = totalElapsedTime.Select(t => t.Part2).Sum(); + var total = totalPart1 + totalPart2 + (configuration.ShowConstructorElapsedTime ? totalConstructors : 0); - if (configuration.ShowConstructorElapsedTime) - { - grid.AddRow("Mean constructors", FormatTime(totalElapsedTime.Select(t => t.Constructor).Average(), configuration)); - } + var grid = new Grid() + .AddColumn(new GridColumn().NoWrap().PadRight(4)) + .AddColumn() + .AddRow() + .AddRow($"[bold]Total ({totalElapsedTime.Count} days[/])", FormatTime(total, configuration, useColor: false)); - grid - .AddRow("Mean parts 1", FormatTime(totalElapsedTime.Select(t => t.Part1).Average(), configuration)) - .AddRow("Mean parts 2", FormatTime(totalElapsedTime.Select(t => t.Part2).Average(), configuration)); + if (configuration.ShowConstructorElapsedTime) + { + grid.AddRow("Total constructors", FormatTime(totalConstructors, configuration, useColor: false)); + } + + grid + .AddRow("Total parts 1", FormatTime(totalPart1, configuration, useColor: false)) + .AddRow("Total parts 2", FormatTime(totalPart2, configuration, useColor: false)) + .AddRow() + .AddRow("[bold]Mean (per day)[/]", FormatTime(total / totalElapsedTime.Count, configuration)); - AnsiConsole.Write( - new Panel(grid) - .Header("[b] Overall results [/]", Justify.Center)); + if (configuration.ShowConstructorElapsedTime) + { + grid.AddRow("Mean constructors", FormatTime(totalElapsedTime.Select(t => t.Constructor).Average(), configuration)); } + + grid + .AddRow("Mean parts 1", FormatTime(totalElapsedTime.Select(t => t.Part1).Average(), configuration)) + .AddRow("Mean parts 2", FormatTime(totalElapsedTime.Select(t => t.Part2).Average(), configuration)); + + AnsiConsole.Write( + new Panel(grid) + .Header("[b] Overall results [/]", Justify.Center)); } } diff --git a/src/AoCHelper/SolverConfiguration.cs b/src/AoCHelper/SolverConfiguration.cs index fbeb83a..9ad3487 100644 --- a/src/AoCHelper/SolverConfiguration.cs +++ b/src/AoCHelper/SolverConfiguration.cs @@ -1,68 +1,67 @@ using System.Reflection; using Spectre.Console; -namespace AoCHelper +namespace AoCHelper; + +public class SolverConfiguration { - public class SolverConfiguration - { - /// - /// Clears previous runs information from the console. - /// True by default. - /// - public bool ClearConsole { get; set; } + /// + /// Clears previous runs information from the console. + /// True by default. + /// + public bool ClearConsole { get; set; } - /// - /// Shows a panel at the end of the run with aggregated stats of the solved problems. - /// True by default when solving multiple problems, false otherwise. - /// - public bool ShowOverallResults { get; set; } + /// + /// Shows a panel at the end of the run with aggregated stats of the solved problems. + /// True by default when solving multiple problems, false otherwise. + /// + public bool ShowOverallResults { get; set; } - /// - /// Shows the time elapsed during the instantiation of a . - /// This normally reflects the elapsed time while parsing the input data. - /// - public bool ShowConstructorElapsedTime { get; set; } + /// + /// Shows the time elapsed during the instantiation of a . + /// This normally reflects the elapsed time while parsing the input data. + /// + public bool ShowConstructorElapsedTime { get; set; } - /// - /// Shows total elapsed time per day. This includes constructor time + part 1 + part 2 - /// - public bool ShowTotalElapsedTimePerDay { get; set; } + /// + /// Shows total elapsed time per day. This includes constructor time + part 1 + part 2 + /// + public bool ShowTotalElapsedTimePerDay { get; set; } - /// - /// Custom numeric format strings used for elapsed millisecods. - /// See https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings - /// - public string? ElapsedTimeFormatSpecifier { get; set; } + /// + /// Custom numeric format strings used for elapsed millisecods. + /// See https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings + /// + public string? ElapsedTimeFormatSpecifier { get; set; } - /// - /// Assemblies where the problems are located. - /// Defaults to the entry assembly: [Assembly.GetEntryAssembly()!] - /// - public List ProblemAssemblies { get; set; } + /// + /// Assemblies where the problems are located. + /// Defaults to the entry assembly: [Assembly.GetEntryAssembly()!] + /// + public List ProblemAssemblies { get; set; } - /// - /// Represents vertical overflow. - /// - /// - internal VerticalOverflow VerticalOverflow { get; set; } + /// + /// Represents vertical overflow. + /// + /// + internal VerticalOverflow VerticalOverflow { get; set; } - /// - /// Represents vertical overflow cropping. - /// - /// - internal VerticalOverflowCropping VerticalOverflowCropping { get; set; } + /// + /// Represents vertical overflow cropping. + /// + /// + internal VerticalOverflowCropping VerticalOverflowCropping { get; set; } - public SolverConfiguration() - { - ProblemAssemblies = [Assembly.GetEntryAssembly()!]; + public SolverConfiguration() + { + ProblemAssemblies = [Assembly.GetEntryAssembly()!]; - ClearConsole = true; - ShowOverallResults = true; - ShowConstructorElapsedTime = false; - ShowTotalElapsedTimePerDay = false; + ClearConsole = true; + ShowOverallResults = true; + ShowConstructorElapsedTime = false; + ShowTotalElapsedTimePerDay = false; - VerticalOverflow = VerticalOverflow.Ellipsis; - VerticalOverflowCropping = VerticalOverflowCropping.Top; - } + VerticalOverflow = VerticalOverflow.Ellipsis; + VerticalOverflowCropping = VerticalOverflowCropping.Top; } } \ No newline at end of file diff --git a/src/AoCHelper/SolvingException.cs b/src/AoCHelper/SolvingException.cs index 5690eed..ba2dbd5 100644 --- a/src/AoCHelper/SolvingException.cs +++ b/src/AoCHelper/SolvingException.cs @@ -1,17 +1,16 @@ -namespace AoCHelper +namespace AoCHelper; + +public class SolvingException : Exception { - public class SolvingException : Exception + public SolvingException() { - public SolvingException() - { - } + } - public SolvingException(string message) : base(message) - { - } + public SolvingException(string message) : base(message) + { + } - public SolvingException(string message, Exception innerException) : base(message, innerException) - { - } + public SolvingException(string message, Exception innerException) : base(message, innerException) + { } } diff --git a/tests/AoCHelper.Test/ModifyInputFileDirPath_SampleTests.cs b/tests/AoCHelper.Test/ModifyInputFileDirPath_SampleTests.cs index b6180cd..a31b9a4 100644 --- a/tests/AoCHelper.Test/ModifyInputFileDirPath_SampleTests.cs +++ b/tests/AoCHelper.Test/ModifyInputFileDirPath_SampleTests.cs @@ -1,6 +1,7 @@ -using AoCHelper; using NUnit.Framework; +namespace AoCHelper.Test; + public class ModifyInputFileDirPathTests { abstract class CustomDirBaseDay : BaseDay diff --git a/tests/AoCHelper.Test/ModifyInputFilePathTests_SampleTests.cs b/tests/AoCHelper.Test/ModifyInputFilePathTests_SampleTests.cs index 87a0328..b46e3e2 100644 --- a/tests/AoCHelper.Test/ModifyInputFilePathTests_SampleTests.cs +++ b/tests/AoCHelper.Test/ModifyInputFilePathTests_SampleTests.cs @@ -1,6 +1,7 @@ -using AoCHelper; using NUnit.Framework; +namespace AoCHelper.Test; + public class ModifyInputFilePathTests { abstract class CustomInputPathBaseDay : BaseDay @@ -20,7 +21,7 @@ class Day_99 : CustomInputPathBaseDay public override ValueTask Solve_2() => new($"No longer useful InputFileDirPath: {InputFileDirPath}"); } - [TestCase(typeof(Day_99), "TestInputs/Day_1234.txt", "Custom file path: TestInputs/Day_1234.txt", $"No longer useful InputFileDirPath: Inputs")] + [TestCase(typeof(Day_99), "TestInputs/Day_1234.txt", "Custom file path: TestInputs/Day_1234.txt", "No longer useful InputFileDirPath: Inputs")] public async Task ModifyInputFilePath(Type type, string inputFilePath, string sol1, string sol2) { if (Activator.CreateInstance(type) is CustomInputPathBaseDay instance) diff --git a/tests/AoCHelper.Test/OverrideClassPrefixTests.cs b/tests/AoCHelper.Test/OverrideClassPrefixTests.cs index a21a93a..dfeb5ca 100644 --- a/tests/AoCHelper.Test/OverrideClassPrefixTests.cs +++ b/tests/AoCHelper.Test/OverrideClassPrefixTests.cs @@ -1,33 +1,32 @@ using Xunit; -namespace AoCHelper.Test +namespace AoCHelper.Test; + +[Collection("Sequential")] +public class OverrideClassPrefixTests { - [Collection("Sequential")] - public class OverrideClassPrefixTests + private abstract class GreatName : BaseProblem { - private abstract class GreatName : BaseProblem - { - protected override string ClassPrefix => nameof(GreatName); - } + protected override string ClassPrefix => nameof(GreatName); + } - private class GreatName01 : GreatName { public override ValueTask Solve_1() => new(""); public override ValueTask Solve_2() => new(""); } - private class GreatName_01 : GreatName { public override ValueTask Solve_1() => new(""); public override ValueTask Solve_2() => new(""); } + private class GreatName01 : GreatName { public override ValueTask Solve_1() => new(""); public override ValueTask Solve_2() => new(""); } + private class GreatName_01 : GreatName { public override ValueTask Solve_1() => new(""); public override ValueTask Solve_2() => new(""); } - [Fact] - public async Task OverrideClassPrefix() - { - await Solver.Solve(); - await Solver.Solve(); - } + [Fact] + public async Task OverrideClassPrefix() + { + await Solver.Solve(); + await Solver.Solve(); + } - private class Day11 : BaseDay { public override ValueTask Solve_1() => new(""); public override ValueTask Solve_2() => new(""); } - private class Day_11 : BaseDay { public override ValueTask Solve_1() => new(""); public override ValueTask Solve_2() => new(""); } + private class Day11 : BaseDay { public override ValueTask Solve_1() => new(""); public override ValueTask Solve_2() => new(""); } + private class Day_11 : BaseDay { public override ValueTask Solve_1() => new(""); public override ValueTask Solve_2() => new(""); } - [Fact] - public async Task BaseDay() - { - await Solver.Solve(); - await Solver.Solve(); - } + [Fact] + public async Task BaseDay() + { + await Solver.Solve(); + await Solver.Solve(); } } diff --git a/tests/AoCHelper.Test/OverrideIndexTests.cs b/tests/AoCHelper.Test/OverrideIndexTests.cs index 1569e30..6586fa7 100644 --- a/tests/AoCHelper.Test/OverrideIndexTests.cs +++ b/tests/AoCHelper.Test/OverrideIndexTests.cs @@ -1,34 +1,33 @@ using Xunit; -namespace AoCHelper.Test +namespace AoCHelper.Test; + +[Collection("Sequential")] +public class OverrideIndexTests { - [Collection("Sequential")] - public class OverrideIndexTests + private abstract class ProblemFixture : BaseProblem { - private abstract class ProblemFixture : BaseProblem - { - public override ValueTask Solve_1() => Solve(); + public override ValueTask Solve_1() => Solve(); - public override ValueTask Solve_2() => Solve(); + public override ValueTask Solve_2() => Solve(); - private ValueTask Solve() + private ValueTask Solve() + { + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new FileNotFoundException(InputFilePath); - } - - return new(string.Empty); + throw new FileNotFoundException(InputFilePath); } + + return new(string.Empty); } + } - private class CustomProblem : ProblemFixture { public override uint CalculateIndex() => 69; } + private class CustomProblem : ProblemFixture { public override uint CalculateIndex() => 69; } - [Fact] - public async Task OverrideIndex() - { - await Solver.Solve(); - await Solver.Solve(); - } + [Fact] + public async Task OverrideIndex() + { + await Solver.Solve(); + await Solver.Solve(); } } diff --git a/tests/AoCHelper.Test/OverrideInputFileDirPathTests.cs b/tests/AoCHelper.Test/OverrideInputFileDirPathTests.cs index 8c8829d..3e40c87 100644 --- a/tests/AoCHelper.Test/OverrideInputFileDirPathTests.cs +++ b/tests/AoCHelper.Test/OverrideInputFileDirPathTests.cs @@ -1,37 +1,36 @@ using Xunit; -namespace AoCHelper.Test +namespace AoCHelper.Test; + +[Collection("Sequential")] +public class OverrideInputFileDirPathTests { - [Collection("Sequential")] - public class OverrideInputFileDirPathTests + private abstract class BaseProblemFixture : BaseProblem { - private abstract class BaseProblemFixture : BaseProblem - { - protected override string InputFileExtension => nameof(OverrideInputFileDirPathTests); + protected override string InputFileExtension => nameof(OverrideInputFileDirPathTests); - public override ValueTask Solve_1() => Solve(); + public override ValueTask Solve_1() => Solve(); - public override ValueTask Solve_2() => Solve(); + public override ValueTask Solve_2() => Solve(); - private ValueTask Solve() + private ValueTask Solve() + { + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new FileNotFoundException(InputFilePath); - } - - return new(string.Empty); + throw new FileNotFoundException(InputFilePath); } + + return new(string.Empty); } + } - private class Problem33 : BaseProblemFixture { protected override string InputFileDirPath { get; } = "AlternativeInputs"; } - private class Problem_33 : BaseProblemFixture { protected override string InputFileDirPath { get; } = "AlternativeInputs"; } + private class Problem33 : BaseProblemFixture { protected override string InputFileDirPath { get; } = "AlternativeInputs"; } + private class Problem_33 : BaseProblemFixture { protected override string InputFileDirPath { get; } = "AlternativeInputs"; } - [Fact] - public async Task OverrideInputFilePathDir() - { - await Solver.Solve(); - await Solver.Solve(); - } + [Fact] + public async Task OverrideInputFilePathDir() + { + await Solver.Solve(); + await Solver.Solve(); } } diff --git a/tests/AoCHelper.Test/OverrideInputFileExtensionTests.cs b/tests/AoCHelper.Test/OverrideInputFileExtensionTests.cs index bc1a136..83cac3d 100644 --- a/tests/AoCHelper.Test/OverrideInputFileExtensionTests.cs +++ b/tests/AoCHelper.Test/OverrideInputFileExtensionTests.cs @@ -1,35 +1,34 @@ using Xunit; -namespace AoCHelper.Test +namespace AoCHelper.Test; + +[Collection("Sequential")] +public class OverrideInputFileExtensionTests { - [Collection("Sequential")] - public class OverrideInputFileExtensionTests + private abstract class BaseProblemFixture : BaseProblem { - private abstract class BaseProblemFixture : BaseProblem - { - public override ValueTask Solve_1() => Solve(); + public override ValueTask Solve_1() => Solve(); - public override ValueTask Solve_2() => Solve(); + public override ValueTask Solve_2() => Solve(); - private ValueTask Solve() + private ValueTask Solve() + { + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new FileNotFoundException(InputFilePath); - } - - return new(string.Empty); + throw new FileNotFoundException(InputFilePath); } + + return new(string.Empty); } + } - private class Problem01 : BaseProblemFixture { protected override string InputFileExtension => nameof(OverrideInputFileExtensionTests); } - private class Problem_01 : BaseProblemFixture { protected override string InputFileExtension => nameof(OverrideInputFileExtensionTests); } + private class Problem01 : BaseProblemFixture { protected override string InputFileExtension => nameof(OverrideInputFileExtensionTests); } + private class Problem_01 : BaseProblemFixture { protected override string InputFileExtension => nameof(OverrideInputFileExtensionTests); } - [Fact] - public async Task OverrideInputFileExtension() - { - await Solver.Solve(); - await Solver.Solve(); - } + [Fact] + public async Task OverrideInputFileExtension() + { + await Solver.Solve(); + await Solver.Solve(); } } diff --git a/tests/AoCHelper.Test/OverrideInputFilePathTests.cs b/tests/AoCHelper.Test/OverrideInputFilePathTests.cs index eafb94b..4951538 100644 --- a/tests/AoCHelper.Test/OverrideInputFilePathTests.cs +++ b/tests/AoCHelper.Test/OverrideInputFilePathTests.cs @@ -1,35 +1,34 @@ using Xunit; -namespace AoCHelper.Test +namespace AoCHelper.Test; + +[Collection("Sequential")] +public class OverrideInputFilePathTests { - [Collection("Sequential")] - public class OverrideInputFilePathTests + private abstract class BaseProblemFixture : BaseProblem { - private abstract class BaseProblemFixture : BaseProblem - { - protected override string InputFileExtension => nameof(OverrideInputFilePathTests); + protected override string InputFileExtension => nameof(OverrideInputFilePathTests); - public override ValueTask Solve_1() => Solve(); + public override ValueTask Solve_1() => Solve(); - public override ValueTask Solve_2() => Solve(); + public override ValueTask Solve_2() => Solve(); - private ValueTask Solve() + private ValueTask Solve() + { + if (!File.Exists(InputFilePath)) { - if (!File.Exists(InputFilePath)) - { - throw new FileNotFoundException(InputFilePath); - } - - return new(string.Empty); + throw new FileNotFoundException(InputFilePath); } + + return new(string.Empty); } + } - private class CustomProblem : BaseProblemFixture { public override string InputFilePath => $"AlternativeInputs/43.{nameof(OverrideInputFilePathTests)}"; } + private class CustomProblem : BaseProblemFixture { public override string InputFilePath => $"AlternativeInputs/43.{nameof(OverrideInputFilePathTests)}"; } - [Fact] - public async Task OverrideInputFilePath() - { - await Solver.Solve(); - } + [Fact] + public async Task OverrideInputFilePath() + { + await Solver.Solve(); } } diff --git a/tests/AoCHelper.Test/SolverTest.cs b/tests/AoCHelper.Test/SolverTest.cs index 8d9ea39..6bbde53 100644 --- a/tests/AoCHelper.Test/SolverTest.cs +++ b/tests/AoCHelper.Test/SolverTest.cs @@ -5,20 +5,17 @@ namespace AdventOfCode.Days._01 { - class Day01 : AoCHelper.Test.SolverTest.ProblemFixture { } - + class Day01 : AoCHelper.Test.SolverTest.ProblemFixture; } namespace AdventOfCode.Days._02 { - class Day02 : AoCHelper.Test.SolverTest.ProblemFixture { } - + class Day02 : AoCHelper.Test.SolverTest.ProblemFixture; } namespace AdventOfCode.Days._10 { - class Day10 : AoCHelper.Test.SolverTest.ProblemFixture { } - + class Day10 : AoCHelper.Test.SolverTest.ProblemFixture; } namespace AoCHelper.Test @@ -43,7 +40,7 @@ private ValueTask Solve() } } - private class Problem66 : ProblemFixture { } + private class Problem66 : ProblemFixture; private class IllCreatedCustomProblem : ProblemFixture {