Day 23: Part 2

Rewrite with a linked list and pointer cache because array allocation was optimistic at best

Signed-off-by: James Griffin <james@unsupervised.ca>
This commit is contained in:
2020-12-23 18:43:51 -04:00
parent 35de155e0b
commit 6584675e5a
3 changed files with 125 additions and 91 deletions

View File

@@ -97,4 +97,5 @@ func main() {
// Day 23
fmt.Println(twentythree.PartOne())
fmt.Println(twentythree.PartTwo())
}

View File

@@ -5,100 +5,131 @@ import (
"strconv"
)
type cup struct {
id int
next *cup
}
type game struct {
cups []int
currentCup int
highestCup int
currentCup *cup
cups int
index map[int]*cup
}
func (g *game) load(input string) error {
for _, c := range input {
if len(input) < 1 {
return fmt.Errorf("Cannot parse empty input")
}
g.cups = len(input)
g.index = map[int]*cup{}
g.currentCup = &cup{}
cp := g.currentCup
for i, c := range input {
cupNumber, err := strconv.Atoi(string(c))
if err != nil {
return err
}
if cupNumber > g.highestCup {
g.highestCup = cupNumber
}
g.cups = append(g.cups, cupNumber)
}
if len(g.cups) > 0 {
g.currentCup = g.cups[0]
cp.id = cupNumber
g.index[cp.id] = cp
if i != g.cups-1 {
cp.next = &cup{}
cp = cp.next
} else {
cp.next = g.currentCup
}
}
return nil
}
func (g *game) play(moves int) string {
func (g *game) milliload(input string) error {
if err := g.load(input); err != nil {
return err
}
start := g.currentCup.next
for start.next != g.currentCup {
start = start.next
}
for g.cups != 1000000 {
start.next = &cup{
id: g.cups + 1,
}
g.index[start.next.id] = start.next
g.cups++
start = start.next
}
start.next = g.currentCup
return nil
}
func destination(cups, possible, missing1, missing2, missing3 int) int {
d := possible
if d <= 0 {
d = cups
}
if d == missing1 || d == missing2 || d == missing3 {
return destination(cups, d-1, missing1, missing2, missing3)
}
return d
}
func (g *game) play(moves int, output bool) string {
for i := 0; i < moves; i++ {
// pickup cups
pickup := g.cups[1:4]
remaining := []int{g.cups[0]}
remaining = append(remaining, g.cups[4:]...)
g.cups = remaining
pickup := g.currentCup.next
g.currentCup.next = g.currentCup.next.next.next.next
// find destination cup
destination := false
destinationIndex := -1
t := g.currentCup - 1
for !destination {
if t <= 0 {
t = g.highestCup
}
for i := 1; i < len(g.cups); i++ {
if g.cups[i] == t {
destinationIndex = i
break
}
}
if destinationIndex != -1 {
destination = true
}
t--
}
dp := g.index[destination(g.cups, g.currentCup.id-1, pickup.id, pickup.next.id, pickup.next.next.id)]
// place cups
newOrder := []int{}
for _, c := range remaining[:destinationIndex+1] {
newOrder = append(newOrder, c)
}
newOrder = append(newOrder, pickup...)
for _, c := range remaining[destinationIndex+1:] {
newOrder = append(newOrder, c)
}
g.cups = newOrder
pickup.next.next.next = dp.next
dp.next = pickup
// Update current cup
g.cups = g.cups[1:]
g.cups = append(g.cups, g.currentCup)
g.currentCup = g.cups[0]
g.currentCup = g.currentCup.next
}
if output {
return g.output()
}
return ""
}
func (g *game) output() string {
state := []int{}
for i, c := range g.cups {
if c == 1 {
state = append(state, g.cups[i+1:]...)
state = append(state, g.cups[:i]...)
}
}
cp := g.index[1].next
output := ""
for _, n := range state {
output += fmt.Sprintf("%d", n)
for g.index[1] != cp {
output += fmt.Sprintf("%d", cp.id)
cp = cp.next
}
return output
}
func (g *game) starProduct() int {
return g.index[1].next.id * g.index[1].next.next.id
}
// PartOne What are the labels on the cups after cup 1
func PartOne() string {
g := game{}
g.load("418976235")
return g.play(100)
return g.play(100, true)
}
// PartTwo What is the product of the two cups clockwise of cup 1
func PartTwo() string {
g := game{}
g.milliload("418976235")
g.play(10000000, false)
return fmt.Sprintf("The start product is %d", g.starProduct())
}

View File

@@ -6,13 +6,8 @@ func Test_load_input(t *testing.T) {
g := game{}
g.load("32415")
if len(g.cups) != 5 {
t.Logf("Expected 5 cups. Got %d", len(g.cups))
t.FailNow()
}
if g.highestCup != 5 {
t.Logf("Expected highest cup to be 5, found %d", g.highestCup)
if g.cups != 5 {
t.Logf("Expected 5 cups. Got %d", g.cups)
t.FailNow()
}
}
@@ -21,17 +16,12 @@ func Test_10_moves(t *testing.T) {
g := game{}
g.load("389125467")
if len(g.cups) != 9 {
t.Logf("Expected 9 cups. Got %d", len(g.cups))
if g.cups != 9 {
t.Logf("Expected 9 cups. Got %d", g.cups)
t.FailNow()
}
if g.highestCup != 9 {
t.Logf("Expected highest cup to be 9, found %d", g.highestCup)
t.FailNow()
}
tenMoves := g.play(10)
tenMoves := g.play(10, true)
if tenMoves != "92658374" {
t.Logf("Expected \"92658374\" but found %q", tenMoves)
t.FailNow()
@@ -42,19 +32,31 @@ func Test_100_moves(t *testing.T) {
g := game{}
g.load("389125467")
if len(g.cups) != 9 {
t.Logf("Expected 9 cups. Got %d", len(g.cups))
if g.cups != 9 {
t.Logf("Expected 9 cups. Got %d", g.cups)
t.FailNow()
}
if g.highestCup != 9 {
t.Logf("Expected highest cup to be 9, found %d", g.highestCup)
t.FailNow()
}
hundredMoves := g.play(100)
hundredMoves := g.play(100, true)
if hundredMoves != "67384529" {
t.Logf("Expected \"67384529\" but found %q", hundredMoves)
t.FailNow()
}
}
func Test_10million_moves(t *testing.T) {
g := game{}
g.milliload("389125467")
if g.cups != 1000000 {
t.Logf("Expected 1000000 cups. Got %d", g.cups)
t.FailNow()
}
g.play(10000000, false)
starProduct := g.starProduct()
if starProduct != 149245887792 {
t.Logf("Expected 149245887792 but got produc %q", 149245887792)
t.FailNow()
}
}