120 lines
2.6 KiB
Go
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
|
|
}
|