diff --git a/main.go b/main.go index 99020fb..28bf10e 100644 --- a/main.go +++ b/main.go @@ -97,4 +97,5 @@ func main() { // Day 23 fmt.Println(twentythree.PartOne()) + fmt.Println(twentythree.PartTwo()) } diff --git a/twentythree/day_twentythree.go b/twentythree/day_twentythree.go index d31f824..f696f6d 100644 --- a/twentythree/day_twentythree.go +++ b/twentythree/day_twentythree.go @@ -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 { - 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 - - // 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-- - } - - // 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 - - // Update current cup - g.cups = g.cups[1:] - g.cups = append(g.cups, g.currentCup) - g.currentCup = g.cups[0] +func (g *game) milliload(input string) error { + if err := g.load(input); err != nil { + return err } - return g.output() + 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.currentCup.next + g.currentCup.next = g.currentCup.next.next.next.next + + // find destination cup + dp := g.index[destination(g.cups, g.currentCup.id-1, pickup.id, pickup.next.id, pickup.next.next.id)] + + // place cups + pickup.next.next.next = dp.next + dp.next = pickup + + 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()) } diff --git a/twentythree/day_twentythree_test.go b/twentythree/day_twentythree_test.go index 5436d0a..8ec5914 100644 --- a/twentythree/day_twentythree_test.go +++ b/twentythree/day_twentythree_test.go @@ -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() } -} \ No newline at end of file +} + +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() + } +}