diff --git a/day11/fixedNoTree.go b/day11/fixedNoTree.go new file mode 100644 index 0000000..d481502 --- /dev/null +++ b/day11/fixedNoTree.go @@ -0,0 +1,79 @@ +package main + +import ( + "fmt" + "hash/fnv" + "strconv" + "sync" + + "git.mstar.dev/mstar/goutils/other" + // "github.com/patrickmn/go-cache" +) + +// var valCache = cache.New(time.Minute, time.Minute*2) +var valCacheMap = map[uint64]uint64{} +var mLock sync.RWMutex + +func hash(val uint64, iter, maxIter int) uint64 { + h := fnv.New64a() + + _, err := h.Write([]byte(fmt.Sprintf("%d.%d.%d", val, iter, maxIter))) + if err != nil { + panic(err) + } + return h.Sum64() +} + +func fromCache(val uint64, iteration, maxIter int) (uint64, bool) { + mLock.RLock() + pv, ok := valCacheMap[hash(val, iteration, maxIter)] + mLock.RUnlock() + // v, ok := valCache.Get(strconv.FormatUint(hash(val, iteration, maxIter), 10)) + // if !ok { + // return 0, false + // } + // pv, ok := v.(uint64) + return pv, ok +} + +func intoCache(val uint64, iteration, maxIter int, result uint64) { + mLock.Lock() + valCacheMap[hash(val, iteration, maxIter)] = result + mLock.Unlock() + // valCache.Set(strconv.FormatUint(hash(val, iteration, maxIter), 10), result, -1) +} + +func Advance(val uint64, iteration int, maxIter int) (out uint64) { + cacheVal, ok := fromCache(val, iteration, maxIter) + if ok { + return cacheVal + } + defer func() { + if _, ok := fromCache(val, iteration, maxIter); !ok { + intoCache(val, iteration, maxIter, out) + } + }() + // fmt.Printf("Iteration %d with val %d\n", iteration, val) + if iteration == maxIter { + // fmt.Println("1") + out = 1 + return + } + if val == 0 { + // fmt.Println("0") + out = Advance(1, iteration+1, maxIter) + return + } + if len(strconv.FormatUint(val, 10))%2 == 0 { + // fmt.Println("Split") + str := strconv.FormatUint(val, 10) + // fmt.Println("format") + left := other.Must(strconv.ParseUint(string([]rune(str)[:len(str)/2]), 10, 64)) + right := other.Must(strconv.ParseUint(string([]rune(str)[len(str)/2:]), 10, 64)) + // fmt.Printf("l: %d, r: %d\n", left, right) + out = Advance(left, iteration+1, maxIter) + Advance(right, iteration+1, maxIter) + return + } + out = Advance(val*2024, iteration+1, maxIter) + return out +} diff --git a/day11/input b/day11/input new file mode 100644 index 0000000..873bb38 --- /dev/null +++ b/day11/input @@ -0,0 +1 @@ +28 4 3179 96938 0 6617406 490 816207 diff --git a/day11/main.go b/day11/main.go new file mode 100644 index 0000000..dbadea9 --- /dev/null +++ b/day11/main.go @@ -0,0 +1,185 @@ +package main + +import ( + "fmt" + "strconv" + "strings" + + "git.mstar.dev/mstar/aoc24/util" + "git.mstar.dev/mstar/goutils/other" + "git.mstar.dev/mstar/goutils/sliceutils" +) + +type TreeNode struct { + Left, Right *TreeNode + Value uint64 + IsLeaf bool +} + +func lineToNumbers(line string) []uint64 { + return sliceutils.Map( + strings.Split(line, " "), + func(t string) uint64 { return other.Must(strconv.ParseUint(t, 10, 64)) }, + ) +} + +func numbersIntoTree(numbers []uint64) *TreeNode { + switch len(numbers) { + case 1: + return &TreeNode{ + Left: &TreeNode{ + Left: nil, + Right: nil, + Value: numbers[0], + IsLeaf: true, + }, + Right: nil, + Value: 0, + IsLeaf: false, + } + case 2: + return &TreeNode{ + Left: &TreeNode{ + Left: nil, + Right: nil, + Value: numbers[0], + IsLeaf: true, + }, + Right: &TreeNode{ + Left: nil, + Right: nil, + Value: numbers[1], + IsLeaf: true, + }, + Value: 0, + IsLeaf: false, + } + default: + l, r := splitListIntoTwo(numbers) + return &TreeNode{ + Left: numbersIntoTree(l), + Right: numbersIntoTree(r), + Value: 0, + IsLeaf: false, + } + } +} + +func splitListIntoTwo[S ~[]T, T any](s S) (S, S) { + middle := len(s) / 2 + left := S{} + right := S{} + + for i, v := range s { + if i < middle { + left = append(left, v) + } else { + right = append(right, v) + } + } + return left, right +} + +func (n *TreeNode) Advance() { + if n.IsLeaf { + // 1. 0 -> 1 + // 2. len("num") % 2 == 0 -> num[len("num")/2:], num[:len("num")/2] + // 2424 -> 24, 24; 435892 -> 435, 892 + // 3. x -> x * 2024 + switch { + case n.Value == 0: + n.Value = 1 + case len(strconv.FormatUint(n.Value, 10))%2 == 0: + str := strconv.FormatUint(n.Value, 10) + left := string([]rune(str)[:len(str)/2]) + right := string([]rune(str)[len(str)/2:]) + n.IsLeaf = false + n.Left = &TreeNode{ + Left: nil, + Right: nil, + Value: other.Must(strconv.ParseUint(left, 10, 64)), + IsLeaf: true, + } + n.Right = &TreeNode{ + Left: nil, + Right: nil, + Value: other.Must(strconv.ParseUint(right, 10, 64)), + IsLeaf: true, + } + default: + n.Value *= 2024 + } + } else { + if n.Left != nil { + n.Left.Advance() + } + if n.Right != nil { + n.Right.Advance() + } + } +} + +func (n *TreeNode) String() string { + if n.IsLeaf { + return fmt.Sprint(n.Value) + } else { + if n.Right != nil { + return fmt.Sprintf("%s %s", n.Left, n.Right) + } else { + return fmt.Sprint(n.Left) + } + } +} + +func (n *TreeNode) Count() uint64 { + if n.IsLeaf { + return 1 + } else { + if n.Right != nil { + return n.Left.Count() + n.Right.Count() + } else { + return n.Left.Count() + } + } +} + +func main() { + nums := lineToNumbers(strings.TrimSpace(string(util.LoadFileFromArgs()))) + tree := numbersIntoTree(nums) + // fmt.Printf("%#v\n", tree.Right) + for range 25 { + tree.Advance() + } + fmt.Printf("Task 1: %d\n", tree.Count()) + var accCheck uint64 = 0 + for _, num := range nums { + accCheck += Advance(num, 0, 25) + } + fmt.Printf("Check 1: %d\n", accCheck) + // for range 50 { + // tree.Advance() + // } + // fmt.Printf("Task 2: %d\n", tree.Count()) + // resetCache() + var acc uint64 = 0 + for _, num := range nums { + acc += Advance(num, 0, 75) + } + // addChan := make(chan uint64, 1) + // wg := sync.WaitGroup{} + // go func() { + // for v := range addChan { + // acc += v + // } + // }() + // for _, num := range nums { + // wg.Add(1) + // go func() { + // addChan <- Advance(num, 0, 75) + // wg.Done() + // }() + // } + // wg.Wait() + // close(addChan) + fmt.Printf("Task 2: %d\n", acc) +} diff --git a/day11/noTreeApproach.go.disabled b/day11/noTreeApproach.go.disabled new file mode 100644 index 0000000..893962d --- /dev/null +++ b/day11/noTreeApproach.go.disabled @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "strconv" + + "git.mstar.dev/mstar/goutils/other" + "github.com/Skarlso/cache" +) + +var stoneCache = cache.New[uint64]() + +// Doesn't work, the cache thing can't handle recursion +func AdvanceBroken(val uint64, iteration int, maxIter int) cache.Cacheable[uint64] { + return func() uint64 { + fmt.Printf("Iteration %d with val %d\n", iteration, val) + if iteration == maxIter { + fmt.Println("1") + return 1 + } + if val == 0 { + fmt.Println("0") + return stoneCache.WithCache( + AdvanceBroken(1, iteration+1, maxIter), + []any{1, iteration + 1, maxIter}, + ) + } + if len(strconv.FormatUint(val, 10))%2 == 0 { + fmt.Println("Split") + str := strconv.FormatUint(val, 10) + fmt.Println("format") + left := other.Must(strconv.ParseUint(string([]rune(str)[:len(str)/2]), 10, 64)) + right := other.Must(strconv.ParseUint(string([]rune(str)[len(str)/2:]), 10, 64)) + fmt.Printf("l: %d, r: %d\n", left, right) + return stoneCache.WithCache( + AdvanceBroken(left, iteration+1, maxIter), + []any{left, iteration + 1, maxIter}, + ) + stoneCache.WithCache( + AdvanceBroken(right, iteration+1, maxIter), + []any{right, iteration + 1, maxIter}, + ) + } + return stoneCache.WithCache( + AdvanceBroken(val*2024, iteration+1, maxIter), + []any{val * 2024, iteration + 1, maxIter}, + ) + } +} diff --git a/day11/sample b/day11/sample new file mode 100644 index 0000000..9b26c84 --- /dev/null +++ b/day11/sample @@ -0,0 +1 @@ +125 17 diff --git a/go.mod b/go.mod index 130153d..16bac48 100644 --- a/go.mod +++ b/go.mod @@ -3,5 +3,3 @@ module git.mstar.dev/mstar/aoc24 go 1.23.3 require git.mstar.dev/mstar/goutils v1.5.4 - -require github.com/BooleanCat/go-functional/v2 v2.3.0 // indirect diff --git a/go.sum b/go.sum index 7b82a65..9638f4e 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,2 @@ -git.mstar.dev/mstar/goutils v1.5.3 h1:Wqa8H8EWnc9IJmzsi6qeVqwYHPvWiDhG/b9WtZr0Nos= -git.mstar.dev/mstar/goutils v1.5.3/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA= git.mstar.dev/mstar/goutils v1.5.4 h1:l/4oQe/fBk9zyXplQkGXbmQndnm0aRdHuy4wgQfNrFo= git.mstar.dev/mstar/goutils v1.5.4/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA= -github.com/BooleanCat/go-functional/v2 v2.3.0 h1:mwVrUa4MSKQNs/N4EPy0fadRLyzP+eoc0NbnzWHEzLM= -github.com/BooleanCat/go-functional/v2 v2.3.0/go.mod h1:IpUUAXAc9CiWDb+YDXkJyyUhtOVqDtyICDRg/de1IaQ= -gitlab.com/mstarongitlab/goutils v1.5.1 h1:UlQL90DctJ7QbfJ5NOPheMK3PdFt6sBIjXR3fJpd3Ms= -gitlab.com/mstarongitlab/goutils v1.5.1/go.mod h1:f71xLeTv05GHiRHKkgDRXfxOPRkjXNteXqLZyg02xhs=