Files
advent-of-gode/2023/07/code.go
2023-12-07 08:34:00 +02:00

286 lines
4.8 KiB
Go

package main
import (
"fmt"
"sort"
"strconv"
"strings"
"unicode"
"github.com/jpillora/puzzler/harness/aoc"
)
func main() {
aoc.Harness(run)
}
type Hand struct {
Cards string
Bid int
}
func getCardPower(card rune) int {
if unicode.IsNumber(card) {
i, _ := strconv.Atoi(string(card))
return i
}
if card == 'T' {
return 10
}
if card == 'J' {
return 11
}
if card == 'Q' {
return 12
}
if card == 'K' {
return 13
}
if card == 'A' {
return 14
}
panic(fmt.Sprintf("Bad card %v", card))
}
func getCardPowerJokerRules(card rune) int {
if unicode.IsNumber(card) {
i, _ := strconv.Atoi(string(card))
return i
}
if card == 'T' {
return 10
}
if card == 'J' {
return 0
}
if card == 'Q' {
return 12
}
if card == 'K' {
return 13
}
if card == 'A' {
return 14
}
panic(fmt.Sprintf("Bad card %v", card))
}
func getHandTypeRanking(hand Hand) int {
cardUniq := make(map[string]int)
for _, c := range strings.Split(hand.Cards, "") {
if _, ok := cardUniq[c]; !ok {
cardUniq[c] = 0
}
cardUniq[c] += 1
}
if len(cardUniq) == 1 {
// Full house, top priority
return 7
}
if len(cardUniq) == 2 {
// Either four of a kind or full house
for _, x := range cardUniq {
if x == 4 || x == 1 {
// Four of a kind
return 6
} else {
// Must be full house
return 5
}
}
}
if len(cardUniq) == 3 {
// Either three of a kind or two pair
for _, x := range cardUniq {
if x == 3 {
// Three of a kind
return 4
} else if x == 2 {
// Two pair
return 3
}
}
}
if len(cardUniq) == 4 {
// Must be one pair
return 2
}
// Must be high card
return 1
}
func getHandTypeRankingJokerRules(hand Hand) int {
cardUniq := make(map[string]int)
jokerCount := 0
for _, c := range strings.Split(hand.Cards, "") {
if _, ok := cardUniq[c]; !ok {
cardUniq[c] = 0
}
cardUniq[c] += 1
if c == "J" {
jokerCount += 1
}
}
if len(cardUniq) == 1 {
// Full house, top priority
return 7
}
if len(cardUniq) == 2 {
// Either four of a kind or full house
for _, x := range cardUniq {
if x == 4 || x == 1 {
// Four of a kind
if jokerCount == 4 || jokerCount == 1 {
// Can be converted to 5oac
return 7
}
return 6
} else {
// Must be full house
if jokerCount == 3 || jokerCount == 2 {
// Can be converted to 5oac
return 7
}
return 5
}
}
}
if len(cardUniq) == 3 {
// Either three of a kind or two pair
for _, x := range cardUniq {
if x == 3 {
// Three of a kind
if jokerCount == 1 || jokerCount == 3 {
// Can be converted to 4oac
return 6
}
return 4
} else if x == 2 {
// Two pair
if jokerCount == 2 {
// Can be converted to 4oac
return 6
} else if jokerCount == 1 {
// Can be converted to full house
return 5
}
return 3
}
}
}
if len(cardUniq) == 4 {
// Must be one pair
if jokerCount == 2 || jokerCount == 1 {
// Can be converted to 3oac
return 4
}
return 2
}
// Must be high card
if jokerCount == 1 {
// Converted to one pair
return 2
}
return 1
}
// on code change, run will be executed 4 times:
// 1. with: false (part1), and example input
// 2. with: true (part2), and example input
// 3. with: false (part1), and user input
// 4. with: true (part2), and user input
// the return value of each run is printed to stdout
func run(part2 bool, input string) any {
hands := make([]Hand, 0)
for _, line := range strings.Split(input, "\n") {
if line == "" {
continue
}
lspl := strings.Split(line, " ")
bid, _ := strconv.Atoi(lspl[1])
hands = append(hands, Hand{lspl[0], bid})
}
if part2 {
sort.SliceStable(hands, func(i, j int) bool {
// First compare rank
hRank1 := getHandTypeRankingJokerRules(hands[i])
hRank2 := getHandTypeRankingJokerRules(hands[j])
if hRank1 != hRank2 {
return hRank1 < hRank2
}
// Otherwise, compare cards
for k := 0; k < 5; k++ {
cRank1 := getCardPowerJokerRules(rune(hands[i].Cards[k]))
cRank2 := getCardPowerJokerRules(rune(hands[j].Cards[k]))
if cRank1 != cRank2 {
return cRank1 < cRank2
}
}
panic(fmt.Sprintf("Could not sort %v and %v", hands[i].Cards, hands[j].Cards))
})
} else {
sort.SliceStable(hands, func(i, j int) bool {
// First compare rank
hRank1 := getHandTypeRanking(hands[i])
hRank2 := getHandTypeRanking(hands[j])
if hRank1 != hRank2 {
return hRank1 < hRank2
}
// Otherwise, compare cards
for k := 0; k < 5; k++ {
cRank1 := getCardPower(rune(hands[i].Cards[k]))
cRank2 := getCardPower(rune(hands[j].Cards[k]))
if cRank1 != cRank2 {
return cRank1 < cRank2
}
}
panic(fmt.Sprintf("Could not sort %v and %v", hands[i].Cards, hands[j].Cards))
})
}
// solve part 1 here
winnings := 0
for i, hand := range hands {
winnings += (i + 1) * hand.Bid
}
return winnings
}