204 lines
5.2 KiB
Go
204 lines
5.2 KiB
Go
package day5
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"math"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
type Almanach struct {
|
|
SeedToSoil,
|
|
SoilToFert,
|
|
FertToWater,
|
|
WaterToLight,
|
|
LightToTemp,
|
|
TempToHum,
|
|
HumToLocation EfficientMap
|
|
}
|
|
|
|
type Triplet struct {
|
|
target, source, rangeLen int
|
|
}
|
|
type EfficientMap struct {
|
|
mappings []Triplet
|
|
}
|
|
|
|
func (m EfficientMap) applyMap(source int) int {
|
|
for _, triplet := range m.mappings {
|
|
sourceStart, sourceEnd := triplet.source, triplet.source + triplet.rangeLen
|
|
if source >= sourceStart && source <= sourceEnd {
|
|
diff := source - sourceStart
|
|
return triplet.target + diff
|
|
}
|
|
}
|
|
|
|
return source
|
|
}
|
|
|
|
func applyMap(source int, mapping map[int]int) int {
|
|
var result int
|
|
result, found := mapping[source]
|
|
if !found {
|
|
result = source
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (a Almanach)locationForSeed(seed int) int {
|
|
// log.Print("starting calc of location for seed %n", seed)
|
|
soil := a.SeedToSoil.applyMap(seed)
|
|
fert := a.SoilToFert.applyMap(soil)
|
|
water := a.FertToWater.applyMap(fert)
|
|
light := a.WaterToLight.applyMap(water)
|
|
temp := a.LightToTemp.applyMap(light)
|
|
hum := a.TempToHum.applyMap(temp)
|
|
location := a.HumToLocation.applyMap(hum)
|
|
return location
|
|
}
|
|
|
|
func Run() int {
|
|
fmt.Println("the day 5")
|
|
inputDir := "day5/input"
|
|
seedToSoil := fmt.Sprint(inputDir, "/seed-to-soil")
|
|
log.Print("finished seed map")
|
|
soilToFert := fmt.Sprint(inputDir, "/soil-to-fertilizer")
|
|
log.Print("finished soil map")
|
|
fertToWater := fmt.Sprint(inputDir, "/fertilizer-to-water")
|
|
log.Print("finished fert map")
|
|
waterToLight := fmt.Sprint(inputDir, "/water-to-light")
|
|
log.Print("finished water map")
|
|
lightToTemp := fmt.Sprint(inputDir, "/light-to-temperature")
|
|
log.Print("finished light map")
|
|
tempToHum := fmt.Sprint(inputDir, "/temperature-to-humidity")
|
|
log.Print("finished temp map")
|
|
humToLocation := fmt.Sprint(inputDir, "/humidity-to-location")
|
|
|
|
seedsFileName := fmt.Sprint(inputDir, "/seeds")
|
|
seedsBytes, err := os.ReadFile(seedsFileName)
|
|
if err != nil {
|
|
panic(fmt.Sprint("error reading seeds file ", seedsFileName))
|
|
}
|
|
seedsLine := string(seedsBytes)
|
|
var seeds []int
|
|
for _, seedStr := range strings.Fields(seedsLine) {
|
|
seed, err := strconv.Atoi(seedStr)
|
|
if err != nil {
|
|
panic(fmt.Sprint("can't read seeds ", seedStr))
|
|
}
|
|
seeds = append(seeds, seed)
|
|
}
|
|
log.Print("finished for seeds file", seeds)
|
|
|
|
almanach := Almanach{
|
|
SeedToSoil: ReadMap(seedToSoil),
|
|
SoilToFert: ReadMap(soilToFert),
|
|
FertToWater: ReadMap(fertToWater),
|
|
WaterToLight: ReadMap(waterToLight),
|
|
LightToTemp: ReadMap(lightToTemp),
|
|
TempToHum: ReadMap(tempToHum),
|
|
HumToLocation: ReadMap(humToLocation),
|
|
}
|
|
log.Print("created almanach")
|
|
|
|
result := math.MaxInt
|
|
log.Print("before seed range")
|
|
seedRanges := make([][]int, 0)
|
|
for i := 0; i < len(seeds)-1; i += 2 {
|
|
start := seeds[i]
|
|
seedRangeLen := seeds[i+1]
|
|
seedRanges = append(seedRanges, []int{start, start+seedRangeLen})
|
|
}
|
|
|
|
rangeMins := make(chan int, len(seedRanges))
|
|
var wg sync.WaitGroup
|
|
|
|
mergedSeedRanges := merge(seedRanges)
|
|
for _, seedRange := range mergedSeedRanges {
|
|
wg.Add(1)
|
|
go func(seedRange []int) {
|
|
log.Printf("starting new range %+v. current min is %d", seedRange, result)
|
|
rangeMin := math.MaxInt
|
|
for i := seedRange[0]; i <= seedRange[1]; i++ {
|
|
location := almanach.locationForSeed(i)
|
|
if location < rangeMin {
|
|
rangeMins<- location
|
|
rangeMin = location
|
|
}
|
|
}
|
|
wg.Done()
|
|
return
|
|
}(seedRange)
|
|
}
|
|
|
|
go func() {
|
|
wg.Wait()
|
|
close(rangeMins)
|
|
}()
|
|
|
|
for rangeMin := range rangeMins {
|
|
log.Printf("processing range min : %d. cur result is %d", rangeMin, result)
|
|
if rangeMin <= result {
|
|
result = rangeMin
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// copy from https://codereview.stackexchange.com/questions/259048/merge-intervalsgolang
|
|
func merge(intervals [][]int) [][]int {
|
|
const start, end = 0, 1
|
|
|
|
var merged [][]int
|
|
|
|
if len(intervals) > 1 {
|
|
sort.Slice(intervals, func(i, j int) bool {
|
|
return intervals[i][start] < intervals[j][start]
|
|
})
|
|
}
|
|
|
|
for _, interval := range intervals {
|
|
last := len(merged) - 1
|
|
if last < 0 || interval[start] > merged[last][end] {
|
|
merged = append(merged,
|
|
[]int{start: interval[start], end: interval[end]},
|
|
)
|
|
} else if interval[end] > merged[last][end] {
|
|
merged[last][end] = interval[end]
|
|
}
|
|
}
|
|
|
|
return merged[:len(merged):len(merged)]
|
|
}
|
|
|
|
func ReadMap(filename string) EfficientMap {
|
|
log.Printf("reading in map from %s", filename)
|
|
result := make([]Triplet, 0)
|
|
bytes, err := os.ReadFile(filename)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("error reading file %s", filename))
|
|
}
|
|
text := string(bytes)
|
|
text = strings.TrimSpace(text)
|
|
for _, line := range strings.Split(text, "\n") {
|
|
nums := strings.Fields(line)
|
|
if len(nums) != 3 {
|
|
panic(fmt.Sprint("error, map line doesn't have 3 nums: ", line, nums))
|
|
}
|
|
destinaitonStart, err1 := strconv.Atoi(nums[0])
|
|
sourceStart, err2 := strconv.Atoi(nums[1])
|
|
rangeLength, err3 := strconv.Atoi(nums[2])
|
|
if err1 != nil || err2 != nil || err3 != nil {
|
|
panic(fmt.Sprint("error converting one of the numbers: ", nums))
|
|
}
|
|
result = append(result, Triplet{destinaitonStart, sourceStart, rangeLength})
|
|
}
|
|
|
|
return EfficientMap{result}
|
|
}
|