diff options
| author | DJ O'Leary <dijitol@proton.me> | 2025-11-29 20:23:02 +0100 |
|---|---|---|
| committer | DJ O'Leary <dijitol@proton.me> | 2025-11-29 20:23:02 +0100 |
| commit | 4c3a50166bf0f78ce89459c352cc1c6536a8b96d (patch) | |
| tree | 02e4b66a1435680ef1e92bf82576747b27f16447 /2023/go/internal/day_07 | |
| parent | be2f8528b3829f8263507da301812d852297c168 (diff) | |
chore: make language folders snake_case
Diffstat (limited to '2023/go/internal/day_07')
| -rw-r--r-- | 2023/go/internal/day_07/part_01/part_01.go | 273 | ||||
| -rw-r--r-- | 2023/go/internal/day_07/part_01/part_01_test.go | 432 |
2 files changed, 705 insertions, 0 deletions
diff --git a/2023/go/internal/day_07/part_01/part_01.go b/2023/go/internal/day_07/part_01/part_01.go new file mode 100644 index 0000000..a485dfd --- /dev/null +++ b/2023/go/internal/day_07/part_01/part_01.go @@ -0,0 +1,273 @@ +package part_01 + +import ( + "os" + "strconv" + "strings" +) + +const USE_SAMPLE = false + +type handType int + +const ( + HIGH_CARD handType = iota + PAIR + TWO_PAIR + THREE_OF_A_KIND + FULL_HOUSE + FOUR_OF_A_KIND + FIVE_OF_A_KIND +) + +func (h handType) String() string { + return [...]string{"HIGH_CARD", "PAIR", "TWO_PAIR", "THREE_OF_A_KIND", "FULL_HOUSE", "FOUR_OF_A_KIND", "FIVE_OF_A_KIND"}[h] +} + +type cardType int + +const ( + TWO cardType = iota + THREE + FOUR + FIVE + SIX + SEVEN + EIGHT + NINE + TEN + JACK + QUEEN + KING + ACE +) + +func (c cardType) String() string { + return [...]string{"TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "TEN", "JACK", "QUEEN", "KING", "ACE"}[c] +} + +type hand struct { + strength handType + cards []cardType + bet int +} + +/** + * Tried: + * - 252489894 (too high) + * - 229176554 (too low) + * - 200253108 (too low) + */ +func Solve() { + var filePath string + if USE_SAMPLE { + filePath = "../_input/day_07_part_01_sample.txt" + } else { + filePath = "../_input/day_07.txt" + } + contents, err := os.ReadFile(filePath) + if err != nil { + panic(err) + } + + lines := strings.Split(string(contents), "\r\n") + var hands []hand + for _, line := range lines { + if line == "" { + continue + } + + hands = append(hands, parseLine(line)) + } + + mappedByStrength := mapByStrength(hands) + + sortedByHighest := make(map[handType][]hand) + for _, strength := range []handType{HIGH_CARD, PAIR, TWO_PAIR, THREE_OF_A_KIND, FULL_HOUSE, FOUR_OF_A_KIND, FIVE_OF_A_KIND} { + if _, ok := mappedByStrength[strength]; !ok { + continue + } + + toBeSorted := mappedByStrength[strength] + sortedByHighest[strength] = sortByHighest(toBeSorted) + } + + flattened := flatten(sortedByHighest) + + answer := calculateWinnings(flattened) + + println("Answer: ", answer) +} + +func parseLine(line string) hand { + parts := strings.Split(line, " ") + + unparsedCards := strings.Split(parts[0], "") + cards := parseCards(unparsedCards) + strength := calculateStrength(cards) + + bet, err := strconv.Atoi(parts[1]) + if err != nil { + panic(err) + } + + return hand{ + strength, + cards, + bet, + } +} + +func parseCards(unparsedCards []string) []cardType { + var cards []cardType + for _, card := range unparsedCards { + switch card { + case "2": + cards = append(cards, TWO) + case "3": + cards = append(cards, THREE) + case "4": + cards = append(cards, FOUR) + case "5": + cards = append(cards, FIVE) + case "6": + cards = append(cards, SIX) + case "7": + cards = append(cards, SEVEN) + case "8": + cards = append(cards, EIGHT) + case "9": + cards = append(cards, NINE) + case "T": + cards = append(cards, TEN) + case "J": + cards = append(cards, JACK) + case "Q": + cards = append(cards, QUEEN) + case "K": + cards = append(cards, KING) + case "A": + cards = append(cards, ACE) + } + } + return cards +} + +func calculateStrength(cards []cardType) handType { + strength := map[cardType]int{} + for _, card := range cards { + if _, ok := strength[card]; !ok { + strength[card] = 0 + } + strength[card]++ + } + + var pairs int + var hasThreeOfAKind bool + for _, count := range strength { + if count == 5 { + return FIVE_OF_A_KIND + } + + if count == 4 { + return FOUR_OF_A_KIND + } + + if count == 3 { + hasThreeOfAKind = true + } + + if count == 2 { + pairs++ + } + } + + if hasThreeOfAKind && pairs == 1 { + return FULL_HOUSE + } + + if hasThreeOfAKind { + return THREE_OF_A_KIND + } + + if pairs == 2 { + return TWO_PAIR + } + + if pairs == 1 { + return PAIR + } + + return HIGH_CARD +} + +func mapByStrength(hands []hand) map[handType][]hand { + + handsByStrength := make(map[handType][]hand) + for _, hand := range hands { + handsByStrength[hand.strength] = append(handsByStrength[hand.strength], hand) + } + + return handsByStrength +} + +// low to high +func sortByHighest(hands []hand) []hand { + var partition = func(arr []hand, low, high int) ([]hand, int) { + pivot := arr[high] + index := low + + for i := low; i < high; i++ { + // Always 5 cards + for j := 0; j < 5; j++ { + if arr[i].cards[j] < pivot.cards[j] { + arr[i], arr[index] = arr[index], arr[i] + index++ + break + } + + if arr[i].cards[j] != pivot.cards[j] { + break + } + } + } + + arr[high], arr[index] = arr[index], arr[high] + + return arr, index + } + + var quickSort func(arr []hand, low, high int) []hand + quickSort = func(arr []hand, low, high int) []hand { + if low < high { + arr, pivot := partition(arr, low, high) + + quickSort(arr, low, pivot-1) + quickSort(arr, pivot+1, high) + } + + return arr + } + + hands = quickSort(hands, 0, len(hands)-1) + + return hands +} + +func flatten(hands map[handType][]hand) []hand { + var flat []hand + for _, handType := range []handType{HIGH_CARD, PAIR, TWO_PAIR, THREE_OF_A_KIND, FULL_HOUSE, FOUR_OF_A_KIND, FIVE_OF_A_KIND} { + flat = append(flat, hands[handType]...) + } + return flat +} + +func calculateWinnings(hands []hand) int { + var winnings int + for i, hand := range hands { + earned := hand.bet * (i + 1) + winnings += earned + println("hand ", i, ": { strength: ", hand.strength.String(), ", cards: [", hand.cards[0], hand.cards[1], hand.cards[2], hand.cards[3], hand.cards[4], "], earned: ", earned, " }") + } + return winnings +} diff --git a/2023/go/internal/day_07/part_01/part_01_test.go b/2023/go/internal/day_07/part_01/part_01_test.go new file mode 100644 index 0000000..6ceabe8 --- /dev/null +++ b/2023/go/internal/day_07/part_01/part_01_test.go @@ -0,0 +1,432 @@ +package part_01 + +import "testing" + +func TestParseLine(t *testing.T) { + tests := map[string]struct { + input string + expected hand + }{ + "sample_1": { + input: "32T3K 765", + expected: hand{ + strength: PAIR, + cards: []cardType{THREE, TWO, TEN, THREE, KING}, + bet: 765, + }, + }, + "sample_2": { + input: "T55J5 684", + expected: hand{ + strength: THREE_OF_A_KIND, + cards: []cardType{TEN, FIVE, FIVE, JACK, FIVE}, + bet: 684, + }, + }, + "sample_3": { + input: "KK677 28", + expected: hand{ + strength: TWO_PAIR, + cards: []cardType{KING, KING, SIX, SEVEN, SEVEN}, + bet: 28, + }, + }, + "sample_4": { + input: "KTJJT 220", + expected: hand{ + strength: TWO_PAIR, + cards: []cardType{KING, TEN, JACK, JACK, TEN}, + bet: 220, + }, + }, + "sample_5": { + input: "QQQJA 483", + expected: hand{ + strength: THREE_OF_A_KIND, + cards: []cardType{QUEEN, QUEEN, QUEEN, JACK, ACE}, + bet: 483, + }, + }, + "full_house": { + input: "QQQAA 999", + expected: hand{ + strength: FULL_HOUSE, + cards: []cardType{QUEEN, QUEEN, QUEEN, ACE, ACE}, + bet: 999, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + actual := parseLine(test.input) + + if actual.strength != test.expected.strength { + t.Errorf("Strength: expected %v, got %v", test.expected.strength, actual.strength) + } + + if len(actual.cards) != len(test.expected.cards) { + t.Errorf("Cards: expected %v, got %v", test.expected.cards, actual.cards) + } + + for i, card := range actual.cards { + if card != test.expected.cards[i] { + t.Errorf("Card Order: expected %v, got %v", test.expected.cards, actual.cards) + } + } + + if actual.bet != test.expected.bet { + t.Errorf("Bet: expected %v, got %v", test.expected.bet, actual.bet) + } + }) + } +} + +func TestMapByStrength(t *testing.T) { + tests := map[string]struct { + input []hand + expected map[handType][]hand + }{ + "sample": { + input: []hand{ + { + strength: PAIR, + cards: []cardType{THREE, TWO, TEN, THREE, KING}, + bet: 765, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{TEN, FIVE, FIVE, JACK, FIVE}, + bet: 684, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, KING, SIX, SEVEN, SEVEN}, + bet: 28, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, TEN, JACK, JACK, TEN}, + bet: 220, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{QUEEN, QUEEN, QUEEN, JACK, ACE}, + bet: 483, + }, + }, + expected: map[handType][]hand{ + PAIR: { + { + strength: PAIR, + cards: []cardType{THREE, TWO, TEN, THREE, KING}, + bet: 765, + }, + }, + TWO_PAIR: { + { + strength: TWO_PAIR, + cards: []cardType{KING, KING, SIX, SEVEN, SEVEN}, + bet: 28, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, TEN, JACK, JACK, TEN}, + bet: 220, + }, + }, + THREE_OF_A_KIND: { + { + strength: THREE_OF_A_KIND, + cards: []cardType{TEN, FIVE, FIVE, JACK, FIVE}, + bet: 684, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{QUEEN, QUEEN, QUEEN, JACK, ACE}, + bet: 483, + }, + }, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + actual := mapByStrength(test.input) + + for strength, hands := range actual { + expected := test.expected[strength] + + if len(hands) != len(expected) { + t.Errorf("Length: expected %v, got %v", len(expected), len(hands)) + } + + for i, hand := range hands { + if hand.strength != expected[i].strength { + t.Errorf("Strength: expected %v, got %v", expected[i].strength, hand.strength) + } + + if len(hand.cards) != len(expected[i].cards) { + t.Errorf("Cards: expected %v, got %v", expected[i].cards, hand.cards) + } + + for j, card := range hand.cards { + if card != expected[i].cards[j] { + t.Errorf("Card Order: expected %v, got %v", expected[i].cards, hand.cards) + } + } + + if hand.bet != expected[i].bet { + t.Errorf("Bet: expected %v, got %v", expected[i].bet, hand.bet) + } + } + } + }) + } +} + +func TestSortByHighest(t *testing.T) { + tests := map[string]struct { + input []hand + expected []hand + }{ + "sample": { + input: []hand{ + { + strength: PAIR, + cards: []cardType{THREE, TWO, TEN, THREE, KING}, + bet: 765, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{TEN, FIVE, FIVE, JACK, FIVE}, + bet: 684, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, KING, SIX, SEVEN, SEVEN}, + bet: 28, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, TEN, JACK, JACK, TEN}, + bet: 220, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{QUEEN, QUEEN, QUEEN, JACK, ACE}, + bet: 483, + }, + }, + expected: []hand{ + { + strength: PAIR, + cards: []cardType{THREE, TWO, TEN, THREE, KING}, + bet: 765, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{TEN, FIVE, FIVE, JACK, FIVE}, + bet: 684, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{QUEEN, QUEEN, QUEEN, JACK, ACE}, + bet: 483, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, TEN, JACK, JACK, TEN}, + bet: 220, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, KING, SIX, SEVEN, SEVEN}, + bet: 28, + }, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + actual := sortByHighest(test.input) + + if len(actual) != len(test.expected) { + t.Errorf("Length: expected %v, got %v", len(test.expected), len(actual)) + } + + for i, hand := range actual { + if hand.strength != test.expected[i].strength { + t.Errorf("Strength: expected %v, got %v", test.expected[i].strength, hand.strength) + } + + if len(hand.cards) != len(test.expected[i].cards) { + t.Errorf("Cards: expected %v, got %v", test.expected[i].cards, hand.cards) + } + + for j, card := range hand.cards { + if card != test.expected[i].cards[j] { + t.Errorf("Card Order: expected %v, got %v", test.expected[i].cards, hand.cards) + } + } + + if hand.bet != test.expected[i].bet { + t.Errorf("Bet: expected %v, got %v", test.expected[i].bet, hand.bet) + } + } + }) + } +} + +func TestFlatten(t *testing.T) { + tests := map[string]struct { + input map[handType][]hand + expected []hand + }{ + "sample": { + // Order jumbled to ensure sorting works + input: map[handType][]hand{ + TWO_PAIR: { + { + strength: TWO_PAIR, + cards: []cardType{KING, TEN, JACK, JACK, TEN}, + bet: 220, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, KING, SIX, SEVEN, SEVEN}, + bet: 28, + }, + }, + PAIR: { + { + strength: PAIR, + cards: []cardType{THREE, TWO, TEN, THREE, KING}, + bet: 765, + }, + }, + THREE_OF_A_KIND: { + { + strength: THREE_OF_A_KIND, + cards: []cardType{TEN, FIVE, FIVE, JACK, FIVE}, + bet: 684, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{QUEEN, QUEEN, QUEEN, JACK, ACE}, + bet: 483, + }, + }, + }, + expected: []hand{ + { + strength: PAIR, + cards: []cardType{THREE, TWO, TEN, THREE, KING}, + bet: 765, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, TEN, JACK, JACK, TEN}, + bet: 220, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, KING, SIX, SEVEN, SEVEN}, + bet: 28, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{TEN, FIVE, FIVE, JACK, FIVE}, + bet: 684, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{QUEEN, QUEEN, QUEEN, JACK, ACE}, + bet: 483, + }, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + actual := flatten(test.input) + + if len(actual) != len(test.expected) { + t.Errorf("Length: expected %v, got %v", len(test.expected), len(actual)) + } + + for i, hand := range actual { + expected := test.expected[i] + + if hand.strength != expected.strength { + t.Errorf("Strength: expected %v, got %v", expected.strength, hand.strength) + } + + if len(hand.cards) != len(expected.cards) { + t.Errorf("Cards: expected %v, got %v", expected.cards, hand.cards) + } + + for i, card := range hand.cards { + if card != expected.cards[i] { + t.Errorf("Card Order: expected %v, got %v", expected.cards, hand.cards) + } + } + + if hand.bet != expected.bet { + t.Errorf("Bet: expected %v, got %v", expected.bet, hand.bet) + } + } + }) + } +} + +func TestCalculateWinnings(t *testing.T) { + tests := map[string]struct { + input []hand + expected int + }{ + "sample": { + input: []hand{ + { + strength: PAIR, + cards: []cardType{THREE, TWO, TEN, THREE, KING}, + bet: 765, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, TEN, JACK, JACK, TEN}, + bet: 220, + }, + { + strength: TWO_PAIR, + cards: []cardType{KING, KING, SIX, SEVEN, SEVEN}, + bet: 28, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{TEN, FIVE, FIVE, JACK, FIVE}, + bet: 684, + }, + { + strength: THREE_OF_A_KIND, + cards: []cardType{QUEEN, QUEEN, QUEEN, JACK, ACE}, + bet: 483, + }, + }, + expected: 6440, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + actual := calculateWinnings(test.input) + + if actual != test.expected { + t.Errorf("Winnings: expected %v, got %v", test.expected, actual) + } + }) + } +} |
