126 lines
3.0 KiB
Go
126 lines
3.0 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"os"
|
||
|
"strconv"
|
||
|
|
||
|
"git.annabunch.es/annabunches/adventofcode/2020/lib/util"
|
||
|
)
|
||
|
|
||
|
type Cup struct {
|
||
|
label int
|
||
|
next *Cup
|
||
|
}
|
||
|
|
||
|
func parseInput(input string, step string) (*Cup, map[int]*Cup) {
|
||
|
labelMap := make(map[int]*Cup)
|
||
|
|
||
|
var firstCup *Cup
|
||
|
var prev *Cup
|
||
|
first := true
|
||
|
for _, char := range input {
|
||
|
label := util.MustAtoi(string(char))
|
||
|
newCup := &Cup{
|
||
|
label: label,
|
||
|
}
|
||
|
labelMap[label] = newCup
|
||
|
|
||
|
if first {
|
||
|
first = false
|
||
|
firstCup = newCup
|
||
|
} else {
|
||
|
prev.next = newCup
|
||
|
}
|
||
|
prev = newCup
|
||
|
}
|
||
|
if step == "2" {
|
||
|
for i := 10; i <= 1000000; i++ {
|
||
|
newCup := &Cup{
|
||
|
label: i,
|
||
|
}
|
||
|
prev.next = newCup
|
||
|
prev = newCup
|
||
|
labelMap[i] = newCup
|
||
|
}
|
||
|
}
|
||
|
|
||
|
prev.next = firstCup
|
||
|
return firstCup, labelMap
|
||
|
}
|
||
|
|
||
|
func move(current *Cup, labels map[int]*Cup, step string) *Cup {
|
||
|
// The crab picks up the three cups that are immediately clockwise of the current cup. They are removed from the circle; cup spacing is adjusted as necessary to maintain the circle.
|
||
|
pickedUp := current.next
|
||
|
current.next = current.next.next.next.next
|
||
|
|
||
|
cursor := pickedUp
|
||
|
holding := make(map[int]struct{})
|
||
|
for i := 0; i < 3; i++ {
|
||
|
holding[cursor.label] = struct{}{}
|
||
|
cursor = cursor.next
|
||
|
}
|
||
|
|
||
|
// The crab selects a destination cup: the cup with a label equal to the current cup's label minus one. If this would select one of the cups that was just picked up, the crab will keep subtracting one until it finds a cup that wasn't just picked up. If at any point in this process the value goes below the lowest value on any cup's label, it wraps around to the highest value on any cup's label instead.
|
||
|
dest := current.label - 1
|
||
|
_, nope := holding[dest]
|
||
|
for nope || dest == 0 {
|
||
|
dest--
|
||
|
if dest <= 0 {
|
||
|
switch step {
|
||
|
case "1":
|
||
|
dest = 9
|
||
|
case "2":
|
||
|
dest = 1000000
|
||
|
}
|
||
|
}
|
||
|
_, nope = holding[dest]
|
||
|
}
|
||
|
|
||
|
// The crab places the cups it just picked up so that they are immediately clockwise of the destination cup. They keep the same order as when they were picked up.
|
||
|
destCup, ok := labels[dest]
|
||
|
if !ok {
|
||
|
log.Panicf("No such cup %d", dest)
|
||
|
}
|
||
|
oldNext := destCup.next
|
||
|
destCup.next = pickedUp
|
||
|
pickedUp.next.next.next = oldNext
|
||
|
|
||
|
// The crab selects a new current cup: the cup which is immediately clockwise of the current cup.
|
||
|
return current.next
|
||
|
}
|
||
|
|
||
|
func formatCups(start *Cup) string {
|
||
|
answer := ""
|
||
|
startLabel := start.label
|
||
|
for cursor := start.next; cursor.label != startLabel; cursor = cursor.next {
|
||
|
answer = answer + strconv.Itoa(cursor.label)
|
||
|
}
|
||
|
return answer
|
||
|
}
|
||
|
|
||
|
// too high: 540428194020
|
||
|
func main() {
|
||
|
step := os.Args[1]
|
||
|
values := os.Args[2]
|
||
|
|
||
|
current, labels := parseInput(values, step)
|
||
|
|
||
|
switch step {
|
||
|
case "1":
|
||
|
for i := 0; i < 100; i++ {
|
||
|
current = move(current, labels, step)
|
||
|
}
|
||
|
fmt.Println(formatCups(labels[1]))
|
||
|
case "2":
|
||
|
for i := 0; i < 10000000; i++ {
|
||
|
current = move(current, labels, step)
|
||
|
}
|
||
|
cursor := labels[1]
|
||
|
fmt.Println(cursor.next.label)
|
||
|
fmt.Println(cursor.next.next.label)
|
||
|
fmt.Println(cursor.next.label * cursor.next.next.label)
|
||
|
}
|
||
|
}
|