Compare commits
3 Commits
8960dcb072
...
da81f67b7f
| Author | SHA1 | Date | |
|---|---|---|---|
| da81f67b7f | |||
| caa7da5a7d | |||
| eebe707ef9 |
119
internal/2016/DayFour/code.go
Normal file
119
internal/2016/DayFour/code.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user