package common import ( "bufio" "strconv" "strings" ) type Monkey interface { Apply() int Invert(int) int IsHuman() bool } type Human struct { name string number int } func (monkey *Human) Apply() int { return monkey.number } func (monkey *Human) Invert(total int) int { return total } func (monkey *Human) IsHuman() bool { return true } type NumberMonkey struct { name string number int } func (monkey *NumberMonkey) Apply() int { return monkey.number } func (monkey *NumberMonkey) Invert(total int) int { return total - monkey.number } func (monkey *NumberMonkey) IsHuman() bool { return false } type PlusMonkey struct { name string operand1 Monkey operand2 Monkey } func (monkey *PlusMonkey) Apply() int { return monkey.operand1.Apply() + monkey.operand2.Apply() } func (monkey *PlusMonkey) Invert(total int) int { if monkey.operand1.IsHuman() { return monkey.operand1.Invert(total - monkey.operand2.Apply()) } else { return monkey.operand2.Invert(total - monkey.operand1.Apply()) } } func (monkey *PlusMonkey) IsHuman() bool { return monkey.operand1.IsHuman() || monkey.operand2.IsHuman() } type MinusMonkey struct { name string operand1 Monkey operand2 Monkey } func (monkey *MinusMonkey) Apply() int { return monkey.operand1.Apply() - monkey.operand2.Apply() } func (monkey *MinusMonkey) Invert(total int) int { if monkey.operand1.IsHuman() { return monkey.operand1.Invert(total + monkey.operand2.Apply()) } else { return monkey.operand2.Invert(monkey.operand1.Apply() - total) } } func (monkey *MinusMonkey) IsHuman() bool { return monkey.operand1.IsHuman() || monkey.operand2.IsHuman() } type TimesMonkey struct { name string operand1 Monkey operand2 Monkey } func (monkey *TimesMonkey) Apply() int { return monkey.operand1.Apply() * monkey.operand2.Apply() } func (monkey *TimesMonkey) Invert(total int) int { if monkey.operand1.IsHuman() { return monkey.operand1.Invert(total / monkey.operand2.Apply()) } else { return monkey.operand2.Invert(total / monkey.operand1.Apply()) } } func (monkey *TimesMonkey) IsHuman() bool { return monkey.operand1.IsHuman() || monkey.operand2.IsHuman() } type DivisionMonkey struct { name string operand1 Monkey operand2 Monkey } func (monkey *DivisionMonkey) Apply() int { return monkey.operand1.Apply() / monkey.operand2.Apply() } func (monkey *DivisionMonkey) Invert(total int) int { if monkey.operand1.IsHuman() { return monkey.operand1.Invert(total * monkey.operand2.Apply()) } else { return monkey.operand2.Invert(monkey.operand1.Apply() / total) } } func (monkey *DivisionMonkey) IsHuman() bool { return monkey.operand1.IsHuman() || monkey.operand2.IsHuman() } type EqualityMonkey struct { name string operand1 Monkey operand2 Monkey } func (monkey *EqualityMonkey) Apply() int { if monkey.operand1.Apply() == monkey.operand2.Apply() { return 1 } else { return 0 } } func (monkey *EqualityMonkey) Invert(total int) int { if monkey.operand1.IsHuman() { return monkey.operand1.Invert(monkey.operand2.Apply() - total) } else { return monkey.operand2.Invert(monkey.operand1.Apply() - total) } } func (monkey *EqualityMonkey) IsHuman() bool { return monkey.operand1.IsHuman() || monkey.operand2.IsHuman() } type StringCouple struct { left string right string } type UnresolvedMonkey struct { name string symbol string } func Parse(scanner bufio.Scanner, rootIsEquality bool) Monkey { unresolvedMonkeys := make(map[StringCouple]UnresolvedMonkey) resolvedMonkeys := make(map[string]Monkey) for scanner.Scan() { line := scanner.Text() splittedLine := strings.Split(line, " ") if len(splittedLine) == 2 { number, _ := strconv.Atoi(splittedLine[1]) if splittedLine[0][:4] == "humn" { resolvedMonkeys[splittedLine[0][:4]] = &Human{splittedLine[0][:4], number} } else { resolvedMonkeys[splittedLine[0][:4]] = &NumberMonkey{splittedLine[0][:4], number} } } else { unresolvedMonkeys[StringCouple{splittedLine[1], splittedLine[3]}] = UnresolvedMonkey{splittedLine[0][:4], splittedLine[2]} } } for len(unresolvedMonkeys) > 0 { for monkeyCouple := range(unresolvedMonkeys) { monkey1, ok1 := resolvedMonkeys[monkeyCouple.left] monkey2, ok2 := resolvedMonkeys[monkeyCouple.right] if ok1 && ok2 { if rootIsEquality && unresolvedMonkeys[monkeyCouple].name == "root" { resolvedMonkeys[unresolvedMonkeys[monkeyCouple].name] = &EqualityMonkey{unresolvedMonkeys[monkeyCouple].name, monkey1, monkey2} } else { switch unresolvedMonkeys[monkeyCouple].symbol { case "+": resolvedMonkeys[unresolvedMonkeys[monkeyCouple].name] = &PlusMonkey{unresolvedMonkeys[monkeyCouple].name, monkey1, monkey2} case "-": resolvedMonkeys[unresolvedMonkeys[monkeyCouple].name] = &MinusMonkey{unresolvedMonkeys[monkeyCouple].name, monkey1, monkey2} case "*": resolvedMonkeys[unresolvedMonkeys[monkeyCouple].name] = &TimesMonkey{unresolvedMonkeys[monkeyCouple].name, monkey1, monkey2} case "/": resolvedMonkeys[unresolvedMonkeys[monkeyCouple].name] = &DivisionMonkey{unresolvedMonkeys[monkeyCouple].name, monkey1, monkey2} } } delete(unresolvedMonkeys, monkeyCouple) } } } return resolvedMonkeys["root"] }