diff --git a/10/HoofIt.Tests/TopoMapTests.cs b/10/HoofIt.Tests/TopoMapTests.cs index 15e8424..37379d4 100644 --- a/10/HoofIt.Tests/TopoMapTests.cs +++ b/10/HoofIt.Tests/TopoMapTests.cs @@ -13,6 +13,11 @@ public class TopoMapTests "10456732", ]; + private async Task GetPuzzleInput() + { + return await File.ReadAllLinesAsync(Path.Combine(AppContext.BaseDirectory, "INPUT.txt")); + } + [Test] public async Task From_WhenCalled_ItShouldReturnNewInstanceOfTopoMap() { @@ -22,7 +27,7 @@ public class TopoMapTests } [Test] - public async Task GetTrailHeadScores_WhenCalledWithExampleInput_ItShouldReturnExpectedValues() + public async Task GetTrailheadScores_WhenCalledWithExampleInput_ItShouldReturnExpectedValues() { var expectedValues = new List() { @@ -37,8 +42,51 @@ public class TopoMapTests 5, }; - var result = TopoMap.From(_exampleInput).GetTrailHeadScores(); + var result = TopoMap.From(_exampleInput).GetTrailheadScores(); - await Assert.That(result).IsEqualTo(36); + await Assert.That(result).IsEquivalentTo(expectedValues); + await Assert.That(result.Sum()).IsEqualTo(36); + } + + [Test] + public async Task PartOne_WhenGivenPuzzleInput_ItShouldReturnExpectedValue() + { + var input = await GetPuzzleInput(); + + var result = TopoMap.From(input).GetTrailheadScores().Sum(); + + await Assert.That(result).IsEqualTo(496); + } + + [Test] + public async Task GetTrailheadRatings_WhenCalledWithExampleInput_ItShouldReturnExpectedValues() + { + var expectedValues = new List() + { + 20, + 24, + 10, + 4, + 1, + 4, + 5, + 8, + 5, + }; + + var result = TopoMap.From(_exampleInput).GetTrailheadRatings(); + + await Assert.That(result).IsEquivalentTo(expectedValues); + await Assert.That(result.Sum()).IsEqualTo(81); + } + + [Test] + public async Task PartTwo_WhenGivenPuzzleInput_ItShouldReturnExpectedValue() + { + var input = await GetPuzzleInput(); + + var result = TopoMap.From(input).GetTrailheadRatings().Sum(); + + await Assert.That(result).IsEqualTo(1120); } } \ No newline at end of file diff --git a/10/HoofIt/Direction.cs b/10/HoofIt/Direction.cs new file mode 100644 index 0000000..560b198 --- /dev/null +++ b/10/HoofIt/Direction.cs @@ -0,0 +1,9 @@ +namespace HoofIt; + +record Direction(int XD, int YD) +{ + public static Direction Up = new(0, -1); + public static Direction Down = new(0, 1); + public static Direction Left = new(-1, 0); + public static Direction Right = new(1, 0); +} \ No newline at end of file diff --git a/10/HoofIt/Position.cs b/10/HoofIt/Position.cs new file mode 100644 index 0000000..ad8e337 --- /dev/null +++ b/10/HoofIt/Position.cs @@ -0,0 +1,35 @@ +namespace HoofIt; + +record Position(int X, int Y, int Height) +{ + public bool TryGetNextPosition(Direction direction, string[] grid, out Position nextPosition) + { + var x = X + direction.XD; + var y = Y + direction.YD; + + nextPosition = new(x, y, -1); + + var isOffGrid = IsOffGrid(nextPosition, grid); + + if (isOffGrid) + { + return false; + } + + if (int.TryParse(grid[y][x].ToString(), out var nextPositionHeight) is false) + { + return false; + } + + nextPosition = nextPosition with { Height = nextPositionHeight }; + return true; + } + + private static bool IsOffGrid(Position position, string[] grid) + { + return position.X < 0 || + position.X > grid[0].Length - 1 || + position.Y > grid.Length - 1 || + position.Y < 0; + } +} \ No newline at end of file diff --git a/10/HoofIt/Program.cs b/10/HoofIt/Program.cs index 4c7aa8d..28bc3aa 100644 --- a/10/HoofIt/Program.cs +++ b/10/HoofIt/Program.cs @@ -1,5 +1,7 @@ using System.Diagnostics; +using HoofIt; + if (args.Length is 0) { Console.WriteLine("Please provide a path to the input file."); @@ -20,132 +22,13 @@ stopwatch.Start(); var map = TopoMap.From(input); -// i'm going to to walk over the grid -// i'm going to only worry about places where -// there is a zero +var nums = isPart2 + ? map.GetTrailheadRatings() + : map.GetTrailheadScores(); -// need to check if we are on a nine -// before we explore...also we should -// be able to add to score here +var result = nums.Sum(); -// when i encounter a zero i need to begin -// exploring nearby tiles that increment by 1 -// in each left, right, up, down direction -// any adj tile that fits this...is a tile i -// need to also explore +var nameOfStat = isPart2 ? "ratings" : "scores"; stopwatch.Stop(); -Console.WriteLine($". ({stopwatch.ElapsedMilliseconds}ms)"); - -class TopoMap -{ - private static Direction[] _directions = [ - Direction.Up, - Direction.Down, - Direction.Left, - Direction.Right, - ]; - - private readonly string[] _grid; - - private TopoMap(string[] input) - { - _grid = input; - } - - public static TopoMap From(string[] input) => new(input); - - // TODO: This actually needs to be GetTrailHeadScore - // I want to perform this search when I know I've reached - // a trail head. - public int GetTrailHeadScores() - { - var trailheadScore = 0; - var visitedPositions = new HashSet(); - var positionsToVisit = new Queue(); - - var startingX = 0; - var startingY = 0; - var startingHeight = int.Parse(_grid[startingY][startingX].ToString()); - positionsToVisit.Enqueue(new(startingX, startingY, startingHeight)); - - while (positionsToVisit.Count != 0) - { - var currentPosition = positionsToVisit.Dequeue(); - - if (visitedPositions.Contains(currentPosition)) - { - continue; - } - - visitedPositions.Add(currentPosition); - - if (currentPosition.Height is 9) - { - trailheadScore++; - continue; - } - - foreach (var direction in _directions) - { - if (currentPosition.TryGetNextPosition(direction, _grid, out var newPosition) is false) - { - continue; - } - - if (visitedPositions.Contains(newPosition)) - { - continue; - } - - var heightDifference = newPosition.Height - currentPosition.Height; - - if (heightDifference is not 1) - { - continue; - } - - positionsToVisit.Enqueue(newPosition); - } - } - - return trailheadScore; - } -} - -record Position(int X, int Y, int Height) -{ - public bool TryGetNextPosition(Direction direction, string[] grid, out Position position) - { - var x = X + direction.XD; - var y = Y + direction.YD; - - position = new(x, y, -1); - - var isOffGrid = IsOffGrid(position, grid); - - if (isOffGrid) - { - return false; - } - - position = position with { Height = grid[y][x] }; - return true; - } - - private bool IsOffGrid(Position position, string[] grid) - { - return position.X < 0 || - position.X > grid[0].Length - 1 || - position.Y > grid.Length - 1 || - position.Y < 0; - } -} - -record Direction(int XD, int YD) -{ - public static Direction Up = new(0, -1); - public static Direction Down = new(0, 1); - public static Direction Left = new(-1, 0); - public static Direction Right = new(1, 0); -} \ No newline at end of file +Console.WriteLine($"The sum of the {nameOfStat} of all trailheads is {result}. ({stopwatch.ElapsedMilliseconds}ms)"); \ No newline at end of file diff --git a/10/HoofIt/TopoMap.cs b/10/HoofIt/TopoMap.cs new file mode 100644 index 0000000..6ab8b2f --- /dev/null +++ b/10/HoofIt/TopoMap.cs @@ -0,0 +1,119 @@ +namespace HoofIt; + +class TopoMap +{ + private static readonly Direction[] Directions = [ + Direction.Up, + Direction.Down, + Direction.Left, + Direction.Right, + ]; + + private readonly string[] _grid; + + private TopoMap(string[] input) + { + _grid = input; + } + + public static TopoMap From(string[] input) => new(input); + + public List GetTrailheadRatings() + { + var ratings = new List(); + + WalkMap((x, y) => + { + var hasHeight = int.TryParse(_grid[y][x].ToString(), out var currentHeight); + + if (hasHeight is false || currentHeight is not 0) + { + return; + } + + var rating = GetPathsForTrail(new(x, y, currentHeight)); + ratings.Add(rating); + }); + + return ratings; + } + + public List GetTrailheadScores() + { + var trailScores = new List(); + + WalkMap((x, y) => + { + var hasHeight = int.TryParse(_grid[y][x].ToString(), out var currentHeight); + + if (hasHeight is false || currentHeight is not 0) + { + return; + } + + var score = GetPathsForTrail(new(x, y, currentHeight), needToBeUnique: true); + trailScores.Add(score); + }); + + return trailScores; + } + + private int GetPathsForTrail(Position trailhead, bool needToBeUnique = false) + { + var trailheadScore = 0; + var visitedPositions = new HashSet(); + var positionsToVisit = new Queue(); + + positionsToVisit.Enqueue(trailhead); + + while (positionsToVisit.Count is not 0) + { + var currentPosition = positionsToVisit.Dequeue(); + + if (needToBeUnique && visitedPositions.Contains(currentPosition)) + { + continue; + } + + visitedPositions.Add(currentPosition); + + if (currentPosition.Height is 9) + { + trailheadScore++; + continue; + } + + foreach (var direction in Directions) + { + if (currentPosition.TryGetNextPosition(direction, _grid, out var newPosition) is false) + { + continue; + } + + var heightDifference = newPosition.Height - currentPosition.Height; + + if (heightDifference is not 1) + { + continue; + } + + positionsToVisit.Enqueue(newPosition); + } + } + + return trailheadScore; + } + + private void WalkMap(Action callback) + { + for (var y = 0; y < _grid.Length; y++) + { + var row = _grid[y]; + + for (var x = 0; x < row.Length; x++) + { + callback(x, y); + } + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index ecae4a7..fcf2c72 100644 --- a/README.md +++ b/README.md @@ -53,3 +53,4 @@ dotnet build | 07 | [Problem](./07/PROBLEM.md) | [Solution](./07/BridgeRepair/) | Not perfect or pretty, but good enough | | 08 | [Problem](./08/PROBLEM.md) | [Solution](./08/ResonantCollinearity/) | Without some help from chat I don't know where I'd be...to be fair the description kind of mislead me. | | 09 | [Problem](./09/PROBLEM.md) | [Solution](./09/DiskFragmenter/) | Doing things with indexes is error prone. | +| 10 | [Problem](./10/PROBLEM.md) | [Solution](./10/HoofIt/) | I recognized the solution for this based on pattern from last year. Solved using BFS. |