package main import ( "flag" "fmt" "log" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) type Monkey struct { name string yelled bool value int m1, m2 *Monkey op string name1, name2 string } var monkeys map[string]*Monkey var verbose bool var humanval int func main() { partOne := flag.Bool("1", false, "run exercise part1, (default: part2)") verboseFlag := flag.Bool("v", false, "verbose: print extra info") humanFlag := flag.Int("h", -1, "set value of \"humn\" monkey, return delta") flag.Parse() verbose = *verboseFlag humanval = *humanFlag lines := h.StdinToStringSlice() parse(lines) var result int if *partOne { vprint("Running Part 1") result = part1(lines) } else { vprint("Running Part 2") result = part2(lines) } fmt.Println(result) } func part1(lines []string) int { return listenCached(monkeys["root"]) } func part2(lines []string) int { root := monkeys["root"] delta := Monkey{name: "delta", m1: root.m1, m2: root.m2, name1: root.name1, name2: root.name2, op: "-"} if humanval != -1 { human := monkeys["humn"] human.yelled = true human.value = humanval return listenCached(&delta) } // We find the value of human making delta == 0 by dichotomy return humanZeroing(&delta) } func parse(lines []string) { var name, name1, op, name2 string var num int monkeys = make(map[string]*Monkey, 0) // 1st pass, gather names for _, line := range lines { if n, _ := fmt.Sscanf(line, "%s %d", &name, &num); n == 2 { m := Monkey{name: name[:len(name)-1], value: num, yelled: true} monkeys[name[:len(name)-1]] = &m } else if n, _ = fmt.Sscanf(line, "%s %s %s %s", &name, &name1, &op, &name2); n == 4 { m := Monkey{name: name[:len(name)-1], op: op, name1: name1, name2: name2} monkeys[name[:len(name)-1]] = &m } else { log.Fatalf("Parse error: %s\n", line) } } // 2nd pass, fill the sub-monkey fields for _, m := range monkeys { if !m.yelled { m.m1 = monkeys[m.name1] m.m2 = monkeys[m.name2] } } } // Resolve and cache result. Faster but cannot be reused func listenCached(m *Monkey) int { if m.yelled { return m.value } n1 := listenCached(m.m1) n2 := listenCached(m.m2) m.yelled = true switch m.op { case "+": m.value = n1 + n2 case "-": m.value = n1 - n2 case "*": m.value = n1 * n2 case "/": m.value = n1 / n2 } return m.value } func listen(m *Monkey) int { if m.yelled { return m.value } n1 := listen(m.m1) n2 := listen(m.m2) switch m.op { case "+": return n1 + n2 case "-": return n1 - n2 case "*": return n1 * n2 case "/": return n1 / n2 } return 0 } func listenFor(d, h *Monkey, value int) int { h.value = value return listen(d) } func humanZeroing(delta *Monkey) int { human := monkeys["humn"] human.yelled = true h1 := 0 h2 := 1000 for { d1 := listenFor(delta, human, h1) d2 := listenFor(delta, human, h2) d2d1 := d2 - d1 vprintf("%d, %d --> %d, %d (diff: %d)\n", h1, h2, d1, d2, d2d1) if d1 == 0 { return humanSmallest(delta, human, h1) } // found if d2 == 0 { return humanSmallest(delta, human, h2) } // found if d2d1 == 0 { log.Fatalf("d2 == d1\n") } // Linear regresion on h to minimize d if absInt(d1) < absInt(d2) { // h1 was closest to the goal: interpolate from it h := h1 - (d1*(h2-h1))/d2d1 vprintf("==> h = %d (h1=%d + %d)\n", h, h1, -(d1*(h2-h1))/d2d1) h2 = h } else { // h2 was closest to the goal: interpolate from it h := h2 - (d2*(h1-h2))/(-d2d1) vprintf("==> h = %d (h2=%d + %d)\n", h, h2, -(d2*(h1-h2))/(-d2d1)) h1 = h } } } func humanSmallest(delta, human *Monkey, hval int) int { h := hval for listenFor(delta, human, h-1) == 0 { h-- } return h } func absInt(i int) int { if i < 0 { return -i } return i } func vprintf(f string, vals ...interface{}) { if !verbose { return } fmt.Printf(f, vals...) } func vprint(txt string) { if !verbose { return } fmt.Println(txt) }