Monkey in the middle

advent 2022 day 11

Monkeys throwing stuff around and making you worried


package problems

import (
	"fmt"
	"sort"
	"strings"

	"github.com/xxxx/advent/internal"
)

type day11 struct {
	lines []string
}

type monkey struct {
	items         []int
	operation     func(int) int
	test          func(int) bool
	true_monkey   int
	false_monkey  int
	total_inspect int
}

func Day11(part int, file string) int {

	in := day11{
		lines: internal.Read(file),
	}

	switch part {
	case 1:
		return in.part_one()
	case 2:
		return in.part_two()
	default:
		return 0
	}

}

func build_operation(a int, operand string) func(old int) int {
	// anonymous function for monkey to use
	// a is a constant
	switch operand {
	case "+":
		return func(old int) int {
			return old + a
		}
	case "*":
		return func(old int) int {
			return old * a
		}
	case "^":
		return func(old int) int {
			return old * old
		}
	default:
		panic(fmt.Sprintf("unknown operand: %s", operand))
	}
}

func build_test(a int) func(old int) bool {
	// anonymous function for monkey to use
	// a is a constant
	return func(old int) bool {
		return old%a == 0
	}
}

func read_input_into_monkeys(lines []string) map[int]*monkey {

	monkeys := make(map[int]*monkey)

	for i, line := range lines {
		monkey_number := i / 7
		m, found := monkeys[monkey_number]
		if found {
			switch i % 7 {
			case 1: // starting items
				data := strings.Split(line, ":")[1]
				m.items = internal.StrSliceToIntSlice(strings.Split(strings.TrimSpace(data), ","))
			case 2: // operation
				data := strings.Split(line, "=")[1]
				if strings.Contains(data, "+") {
					a := internal.StrToInt(strings.TrimSpace(strings.Split(data, "+")[1]))
					m.operation = build_operation(a, "+")
				} else if strings.Contains(data, "*") {
					constant := strings.TrimSpace(strings.Split(data, "*")[1])
					if constant == "old" {
						m.operation = build_operation(0, "^")
					} else {
						a := internal.StrToInt(strings.TrimSpace(strings.Split(data, "*")[1]))
						m.operation = build_operation(a, "*")
					}
				} else {
					panic(data)
				}
			case 3: // test
				a := internal.StrToInt(strings.TrimSpace(strings.Split(line, "by")[1]))
				m.test = build_test(a)
			case 4: // true monkey
				a := internal.StrToInt(strings.TrimSpace(strings.Split(line, "monkey")[1]))
				m.true_monkey = a
			case 5: // false monkey
				a := internal.StrToInt(strings.TrimSpace(strings.Split(line, "monkey")[1]))
				m.false_monkey = a
			case 6:
			default:
				panic(line)
			}

		} else {
			monkeys[monkey_number] = &monkey{
				items:         nil,
				operation:     nil,
				test:          nil,
				true_monkey:   0,
				total_inspect: 0,
			}
		}
	}

	return monkeys
}

func (input day11) part_one() int {

	monkeys := read_input_into_monkeys(input.lines)

	for round := 0; round < 20; round++ {
		for i := 0; i < len(monkeys); i++ {
			monkey := monkeys[i]
			for _, item := range monkey.items {
				worry_level := monkey.operation(item) / 3
				if monkey.test(worry_level) {
					monkeys[monkey.true_monkey].items = append(monkeys[monkey.true_monkey].items, worry_level)
				} else {
					monkeys[monkey.false_monkey].items = append(monkeys[monkey.false_monkey].items, worry_level)
				}
			}
			monkey.total_inspect = monkey.total_inspect + len(monkey.items)
			monkey.items = []int{}
		}
	}

	monkey_inspect_count := []int{}
	for i := 0; i < len(monkeys); i++ {
		monkey_inspect_count = append(monkey_inspect_count, monkeys[i].total_inspect)
	}

	sort.Sort(sort.Reverse(sort.IntSlice(monkey_inspect_count)))
	return monkey_inspect_count[0] * monkey_inspect_count[1]
}

func (input day11) part_two() int {
	monkeys := read_input_into_monkeys(input.lines)

	for round := 0; round < 10000; round++ {
		fmt.Println(round)
		for i := 0; i < len(monkeys); i++ {
			monkey := monkeys[i]
			for _, item := range monkey.items {
				worry_level := monkey.operation(item)
				if worry_level > 9699690 {
					worry_level = worry_level % 9699690
				}
				if monkey.test(worry_level) {
					monkeys[monkey.true_monkey].items = append(monkeys[monkey.true_monkey].items, worry_level)
				} else {
					monkeys[monkey.false_monkey].items = append(monkeys[monkey.false_monkey].items, worry_level)
				}
			}
			monkey.total_inspect = monkey.total_inspect + len(monkey.items)
			monkey.items = []int{}
		}
	}

	monkey_inspect_count := []int{}
	for i := 0; i < len(monkeys); i++ {
		monkey_inspect_count = append(monkey_inspect_count, monkeys[i].total_inspect)
	}

	sort.Sort(sort.Reverse(sort.IntSlice(monkey_inspect_count)))
	return monkey_inspect_count[0] * monkey_inspect_count[1]
}