108 lines
2.0 KiB
Go
108 lines
2.0 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"math"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
inp := h.StdinToString()
|
||
|
mc := part1(parseStones(inp))
|
||
|
part2(parseStones(inp), mc)
|
||
|
}
|
||
|
|
||
|
func part1(inp []int) *MemoCache {
|
||
|
mc := NewMemo()
|
||
|
fmt.Println("# Part 1")
|
||
|
fmt.Println("Stones After 25 Blinks:", mc.StonesAfter(inp, 25))
|
||
|
return mc
|
||
|
}
|
||
|
|
||
|
func part2(inp []int, mc *MemoCache) {
|
||
|
fmt.Println("# Part 2")
|
||
|
fmt.Println("Stones After 75 Blinks:", mc.StonesAfter(inp, 75))
|
||
|
}
|
||
|
|
||
|
func blink(stone int) []int {
|
||
|
var ret []int
|
||
|
if stone == 0 {
|
||
|
ret = append(ret, 1)
|
||
|
} else if evenDigits(stone) {
|
||
|
ret = append(ret, split(stone)...)
|
||
|
} else {
|
||
|
ret = append(ret, stone*2024)
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
// Attempt 1, I tracked the actual stones, but that's too much.
|
||
|
// Attempt 2, Switch from tracking the actual stones to just the length
|
||
|
type MemoKey struct {
|
||
|
stone int
|
||
|
blinks int
|
||
|
}
|
||
|
type MemoCache struct {
|
||
|
cache map[MemoKey]int
|
||
|
}
|
||
|
|
||
|
func NewMemo() *MemoCache {
|
||
|
return &MemoCache{cache: make(map[MemoKey]int)}
|
||
|
}
|
||
|
|
||
|
func (mc *MemoCache) StonesAfter(stones []int, b int) int {
|
||
|
var res int
|
||
|
for _, s := range stones {
|
||
|
res += mc.SizeAfterBlinks(s, b)
|
||
|
}
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
func (mc *MemoCache) SizeAfterBlinks(s, b int) int {
|
||
|
key := MemoKey{stone: s, blinks: b}
|
||
|
if v, ok := mc.cache[key]; ok {
|
||
|
return v
|
||
|
}
|
||
|
res := blink(s)
|
||
|
if b == 1 {
|
||
|
mc.cache[key] = len(res)
|
||
|
return len(res)
|
||
|
}
|
||
|
n := mc.SizeAfterBlinks(res[0], b-1)
|
||
|
if len(res) == 2 {
|
||
|
n += mc.SizeAfterBlinks(res[1], b-1)
|
||
|
}
|
||
|
mc.cache[key] = n
|
||
|
return n
|
||
|
}
|
||
|
|
||
|
func countDigits(i int) int {
|
||
|
if i == 0 {
|
||
|
return 0
|
||
|
}
|
||
|
return int(math.Floor(math.Log10(float64(i))) + 1)
|
||
|
}
|
||
|
|
||
|
func evenDigits(i int) bool {
|
||
|
return countDigits(i)%2 == 0
|
||
|
}
|
||
|
|
||
|
func split(i int) []int {
|
||
|
cnt := countDigits(i)
|
||
|
splitAt := cnt / 2
|
||
|
return []int{int(float64(i) / math.Pow10(splitAt)), i % int(math.Pow10(splitAt))}
|
||
|
}
|
||
|
|
||
|
func parseStones(inp string) []int {
|
||
|
var ret []int
|
||
|
pts := strings.Split(inp, " ")
|
||
|
for i := range pts {
|
||
|
w, _ := strconv.Atoi(pts[i])
|
||
|
ret = append(ret, w)
|
||
|
}
|
||
|
return ret
|
||
|
}
|