Files
2025-12-09 20:59:57 +01:00

120 lines
2.6 KiB
Go

package dayfour
import (
"advent-of-code/internal/registry"
"os"
"regexp"
"sort"
"strconv"
"strings"
)
func init() {
registry.Register("2016D4", ParseInput, PartOne, PartTwo)
}
func ParseInput(filepath string) []string {
content, _ := os.ReadFile(filepath)
return strings.Split(string(content), "\n")
}
func isValidRoom(encryptedName string, expectedChecksum string) bool {
letterFrequency := [26]int{}
for _, character := range encryptedName {
if character >= 'a' && character <= 'z' {
letterFrequency[character-'a']++
}
}
type letterFrequencyPair struct {
letter rune
count int
}
letterFrequencyPairs := make([]letterFrequencyPair, 0, 26)
for index, frequency := range letterFrequency {
if frequency > 0 {
letterFrequencyPairs = append(letterFrequencyPairs, letterFrequencyPair{
letter: rune('a' + index),
count: frequency,
})
}
}
sort.Slice(letterFrequencyPairs, func(i, j int) bool {
if letterFrequencyPairs[i].count != letterFrequencyPairs[j].count {
return letterFrequencyPairs[i].count > letterFrequencyPairs[j].count
}
return letterFrequencyPairs[i].letter < letterFrequencyPairs[j].letter
})
if len(letterFrequencyPairs) < 5 {
return false
}
for index := range 5 {
if letterFrequencyPairs[index].letter != rune(expectedChecksum[index]) {
return false
}
}
return true
}
func decryptRoomName(encryptedName string, sectorID int) string {
result := strings.Builder{}
result.Grow(len(encryptedName))
shift := sectorID % 26
for _, char := range encryptedName {
if char == '-' {
result.WriteByte(' ')
} else if char >= 'a' && char <= 'z' {
shifted := ((int(char-'a') + shift) % 26) + 'a'
result.WriteByte(byte(shifted))
}
}
return result.String()
}
var roomPattern = regexp.MustCompile(`^(.+)-(\d+)(?:\[([a-z]{5})\])?$`)
func PartOne(data []string) int {
sum := 0
for _, line := range data {
if line == "" {
continue
}
matches := roomPattern.FindStringSubmatch(line)
if len(matches) < 4 || matches[3] == "" {
continue
}
encryptedName := matches[1]
sectorIdentifier, _ := strconv.Atoi(matches[2])
actualChecksum := matches[3]
if isValidRoom(encryptedName, actualChecksum) {
sum += sectorIdentifier
}
}
return sum
}
func PartTwo(data []string) int {
for _, line := range data {
matches := roomPattern.FindStringSubmatch(line)
encryptedName := matches[1]
sectorIdentifier, _ := strconv.Atoi(matches[2])
checksum := matches[3]
if !isValidRoom(encryptedName, checksum) {
continue
}
decrypted := decryptRoomName(encryptedName, sectorIdentifier)
if strings.Contains(decrypted, "northpole") {
return sectorIdentifier
}
}
return 0
}