192 lines
3.9 KiB
Go
192 lines
3.9 KiB
Go
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)
|
|
}
|