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) }