Compare commits

...

3 Commits

Author SHA1 Message Date
da81f67b7f feat: solve part two 2025-12-09 20:59:57 +01:00
caa7da5a7d test: add reverse unit test for part two 2025-12-09 20:59:46 +01:00
eebe707ef9 feat: solve part one 2025-12-09 20:40:59 +01:00
2 changed files with 132 additions and 2 deletions

View File

@@ -0,0 +1,119 @@
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
}

View File

@@ -1,18 +1,29 @@
package dayfour package dayfour
import "testing" import (
"testing"
)
var testInput = []string{ var testInput = []string{
"aaaaa-bbb-z-y-x-123[abxyz]", "aaaaa-bbb-z-y-x-123[abxyz]",
"a-b-c-d-e-f-g-h-987[abcde]", "a-b-c-d-e-f-g-h-987[abcde]",
"not-a-real-room-404[oarel]", "not-a-real-room-404[oarel]",
"totally-real-room-200[decoy]", "totally-real-room-200[decoy]",
"ijmockjgz-storage-5[gjoac]",
} }
func TestPartOne(t *testing.T) { func TestPartOne(t *testing.T) {
expected := 1514 expected := 1519
got := PartOne(testInput) got := PartOne(testInput)
if got != expected { if got != expected {
t.Errorf("PartOne() = %d, want %d", got, expected) t.Errorf("PartOne() = %d, want %d", got, expected)
} }
} }
func TestPartTwo(t *testing.T) {
expected := 5
got := PartTwo(testInput)
if got != expected {
t.Errorf("PartTwo() = %d, want %d", got, expected)
}
}