implement level1 and level2

This commit is contained in:
Simon Diesenreiter 2024-12-02 18:51:49 +01:00
parent f7f3a2b2a0
commit 377ef9afe8
15 changed files with 3708 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
**/bin
**/obj
.gitea/conventional_commits/tmp/*

14
Common/Common.csproj Normal file
View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LevelInputProvider" Version="*" />
<PackageReference Include="TextParser" Version="*" />
</ItemGroup>
</Project>

37
Common/LevelSolverBase.cs Normal file
View File

@ -0,0 +1,37 @@
namespace AoC24.Common;
using AoCLevelInputProvider;
using Parsing;
using Parsing.Schema;
using Parsing.Tokenization;
public abstract class LevelSolverBase
{
public abstract int LevelNumber { get; }
private int Year = 2024;
private LevelInputProvider inputProvider = new LevelInputProvider();
protected string GetLevelInput()
{
return inputProvider.WithYear(this.Year).WithLevel(this.LevelNumber).Provide();
}
public abstract string SolveFirstStar();
public abstract string SolveSecondStar();
protected abstract InputSchemaBuilder DefineInputSchema(InputSchemaBuilder schemaBuilder);
public TokenConverter GetData()
{
var schemaBuilder = new InputSchemaBuilder();
var schema = this.DefineInputSchema(schemaBuilder).Build();
var parser = new TextParser(schema);
var data = parser
.SetInputText(this.GetLevelInput())
.Parse();
return data;
}
}

3096
Common/msbuild.log Normal file

File diff suppressed because one or more lines are too long

14
Level1/Level1.csproj Normal file
View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Common/Common.csproj" />
</ItemGroup>
</Project>

74
Level1/Level1Solver.cs Normal file
View File

@ -0,0 +1,74 @@
namespace AoC24;
using AoC24.Common;
using AoCLevelInputProvider;
using Parsing;
using Parsing.Schema;
public class Level1Solver : LevelSolverBase
{
public override int LevelNumber
{
get { return 1; }
}
protected override InputSchemaBuilder DefineInputSchema(InputSchemaBuilder schemaBuilder)
{
return schemaBuilder
.Repeat(2)
.Expect(InputType.Integer)
.EndRepetition();
}
public override string SolveFirstStar()
{
var data = this.GetData()
.AsListColumns<int>();
var sortedLeftColumn = data[0];
sortedLeftColumn.Sort();
var sortedRightColumn = data[1];
sortedRightColumn.Sort();
var sumDifferences = 0;
for (var i = 0; i < sortedLeftColumn.Count; i++)
{
sumDifferences += Math.Abs(sortedLeftColumn[i]-sortedRightColumn[i]);
}
return sumDifferences.ToString();
}
public override string SolveSecondStar()
{
var data = this.GetData()
.AsListColumns<int>();
var uniqueLeftColumn = data[0].Distinct();
var rightColumn = data[1];
Dictionary<int, int> numInstances = new Dictionary<int, int>();
foreach(var value in uniqueLeftColumn)
{
numInstances[value] = 0;
}
foreach(var value in rightColumn)
{
if (numInstances.ContainsKey(value))
{
numInstances[value] = numInstances[value] + 1;
}
}
var sumScore = 0;
foreach(var key in numInstances.Keys)
{
sumScore = sumScore + numInstances[key] * key;
}
return sumScore.ToString();
}
}

24
Level1/Program.cs Normal file
View File

@ -0,0 +1,24 @@
// See https://aka.ms/new-console-template for more information
using AoC24;
var levelSolver = new Level1Solver();
var solution1 = levelSolver.SolveFirstStar();
var solution2 = levelSolver.SolveSecondStar();
if (!string.IsNullOrEmpty(solution1))
{
Console.WriteLine("Solution for example 1 is: " + solution1);
}
else
{
Console.WriteLine("Example 1 has not been solved yet!");
}
if (!string.IsNullOrEmpty(solution2))
{
Console.WriteLine("Solution for example 2 is: " + solution2);
}
else
{
Console.WriteLine("Example 2 has not been solved yet!");
}

View File

@ -0,0 +1,212 @@
namespace AoC24;
using AoC24.Common;
using AoCLevelInputProvider;
using Parsing;
using Parsing.Schema;
public class DataStreamConstraintVerifier<T>
{
public interface IIntegerDataStreamConstraint
{
public int Verify(T leftValue, T rightValue);
}
private class DefaultTrendChecker : IIntegerDataStreamConstraint
{
public int Verify(T leftValue, T rightValue)
{
return string.Compare(leftValue?.ToString(), rightValue?.ToString()) < 0 ? 1 : -1;
}
}
private class DefaultDistanceChecker : IIntegerDataStreamConstraint
{
public int Verify(T leftValue, T rightValue)
{
return string.Compare(leftValue?.ToString(), rightValue?.ToString());
}
}
private List<T> dataSet = new List<T>();
private int distanceLowerLimit = 0;
private int distanceUpperLimit = 1;
private int faultTolerance = int.MaxValue;
private int allowedIgnores = 0;
private int numFaults = 0;
private bool checkFaults = false;
private IIntegerDataStreamConstraint trendChecker = new DefaultTrendChecker();
private IIntegerDataStreamConstraint distanceChecker = new DefaultDistanceChecker();
// compute a trend value between two items in the dataset
// the same trend has to be returned by all checks for the constraint to pass
public DataStreamConstraintVerifier<T> WithTrendChecker(IIntegerDataStreamConstraint trendChecker)
{
this.trendChecker = trendChecker;
return this;
}
public DataStreamConstraintVerifier<T> OnData(List<T> dataSet)
{
this.dataSet = dataSet;
return this;
}
// set number of faults that get ignored until check is considered failed
public DataStreamConstraintVerifier<T> WithFaultTolerance(int faultTolerance)
{
if(this.allowedIgnores > 0)
{
throw new Exception("Cannot enable fault tolerance along with allowed ignores!");
}
this.faultTolerance = faultTolerance;
this.checkFaults = true;
return this;
}
// set number of faults that get ignored until check is considered failed
public DataStreamConstraintVerifier<T> WithAllowedIgnores(int allowedIgnores)
{
if(this.checkFaults)
{
throw new Exception("Cannot enable allowed ignores along with fault tolerance!");
}
this.allowedIgnores = allowedIgnores;
return this;
}
// set specific limits for the distance checks
public DataStreamConstraintVerifier<T> WithDistanceLimits(int lowerLimit, int upperLimit)
{
this.distanceLowerLimit = lowerLimit;
this.distanceUpperLimit = upperLimit;
return this;
}
// compute a distance value between two items in the dataset
// the distance returned by all checks has to be within distance limits for constraint to pass
public DataStreamConstraintVerifier<T> WithDistanceChecker(IIntegerDataStreamConstraint distanceChecker)
{
this.distanceChecker = distanceChecker;
return this;
}
private int TranslateIndex(int index, List<int> indicesToIgnore)
{
var newIndex = index;
foreach(var value in indicesToIgnore)
{
if(value <= index)
{
newIndex++;
}
}
return newIndex;
}
private bool VerifyDistance(int remainingAllowedIgnores, List<int> indicesToIgnore)
{
for(int i = 0; i < (this.dataSet.Count-indicesToIgnore.Count-1); i++)
{
T comparisonLeft = this.dataSet[TranslateIndex(i,indicesToIgnore)];
T comparisonRight = this.dataSet[TranslateIndex(i+1,indicesToIgnore)];
int distance = this.distanceChecker.Verify(comparisonLeft, comparisonRight);
if(distance < this.distanceLowerLimit || distance > this.distanceUpperLimit)
{
this.numFaults++;
// small optimization:
if (!this.checkFaults || this.numFaults > this.faultTolerance)
{
return false;
}
}
}
return true;
}
private bool VerifyTrend(int remainingAllowedIgnores, List<int> indicesToIgnore)
{
T firstComparisonLeft = this.dataSet[TranslateIndex(0,indicesToIgnore)];
T firstComparisonRight = this.dataSet[TranslateIndex(1,indicesToIgnore)];
int initialTrend = this.trendChecker.Verify(firstComparisonLeft, firstComparisonRight);
for(int i = 0; i < (this.dataSet.Count-indicesToIgnore.Count-1); i++)
{
T comparisonLeft = this.dataSet[TranslateIndex(i,indicesToIgnore)];
T comparisonRight = this.dataSet[TranslateIndex(i+1,indicesToIgnore)];
int trend = this.trendChecker.Verify(comparisonLeft, comparisonRight);
if(initialTrend != trend)
{
this.numFaults++;
// small optimization:
if (!this.checkFaults || this.numFaults > this.faultTolerance)
{
return false;
}
}
}
return true;
}
private bool Verify(int remainingAllowedIgnores, List<int> indicesToIgnore)
{
if(remainingAllowedIgnores > 0)
{
bool success = this.Verify(0, indicesToIgnore);
if (success)
{
return true;
}
for(int i = 0; i < this.dataSet.Count; i++)
{
if(indicesToIgnore.Contains(i))
{
continue;
}
List<int> newIndicesToIgnore = new List<int>();
newIndicesToIgnore.AddRange(indicesToIgnore);
newIndicesToIgnore.Add(i);
success = this.Verify(remainingAllowedIgnores-1, newIndicesToIgnore);
if (success)
{
return true;
}
}
return false;
}
else
{
this.numFaults = 0;
bool success = true;
if(this.distanceChecker != null)
{
success = this.VerifyDistance(this.allowedIgnores, indicesToIgnore);
}
if (!success || (this.checkFaults && this.numFaults > this.faultTolerance))
{
return false;
}
if(this.trendChecker != null)
{
success = this.VerifyTrend(this.allowedIgnores, indicesToIgnore);
}
if (this.checkFaults && this.numFaults > this.faultTolerance)
{
return false;
}
return success;
}
}
public bool Verify()
{
return this.Verify(this.allowedIgnores, new List<int>());
}
}

14
Level2/Level2.csproj Normal file
View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Common/Common.csproj" />
</ItemGroup>
</Project>

93
Level2/Level2Solver.cs Normal file
View File

@ -0,0 +1,93 @@
namespace AoC24;
using AoC24.Common;
using AoCLevelInputProvider;
using Parsing;
using Parsing.Schema;
public class Level2Solver : LevelSolverBase
{
public override int LevelNumber
{
get { return 2; }
}
protected override InputSchemaBuilder DefineInputSchema(InputSchemaBuilder schemaBuilder)
{
return schemaBuilder
.Repeat()
.Expect(InputType.Integer)
.EndRepetition();
}
private class MyDistanceChecker : DataStreamConstraintVerifier<int>.IIntegerDataStreamConstraint
{
public int Verify(int leftValue, int rightValue)
{
return Math.Abs(leftValue - rightValue);
}
}
private class MyTrendChecker : DataStreamConstraintVerifier<int>.IIntegerDataStreamConstraint
{
public int Verify(int leftValue, int rightValue)
{
return (leftValue - rightValue) < 0 ? -1 : 1;
}
}
public override string SolveFirstStar()
{
var data = this.GetData()
.AsListRows<int>();
var distanceChecker = new MyDistanceChecker();
var trendChecker = new MyTrendChecker();
var verifier = new DataStreamConstraintVerifier<int>()
.WithDistanceChecker(distanceChecker)
.WithDistanceLimits(1, 3)
.WithTrendChecker(trendChecker);
int numSafeReports = 0;
foreach(var row in data)
{
if(verifier.OnData(row).Verify())
{
numSafeReports++;
}
}
return numSafeReports.ToString();
}
public override string SolveSecondStar()
{
var data = this.GetData()
.AsListRows<int>();
var distanceChecker = new MyDistanceChecker();
var trendChecker = new MyTrendChecker();
var verifier = new DataStreamConstraintVerifier<int>()
.WithDistanceChecker(distanceChecker)
.WithDistanceLimits(1, 3)
.WithTrendChecker(trendChecker)
.WithAllowedIgnores(1);
int numSafeReports = 0;
foreach(var row in data)
{
if(verifier.OnData(row).Verify())
{
numSafeReports++;
}
}
return numSafeReports.ToString();
}
}

24
Level2/Program.cs Normal file
View File

@ -0,0 +1,24 @@
// See https://aka.ms/new-console-template for more information
using AoC24;
var levelSolver = new Level2Solver();
var solution1 = levelSolver.SolveFirstStar();
var solution2 = levelSolver.SolveSecondStar();
if (!string.IsNullOrEmpty(solution1))
{
Console.WriteLine("Solution for example 1 is: " + solution1);
}
else
{
Console.WriteLine("Example 1 has not been solved yet!");
}
if (!string.IsNullOrEmpty(solution2))
{
Console.WriteLine("Solution for example 2 is: " + solution2);
}
else
{
Console.WriteLine("Example 2 has not been solved yet!");
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Common/Common.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,43 @@
namespace AoC24;
using AoC24.Common;
using AoCLevelInputProvider;
using Parsing;
using Parsing.Schema;
public class LevelXSolver : LevelSolverBase
{
public override int LevelNumber
{
// TODO: update level number, csproj file and rename class!
get { return 1; }
}
protected override InputSchemaBuilder DefineInputSchema(InputSchemaBuilder schemaBuilder)
{
return schemaBuilder
.Repeat()
.Expect(InputType.Integer)
.EndRepetition();
}
public override string SolveFirstStar()
{
var data = this.GetData()
.AsListRows<int>();
// TODO: implement
return string.Empty;
}
public override string SolveSecondStar()
{
var data = this.GetData()
.AsListRows<int>();
// TODO: implement
return string.Empty;
}
}

24
LevelTemplate/Program.cs Normal file
View File

@ -0,0 +1,24 @@
// See https://aka.ms/new-console-template for more information
using AoC24;
var levelSolver = new LevelXSolver();
var solution1 = levelSolver.SolveFirstStar();
var solution2 = levelSolver.SolveSecondStar();
if (!string.IsNullOrEmpty(solution1))
{
Console.WriteLine("Solution for example 1 is: " + solution1);
}
else
{
Console.WriteLine("Example 1 has not been solved yet!");
}
if (!string.IsNullOrEmpty(solution2))
{
Console.WriteLine("Solution for example 2 is: " + solution2);
}
else
{
Console.WriteLine("Example 2 has not been solved yet!");
}

22
nuget.config Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<clear />
<add key="gitea-projects" value="https://git.disi.dev/api/packages/Projects/nuget/index.json" />
<add key="gitea-homelab" value="https://git.disi.dev/api/packages/Homelab/nuget/index.json" />
<add key="gitea-artifacts" value="https://git.disi.dev/api/packages/Artifacts/nuget/index.json" />
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<!-- ensure TextParser is pulled from private feed - there are public conflicts-->
<packageSource key="nuget">
<package pattern="*" />
</packageSource>
<packageSource key="gitea-projects">
<package pattern="TextParser" />
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>