summaryrefslogtreecommitdiff
path: root/go/2023/internal/day_02
diff options
context:
space:
mode:
authorDJ O'Leary <dijitol@proton.me>2026-06-26 18:04:09 +0200
committerDJ O'Leary <dijitol@proton.me>2026-06-26 18:04:09 +0200
commit6d8fb621d1e93b83786890d6d9cb6f0d8defa8ae (patch)
tree17c90d3984c114aa023600cc5bec6f6004b02243 /go/2023/internal/day_02
parentad4ea5ca4bc9f7540ee1d6a4fa2d881709dc6459 (diff)
refactor!: invert repo directory structure
now is language first
Diffstat (limited to 'go/2023/internal/day_02')
-rw-r--r--go/2023/internal/day_02/part_01/part_01.go138
-rw-r--r--go/2023/internal/day_02/part_01/part_01_test.go102
-rw-r--r--go/2023/internal/day_02/part_02/part_02.go114
-rw-r--r--go/2023/internal/day_02/part_02/part_02_test.go84
4 files changed, 438 insertions, 0 deletions
diff --git a/go/2023/internal/day_02/part_01/part_01.go b/go/2023/internal/day_02/part_01/part_01.go
new file mode 100644
index 0000000..9efe20f
--- /dev/null
+++ b/go/2023/internal/day_02/part_01/part_01.go
@@ -0,0 +1,138 @@
+package part_01
+
+import (
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+type cubes struct {
+ red int
+ blue int
+ green int
+}
+
+func Solve() {
+ contents, err := os.ReadFile("../_input/day_02.txt")
+ if err != nil {
+ panic(err)
+ }
+
+ lines := strings.Split(string(contents), "\n")
+ possibleGameIds := processLines(lines)
+
+ println(accumulate(possibleGameIds))
+}
+
+func processLines(lines []string) []int {
+ var possibleGames []int
+
+ maxCubes := cubes{red: 12, blue: 14, green: 13}
+
+ for _, line := range lines {
+ if line == "" || line == "\n" {
+ continue
+ }
+
+ shownCubes := convertLineToCubes(line)
+
+ isPossible := isPossibleGame(shownCubes, maxCubes)
+ if isPossible {
+ gameId := getGameId(line)
+ possibleGames = append(possibleGames, gameId)
+ }
+ }
+
+ return possibleGames
+}
+
+func convertLineToCubes(line string) []cubes {
+ var shownCubes []cubes
+
+ redRegex := regexp.MustCompile(`(\d+) red`)
+ blueRegex := regexp.MustCompile(`(\d+) blue`)
+ greenRegex := regexp.MustCompile(`(\d+) green`)
+
+ for _, round := range strings.Split(line, ";") {
+ var cubes cubes
+
+ redMatches := redRegex.FindStringSubmatch(round)
+ if redMatches != nil {
+ red, err := strconv.Atoi(redMatches[1])
+ if err != nil {
+ panic("Could not convert red to int: " + redMatches[1])
+ } else {
+ cubes.red = red
+ }
+ }
+
+ blueMatches := blueRegex.FindStringSubmatch(round)
+ if blueMatches != nil {
+ blue, err := strconv.Atoi(blueMatches[1])
+ if err != nil {
+ panic("Could not convert green to int: " + blueMatches[1])
+ } else {
+ cubes.blue = blue
+ }
+ }
+
+ greenMatches := greenRegex.FindStringSubmatch(round)
+ if greenMatches != nil {
+ green, err := strconv.Atoi(greenMatches[1])
+ if err != nil {
+ panic("Could not convert green to int: " + greenMatches[1])
+ } else {
+ cubes.green = green
+ }
+ }
+
+ shownCubes = append(shownCubes, cubes)
+ }
+
+ return shownCubes
+}
+
+func isPossibleGame(shownCubes []cubes, maxCubes cubes) bool {
+ for _, cubes := range shownCubes {
+ if cubes.red > maxCubes.red {
+ return false
+ }
+
+ if cubes.blue > maxCubes.blue {
+ return false
+ }
+
+ if cubes.green > maxCubes.green {
+ return false
+ }
+ }
+
+ return true
+}
+
+func getGameId(line string) int {
+ regex := regexp.MustCompile(`^Game (\d+):`)
+ matches := regex.FindStringSubmatch(line)
+ if matches == nil || len(matches) < 2 {
+ println(matches)
+ panic("No game id found in line: '" + line + "'")
+ }
+
+ gameId, err := strconv.Atoi(matches[1])
+ if err != nil {
+ panic("Could not convert game id to int: " + matches[1])
+ }
+
+ return gameId
+}
+
+func accumulate(gameIds []int) int {
+ var acc int
+
+ for _, gameId := range gameIds {
+ acc += gameId
+ }
+
+ return acc
+}
diff --git a/go/2023/internal/day_02/part_01/part_01_test.go b/go/2023/internal/day_02/part_01/part_01_test.go
new file mode 100644
index 0000000..33e5fbf
--- /dev/null
+++ b/go/2023/internal/day_02/part_01/part_01_test.go
@@ -0,0 +1,102 @@
+package part_01
+
+import (
+ "testing"
+)
+
+type data struct {
+ gameId int
+ isPossible bool
+ shownCubes []cubes
+ line string
+}
+
+var testInput = []data{
+ {
+ gameId: 1,
+ isPossible: true,
+ shownCubes: []cubes{
+ {red: 4, blue: 3},
+ {red: 1, blue: 6, green: 2},
+ {green: 2},
+ },
+ line: "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green",
+ },
+ {
+ gameId: 2,
+ isPossible: true,
+ shownCubes: []cubes{
+ {blue: 1, green: 2},
+ {red: 1, blue: 4, green: 3},
+ {blue: 1, green: 1},
+ },
+ line: "Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue",
+ },
+ {
+ gameId: 3,
+ isPossible: false,
+ shownCubes: []cubes{
+ {red: 20, blue: 6, green: 8},
+ {red: 4, blue: 5, green: 13},
+ {red: 1, green: 5},
+ },
+ line: "Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red",
+ },
+ {
+ gameId: 4,
+ isPossible: false,
+ shownCubes: []cubes{
+ {red: 3, blue: 6, green: 1},
+ {red: 6, green: 3},
+ {red: 14, blue: 15, green: 3},
+ },
+ line: "Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red",
+ },
+ {
+ gameId: 5,
+ isPossible: true,
+ shownCubes: []cubes{
+ {red: 6, blue: 1, green: 3},
+ {red: 1, blue: 2, green: 2},
+ },
+ line: "Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green",
+ },
+}
+
+func TestConvertLineToCubes(t *testing.T) {
+ for _, data := range testInput {
+ actualCubes := convertLineToCubes(data.line)
+
+ if len(data.shownCubes) != len(actualCubes) {
+ t.Errorf("Expected length of shownCubes to be %d, got %d", len(data.shownCubes), len(actualCubes))
+ }
+
+ for i, cubes := range data.shownCubes {
+ if cubes != actualCubes[i] {
+ t.Errorf("Expected shownCubes to be %v, got %v", cubes, actualCubes[i])
+ }
+ }
+ }
+}
+
+func TestIsPossibleGame(t *testing.T) {
+ maxCubes := cubes{red: 12, blue: 13, green: 14}
+
+ for _, data := range testInput {
+ actualIsPossible := isPossibleGame(data.shownCubes, maxCubes)
+
+ if data.isPossible != actualIsPossible {
+ t.Errorf("Expected isPossibleGame to be %t, got %t for game id %d", data.isPossible, actualIsPossible, data.gameId)
+ }
+ }
+}
+
+func TestGetGameId(t *testing.T) {
+ for _, data := range testInput {
+ actualGameId := getGameId(data.line)
+
+ if data.gameId != actualGameId {
+ t.Errorf("Expected gameId to be %d, got %d", data.gameId, actualGameId)
+ }
+ }
+}
diff --git a/go/2023/internal/day_02/part_02/part_02.go b/go/2023/internal/day_02/part_02/part_02.go
new file mode 100644
index 0000000..ffb9691
--- /dev/null
+++ b/go/2023/internal/day_02/part_02/part_02.go
@@ -0,0 +1,114 @@
+package part_02
+
+import (
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+type cubes struct {
+ red int
+ blue int
+ green int
+}
+
+func Solve() {
+ contents, err := os.ReadFile("../_input/day_02.txt")
+ if err != nil {
+ panic(err)
+ }
+
+ lines := strings.Split(string(contents), "\n")
+ answer := processLines(lines)
+
+ println(answer)
+}
+
+func processLines(lines []string) int {
+ var answer int
+
+ for _, line := range lines {
+ if line == "" || line == "\n" {
+ continue
+ }
+
+ shownCubes := convertLineToCubes(line)
+
+ maxCubes := getMaximumCubes(shownCubes)
+ power := getPower(maxCubes)
+ answer += power
+ }
+
+ return answer
+}
+
+func convertLineToCubes(line string) []cubes {
+ var shownCubes []cubes
+
+ redRegex := regexp.MustCompile(`(\d+) red`)
+ blueRegex := regexp.MustCompile(`(\d+) blue`)
+ greenRegex := regexp.MustCompile(`(\d+) green`)
+
+ for _, round := range strings.Split(line, ";") {
+ var cubes cubes
+
+ redMatches := redRegex.FindStringSubmatch(round)
+ if redMatches != nil {
+ red, err := strconv.Atoi(redMatches[1])
+ if err != nil {
+ panic("Could not convert red to int: " + redMatches[1])
+ } else {
+ cubes.red = red
+ }
+ }
+
+ blueMatches := blueRegex.FindStringSubmatch(round)
+ if blueMatches != nil {
+ blue, err := strconv.Atoi(blueMatches[1])
+ if err != nil {
+ panic("Could not convert green to int: " + blueMatches[1])
+ } else {
+ cubes.blue = blue
+ }
+ }
+
+ greenMatches := greenRegex.FindStringSubmatch(round)
+ if greenMatches != nil {
+ green, err := strconv.Atoi(greenMatches[1])
+ if err != nil {
+ panic("Could not convert green to int: " + greenMatches[1])
+ } else {
+ cubes.green = green
+ }
+ }
+
+ shownCubes = append(shownCubes, cubes)
+ }
+
+ return shownCubes
+}
+
+func getMaximumCubes(shownCubes []cubes) cubes {
+ var maxCubes cubes
+
+ for _, cubes := range shownCubes {
+ if cubes.red > maxCubes.red {
+ maxCubes.red = cubes.red
+ }
+
+ if cubes.blue > maxCubes.blue {
+ maxCubes.blue = cubes.blue
+ }
+
+ if cubes.green > maxCubes.green {
+ maxCubes.green = cubes.green
+ }
+ }
+
+ return maxCubes
+}
+
+func getPower(maxCubes cubes) int {
+ return maxCubes.red * maxCubes.blue * maxCubes.green
+}
diff --git a/go/2023/internal/day_02/part_02/part_02_test.go b/go/2023/internal/day_02/part_02/part_02_test.go
new file mode 100644
index 0000000..6952899
--- /dev/null
+++ b/go/2023/internal/day_02/part_02/part_02_test.go
@@ -0,0 +1,84 @@
+package part_02
+
+import (
+ "testing"
+)
+
+type data struct {
+ shownCubes []cubes
+ maxCubes cubes
+ line string
+}
+
+var testInput = []data{
+ {
+ shownCubes: []cubes{
+ {red: 4, blue: 3},
+ {red: 1, blue: 6, green: 2},
+ {green: 2},
+ },
+ maxCubes: cubes{red: 4, blue: 6, green: 2},
+ line: "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green",
+ },
+ {
+ shownCubes: []cubes{
+ {blue: 1, green: 2},
+ {red: 1, blue: 4, green: 3},
+ {blue: 1, green: 1},
+ },
+ maxCubes: cubes{red: 1, blue: 4, green: 3},
+ line: "Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue",
+ },
+ {
+ shownCubes: []cubes{
+ {red: 20, blue: 6, green: 8},
+ {red: 4, blue: 5, green: 13},
+ {red: 1, green: 5},
+ },
+ maxCubes: cubes{red: 20, blue: 6, green: 13},
+ line: "Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red",
+ },
+ {
+ shownCubes: []cubes{
+ {red: 3, blue: 6, green: 1},
+ {red: 6, green: 3},
+ {red: 14, blue: 15, green: 3},
+ },
+ maxCubes: cubes{red: 14, blue: 15, green: 3},
+ line: "Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red",
+ },
+ {
+ shownCubes: []cubes{
+ {red: 6, blue: 1, green: 3},
+ {red: 1, blue: 2, green: 2},
+ },
+ maxCubes: cubes{red: 6, blue: 2, green: 3},
+ line: "Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green",
+ },
+}
+
+func TestConvertLineToCubes(t *testing.T) {
+ for _, data := range testInput {
+ actualCubes := convertLineToCubes(data.line)
+
+ if len(data.shownCubes) != len(actualCubes) {
+ t.Errorf("Expected length of shownCubes to be %d, got %d", len(data.shownCubes), len(actualCubes))
+ }
+
+ for i, cubes := range data.shownCubes {
+ if cubes != actualCubes[i] {
+ t.Errorf("Expected shownCubes to be %v, got %v", cubes, actualCubes[i])
+ }
+ }
+ }
+}
+
+func TestGetMaximumCubes(t *testing.T) {
+ for _, data := range testInput {
+ actualMaxCubes := getMaximumCubes(data.shownCubes)
+
+ if actualMaxCubes != data.maxCubes {
+ t.Errorf("Expected maxCubes to be %v, got %v", data.maxCubes, actualMaxCubes)
+ }
+ }
+}