2023 Day 5 Complete!
This commit is contained in:
218
2023/day05/main.go
Normal file
218
2023/day05/main.go
Normal file
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user