package main import ( "fmt" "sort" "strconv" "strings" "time" "internal/util" ) func main() { data := util.ReadInput() shifts := ParseInput(data) guards := BuildGuards(shifts) sleepiest, sleepiestMinute := FindSleepiestData(guards) fmt.Printf("Sleepiest Guard: %d\n", sleepiest.id) fmt.Printf("Sleepiest Minute: %d\n", sleepiestMinute) fmt.Printf("Result: %d\n", sleepiest.id*sleepiestMinute) } func FindSleepiestData(guards map[int]*Guard) (*Guard, int) { var sleepiest *Guard for _, guard := range guards { if sleepiest == nil { sleepiest = guard continue } if guard.sleepTotal > sleepiest.sleepTotal { sleepiest = guard } } sleepiestMinute := -1 sleepiestCount := 0 for minute, sleepiness := range sleepiest.sleep { if sleepiness > sleepiestCount { sleepiestCount = sleepiness sleepiestMinute = minute } } return sleepiest, sleepiestMinute } type Guard struct { id int sleep [60]int sleepTotal int } type SleepEvent struct { start, end int } type Shift struct { start time.Time guard int sleepEvents []*SleepEvent } // ParseInput takes the input data and creates a series of "Shift" objects, // which are just a structured form of the data. func ParseInput(rawData []string) []*Shift { sort.Strings(rawData) shifts := []*Shift{} var currentShift *Shift var currentSleepEvent *SleepEvent for _, line := range rawData { data := strings.Split(line, "]") timestamp, _ := time.Parse("2006-01-02 15:04", data[0][1:]) event := data[1][1:] // handle beginning of shift if strings.HasPrefix(event, "Guard #") { // save old shift first if currentShift != nil { shifts = append(shifts, currentShift) } // now extract the guard ID and create a new Shift object guardID, _ := strconv.Atoi(strings.Split(event, " ")[1][1:]) currentShift = &Shift{ guard: guardID, start: timestamp, } } if strings.HasPrefix(event, "falls") { currentSleepEvent = &SleepEvent{ start: timestamp.Minute(), } } if strings.HasPrefix(event, "wakes") { currentSleepEvent.end = timestamp.Minute() currentShift.sleepEvents = append(currentShift.sleepEvents, currentSleepEvent) } } // save last shift shifts = append(shifts, currentShift) return shifts } // BuildGuards takes the shift data created by ParseInput and builds a map of // 'guards', with a per-minute counter (and handy sum) of their sleep habits. func BuildGuards(data []*Shift) map[int]*Guard { guards := make(map[int]*Guard) for _, shift := range data { // fetch the guard for this shift, initialize if necessary var guard *Guard // declaring 'ok' here seems to be the only way to keep 'guard' from becoming // a local variable inside the conditional. This seems like a flaw. var ok bool if guard, ok = guards[shift.guard]; !ok { guard = &Guard{ id: shift.guard, } guards[guard.id] = guard } for _, event := range shift.sleepEvents { guard.sleepTotal += event.end - event.start for i := event.start; i < event.end; i++ { guard.sleep[i]++ } } } return guards }