From 1c5f9604265f1932e1531a81427da23dbc8ad0f3 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Tue, 5 Dec 2023 11:23:48 -0600 Subject: [PATCH] 2023 Day 5 Complete! --- 2023/day05/input | 201 +++++++++++++++++++++++++++++++++++++++ 2023/day05/main.go | 218 +++++++++++++++++++++++++++++++++++++++++++ 2023/day05/problem | 172 ++++++++++++++++++++++++++++++++++ 2023/day05/testinput | 33 +++++++ helpers/helpers.go | 12 +++ 5 files changed, 636 insertions(+) create mode 100644 2023/day05/input create mode 100644 2023/day05/main.go create mode 100644 2023/day05/problem create mode 100644 2023/day05/testinput diff --git a/2023/day05/input b/2023/day05/input new file mode 100644 index 0000000..115b743 --- /dev/null +++ b/2023/day05/input @@ -0,0 +1,201 @@ +seeds: 2637529854 223394899 3007537707 503983167 307349251 197383535 3543757609 276648400 2296792159 141010855 116452725 5160533 2246652813 49767336 762696372 160455077 3960442213 105867001 1197133308 38546766 + +seed-to-soil map: +1024364543 1121869540 764570177 +1788934720 0 30748436 +710359306 576061773 314005237 +1819683156 509305212 66756561 +478556776 890067010 231802530 +0 30748436 478556776 + +soil-to-fertilizer map: +3998185854 3762642503 103735883 +2968507762 2068943953 132922295 +1615660383 3128249668 130118355 +730037950 2201866248 85080413 +30504699 101283437 22743985 +1963198232 3098529992 29719676 +2445396241 816829001 206574911 +4101921737 3258368023 13795434 +1745778738 3272163457 98082636 +184429295 124027422 42778761 +815118363 1688321922 16510291 +227208056 0 101283437 +3101430057 730037950 86791051 +2855369613 1073802717 8286129 +2726882317 4266444706 28522590 +2755404907 1647814534 40507388 +1992917908 4002766747 184714231 +0 307861200 20630293 +3437715786 2286946661 488868406 +2212191740 2910044568 188485424 +1875478802 3756833622 5808881 +1386084226 4200760960 31124145 +4115717171 1704832213 179250125 +2177632139 4231885105 34559601 +968017015 3370246093 57414778 +1957518504 1068122989 5679728 +20630293 205631872 9874406 +1248075951 2775815067 134229501 +1417208371 1449362522 198452012 +145603606 166806183 38825689 +3926584192 1318303542 71601662 +2651971152 3650305029 74911165 +2863655742 1884082338 104852020 +2400677164 1023403912 44719077 +1025431793 3427660871 222644158 +53248684 215506278 92354922 +2795912295 1389905204 59457318 +831628654 3866378386 136388361 +1382305452 2065165179 3778774 +1843861374 3725216194 31617428 +3188221108 1082088846 236214696 +1881287683 1988934358 76230821 +3424435804 4187480978 13279982 + +fertilizer-to-water map: +52426778 0 42548209 +2317094055 2628693938 80884528 +4273469923 2317094055 21497373 +3504443816 3801741356 19441920 +1168510873 722677906 36277136 +766143928 2147127897 113763545 +2859981871 4203009036 91958260 +3742911607 2903448576 530558316 +2533492330 3494200839 307540517 +1048443265 602610298 120067608 +1606177181 2049845518 37728052 +0 2087573570 24184027 +2228508217 539688172 62922126 +3523885736 4014822611 112866625 +3636752361 3821183276 106159246 +2204777013 515956968 23731204 +163272748 80266080 177461896 +340734644 1624436234 425409284 +1568459310 42548209 37717871 +3039420220 2338591428 290102510 +1336392351 257727976 5651080 +2397978583 3434006892 60193947 +2951940131 3927342522 87480089 +94974987 341413667 68297761 +1750150773 263379056 78034611 +1932038162 1351697383 272738851 +1828185384 897499179 73313877 +1563080665 758955042 5378645 +1643905233 409711428 106245540 +3329522730 2799034919 104413657 +1901499261 2260891442 30538901 +879907473 764333687 133165492 +1342043431 1130660149 221037234 +2458172530 4127689236 75319800 +2841032847 2709578466 18949024 +24184027 1102417398 28242751 +1204788009 970813056 131604342 +1013072965 2111757597 35370300 +3433936387 2728527490 70507429 + +water-to-light map: +71899121 201997255 39056119 +1870747295 791060221 18793056 +1109877678 2647277659 20201672 +496126127 1366401345 54786206 +2289070096 3946384506 79288366 +2872432379 3633289978 245210479 +2566896529 1931266150 305535850 +2094153218 1065408320 14844701 +1747071839 1759996387 105111897 +3528644588 1080253021 193678883 +675287597 4169571318 125395978 +1852183736 3256553708 6224195 +611190115 1486911572 64097482 +1972384294 809853277 55611058 +4292231732 3217343431 613168 +1498491937 2667479331 11999801 +1356554237 502551806 141937700 +110955240 241053374 38164682 +800683575 2316420571 309194103 +2368358462 2236802000 62837191 +2523665094 3878500457 34541659 +3488154933 3913042116 10582322 +567693713 1443415170 43496402 +2558206753 3247863932 8689776 +4065356099 2990467798 226875633 +3992305706 2625614674 21662985 +4013968691 739672813 51387408 +1330023335 1421187551 22227619 +550912333 2299639191 16781380 +1352250954 496126127 4303283 +2027995352 1865108284 66157866 +1889540351 644489506 82843943 +3117642858 3262777903 370512075 +3498737255 3217956599 29907333 +2431195653 1273931904 92469441 +1858407931 727333449 12339364 +0 130098134 71899121 +2170307311 2679479132 41006431 +2211313742 4025672872 54996286 +4292844900 500429410 2122396 +149119922 0 130098134 +1719479071 4141978550 27592768 +3722323471 2720485563 269982235 +2266310028 3923624438 22760068 +2108997919 4080669158 61309392 +1510491738 1551009054 208987333 +1130079350 865464335 199943985 + +light-to-temperature map: +2246981140 2123929713 26608756 +506825382 1211085022 100001399 +2273589896 3259651351 49795378 +606826781 48305627 403771586 +2441004040 1311086421 255128583 +2696132623 887597567 323487455 +3131241320 452077213 69964335 +1483012724 2119418493 4511220 +4100438569 4209824659 85142637 +2046786877 3309446729 95808859 +1487523944 2344091207 205790126 +3125128781 522041548 6112539 +74735386 1566215004 432089996 +2323385274 2937379483 117618766 +46634343 2549881333 28101043 +1215251469 1998305000 118258255 +3201205655 0 1671284 +3202876939 2150538469 193552738 +1010598367 3054998249 204653102 +3019620078 2577982376 105508703 +2142595736 2832994079 104385404 +1333509724 2683491079 149503000 +4185581206 4100438569 109386090 +1693314070 534124760 353472807 +3396429677 528154087 5970673 +0 1671284 46634343 +3402400350 2116563255 2855238 + +temperature-to-humidity map: +3585785215 3115915735 709182081 +1595400550 915086594 131894638 +3115915735 3825097816 469869480 +257043810 1628903919 98391269 +355435079 1106926810 521977109 +877412188 0 717988362 +59945578 717988362 197098232 +0 1046981232 59945578 + +humidity-to-location map: +2826523858 2649651094 205250361 +2028276378 1553868404 798247480 +225543770 4100328990 5722049 +595208874 2352115884 297535210 +3031774219 2854901455 205611797 +1267642800 3435411968 22064409 +138173954 4119345931 87369816 +231265819 49632084 363943055 +1877617809 1403209835 150658569 +0 413575139 138173954 +3237386016 3457476377 642852613 +892744084 3060513252 374898716 +3893533521 551749093 313182226 +1339339293 864931319 538278516 +1289707209 0 49632084 +3880238629 4106051039 13294892 diff --git a/2023/day05/main.go b/2023/day05/main.go new file mode 100644 index 0000000..1a831d7 --- /dev/null +++ b/2023/day05/main.go @@ -0,0 +1,218 @@ +package main + +import ( + "fmt" + "math" + "strings" + + h "git.bullercodeworks.com/brian/adventofcode/helpers" +) + +func main() { + inp := h.StdinToStringSlice() + part1(inp) + fmt.Println() + part2(inp) +} + +func part1(input []string) { + a := BuildAlmanac(input) + fmt.Println("# Part 1") + fmt.Println(h.SliceMin(a.findAllSeedLocations())) +} + +func part2(input []string) { + a := BuildAlmanac(input) + fmt.Println("# Part 2") + fmt.Println(a.findLowestFromPairs()) +} + +type Seed struct { + start, length int +} +type Almanac struct { + seeds []int + seedRanges []Seed + mappings map[string]*RangeMap + rangeMap *RangeMap + locRangeMap *RangeMap + mapOrder []string +} + +func BuildAlmanac(input []string) *Almanac { + a := Almanac{ + mappings: make(map[string]*RangeMap), + mapOrder: []string{ + "seed-to-soil", + "soil-to-fertilizer", + "fertilizer-to-water", + "water-to-light", + "light-to-temperature", + "temperature-to-humidity", + "humidity-to-location", + }, + } + for _, v := range strings.Fields(input[0])[1:] { + a.seeds = append(a.seeds, h.Atoi(v)) + } + i := 2 + for ; i < len(input); i++ { + wrkMap, wrkI := parseMapAt(input[i:]) + a.mappings[wrkMap.name] = wrkMap + i = i + wrkI + } + for i, m := range a.mapOrder { + if i == 0 { + a.rangeMap = a.mappings[m] + } + if i < len(a.mapOrder)-1 { + a.mappings[m].next = a.mappings[a.mapOrder[i+1]] + a.mappings[a.mapOrder[i+1]].prev = a.mappings[m] + } else { + a.mappings[m].prev = a.mappings[a.mapOrder[i-1]] + a.locRangeMap = a.mappings[m] + } + } + a.getSeedRanges() + return &a +} + +func (a *Almanac) getSeedRanges() { + for i := 0; i < len(a.seeds); i++ { + seed := a.seeds[i] + i++ + num := a.seeds[i] + a.seedRanges = append(a.seedRanges, Seed{ + start: seed, + length: num, + }) + } +} + +func (a *Almanac) findAllSeedLocations() []int { + var res []int + for i := range a.seeds { + res = append(res, a.rangeMap.find(a.seeds[i])) + } + return res +} + +func (a *Almanac) findLowestFromPairs() int { + for loc := 0; loc < math.MaxInt; loc++ { + seed := a.locRangeMap.findDest(loc) + for _, s := range a.seedRanges { + if seed >= s.start && seed <= s.start+s.length-1 { + return loc + } + } + } + return h.MIN_INT +} + +func (a Almanac) String() string { + res := fmt.Sprintln("Seeds:", a.seeds) + for i := range a.mappings { + res = fmt.Sprintf("%s\n%s\n", res, a.mappings[i]) + } + return res +} + +type RangeMap struct { + name string + next *RangeMap + prev *RangeMap + associations []Association + hereToLoc map[int]int + lastFind int +} + +func parseMapAt(input []string) (*RangeMap, int) { + name := strings.Fields(input[0])[0] + res := &RangeMap{ + name: name, + associations: []Association{}, + hereToLoc: make(map[int]int), + } + i := 1 + for ; i < len(input); i++ { + if input[i] == "" { + break + } + pts := strings.Fields(input[i]) + assoc := Association{ + startSource: h.Atoi(pts[1]), + startDest: h.Atoi(pts[0]), + assocRange: h.Atoi(pts[2]), + } + assoc.diff = assoc.startDest - assoc.startSource + res.associations = append(res.associations, assoc) + } + return res, i +} + +func (r *RangeMap) find(source int) int { + if v, ok := r.hereToLoc[source]; ok { + return v + } + next := source + for _, a := range r.associations { + if a.has(source) { + next = a.find(source) + } + } + if r.next == nil { + return next + } else { + ret := r.next.find(next) + r.hereToLoc[source] = ret + return ret + } +} +func (r *RangeMap) findDest(dest int) int { + prev := dest + for i := len(r.associations) - 1; i >= 0; i-- { + a := r.associations[i] + if a.hasDest(dest) { + prev = a.findSource(dest) + } + } + if r.prev == nil { + return prev + } else { + return r.prev.findDest(prev) + } +} +func (r *RangeMap) setLastFindResult(to int) { + r.hereToLoc[r.lastFind] = to +} + +func (r RangeMap) String() string { + res := fmt.Sprintf("%s map:", r.name) + for _, v := range r.associations { + res = fmt.Sprintf("%s\n%s", res, v) + } + return res +} + +type Association struct { + startSource int + startDest int + assocRange int + diff int +} + +func (a *Association) has(source int) bool { + return source >= a.startSource && source < a.startSource+a.assocRange +} +func (a *Association) find(source int) int { + return source + a.diff +} +func (a *Association) hasDest(dest int) bool { + return dest >= a.startDest && dest < a.startDest+a.assocRange +} +func (a *Association) findSource(dest int) int { + return dest - a.diff +} +func (a Association) String() string { + return fmt.Sprintf("%d %d %d", a.startSource, a.startDest, a.assocRange) +} diff --git a/2023/day05/problem b/2023/day05/problem new file mode 100644 index 0000000..9be56dd --- /dev/null +++ b/2023/day05/problem @@ -0,0 +1,172 @@ +Advent of Code +br0xen (AoC++) 10* + +--- 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? + + Your puzzle answer was 323142486. + +--- 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? + + Your puzzle answer was 79874951. + + Both parts of this puzzle are complete! They provide two gold stars: ** + + At this point, you should return to your Advent calendar and try another puzzle. + + If you still want to see it, you can get your puzzle input. + + You can also [Shareon Twitter Mastodon] this puzzle. + +References + + Visible links + . https://adventofcode.com/ + . https://adventofcode.com/2023/about + . https://adventofcode.com/2023/events + . https://teespring.com/stores/advent-of-code + . https://adventofcode.com/2023/settings + . https://adventofcode.com/2023/auth/logout + . Advent of Code Supporter + https://adventofcode.com/2023/support + . https://adventofcode.com/2023 + . https://adventofcode.com/2023 + . https://adventofcode.com/2023/support + . https://adventofcode.com/2023/sponsors + . https://adventofcode.com/2023/leaderboard + . https://adventofcode.com/2023/stats + . https://adventofcode.com/2023/sponsors + . https://en.wikipedia.org/wiki/Sand_filter + . https://en.wikipedia.org/wiki/Almanac + . https://adventofcode.com/2023 + . https://adventofcode.com/2023/day/5/input diff --git a/2023/day05/testinput b/2023/day05/testinput new file mode 100644 index 0000000..f756727 --- /dev/null +++ b/2023/day05/testinput @@ -0,0 +1,33 @@ +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 diff --git a/helpers/helpers.go b/helpers/helpers.go index bb51c18..fd65c77 100644 --- a/helpers/helpers.go +++ b/helpers/helpers.go @@ -389,6 +389,18 @@ func IsPrime(value int) bool { return value > 1 } +func SliceMin(sl []int) int { + switch len(sl) { + case 0: + return MIN_INT + case 1: + return sl[0] + case 2: + return Min(sl[0], sl[1]) + default: + return Min(sl[0], sl[1], sl[2:]...) + } +} func Min(v1, v2 int, vrest ...int) int { min := v2 if v1 < v2 {