Files
advent-of-code/internal/2018/DayFour/code.go
2025-12-07 00:00:16 +01:00

134 lines
3.2 KiB
Go

package dayfour
import (
"os"
"regexp"
"slices"
"strconv"
"strings"
"time"
"advent-of-code/internal/registry"
)
var (
guardRegex = regexp.MustCompile(`Guard #(\d+) begins shift`)
timeLayout = "2006-01-02 15:04"
)
func init() {
registry.Register("2018D4", ParseInput, PartOne, PartTwo)
}
func ParseInput(filepath string) []string {
content, _ := os.ReadFile(filepath)
return strings.Split(string(content), "\n")
}
type record struct {
timestamp time.Time
action string
guardID int
}
func PartOne(data []string) int {
records := make([]record, 0, len(data))
for _, line := range data {
timeStr, action, _ := strings.Cut(line[1:], "] ")
timestamp, _ := time.Parse(timeLayout, timeStr)
guardID := -1
if matches := guardRegex.FindStringSubmatch(action); matches != nil {
guardID, _ = strconv.Atoi(matches[1])
}
records = append(records, record{timestamp, action, guardID})
}
slices.SortFunc(records, func(a, b record) int {
return a.timestamp.Compare(b.timestamp)
})
guardSleepMinutes := make(map[int]int)
guardMinuteCount := make(map[int]map[int]int)
currentGuard := -1
for i := range records {
if records[i].guardID != -1 {
currentGuard = records[i].guardID
if guardMinuteCount[currentGuard] == nil {
guardMinuteCount[currentGuard] = make(map[int]int)
}
}
if records[i].action == "falls asleep" && i+1 < len(records) {
start, end := records[i].timestamp.Minute(), records[i+1].timestamp.Minute()
guardSleepMinutes[currentGuard] += end - start
for m := start; m < end; m++ {
guardMinuteCount[currentGuard][m]++
}
}
}
maxGuard, maxMinutes := -1, 0
for id, minutes := range guardSleepMinutes {
if minutes > maxMinutes {
maxGuard, maxMinutes = id, minutes
}
}
maxMinute, maxCount := -1, 0
for m, count := range guardMinuteCount[maxGuard] {
if count > maxCount {
maxMinute, maxCount = m, count
}
}
return maxGuard * maxMinute
}
func PartTwo(data []string) int {
records := make([]record, 0, len(data))
for _, line := range data {
timeStr, action, _ := strings.Cut(line[1:], "] ")
timestamp, _ := time.Parse(timeLayout, timeStr)
guardID := -1
if matches := guardRegex.FindStringSubmatch(action); matches != nil {
guardID, _ = strconv.Atoi(matches[1])
}
records = append(records, record{timestamp, action, guardID})
}
slices.SortFunc(records, func(a, b record) int {
return a.timestamp.Compare(b.timestamp)
})
guardMinuteCount := make(map[int]map[int]int)
currentGuard := -1
for idx := range records {
if records[idx].guardID != -1 {
currentGuard = records[idx].guardID
if guardMinuteCount[currentGuard] == nil {
guardMinuteCount[currentGuard] = make(map[int]int)
}
}
if records[idx].action == "falls asleep" && idx+1 < len(records) {
start, end := records[idx].timestamp.Minute(), records[idx+1].timestamp.Minute()
for m := start; m < end; m++ {
guardMinuteCount[currentGuard][m]++
}
}
}
maxGuard, maxMinute, maxCount := -1, -1, 0
for guardID, minuteCounts := range guardMinuteCount {
for minute, count := range minuteCounts {
if count > maxCount {
maxGuard, maxMinute, maxCount = guardID, minute, count
}
}
}
return maxGuard * maxMinute
}