From 34cbb141a31bc457918815f25e0f28b0795a4b56 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:34:43 +0000 Subject: [PATCH] feat(2023-05): Add day 5 --- AdventOfCode/AOC.Tests/AOC.Tests.csproj | 67 ++--- AdventOfCode/AOC.Tests/Y2023/Data/Day05.dat | 249 +++++++++++++++++++ AdventOfCode/AOC.Tests/Y2023/Day05.cs | 244 ++++++++++++++++++ AdventOfCode/AOC.Tests/Y2023/Tasks/Day05.txt | 108 ++++++++ 4 files changed, 635 insertions(+), 33 deletions(-) create mode 100644 AdventOfCode/AOC.Tests/Y2023/Data/Day05.dat create mode 100644 AdventOfCode/AOC.Tests/Y2023/Day05.cs create mode 100644 AdventOfCode/AOC.Tests/Y2023/Tasks/Day05.txt diff --git a/AdventOfCode/AOC.Tests/AOC.Tests.csproj b/AdventOfCode/AOC.Tests/AOC.Tests.csproj index 1f13e67..b181c11 100644 --- a/AdventOfCode/AOC.Tests/AOC.Tests.csproj +++ b/AdventOfCode/AOC.Tests/AOC.Tests.csproj @@ -13,41 +13,42 @@ true - - - - Always - - - - - - - Always - - - - - - - Always - - - - - - - Always - - + + + + Always + + - - - - - - + + + Always + + + + + + + Always + + + + + + + Always + + + + + + + + + + + diff --git a/AdventOfCode/AOC.Tests/Y2023/Data/Day05.dat b/AdventOfCode/AOC.Tests/Y2023/Data/Day05.dat new file mode 100644 index 0000000..932c1e1 --- /dev/null +++ b/AdventOfCode/AOC.Tests/Y2023/Data/Day05.dat @@ -0,0 +1,249 @@ +seeds: 763445965 78570222 1693788857 146680070 1157620425 535920936 3187993807 180072493 1047354752 20193861 2130924847 274042257 20816377 596708258 950268560 11451287 3503767450 182465951 3760349291 265669041 + +seed-to-soil map: +0 1894195346 315486903 +1184603419 2977305241 40929361 +1225532780 597717 4698739 +1988113706 1603988885 78481073 +679195301 529385087 505408118 +1781158512 2285166785 39457705 +352613463 2324624490 326581838 +1820616217 1738931330 104130014 +2066594779 2671974456 78036460 +1288754536 1682469958 56461372 +1371340411 3442489267 409818101 +3341036988 1092718505 511270380 +315486903 1857068786 37126560 +1924746231 2209682249 49360033 +1345215908 2259042282 26124503 +2917167497 2651206328 20768128 +1230231519 1034793205 57925300 +2144631239 3421335965 21153302 +2689873172 2750010916 227294325 +1974106264 1843061344 14007442 +2165784541 5296456 524088631 +1288156819 0 597717 +2937935625 3018234602 403101363 + +soil-to-fertilizer map: +3849355959 3101885274 170573538 +3006688391 3025445580 66663107 +1656285214 0 92188755 +2361256790 4033128077 180329908 +2073349245 3676064680 287907545 +3458323699 4213457985 81509311 +2607585697 2380345050 99923790 +2541586698 3332501375 61727497 +3789313396 3272458812 60042563 +1858319884 917612384 162600389 +404885474 510011966 407600418 +4019929497 3603127472 72937208 +812485892 1190058688 253881561 +2603314195 2073349245 4271502 +1469824760 92188755 186460454 +0 278649209 231362757 +1249323571 1443940249 220501189 +4285190709 3092108687 9776587 +1066367453 1817839802 182956118 +3257369783 2724171240 184379320 +2707509487 2480268840 111317034 +2818826521 2192483180 187861870 +1748473969 1080212773 109845915 +3441749103 3394228872 16574596 +3142507350 2077620747 114862433 +3539833010 2908550560 116895020 +231362757 1664441438 153398364 +384761121 2000795920 20124353 +3656728030 2591585874 132585366 +3073351498 3963972225 69155852 +4092866705 3410803468 192324004 + +fertilizer-to-water map: +2289944373 962149121 35670423 +3187295333 3295892606 34862460 +3798694276 3187295333 48036047 +3633796172 3644673845 164898104 +3222157793 3909959418 311250910 +922447685 2248438960 142418952 +648510907 2390857912 2187794 +3533408703 3809571949 100387469 +650698701 1840321280 271748984 +464371310 553340958 30176545 +3907291549 3330755066 313918779 +2791296727 461310335 25110510 +397451197 486420845 66920113 +3846730323 3235331380 60561226 +2575399801 2393045706 96151163 +1064866637 583517503 106955439 +1308190772 822897256 139251865 +0 236670371 224639964 +2671550964 0 119745763 +1171822076 2112070264 136368696 +224639964 690472942 132424314 +494547855 2489196869 153963052 +2498862112 160132682 76537689 +1447442637 997819544 842501736 +357064278 119745763 40386919 +2325614796 2643159921 173247316 + +water-to-light map: +3181220930 3836164803 159006567 +1405924027 2473049234 39930303 +1275636734 1411161599 10830099 +3831476263 1580605379 266491658 +1570444205 1294782382 116379217 +3133505036 2271845049 47715894 +1305612481 2512979537 100311546 +1286466833 1275636734 19145648 +167024508 145118817 64336111 +92584517 615923894 40913901 +2227655152 2098041405 130620812 +2181794752 2613291083 34326669 +433451288 209454928 64553504 +4223684323 2228662217 43182832 +3527642272 3532330812 303833991 +2855274869 2647617752 119788915 +4266867155 2319560943 28100141 +231360619 1167459710 18705935 +2136463524 3995171370 45331228 +2978600831 1421991698 154904205 +4097967921 2825698355 78160170 +0 489813287 92584517 +1686823422 3023927616 328136942 +2014960364 2903858525 116532044 +1512152517 2767406667 58291688 +395185371 1129193793 38265917 +498004792 274008432 215804855 +2975063784 3020390569 3537047 +3340227497 3352064558 180266254 +3524203227 4040502598 3439045 +713809647 656837795 472355998 +1445854330 2395217316 66298187 +3520493751 1576895903 3709476 +4176128091 2347661084 47556232 +2216121421 2461515503 11533731 +2604330501 1847097037 250944368 +2358275964 4043941643 246054537 +133498418 582397804 33526090 +2131492408 4289996180 4971116 +250066554 0 145118817 + +light-to-temperature map: +1212130960 1395832798 58636484 +542019293 812004214 195851408 +1706861165 2688952641 3198757 +3097592274 3018951608 116723898 +1148165381 3135675506 2936711 +938881022 300892136 26815610 +2884354982 3318488806 103553821 +789335095 421331130 149545927 +3270968199 1036156523 69989778 +1142047574 1030038716 6117807 +1710059922 1367448764 28384034 +3961474833 1257923390 109525374 +300892136 570877057 241127157 +2564647329 3422042627 200129932 +3534803741 2001828127 16273450 +1738443956 1454469282 486329977 +3717055162 2018101577 244419671 +4071000207 2781125469 223967089 +1030038716 3005092558 13859050 +737870701 358179343 20992797 +3388483069 4209077325 84901810 +3551077191 3622172559 62988195 +3614065386 2769635501 11489968 +3215304333 3262824940 55663866 +2278270544 2395185346 286376785 +1151102092 1940799259 61028868 +3625555354 4044566844 91499808 +2987908803 1106146301 109683471 +1134657064 2681562131 7390510 +2236365744 2353280546 41904800 +1270767444 3685160754 311880998 +1043897766 2262521248 90759298 +2806870879 2692151398 77484103 +3340957977 3997041752 47525092 +3214316172 4293979135 988161 +2764777261 1215829772 42093618 +965696632 379172140 42158990 +3473384879 4147658463 61418862 +2224773933 4136066652 11591811 +1582648442 3201806221 61018719 +758863498 327707746 30471597 +1643667161 3138612217 63194004 + +temperature-to-humidity map: +3893671309 2897869925 193525827 +1232495182 999263760 191357681 +2440090529 1477966061 163000786 +3554817869 2745268041 3258240 +2845194314 3091395752 30017739 +4087197136 1397536284 33207741 +131252236 827398049 11627380 +2183328852 2476037434 27127608 +2603091315 2503165042 242102999 +2875212053 1778930312 348370034 +1427151141 891672290 77843721 +1642958327 3928570372 200739395 +3610489285 3219006125 283182024 +3277129360 3580618829 128344865 +2233175686 1190621441 206914843 +891672290 4129309767 165657529 +0 205218291 35419402 +3584280491 3551111757 26208794 +4247745260 1430744025 47222036 +2210456460 3502188149 21171921 +1843697722 2136406304 209010485 +1423852863 3577320551 3298278 +348097907 336470527 490927522 +1057329819 3708963694 175165363 +2231628381 3549564452 1547305 +3268023402 2127300346 9105958 +142879616 0 205218291 +3558076109 3523360070 26204382 +2052708207 2345416789 130620645 +4120404877 969516011 29747749 +35419402 240637693 95832834 +4150152626 3121413491 97592634 +3223582087 3884129057 44441315 +1504994862 1640966847 137963465 +3405474225 2748526281 149343644 + +humidity-to-location map: +3554305993 3441523152 94289982 +4052786034 3015714705 242181262 +491965155 895492593 72049890 +2437667299 3726944956 38441480 +0 387558480 141314689 +3811971022 3765386436 101972362 +3648595975 3535813134 51583790 +1318420824 1500487572 135197760 +230758172 30039204 163596268 +3097413876 4221249785 73717511 +3041068351 3257895967 56345525 +1454254795 641271247 181430537 +394354440 1139828795 97610715 +3215016925 2618366793 339289068 +827848339 193635472 113349295 +150184459 306984767 80573713 +1778801395 2099048921 519317872 +1176891800 1379491771 111489820 +1288381620 0 30039204 +3171131387 3891749067 43885538 +564015045 528873169 112398078 +3913943384 3935634605 11560990 +2767014161 3947195595 274054190 +941197634 822701784 72790809 +1453618584 1499851361 636211 +2298119267 3587396924 139548032 +3700179765 3867358798 24390269 +3742730647 1778801395 11181531 +1013988443 967542483 20851096 +141314689 1490981591 8869770 +3925504374 3314241492 127281660 +2476108779 2088802749 10246172 +676413123 988393579 151435216 +3753912178 2957655861 58058844 +2486354951 1789982926 280659210 +3724570034 2070642136 18160613 +1034839539 1237439510 142052261 \ No newline at end of file diff --git a/AdventOfCode/AOC.Tests/Y2023/Day05.cs b/AdventOfCode/AOC.Tests/Y2023/Day05.cs new file mode 100644 index 0000000..52acffc --- /dev/null +++ b/AdventOfCode/AOC.Tests/Y2023/Day05.cs @@ -0,0 +1,244 @@ +using Microsoft.Data.Sqlite; + +namespace AOC.Tests.Y2023; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class Day05 +{ + [SetUp] + public void Setup() + { + realData = File.ReadAllLines(Path.Combine(TestContext.CurrentContext.TestDirectory, "Y2023", "Data", + $"{GetThisClassName()}.dat")); + } + + protected string GetThisClassName() { return GetType().Name; } + private string[] realData; + + private record Map + { + public long DestinationRangeStart { get; set; } + public long SourceRangeStart { get; set; } + public long RangeLength { get; set; } + } + + private record Almanac + { + public List SeedsToBePlanted { get; set; } + public List SeedToSoilMap { get; set; } + public List SoilToFertiliserMap { get; set; } + public List FertiliserToWaterMap { get; set; } + public List WaterToLightMap { get; set; } + public List LightToTemperatureMap { get; set; } + public List TemperatureToHumidityMap { get; set; } + public List HumidityToLocationMap { get; set; } + } + + private List GetMap(string[] lines, string mapName) + { + List map = new(); + int lineNumber = 0; + while (lines[lineNumber] != mapName) + { + lineNumber++; + } + + lineNumber++; + while (lineNumber < lines.Length && lines[lineNumber] != "") + { + string[] mapLine = lines[lineNumber].Split(" "); + map.Add(new Map + { + DestinationRangeStart = long.Parse(mapLine[0]), + SourceRangeStart = long.Parse(mapLine[1]), + RangeLength = long.Parse(mapLine[2]) + }); + lineNumber++; + } + + return map; + } + + private long GetValueUsingMaps(List maps, long sourceValue) + { + // is the seed number within any of the ranges for the seed to soil map? + if (maps.Any(m => m.SourceRangeStart + m.RangeLength >= sourceValue && sourceValue >= m.SourceRangeStart)) + { + Map appropriateMap = maps + .OrderBy(m => m.DestinationRangeStart) + .First(m => + m.SourceRangeStart + m.RangeLength >= sourceValue && sourceValue >= m.SourceRangeStart); + + // Calculate how far to move from the start of the source range to our seed number + long increment = sourceValue - appropriateMap.SourceRangeStart; + return appropriateMap.DestinationRangeStart + increment; + } + + // Just use the same value as the source + return sourceValue; + } + + + [TestCase(@"seeds: 79 14 55 13 + +seed-to-soil map: +50 98 2 +52 50 48 + +soil-to-fertilizer map: +0 15 37 +37 52 2 +39 0 15 + +fertilizer-to-water map: +49 53 8 +0 11 42 +42 0 7 +57 7 4 + +water-to-light map: +88 18 7 +18 25 70 + +light-to-temperature map: +45 77 23 +81 45 19 +68 64 13 + +temperature-to-humidity map: +0 69 1 +1 0 69 + +humidity-to-location map: +60 56 37 +56 93 4", 35)] + [TestCase(null, 265018614)] // The actual answer + public void Part1(string input, int? expected) + { + string[] lines = input != null ? input.Split("\n") : realData; + Almanac almanac = new(); + + // Get the seeds from the first line + almanac.SeedsToBePlanted = lines[0].Split(": ")[1].Split(" ").Select(long.Parse).ToList(); + + // Get the maps when we don't know what line number they occur on + almanac.SeedToSoilMap = GetMap(lines, "seed-to-soil map:"); + almanac.SoilToFertiliserMap = GetMap(lines, "soil-to-fertilizer map:"); + almanac.FertiliserToWaterMap = GetMap(lines, "fertilizer-to-water map:"); + almanac.WaterToLightMap = GetMap(lines, "water-to-light map:"); + almanac.LightToTemperatureMap = GetMap(lines, "light-to-temperature map:"); + almanac.TemperatureToHumidityMap = GetMap(lines, "temperature-to-humidity map:"); + almanac.HumidityToLocationMap = GetMap(lines, "humidity-to-location map:"); + + List locations = new(); + + foreach (long seed in almanac.SeedsToBePlanted) + { + // Get the soil, fertilizer, water, light, temperature, humidity, and location + long soil = GetValueUsingMaps(almanac.SeedToSoilMap, seed); + long fertiliser = GetValueUsingMaps(almanac.SoilToFertiliserMap, soil); + long water = GetValueUsingMaps(almanac.FertiliserToWaterMap, fertiliser); + long light = GetValueUsingMaps(almanac.WaterToLightMap, water); + long temperature = GetValueUsingMaps(almanac.LightToTemperatureMap, light); + long humidity = GetValueUsingMaps(almanac.TemperatureToHumidityMap, temperature); + long location = GetValueUsingMaps(almanac.HumidityToLocationMap, humidity); + + Console.WriteLine($"Seed {seed} grows in location {location}"); + locations.Add(location); + } + + long result = locations.Min(); + + if (expected != null) + { + Assert.That(result, Is.EqualTo(expected.Value)); + } + + Console.WriteLine($"Part 1: {result}"); + } + + + [TestCase(@"seeds: 79 14 55 13 + +seed-to-soil map: +50 98 2 +52 50 48 + +soil-to-fertilizer map: +0 15 37 +37 52 2 +39 0 15 + +fertilizer-to-water map: +49 53 8 +0 11 42 +42 0 7 +57 7 4 + +water-to-light map: +88 18 7 +18 25 70 + +light-to-temperature map: +45 77 23 +81 45 19 +68 64 13 + +temperature-to-humidity map: +0 69 1 +1 0 69 + +humidity-to-location map: +60 56 37 +56 93 4", 46)] + [TestCase(null, 1783)] // The actual answer + public void Part2(string input, int? expected) + { + string[] lines = input != null ? input.Split("\n") : realData; + Almanac almanac = new(); + + // Get the maps when we don't know what line number they occur on + almanac.SeedToSoilMap = GetMap(lines, "seed-to-soil map:"); + almanac.SoilToFertiliserMap = GetMap(lines, "soil-to-fertilizer map:"); + almanac.FertiliserToWaterMap = GetMap(lines, "fertilizer-to-water map:"); + almanac.WaterToLightMap = GetMap(lines, "water-to-light map:"); + almanac.LightToTemperatureMap = GetMap(lines, "light-to-temperature map:"); + almanac.TemperatureToHumidityMap = GetMap(lines, "temperature-to-humidity map:"); + almanac.HumidityToLocationMap = GetMap(lines, "humidity-to-location map:"); + + List locations = new(); + + List values = lines[0].Split(": ")[1].Split(" ").Select(long.Parse).ToList(); + for (int i = 0; i < values.Count; i += 2) + { + long startRange = values[i]; + long rangeLength = values[i + 1]; + + for (long seed = startRange; seed < startRange + rangeLength; seed++) + { + // Get the soil, fertilizer, water, light, temperature, humidity, and location + long soil = GetValueUsingMaps(almanac.SeedToSoilMap, seed); + long fertiliser = GetValueUsingMaps(almanac.SoilToFertiliserMap, soil); + long water = GetValueUsingMaps(almanac.FertiliserToWaterMap, fertiliser); + long light = GetValueUsingMaps(almanac.WaterToLightMap, water); + long temperature = GetValueUsingMaps(almanac.LightToTemperatureMap, light); + long humidity = GetValueUsingMaps(almanac.TemperatureToHumidityMap, temperature); + long location = GetValueUsingMaps(almanac.HumidityToLocationMap, humidity); + + // Console.WriteLine( + // $"Seed {seed} corresponds to soil {soil}, fertiliser {fertiliser}, water {water}, light {light}, temperature {temperature}, humidity {humidity}, and location {location}"); + locations.Add(location); + } + } + + long result = locations.Min(); + + if (expected != null) + { + Assert.That(result, Is.EqualTo(expected.Value)); + } + + Console.WriteLine($"Part 2: {result}"); + } +} diff --git a/AdventOfCode/AOC.Tests/Y2023/Tasks/Day05.txt b/AdventOfCode/AOC.Tests/Y2023/Tasks/Day05.txt new file mode 100644 index 0000000..735df49 --- /dev/null +++ b/AdventOfCode/AOC.Tests/Y2023/Tasks/Day05.txt @@ -0,0 +1,108 @@ +--- Day 5: If You Give A Seed A Fertilizer --- +You take the boat and find the gardener right where you were told he would be: managing a giant "garden" that looks more to you like a farm. + +"A water source? Island Island is the water source!" You point out that Snow Island isn't receiving any water. + +"Oh, we had to stop the water because we ran out of sand to filter it with! Can't make snow with dirty water. Don't worry, I'm sure we'll get more sand soon; we only turned off the water a few days... weeks... oh no." His face sinks into a look of horrified realization. + +"I've been so busy making sure everyone here has food that I completely forgot to check why we stopped getting more sand! There's a ferry leaving soon that is headed over in that direction - it's much faster than your boat. Could you please go check it out?" + +You barely have time to agree to this request when he brings up another. "While you wait for the ferry, maybe you can help us with our food production problem. The latest Island Island Almanac just arrived and we're having trouble making sense of it." + +The almanac (your puzzle input) lists all of the seeds that need to be planted. It also lists what type of soil to use with each kind of seed, what type of fertilizer to use with each kind of soil, what type of water to use with each kind of fertilizer, and so on. Every type of seed, soil, fertilizer and so on is identified with a number, but numbers are reused by each category - that is, soil 123 and fertilizer 123 aren't necessarily related to each other. + +For example: + +seeds: 79 14 55 13 + +seed-to-soil map: +50 98 2 +52 50 48 + +soil-to-fertilizer map: +0 15 37 +37 52 2 +39 0 15 + +fertilizer-to-water map: +49 53 8 +0 11 42 +42 0 7 +57 7 4 + +water-to-light map: +88 18 7 +18 25 70 + +light-to-temperature map: +45 77 23 +81 45 19 +68 64 13 + +temperature-to-humidity map: +0 69 1 +1 0 69 + +humidity-to-location map: +60 56 37 +56 93 4 +The almanac starts by listing which seeds need to be planted: seeds 79, 14, 55, and 13. + +The rest of the almanac contains a list of maps which describe how to convert numbers from a source category into numbers in a destination category. That is, the section that starts with seed-to-soil map: describes how to convert a seed number (the source) to a soil number (the destination). This lets the gardener and his team know which soil to use with which seeds, which water to use with which fertilizer, and so on. + +Rather than list every source number and its corresponding destination number one by one, the maps describe entire ranges of numbers that can be converted. Each line within a map contains three numbers: the destination range start, the source range start, and the range length. + +Consider again the example seed-to-soil map: + +50 98 2 +52 50 48 +The first line has a destination range start of 50, a source range start of 98, and a range length of 2. This line means that the source range starts at 98 and contains two values: 98 and 99. The destination range is the same length, but it starts at 50, so its two values are 50 and 51. With this information, you know that seed number 98 corresponds to soil number 50 and that seed number 99 corresponds to soil number 51. + +The second line means that the source range starts at 50 and contains 48 values: 50, 51, ..., 96, 97. This corresponds to a destination range starting at 52 and also containing 48 values: 52, 53, ..., 98, 99. So, seed number 53 corresponds to soil number 55. + +Any source numbers that aren't mapped correspond to the same destination number. So, seed number 10 corresponds to soil number 10. + +So, the entire list of seed numbers and their corresponding soil numbers looks like this: + +seed soil +0 0 +1 1 +... ... +48 48 +49 49 +50 52 +51 53 +... ... +96 98 +97 99 +98 50 +99 51 +With this map, you can look up the soil number required for each initial seed number: + +Seed number 79 corresponds to soil number 81. +Seed number 14 corresponds to soil number 14. +Seed number 55 corresponds to soil number 57. +Seed number 13 corresponds to soil number 13. +The gardener and his team want to get started as soon as possible, so they'd like to know the closest location that needs a seed. Using these maps, find the lowest location number that corresponds to any of the initial seeds. To do this, you'll need to convert each seed number through other categories until you can find its corresponding location number. In this example, the corresponding types are: + +Seed 79, soil 81, fertilizer 81, water 81, light 74, temperature 78, humidity 78, location 82. +Seed 14, soil 14, fertilizer 53, water 49, light 42, temperature 42, humidity 43, location 43. +Seed 55, soil 57, fertilizer 57, water 53, light 46, temperature 82, humidity 82, location 86. +Seed 13, soil 13, fertilizer 52, water 41, light 34, temperature 34, humidity 35, location 35. +So, the lowest location number in this example is 35. + +What is the lowest location number that corresponds to any of the initial seed numbers? + +--- Part Two --- +Everyone will starve if you only plant such a small number of seeds. Re-reading the almanac, it looks like the seeds: line actually describes ranges of seed numbers. + +The values on the initial seeds: line come in pairs. Within each pair, the first value is the start of the range and the second value is the length of the range. So, in the first line of the example above: + +seeds: 79 14 55 13 +This line describes two ranges of seed numbers to be planted in the garden. The first range starts with seed number 79 and contains 14 values: 79, 80, ..., 91, 92. The second range starts with seed number 55 and contains 13 values: 55, 56, ..., 66, 67. + +Now, rather than considering four seed numbers, you need to consider a total of 27 seed numbers. + +In the above example, the lowest location number can be obtained from seed number 82, which corresponds to soil 84, fertilizer 84, water 84, light 77, temperature 45, humidity 46, and location 46. So, the lowest location number is 46. + +Consider all of the initial seed numbers listed in the ranges on the first line of the almanac. What is the lowest location number that corresponds to any of the initial seed numbers? \ No newline at end of file