From 9c387bb9c7c61df53edee8d8f28f5fbe8e0352e3 Mon Sep 17 00:00:00 2001 From: Andreas Kohn Date: Sun, 11 Dec 2022 21:43:12 +0100 Subject: [PATCH] Day 11, Task 2: Try keeping numbers as their unique prime factors The idea was that this should be fine, because we test divisibility of primes only. Unfortunately this doesn't work out, because we need the "complete" number for the additions done for some of the monkey operations. --- .vscode/launch.json | 2 +- days/11/README.md | 12 +++++ days/11/day11.go | 116 +++++++++++++++++++++++--------------------- utils/prime.go | 47 ++++++++++++++++++ 4 files changed, 122 insertions(+), 55 deletions(-) create mode 100644 days/11/README.md diff --git a/.vscode/launch.json b/.vscode/launch.json index ffe1b2f..42e6db5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,7 +12,7 @@ "args": [ "day11", "--sample=true", - "--debug" + "--debug=2" ], "program": "${workspaceFolder}" }, diff --git a/days/11/README.md b/days/11/README.md new file mode 100644 index 0000000..cc26d3b --- /dev/null +++ b/days/11/README.md @@ -0,0 +1,12 @@ +79=79 +98=2, 7, 7 +54=2, 3, 3, 3 +65, 75, 74 + +19 +23 +13 +17 + +Keep in prime factors, refactorize after +? +We can presumably drop all powers, i.e. 2*7*7 is actually identical to 2*7 for our case. diff --git a/days/11/day11.go b/days/11/day11.go index abb2254..fcf71dc 100644 --- a/days/11/day11.go +++ b/days/11/day11.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/ankon/adventofcode/2022/days" + "github.com/ankon/adventofcode/2022/utils" "github.com/spf13/cobra" "golang.org/x/exp/slices" ) @@ -17,10 +18,10 @@ var sampleInput string //go:embed input.txt var fullInput string -var debug = false +var debug = 0 func ConfigureCommand(cmd *cobra.Command) { - cmd.Flags().BoolVar(&debug, "debug", false, "Enable debug output") + cmd.Flags().IntVar(&debug, "debug", 0, "Enable debug output") } func Run(useSampleInput bool) error { @@ -42,12 +43,12 @@ func Run(useSampleInput bool) error { return nil } -type opFunc func(int) int -type testFunc func(int) int +type opFunc func(utils.UniquePrimeFactors) utils.UniquePrimeFactors +type testFunc func(utils.UniquePrimeFactors) int type monkey struct { // Items the monkey has (only tracking the worry level) - items []int + items []utils.UniquePrimeFactors op opFunc test testFunc @@ -55,9 +56,9 @@ type monkey struct { inspections int } -func (m *monkey) inspectItem() (int, bool) { +func (m *monkey) inspectItem() (utils.UniquePrimeFactors, bool) { if len(m.items) == 0 { - return 0, false + return utils.UniquePrimeFactors{}, false } result := m.items[0] m.items = m.items[1:] @@ -65,8 +66,8 @@ func (m *monkey) inspectItem() (int, bool) { return result, true } -func (m *monkey) catchItem(level int) { - m.items = append(m.items, level) +func (m *monkey) catchItem(item utils.UniquePrimeFactors) { + m.items = append(m.items, item) } const startItemsPrefix = " Starting items: " @@ -76,7 +77,7 @@ const testIfTrueThrowPrefix = " If true: throw to monkey " const testIfFalseThrowPrefix = " If false: throw to monkey " func parseMonkey(lines []string) (monkey, int, error) { - items := []int{} + items := []utils.UniquePrimeFactors{} var op opFunc var test testFunc @@ -92,45 +93,51 @@ func parseMonkey(lines []string) (monkey, int, error) { if err != nil { return monkey{}, i, fmt.Errorf("cannot parse starting items %q", ss) } - items = append(items, v) + items = append(items, utils.Factorize(v)) } } else if strings.HasPrefix(line, opPrefix) { ss := strings.Split(line[len(opPrefix):], " ") - arg1, _ := strconv.Atoi(ss[0]) arg2, _ := strconv.Atoi(ss[2]) - op = func(level int) int { - var result int - if ss[0] == "old" { - result = level - } else { - result = arg1 + switch ss[1] { + case "+": + if ss[0] != "old" { + return monkey{}, i, fmt.Errorf("first operand must be %q for +", "old") } - var other int if ss[2] == "old" { - other = level - } else { - other = arg2 + return monkey{}, i, fmt.Errorf("second operand must not be %q for +", ss[2]) } - - switch ss[1] { - case "+": - result += other - case "-": - result -= other - case "*": - result *= other - case "/": - result /= other + op = func(pf utils.UniquePrimeFactors) utils.UniquePrimeFactors { + return utils.Factorize(pf.Value() + arg2) } - - return result + case "*": + if ss[0] != "old" { + return monkey{}, i, fmt.Errorf("first operand must be %q for *", "old") + } + if ss[2] != "old" && !utils.IsPrime(arg2) { + return monkey{}, i, fmt.Errorf("second operand must be %q or prime for *", "old") + } + op = func(pf utils.UniquePrimeFactors) utils.UniquePrimeFactors { + if ss[2] == "old" { + for _, f := range pf { + pf.Insert(f) + } + } else { + pf.Insert(arg2) + } + return pf + } + default: + return monkey{}, i, fmt.Errorf("unsupported operator %q", ss[1]) } } else if strings.HasPrefix(line, testDivisibleByPrefix) { divisor, err := strconv.Atoi(line[len(testDivisibleByPrefix):]) if err != nil { return monkey{}, i, fmt.Errorf("cannot parse test %q", line) } + if !utils.IsPrime(divisor) { + return monkey{}, i, fmt.Errorf("divisor %d in test must be prime", divisor) + } ifTrueLine := lines[i+1] trueMonkey, err := strconv.Atoi(ifTrueLine[len(testIfTrueThrowPrefix):]) if err != nil { @@ -143,12 +150,13 @@ func parseMonkey(lines []string) (monkey, int, error) { } i += 2 - test = func(level int) int { - if level%divisor == 0 { - return trueMonkey - } else { - return falseMonkey + test = func(item utils.UniquePrimeFactors) int { + for _, p := range item { + if p == divisor { + return trueMonkey + } } + return falseMonkey } } } @@ -190,7 +198,7 @@ func parseMonkeys(lines []string) ([]*monkey, error) { func playRound(monkeys []*monkey) { for i, m := range monkeys { - if debug { + if debug > 1 { fmt.Printf("Monkey %d:\n", i) } for { @@ -199,26 +207,26 @@ func playRound(monkeys []*monkey) { break } - if debug { - fmt.Printf(" Monkey inspects an item with a worry level of %d.\n", item) + if debug > 1 { + fmt.Printf(" Monkey inspects an item with a worry level of %v.\n", item) } - level := m.op(item) - if debug { - fmt.Printf(" Worry level is now %d.\n", level) + newItem := m.op(item) + if debug > 1 { + fmt.Printf(" Worry level is now %v.\n", newItem) } - level /= 3 - if debug { - fmt.Printf(" Monkey gets bored with item. Worry level is divided by 3 to %d.\n", level) - } + // level /= 3 + // if debug > 1 { + // fmt.Printf(" Monkey gets bored with item. Worry level is divided by 3 to %d.\n", level) + // } - throwTo := m.test(level) - if debug { - fmt.Printf(" Item with worry level %d is thrown to monkey %d.\n", level, throwTo) + throwTo := m.test(newItem) + if debug > 1 { + fmt.Printf(" Item with worry level %v is thrown to monkey %d.\n", newItem, throwTo) } - monkeys[throwTo].catchItem(level) + monkeys[throwTo].catchItem(newItem) } } } @@ -227,7 +235,7 @@ func countItemInspections(monkeys []*monkey, rounds int) []int { for round := 0; round < rounds; round++ { playRound(monkeys) - if debug { + if debug > 0 { fmt.Printf("After round %d, the monkeys are holding items with these worry levels:\n", round+1) for k, m := range monkeys { fmt.Printf("Monkey %d: %v\n", k, m.items) diff --git a/utils/prime.go b/utils/prime.go index 971208a..d8b3589 100644 --- a/utils/prime.go +++ b/utils/prime.go @@ -35,3 +35,50 @@ func IsPrime(p int) bool { } return true } + +// (Unique) prime factors +type UniquePrimeFactors []int + +func (p *UniquePrimeFactors) Value() int { + result := 1 + for _, f := range *p { + result *= f + } + return result +} + +func (p *UniquePrimeFactors) Insert(f int) bool { + if !IsPrime(f) { + panic("cannot insert non-prime") + } + i := sort.SearchInts(*p, f) + if i < len(*p) && (*p)[i] == f { + return false + } + if i == len(*p) { + newP := append(*p, f) + p = &newP + } else { + newP := make(UniquePrimeFactors, 0, len(*p) + 1) + newP = append(newP, (*p)[0:i]...) + newP = append(newP, f) + newP = append(newP, (*p)[i:]...) + p = &newP + } + return true +} + +// Factorize returns the unique prime factors of the given number +func Factorize(n int) UniquePrimeFactors { + result := UniquePrimeFactors{} + for p := 2; n > 1; p = nextPrimeAfter(p) { + f := 0 + for ; n%p == 0; n /= p { + f++ + } + if f > 0 { + result = append(result, p) + } + } + return result +}