From 4c3a50166bf0f78ce89459c352cc1c6536a8b96d Mon Sep 17 00:00:00 2001 From: DJ O'Leary Date: Sat, 29 Nov 2025 20:23:02 +0100 Subject: chore: make language folders snake_case --- 2023/go/internal/day_04/part_01/part_01.go | 130 +++++++++++++++++++++ 2023/go/internal/day_04/part_01/part_01_test.go | 48 ++++++++ 2023/go/internal/day_04/part_02/part_02.go | 142 +++++++++++++++++++++++ 2023/go/internal/day_04/part_02/part_02_test.go | 143 ++++++++++++++++++++++++ 4 files changed, 463 insertions(+) create mode 100644 2023/go/internal/day_04/part_01/part_01.go create mode 100644 2023/go/internal/day_04/part_01/part_01_test.go create mode 100644 2023/go/internal/day_04/part_02/part_02.go create mode 100644 2023/go/internal/day_04/part_02/part_02_test.go (limited to '2023/go/internal/day_04') diff --git a/2023/go/internal/day_04/part_01/part_01.go b/2023/go/internal/day_04/part_01/part_01.go new file mode 100644 index 0000000..1a215b1 --- /dev/null +++ b/2023/go/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/2023/go/internal/day_04/part_01/part_01_test.go b/2023/go/internal/day_04/part_01/part_01_test.go new file mode 100644 index 0000000..f6f1a83 --- /dev/null +++ b/2023/go/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/2023/go/internal/day_04/part_02/part_02.go b/2023/go/internal/day_04/part_02/part_02.go new file mode 100644 index 0000000..a9e3152 --- /dev/null +++ b/2023/go/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/2023/go/internal/day_04/part_02/part_02_test.go b/2023/go/internal/day_04/part_02/part_02_test.go new file mode 100644 index 0000000..3c66a2e --- /dev/null +++ b/2023/go/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) + } +} -- cgit v1.2.3