adventofcode/2023/day05/main.go

219 lines
4.4 KiB
Go
Raw Normal View History

2023-12-05 17:23:48 +00:00
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)
}