chore: cleanup day 7 #10

Merged
xuu merged 3 commits from day07-enhance into main 2023-12-26 13:18:08 -07:00
4 changed files with 480 additions and 118 deletions
Showing only changes of commit f1ac3ea35f - Show all commits

View File

@ -3,30 +3,20 @@ package main
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"os"
"sort" "sort"
aoc "go.sour.is/advent-of-code"
) )
func main() { func main() { aoc.MustResult(aoc.Runner(run)) }
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "Usage: day07 FILE")
}
input, err := os.Open(os.Args[1]) type result struct {
if err != nil { valuePT1 uint64
fmt.Fprintln(os.Stderr, err) valuePT2 uint64
}
scan := bufio.NewScanner(input)
score1, score2 := run(scan)
fmt.Println("score 1", score1)
fmt.Println("score 2", score2)
} }
func run(scan *bufio.Scanner) (uint64, uint64) { func run(scan *bufio.Scanner) (result, error) {
var game1, game2 Game var game Game
for scan.Scan() { for scan.Scan() {
var cards string var cards string
@ -37,20 +27,17 @@ func run(scan *bufio.Scanner) (uint64, uint64) {
} }
fmt.Println("cards", cards, "bid", bid) fmt.Println("cards", cards, "bid", bid)
game1.Append(cards, bid) game.plays = append(game.plays, Play{bid, []rune(cards), &game})
game2.Append(cards, bid)
} }
game1.cardTypes = cardTypes1 game.cardOrder = getOrder(cardTypes1)
game1.cardOrder = getOrder(cardTypes1) product1 := calcProduct(game)
product1 := calcProduct(game1)
game2.cardTypes = cardTypes2 game.cardOrder = getOrder(cardTypes2)
game2.cardOrder = getOrder(cardTypes2) game.wildCard = 'J'
game2.wildCard = 'J' product2 := calcProduct(game)
product2 := calcProduct(game2)
return product1, product2 return result{product1, product2}, nil
} }
var cardTypes1 = []rune{'A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2'} var cardTypes1 = []rune{'A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2'}
@ -79,108 +66,92 @@ func getOrder(cardTypes []rune) map[rune]int {
type Game struct { type Game struct {
plays Plays plays Plays
cardOrder map[rune]int cardOrder map[rune]int
cardTypes []rune
wildCard rune wildCard rune
} }
func (g *Game) Append(cards string, bid int) {
p := Play{bid: bid, hand: []rune(cards), game: g}
g.plays = append(g.plays, p)
}
type Play struct { type Play struct {
bid int bid int
hand Hand hand Hand
cardCounts map[rune]int
strength int
game *Game game *Game
} }
type Hand []rune type Hand []rune
func (h *Play) HandType() string { func (h Play) HandType() string {
hs := h.HandStrength() hc, _ := h.HighCard()
kind := hs& 0xf00000 switch {
hc := h.game.cardTypes[13-hs&0xf0000>>16] case h.IsFiveOfKind():
switch kind {
case 0x700000:
return "5K-" + string(hc) return "5K-" + string(hc)
case 0x600000: case h.IsFourOfKind():
return "4K-" + string(hc) return "4K-" + string(hc)
case 0x500000: case h.IsFullHouse():
return "FH-" + string(hc) return "FH-" + string(hc)
case 0x400000: case h.IsThreeOfKind():
return "3K-" + string(hc) return "3K-" + string(hc)
case 0x300000: case h.IsTwoPair():
return "2P-" + string(hc) return "2P-" + string(hc)
case 0x200000: case h.IsOnePair():
return "1P-" + string(hc) return "1P-" + string(hc)
case 0x100000: case h.IsHighCard():
return "HC-" + string(hc) return "HC-" + string(hc)
} }
return "Uno" return "Uno"
} }
func (p *Play) HandStrength() int { func (h Play) HandStrength() int {
_, v := p.HighCard() _, v := h.HighCard()
switch { switch {
case p.IsFiveOfKind(): case h.IsFiveOfKind():
p.strength = 0x700000 | v return 0x700000 | v
case p.IsFourOfKind(): case h.IsFourOfKind():
p.strength = 0x600000 | v return 0x600000 | v
case p.IsFullHouse(): case h.IsFullHouse():
p.strength = 0x500000 | v return 0x500000 | v
case p.IsThreeOfKind(): case h.IsThreeOfKind():
p.strength = 0x400000 | v return 0x400000 | v
case p.IsTwoPair(): case h.IsTwoPair():
p.strength = 0x300000 | v return 0x300000 | v
case p.IsOnePair(): case h.IsOnePair():
p.strength = 0x200000 | v return 0x200000 | v
case p.IsHighCard(): case h.IsHighCard():
p.strength = 0x100000 | v return 0x100000 | v
} }
return p.strength return 0
} }
func (h Play) IsFiveOfKind() bool { func (h Play) IsFiveOfKind() bool {
_, _, _, _, has5 := h.game.hasSame(h.cardCounts) _, _, _, _, has5 := h.game.hasSame(h.hand)
return has5 return has5
} }
func (h Play) IsFourOfKind() bool { func (h Play) IsFourOfKind() bool {
_, _, _, has4, _ := h.game.hasSame(h.cardCounts) _, _, _, has4, _ := h.game.hasSame(h.hand)
return has4 return has4
} }
func (h Play) IsFullHouse() bool { func (h Play) IsFullHouse() bool {
_, has2, has3, _, _ := h.game.hasSame(h.cardCounts) _, has2, has3, _, _ := h.game.hasSame(h.hand)
return has3 && has2 return has3 && has2
} }
func (h Play) IsThreeOfKind() bool { func (h Play) IsThreeOfKind() bool {
has1, _, has3, _, _ := h.game.hasSame(h.cardCounts) has1, _, has3, _, _ := h.game.hasSame(h.hand)
return has3 && has1 return has3 && has1
} }
func (h Play) IsTwoPair() bool { func (h Play) IsTwoPair() bool {
_, has2, has3, _, _ := h.game.hasSame(h.cardCounts) _, has2, has3, _, _ := h.game.hasSame(h.hand)
return !has3 && has2 && h.game.pairs(h.cardCounts) == 2 return !has3 && has2 && h.game.pairs(h.hand) == 2
} }
func (h Play) IsOnePair() bool { func (h Play) IsOnePair() bool {
_, has2, has3, _, _ := h.game.hasSame(h.cardCounts) _, has2, has3, _, _ := h.game.hasSame(h.hand)
return !has3 && has2 && h.game.pairs(h.cardCounts) == 1 return !has3 && has2 && h.game.pairs(h.hand) == 1
} }
func (h Play) IsHighCard() bool { func (h Play) IsHighCard() bool {
has1, has2, has3, has4, _ := h.game.hasSame(h.cardCounts) has1, has2, has3, has4, _ := h.game.hasSame(h.hand)
return has1 && !has2 && !has3 && !has4 return has1 && !has2 && !has3 && !has4
} }
func (h *Play) HighCard() (rune, int) { func (h Play) HighCard() (rune, int) {
if h.cardCounts == nil {
h.generateCounts()
}
var i int var i int
pairs := make(Pairs, 5) pairs := make(Pairs, 5)
for r, c := range h.cardCounts { cnt := h.game.Counts(h.hand)
for r, c := range cnt {
pairs[i].c = c pairs[i].c = c
pairs[i].r = r pairs[i].r = r
pairs[i].o = h.game.cardOrder[r] pairs[i].o = h.game.cardOrder[r]
@ -223,32 +194,29 @@ 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) 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] } func (p Plays) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p *Play) generateCounts() { func (g *Game) Counts(cards []rune) map[rune]int {
cardOrder := p.game.cardOrder m := make(map[rune]int, len(g.cardOrder))
wildCard := p.game.wildCard for _, c := range cards {
m[c]++
p.cardCounts = make(map[rune]int, len(cardOrder))
for _, c := range p.hand {
p.cardCounts[c]++
} }
if g.wildCard != 0 && m[g.wildCard] > 0 {
if wildCard != 0 && p.cardCounts[wildCard] > 0 {
var maxK rune var maxK rune
var maxV int var maxV int
for k, v := range p.cardCounts { for k, v := range m {
if k != wildCard && v > maxV { if k != g.wildCard && v > maxV {
maxK, maxV = k, v maxK, maxV = k, v
} }
} }
if maxK != 0 { if maxK != 0 {
p.cardCounts[maxK] += p.cardCounts[wildCard] m[maxK] += m[g.wildCard]
delete(p.cardCounts, wildCard) delete(m, g.wildCard)
} }
} }
return m
} }
func (g *Game) hasSame(counts map[rune]int) (has1, has2, has3, has4, has5 bool) { func (g *Game) hasSame(cards []rune) (has1, has2, has3, has4, has5 bool) {
for _, c := range counts { cnt := g.Counts(cards)
for _, c := range cnt {
switch c { switch c {
case 1: case 1:
has1 = true has1 = true
@ -268,9 +236,9 @@ func (g *Game) hasSame(counts map[rune]int) (has1, has2, has3, has4, has5 bool)
} }
return return
} }
func (g *Game) pairs(counts map[rune]int) int { func (g *Game) pairs(cards []rune) int {
pairs := 0 pairs := 0
for _, n := range counts { for _, n := range g.Counts(cards) {
if n == 2 { if n == 2 {
pairs++ pairs++
} }

View File

@ -26,21 +26,18 @@ func TestHands(t *testing.T) {
var game Game var game Game
game.cardOrder = getOrder(cardTypes1) game.cardOrder = getOrder(cardTypes1)
h := Play{hand: []rune("AAA23"), game: &game} h := Play{0, []rune("AAA23"), &game}
// h.generateCounts()
is.Equal(h.HandType(), "3K-A") is.Equal(h.HandType(), "3K-A")
h = Play{hand: []rune("JJJJJ"), game:&game} h = Play{0, []rune("JJJJJ"), &game}
h.generateCounts()
is.Equal(h.HandType(), "5K-J") is.Equal(h.HandType(), "5K-J")
is.Equal(fmt.Sprintf("%x", h.HandStrength()), "7aaaaa") is.Equal(fmt.Sprintf("%x", h.HandStrength()), "7aaaaa")
h = Play{hand: []rune("KKKKJ"), game: &game} h = Play{0, []rune("KKKKJ"), &game}
is.Equal(h.HandType(), "4K-K") is.Equal(h.HandType(), "4K-K")
is.Equal(fmt.Sprintf("%x", h.HandStrength()), "6cccca") is.Equal(fmt.Sprintf("%x", h.HandStrength()), "6cccca")
h = Play{hand: []rune("QQQJA"), game: &game} h = Play{0, []rune("QQQJA"), &game}
is.Equal(h.HandType(), "3K-Q") is.Equal(h.HandType(), "3K-Q")
is.Equal(fmt.Sprintf("%x", h.HandStrength()), "4bbbad") is.Equal(fmt.Sprintf("%x", h.HandStrength()), "4bbbad")
} }
@ -57,19 +54,21 @@ func TestExample(t *testing.T) {
is := is.New(t) is := is.New(t)
scan := bufio.NewScanner(bytes.NewReader(example)) scan := bufio.NewScanner(bytes.NewReader(example))
score1, score2 := run(scan) r, err := run(scan)
is.Equal(score1, uint64(6440)) is.NoErr(err)
is.Equal(score2, uint64(5905)) is.Equal(r.valuePT1, uint64(6440))
is.Equal(r.valuePT2, uint64(5905))
} }
func TestSolution(t *testing.T) { func TestSolution(t *testing.T) {
is := is.New(t) is := is.New(t)
scan := bufio.NewScanner(bytes.NewReader(input)) scan := bufio.NewScanner(bytes.NewReader(input))
score1, score2 := run(scan) r, err := run(scan)
t.Log("score1", score1) is.NoErr(err)
is.Equal(score1, uint64(248559379)) t.Log("score1", r.valuePT1)
is.Equal(r.valuePT1, uint64(248559379))
t.Log("score2", score2) t.Log("score2", r.valuePT2)
is.Equal(score2, uint64(249631254)) is.Equal(r.valuePT2, uint64(249631254))
} }

2
go.mod
View File

@ -1,4 +1,4 @@
module go.sour.is/advent-of-code-2023 module go.sour.is/advent-of-code
go 1.21.3 go 1.21.3

395
tools.go Normal file
View File

@ -0,0 +1,395 @@
package aoc
import (
"bufio"
"cmp"
"fmt"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
)
func Runner[R any, F func(*bufio.Scanner) (R, error)](run F) (R, error) {
if len(os.Args) != 2 {
Log("Usage:", filepath.Base(os.Args[0]), "FILE")
os.Exit(22)
}
input, err := os.Open(os.Args[1])
if err != nil {
Log(err)
os.Exit(1)
}
scan := bufio.NewScanner(input)
return run(scan)
}
func MustResult[T any](result T, err error) {
if err != nil {
fmt.Println("ERR", err)
os.Exit(1)
}
Log("result", result)
}
func Log(v ...any) { fmt.Fprintln(os.Stderr, v...) }
func Logf(format string, v ...any) {
if !strings.HasSuffix(format, "\n") {
format += "\n"
}
fmt.Fprintf(os.Stderr, format, v...)
}
func Reverse[T any](arr []T) []T {
for i := 0; i < len(arr)/2; i++ {
arr[i], arr[len(arr)-i-1] = arr[len(arr)-i-1], arr[i]
}
return arr
}
type uinteger interface {
uint | uint8 | uint16 | uint32 | uint64
}
type sinteger interface {
int | int8 | int16 | int32 | int64
}
type integer interface {
sinteger | uinteger
}
// type float interface {
// complex64 | complex128 | float32 | float64
// }
// type number interface{ integer | float }
// greatest common divisor (GCD) via Euclidean algorithm
func GCD[T integer](a, b T) T {
for b != 0 {
t := b
b = a % b
a = t
}
return a
}
// find Least Common Multiple (LCM) via GCD
func LCM[T integer](integers ...T) T {
if len(integers) == 0 {
return 0
}
if len(integers) == 1 {
return integers[0]
}
a, b := integers[0], integers[1]
result := a * b / GCD(a, b)
for _, c := range integers[2:] {
result = LCM(result, c)
}
return result
}
func ReadStringToInts(fields []string) []int {
return SliceMap(Atoi, fields...)
}
type Node[T any] struct {
value T
pos int
left *Node[T]
}
func (n *Node[T]) add(a *Node[T]) *Node[T] {
if a == nil {
return n
}
if n == nil {
return a
}
n.left = a
return a
}
func (n *Node[T]) Value() (value T, ok bool) {
if n == nil {
return
}
return n.value, true
}
func (n *Node[T]) Position() int {
if n == nil {
return -1
}
return n.pos
}
func (n *Node[T]) SetPosition(i int) {
if n == nil {
return
}
n.pos = i
}
func (n *Node[T]) Next() *Node[T] {
if n == nil {
return nil
}
return n.left
}
func (n *Node[T]) String() string {
if n == nil {
return "EOL"
}
return fmt.Sprintf("node %v", n.value)
}
type List[T any] struct {
head *Node[T]
n *Node[T]
p map[int]*Node[T]
}
func NewList[T any](a *Node[T]) *List[T] {
lis := &List[T]{
head: a,
n: a,
p: make(map[int]*Node[T]),
}
lis.add(a)
return lis
}
func (l *List[T]) Add(value T, pos int) {
a := &Node[T]{value: value, pos: pos}
l.add(a)
}
func (l *List[T]) add(a *Node[T]) {
if l.head == nil {
l.head = a
}
if a == nil {
return
}
l.n = l.n.add(a)
l.p[a.pos] = a
}
func (l *List[T]) Get(pos int) *Node[T] {
return l.p[pos]
}
func (l *List[T]) GetN(pos ...int) []*Node[T] {
lis := make([]*Node[T], len(pos))
for i, p := range pos {
lis[i] = l.p[p]
}
return lis
}
func (l *List[T]) Head() *Node[T] {
return l.head
}
func SliceMap[T, U any](fn func(T) U, in ...T) []U {
lis := make([]U, len(in))
for i := range lis {
lis[i] = fn(in[i])
}
return lis
}
func SliceIMap[T, U any](fn func(int, T) U, in ...T) []U {
lis := make([]U, len(in))
for i := range lis {
lis[i] = fn(i, in[i])
}
return lis
}
func Atoi(s string) int {
i, _ := strconv.Atoi(s)
return i
}
func Repeat[T any](s T, i int) []T {
lis := make([]T, i)
for i := range lis {
lis[i] = s
}
return lis
}
func Sum[T integer](arr ...T) T {
var acc T
for _, a := range arr {
acc += a
}
return acc
}
func SumFunc[T any, U integer](fn func(T) U, input ...T) U {
return Sum(SliceMap(fn, input...)...)
}
func SumIFunc[T any, U integer](fn func(int, T) U, input ...T) U {
return Sum(SliceIMap(fn, input...)...)
}
func Power2(n int) int {
if n == 0 {
return 1
}
p := 2
for ; n > 1; n-- {
p *= 2
}
return p
}
func ABS(i int) int {
if i < 0 {
return -i
}
return i
}
func Transpose[T any](matrix [][]T) [][]T {
rows, cols := len(matrix), len(matrix[0])
m := make([][]T, cols)
for i := range m {
m[i] = make([]T, rows)
}
for i := 0; i < cols; i++ {
for j := 0; j < rows; j++ {
m[i][j] = matrix[j][i]
}
}
return m
}
func Reduce[T, U any](fn func(int, T, U) U, u U, list ...T) U {
for i, t := range list {
u = fn(i, t, u)
}
return u
}
func Max[T cmp.Ordered](a T, v ...T) T {
for _, b := range v {
if b > a {
a = b
}
}
return a
}
func Min[T cmp.Ordered](a T, v ...T) T {
for _, b := range v {
if b < a {
a = b
}
}
return a
}
type PQElem[T any, I integer] struct {
Value T
Priority I
}
type PQList[T any, I integer] []PQElem[T, I]
func (pq PQList[T, I]) Len() int {
return len(pq)
}
func (pq PQList[T, I]) Less(i int, j int) bool {
return pq[i].Priority < pq[j].Priority
}
func (pq PQList[T, I]) Swap(i int, j int) {
pq[i], pq[j] = pq[j], pq[i]
}
var _ sort.Interface = (*PQList[rune, int])(nil)
type PriorityQueue[T any, I integer] struct {
elem PQList[T, I]
}
func (pq *PriorityQueue[T, I]) Enqueue(elem T, priority I) {
pq.elem = append(pq.elem, PQElem[T, I]{elem, priority})
sort.Sort(pq.elem)
}
func (pq *PriorityQueue[T, I]) IsEmpty() bool {
return len(pq.elem) == 0
}
func (pq *PriorityQueue[T, I]) Dequeue() (T, bool) {
var elem T
if pq.IsEmpty() {
return elem, false
}
elem, pq.elem = pq.elem[0].Value, pq.elem[1:]
return elem, true
}
type Vertex[V comparable, I integer] struct {
to V
score I
}
type graph[V comparable, I uinteger] struct {
adj map[V][]Vertex[V, I]
}
func Graph[V comparable, I uinteger](size int) *graph[V, I] {
return &graph[V, I]{
adj: make(map[V][]Vertex[V, I], size),
}
}
func (g *graph[V, I]) AddEdge(u, v V, w I) {
g.adj[u] = append(g.adj[u], Vertex[V, I]{to: v, score: w})
g.adj[v] = append(g.adj[v], Vertex[V, I]{to: u, score: w})
}
func (g *graph[V, I]) Dijkstra(m interface{Get()}, src V) map[V]I {
pq := PriorityQueue[V, I]{}
dist := make(map[V]I, len(g.adj))
visited := make(map[V]bool, len(g.adj))
var INF I
INF = ^INF
pq.Enqueue(src, 0)
dist[src] = 0
for !pq.IsEmpty() {
u, _ := pq.Dequeue()
if _, ok := visited[u]; ok {
continue
}
visited[u] = true
for _, v := range g.adj[u] {
_, ok := visited[v.to]
var du, dv I
if d, inf := dist[u]; !inf {
du = INF
} else {
du = d
}
if d, inf := dist[v.to]; !inf {
dv = INF
} else {
dv = d
}
if !ok && du+v.score < dv {
dist[v.to] = du + v.score
pq.Enqueue(v.to, du+v.score)
}
}
}
return dist
}