mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-18 19:13:27 +02:00
118 lines
2.6 KiB
Go
118 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/alexchao26/advent-of-code-go/cast"
|
|
"github.com/alexchao26/advent-of-code-go/util"
|
|
)
|
|
|
|
func main() {
|
|
var part int
|
|
flag.IntVar(&part, "part", 1, "part 1 or 2")
|
|
flag.Parse()
|
|
fmt.Println("Running part", part)
|
|
|
|
ans := crabCups(util.ReadFile("./input.txt"), part)
|
|
fmt.Println("Output:", ans)
|
|
}
|
|
|
|
// LLNode is a doubly linked list node
|
|
type LLNode struct {
|
|
val int
|
|
left, right *LLNode
|
|
}
|
|
|
|
// ~1250/ 199
|
|
func crabCups(input string, part int) int {
|
|
cups := strings.Split(input, "")
|
|
|
|
currentCup := &LLNode{val: cast.ToInt(cups[0])}
|
|
// LRU caches are a hashmap of integers pointing to a pointer of a (doubly) LL node
|
|
// so finding a given node is constant time through the hash map
|
|
lruCacheMap := map[int]*LLNode{currentCup.val: currentCup}
|
|
|
|
// setup initial cups
|
|
iter := currentCup
|
|
for _, v := range cups[1:] {
|
|
iter.right = &LLNode{
|
|
val: cast.ToInt(v),
|
|
left: iter,
|
|
}
|
|
iter = iter.right
|
|
lruCacheMap[iter.val] = iter
|
|
}
|
|
rounds := 100 // part 1 default
|
|
|
|
// for part 2 make rounds higher and add a bunch of extra nodes
|
|
if part == 2 {
|
|
rounds = 10000000
|
|
for i := 10; i <= 1000000; i++ {
|
|
iter.right = &LLNode{
|
|
val: i,
|
|
left: iter,
|
|
}
|
|
iter = iter.right
|
|
lruCacheMap[iter.val] = iter
|
|
}
|
|
}
|
|
|
|
iter.right = currentCup
|
|
currentCup.left = iter
|
|
|
|
for i := 0; i < rounds; i++ {
|
|
valToFind := currentCup.val - 1
|
|
|
|
ignoreVals := map[int]bool{}
|
|
for it := currentCup.right; len(ignoreVals) < 3; it = it.right {
|
|
ignoreVals[it.val] = true
|
|
}
|
|
|
|
// if the val to find is in ignore vals or is less than smallest possible
|
|
// value, then loop and decrement until a valid value is found
|
|
for ignoreVals[valToFind] || valToFind <= 0 {
|
|
valToFind--
|
|
// loop back to highest numbered cup if value goes below zero
|
|
if valToFind <= 0 {
|
|
valToFind = len(lruCacheMap)
|
|
}
|
|
}
|
|
|
|
// next 3
|
|
startOfThree := currentCup.right
|
|
endOfThree := currentCup.right.right.right
|
|
|
|
// remove the three off the end of the current cup
|
|
currentCup.right = endOfThree.right
|
|
currentCup.right.left = currentCup
|
|
|
|
// find the LL node to attach the 3 cups to (directly from LRU cache)
|
|
attachAfterMe := lruCacheMap[valToFind]
|
|
// mash the 3 cups into the doubly LL
|
|
endOfThree.right = attachAfterMe.right
|
|
attachAfterMe.right.left = endOfThree
|
|
attachAfterMe.right = startOfThree
|
|
startOfThree.left = attachAfterMe
|
|
|
|
// move currentCup pointer
|
|
currentCup = currentCup.right
|
|
}
|
|
|
|
oneCup := lruCacheMap[1]
|
|
|
|
if part == 2 {
|
|
return oneCup.right.val * oneCup.right.right.val
|
|
}
|
|
|
|
var ans int
|
|
for i := 0; i < 8; i++ {
|
|
oneCup = oneCup.right
|
|
ans *= 10
|
|
ans += oneCup.val
|
|
}
|
|
|
|
return ans
|
|
}
|