Skip to content

Commit ed4d977

Browse files
committed
2023 day07 optimised
1 parent ba547e5 commit ed4d977

File tree

5 files changed

+165
-132
lines changed

5 files changed

+165
-132
lines changed

2023/day07/.bench

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"Lines":[{"Name":"Part 1","N":87,"NsPerOp":12566992,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0},{"Name":"Part 2","N":78,"NsPerOp":15769298,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0}],"Measured":1}
1+
{"Lines":[{"Name":"Part 1","N":708,"NsPerOp":1608818,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0},{"Name":"Part 2","N":655,"NsPerOp":1835345,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0}],"Measured":1}

2023/day07/main.go

Lines changed: 100 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package main
22

33
import (
44
_ "embed"
5-
"fmt"
65
"os"
6+
"slices"
77
"strings"
88
)
99

@@ -13,56 +13,121 @@ var input string
1313
//go:embed input_test.txt
1414
var inputTest string
1515

16+
type Hand struct {
17+
cards [5]int
18+
bid int
19+
}
20+
21+
var hands []Hand
22+
1623
func main() {
1724
// Check argv if we use test input or not
1825
if len(os.Args) > 1 && os.Args[1] == "test" {
1926
input = inputTest
2027
}
2128

22-
answer := doPartOne(input)
29+
parseInput(input)
30+
31+
answer := doPartOne()
2332
println(answer)
2433

25-
answer = doPartTwo(input)
34+
answer = doPartTwo()
2635
println(answer)
2736
}
2837

29-
type Hands []Hand
30-
type Hand struct {
31-
hand []int
32-
bid int
33-
}
34-
type Score struct {
35-
hand []int
36-
strength int
38+
func parseInput(input string) {
39+
lines := strings.Split(strings.TrimSpace(input), "\n")
40+
hands = make([]Hand, len(lines))
41+
42+
for i, line := range lines {
43+
spaceIdx := strings.Index(line, " ")
44+
45+
// Parse hand
46+
var h Hand
47+
for j := 0; j < 5; j++ {
48+
h.cards[j] = cardValue(line[j])
49+
}
50+
51+
// Parse bid
52+
bid := 0
53+
for j := spaceIdx + 1; j < len(line); j++ {
54+
bid = bid*10 + int(line[j]-'0')
55+
}
56+
h.bid = bid
57+
58+
hands[i] = h
59+
}
3760
}
3861

39-
var (
40-
handscores = []Score{
41-
{[]int{5}, 10}, {[]int{1, 4}, 9}, {[]int{2, 3}, 8}, {[]int{1, 1, 3}, 7},
42-
{[]int{1, 2, 2}, 6}, {[]int{1, 1, 1, 2}, 5}, {[]int{1, 1, 1, 1, 1}, 4},
62+
func cardValue(c byte) int {
63+
switch c {
64+
case 'A':
65+
return 14
66+
case 'K':
67+
return 13
68+
case 'Q':
69+
return 12
70+
case 'J':
71+
return 11
72+
case 'T':
73+
return 10
74+
default:
75+
return int(c - '0')
4376
}
77+
}
4478

45-
cardscore = map[rune]int{
46-
'1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7,
47-
'8': 8, '9': 9, 'T': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 14,
79+
// Calculate hand strength from card counts
80+
// Five of a kind: 6, Four of a kind: 5, Full house: 4, Three of a kind: 3, Two pair: 2, One pair: 1, High card: 0
81+
func handStrength(counts [5]int) int {
82+
slices.Sort(counts[:])
83+
// counts is now sorted, highest count at the end
84+
switch counts[4] {
85+
case 5:
86+
return 6 // Five of a kind
87+
case 4:
88+
return 5 // Four of a kind
89+
case 3:
90+
if counts[3] == 2 {
91+
return 4 // Full house
92+
}
93+
return 3 // Three of a kind
94+
case 2:
95+
if counts[3] == 2 {
96+
return 2 // Two pair
97+
}
98+
return 1 // One pair
99+
default:
100+
return 0 // High card
48101
}
49-
)
102+
}
50103

51-
func getHands(input string, p2 bool) Hands {
52-
var hands Hands
53-
for _, line := range strings.Split(strings.TrimSpace(input), "\n") {
54-
var H string
55-
var B int
56-
fmt.Sscanf(line, "%s %d", &H, &B)
57-
hand := []int{}
58-
for _, c := range H {
59-
n := cardscore[c]
60-
if c == 'J' && p2 {
61-
n = 1
62-
}
63-
hand = append(hand, n)
64-
}
65-
hands = append(hands, Hand{hand, B})
104+
func getHandStrength(h Hand, jokerRule bool) int {
105+
counts := [5]int{}
106+
cardCounts := make(map[int]int)
107+
108+
for _, card := range h.cards {
109+
cardCounts[card]++
110+
}
111+
112+
// Handle jokers (value 11 in part 1, converted to 1 in part 2)
113+
jokers := 0
114+
if jokerRule {
115+
jokers = cardCounts[1]
116+
delete(cardCounts, 1)
117+
}
118+
119+
// Fill counts array
120+
i := 0
121+
for _, count := range cardCounts {
122+
counts[i] = count
123+
i++
124+
}
125+
126+
// Add jokers to the highest count
127+
if jokers > 0 {
128+
slices.Sort(counts[:])
129+
counts[4] += jokers
66130
}
67-
return hands
131+
132+
return handStrength(counts)
68133
}

2023/day07/main_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@ package main
33
import "testing"
44

55
func BenchmarkPartOne(b *testing.B) {
6+
parseInput(input)
7+
b.ResetTimer()
8+
69
for n := 0; n < b.N; n++ {
7-
doPartOne(input)
10+
doPartOne()
811
}
912
}
1013

1114
func BenchmarkPartTwo(b *testing.B) {
15+
parseInput(input)
16+
b.ResetTimer()
17+
1218
for n := 0; n < b.N; n++ {
13-
doPartTwo(input)
19+
doPartTwo()
1420
}
1521
}

2023/day07/part1.go

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,33 @@
11
package main
22

3-
import (
4-
"reflect"
5-
"slices"
6-
)
3+
import "slices"
74

8-
func doPartOne(input string) int {
9-
hands := getHands(input, false)
10-
Counter := func(H Hand) int {
11-
C := map[int]int{}
12-
for _, c := range H.hand {
13-
C[c]++
5+
func doPartOne() int {
6+
// Create a copy for sorting
7+
sortedHands := make([]Hand, len(hands))
8+
copy(sortedHands, hands)
9+
10+
slices.SortFunc(sortedHands, func(a, b Hand) int {
11+
// Compare hand strengths
12+
sa := getHandStrength(a, false)
13+
sb := getHandStrength(b, false)
14+
15+
if sa != sb {
16+
return sa - sb
1417
}
15-
res := []int{}
16-
for _, v := range C {
17-
res = append(res, v)
18-
}
19-
slices.Sort(res)
20-
for _, S := range handscores {
21-
if reflect.DeepEqual(res, S.hand) {
22-
return S.strength
23-
}
24-
}
25-
return 0
26-
}
27-
slices.SortFunc(hands, func(a, b Hand) int {
28-
Ca := Counter(a)
29-
Cb := Counter(b)
30-
if Ca < Cb {
31-
return -1
32-
}
33-
if Ca > Cb {
34-
return 1
35-
}
36-
// If we get here, they have the same score, and so need to iterate through the cards
37-
for i := 0; i < len(a.hand); i++ {
38-
if a.hand[i] < b.hand[i] {
39-
return -1
40-
}
41-
if a.hand[i] > b.hand[i] {
42-
return 1
18+
19+
// Same strength, compare cards in order
20+
for i := 0; i < 5; i++ {
21+
if a.cards[i] != b.cards[i] {
22+
return a.cards[i] - b.cards[i]
4323
}
4424
}
4525
return 0
4626
})
47-
res := 0
48-
for i, H := range hands {
49-
res += (i + 1) * H.bid
27+
28+
result := 0
29+
for i, h := range sortedHands {
30+
result += (i + 1) * h.bid
5031
}
51-
return res
32+
return result
5233
}

2023/day07/part2.go

Lines changed: 33 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,45 @@
11
package main
22

3-
import (
4-
"reflect"
5-
"slices"
6-
)
3+
import "slices"
74

8-
func doPartTwo(input string) int {
9-
hands := getHands(input, true)
10-
Counter := func(H Hand) int {
11-
C := map[int]int{}
12-
for _, c := range H.hand {
13-
C[c]++
14-
}
15-
T := reflect.ValueOf(C).MapKeys()[0].Interface()
16-
for k := range C {
17-
if k != 1 {
18-
if C[k] > C[T.(int)] || T == 1 {
19-
T = k
20-
}
21-
}
22-
}
23-
if _, ok := C[1]; ok && T.(int) != 1 {
24-
C[T.(int)] += C[1]
25-
delete(C, 1)
26-
}
27-
res := []int{}
28-
for _, v := range C {
29-
res = append(res, v)
30-
}
31-
slices.Sort(res)
32-
for _, S := range handscores {
33-
if reflect.DeepEqual(res, S.hand) {
34-
return S.strength
5+
func doPartTwo() int {
6+
// Create a copy for sorting with joker rules
7+
sortedHands := make([]Hand, len(hands))
8+
for i, h := range hands {
9+
// Convert J (11) to joker (1)
10+
var newHand Hand
11+
newHand.bid = h.bid
12+
for j := 0; j < 5; j++ {
13+
if h.cards[j] == 11 {
14+
newHand.cards[j] = 1
15+
} else {
16+
newHand.cards[j] = h.cards[j]
3517
}
3618
}
37-
return 0
19+
sortedHands[i] = newHand
3820
}
39-
slices.SortFunc(hands, func(a, b Hand) int {
40-
Ca := Counter(a)
41-
Cb := Counter(b)
42-
if Ca < Cb {
43-
return -1
44-
}
45-
if Ca > Cb {
46-
return 1
47-
}
48-
// If we get here, they have the same score, and so need to iterate through the cards
49-
for i := 0; i < len(a.hand); i++ {
50-
if a.hand[i] < b.hand[i] {
51-
return -1
52-
}
53-
if a.hand[i] > b.hand[i] {
54-
return 1
21+
22+
slices.SortFunc(sortedHands, func(a, b Hand) int {
23+
// Compare hand strengths with joker rule
24+
sa := getHandStrength(a, true)
25+
sb := getHandStrength(b, true)
26+
27+
if sa != sb {
28+
return sa - sb
29+
}
30+
31+
// Same strength, compare cards in order
32+
for i := 0; i < 5; i++ {
33+
if a.cards[i] != b.cards[i] {
34+
return a.cards[i] - b.cards[i]
5535
}
5636
}
5737
return 0
5838
})
59-
res := 0
60-
for i, H := range hands {
61-
res += (i + 1) * H.bid
39+
40+
result := 0
41+
for i, h := range sortedHands {
42+
result += (i + 1) * h.bid
6243
}
63-
return res
44+
return result
6445
}

0 commit comments

Comments
 (0)