Merge pull request #2 from StevanFreeborn/stevanfreeborn/feat/solve-puzzle-2

feat: solve puzzle 2
This commit is contained in:
Stevan Freeborn
2023-12-02 12:50:04 -06:00
committed by GitHub
14 changed files with 767 additions and 29 deletions
+2 -1
View File
@@ -1,2 +1,3 @@
global using Xunit;
global using FluentAssertions;
global using Xunit;
@@ -36,6 +36,6 @@ public class PartTwoPuzzleSolverTests
{
var input = await File.ReadAllLinesAsync("INPUT.txt");
var result = _sut.SumCalibrationValues(input);
result.Should().BeLessThan(54249);
result.Should().Be(54249);
}
}
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CubeConundrum\CubeConundrum.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="..\INPUT.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
+52
View File
@@ -0,0 +1,52 @@
namespace CubeConundrum.Tests;
public class GameTests
{
[Theory, MemberData(nameof(TestData.Games), MemberType = typeof(TestData))]
public void PowerOfMinimumCubesNeeded_GivenAGame_ItShouldReturnTheSumOfTheMinimumCubesNeededForEachResult(Game game, int expected)
{
game.PowerOfMinimumCubesNeeded.Should().Be(expected);
}
public static class TestData
{
public static readonly IEnumerable<object[]> Games = new List<object[]>
{
new object[]
{
new Game
{
Id = 1,
Results =
{
new()
{
Cubes =
{
new() { Count = 3, Color = CubeColor.Blue },
new() { Count = 4, Color = CubeColor.Red }
}
},
new()
{
Cubes =
{
new() { Count = 1, Color = CubeColor.Red },
new() { Count = 2, Color = CubeColor.Green },
new() { Count = 6, Color = CubeColor.Blue }
}
},
new()
{
Cubes =
{
new() { Count = 2, Color = CubeColor.Green },
}
}
}
},
48
}
};
}
}
+3
View File
@@ -0,0 +1,3 @@
global using FluentAssertions;
global using Xunit;
+18
View File
@@ -0,0 +1,18 @@
namespace CubeConundrum.Tests;
public class ProgramTests
{
[Fact]
public async Task Main_GivenInput_ItShouldReturnExpectedSum()
{
var result = await Program.Main(["INPUT.txt"]);
result.Should().Be(2879);
}
[Fact]
public async Task Main_GivenInputAndPart2_ItShouldReturnExpectedSum()
{
var result = await Program.Main(["INPUT.txt", "part2"]);
result.Should().Be(65122);
}
}
+159
View File
@@ -0,0 +1,159 @@
namespace CubeConundrum.Tests;
public class PuzzleSolverTests
{
private readonly PuzzleSolver _sut = new();
[Theory, MemberData(nameof(TestData.Results), MemberType = typeof(TestData))]
public void IsResultPossible_GivenAResult_ItShouldReturnExpectedOutcome(Result givenResult, bool expected)
{
var result = _sut.IsResultPossible(givenResult);
result.Should().Be(expected);
}
[Theory, MemberData(nameof(TestData.Games), MemberType = typeof(TestData))]
public void IsGamePossible_GivenAGame_ItShouldReturnExpectedOutcome(Game givenGame, bool expected)
{
var result = _sut.IsGamePossible(givenGame);
result.Should().Be(expected);
}
[Fact]
public void SumPossibleGameIds_GivenAGameCollection_ItShouldReturnTheSumOfTheIdsOfAllPossibleGames()
{
var games = TestData.Games.Select(o => (Game)o[0]);
var result = _sut.SumPossibleGameIds(games);
result.Should().Be(1);
}
public static class TestData
{
public static readonly IEnumerable<object[]> Games = new List<object[]>
{
new object[]
{
new Game
{
Id = 1,
Results =
{
new()
{
Cubes =
{
new() { Count = 3, Color = CubeColor.Blue },
new() { Count = 4, Color = CubeColor.Red }
}
},
new()
{
Cubes =
{
new() { Count = 1, Color = CubeColor.Red },
new() { Count = 2, Color = CubeColor.Green },
new() { Count = 6, Color = CubeColor.Blue }
}
},
new()
{
Cubes =
{
new() { Count = 2, Color = CubeColor.Green }
}
}
}
},
true
},
new object[]
{
new Game
{
Id = 2,
Results =
{
new()
{
Cubes =
{
new() { Count = 3, Color = CubeColor.Blue },
new() { Count = 4, Color = CubeColor.Red }
}
},
new()
{
Cubes =
{
new() { Count = 1, Color = CubeColor.Red },
new() { Count = 2, Color = CubeColor.Green },
new() { Count = 6, Color = CubeColor.Blue }
}
},
new()
{
Cubes =
{
new() { Count = 40, Color = CubeColor.Green }
}
},
}
},
false
},
};
public static readonly IEnumerable<object[]> Results = new List<object[]>
{
new object[]
{
new Result
{
Cubes =
{
new() { Count = 4, Color = CubeColor.Red },
new() { Count = 1, Color = CubeColor.Green },
new() { Count = 2, Color = CubeColor.Blue }
}
},
true
},
new object[]
{
new Result
{
Cubes =
{
new() { Count = 13, Color = CubeColor.Red },
new() { Count = 14, Color = CubeColor.Green },
new() { Count = 14, Color = CubeColor.Blue },
}
},
false
},
new object[]
{
new Result
{
Cubes =
{
new() { Count = 13, Color = CubeColor.Red },
new() { Count = 5, Color = CubeColor.Green },
new() { Count = 5, Color = CubeColor.Blue },
}
},
false
},
new object[]
{
new Result
{
Cubes =
{
new() { Count = 40, Color = CubeColor.Red },
}
},
false
}
};
}
}
@@ -0,0 +1,93 @@
namespace CubeConundrum.Tests;
public class PuzzleParserTests
{
private readonly PuzzleParser _sut = new();
[Theory, MemberData(nameof(TestData.CubeStrings), MemberType = typeof(TestData))]
public void ParseCube_GivenAStringThatRepresentsACube_ItShouldReturnTheEquivalentCubeModel(string input, CubeCollection expected)
{
var result = _sut.ParseCube(input);
result.Should().BeEquivalentTo(expected);
}
[Theory, MemberData(nameof(TestData.ResultStrings), MemberType = typeof(TestData))]
public void ParseResult_GivenAStringThatRepresentsAResult_ItShouldReturnTheEquivalentResultModel(string input, Result expected)
{
var result = _sut.ParseResult(input);
result.Should().BeEquivalentTo(expected);
}
[Theory, MemberData(nameof(TestData.GameStrings), MemberType = typeof(TestData))]
public void ParseGame_GivenAStringThatRepresentsAGame_ItShouldReturnTheEquivalentGameModel(string input, Game expected)
{
var result = _sut.ParseGame(input);
result.Should().BeEquivalentTo(expected);
}
public static class TestData
{
public static readonly IEnumerable<object[]> CubeStrings = new List<object[]>
{
new object[] { "4 red", new CubeCollection { Count = 4, Color = CubeColor.Red } },
new object[] { "1 green", new CubeCollection { Count = 1, Color = CubeColor.Green } },
new object[] { "2 blue", new CubeCollection { Count = 2, Color = CubeColor.Blue } }
};
public static readonly IEnumerable<object[]> ResultStrings = new List<object[]>
{
new object[]
{
"4 red, 1 green, 2 blue",
new Result
{
Cubes =
[
new (){ Count = 4, Color = CubeColor.Red },
new (){ Count = 1, Color = CubeColor.Green },
new() { Count = 2, Color = CubeColor.Blue }
]
}
}
};
public static readonly IEnumerable<object[]> GameStrings = new List<object[]>
{
new object[]
{
"Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green",
new Game
{
Id = 1,
Results =
[
new()
{
Cubes =
[
new() { Count = 3, Color = CubeColor.Blue },
new() { Count = 4, Color = CubeColor.Red }
]
},
new Result
{
Cubes =
[
new() { Count = 1, Color = CubeColor.Red },
new() { Count = 2, Color = CubeColor.Green },
new() { Count = 6, Color = CubeColor.Blue }
]
},
new Result
{
Cubes =
[
new(){ Count = 2, Color = CubeColor.Green }
]
}
]
}
}
};
}
}
+23
View File
@@ -0,0 +1,23 @@
namespace CubeConundrum.Tests;
public class ResultTests
{
[Fact]
public void CountProperties_GivenAResult_ItShouldReturnTheCorrectCount()
{
var result = new Result
{
Cubes =
{
new() { Count = 4, Color = CubeColor.Red },
new() { Count = 1, Color = CubeColor.Green },
new() { Count = 2, Color = CubeColor.Blue }
}
};
result.TotalCubeCount.Should().Be(7);
result.RedCubeCount.Should().Be(4);
result.GreenCubeCount.Should().Be(1);
result.BlueCubeCount.Should().Be(2);
}
}
+10
View File
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
+252
View File
@@ -0,0 +1,252 @@
namespace CubeConundrum;
public class Program
{
public async static Task<int> Main(string[] args)
{
if (args.Length is 0)
{
Console.WriteLine("Please provide a path to the input file.");
return -1;
}
if (File.Exists(args[0]) is false)
{
Console.WriteLine("The provided file does not exist.");
return -2;
}
var puzzleParser = new PuzzleParser();
var puzzleSolver = new PuzzleSolver();
var input = await File.ReadAllLinesAsync(args[0]);
var games = input.Select(puzzleParser.ParseGame);
var result = args.Length > 1 && args[1] == "part2"
? puzzleSolver.SumGameMinimumPowers(games)
: puzzleSolver.SumPossibleGameIds(games);
Console.WriteLine($"The sum of all possible game ids is {result}.");
return result;
}
}
/// <summary>
/// Puzzle solver.
/// </summary>
public class PuzzleSolver
{
/// <summary>
/// The maximum number of cubes that can be used in a game.
/// </summary>
private readonly int _maxCubeCount = 39;
/// <summary>
/// The maximum number of red cubes that can be used in a game.
/// </summary>
private readonly int _maxRedCubeCount = 12;
/// <summary>
/// The maximum number of green cubes that can be used in a game.
/// </summary>
private readonly int _maxGreenCubeCount = 13;
/// <summary>
/// The maximum number of blue cubes that can be used in a game.
/// </summary>
private readonly int _maxBlueCubeCount = 14;
/// <summary>
/// Determines whether a result is possible.
/// </summary>
/// <param name="result">The result to check.</param>
/// <returns>True if the result is possible, otherwise false.</returns>
public bool IsResultPossible(Result result) => result.TotalCubeCount <= _maxCubeCount
&& result.RedCubeCount <= _maxRedCubeCount
&& result.GreenCubeCount <= _maxGreenCubeCount
&& result.BlueCubeCount <= _maxBlueCubeCount;
/// <summary>
/// Determines whether a game is possible.
/// </summary>
/// <param name="game">The game to check.</param>
/// <returns>True if the game is possible, otherwise false.</returns>
public bool IsGamePossible(Game game) => game.Results.All(IsResultPossible);
/// <summary>
/// Sums all possible game ids.
/// </summary>
/// <param name="games">The games to sum.</param>
/// <returns>The sum of all possible game ids.</returns>
public int SumPossibleGameIds(IEnumerable<Game> games) => games
.Where(IsGamePossible)
.Sum(g => g.Id);
/// <summary>
/// Sums the minimum powers of all games.
/// </summary>
/// <param name="games">The games to sum.</param>
/// <returns>The sum of the minimum powers of all games.</returns>
public int SumGameMinimumPowers(IEnumerable<Game> games) => games
.Select(g => g.PowerOfMinimumCubesNeeded)
.Sum();
}
/// <summary>
/// Puzzle parser responsible for parsing puzzle input.
/// </summary>
public class PuzzleParser
{
/// <summary>
/// Parses a cube from a string.
/// </summary>
/// <param name="cubeString">The string to parse.</param>
/// <returns>An instance of <see cref="CubeCollection"/>.</returns>
public CubeCollection ParseCube(string cubeString)
{
var parts = cubeString.Split(' ');
var count = int.Parse(parts[0]);
var color = Enum.Parse<CubeColor>(parts[1], true);
return new()
{
Count = count,
Color = color
};
}
/// <summary>
/// Parses a result from a string.
/// </summary>
/// <param name="resultString">The string to parse.</param>
/// <returns>An instance of <see cref="Result"/>.</returns>
public Result ParseResult(string resultString)
{
var cubes = resultString
.Split(',')
.Select(s => s.Trim())
.Select(ParseCube)
.ToList();
return new()
{
Cubes = cubes
};
}
/// <summary>
/// Parses a game from a string.
/// </summary>
/// <param name="gameString">The string to parse.</param>
/// <returns>An instance of <see cref="Game"/>.</returns>
public Game ParseGame(string gameString)
{
var parts = gameString.Split(':');
var gameId = int.Parse(parts[0].Split(' ')[1]);
var results = parts[1]
.Split(';')
.Select(s => s.Trim())
.Select(ParseResult)
.ToList();
return new()
{
Id = gameId,
Results = results
};
}
}
/// <summary>
/// Represents a game.
/// </summary>
public class Game
{
/// <summary>
/// The id of the game.
/// </summary>
public int Id { get; set; }
/// <summary>
/// The results of the game.
/// </summary>
public List<Result> Results { get; set; } = [];
private int MinimumRedCubesNeeded => Results.Max(r => r.RedCubeCount);
private int MinimumGreenCubesNeeded => Results.Max(r => r.GreenCubeCount);
private int MinimumBlueCubesNeeded => Results.Max(r => r.BlueCubeCount);
/// <summary>
/// The power of the minimum number of each cube type needed to play the game.
/// </summary>
public int PowerOfMinimumCubesNeeded => MinimumRedCubesNeeded * MinimumGreenCubesNeeded * MinimumBlueCubesNeeded;
}
/// <summary>
/// Represents a result.
/// </summary>
public class Result
{
/// <summary>
/// The cubes in the result.
/// </summary>
public List<CubeCollection> Cubes { get; set; } = [];
/// <summary>
/// The total number of cubes in the result.
/// </summary>
public int TotalCubeCount => Cubes.Sum(c => c.Count);
/// <summary>
/// The number of red cubes in the result.
/// </summary>
public int RedCubeCount => Cubes.Where(c => c.Color == CubeColor.Red).Sum(c => c.Count);
/// <summary>
/// The number of green cubes in the result.
/// </summary>
public int GreenCubeCount => Cubes.Where(c => c.Color == CubeColor.Green).Sum(c => c.Count);
/// <summary>
/// The number of blue cubes in the result.
/// </summary>
public int BlueCubeCount => Cubes.Where(c => c.Color == CubeColor.Blue).Sum(c => c.Count);
}
/// <summary>
/// Represents cubes of a given color.
/// </summary>
public class CubeCollection
{
/// <summary>
/// The number of cubes.
/// </summary>
public int Count { get; set; }
/// <summary>
/// The color of the cubes.
/// </summary>
public CubeColor Color { get; set; }
}
/// <summary>
/// Represents a cube color.
/// </summary>
public enum CubeColor
{
/// <summary>
/// The red cube color.
/// </summary>
Red,
/// <summary>
/// The green cube color.
/// </summary>
Green,
/// <summary>
/// The blue cube color.
/// </summary>
Blue
}
+75
View File
@@ -0,0 +1,75 @@
# Notes
## Constraints
### Total number of cubes: 39
The elf can't show me more than 39 dice at a time.
### Total number of each cube type: 12 red, 13 green, 14 blue
The elf can't show me more than 12 red cubes at a time.
The elf can't show me more than 13 green cubes at a time.
The elf can't show me more than 14 blue cubes at a time.
### Types of cubes: red, green, blue
The elf can only show me red, green, or blue cubes.
The elf though can show me any combination of red, green, or blue cubes including none of a particular color or all of a particular color.
## Structure
Each game is a line of text.
Each game is delimited by "Game N: " where N is the game id.
Each game consists of a semi-colon delimited list of sets of cubes.
Each set of cubes is a comma delimited list of the number of cubes and the color of the cubes.
```text
Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
```
❓ How would I model this?
```csharp
class Game
{
public int Id { get; set; }
public List<Results> Results { get; set; }
}
class Results
{
public List<Cube> Cubes { get; set; }
}
class Cube
{
public int Count { get; set; }
public CubeColor Color { get; set; }
}
enum CubeColor
{
Red,
Green,
Blue
}
```
## Problems
### Problem 1
🔴 Need to determine which results are possible given the known # of cubes and known # of cubes for each color.
### Problem 2
🔴 Need to determine if all the results for a given game are possible
### Problem 3
🔴 Need to determine the sum of the game ids for all the possible games
### Problem 4
🔴 Need to determine the sum of the game ids for all the possible games
+16
View File
@@ -9,6 +9,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Trebuchet", "01\Trebuchet\T
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Trebuchet.Tests", "01\Trebuchet.Tests\Trebuchet.Tests.csproj", "{4A5226E4-5B90-4D6C-9224-EBE3F04470ED}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02", "02", "{4FD81B09-302F-4C76-BECB-B4559DF3984F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CubeConundrum", "02\CubeConundrum\CubeConundrum.csproj", "{95D6522F-4893-4A66-9B39-7F0268E290B8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CubeConundrum.Tests", "02\CubeConundrum.Tests\CubeConundrum.Tests.csproj", "{4825CE42-4717-4DA6-944F-9BD9A825932B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -26,9 +32,19 @@ Global
{4A5226E4-5B90-4D6C-9224-EBE3F04470ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A5226E4-5B90-4D6C-9224-EBE3F04470ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A5226E4-5B90-4D6C-9224-EBE3F04470ED}.Release|Any CPU.Build.0 = Release|Any CPU
{95D6522F-4893-4A66-9B39-7F0268E290B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{95D6522F-4893-4A66-9B39-7F0268E290B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{95D6522F-4893-4A66-9B39-7F0268E290B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{95D6522F-4893-4A66-9B39-7F0268E290B8}.Release|Any CPU.Build.0 = Release|Any CPU
{4825CE42-4717-4DA6-944F-9BD9A825932B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4825CE42-4717-4DA6-944F-9BD9A825932B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4825CE42-4717-4DA6-944F-9BD9A825932B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4825CE42-4717-4DA6-944F-9BD9A825932B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{3F8EAC09-4BC7-43AA-B72B-48DDD2710F6D} = {8C29858C-623A-461A-BF0B-254E151CD9C2}
{4A5226E4-5B90-4D6C-9224-EBE3F04470ED} = {8C29858C-623A-461A-BF0B-254E151CD9C2}
{95D6522F-4893-4A66-9B39-7F0268E290B8} = {4FD81B09-302F-4C76-BECB-B4559DF3984F}
{4825CE42-4717-4DA6-944F-9BD9A825932B} = {4FD81B09-302F-4C76-BECB-B4559DF3984F}
EndGlobalSection
EndGlobal
+26 -26
View File
@@ -5,29 +5,29 @@ This repository contains my solutions for the [Advent of Code 2023](https://adve
## Challenges
| Day | Problem | Solution | Status | Notes |
| --- | -------------------------- | :-----------------------------------: | :----------------------: | ------------------------------------------------------------------- |
| 01 | [Problem](./01/PROBLEM.md) | [Solution](./01/Trebuchet/Program.cs) | :white_check_mark: | The trickiest part here was accounting for overlapping digit words. |
| 02 | [Problem](./02/PROBLEM.md) | [Solution](./02/) | :hourglass_flowing_sand: |
| 03 | [Problem](./03/PROBLEM.md) | [Solution](./03/) | :hourglass_flowing_sand: |
| 04 | [Problem](./04/PROBLEM.md) | [Solution](./04/) | :hourglass_flowing_sand: |
| 05 | [Problem](./05/PROBLEM.md) | [Solution](./05/) | :hourglass_flowing_sand: |
| 06 | [Problem](./06/PROBLEM.md) | [Solution](./06/) | :hourglass_flowing_sand: |
| 07 | [Problem](./07/PROBLEM.md) | [Solution](./07/) | :hourglass_flowing_sand: |
| 08 | [Problem](./08/PROBLEM.md) | [Solution](./08/) | :hourglass_flowing_sand: |
| 09 | [Problem](./09/PROBLEM.md) | [Solution](./09/) | :hourglass_flowing_sand: |
| 10 | [Problem](./10/PROBLEM.md) | [Solution](./10/) | :hourglass_flowing_sand: |
| 11 | [Problem](./11/PROBLEM.md) | [Solution](./11/) | :hourglass_flowing_sand: |
| 12 | [Problem](./12/PROBLEM.md) | [Solution](./12/) | :hourglass_flowing_sand: |
| 13 | [Problem](./13/PROBLEM.md) | [Solution](./13/) | :hourglass_flowing_sand: |
| 14 | [Problem](./14/PROBLEM.md) | [Solution](./14/) | :hourglass_flowing_sand: |
| 15 | [Problem](./15/PROBLEM.md) | [Solution](./15/) | :hourglass_flowing_sand: |
| 16 | [Problem](./16/PROBLEM.md) | [Solution](./16/) | :hourglass_flowing_sand: |
| 17 | [Problem](./17/PROBLEM.md) | [Solution](./17/) | :hourglass_flowing_sand: |
| 18 | [Problem](./18/PROBLEM.md) | [Solution](./18/) | :hourglass_flowing_sand: |
| 19 | [Problem](./19/PROBLEM.md) | [Solution](./19/) | :hourglass_flowing_sand: |
| 20 | [Problem](./20/PROBLEM.md) | [Solution](./20/) | :hourglass_flowing_sand: |
| 21 | [Problem](./21/PROBLEM.md) | [Solution](./21/) | :hourglass_flowing_sand: |
| 22 | [Problem](./22/PROBLEM.md) | [Solution](./22/) | :hourglass_flowing_sand: |
| 23 | [Problem](./23/PROBLEM.md) | [Solution](./23/) | :hourglass_flowing_sand: |
| 24 | [Problem](./24/PROBLEM.md) | [Solution](./24/) | :hourglass_flowing_sand: |
| 25 | [Problem](./25/PROBLEM.md) | [Solution](./25/) | :hourglass_flowing_sand: |
| --- | -------------------------- | :---------------------------------------: | :----: | ------------------------------------------------------------------- |
| 01 | [Problem](./01/PROBLEM.md) | [Solution](./01/Trebuchet/Program.cs) | | The trickiest part here was accounting for overlapping digit words. |
| 02 | [Problem](./02/PROBLEM.md) | [Solution](./02/CubeConundrum/Program.cs) | | The key to me here was to parse the input into a useful model. |
| 03 | [Problem](./03/PROBLEM.md) | [Solution](./03/) | ⌛ |
| 04 | [Problem](./04/PROBLEM.md) | [Solution](./04/) | ⌛ |
| 05 | [Problem](./05/PROBLEM.md) | [Solution](./05/) | ⌛ |
| 06 | [Problem](./06/PROBLEM.md) | [Solution](./06/) | ⌛ |
| 07 | [Problem](./07/PROBLEM.md) | [Solution](./07/) | ⌛ |
| 08 | [Problem](./08/PROBLEM.md) | [Solution](./08/) | ⌛ |
| 09 | [Problem](./09/PROBLEM.md) | [Solution](./09/) | ⌛ |
| 10 | [Problem](./10/PROBLEM.md) | [Solution](./10/) | ⌛ |
| 11 | [Problem](./11/PROBLEM.md) | [Solution](./11/) | ⌛ |
| 12 | [Problem](./12/PROBLEM.md) | [Solution](./12/) | ⌛ |
| 13 | [Problem](./13/PROBLEM.md) | [Solution](./13/) | ⌛ |
| 14 | [Problem](./14/PROBLEM.md) | [Solution](./14/) | ⌛ |
| 15 | [Problem](./15/PROBLEM.md) | [Solution](./15/) | ⌛ |
| 16 | [Problem](./16/PROBLEM.md) | [Solution](./16/) | ⌛ |
| 17 | [Problem](./17/PROBLEM.md) | [Solution](./17/) | ⌛ |
| 18 | [Problem](./18/PROBLEM.md) | [Solution](./18/) | ⌛ |
| 19 | [Problem](./19/PROBLEM.md) | [Solution](./19/) | ⌛ |
| 20 | [Problem](./20/PROBLEM.md) | [Solution](./20/) | ⌛ |
| 21 | [Problem](./21/PROBLEM.md) | [Solution](./21/) | ⌛ |
| 22 | [Problem](./22/PROBLEM.md) | [Solution](./22/) | ⌛ |
| 23 | [Problem](./23/PROBLEM.md) | [Solution](./23/) | ⌛ |
| 24 | [Problem](./24/PROBLEM.md) | [Solution](./24/) | ⌛ |
| 25 | [Problem](./25/PROBLEM.md) | [Solution](./25/) | ⌛ |