mirror of
https://github.com/onyx-and-iris/aoc2023.git
synced 2024-11-21 18:00:47 +00:00
day-7
This commit is contained in:
parent
1c686b53de
commit
4aafad4e8d
8
day-7/go.mod
Normal file
8
day-7/go.mod
Normal file
@ -0,0 +1,8 @@
|
||||
module github.com/onyx-and-iris/aoc2023/day-7
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/samber/lo v1.39.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
|
||||
)
|
4
day-7/go.sum
Normal file
4
day-7/go.sum
Normal file
@ -0,0 +1,4 @@
|
||||
github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
|
||||
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
100
day-7/hand_test.go
Normal file
100
day-7/hand_test.go
Normal file
@ -0,0 +1,100 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetMatches(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h1 := newHand("32T3K", 0)
|
||||
h2 := newHand("ABC3D", 0)
|
||||
|
||||
res1 := map[rune]int{
|
||||
'3': 2,
|
||||
'2': 1,
|
||||
'T': 1,
|
||||
'K': 1,
|
||||
}
|
||||
res2 := map[rune]int{
|
||||
'A': 1,
|
||||
'B': 1,
|
||||
'C': 1,
|
||||
'3': 1,
|
||||
'D': 1,
|
||||
}
|
||||
|
||||
t.Run("Should produce equal maps", func(t *testing.T) {
|
||||
assert.Equal(t, res1, h1.getMatches())
|
||||
})
|
||||
|
||||
t.Run("Should produce equal maps", func(t *testing.T) {
|
||||
assert.Equal(t, res2, h2.getMatches())
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsHighCard(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h1 := newHand("ABC12", 0)
|
||||
|
||||
t.Run("Should be a high card", func(t *testing.T) {
|
||||
assert.Equal(t, true, isHighCard(h1))
|
||||
})
|
||||
|
||||
t.Run("Should not be a four of a kind", func(t *testing.T) {
|
||||
assert.Equal(t, false, isFourOfAKind(h1))
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsFourOfAKind(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h1 := newHand("UUUU9", 0)
|
||||
h2 := newHand("UUU99", 0)
|
||||
|
||||
t.Run("Should be a four of a kind", func(t *testing.T) {
|
||||
assert.Equal(t, true, isFourOfAKind(h1))
|
||||
})
|
||||
|
||||
t.Run("Should not be a four of a kind", func(t *testing.T) {
|
||||
assert.Equal(t, false, isFourOfAKind(h2))
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsFullHouse(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h1 := newHand("333KK", 0)
|
||||
h2 := newHand("3338K", 0)
|
||||
|
||||
t.Run("Should be a fullhouse", func(t *testing.T) {
|
||||
assert.Equal(t, true, isFullHouse(h1))
|
||||
})
|
||||
|
||||
t.Run("Should not be a fullhouse", func(t *testing.T) {
|
||||
assert.Equal(t, false, isFullHouse(h2))
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsTwoPair(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h1 := newHand("33QQA", 0)
|
||||
|
||||
t.Run("Should be a twopair", func(t *testing.T) {
|
||||
assert.Equal(t, true, isTwoPair(h1))
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsOnePair(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h1 := newHand("33QKA", 0)
|
||||
|
||||
t.Run("Should be a onepair", func(t *testing.T) {
|
||||
assert.Equal(t, true, isOnePair(h1))
|
||||
})
|
||||
}
|
108
day-7/hands.go
Normal file
108
day-7/hands.go
Normal file
@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
var hands = []*hand{}
|
||||
|
||||
type hand struct {
|
||||
cards string
|
||||
bid int
|
||||
matches map[rune]int
|
||||
numPairs int
|
||||
_kind int
|
||||
}
|
||||
|
||||
func newHand(cards string, bid int) *hand {
|
||||
h := &hand{cards: cards, bid: bid}
|
||||
setKind(h)
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *hand) kind() int {
|
||||
return h._kind
|
||||
}
|
||||
|
||||
func (h *hand) getMatches() map[rune]int {
|
||||
if h.matches == nil {
|
||||
h.matches = matches(h.cards)
|
||||
}
|
||||
return h.matches
|
||||
}
|
||||
|
||||
func setKind(h *hand) {
|
||||
if isFiveOfAKind(h) {
|
||||
h._kind = fiveofakind
|
||||
} else if isHighCard(h) {
|
||||
h._kind = highcard
|
||||
} else if isFourOfAKind(h) {
|
||||
h._kind = fourofakind
|
||||
} else if isFullHouse(h) {
|
||||
h._kind = fullhouse
|
||||
} else if isThreeOfAKind(h) {
|
||||
h._kind = threeofakind
|
||||
} else if isTwoPair(h) {
|
||||
h._kind = twopair
|
||||
} else if isOnePair(h) {
|
||||
h._kind = onepair
|
||||
}
|
||||
}
|
||||
|
||||
func isFiveOfAKind(h *hand) bool {
|
||||
return len(h.getMatches()) == 1
|
||||
}
|
||||
|
||||
func isHighCard(h *hand) bool {
|
||||
return len(h.getMatches()) == 5
|
||||
}
|
||||
|
||||
func isFourOfAKind(h *hand) bool {
|
||||
if len(h.getMatches()) == 2 {
|
||||
for _, c := range h.matches {
|
||||
if c == 4 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isFullHouse(h *hand) bool {
|
||||
if len(h.getMatches()) == 2 {
|
||||
return !isFourOfAKind(h)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isThreeOfAKind(h *hand) bool {
|
||||
if len(h.getMatches()) == 3 {
|
||||
for _, c := range h.matches {
|
||||
if c == 3 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getNumPairs(h *hand) int {
|
||||
if h.numPairs == 0 {
|
||||
allowed := []int{1, 2}
|
||||
for _, n := range h.getMatches() {
|
||||
if !contains(allowed, n) {
|
||||
return -1 // there may be a pair but its a fullhouse
|
||||
}
|
||||
if n == 2 {
|
||||
h.numPairs++
|
||||
}
|
||||
}
|
||||
}
|
||||
return h.numPairs
|
||||
}
|
||||
|
||||
func isTwoPair(h *hand) bool {
|
||||
return getNumPairs(h) == 2
|
||||
}
|
||||
|
||||
func isOnePair(h *hand) bool {
|
||||
return !isTwoPair(h) && h.numPairs == 1
|
||||
}
|
31
day-7/one.go
Normal file
31
day-7/one.go
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import log "github.com/sirupsen/logrus"
|
||||
|
||||
const (
|
||||
highcard = iota // 0
|
||||
onepair = iota // 1
|
||||
twopair = iota // 2
|
||||
threeofakind = iota // 3
|
||||
fullhouse = iota // 4
|
||||
fourofakind = iota // 5
|
||||
fiveofakind = iota // 6
|
||||
numKinds = iota
|
||||
)
|
||||
|
||||
// one returns the sum of products of hand values by bids
|
||||
func one(lines []string) (int, error) {
|
||||
parselines(lines)
|
||||
|
||||
strength := []rune{'A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2'}
|
||||
log.Debug("strength: ", strength)
|
||||
sortByKindAndStrength(strength)
|
||||
|
||||
sum := 0
|
||||
|
||||
for i, hand := range hands {
|
||||
sum += (i + 1) * hand.bid
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
27
day-7/solution.go
Normal file
27
day-7/solution.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetLevel(log.InfoLevel)
|
||||
}
|
||||
|
||||
func main() {
|
||||
lines := readlines()
|
||||
|
||||
ans, err := one(lines)
|
||||
if err != nil {
|
||||
log.Fatal()
|
||||
}
|
||||
fmt.Printf("solution one: %d\n", ans)
|
||||
|
||||
ans, err = two(lines)
|
||||
if err != nil {
|
||||
log.Fatal()
|
||||
}
|
||||
fmt.Printf("solution two: %d\n", ans)
|
||||
}
|
74
day-7/two.go
Normal file
74
day-7/two.go
Normal file
@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
import log "github.com/sirupsen/logrus"
|
||||
|
||||
// promote alters a hands kind based on joker rules
|
||||
func promote(hand *hand) {
|
||||
m := matches(hand.cards)
|
||||
log.Debug(m)
|
||||
|
||||
switch hand.kind() {
|
||||
case fiveofakind:
|
||||
break
|
||||
case fourofakind:
|
||||
hand._kind = fiveofakind
|
||||
case fullhouse:
|
||||
if m['J'] == 2 || m['J'] == 3 {
|
||||
hand._kind = fiveofakind
|
||||
}
|
||||
log.Debug(hand.cards, " was promoted to ", hand.kind())
|
||||
case threeofakind:
|
||||
if m['J'] == 1 || m['J'] == 3 {
|
||||
hand._kind = fourofakind
|
||||
}
|
||||
log.Debug(hand.cards, " was promoted to ", hand.kind())
|
||||
case twopair:
|
||||
if m['J'] == 1 {
|
||||
hand._kind = fullhouse
|
||||
} else if m['J'] == 2 {
|
||||
hand._kind = fourofakind
|
||||
}
|
||||
log.Debug(hand.cards, " was promoted to ", hand.kind())
|
||||
case onepair:
|
||||
if m['J'] == 1 || m['J'] == 2 {
|
||||
hand._kind = threeofakind
|
||||
}
|
||||
log.Debug(hand.cards, " was promoted to ", hand.kind())
|
||||
case highcard:
|
||||
hand._kind = onepair
|
||||
log.Debug(hand.cards, " was promoted to ", hand.kind())
|
||||
}
|
||||
}
|
||||
|
||||
// two coreturns the sum of products of hand values by bids
|
||||
// it uses new joker rules
|
||||
func two(lines []string) (int, error) {
|
||||
for _, hand := range hands {
|
||||
if containsChar(hand.cards, "J") {
|
||||
promote(hand)
|
||||
}
|
||||
}
|
||||
|
||||
strength := []rune{'A', 'K', 'Q', 'T', '9', '8', '7', '6', '5', '4', '3', '2', 'J'}
|
||||
log.Debug("strength: ", strength)
|
||||
sortByKindAndStrength(strength)
|
||||
|
||||
sum := 0
|
||||
|
||||
var debugNames = map[int]string{
|
||||
0: "highcard",
|
||||
1: "onepair",
|
||||
2: "twopair",
|
||||
3: "threeofakind",
|
||||
4: "fullhouse",
|
||||
5: "fourofakind",
|
||||
6: "fiveofakind",
|
||||
}
|
||||
|
||||
for i, hand := range hands {
|
||||
log.Debug(hand.cards, ": ", hand.bid, " by ", i+1, " || ", debugNames[hand.kind()])
|
||||
sum += (i + 1) * hand.bid
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
103
day-7/two_test.go
Normal file
103
day-7/two_test.go
Normal file
@ -0,0 +1,103 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPromoteHighCard(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h := newHand("1234J", 0)
|
||||
promote(h)
|
||||
|
||||
t.Run("Should promote to onepair", func(t *testing.T) {
|
||||
assert.Equal(t, onepair, h.kind())
|
||||
})
|
||||
}
|
||||
|
||||
func TestPromoteOnePair(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h1 := newHand("1233J", 0)
|
||||
promote(h1)
|
||||
h2 := newHand("123JJ", 0)
|
||||
promote(h2)
|
||||
|
||||
t.Run("Should promote to threeofakind", func(t *testing.T) {
|
||||
assert.Equal(t, threeofakind, h1.kind())
|
||||
})
|
||||
|
||||
t.Run("Should promote to threeofakind", func(t *testing.T) {
|
||||
assert.Equal(t, threeofakind, h2.kind())
|
||||
})
|
||||
}
|
||||
|
||||
func TestPromoteTwoPair(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h1 := newHand("1133J", 0)
|
||||
promote(h1)
|
||||
h2 := newHand("133JJ", 0)
|
||||
promote(h2)
|
||||
|
||||
t.Run("Should promote to fullhouse", func(t *testing.T) {
|
||||
assert.Equal(t, fullhouse, h1.kind())
|
||||
})
|
||||
|
||||
t.Run("Should promote to fourofakind", func(t *testing.T) {
|
||||
assert.Equal(t, fourofakind, h2.kind())
|
||||
})
|
||||
}
|
||||
|
||||
func TestPromoteThreeOfAKind(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h1 := newHand("1333J", 0)
|
||||
promote(h1)
|
||||
h2 := newHand("13JJJ", 0)
|
||||
promote(h2)
|
||||
|
||||
t.Run("Should promote to fourofakind", func(t *testing.T) {
|
||||
assert.Equal(t, fourofakind, h1.kind())
|
||||
})
|
||||
|
||||
t.Run("Should promote to fourofakind", func(t *testing.T) {
|
||||
assert.Equal(t, fourofakind, h2.kind())
|
||||
})
|
||||
}
|
||||
|
||||
func TestPromoteFullHOuse(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h1 := newHand("J333J", 0)
|
||||
promote(h1)
|
||||
h2 := newHand("33JJJ", 0)
|
||||
promote(h2)
|
||||
|
||||
t.Run("Should promote to fiveofakind", func(t *testing.T) {
|
||||
assert.Equal(t, fiveofakind, h1.kind())
|
||||
})
|
||||
|
||||
t.Run("Should promote to fiveofakind", func(t *testing.T) {
|
||||
assert.Equal(t, fiveofakind, h2.kind())
|
||||
})
|
||||
}
|
||||
|
||||
func TestPromoteFourOfAKind(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
h1 := newHand("JJJJ3", 0)
|
||||
promote(h1)
|
||||
h2 := newHand("3333J", 0)
|
||||
promote(h2)
|
||||
|
||||
t.Run("Should promote to fiveofakind", func(t *testing.T) {
|
||||
assert.Equal(t, fiveofakind, h1.kind())
|
||||
})
|
||||
|
||||
t.Run("Should promote to fiveofakind", func(t *testing.T) {
|
||||
assert.Equal(t, fiveofakind, h2.kind())
|
||||
})
|
||||
}
|
91
day-7/util.go
Normal file
91
day-7/util.go
Normal file
@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// readlines reads lines from stdin.
|
||||
// Then it returns them as an array of strings
|
||||
func readlines() []string {
|
||||
lines := []string{}
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
lines = append(lines, scanner.Text())
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func parselines(lines []string) {
|
||||
for _, line := range lines {
|
||||
splitted := strings.Split(line, " ")
|
||||
n, _ := strconv.Atoi(splitted[1])
|
||||
hands = append(hands, newHand(splitted[0], n))
|
||||
}
|
||||
}
|
||||
|
||||
// matches checks for number of matching cards in hand
|
||||
func matches(cards string) map[rune]int {
|
||||
m := map[rune]int{}
|
||||
for _, r := range cards {
|
||||
_, ok := m[r]
|
||||
if !ok {
|
||||
m[r] = strings.Count(cards, string(r))
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// contains returns true if a slice of elements contains a given element
|
||||
func contains[T comparable](elems []T, v T) bool {
|
||||
for _, s := range elems {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// indexOf returns the index of an element in a slice
|
||||
func indexOf(collection []rune, elem rune) int {
|
||||
for i, x := range collection {
|
||||
if x == elem {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func containsChar(s string, c string) bool {
|
||||
for _, v := range s {
|
||||
if c == string(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func sortByKindAndStrength(strength []rune) {
|
||||
sort.Slice(hands, func(i, j int) bool {
|
||||
if hands[i].kind() == hands[j].kind() {
|
||||
for k := range hands[i].cards {
|
||||
if hands[i].cards[k] == hands[j].cards[k] {
|
||||
continue
|
||||
}
|
||||
return indexOf(strength, rune(hands[i].cards[k])) > indexOf(strength, rune(hands[j].cards[k]))
|
||||
}
|
||||
}
|
||||
return hands[i].kind() < hands[j].kind()
|
||||
})
|
||||
}
|
22
day-7/util_test.go
Normal file
22
day-7/util_test.go
Normal file
@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMatches(t *testing.T) {
|
||||
//t.Skip("skipping test")
|
||||
|
||||
m1 := matches("32T3K")
|
||||
m2 := matches("JJU8J")
|
||||
|
||||
t.Run("Should produce equal maps", func(t *testing.T) {
|
||||
assert.Equal(t, map[rune]int{'3': 2, '2': 1, 'T': 1, 'K': 1}, m1)
|
||||
})
|
||||
|
||||
t.Run("Should produce equal maps", func(t *testing.T) {
|
||||
assert.Equal(t, map[rune]int{'J': 3, 'U': 1, '8': 1}, m2)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user