From bf97f15d04017d347ec745ceb619f8e952c6a0d0 Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Sat, 2 Dec 2023 20:13:42 +0000 Subject: [PATCH] day-2 --- day-2/go.mod | 3 +++ day-2/one.go | 65 +++++++++++++++++++++++++++++++++++++++++++++++ day-2/one_test.go | 41 ++++++++++++++++++++++++++++++ day-2/run.sh | 5 ++++ day-2/solution.go | 22 ++++++++++++++++ day-2/two.go | 51 +++++++++++++++++++++++++++++++++++++ day-2/two_test.go | 42 ++++++++++++++++++++++++++++++ day-2/util.go | 37 +++++++++++++++++++++++++++ 8 files changed, 266 insertions(+) create mode 100644 day-2/go.mod create mode 100644 day-2/one.go create mode 100644 day-2/one_test.go create mode 100755 day-2/run.sh create mode 100644 day-2/solution.go create mode 100644 day-2/two.go create mode 100644 day-2/two_test.go create mode 100644 day-2/util.go diff --git a/day-2/go.mod b/day-2/go.mod new file mode 100644 index 0000000..bcb3f01 --- /dev/null +++ b/day-2/go.mod @@ -0,0 +1,3 @@ +module github.com/onyx-and-iris/aoc2023/day-2 + +go 1.20 diff --git a/day-2/one.go b/day-2/one.go new file mode 100644 index 0000000..d2db6c4 --- /dev/null +++ b/day-2/one.go @@ -0,0 +1,65 @@ +package main + +import ( + "regexp" + "strconv" + "strings" +) + +var regex = map[string]*regexp.Regexp{ + "red": regexp.MustCompile(`(?P\d+) red`), + "green": regexp.MustCompile(`(?P\d+) green`), + "blue": regexp.MustCompile(`(?P\d+) blue`), + "game": regexp.MustCompile(`Game (?P\d+)`), +} + +var limits = map[string]int{ + "red": 12, + "green": 13, + "blue": 14, +} + +// isValidGame calculates if a game was possible based on the limits of each cube colour +func isValidGame(data string) (bool, error) { + valid := true + for _, colour := range []string{"red", "green", "blue"} { + m := regex[colour].FindAllStringSubmatch(data, -1) + for _, s := range m { + n, err := strconv.Atoi(s[1]) + if err != nil { + return false, err + } + if n > limits[colour] { + valid = false + } + } + } + + return valid, nil +} + +// one returns the sum of ids for all games that were possible +func one(lines []string) (int, error) { + sum := 0 + + for _, line := range lines { + identifier, data := func() (string, string) { + x := strings.Split(line, ":") + return x[0], x[1] + }() + + isValidGame, err := isValidGame(data) + if err != nil { + return 0, err + } + if isValidGame { + id, err := getIdForGame(identifier) + if err != nil { + return 0, err + } + sum += id + } + } + + return sum, nil +} diff --git a/day-2/one_test.go b/day-2/one_test.go new file mode 100644 index 0000000..aac1842 --- /dev/null +++ b/day-2/one_test.go @@ -0,0 +1,41 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsValidGame(t *testing.T) { + //t.Skip("skipping test") + game1 := "3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green" + game2 := "1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue" + game3 := "8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red" + game4 := "1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red" + game5 := "6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green" + isValid1, _ := isValidGame(game1) + isValid2, _ := isValidGame(game2) + isValid3, _ := isValidGame(game3) + isValid4, _ := isValidGame(game4) + isValid5, _ := isValidGame(game5) + + t.Run("Should be a valid game", func(t *testing.T) { + assert.Equal(t, true, isValid1) + }) + + t.Run("Should be a valid game", func(t *testing.T) { + assert.Equal(t, true, isValid2) + }) + + t.Run("Should not be a valid game", func(t *testing.T) { + assert.Equal(t, false, isValid3) + }) + + t.Run("Should not be a valid game", func(t *testing.T) { + assert.Equal(t, false, isValid4) + }) + + t.Run("Should be a valid game", func(t *testing.T) { + assert.Equal(t, true, isValid5) + }) +} diff --git a/day-2/run.sh b/day-2/run.sh new file mode 100755 index 0000000..3f6776a --- /dev/null +++ b/day-2/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-2/solution.go b/day-2/solution.go new file mode 100644 index 0000000..3ea39e6 --- /dev/null +++ b/day-2/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-2/two.go b/day-2/two.go new file mode 100644 index 0000000..2623be6 --- /dev/null +++ b/day-2/two.go @@ -0,0 +1,51 @@ +package main + +import ( + "strconv" + "strings" +) + +// powerForGame returns the power of the minimum set of cubes for a game +func powerForGame(data string) (int, error) { + var counter = map[string]int{ + "red": 0, + "green": 0, + "blue": 0, + } + + for _, colour := range []string{"red", "green", "blue"} { + m := regex[colour].FindAllStringSubmatch(data, -1) + for _, s := range m { + n, err := strconv.Atoi(s[1]) + if err != nil { + return 0, err + } + if counter[colour] == 0 { + counter[colour] = n + } else if counter[colour] < n { + counter[colour] = n + } + } + } + return counter["red"] * counter["green"] * counter["blue"], nil +} + +// two returns the sum of powers for all games +func two(lines []string) (int, error) { + sum := 0 + + for _, line := range lines { + _, data := func() (string, string) { + x := strings.Split(line, ":") + return x[0], x[1] + }() + + pow, err := powerForGame(data) + if err != nil { + return 0, err + } + sum += pow + } + + return sum, nil +} diff --git a/day-2/two_test.go b/day-2/two_test.go new file mode 100644 index 0000000..c3f9bce --- /dev/null +++ b/day-2/two_test.go @@ -0,0 +1,42 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPowerForGame(t *testing.T) { + //t.Skip("skipping test") + game1 := "3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green" + game2 := "1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue" + game3 := "8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red" + game4 := "1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red" + game5 := "6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green" + + power1, _ := powerForGame(game1) + power2, _ := powerForGame(game2) + power3, _ := powerForGame(game3) + power4, _ := powerForGame(game4) + power5, _ := powerForGame(game5) + + t.Run("Should have power value 48", func(t *testing.T) { + assert.Equal(t, 48, power1) + }) + + t.Run("Should have power value 12", func(t *testing.T) { + assert.Equal(t, 12, power2) + }) + + t.Run("Should have power value 1560", func(t *testing.T) { + assert.Equal(t, 1560, power3) + }) + + t.Run("Should have power value 630", func(t *testing.T) { + assert.Equal(t, 630, power4) + }) + + t.Run("Should have power value 36", func(t *testing.T) { + assert.Equal(t, 36, power5) + }) +} diff --git a/day-2/util.go b/day-2/util.go new file mode 100644 index 0000000..6ad8283 --- /dev/null +++ b/day-2/util.go @@ -0,0 +1,37 @@ +package main + +import ( + "bufio" + "log" + "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 +} + +// getIdForGame returns the id for a game +func getIdForGame(identifier string) (int, error) { + m := regex["game"].FindStringSubmatch(identifier) + i := regex["game"].SubexpIndex("game") + id, err := strconv.Atoi(m[i]) + if err != nil { + return 0, err + } + + return id, nil +}