Files
2022-12-15 14:56:28 +01:00

306 lines
7.7 KiB
Go

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
}