Add day 4 part 2

master
Nick Krichevsky 2023-12-04 08:54:23 -05:00
parent ab92cf620a
commit 8900459fc6
1 changed files with 95 additions and 16 deletions

View File

@ -6,31 +6,48 @@ import (
"io"
"os"
"regexp"
"slices"
"strconv"
"strings"
)
type Card struct {
id int
winningNumbers []int
ourNumbers []int
}
func (card Card) Score() int {
score := 0
func (card Card) NumMatchingNumbers() int {
matchingNumbers := 0
winningNumbers := makeSet(card.winningNumbers)
for _, ourNumber := range card.ourNumbers {
if _, ok := winningNumbers[ourNumber]; !ok {
continue
}
if score == 0 {
score = 1
} else {
score *= 2
if _, ok := winningNumbers[ourNumber]; ok {
matchingNumbers++
}
}
return score
return matchingNumbers
}
func (card Card) Score() int {
numMatchingNumbers := card.NumMatchingNumbers()
if numMatchingNumbers == 0 {
return 0
}
return pow2(numMatchingNumbers)
}
func (card Card) WinsCardsWithIDs() []int {
numMatchingNumbers := card.NumMatchingNumbers()
wonCards := make([]int, numMatchingNumbers)
for i := 0; i < numMatchingNumbers; i++ {
wonCards[i] = card.id + i + 1
}
return wonCards
}
func main() {
@ -60,6 +77,7 @@ func main() {
}
fmt.Printf("Part 1: %d\n", part1(cards))
fmt.Printf("Part 2: %d\n", part2(cards))
}
func part1(cards []Card) int {
@ -71,28 +89,76 @@ func part1(cards []Card) int {
return score
}
func part2(cards []Card) int {
if len(cards) == 0 {
return 0
}
cardsByID := map[int]Card{}
for _, card := range cards {
cardsByID[card.id] = card
}
// This solution is a bit naive; I didn't get particularly clever with
// the number of cards we had and treated it like a tree problem (I could
// have just made this faster by doing
// visitedCardIds[wonCard] += visitedCardIds[card.id]
// which would have been equivalent, and faster, but meh, I like this
// solution even if it's slow)
visitedCardIDs := map[int]int{}
cardsInPlay := slices.Clone(cards)
for len(cardsInPlay) > 0 {
card := cardsInPlay[0]
cardsInPlay = cardsInPlay[1:]
visitedCardIDs[card.id]++
wonCardIDs := card.WinsCardsWithIDs()
for _, wonCardID := range wonCardIDs {
cardsInPlay = append(cardsInPlay, cardsByID[wonCardID])
}
}
totalCards := 0
for _, numVisited := range visitedCardIDs {
totalCards += numVisited
}
return totalCards
}
func parseCards(inputLines []string) ([]Card, error) {
return tryParse(inputLines, parseCard)
}
func parseCard(inputLine string) (Card, error) {
pattern := regexp.MustCompile(`^Card\s+\d+: ((?:\s*\d+\s*?)+) \| ((?:\s*\d+\s*)+)$`)
pattern := regexp.MustCompile(`^Card\s+(\d+): ((?:\s*\d+\s*?)+) \| ((?:\s*\d+\s*)+)$`)
matches := pattern.FindStringSubmatch(inputLine)
if matches == nil {
return Card{}, errors.New("did not match line pattern")
}
winningNumbers, err := parseCardNumbers(matches[1])
id, err := strconv.Atoi(matches[1])
if err != nil {
return Card{}, fmt.Errorf("parse id: %w", err)
}
winningNumbers, err := parseCardNumbers(matches[2])
if err != nil {
return Card{}, fmt.Errorf("parse winning numbers: %w", err)
}
ourNumbers, err := parseCardNumbers(matches[2])
ourNumbers, err := parseCardNumbers(matches[3])
if err != nil {
return Card{}, fmt.Errorf("parse winning numbers: %w", err)
return Card{}, fmt.Errorf("parse our numbers: %w", err)
}
return Card{winningNumbers: winningNumbers, ourNumbers: ourNumbers}, nil
return Card{
id: id,
winningNumbers: winningNumbers,
ourNumbers: ourNumbers,
}, nil
}
func normalizeSeparatingSpaces(s string) string {
@ -131,3 +197,16 @@ func makeSet[T comparable, S ~[]T](items S) map[T]struct{} {
return set
}
func pow2(exp int) int {
if exp == 0 {
return 1
}
res := 1
for i := 1; i < exp; i++ {
res *= 2
}
return res
}