190 lines
3.6 KiB
Go
190 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func main() {
|
|
var level slog.Level
|
|
if err := level.UnmarshalText([]byte(os.Getenv("DEBUG_LEVEL"))); err == nil && level != 0 {
|
|
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level})))
|
|
}
|
|
|
|
if len(os.Args) != 2 {
|
|
fmt.Fprintln(os.Stderr, "Usage: day05 FILE")
|
|
}
|
|
|
|
input, err := os.Open(os.Args[1])
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
}
|
|
|
|
scan := bufio.NewScanner(input)
|
|
|
|
minLocation, minRangeLocation := run(scan)
|
|
|
|
fmt.Println("min location:", minLocation)
|
|
fmt.Println("min range location:", minRangeLocation)
|
|
|
|
}
|
|
|
|
func run(scan *bufio.Scanner) (int, int) {
|
|
var seeds []int
|
|
var seedRanges []int
|
|
lookup := map[string]*Lookup{}
|
|
|
|
for scan.Scan() {
|
|
text := scan.Text()
|
|
if strings.HasPrefix(text, "seeds:") && len(seeds) == 0 {
|
|
seeds, seedRanges = readSeeds(text)
|
|
}
|
|
|
|
lookup = readMaps(scan)
|
|
}
|
|
|
|
find := NewFinder(
|
|
lookup["seed-to-soil"],
|
|
lookup["soil-to-fertilizer"],
|
|
lookup["fertilizer-to-water"],
|
|
lookup["water-to-light"],
|
|
lookup["light-to-temperature"],
|
|
lookup["temperature-to-humidity"],
|
|
lookup["humidity-to-location"],
|
|
)
|
|
|
|
seedLocations := make([]int, len(seeds))
|
|
for i, s := range seeds {
|
|
seedLocations[i] = find.Find(s)
|
|
}
|
|
minLocation := min(seedLocations...)
|
|
|
|
seedRangeLocations := make([]int, len(seedRanges))
|
|
for i, s := range seedRanges {
|
|
seedRangeLocations[i] = find.Find(s)
|
|
}
|
|
minRangeLocation := min(seedRangeLocations...)
|
|
|
|
return minLocation, minRangeLocation
|
|
}
|
|
|
|
|
|
func readSeeds(text string) ([]int, []int) {
|
|
var seeds, seedRanges []int
|
|
sp := strings.Fields(strings.TrimPrefix(text, "seeds: "))
|
|
for i, s := range sp {
|
|
n, _ := strconv.Atoi(s)
|
|
seeds = append(seeds, n)
|
|
|
|
if i%2 == 0 {
|
|
seedRanges = append(seedRanges, n)
|
|
} else {
|
|
lastN := seedRanges[len(seedRanges)-1]
|
|
r := make([]int, n-1)
|
|
for i := range r {
|
|
r[i] = lastN + i + 1
|
|
}
|
|
seedRanges = append(seedRanges, r...)
|
|
}
|
|
}
|
|
return seeds, seedRanges
|
|
}
|
|
|
|
func readMaps(scan *bufio.Scanner) map[string]*Lookup {
|
|
var cur *Lookup
|
|
lookup := make(map[string]*Lookup)
|
|
for scan.Scan() {
|
|
text := scan.Text()
|
|
|
|
if strings.HasSuffix(text, "map:") {
|
|
if cur != nil {
|
|
cur.Sort()
|
|
}
|
|
cur = &Lookup{}
|
|
title := strings.TrimSuffix(text, " map:")
|
|
lookup[title] = cur
|
|
}
|
|
|
|
numbers := strings.Fields(text)
|
|
if len(numbers) == 3 {
|
|
rng := make([]int, 3)
|
|
for i, s := range numbers {
|
|
n, _ := strconv.Atoi(s)
|
|
rng[i] = n
|
|
}
|
|
cur.Add(rng[1], rng[0], rng[2])
|
|
}
|
|
}
|
|
return lookup
|
|
}
|
|
|
|
type Range struct {
|
|
src int
|
|
dest int
|
|
len int
|
|
}
|
|
type Ranges []Range
|
|
|
|
func (r Ranges) Len() int { return len(r) }
|
|
func (r Ranges) Less(i, j int) bool { return r[i].src < r[j].src }
|
|
func (r Ranges) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
|
|
|
type Lookup struct {
|
|
ranges Ranges
|
|
}
|
|
|
|
func (l *Lookup) Add(src, dest, len int) {
|
|
if l == nil {
|
|
return
|
|
}
|
|
l.ranges = append(l.ranges, Range{src, dest, len})
|
|
}
|
|
func (l *Lookup) Sort() {
|
|
sort.Sort(sort.Reverse(l.ranges))
|
|
}
|
|
func (l *Lookup) Find(n int) int {
|
|
for _, r := range l.ranges {
|
|
if n >= r.src && n <= r.src+r.len {
|
|
diff := n - r.src
|
|
if diff < r.len {
|
|
return r.dest + diff
|
|
}
|
|
}
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
type Finder struct {
|
|
stack []*Lookup
|
|
}
|
|
|
|
func NewFinder(stack ...*Lookup) *Finder {
|
|
return &Finder{stack: stack}
|
|
}
|
|
func (f *Finder) Find(n int) int {
|
|
// fmt.Print("Find: ")
|
|
for _, l := range f.stack {
|
|
// fmt.Print(n, "->")
|
|
n = l.Find(n)
|
|
// fmt.Print(n, " ")
|
|
}
|
|
// fmt.Println("")
|
|
return n
|
|
}
|
|
|
|
func min(arr ...int) int {
|
|
m := arr[0]
|
|
for _, a := range arr[1:] {
|
|
if m > a {
|
|
m = a
|
|
}
|
|
}
|
|
return m
|
|
}
|