Add day 7 part 1
parent
89112a526f
commit
440c89b9b2
|
@ -0,0 +1,221 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Card int
|
||||
|
||||
const (
|
||||
Two Card = iota + 2
|
||||
Three
|
||||
Four
|
||||
Five
|
||||
Six
|
||||
Seven
|
||||
Eight
|
||||
Nine
|
||||
Ten
|
||||
Jack
|
||||
Queen
|
||||
King
|
||||
Ace
|
||||
)
|
||||
|
||||
type HandKind int
|
||||
|
||||
const (
|
||||
UnknownKind HandKind = iota
|
||||
HighCard
|
||||
OnePair
|
||||
TwoPair
|
||||
ThreeOfAKind
|
||||
FullHouse
|
||||
FourOfAKind
|
||||
FiveOfAKind
|
||||
)
|
||||
|
||||
type Hand []Card
|
||||
|
||||
type Player struct {
|
||||
bid int
|
||||
hand Hand
|
||||
}
|
||||
|
||||
func (hand Hand) Kind() (HandKind, error) {
|
||||
handCounts := map[HandKind][]int{
|
||||
FiveOfAKind: {5},
|
||||
FourOfAKind: {1, 4},
|
||||
FullHouse: {2, 3},
|
||||
ThreeOfAKind: {1, 1, 3},
|
||||
TwoPair: {1, 2, 2},
|
||||
OnePair: {1, 1, 1, 2},
|
||||
HighCard: {1, 1, 1, 1, 1},
|
||||
}
|
||||
|
||||
distinctCounts := hand.CountDistinct()
|
||||
cardCounts := mapValues(distinctCounts)
|
||||
slices.Sort(cardCounts)
|
||||
for kind, kindCounts := range handCounts {
|
||||
if slices.Equal(cardCounts, kindCounts) {
|
||||
return kind, nil
|
||||
}
|
||||
}
|
||||
|
||||
return UnknownKind, errors.New("got it")
|
||||
}
|
||||
|
||||
func (hand Hand) CountDistinct() map[Card]int {
|
||||
buckets := make(map[Card]int)
|
||||
for _, card := range hand {
|
||||
buckets[card]++
|
||||
}
|
||||
|
||||
return buckets
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 && len(os.Args) != 3 {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s inputfile\n", os.Args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
inputFilename := os.Args[1]
|
||||
inputFile, err := os.Open(inputFilename)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("could not open input file: %s", err))
|
||||
}
|
||||
|
||||
defer inputFile.Close()
|
||||
|
||||
inputBytes, err := io.ReadAll(inputFile)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("could not read input file: %s", err))
|
||||
}
|
||||
|
||||
input := strings.TrimSpace(string(inputBytes))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse input: %s", err))
|
||||
}
|
||||
|
||||
inputLines := strings.Split(input, "\n")
|
||||
players, err := parsePlayers(inputLines)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse races: %s", err))
|
||||
}
|
||||
|
||||
for _, player := range players {
|
||||
kind, _ := player.hand.Kind()
|
||||
fmt.Printf("%+v %+v\n", player.hand, kind)
|
||||
}
|
||||
|
||||
fmt.Printf("Part 1: %d\n", part1(players))
|
||||
}
|
||||
|
||||
func part1(players []Player) int {
|
||||
sortedPlayers := slices.Clone(players)
|
||||
slices.SortFunc(sortedPlayers, func(a, b Player) int {
|
||||
aKind, err := a.hand.Kind()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid hand %+v: %w", a, err))
|
||||
}
|
||||
|
||||
bKind, err := b.hand.Kind()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("invalid hand %+v: %w", b, err))
|
||||
}
|
||||
|
||||
compareHands := cmp.Compare(aKind, bKind)
|
||||
if compareHands == 0 {
|
||||
return slices.Compare(a.hand, b.hand)
|
||||
} else {
|
||||
return compareHands
|
||||
}
|
||||
})
|
||||
|
||||
winnings := 0
|
||||
for i, player := range sortedPlayers {
|
||||
winnings += (i + 1) * player.bid
|
||||
}
|
||||
|
||||
return winnings
|
||||
}
|
||||
|
||||
func parsePlayers(inputLines []string) ([]Player, error) {
|
||||
players := make([]Player, 0, len(inputLines))
|
||||
for i, line := range inputLines {
|
||||
player, err := parsePlayer(line)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse line %d: %w", i, err)
|
||||
}
|
||||
|
||||
players = append(players, player)
|
||||
}
|
||||
|
||||
return players, nil
|
||||
}
|
||||
|
||||
func parsePlayer(inputLine string) (Player, error) {
|
||||
lineComponents := strings.Split(inputLine, " ")
|
||||
if len(lineComponents) != 2 {
|
||||
return Player{}, errors.New("malformed player line")
|
||||
}
|
||||
|
||||
hand, err := parseHand(lineComponents[0])
|
||||
if err != nil {
|
||||
return Player{}, fmt.Errorf("parse hand: %w", err)
|
||||
}
|
||||
|
||||
bid, err := strconv.Atoi(lineComponents[1])
|
||||
if err != nil {
|
||||
return Player{}, fmt.Errorf("parse bid: %w", err)
|
||||
}
|
||||
|
||||
return Player{bid: bid, hand: hand}, nil
|
||||
}
|
||||
|
||||
func parseHand(handStr string) (Hand, error) {
|
||||
cardMap := map[byte]Card{
|
||||
'A': Ace,
|
||||
'K': King,
|
||||
'Q': Queen,
|
||||
'J': Jack,
|
||||
'T': Ten,
|
||||
'9': Nine,
|
||||
'8': Eight,
|
||||
'7': Seven,
|
||||
'6': Six,
|
||||
'5': Five,
|
||||
'4': Four,
|
||||
'3': Three,
|
||||
'2': Two,
|
||||
}
|
||||
|
||||
hand := Hand{}
|
||||
for _, handChar := range handStr {
|
||||
card, ok := cardMap[byte(handChar)]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid card character %c", handChar)
|
||||
}
|
||||
|
||||
hand = append(hand, card)
|
||||
}
|
||||
|
||||
return hand, nil
|
||||
}
|
||||
|
||||
func mapValues[T comparable, U any](m map[T]U) []U {
|
||||
values := make([]U, 0, len(m))
|
||||
for _, value := range m {
|
||||
values = append(values, value)
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
Loading…
Reference in New Issue