155 lines
5.3 KiB
C#
155 lines
5.3 KiB
C#
using AdventOfCode.Core;
|
|
using AdventOfCode.Core.Shared.Grid;
|
|
using System.Linq.Expressions;
|
|
|
|
namespace AdventOfCode.Solutions._2023
|
|
{
|
|
public class Day10(InputReader reader) : IChallange
|
|
{
|
|
private InputReader _inputReader = reader;
|
|
|
|
public async Task<string> GetSolutionPart1()
|
|
{
|
|
Grid<Pipe> grid = await _inputReader.ReadToGrid<Pipe>();
|
|
return GetLoop(grid).Max(p => p.Steps).ToString();
|
|
}
|
|
|
|
public async Task<string> GetSolutionPart2()
|
|
{
|
|
Grid<Pipe> grid = await _inputReader.ReadToGrid<Pipe>();
|
|
_ = GetLoop(grid); // marks all the pipes from the main loop
|
|
|
|
int inside = 0;
|
|
|
|
for (int rowIndex = 0; rowIndex < grid.Rows; rowIndex++)
|
|
{
|
|
char prevSegment = '.';
|
|
int pipesCrossed = 0;
|
|
for (int columnIndex = 0; columnIndex < grid.Columns; columnIndex++)
|
|
{
|
|
// filter the special cases that need checking
|
|
Pipe value = grid.GetNode(columnIndex, rowIndex);
|
|
|
|
if (value.Steps >= 0) // this is in the main loop
|
|
{
|
|
if (value.Value == '-') // never crosses, also to not update prev
|
|
continue;
|
|
|
|
if ((prevSegment == 'L' && value.Value == '7') // this is L7 meaning that there is no crossing
|
|
|| prevSegment == 'F' && value.Value == 'J') // this is FJ meaning that there is no crossing
|
|
{
|
|
prevSegment = value.Value;
|
|
continue;
|
|
}
|
|
|
|
prevSegment = value.Value;
|
|
pipesCrossed++;
|
|
continue;
|
|
}
|
|
|
|
if (pipesCrossed % 2 == 1) // inside the main loop
|
|
inside++;
|
|
}
|
|
}
|
|
|
|
return inside.ToString();
|
|
}
|
|
|
|
private List<Pipe> GetLoop(Grid<Pipe> grid)
|
|
{
|
|
Pipe startPipe = grid.FindWithValue('S').First();
|
|
startPipe.Steps = 0;
|
|
|
|
Pipe[] lineToWalk = grid.GetNeighbors(startPipe, false)
|
|
.Where(n => n.Value != '.')
|
|
.Where(n => n.GetNeighbors().Any(np => np.X == startPipe.X && np.Y == startPipe.Y))
|
|
.ToArray();
|
|
|
|
for (int i = 0; i < lineToWalk.Length; i++)
|
|
lineToWalk[i].From = startPipe;
|
|
|
|
List<Pipe> mainLoop = [startPipe];
|
|
|
|
int steps = 1;
|
|
while (true)
|
|
{
|
|
|
|
List<Pipe> nextLoop = [];
|
|
foreach (Pipe line in lineToWalk)
|
|
{
|
|
line.Steps = steps;
|
|
|
|
// update the pipe in the grid with the steps
|
|
grid.UpdateNode(line);
|
|
mainLoop.Add(line);
|
|
// get the pipe location
|
|
Pipe next = line.GetNextPipe();
|
|
|
|
// get the actual pipe from the grid
|
|
next = grid.GetNode(next.X, next.Y);
|
|
if (next.Steps != -1)
|
|
{
|
|
return mainLoop;
|
|
}
|
|
next.From = line;
|
|
|
|
nextLoop.Add(next);
|
|
}
|
|
lineToWalk = [.. nextLoop];
|
|
steps++;
|
|
}
|
|
}
|
|
|
|
public class Pipe : Point
|
|
{
|
|
public Pipe From { get; set; } = null;
|
|
|
|
public int Steps { get; set; } = -1;
|
|
|
|
public Pipe GetNextPipe()
|
|
{
|
|
if (From == null)
|
|
throw new ArgumentException("From not set");
|
|
var neighbors = GetNeighbors();
|
|
return neighbors.First(n => n.X != From.X || n.Y != From.Y);
|
|
}
|
|
|
|
public IEnumerable<Pipe> GetNeighbors()
|
|
{
|
|
switch (Value)
|
|
{
|
|
case '|':
|
|
yield return GetNorth();
|
|
yield return GetSouth();
|
|
yield break;
|
|
case '-':
|
|
yield return GetWest();
|
|
yield return GetEast();
|
|
yield break;
|
|
|
|
case 'L':
|
|
yield return GetNorth();
|
|
yield return GetEast();
|
|
yield break;
|
|
case 'J':
|
|
yield return GetNorth();
|
|
yield return GetWest();
|
|
yield break;
|
|
case '7':
|
|
yield return GetSouth();
|
|
yield return GetWest();
|
|
yield break;
|
|
case 'F':
|
|
yield return GetSouth();
|
|
yield return GetEast();
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
private Pipe GetNorth() => new() { X = this.X, Y = this.Y - 1 };
|
|
private Pipe GetSouth() => new() { X = this.X, Y = this.Y + 1 };
|
|
private Pipe GetWest() => new() { X = this.X - 1, Y = this.Y };
|
|
private Pipe GetEast() => new() { X = this.X + 1, Y = this.Y };
|
|
}
|
|
}
|
|
} |