AdventOfCode/AdventOfCode.Solutions/2023/Day 03/Day03.cs
2023-12-04 09:32:44 +01:00

164 lines
6.2 KiB
C#

using AdventOfCode.Core;
using System.Text.RegularExpressions;
namespace AdventOfCode.Solutions._2023
{
public partial class Day03(InputReader reader) : IChallange
{
private InputReader _inputReader = reader;
private struct Point
{
public int X { get; set; }
public int Y { get; set; }
}
private class PartNumber
{
public int X { get; set; }
public int Y { get; set; }
public string Section { get; set; } = string.Empty;
public int Length => Section.Length;
public bool IsDigit() => Section.Length > 1;
public bool IsPartSymbol() => !IsDigit();
public override string ToString()
{
return $"[{Y},{X}] {Section}";
}
}
public async Task<string> GetSolutionPart1()
{
List<PartNumber> parts = [];
int row = 0;
int total = 0;
await foreach (string line in _inputReader.ReadAsStringLine())
{
MatchCollection matchCollection = FindPartItems().Matches(line);
parts.AddRange(matchCollection.Select(match => new PartNumber { X = match.Index, Y = row, Section = match.Value }));
row++;
}
List<PartNumber> numbers = parts.Where(p => p.IsDigit()).ToList();
List<PartNumber> symbols = parts.Where(p => p.IsPartSymbol()).ToList();
var intersected = numbers.Where(number => IsPointInAnyBox(symbols, number)).ToList();
//var intersected = numbers.Where(number => GetSection(symbols, number.X - 1, number.Y - 1, number.X + number.Length, number.Y + 1).Any()).ToList();
total = numbers.Where(number => GetSection(symbols, number.X - 1, number.Y - 1, number.X + number.Length, number.Y + 1).Any()).Sum(number => int.Parse(number.Section));
//Grid<Node> grid = await _inputReader.ReadToGrid<Node>();
//int row = 0;
//
//await foreach(string line in _inputReader.ReadAsStringLine())
//{
// MatchCollection matchCollection = FindDigits().Matches(line);
//
// foreach (Match match in matchCollection.Cast<Match>())
// {
// var section = grid.GetSection(match.Index - 1, row - 1, match.Index + match.Length, row + 1).ToList();
// bool isPartNumber = section.Any(n => !(n.Char == '.' || (n.Char >= '0' && n.Char <= '9')));
// if (isPartNumber) {
// total += int.Parse(match.Value);
// }
// }
//
// row++;
//}
return total.ToString();
}
public async Task<string> GetSolutionPart2()
{
List<PartNumber> parts = [];
int row = 0;
int total = 0;
await foreach (string line in _inputReader.ReadAsStringLine())
{
MatchCollection matchCollection = FindPartItems().Matches(line);
parts.AddRange(matchCollection.Select(match => new PartNumber { X = match.Index, Y = row, Section = match.Value }));
row++;
}
List<PartNumber> numbers = parts.Where(p => p.IsDigit()).ToList();
List<PartNumber> gears = parts.Where(p => p.IsPartSymbol() && p.Section == "*").ToList();
foreach (PartNumber gear in gears)
{
// check if there are 2 numbers around
List<PartNumber> ratios = GetSection(numbers, gear).ToList();
if (ratios.Count != 2)
continue;
int totalRatio = int.Parse(ratios[0].Section) * int.Parse(ratios[1].Section);
total += totalRatio;
}
return total.ToString();
}
private IEnumerable<PartNumber> GetSection(List<PartNumber> toSearch, int fromX, int fromY, int toX, int toY)
{
return toSearch.Where(node => node.X >= fromX && node.X + node.Length <= toX && node.Y >= fromY && node.Y <= toY);
}
private bool IsPointInAnyBox(List<PartNumber> targets, PartNumber source)
{
return targets.Any(target => IsPointInBox(target, source));
}
private bool IsPointInBox(PartNumber target, PartNumber source)
{
Point targetA = new Point { X = target.X, Y = target.Y }, targetB = new Point { X = target.X + target.Length - 1, Y = target.Y };
Point sourceA = new Point { X = source.X, Y = source.Y };
return IsPointInBox(targetA, targetB, sourceA);
}
private bool DoBoxesOverlap(List<PartNumber> targets, PartNumber source)
{
return targets.Any(target => DoBoxesOverlap(target, source));
}
private bool DoBoxesOverlap(PartNumber target, PartNumber source)
{
Point targetA = new Point { X = target.X, Y = target.Y }, targetB = new Point { X = target.X + target.Length - 1, Y = target.Y };
Point sourceA = new Point { X = source.X - 1, Y = source.Y - 1 }, sourceB = new Point { X = source.X + source.Length, Y = source.Y + 1 };
return IsPointInBox(targetA, targetB, sourceA) && IsPointInBox(targetA, targetB, sourceB);
}
private bool IsPointInBox(Point boxPointA, Point boxPointB, Point pointSource)
{
return pointSource.X >= boxPointA.X &&
pointSource.X <= boxPointB.X &&
pointSource.Y >= boxPointA.Y &&
pointSource.Y <= boxPointB.Y;
}
private IEnumerable<PartNumber> GetSection(List<PartNumber> toSearch, PartNumber source)
{
return toSearch.Where(target =>
target.X >= source.X - 1 &&
target.X + target.Length <= source.X &&
target.Y >= source.Y - 1 &&
target.Y <= source.Y + 1);
}
[GeneratedRegex("(\\d+)", RegexOptions.Compiled)]
private static partial Regex FindDigits();
[GeneratedRegex("(\\d+)|([^.])", RegexOptions.Compiled)]
private static partial Regex FindPartItems();
}
}