139 lines
2.4 KiB
Go
139 lines
2.4 KiB
Go
package thirteen
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type bus struct {
|
|
id int
|
|
}
|
|
|
|
type note struct {
|
|
departure int
|
|
routes []bus
|
|
sequence []int
|
|
}
|
|
|
|
func notes(f string) (*note, error) {
|
|
n := ¬e{}
|
|
if err := n.load(f); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
func (n *note) load(filename string) error {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
first := true
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
if first {
|
|
time, err := strconv.Atoi(scanner.Text())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n.departure = time
|
|
first = false
|
|
continue
|
|
}
|
|
|
|
buses := strings.Split(scanner.Text(), ",")
|
|
for _, b := range buses {
|
|
if b == "x" {
|
|
n.sequence = append(n.sequence, -1)
|
|
continue
|
|
}
|
|
|
|
id, err := strconv.Atoi(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n.routes = append(n.routes, bus{id: id})
|
|
n.sequence = append(n.sequence, id)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (n *note) firstBus() int {
|
|
wait := 0
|
|
now := n.departure
|
|
|
|
for {
|
|
for _, bus := range n.routes {
|
|
if now%bus.id == 0 {
|
|
fmt.Println("Found", bus.id, "after a wait of", wait)
|
|
return wait * bus.id
|
|
}
|
|
}
|
|
|
|
now++
|
|
wait++
|
|
}
|
|
}
|
|
|
|
func (n *note) sequenceStart() int {
|
|
if len(n.sequence) == 0 {
|
|
return -1
|
|
}
|
|
|
|
time := 0
|
|
for {
|
|
// Naive interval skipping, only check times where the first bus is scheduled
|
|
interval := n.sequence[0]
|
|
found := true
|
|
for i := 1; i < len(n.sequence); i++ {
|
|
// Skip unconstrained rules
|
|
if n.sequence[i] == -1 {
|
|
continue
|
|
}
|
|
|
|
if (time+i)%n.sequence[i] == 0 {
|
|
// We know that the bus at i is correct, so we can skip interval*bus id ahead for the next time this is valid
|
|
interval *= n.sequence[i]
|
|
continue
|
|
}
|
|
|
|
// We reach this point if the sequence is broken. We now advance
|
|
// to the next time the first bus is valid
|
|
time += interval
|
|
found = false
|
|
break
|
|
}
|
|
if found {
|
|
return time
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// PartOne find the product of the wait * bus id to get to the airport
|
|
func PartOne() string {
|
|
n, err := notes("thirteen/input.txt")
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
|
|
return fmt.Sprintf("Found a product of %d", n.firstBus())
|
|
}
|
|
|
|
// PartTwo What is the answer to the consequtive departure minutes contest
|
|
func PartTwo() string {
|
|
n, err := notes("thirteen/input.txt")
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
|
|
return fmt.Sprintf("The earliest minute is %d", n.sequenceStart())
|
|
}
|