Add day 15 part 2

This commit is contained in:
Nick Krichevsky 2023-12-17 22:29:31 -05:00
parent ef7d3b5f08
commit 6817e25645

View file

@ -1,12 +1,83 @@
package main package main
import ( import (
"container/list"
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
"regexp"
"strconv"
"strings" "strings"
) )
type HashMap[V any] []*list.List
type HashMapItem[V any] struct {
key string
value V
}
// Operation is some sort of operation we can perform on a hashmap
type Operation[V any] func(HashMap[V])
// makeSetOperation makes an Operation that will set the given key to the given value in the hashmap
func makeSetOperation[V any](key string, value V) Operation[V] {
return func(hm HashMap[V]) {
keyHash := hash(key)
element := findInList(hm[keyHash], func(item HashMapItem[V]) bool {
return item.key == key
})
if element == nil {
hm[keyHash].PushBack(HashMapItem[V]{key: key, value: value})
} else {
element.Value = HashMapItem[V]{value: value, key: key}
}
}
}
// makeDeleteOperation makes an Operation that will delete the given key from the hashmap
func makeDeleteOperation[V any](key string) Operation[V] {
return func(hm HashMap[V]) {
keyHash := hash(key)
element := findInList(hm[keyHash], func(item HashMapItem[V]) bool {
return item.key == key
})
if element != nil {
hm[keyHash].Remove(element)
}
}
}
// NewHashMap makes a new hash map with the given value type
func NewHashMap[V any]() HashMap[V] {
m := make(HashMap[V], 256)
for i := range m {
m[i] = list.New().Init()
}
return m
}
// EntriesInBox gets all the entries in the hash map box with the given index
func (hm HashMap[V]) EntriesInBox(idx int) ([]HashMapItem[V], error) {
if idx > len(hm) {
return nil, errors.New("out of bounds box id")
}
entryList := hm[idx]
res := make([]HashMapItem[V], 0, entryList.Len())
for cursor := entryList.Front(); cursor != nil; cursor = cursor.Next() {
value := cursor.Value.(HashMapItem[V])
res = append(res, value)
}
return res, nil
}
func main() { func main() {
if len(os.Args) != 2 && len(os.Args) != 3 { if len(os.Args) != 2 && len(os.Args) != 3 {
fmt.Fprintf(os.Stderr, "Usage: %s inputfile\n", os.Args[0]) fmt.Fprintf(os.Stderr, "Usage: %s inputfile\n", os.Args[0])
@ -30,6 +101,7 @@ func main() {
inputElements := strings.Split(input, ",") inputElements := strings.Split(input, ",")
fmt.Printf("Part 1: %d\n", part1(inputElements)) fmt.Printf("Part 1: %d\n", part1(inputElements))
fmt.Printf("Part 2: %d\n", part2(inputElements))
} }
func part1(inputElements []string) int { func part1(inputElements []string) int {
@ -41,6 +113,39 @@ func part1(inputElements []string) int {
return sum return sum
} }
func part2(inputElements []string) int {
operations := make([]Operation[int], 0, len(inputElements))
for _, element := range inputElements {
operation, err := parseOperation(element)
if err != nil {
panic(fmt.Sprintf("could not parse operation %q: %s", element, err))
}
operations = append(operations, operation)
}
hm := NewHashMap[int]()
for _, operation := range operations {
operation(hm)
}
power := 0
for i := range hm {
boxEntries, err := hm.EntriesInBox(i)
if err != nil {
// Can't happen since we're iterating over the known array
panic(err)
}
boxNumber := i + 1
for j, item := range boxEntries {
power += boxNumber * (j + 1) * item.value
}
}
return power
}
func hash(s string) int { func hash(s string) int {
res := 0 res := 0
for _, char := range s { for _, char := range s {
@ -51,3 +156,37 @@ func hash(s string) int {
return res return res
} }
// findInList finds the given element in the linked list. The element *MUST* be of the type V, or this will panic
func findInList[V any](l *list.List, isEqual func(V) bool) *list.Element {
for cursor := l.Front(); cursor != nil; cursor = cursor.Next() {
if isEqual(cursor.Value.(V)) {
return cursor
}
}
return nil
}
func parseOperation(s string) (Operation[int], error) {
setPattern := regexp.MustCompile(`^([a-z]+)=(\d+)`)
deletePattern := regexp.MustCompile(`^([a-z]+)-$`)
setMatches := setPattern.FindStringSubmatch(s)
deleteMatches := deletePattern.FindStringSubmatch(s)
if setMatches != nil {
key := setMatches[1]
value, err := strconv.Atoi(setMatches[2])
if err != nil {
// cannot happen, since the pattern says this is a number
panic(err)
}
return makeSetOperation(key, value), nil
} else if deleteMatches != nil {
key := deleteMatches[1]
return makeDeleteOperation[int](key), nil
} else {
return nil, errors.New("invalid operation string")
}
}