AdventOfCode/AdventOfCode.Solutions/2023/Day 05/Day05.cs
2024-12-01 10:17:24 +01:00

222 lines
9.5 KiB
C#

using AdventOfCode.Core.Shared.IO;
namespace AdventOfCode.Solutions._2023
{
public class Day05 : IChallange
{
public int Year => 2023;
public int Day => 5;
public const string SeedSoil = "seed-to-soil map:";
public const string SoilFertilizer = "soil-to-fertilizer map:";
public const string FertilizerWater = "fertilizer-to-water map:";
public const string WaterLight = "water-to-light map:";
public const string LightTemperature = "light-to-temperature map:";
public const string TemperatureHumidity = "temperature-to-humidity map:";
public const string HumidityLocation = "humidity-to-location map:";
private readonly IInputReader _inputReader;
public Day05(IInputReader inputReader)
{
_inputReader = inputReader;
_inputReader.SetInput(this);
}
public async Task<string> GetSolutionPart1()
{
List<string> data = new(await _inputReader.ReadAsArrayString());
SeedLocationMapper[] seeds = data[0].Split(' ').Skip(1).Select(s => new SeedLocationMapper { Seed = long.Parse(s) }).ToArray();
// creating maps
int stringSeperator = data.IndexOf(SeedSoil) + 1;
List<InOutMapper> soil = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(SoilFertilizer) + 1;
List<InOutMapper> fertilizer = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(FertilizerWater) + 1;
List<InOutMapper> water = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(WaterLight) + 1;
List<InOutMapper> light = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(LightTemperature) + 1;
List<InOutMapper> temperature = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(TemperatureHumidity) + 1;
List<InOutMapper> humidity = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(HumidityLocation) + 1;
List<InOutMapper> location = MapData(data, stringSeperator, data.Count);
foreach (SeedLocationMapper seed in seeds)
{
seed.Soil = GetTargetLocation(soil, seed.Seed);
seed.Fertilizer = GetTargetLocation(fertilizer, seed.Soil);
seed.Water = GetTargetLocation(water, seed.Fertilizer);
seed.Light = GetTargetLocation(light, seed.Water);
seed.Temperature = GetTargetLocation(temperature, seed.Light);
seed.Humidity = GetTargetLocation(humidity, seed.Temperature);
seed.Location = GetTargetLocation(location, seed.Humidity);
}
return seeds.Min(seed => seed.Location).ToString();
}
public async Task<string> GetSolutionPart2()
{
List<string> data = new(await _inputReader.ReadAsArrayString());
int stringSeperator = data.IndexOf(SeedSoil) + 1;
List<InOutMapper> soil = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(SoilFertilizer) + 1;
List<InOutMapper> fertilizer = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(FertilizerWater) + 1;
List<InOutMapper> water = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(WaterLight) + 1;
List<InOutMapper> light = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(LightTemperature) + 1;
List<InOutMapper> temperature = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(TemperatureHumidity) + 1;
List<InOutMapper> humidity = MapData(data, stringSeperator, data.IndexOf(string.Empty, stringSeperator));
stringSeperator = data.IndexOf(HumidityLocation) + 1;
List<InOutMapper> location = MapData(data, stringSeperator, data.Count);
long[] seedData = data[0].Split(' ').Skip(1).Select(long.Parse).ToArray();
List<Line> ranges = [];
for(int index = 0; index < seedData.Length / 2; index++)
{
Line[] seedRange = [new Line(seedData[index * 2], 0, seedData[(index * 2) + 1])];
Line[] soilMapped = GetTargetLocation(soil, seedRange);
Line[] fertilizerMapped = GetTargetLocation(fertilizer, soilMapped);
Line[] waterMapped = GetTargetLocation(water, fertilizerMapped);
Line[] lightMapped = GetTargetLocation(light, waterMapped);
Line[] temperatureMapped = GetTargetLocation(temperature, lightMapped);
Line[] humidityMapped = GetTargetLocation(humidity, temperatureMapped);
Line[] locationMapped = GetTargetLocation(location, humidityMapped);
ranges.Add(locationMapped.OrderBy(l => l.Start.X).First());
}
return ranges.OrderBy(l => l.Start.X).First().Start.X.ToString();
}
private static List<InOutMapper> MapData(List<string> data, int startIndex, int endIndex)
{
return data
.Skip(startIndex)
.Take(endIndex - startIndex)
.Select(line => line
.Split(' ')
.Select(long.Parse)
.ToArray())
.Select(values =>
new InOutMapper (
destinationStart: values[0],
sourceStart: values[1],
range: values[2]
))
.ToList();
}
private static long GetTargetLocation(List<InOutMapper> mapping, long location)
{
InOutMapper? mapper = mapping.FirstOrDefault(mapping => mapping.Intersect(location));
return mapper != null ? location + mapper.Delta : location;
}
private static Line[] GetTargetLocation(List<InOutMapper> mapping, Line[] seedRange)
{
List<Line> resultRanges = [];
for (int seedRangeIndex = 0; seedRangeIndex < seedRange.Length; seedRangeIndex++)
{
Line currentSeedRange = seedRange[seedRangeIndex];
InOutMapper[] mappers = [.. mapping.Where(mapping => mapping.Intersect(currentSeedRange)).OrderBy(map => map.SourceStart)];
if (mappers.Length == 0)
{
// I do not expect to hit this but just to be sure.
resultRanges.Add(currentSeedRange);
continue;
}
long lastId = currentSeedRange.Start.X;
foreach(InOutMapper mapper in mappers.OrderBy(m => m.Start.X))
{
//List<Line> mapperResults = [];
if (lastId < mapper.SourceStart)
{
// there is some uneffected space infront of the mapper
resultRanges.Add(new Line(lastId, 0, mapper.SourceStart - lastId));
lastId = mapper.SourceStart;
}
long end = mapper.End.X > currentSeedRange.End.X ? currentSeedRange.End.X : mapper.End.X;
Line map = new(lastId, 0, end - lastId + 1);
map.Start.X += mapper.Delta;
map.End.X += mapper.Delta;
resultRanges.Add(map);
lastId = end + 1;
}
if (lastId < currentSeedRange.End.X)
{
Line map = new(new Point(lastId, 0, ' '), currentSeedRange.End);
resultRanges.Add(map);
}
}
// validate
if (seedRange.Sum(r => r.Length) != resultRanges.Sum(r => r.Length))
{
Console.WriteLine("Lost seeds!");
}
return [.. resultRanges];
}
private class SeedLocationMapper
{
public long Seed { get; set; }
public long Soil { get; set; }
public long Fertilizer { get; set; }
public long Water { get; set; }
public long Light { get; set; }
public long Temperature { get; set; }
public long Humidity { get; set; }
public long Location { get; set; }
}
private class InOutMapper : Line
{
public long SourceStart => base.Start.X;
public long DestinationStart { get; set; }
public long Range => base.Length;
public long Delta => DestinationStart - SourceStart;
public bool Intersect(long value) => base.Intersect(new Point(value, 0));
public InOutMapper(long sourceStart, long destinationStart, long range) : base(sourceStart, 0, range)
{
DestinationStart = destinationStart;
}
public override string ToString()
{
return $"Dest start: {DestinationStart}, Source start: {SourceStart}, Range: {Range}";
}
}
}
}