advent-of-code/day07/main.go

270 lines
5.2 KiB
Go
Raw Normal View History

2023-12-07 19:21:10 -07:00
package main
import (
"bufio"
"fmt"
"sort"
2023-12-15 15:13:24 -07:00
aoc "go.sour.is/advent-of-code"
2023-12-13 08:32:37 -07:00
)
2023-12-07 19:21:10 -07:00
2023-12-13 08:32:37 -07:00
func main() { aoc.MustResult(aoc.Runner(run)) }
2023-12-07 19:21:10 -07:00
2023-12-13 08:32:37 -07:00
type result struct {
valuePT1 uint64
valuePT2 uint64
2023-12-07 19:21:10 -07:00
}
2023-12-13 08:32:37 -07:00
func run(scan *bufio.Scanner) (result, error) {
2023-12-08 12:11:44 -07:00
var game1, game2 Game
2023-12-07 19:21:10 -07:00
for scan.Scan() {
var cards string
var bid int
_, err := fmt.Sscanf(scan.Text(), "%s %d", &cards, &bid)
if err != nil {
panic(err)
}
fmt.Println("cards", cards, "bid", bid)
2023-12-08 12:11:44 -07:00
game1.Append(cards, bid)
game2.Append(cards, bid)
2023-12-07 19:21:10 -07:00
}
2023-12-08 12:11:44 -07:00
game1.cardTypes = cardTypes1
game1.cardOrder = getOrder(cardTypes1)
product1 := calcProduct(game1)
2023-12-07 19:21:10 -07:00
2023-12-08 12:11:44 -07:00
game2.cardTypes = cardTypes2
game2.cardOrder = getOrder(cardTypes2)
game2.wildCard = 'J'
product2 := calcProduct(game2)
2023-12-07 19:21:10 -07:00
2023-12-13 08:32:37 -07:00
return result{product1, product2}, nil
2023-12-07 19:21:10 -07:00
}
var cardTypes1 = []rune{'A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2'}
var cardTypes2 = []rune{'A', 'K', 'Q', 'T', '9', '8', '7', '6', '5', '4', '3', '2', 'J'}
2023-12-08 12:11:44 -07:00
func calcProduct(game Game) uint64 {
2023-12-07 19:21:10 -07:00
sort.Sort(game.plays)
var product uint64
for i, play := range game.plays {
rank := i + 1
fmt.Printf("play %d %s %d %s %x\n", rank, string(play.hand), play.bid, play.HandType(), play.HandStrength())
product += uint64(play.bid * rank)
}
return product
}
2023-12-07 20:09:58 -07:00
func getOrder(cardTypes []rune) map[rune]int {
2023-12-07 19:21:10 -07:00
cardOrder := make(map[rune]int, len(cardTypes))
for i, r := range cardTypes {
cardOrder[r] = len(cardTypes) - i
}
return cardOrder
}
type Game struct {
plays Plays
cardOrder map[rune]int
2023-12-08 12:11:44 -07:00
cardTypes []rune
2023-12-07 19:21:10 -07:00
wildCard rune
}
2023-12-08 12:11:44 -07:00
func (g *Game) Append(cards string, bid int) {
p := Play{bid: bid, hand: []rune(cards), game: g}
g.plays = append(g.plays, p)
}
2023-12-07 19:21:10 -07:00
type Play struct {
bid int
hand Hand
2023-12-08 12:11:44 -07:00
cardCounts map[rune]int
strength int
2023-12-07 19:21:10 -07:00
game *Game
}
type Hand []rune
2023-12-08 12:11:44 -07:00
func (h *Play) HandType() string {
hs := h.HandStrength()
kind := hs& 0xf00000
hc := h.game.cardTypes[13-hs&0xf0000>>16]
switch kind {
case 0x700000:
2023-12-07 19:21:10 -07:00
return "5K-" + string(hc)
2023-12-08 12:11:44 -07:00
case 0x600000:
2023-12-07 19:21:10 -07:00
return "4K-" + string(hc)
2023-12-08 12:11:44 -07:00
case 0x500000:
2023-12-07 19:21:10 -07:00
return "FH-" + string(hc)
2023-12-08 12:11:44 -07:00
case 0x400000:
2023-12-07 19:21:10 -07:00
return "3K-" + string(hc)
2023-12-08 12:11:44 -07:00
case 0x300000:
2023-12-07 19:21:10 -07:00
return "2P-" + string(hc)
2023-12-08 12:11:44 -07:00
case 0x200000:
2023-12-07 19:21:10 -07:00
return "1P-" + string(hc)
2023-12-08 12:11:44 -07:00
case 0x100000:
2023-12-07 19:21:10 -07:00
return "HC-" + string(hc)
}
return "Uno"
}
2023-12-08 12:11:44 -07:00
func (p *Play) HandStrength() int {
_, v := p.HighCard()
2023-12-07 19:21:10 -07:00
switch {
2023-12-08 12:11:44 -07:00
case p.IsFiveOfKind():
p.strength = 0x700000 | v
case p.IsFourOfKind():
p.strength = 0x600000 | v
case p.IsFullHouse():
p.strength = 0x500000 | v
case p.IsThreeOfKind():
p.strength = 0x400000 | v
case p.IsTwoPair():
p.strength = 0x300000 | v
case p.IsOnePair():
p.strength = 0x200000 | v
case p.IsHighCard():
p.strength = 0x100000 | v
2023-12-07 19:21:10 -07:00
}
2023-12-08 12:11:44 -07:00
return p.strength
2023-12-07 19:21:10 -07:00
}
func (h Play) IsFiveOfKind() bool {
2023-12-08 12:11:44 -07:00
_, _, _, _, has5 := h.game.hasSame(h.cardCounts)
2023-12-07 19:21:10 -07:00
return has5
}
func (h Play) IsFourOfKind() bool {
2023-12-08 12:11:44 -07:00
_, _, _, has4, _ := h.game.hasSame(h.cardCounts)
2023-12-07 19:21:10 -07:00
return has4
}
func (h Play) IsFullHouse() bool {
2023-12-08 12:11:44 -07:00
_, has2, has3, _, _ := h.game.hasSame(h.cardCounts)
2023-12-07 19:21:10 -07:00
return has3 && has2
}
func (h Play) IsThreeOfKind() bool {
2023-12-08 12:11:44 -07:00
has1, _, has3, _, _ := h.game.hasSame(h.cardCounts)
2023-12-07 19:21:10 -07:00
return has3 && has1
}
func (h Play) IsTwoPair() bool {
2023-12-08 12:11:44 -07:00
_, has2, has3, _, _ := h.game.hasSame(h.cardCounts)
return !has3 && has2 && h.game.pairs(h.cardCounts) == 2
2023-12-07 19:21:10 -07:00
}
func (h Play) IsOnePair() bool {
2023-12-08 12:11:44 -07:00
_, has2, has3, _, _ := h.game.hasSame(h.cardCounts)
return !has3 && has2 && h.game.pairs(h.cardCounts) == 1
2023-12-07 19:21:10 -07:00
}
func (h Play) IsHighCard() bool {
2023-12-08 12:11:44 -07:00
has1, has2, has3, has4, _ := h.game.hasSame(h.cardCounts)
2023-12-07 19:21:10 -07:00
return has1 && !has2 && !has3 && !has4
}
2023-12-08 12:11:44 -07:00
func (h *Play) HighCard() (rune, int) {
if h.cardCounts == nil {
h.generateCounts()
}
2023-12-07 19:21:10 -07:00
var i int
pairs := make(Pairs, 5)
2023-12-08 12:11:44 -07:00
for r, c := range h.cardCounts {
2023-12-07 19:21:10 -07:00
pairs[i].c = c
pairs[i].r = r
pairs[i].o = h.game.cardOrder[r]
i++
}
sort.Sort(sort.Reverse(pairs))
value := 0
for i, r := range h.hand {
if r == 0 {
continue
}
value |= h.game.cardOrder[r] << (4 * (4 - i))
}
return pairs[0].r, value
}
type Pairs []struct {
r rune
c int
o int
}
func (p Pairs) Len() int { return len(p) }
func (p Pairs) Less(i, j int) bool {
if p[i].c == p[j].c {
return p[i].o < p[j].o
}
return p[i].c < p[j].c
}
func (p Pairs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
type Plays []Play
func (p Plays) Len() int { return len(p) }
func (p Plays) Less(i, j int) bool { return p[i].HandStrength() < p[j].HandStrength() }
func (p Plays) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
2023-12-08 12:11:44 -07:00
func (p *Play) generateCounts() {
cardOrder := p.game.cardOrder
wildCard := p.game.wildCard
p.cardCounts = make(map[rune]int, len(cardOrder))
for _, c := range p.hand {
p.cardCounts[c]++
2023-12-07 19:21:10 -07:00
}
2023-12-08 12:11:44 -07:00
if wildCard != 0 && p.cardCounts[wildCard] > 0 {
2023-12-07 19:21:10 -07:00
var maxK rune
var maxV int
2023-12-08 12:11:44 -07:00
for k, v := range p.cardCounts {
if k != wildCard && v > maxV {
2023-12-07 19:21:10 -07:00
maxK, maxV = k, v
}
}
2023-12-08 12:11:44 -07:00
2023-12-07 19:21:10 -07:00
if maxK != 0 {
2023-12-08 12:11:44 -07:00
p.cardCounts[maxK] += p.cardCounts[wildCard]
delete(p.cardCounts, wildCard)
2023-12-07 19:21:10 -07:00
}
}
}
2023-12-08 12:11:44 -07:00
func (g *Game) hasSame(counts map[rune]int) (has1, has2, has3, has4, has5 bool) {
for _, c := range counts {
2023-12-07 19:21:10 -07:00
switch c {
case 1:
has1 = true
case 2:
has2 = true
case 3:
has3 = true
case 4:
has4 = true
case 5:
has5 = true
}
}
return
}
2023-12-08 12:11:44 -07:00
func (g *Game) pairs(counts map[rune]int) int {
2023-12-07 19:21:10 -07:00
pairs := 0
2023-12-08 12:11:44 -07:00
for _, n := range counts {
2023-12-07 19:21:10 -07:00
if n == 2 {
pairs++
}
}
return pairs
}