Created
December 11, 2023 21:07
-
-
Save phrozen/d27f6b775af634d34d6214ee103e4f51 to your computer and use it in GitHub Desktop.
Advent of Code 2023 - Day 7
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"advent/utils" | |
"fmt" | |
"math" | |
"sort" | |
"strconv" | |
"strings" | |
) | |
const ( | |
_ int = iota | |
HighCard | |
OnePair | |
TwoPair | |
ThreeOfAKind | |
FullHouse | |
FourOfAKind | |
FiveOfAKind | |
) | |
var VALUES = map[rune]int{ | |
'2': 2, | |
'3': 3, | |
'4': 4, | |
'5': 5, | |
'6': 6, | |
'7': 7, | |
'8': 8, | |
'9': 9, | |
'T': 10, | |
'J': 11, | |
'Q': 12, | |
'K': 13, | |
'A': 14, | |
} | |
func Value(b rune) int { | |
return VALUES[b] | |
} | |
// Helper type to sort by card real value | |
type Cards []byte | |
func (c Cards) Len() int { return len(c) } | |
func (c Cards) Swap(i, j int) { c[i], c[j] = c[j], c[i] } | |
func (c Cards) Less(i, j int) bool { return Value(rune(c[i])) < Value(rune(c[j])) } | |
// Helper type to sort cards by Rank (Level -> Points) | |
type Hands []Hand | |
func (h Hands) Len() int { return len(h) } | |
func (h Hands) Swap(i, j int) { h[i], h[j] = h[j], h[i] } | |
func (h Hands) Less(i, j int) bool { | |
if h[i].Level == h[j].Level { | |
return h[i].Points < h[j].Points | |
} | |
return h[i].Level < h[j].Level | |
} | |
func (h Hands) String() string { | |
s := "" | |
for i := range h { | |
s += fmt.Sprintf("%5d : %s [L:%d] (%7d pts)\n", i+1, h[i].Raw, h[i].Level, h[i].Points) | |
} | |
return s | |
} | |
type Hand struct { | |
Raw string | |
Bid int | |
Points int | |
Level int | |
} | |
func ParseInput(src string) []Hand { | |
hands := make([]Hand, 0) | |
lines := utils.LoadLinesFromFile(src) | |
for _, line := range lines { | |
data := strings.Split(line, " ") | |
// RAW HAND DATA | |
raw := data[0] | |
bid, _ := strconv.Atoi(data[1]) | |
h := Hand{ | |
Raw: raw, | |
Bid: bid, | |
} | |
hands = append(hands, h) | |
} | |
return hands | |
} | |
func HandLevel(hand string) int { | |
sorted := Cards([]byte(hand)) | |
sort.Sort(sorted) | |
c1, c2, c3, c4, c5 := sorted[0], sorted[1], sorted[2], sorted[3], sorted[4] | |
switch { | |
case c1 == c5: | |
return FiveOfAKind | |
case c1 == c4 || c2 == c5: | |
return FourOfAKind | |
case (c1 == c3 && c4 == c5) || (c1 == c2 && c3 == c5): | |
return FullHouse | |
case c1 == c3 || c2 == c4 || c3 == c5: | |
return ThreeOfAKind | |
case (c1 == c2 && c3 == c4) || (c2 == c3 && c4 == c5) || (c1 == c2 && c4 == c5): | |
return TwoPair | |
case c1 == c2 || c2 == c3 || c3 == c4 || c4 == c5: | |
return OnePair | |
default: | |
return HighCard | |
} | |
} | |
func PointHand(hand string) int { | |
points := 0 | |
for i := range hand { | |
points += Value(rune(hand[i])) * int(math.Pow10(10-i*2)) | |
} | |
return points | |
} | |
func ReplaceJokers(hand string) string { | |
if hand == "JJJJJ" { | |
return hand | |
} | |
counts := make(map[rune]int) | |
var maxRune rune | |
var maxCount int | |
for _, c := range hand { | |
if c == 'J' { | |
continue | |
} | |
counts[c]++ | |
if counts[c] > maxCount { | |
maxRune = c | |
maxCount = counts[c] | |
} | |
if counts[c] == maxCount && Value(c) > Value(maxRune) { | |
maxRune = c | |
} | |
} | |
mod := strings.ReplaceAll(hand, "J", string(maxRune)) | |
return mod | |
} | |
func PartOne(hands Hands) int { | |
for i := range hands { | |
hands[i].Level = HandLevel(hands[i].Raw) | |
hands[i].Points = PointHand(hands[i].Raw) | |
} | |
sort.Sort(hands) | |
fmt.Println(hands) | |
winnings := 0 | |
for i := range hands { | |
winnings += hands[i].Bid * (i + 1) | |
} | |
return winnings | |
} | |
func PartTwo(hands Hands) int { | |
// Modify Joker value for pointing | |
VALUES['J'] = 1 | |
// If hand contains J replace them with most common card | |
// for calculating the hand Level, else business as usual | |
for i := range hands { | |
hand := hands[i].Raw | |
if strings.Contains(hand, "J") { | |
hand = ReplaceJokers(hand) | |
} | |
hands[i].Level = HandLevel(hand) | |
hands[i].Points = PointHand(hands[i].Raw) | |
} | |
sort.Sort(hands) | |
fmt.Println(hands) | |
winnings := 0 | |
for i := range hands { | |
winnings += hands[i].Bid * (i + 1) | |
} | |
return winnings | |
} | |
func main() { | |
fmt.Println(PartOne(ParseInput("input.txt"))) | |
fmt.Println(PartTwo(ParseInput("input.txt"))) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment