diff --git a/day-4/go.mod b/day-4/go.mod new file mode 100644 index 0000000..78166bc --- /dev/null +++ b/day-4/go.mod @@ -0,0 +1,5 @@ +module github.com/onyx-and-iris/aoc2023/day-4 + +go 1.20 + +require github.com/go-playground/assert v1.2.1 diff --git a/day-4/go.sum b/day-4/go.sum new file mode 100644 index 0000000..80dcf13 --- /dev/null +++ b/day-4/go.sum @@ -0,0 +1,2 @@ +github.com/go-playground/assert v1.2.1 h1:ad06XqC+TOv0nJWnbULSlh3ehp5uLuQEojZY5Tq8RgI= +github.com/go-playground/assert v1.2.1/go.mod h1:Lgy+k19nOB/wQG/fVSQ7rra5qYugmytMQqvQ2dgjWn8= diff --git a/day-4/one.go b/day-4/one.go new file mode 100644 index 0000000..d2299ec --- /dev/null +++ b/day-4/one.go @@ -0,0 +1,61 @@ +package main + +import ( + "regexp" +) + +type M map[string]interface{} + +var allCards []M + +var _regex_winning_numbers = regexp.MustCompile( + `^Card (?P[\s\d]+):\s` + + `(?P[0-9\s]+)\s` + + `\|\s` + + `(?P[0-9\s]+)$`) + +var _regex_nums = regexp.MustCompile("[0-9]+") + +// compareLists returns the number of matching elements between winning and mynums +func compareLists(winningStr string, mynumsStr string) (int, error) { + x, err := numStrToArr(winningStr, []int{}) + if err != nil { + return 0, err + } + winning := x + x, err = numStrToArr(mynumsStr, []int{}) + if err != nil { + return 0, err + } + mynums := x + + n := 0 + for _, num := range mynums { + if contains(winning, num) { + n += 1 + } + } + return n, nil +} + +// one computes points based on matching numbers +func one(lines []string) (int, error) { + for _, line := range lines { + parseAllCards(line) + } + cards = make([]Card, len(lines)) + + sum := 0 + for x, card := range allCards { + m, err := compareLists(card["winning"].(string), card["mynums"].(string)) + if err != nil { + return 0, err + } + cards[x].matches = m + if cards[x].matches > 0 { + sum += (pow(2, cards[x].matches-1)) + } + } + + return sum, nil +} diff --git a/day-4/one_test.go b/day-4/one_test.go new file mode 100644 index 0000000..0eab921 --- /dev/null +++ b/day-4/one_test.go @@ -0,0 +1,25 @@ +package main + +import ( + "testing" + + "github.com/go-playground/assert/v2" +) + +func TestCompareLists(t *testing.T) { + //t.Skip("skipping test") + t.Run("Should find 4 matches", func(t *testing.T) { + res, _ := compareLists("41 48 83 86 17", "83 86 6 31 17 9 48 53") + assert.Equal(t, 4, res) + }) + + t.Run("Should find 2 matches", func(t *testing.T) { + res, _ := compareLists(" 1 21 53 59 44 ", "69 82 63 72 16 21 14 1") + assert.Equal(t, 2, res) + }) + + t.Run("Should find 0 matches", func(t *testing.T) { + res, _ := compareLists("87 83 26 28 32", "88 30 70 12 93 22 82 36") + assert.Equal(t, 0, res) + }) +} diff --git a/day-4/run.sh b/day-4/run.sh new file mode 100755 index 0000000..3f6776a --- /dev/null +++ b/day-4/run.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +INPUT="input.txt" + +cat $INPUT | go run . \ No newline at end of file diff --git a/day-4/solution.go b/day-4/solution.go new file mode 100644 index 0000000..3ea39e6 --- /dev/null +++ b/day-4/solution.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "log" +) + +func main() { + lines := readlines() + + ans, err := one(lines) + if err != nil { + log.Fatal(err) + } + fmt.Printf("solution one: %d\n", ans) + + ans, err = two(lines) + if err != nil { + log.Fatal(err) + } + fmt.Printf("solution two: %d\n", ans) +} diff --git a/day-4/two.go b/day-4/two.go new file mode 100644 index 0000000..8732ca5 --- /dev/null +++ b/day-4/two.go @@ -0,0 +1,27 @@ +package main + +// Cards represents a single card. +// it tracks its matching numbers and occurrences +type Card struct { + matches int + occurrences int +} + +var cards = []Card{} + +// two returns the total number of occurrences for all cards +func two(lines []string) (int, error) { + var sum = 0 + + for i := range cards { + cards[i].occurrences++ + + for j := i + 1; j <= i+cards[i].matches; j++ { + cards[j].occurrences += cards[i].occurrences + } + + sum += cards[i].occurrences + } + + return sum, nil +} diff --git a/day-4/util.go b/day-4/util.go new file mode 100644 index 0000000..29fbc79 --- /dev/null +++ b/day-4/util.go @@ -0,0 +1,63 @@ +package main + +import ( + "bufio" + "log" + "math" + "os" + "strconv" +) + +// readlines reads lines from stdin. +// Then it returns them as an array of strings +func readlines() []string { + lines := []string{} + + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + + return lines +} + +func parseAllCards(line string) { + groups := M{} + res := _regex_winning_numbers.FindStringSubmatch(line) + names := _regex_winning_numbers.SubexpNames() + for i := range res { + groups[names[i]] = res[i] + } + allCards = append(allCards, groups) +} + +// numStrToArr converts a string of numbers or an array of numbers +func numStrToArr(nums string, arr []int) ([]int, error) { + for _, num := range _regex_nums.FindAllString(nums, -1) { + n, err := strconv.Atoi(num) + if err != nil { + return []int{}, err + } + arr = append(arr, n) + } + return arr, nil +} + +// contains returns true if an slice of elements contains a given element +func contains[T comparable](elems []T, v T) bool { + for _, s := range elems { + if v == s { + return true + } + } + return false +} + +// pow returns the value of x to the n +func pow(x, n int) int { + return int(math.Pow(float64(x), float64(n))) +}