package common import ( "bufio" "strconv" "strings" ) type WorryLevel interface { Plus(int) WorryLevel Times(int) WorryLevel Power(int) WorryLevel Relief() WorryLevel Modulo(int) int } type WorryLevelWithRelief struct { level int } func (worryLevel WorryLevelWithRelief) Plus(operand int) WorryLevel { return WorryLevelWithRelief{worryLevel.level + operand} } func (worryLevel WorryLevelWithRelief) Times(operand int) WorryLevel { return WorryLevelWithRelief{worryLevel.level * operand} } func (worryLevel WorryLevelWithRelief) Power(operand int) WorryLevel { result := 1 for i := 0; i < operand; i++ { result *= worryLevel.level } return WorryLevelWithRelief{result} } func (worryLevel WorryLevelWithRelief) Relief() WorryLevel { return WorryLevelWithRelief{worryLevel.level / 3} } func (worryLevel WorryLevelWithRelief) Modulo(operand int) int { return worryLevel.level % operand } type WorryLevelWithoutReliefBase struct { level int } func (worryLevel *WorryLevelWithoutReliefBase) Plus(operand int) WorryLevel { return &WorryLevelWithoutReliefPlus{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefBase) Times(operand int) WorryLevel { return &WorryLevelWithoutReliefTimes{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefBase) Power(operand int) WorryLevel { return &WorryLevelWithoutReliefPower{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefBase) Relief() WorryLevel { return worryLevel } func (worryLevel *WorryLevelWithoutReliefBase) Modulo(operand int) int { return worryLevel.level % operand } type WorryLevelWithoutReliefPlus struct { worryLevel WorryLevel operand int modulos map[int]int } func (worryLevel *WorryLevelWithoutReliefPlus) Plus(operand int) WorryLevel { return &WorryLevelWithoutReliefPlus{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefPlus) Times(operand int) WorryLevel { return &WorryLevelWithoutReliefTimes{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefPlus) Power(operand int) WorryLevel { return &WorryLevelWithoutReliefPower{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefPlus) Relief() WorryLevel { return worryLevel } func (worryLevel *WorryLevelWithoutReliefPlus) Modulo(operand int) int { if val, ok := worryLevel.modulos[operand]; ok { return val } else { val = (worryLevel.worryLevel.Modulo(operand) + worryLevel.operand % operand) % operand worryLevel.modulos[operand] = val return val } } type WorryLevelWithoutReliefTimes struct { worryLevel WorryLevel operand int modulos map[int]int } func (worryLevel *WorryLevelWithoutReliefTimes) Plus(operand int) WorryLevel { return &WorryLevelWithoutReliefPlus{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefTimes) Times(operand int) WorryLevel { return &WorryLevelWithoutReliefTimes{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefTimes) Power(operand int) WorryLevel { return &WorryLevelWithoutReliefPower{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefTimes) Relief() WorryLevel { return worryLevel } func (worryLevel *WorryLevelWithoutReliefTimes) Modulo(operand int) int { if val, ok := worryLevel.modulos[operand]; ok { return val } else { val = (worryLevel.worryLevel.Modulo(operand) * (worryLevel.operand % operand)) % operand worryLevel.modulos[operand] = val return val } } type WorryLevelWithoutReliefPower struct { worryLevel WorryLevel operand int modulos map[int]int } func (worryLevel *WorryLevelWithoutReliefPower) Plus(operand int) WorryLevel { return &WorryLevelWithoutReliefPlus{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefPower) Times(operand int) WorryLevel { return &WorryLevelWithoutReliefTimes{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefPower) Power(operand int) WorryLevel { return &WorryLevelWithoutReliefPower{worryLevel, operand, make(map[int]int)} } func (worryLevel *WorryLevelWithoutReliefPower) Relief() WorryLevel { return worryLevel } func (worryLevel *WorryLevelWithoutReliefPower) Modulo(operand int) int { if val, ok := worryLevel.modulos[operand]; ok { return val } else { mod := worryLevel.worryLevel.Modulo(operand) result := 1 for i := 0; i < worryLevel.operand; i++ { result *= mod } val = result % operand worryLevel.modulos[operand] = val return val } } type Item struct { worryLevel WorryLevel } func NewItem(worryLevel WorryLevel) *Item { return &Item{worryLevel} } type Operation interface { Apply(WorryLevel) WorryLevel } type Addition struct { operand int } func (operation Addition) Apply(operand WorryLevel) WorryLevel { return operand.Plus(operation.operand) } type Multiplication struct { operand int } func (operation Multiplication) Apply(operand WorryLevel) WorryLevel { return operand.Times(operation.operand) } type Power struct { operand int } func (operation Power) Apply(operand WorryLevel) WorryLevel { return operand.Power(operation.operand) } type Test struct { divisible int targets map[bool]*Monkey } type Monkey struct { heldItems []Item operation Operation test Test nInspections int } func (monkey *Monkey) ReceiveItem(item Item) { monkey.heldItems = append(monkey.heldItems, item) } func (monkey *Monkey) GetNumberOfInspections() int { return monkey.nInspections } func (monkey *Monkey) NextRound() { for len(monkey.heldItems) > 0 { monkey.nInspections++ item := monkey.heldItems[0] monkey.heldItems = monkey.heldItems[1:] worryLevel := item.worryLevel worryLevel = monkey.operation.Apply(worryLevel) worryLevel = worryLevel.Relief() monkey.test.targets[worryLevel.Modulo(monkey.test.divisible) == 0].ReceiveItem(Item{worryLevel}) } } func Parse(scanner bufio.Scanner, relief bool) []Monkey { monkeys := []Monkey{} targets := make(map[int]map[bool]int) var monkey Monkey for scanner.Scan() { line := scanner.Text() if line == "" { monkeys = append(monkeys, monkey) } else if line[:6] == "Monkey" { monkey = Monkey{} } else if line[:16] == " Starting items" { items := strings.Split(line[18:], ", ") for _, item := range(items) { itemInt, _ := strconv.Atoi(item) if (relief) { monkey.heldItems = append(monkey.heldItems, *NewItem(WorryLevelWithRelief{itemInt})) } else { monkey.heldItems = append(monkey.heldItems, *NewItem(&WorryLevelWithoutReliefBase{itemInt})) } } } else if line[:11] == " Operation" { if line[23] == '+' { operand, _ := strconv.Atoi(line[25:]) monkey.operation = Addition{operand} } else if line[23] == '*' { operand, err := strconv.Atoi(line[25:]) if err == nil { monkey.operation = Multiplication{operand} } else { monkey.operation = Power{2} } } } else if line[:6] == " Test" { divisible, _ := strconv.Atoi(line[21:]) monkey.test.divisible = divisible targets[len(monkeys)] = make(map[bool]int) } else if line[:11] == " If true" { targets[len(monkeys)][true] = int(line[29] - '0') } else if line[:12] == " If false" { targets[len(monkeys)][false] = int(line[30] - '0') } } monkeys = append(monkeys, monkey) for i, target := range targets { monkeys[i].test.targets = make(map[bool]*Monkey) monkeys[i].test.targets[true] = &monkeys[target[true]] monkeys[i].test.targets[false] = &monkeys[target[false]] } return monkeys }