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 +}