Merge pull request #14 from StevanFreeborn/stevanfreeborn/feat/solve-puzzle-14
feat: solve puzzle 14
This commit is contained in:
@@ -0,0 +1,220 @@
|
|||||||
|
namespace ParabolicReflectorDish.Tests;
|
||||||
|
|
||||||
|
public class DishTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(TestData.ParseTestData), MemberType = typeof(TestData))]
|
||||||
|
public void Parse_GivenInput_ItShouldReturnDishInstance(string[] input, Dish expected)
|
||||||
|
{
|
||||||
|
var result = Dish.Parse(input);
|
||||||
|
result.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(TestData.CalculateTotalLoadTestData), MemberType = typeof(TestData))]
|
||||||
|
public void CalculateTotalLoad_WhenCalled_ItShouldReturnExpectedLoad(Dish dish, long expected)
|
||||||
|
{
|
||||||
|
var result = dish.CalculateTotalLoad();
|
||||||
|
result.Should().Be(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(TestData.CalculateTotalLoadWithSpinCycleData), MemberType = typeof(TestData))]
|
||||||
|
public void CalculateTotalLoad_WhenCalledWithSpinCycle_ItShouldReturnExpectedLoad(Dish dish, long expected)
|
||||||
|
{
|
||||||
|
var result = dish.CalculateTotalLoad(true);
|
||||||
|
result.Should().Be(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(TestData.TiltTestData), MemberType = typeof(TestData))]
|
||||||
|
public void TiltDish_WhenCalled_ItShouldReturnExpectedDish(Dish dish, int times, Dish expected)
|
||||||
|
{
|
||||||
|
var result = dish.TiltDish(times);
|
||||||
|
result.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CalculateTotalLoad_WhenCalledWithExample_ItShouldReturnExpectedLoad()
|
||||||
|
{
|
||||||
|
var input = File.ReadAllLines("EXAMPLE.txt");
|
||||||
|
var dish = Dish.Parse(input);
|
||||||
|
var result = dish.CalculateTotalLoad();
|
||||||
|
result.Should().Be(136);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CalculateTotalLoad_WhenCalledWithInput_ItShouldReturnExpectedLoad()
|
||||||
|
{
|
||||||
|
var input = File.ReadAllLines("INPUT.txt");
|
||||||
|
var dish = Dish.Parse(input);
|
||||||
|
var result = dish.CalculateTotalLoad();
|
||||||
|
result.Should().Be(106997);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CalculateTotalLoad_WhenCalledWithExampleAndSpinCycle_ItShouldReturnExpectedLoad()
|
||||||
|
{
|
||||||
|
var input = File.ReadAllLines("EXAMPLE.txt");
|
||||||
|
var dish = Dish.Parse(input);
|
||||||
|
var result = dish.CalculateTotalLoad(true);
|
||||||
|
result.Should().Be(64);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CalculateTotalLoad_WhenCalledWithInputAndSpinCycle_ItShouldReturnExpectedLoad()
|
||||||
|
{
|
||||||
|
var input = File.ReadAllLines("INPUT.txt");
|
||||||
|
var dish = Dish.Parse(input);
|
||||||
|
var result = dish.CalculateTotalLoad(true);
|
||||||
|
result.Should().Be(99641);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestData
|
||||||
|
{
|
||||||
|
private static readonly Dish TestDish = new(
|
||||||
|
[
|
||||||
|
['O', '.', '.', '.', '.', '#', '.', '.', '.', '.'],
|
||||||
|
['O', '.', 'O', 'O', '#', '.', '.', '.', '.', '#'],
|
||||||
|
['.', '.', '.', '.', '.', '#', '#', '.', '.', '.'],
|
||||||
|
['O', 'O', '.', '#', 'O', '.', '.', '.', '.', 'O'],
|
||||||
|
['.', 'O', '.', '.', '.', '.', '.', 'O', '#', '.'],
|
||||||
|
['O', '.', '#', '.', '.', 'O', '.', '#', '.', '#'],
|
||||||
|
['.', '.', 'O', '.', '.', '#', 'O', '.', '.', 'O'],
|
||||||
|
['.', '.', '.', '.', '.', '.', '.', 'O', '.', '.'],
|
||||||
|
['#', '.', '.', '.', '.', '#', '#', '#', '.', '.'],
|
||||||
|
['#', 'O', 'O', '.', '.', '#', '.', '.', '.', '.'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> TiltTestData =>
|
||||||
|
new List<object[]>
|
||||||
|
{
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
TestDish,
|
||||||
|
1,
|
||||||
|
new Dish(
|
||||||
|
[
|
||||||
|
['.', '.', '.', '.', '.', '#', '.', '.', '.', '.'],
|
||||||
|
['.', '.', '.', '.', '#', '.', '.', '.', 'O', '#'],
|
||||||
|
['.', '.', '.', 'O', 'O', '#', '#', '.', '.', '.'],
|
||||||
|
['.', 'O', 'O', '#', '.', '.', '.', '.', '.', '.'],
|
||||||
|
['.', '.', '.', '.', '.', 'O', 'O', 'O', '#', '.'],
|
||||||
|
['.', 'O', '#', '.', '.', '.', 'O', '#', '.', '#'],
|
||||||
|
['.', '.', '.', '.', 'O', '#', '.', '.', '.', '.'],
|
||||||
|
['.', '.', '.', '.', '.', '.', 'O', 'O', 'O', 'O'],
|
||||||
|
['#', '.', '.', '.', 'O', '#', '#', '#', '.', '.'],
|
||||||
|
['#', '.', '.', 'O', 'O', '#', '.', '.', '.', '.'],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
TestDish,
|
||||||
|
2,
|
||||||
|
new Dish(
|
||||||
|
[
|
||||||
|
['.', '.', '.', '.', '.', '#', '.', '.', '.', '.'],
|
||||||
|
['.', '.', '.', '.', '#', '.', '.', '.', 'O', '#'],
|
||||||
|
['.', '.', '.', '.', '.', '#', '#', '.', '.', '.'],
|
||||||
|
['.', '.', 'O', '#', '.', '.', '.', '.', '.', '.'],
|
||||||
|
['.', '.', '.', '.', '.', 'O', 'O', 'O', '#', '.'],
|
||||||
|
['.', 'O', '#', '.', '.', '.', 'O', '#', '.', '#'],
|
||||||
|
['.', '.', '.', '.', 'O', '#', '.', '.', '.', 'O'],
|
||||||
|
['.', '.', '.', '.', '.', '.', '.', 'O', 'O', 'O'],
|
||||||
|
['#', '.', '.', 'O', 'O', '#', '#', '#', '.', '.'],
|
||||||
|
['#', '.', 'O', 'O', 'O', '#', '.', '.', '.', 'O'],
|
||||||
|
]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
TestDish,
|
||||||
|
3,
|
||||||
|
new Dish(
|
||||||
|
[
|
||||||
|
['.', '.', '.', '.', '.', '#', '.', '.', '.', '.'],
|
||||||
|
['.', '.', '.', '.', '#', '.', '.', '.', 'O', '#'],
|
||||||
|
['.', '.', '.', '.', '.', '#', '#', '.', '.', '.'],
|
||||||
|
['.', '.', 'O', '#', '.', '.', '.', '.', '.', '.'],
|
||||||
|
['.', '.', '.', '.', '.', 'O', 'O', 'O', '#', '.'],
|
||||||
|
['.', 'O', '#', '.', '.', '.', 'O', '#', '.', '#'],
|
||||||
|
['.', '.', '.', '.', 'O', '#', '.', '.', '.', 'O'],
|
||||||
|
['.', '.', '.', '.', '.', '.', '.', 'O', 'O', 'O'],
|
||||||
|
['#', '.', '.', '.', 'O', '#', '#', '#', '.', 'O'],
|
||||||
|
['#', '.', 'O', 'O', 'O', '#', '.', '.', '.', 'O'],
|
||||||
|
]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> CalculateTotalLoadWithSpinCycleData =>
|
||||||
|
new List<object[]>
|
||||||
|
{
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
TestDish,
|
||||||
|
64,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> CalculateTotalLoadTestData =>
|
||||||
|
new List<object[]>
|
||||||
|
{
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new Dish(
|
||||||
|
[
|
||||||
|
['O', 'O', 'O', 'O', '.', '#', '.', 'O', '.', '.'],
|
||||||
|
['O', 'O', '.', '.', '#', '.', '.', '.', '.', '#'],
|
||||||
|
['O', 'O', '.', '.', 'O', '#', '#', '.', '.', 'O'],
|
||||||
|
['O', '.', '.', '#', '.', 'O', 'O', '.', '.', '.'],
|
||||||
|
['.', '.', '.', '.', '.', '.', '.', '.', '#', '.'],
|
||||||
|
['.', '.', '#', '.', '.', '.', '.', '#', '.', '#'],
|
||||||
|
['.', '.', 'O', '.', '.', '#', '.', 'O', '.', 'O'],
|
||||||
|
['.', '.', 'O', '.', '.', '.', '.', '.', '.', '.'],
|
||||||
|
['#', '.', '.', '.', '.', '#', '#', '#', '.', '.'],
|
||||||
|
['#', '.', '.', '.', '.', '#', '.', '.', '.', '.'],
|
||||||
|
]
|
||||||
|
),
|
||||||
|
136
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> ParseTestData =>
|
||||||
|
new List<object[]>
|
||||||
|
{
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"O....#....",
|
||||||
|
"O.OO#....#",
|
||||||
|
".....##...",
|
||||||
|
"OO.#O....O",
|
||||||
|
".O.....O#.",
|
||||||
|
"O.#..O.#.#",
|
||||||
|
"..O..#O..O",
|
||||||
|
".......O..",
|
||||||
|
"#....###..",
|
||||||
|
"#OO..#....",
|
||||||
|
},
|
||||||
|
new Dish(
|
||||||
|
[
|
||||||
|
['O', '.', '.', '.', '.', '#', '.', '.', '.', '.'],
|
||||||
|
['O', '.', 'O', 'O', '#', '.', '.', '.', '.', '#'],
|
||||||
|
['.', '.', '.', '.', '.', '#', '#', '.', '.', '.'],
|
||||||
|
['O', 'O', '.', '#', 'O', '.', '.', '.', '.', 'O'],
|
||||||
|
['.', 'O', '.', '.', '.', '.', '.', 'O', '#', '.'],
|
||||||
|
['O', '.', '#', '.', '.', 'O', '.', '#', '.', '#'],
|
||||||
|
['.', '.', 'O', '.', '.', '#', 'O', '.', '.', 'O'],
|
||||||
|
['.', '.', '.', '.', '.', '.', '.', 'O', '.', '.'],
|
||||||
|
['#', '.', '.', '.', '.', '#', '#', '#', '.', '.'],
|
||||||
|
['#', 'O', 'O', '.', '.', '#', '.', '.', '.', '.'],
|
||||||
|
]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
global using Xunit;
|
||||||
|
global using FluentAssertions;
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<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="..\ParabolicReflectorDish\ParabolicReflectorDish.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="..\INPUT.txt">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="..\EXAMPLE.txt">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -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>
|
||||||
@@ -0,0 +1,363 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace ParabolicReflectorDish;
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static async 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 isPart2 = args.Length > 1 && args[1] == "part2";
|
||||||
|
var input = await File.ReadAllLinesAsync(args[0]);
|
||||||
|
|
||||||
|
var stopwatch = new Stopwatch();
|
||||||
|
stopwatch.Start();
|
||||||
|
|
||||||
|
var result = Dish
|
||||||
|
.Parse(input)
|
||||||
|
.CalculateTotalLoad(isPart2);
|
||||||
|
|
||||||
|
stopwatch.Stop();
|
||||||
|
|
||||||
|
Console.WriteLine($"The total load is {result}. ({stopwatch.ElapsedMilliseconds}ms)");
|
||||||
|
|
||||||
|
return (int)result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a parabolic reflector dish.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rows">The rows of the dish.</param>
|
||||||
|
/// <returns>An instance of the <see cref="Dish"/> class.</returns>
|
||||||
|
public class Dish(
|
||||||
|
List<List<char>> rows
|
||||||
|
)
|
||||||
|
{
|
||||||
|
private const char RoundRockSymbol = 'O';
|
||||||
|
private const char SquareRockSymbol = '#';
|
||||||
|
private const char EmptySpaceSymbol = '.';
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the rows of the dish.
|
||||||
|
/// </summary>
|
||||||
|
public List<List<char>> Rows { get; init; } = rows;
|
||||||
|
|
||||||
|
private List<List<char>> TiltDishToNorth(List<List<char>> rows)
|
||||||
|
{
|
||||||
|
// create a copy of the rows which we can modify
|
||||||
|
var modifiedRows = rows
|
||||||
|
.Select(r => r.ToList())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var numOfColumns = modifiedRows[0].Count;
|
||||||
|
var numOfRows = modifiedRows.Count;
|
||||||
|
|
||||||
|
// iterate over the columns from left to right
|
||||||
|
for (var currentColumnIndex = 0; currentColumnIndex < numOfColumns; currentColumnIndex++)
|
||||||
|
{
|
||||||
|
// iterate over the rows from top to bottom
|
||||||
|
for (var currentRowIndex = 0; currentRowIndex < numOfRows; currentRowIndex++)
|
||||||
|
{
|
||||||
|
// get the current symbol
|
||||||
|
var current = modifiedRows[currentRowIndex][currentColumnIndex];
|
||||||
|
|
||||||
|
// if the current symbol is an empty space
|
||||||
|
if (current is EmptySpaceSymbol)
|
||||||
|
{
|
||||||
|
// iterate over the rows below the current row
|
||||||
|
// starting from the row after the current row
|
||||||
|
for (var i = currentRowIndex + 1; i < numOfRows; i++)
|
||||||
|
{
|
||||||
|
// get the next symbol in the current column
|
||||||
|
var next = modifiedRows[i][currentColumnIndex];
|
||||||
|
|
||||||
|
// if the next symbol is a square rock
|
||||||
|
// we can stop iterating over the rows below
|
||||||
|
// because the square rock blocks moving
|
||||||
|
// any round rock to fill the empty space
|
||||||
|
if (next is SquareRockSymbol)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the next symbol is a round rock
|
||||||
|
// we can move the round rock to the empty space
|
||||||
|
// and stop iterating over the rows below
|
||||||
|
if (next is RoundRockSymbol)
|
||||||
|
{
|
||||||
|
modifiedRows[currentRowIndex][currentColumnIndex] = RoundRockSymbol;
|
||||||
|
modifiedRows[i][currentColumnIndex] = EmptySpaceSymbol;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<List<char>> TiltDishToWest(List<List<char>> rows)
|
||||||
|
{
|
||||||
|
// create a copy of the rows which we can modify
|
||||||
|
var modifiedRows = rows
|
||||||
|
.Select(r => r.ToList())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var numOfRows = modifiedRows.Count;
|
||||||
|
var numOfColumns = modifiedRows[0].Count;
|
||||||
|
|
||||||
|
// iterate over the rows from top to bottom
|
||||||
|
for (var currentRowIndex = 0; currentRowIndex < numOfRows; currentRowIndex++)
|
||||||
|
{
|
||||||
|
// iterate over the columns from left to right
|
||||||
|
for (var currentColumnIndex = 0; currentColumnIndex < numOfRows; currentColumnIndex++)
|
||||||
|
{
|
||||||
|
// get the current symbol
|
||||||
|
var current = modifiedRows[currentRowIndex][currentColumnIndex];
|
||||||
|
|
||||||
|
// if the current symbol is an empty space
|
||||||
|
if (current is EmptySpaceSymbol)
|
||||||
|
{
|
||||||
|
// iterate over the columns to the left of the current column
|
||||||
|
for (var i = currentColumnIndex + 1; i < numOfColumns; i++)
|
||||||
|
{
|
||||||
|
// get the next symbol in the current row
|
||||||
|
var next = modifiedRows[currentRowIndex][i];
|
||||||
|
|
||||||
|
// if the next symbol is a square rock
|
||||||
|
// we can stop iterating over the columns to the left
|
||||||
|
// because the square rock blocks moving
|
||||||
|
// any round rock to fill the empty space
|
||||||
|
if (next is SquareRockSymbol)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the next symbol is a round rock
|
||||||
|
// we can move the round rock to the empty space
|
||||||
|
// and stop iterating over the columns to the left
|
||||||
|
if (next is RoundRockSymbol)
|
||||||
|
{
|
||||||
|
modifiedRows[currentRowIndex][currentColumnIndex] = RoundRockSymbol;
|
||||||
|
modifiedRows[currentRowIndex][i] = EmptySpaceSymbol;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<List<char>> TiltDishToSouth(List<List<char>> rows)
|
||||||
|
{
|
||||||
|
// create a copy of the rows which we can modify
|
||||||
|
var modifiedRows = rows
|
||||||
|
.Select(r => r.ToList())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var numOfColumns = modifiedRows[0].Count;
|
||||||
|
var numOfRows = modifiedRows.Count;
|
||||||
|
|
||||||
|
// iterate over the columns from left to right
|
||||||
|
for (var currentColumnIndex = 0; currentColumnIndex < numOfColumns; currentColumnIndex++)
|
||||||
|
{
|
||||||
|
// iterate over the rows from bottom to top
|
||||||
|
for (var currentRowIndex = numOfRows - 1; currentRowIndex >= 0; currentRowIndex--)
|
||||||
|
{
|
||||||
|
// get the current symbol
|
||||||
|
var current = modifiedRows[currentRowIndex][currentColumnIndex];
|
||||||
|
|
||||||
|
// if the current symbol is an empty space
|
||||||
|
if (current is EmptySpaceSymbol)
|
||||||
|
{
|
||||||
|
// iterate over the rows above the current row
|
||||||
|
// starting from the row before the current row
|
||||||
|
for (var i = currentRowIndex - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
// get the next symbol in the current column
|
||||||
|
var next = modifiedRows[i][currentColumnIndex];
|
||||||
|
|
||||||
|
// if the next symbol is a square rock
|
||||||
|
// we can stop iterating over the rows below
|
||||||
|
// because the square rock blocks moving
|
||||||
|
// any round rock to fill the empty space
|
||||||
|
if (next is SquareRockSymbol)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the next symbol is a round rock
|
||||||
|
// we can move the round rock to the empty space
|
||||||
|
// and stop iterating over the rows below
|
||||||
|
if (next is RoundRockSymbol)
|
||||||
|
{
|
||||||
|
modifiedRows[currentRowIndex][currentColumnIndex] = RoundRockSymbol;
|
||||||
|
modifiedRows[i][currentColumnIndex] = EmptySpaceSymbol;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<List<char>> TiltDishToEast(List<List<char>> rows)
|
||||||
|
{
|
||||||
|
// create a copy of the rows which we can modify
|
||||||
|
var modifiedRows = rows
|
||||||
|
.Select(r => r.ToList())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var numOfRows = modifiedRows.Count;
|
||||||
|
var numOfColumns = modifiedRows[0].Count;
|
||||||
|
|
||||||
|
// iterate over the rows from top to bottom
|
||||||
|
for (var currentRowIndex = 0; currentRowIndex < numOfRows; currentRowIndex++)
|
||||||
|
{
|
||||||
|
// iterate over the columns from right to left
|
||||||
|
for (var currentColumnIndex = numOfColumns - 1; currentColumnIndex >= 0; currentColumnIndex--)
|
||||||
|
{
|
||||||
|
// get the current symbol
|
||||||
|
var current = modifiedRows[currentRowIndex][currentColumnIndex];
|
||||||
|
|
||||||
|
// if the current symbol is an empty space
|
||||||
|
if (current is EmptySpaceSymbol)
|
||||||
|
{
|
||||||
|
// iterate over the columns to the left of the current column
|
||||||
|
for (var i = currentColumnIndex - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
// get the next symbol in the current row
|
||||||
|
var next = modifiedRows[currentRowIndex][i];
|
||||||
|
|
||||||
|
// if the next symbol is a square rock
|
||||||
|
// we can stop iterating over the columns to the left
|
||||||
|
// because the square rock blocks moving
|
||||||
|
// any round rock to fill the empty space
|
||||||
|
if (next is SquareRockSymbol)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the next symbol is a round rock
|
||||||
|
// we can move the round rock to the empty space
|
||||||
|
// and stop iterating over the columns to the left
|
||||||
|
if (next is RoundRockSymbol)
|
||||||
|
{
|
||||||
|
modifiedRows[currentRowIndex][currentColumnIndex] = RoundRockSymbol;
|
||||||
|
modifiedRows[currentRowIndex][i] = EmptySpaceSymbol;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tilt the dish to the north, west, south and east.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="numOfCycles">The number of cycles to tilt the dish.</param>
|
||||||
|
/// <returns>An instance of the <see cref="Dish"/> class.</returns>
|
||||||
|
public Dish TiltDish(long numOfCycles)
|
||||||
|
{
|
||||||
|
var modifiedRows = Rows
|
||||||
|
.Select(r => r.ToList())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// create a cache to store the modified rows
|
||||||
|
// and the index of the cycle in which they were modified
|
||||||
|
// this should allow us to detect cycles
|
||||||
|
var cache = new Dictionary<string, long>();
|
||||||
|
|
||||||
|
for (var i = 0; i < numOfCycles; i++)
|
||||||
|
{
|
||||||
|
modifiedRows = TiltDishToNorth(modifiedRows);
|
||||||
|
modifiedRows = TiltDishToWest(modifiedRows);
|
||||||
|
modifiedRows = TiltDishToSouth(modifiedRows);
|
||||||
|
modifiedRows = TiltDishToEast(modifiedRows);
|
||||||
|
|
||||||
|
// use the modified rows as a key for the cache
|
||||||
|
var key = string.Join(Environment.NewLine, modifiedRows.Select(r => string.Join(string.Empty, r)));
|
||||||
|
|
||||||
|
// if the cache already contains the key
|
||||||
|
if (cache.TryGetValue(key, out var matchingIndex))
|
||||||
|
{
|
||||||
|
// calculate the start index of the current cycle
|
||||||
|
// and identify the key for the start index value
|
||||||
|
var cycleLength = i - matchingIndex;
|
||||||
|
var remainingCycles = numOfCycles - i;
|
||||||
|
var cycleIndex = remainingCycles % cycleLength;
|
||||||
|
var cycleStartIndex = cycleIndex + matchingIndex - 1;
|
||||||
|
var startCycleKey = cache.First(c => c.Value == cycleStartIndex).Key;
|
||||||
|
|
||||||
|
// parse the key for the start index value
|
||||||
|
// and use it as the modified rows
|
||||||
|
modifiedRows = startCycleKey
|
||||||
|
.Split(Environment.NewLine)
|
||||||
|
.Select(s => s.ToList())
|
||||||
|
.ToList();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the modified rows to the cache
|
||||||
|
cache.Add(key, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Dish(modifiedRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate the total load of the dish.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="spinDish">Whether to spin the dish.</param>
|
||||||
|
/// <returns>The total load of the dish.</returns>
|
||||||
|
public long CalculateTotalLoad(bool spinDish = false)
|
||||||
|
{
|
||||||
|
var rows = spinDish
|
||||||
|
? TiltDish(1_000_000_000).Rows
|
||||||
|
: TiltDishToNorth(Rows);
|
||||||
|
|
||||||
|
rows.Reverse();
|
||||||
|
|
||||||
|
return rows
|
||||||
|
.Select((r, index) =>
|
||||||
|
{
|
||||||
|
var numOfRoundRocks = r.Where(c => c is RoundRockSymbol).Count();
|
||||||
|
return (index + 1) * numOfRoundRocks;
|
||||||
|
})
|
||||||
|
.Sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the input into an instance of the <see cref="Dish"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">The input to parse.</param>
|
||||||
|
/// <returns>An instance of the <see cref="Dish"/> class.</returns>
|
||||||
|
public static Dish Parse(string[] input)
|
||||||
|
{
|
||||||
|
var inputList = input
|
||||||
|
.Select(s => s.ToList())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return new Dish(inputList);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,6 +81,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PointOfIncidence", "13\Poin
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PointOfIncidence.Tests", "13\PointOfIncidence.Tests\PointOfIncidence.Tests.csproj", "{0E74E842-A876-4E8C-9D10-BA09F40039EC}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PointOfIncidence.Tests", "13\PointOfIncidence.Tests\PointOfIncidence.Tests.csproj", "{0E74E842-A876-4E8C-9D10-BA09F40039EC}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "14", "14", "{D00D5BF8-754D-4D1B-9AF0-D701DFFEC9B1}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParabolicReflectorDish", "14\ParabolicReflectorDish\ParabolicReflectorDish.csproj", "{671E1E43-48A1-441F-A681-111F64445504}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParabolicReflectorDish.Tests", "14\ParabolicReflectorDish.Tests\ParabolicReflectorDish.Tests.csproj", "{631931F8-01C5-428A-9ECE-DB54FA9032FE}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -194,6 +200,14 @@ Global
|
|||||||
{0E74E842-A876-4E8C-9D10-BA09F40039EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{0E74E842-A876-4E8C-9D10-BA09F40039EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{0E74E842-A876-4E8C-9D10-BA09F40039EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{0E74E842-A876-4E8C-9D10-BA09F40039EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{0E74E842-A876-4E8C-9D10-BA09F40039EC}.Release|Any CPU.Build.0 = Release|Any CPU
|
{0E74E842-A876-4E8C-9D10-BA09F40039EC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{671E1E43-48A1-441F-A681-111F64445504}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{671E1E43-48A1-441F-A681-111F64445504}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{671E1E43-48A1-441F-A681-111F64445504}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{671E1E43-48A1-441F-A681-111F64445504}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{631931F8-01C5-428A-9ECE-DB54FA9032FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{631931F8-01C5-428A-9ECE-DB54FA9032FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{631931F8-01C5-428A-9ECE-DB54FA9032FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{631931F8-01C5-428A-9ECE-DB54FA9032FE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{3F8EAC09-4BC7-43AA-B72B-48DDD2710F6D} = {8C29858C-623A-461A-BF0B-254E151CD9C2}
|
{3F8EAC09-4BC7-43AA-B72B-48DDD2710F6D} = {8C29858C-623A-461A-BF0B-254E151CD9C2}
|
||||||
@@ -222,5 +236,7 @@ Global
|
|||||||
{2731DE56-AF4B-4009-8259-2EA5A9A102FE} = {ABF03698-B81C-45DE-B76A-C7FBC0C72DB6}
|
{2731DE56-AF4B-4009-8259-2EA5A9A102FE} = {ABF03698-B81C-45DE-B76A-C7FBC0C72DB6}
|
||||||
{334D6DFF-6131-49C1-B54C-B9E8E00F1E3E} = {DC4E691A-0061-4937-8732-E8D8073C868D}
|
{334D6DFF-6131-49C1-B54C-B9E8E00F1E3E} = {DC4E691A-0061-4937-8732-E8D8073C868D}
|
||||||
{0E74E842-A876-4E8C-9D10-BA09F40039EC} = {DC4E691A-0061-4937-8732-E8D8073C868D}
|
{0E74E842-A876-4E8C-9D10-BA09F40039EC} = {DC4E691A-0061-4937-8732-E8D8073C868D}
|
||||||
|
{671E1E43-48A1-441F-A681-111F64445504} = {D00D5BF8-754D-4D1B-9AF0-D701DFFEC9B1}
|
||||||
|
{631931F8-01C5-428A-9ECE-DB54FA9032FE} = {D00D5BF8-754D-4D1B-9AF0-D701DFFEC9B1}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -42,30 +42,30 @@ dotnet build
|
|||||||
|
|
||||||
## Challenges
|
## Challenges
|
||||||
|
|
||||||
| Day | Problem | Solution | Status | Notes |
|
| Day | Problem | Solution | Status | Notes |
|
||||||
| --- | -------------------------- | :---------------------------------: | :----: | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| --- | -------------------------- | :--------------------------------------: | :----: | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| 01 | [Problem](./01/PROBLEM.md) | [Solution](./01/Trebuchet/) | ✅ | The trickiest part here was accounting for overlapping digit words. |
|
| 01 | [Problem](./01/PROBLEM.md) | [Solution](./01/Trebuchet/) | ✅ | The trickiest part here was accounting for overlapping digit words. |
|
||||||
| 02 | [Problem](./02/PROBLEM.md) | [Solution](./02/CubeConundrum/) | ✅ | The key to me here was to parse the input into a useful model. |
|
| 02 | [Problem](./02/PROBLEM.md) | [Solution](./02/CubeConundrum/) | ✅ | The key to me here was to parse the input into a useful model. |
|
||||||
| 03 | [Problem](./03/PROBLEM.md) | [Solution](./03/GearRatios/) | ✅ | The edge case that got me here was lines ending with a part number. |
|
| 03 | [Problem](./03/PROBLEM.md) | [Solution](./03/GearRatios/) | ✅ | The edge case that got me here was lines ending with a part number. |
|
||||||
| 04 | [Problem](./04/PROBLEM.md) | [Solution](./04/Scratchcards/) | ✅ | Part 2 gets out of hand quickly with just 200 cards. |
|
| 04 | [Problem](./04/PROBLEM.md) | [Solution](./04/Scratchcards/) | ✅ | Part 2 gets out of hand quickly with just 200 cards. |
|
||||||
| 05 | [Problem](./05/PROBLEM.md) | [Solution](./05/IYGASAF/) | ✅ | I brute forced part 2 using parallelism. I know shame. |
|
| 05 | [Problem](./05/PROBLEM.md) | [Solution](./05/IYGASAF/) | ✅ | I brute forced part 2 using parallelism. I know shame. |
|
||||||
| 06 | [Problem](./06/PROBLEM.md) | [Solution](./06/WaitForIt/) | ✅ | Thank goodness part 2 was not like 5's part 2. 😅 |
|
| 06 | [Problem](./06/PROBLEM.md) | [Solution](./06/WaitForIt/) | ✅ | Thank goodness part 2 was not like 5's part 2. 😅 |
|
||||||
| 07 | [Problem](./07/PROBLEM.md) | [Solution](./07/CamelCards/) | ✅ | What took me longest here was I missed a case when jokers are wild and there are three groups of cards. |
|
| 07 | [Problem](./07/PROBLEM.md) | [Solution](./07/CamelCards/) | ✅ | What took me longest here was I missed a case when jokers are wild and there are three groups of cards. |
|
||||||
| 08 | [Problem](./08/PROBLEM.md) | [Solution](./08/HauntedWasteland/) | ✅ | Got to implement a least common multiple algorithm for part 2. |
|
| 08 | [Problem](./08/PROBLEM.md) | [Solution](./08/HauntedWasteland/) | ✅ | Got to implement a least common multiple algorithm for part 2. |
|
||||||
| 09 | [Problem](./09/PROBLEM.md) | [Solution](./09/MirageMaintenance/) | ✅ | Part 1 took me unnecessarily long. The bug was summing a line to check for all zeros is bad idea when negative numbers are involved. |
|
| 09 | [Problem](./09/PROBLEM.md) | [Solution](./09/MirageMaintenance/) | ✅ | Part 1 took me unnecessarily long. The bug was summing a line to check for all zeros is bad idea when negative numbers are involved. |
|
||||||
| 10 | [Problem](./10/PROBLEM.md) | [Solution](./10/PipeMaze/) | ✅ | So...my solutions are rather slow, but they work. Part 2 appears to have a mathematical solution, but scanning is what I came up with on my own. |
|
| 10 | [Problem](./10/PROBLEM.md) | [Solution](./10/PipeMaze/) | ✅ | So...my solutions are rather slow, but they work. Part 2 appears to have a mathematical solution, but scanning is what I came up with on my own. |
|
||||||
| 11 | [Problem](./11/PROBLEM.md) | [Solution](./11/CosmicExpansion/) | ✅ | Expanding the universe was fine in part 1, but part 2 showed I actually needed to calculate the expansion instead of expanding the input. |
|
| 11 | [Problem](./11/PROBLEM.md) | [Solution](./11/CosmicExpansion/) | ✅ | Expanding the universe was fine in part 1, but part 2 showed I actually needed to calculate the expansion instead of expanding the input. |
|
||||||
| 12 | [Problem](./12/PROBLEM.md) | [Solution](./12/HotSprings/) | ✅ | This one stretched my skills. I needed lots of help from the interwebz. |
|
| 12 | [Problem](./12/PROBLEM.md) | [Solution](./12/HotSprings/) | ✅ | This one stretched my skills. I needed lots of help from the interwebz. |
|
||||||
| 13 | [Problem](./13/PROBLEM.md) | [Solution](./13/PointOfIncidence/) | ✅ | This solution is the one I think I'm most proud of so far. |
|
| 13 | [Problem](./13/PROBLEM.md) | [Solution](./13/PointOfIncidence/) | ✅ | This solution is the one I think I'm most proud of so far. |
|
||||||
| 14 | [Problem](./14/PROBLEM.md) | [Solution](./14/) | ⌛ |
|
| 14 | [Problem](./14/PROBLEM.md) | [Solution](./14/ParabolicReflectorDish/) | ✅ | Cycles and a cache...that's all I'm going to say. |
|
||||||
| 15 | [Problem](./15/PROBLEM.md) | [Solution](./15/) | ⌛ |
|
| 15 | [Problem](./15/PROBLEM.md) | [Solution](./15/) | ⌛ |
|
||||||
| 16 | [Problem](./16/PROBLEM.md) | [Solution](./16/) | ⌛ |
|
| 16 | [Problem](./16/PROBLEM.md) | [Solution](./16/) | ⌛ |
|
||||||
| 17 | [Problem](./17/PROBLEM.md) | [Solution](./17/) | ⌛ |
|
| 17 | [Problem](./17/PROBLEM.md) | [Solution](./17/) | ⌛ |
|
||||||
| 18 | [Problem](./18/PROBLEM.md) | [Solution](./18/) | ⌛ |
|
| 18 | [Problem](./18/PROBLEM.md) | [Solution](./18/) | ⌛ |
|
||||||
| 19 | [Problem](./19/PROBLEM.md) | [Solution](./19/) | ⌛ |
|
| 19 | [Problem](./19/PROBLEM.md) | [Solution](./19/) | ⌛ |
|
||||||
| 20 | [Problem](./20/PROBLEM.md) | [Solution](./20/) | ⌛ |
|
| 20 | [Problem](./20/PROBLEM.md) | [Solution](./20/) | ⌛ |
|
||||||
| 21 | [Problem](./21/PROBLEM.md) | [Solution](./21/) | ⌛ |
|
| 21 | [Problem](./21/PROBLEM.md) | [Solution](./21/) | ⌛ |
|
||||||
| 22 | [Problem](./22/PROBLEM.md) | [Solution](./22/) | ⌛ |
|
| 22 | [Problem](./22/PROBLEM.md) | [Solution](./22/) | ⌛ |
|
||||||
| 23 | [Problem](./23/PROBLEM.md) | [Solution](./23/) | ⌛ |
|
| 23 | [Problem](./23/PROBLEM.md) | [Solution](./23/) | ⌛ |
|
||||||
| 24 | [Problem](./24/PROBLEM.md) | [Solution](./24/) | ⌛ |
|
| 24 | [Problem](./24/PROBLEM.md) | [Solution](./24/) | ⌛ |
|
||||||
| 25 | [Problem](./25/PROBLEM.md) | [Solution](./25/) | ⌛ |
|
| 25 | [Problem](./25/PROBLEM.md) | [Solution](./25/) | ⌛ |
|
||||||
|
|||||||
Reference in New Issue
Block a user