From 66ae46843558a1a99c9b02052755cee3c57a1c09 Mon Sep 17 00:00:00 2001 From: Anna Wiggins Date: Tue, 4 Dec 2018 03:23:19 -0500 Subject: [PATCH] Add day 4 solution, part 1. --- 2018/day04-1.go | 140 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 2018/day04-1.go diff --git a/2018/day04-1.go b/2018/day04-1.go new file mode 100644 index 0000000..5c01886 --- /dev/null +++ b/2018/day04-1.go @@ -0,0 +1,140 @@ +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 +}