summaryrefslogtreecommitdiff
path: root/go/2023/internal/day_04
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_04
parentad4ea5ca4bc9f7540ee1d6a4fa2d881709dc6459 (diff)
refactor!: invert repo directory structure
now is language first
Diffstat (limited to 'go/2023/internal/day_04')
-rw-r--r--go/2023/internal/day_04/part_01/part_01.go130
-rw-r--r--go/2023/internal/day_04/part_01/part_01_test.go48
-rw-r--r--go/2023/internal/day_04/part_02/part_02.go142
-rw-r--r--go/2023/internal/day_04/part_02/part_02_test.go143
4 files changed, 463 insertions, 0 deletions
diff --git a/go/2023/internal/day_04/part_01/part_01.go b/go/2023/internal/day_04/part_01/part_01.go
new file mode 100644
index 0000000..1a215b1
--- /dev/null
+++ b/go/2023/internal/day_04/part_01/part_01.go
@@ -0,0 +1,130 @@
+package part_01
+
+import (
+ "os"
+ "slices"
+ "strconv"
+ "strings"
+)
+
+const USE_SAMPLE = false
+
+type UnparsedCard struct {
+ id string
+ winningNumbers []string
+ haveNumbers []string
+}
+
+type Card struct {
+ id int
+ winningNumbers []int
+ haveNumbers []int
+}
+
+func (c UnparsedCard) parse() Card {
+ var card Card
+
+ id, err := strconv.Atoi(c.id)
+ if err != nil {
+ panic(err)
+ }
+ card.id = id
+
+ for _, number := range c.winningNumbers {
+ num, err := strconv.Atoi(number)
+ if err != nil {
+ panic(err)
+ }
+ card.winningNumbers = append(card.winningNumbers, num)
+ }
+
+ for _, number := range c.haveNumbers {
+ num, err := strconv.Atoi(number)
+ if err != nil {
+ panic(err)
+ }
+ card.haveNumbers = append(card.haveNumbers, num)
+ }
+
+ return card
+}
+
+func Solve() {
+ var contents []byte
+ if USE_SAMPLE {
+ contents, _ = os.ReadFile("../_input/day_04_part_01_sample.txt")
+ } else {
+ contents, _ = os.ReadFile("../_input/day_04.txt")
+ }
+
+ lines := strings.Split(string(contents), "\n")
+
+ answer := 0
+ for _, line := range lines {
+ if line == "" || line == "\n" {
+ continue
+ }
+
+ card := parseCardLine(line)
+ score := tallyScore(card)
+
+ answer += score
+ }
+
+ println(answer)
+}
+
+// "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53"
+func parseCardLine(line string) Card {
+ var unparsedCard UnparsedCard
+
+ filteredLine := strings.ReplaceAll(line, "\r", "")
+
+ // "Card 1"
+ card_parts := strings.Split(filteredLine, ":")[0]
+ id_parts := strings.Split(card_parts, " ")
+ id := id_parts[len(id_parts)-1]
+ unparsedCard.id = strings.ReplaceAll(id, " ", "")
+
+ // "41 48 83 86 17 | 83 86 6 31 17 9 48 53"
+ numbers := strings.Split(filteredLine, ":")[1]
+
+ // " 41 48 83 86 17"
+ winningNumberString := strings.Split(numbers, "|")[0]
+ // ["", "41", "48", "83", "86", "17"]
+ winningNumbers := strings.Split(winningNumberString, " ")
+ for _, num := range winningNumbers {
+ if num == "" || num == " " {
+ continue
+ }
+ unparsedCard.winningNumbers = append(unparsedCard.winningNumbers, num)
+ }
+
+ // " 83 86 6 31 17 9 48 53"
+ haveNumberString := strings.Split(numbers, "|")[1]
+ // ["", "83", "86", "", "6", "31", "17", "", "9", "48", "53"]
+ haveNumbers := strings.Split(haveNumberString, " ")
+ for _, num := range haveNumbers {
+ if num == "" || num == " " {
+ continue
+ }
+ unparsedCard.haveNumbers = append(unparsedCard.haveNumbers, num)
+ }
+
+ return unparsedCard.parse()
+}
+
+func tallyScore(card Card) int {
+ score := 0
+ for _, winningNumber := range card.winningNumbers {
+ if slices.Contains(card.haveNumbers, winningNumber) {
+ if score == 0 {
+ score = 1
+ } else {
+ score *= 2
+ }
+ }
+ }
+
+ return score
+}
diff --git a/go/2023/internal/day_04/part_01/part_01_test.go b/go/2023/internal/day_04/part_01/part_01_test.go
new file mode 100644
index 0000000..f6f1a83
--- /dev/null
+++ b/go/2023/internal/day_04/part_01/part_01_test.go
@@ -0,0 +1,48 @@
+package part_01
+
+import (
+ "testing"
+)
+
+func TestParseCardLine(t *testing.T) {
+ input := "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53"
+ expected := Card{1, []int{41, 48, 83, 86, 17}, []int{83, 86, 6, 31, 17, 9, 48, 53}}
+
+ actual := parseCardLine(input)
+
+ if actual.id != expected.id {
+ t.Errorf("ERROR: ID %d does not match expected %d", actual.id, expected.id)
+ }
+
+ if len(actual.winningNumbers) != len(expected.winningNumbers) {
+ t.Errorf("ERROR: len(WinningNumbers) %d does not match expected %d", len(actual.winningNumbers), len(expected.winningNumbers))
+ }
+
+ if len(actual.haveNumbers) != len(expected.haveNumbers) {
+ t.Errorf("ERROR: len(HaveNumbers) %d does not match expected %d", len(actual.haveNumbers), len(expected.haveNumbers))
+ }
+
+ for i, num := range actual.winningNumbers {
+ if num != expected.winningNumbers[i] {
+ t.Errorf("ERROR: WinningNumber %d does not match expected %d", num, expected.winningNumbers[i])
+ }
+ }
+
+ for i, num := range actual.haveNumbers {
+ if num != expected.haveNumbers[i] {
+ t.Errorf("ERROR: HaveNumber %d does not match expected %d", num, expected.haveNumbers[i])
+ }
+ }
+
+}
+
+func TestTallyScore(t *testing.T) {
+ card := Card{1, []int{41, 48, 83, 86, 17}, []int{83, 86, 6, 31, 17, 9, 48, 53}}
+ expected := 8
+
+ actual := tallyScore(card)
+
+ if actual != expected {
+ t.Errorf("ERROR: Score %d does not match expected %d", actual, expected)
+ }
+}
diff --git a/go/2023/internal/day_04/part_02/part_02.go b/go/2023/internal/day_04/part_02/part_02.go
new file mode 100644
index 0000000..a9e3152
--- /dev/null
+++ b/go/2023/internal/day_04/part_02/part_02.go
@@ -0,0 +1,142 @@
+package part_02
+
+import (
+ "os"
+ "slices"
+ "strconv"
+ "strings"
+)
+
+const USE_SAMPLE = false
+
+type UnparsedCard struct {
+ id string
+ winningNumbers []string
+ haveNumbers []string
+}
+
+type Card struct {
+ id int
+ winningNumbers []int
+ haveNumbers []int
+}
+
+func (c UnparsedCard) parse() Card {
+ var card Card
+
+ id, err := strconv.Atoi(c.id)
+ if err != nil {
+ panic(err)
+ }
+ card.id = id
+
+ for _, number := range c.winningNumbers {
+ num, err := strconv.Atoi(number)
+ if err != nil {
+ panic(err)
+ }
+ card.winningNumbers = append(card.winningNumbers, num)
+ }
+
+ for _, number := range c.haveNumbers {
+ num, err := strconv.Atoi(number)
+ if err != nil {
+ panic(err)
+ }
+ card.haveNumbers = append(card.haveNumbers, num)
+ }
+
+ return card
+}
+
+func Solve() {
+ var contents []byte
+ if USE_SAMPLE {
+ contents, _ = os.ReadFile("../_input/day_04_part_02_sample.txt")
+ } else {
+ contents, _ = os.ReadFile("../_input/day_04.txt")
+ }
+
+ lines := strings.Split(string(contents), "\n")
+
+ dict := map[int]Card{}
+ var cards []Card
+ for _, line := range lines {
+ if line == "" || line == "\n" {
+ continue
+ }
+
+ card := parseCardLine(line)
+
+ dict[card.id] = card
+ cards = append(cards, card)
+ }
+
+ answer := processCards(dict, cards)
+
+ println(answer)
+}
+
+// "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53"
+func parseCardLine(line string) Card {
+ var unparsedCard UnparsedCard
+
+ filteredLine := strings.ReplaceAll(line, "\r", "")
+
+ // "Card 1"
+ card_parts := strings.Split(filteredLine, ":")[0]
+ id_parts := strings.Split(card_parts, " ")
+ id := id_parts[len(id_parts)-1]
+ unparsedCard.id = strings.ReplaceAll(id, " ", "")
+
+ // "41 48 83 86 17 | 83 86 6 31 17 9 48 53"
+ numbers := strings.Split(filteredLine, ":")[1]
+
+ // " 41 48 83 86 17"
+ winningNumberString := strings.Split(numbers, "|")[0]
+ // ["", "41", "48", "83", "86", "17"]
+ winningNumbers := strings.Split(winningNumberString, " ")
+ for _, num := range winningNumbers {
+ if num == "" || num == " " {
+ continue
+ }
+ unparsedCard.winningNumbers = append(unparsedCard.winningNumbers, num)
+ }
+
+ // " 83 86 6 31 17 9 48 53"
+ haveNumberString := strings.Split(numbers, "|")[1]
+ // ["", "83", "86", "", "6", "31", "17", "", "9", "48", "53"]
+ haveNumbers := strings.Split(haveNumberString, " ")
+ for _, num := range haveNumbers {
+ if num == "" || num == " " {
+ continue
+ }
+ unparsedCard.haveNumbers = append(unparsedCard.haveNumbers, num)
+ }
+
+ return unparsedCard.parse()
+}
+
+func processCards(dict map[int]Card, cards []Card) int {
+ for i := 0; i < len(cards); i++ {
+ card := cards[i]
+ matchCount := calculateNumberOfMatches(card)
+ for j := card.id + 1; j < (card.id + 1 + matchCount); j++ {
+ copyCard := dict[j]
+ cards = append(cards, copyCard)
+ }
+ }
+
+ return len(cards)
+}
+
+func calculateNumberOfMatches(card Card) int {
+ matchCount := 0
+ for _, winningNumber := range card.winningNumbers {
+ if slices.Contains(card.haveNumbers, winningNumber) {
+ matchCount++
+ }
+ }
+
+ return matchCount
+}
diff --git a/go/2023/internal/day_04/part_02/part_02_test.go b/go/2023/internal/day_04/part_02/part_02_test.go
new file mode 100644
index 0000000..3c66a2e
--- /dev/null
+++ b/go/2023/internal/day_04/part_02/part_02_test.go
@@ -0,0 +1,143 @@
+package part_02
+
+import (
+ "testing"
+)
+
+func TestParseCardLine(t *testing.T) {
+ input := "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53"
+ expected := Card{1, []int{41, 48, 83, 86, 17}, []int{83, 86, 6, 31, 17, 9, 48, 53}}
+
+ actual := parseCardLine(input)
+
+ if actual.id != expected.id {
+ t.Errorf("ERROR: ID %d does not match expected %d", actual.id, expected.id)
+ }
+
+ if len(actual.winningNumbers) != len(expected.winningNumbers) {
+ t.Errorf("ERROR: len(WinningNumbers) %d does not match expected %d", len(actual.winningNumbers), len(expected.winningNumbers))
+ }
+
+ if len(actual.haveNumbers) != len(expected.haveNumbers) {
+ t.Errorf("ERROR: len(HaveNumbers) %d does not match expected %d", len(actual.haveNumbers), len(expected.haveNumbers))
+ }
+
+ for i, num := range actual.winningNumbers {
+ if num != expected.winningNumbers[i] {
+ t.Errorf("ERROR: WinningNumber %d does not match expected %d", num, expected.winningNumbers[i])
+ }
+ }
+
+ for i, num := range actual.haveNumbers {
+ if num != expected.haveNumbers[i] {
+ t.Errorf("ERROR: HaveNumber %d does not match expected %d", num, expected.haveNumbers[i])
+ }
+ }
+
+}
+
+func TestProcessCards(t *testing.T) {
+ cards := []Card{
+ // Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
+ {
+ 1,
+ []int{41, 48, 83, 86, 17},
+ []int{83, 86, 6, 31, 17, 9, 48, 53},
+ },
+ // Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
+ {
+ 2,
+ []int{13, 32, 20, 16, 61},
+ []int{61, 30, 68, 82, 17, 32, 24, 19},
+ },
+ // Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
+ {
+ 3,
+ []int{1, 21, 53, 59, 44},
+ []int{69, 82, 63, 72, 16, 21, 14, 1},
+ },
+ // Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
+ {
+ 4,
+ []int{41, 92, 73, 84, 69},
+ []int{59, 84, 76, 51, 58, 5, 54, 83},
+ },
+ // Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
+ {
+ 5,
+ []int{87, 83, 26, 28, 32},
+ []int{88, 30, 70, 12, 93, 22, 82, 36},
+ },
+ // Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
+ {
+ 6,
+ []int{31, 18, 13, 56, 72},
+ []int{74, 77, 10, 23, 35, 67, 36, 11},
+ },
+ }
+ dict := map[int]Card{}
+ for _, card := range cards {
+ dict[card.id] = card
+ }
+ expected := 30
+
+ actual := processCards(dict, cards)
+
+ if actual != expected {
+ t.Errorf("ERROR: Total number of cards %d does not match expected %d", actual, expected)
+ }
+}
+
+func TestCalculateNumberOfMatches(t *testing.T) {
+ cards := []Card{
+ // Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
+ {
+ 1,
+ []int{41, 48, 83, 86, 17},
+ []int{83, 86, 6, 31, 17, 9, 48, 53},
+ },
+ // Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
+ {
+ 2,
+ []int{13, 32, 20, 16, 61},
+ []int{61, 30, 68, 82, 17, 32, 24, 19},
+ },
+ // Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
+ {
+ 3,
+ []int{1, 21, 53, 59, 44},
+ []int{69, 82, 63, 72, 16, 21, 14, 1},
+ },
+ // Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
+ {
+ 4,
+ []int{41, 92, 73, 84, 69},
+ []int{59, 84, 76, 51, 58, 5, 54, 83},
+ },
+ // Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
+ {
+ 5,
+ []int{87, 83, 26, 28, 32},
+ []int{88, 30, 70, 12, 93, 22, 82, 36},
+ },
+ // Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
+ {
+ 6,
+ []int{31, 18, 13, 56, 72},
+ []int{74, 77, 10, 23, 35, 67, 36, 11},
+ },
+ }
+ expected := 9
+
+ total := 0
+ for _, card := range cards {
+ matches := calculateNumberOfMatches(card)
+ total += matches
+
+ cards = append(cards, card)
+ }
+
+ if total != expected {
+ t.Errorf("ERROR: Score %d does not match expected %d", total, expected)
+ }
+}