2022 Complete
This commit is contained in:
@@ -1,143 +1,191 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"log"
|
||||
|
||||
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
||||
)
|
||||
|
||||
var output []string
|
||||
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() {
|
||||
inp := h.StdinToStringSlice()
|
||||
part1(inp)
|
||||
part2(inp)
|
||||
}
|
||||
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)
|
||||
|
||||
func showLog() {
|
||||
fmt.Println(h.CLEAR_SCREEN)
|
||||
fmt.Println(strings.Join(output, "\n"))
|
||||
}
|
||||
|
||||
func setLog(v ...string) {
|
||||
output = []string{}
|
||||
log(v...)
|
||||
}
|
||||
func log(v ...string) {
|
||||
output = append(output, v...)
|
||||
}
|
||||
|
||||
func loadMonkeys(inp []string) map[string]*Monkey {
|
||||
monkeys := make(map[string]*Monkey)
|
||||
for _, v := range inp {
|
||||
if len(v) == 0 {
|
||||
break
|
||||
}
|
||||
m := NewMonkey(strings.Split(v, ": ")...)
|
||||
monkeys[m.name] = m
|
||||
}
|
||||
for _, v := range monkeys {
|
||||
v.init(monkeys, 1)
|
||||
}
|
||||
return monkeys
|
||||
}
|
||||
|
||||
func part1(inp []string) {
|
||||
monkeys := loadMonkeys(inp)
|
||||
log("# Part 1", fmt.Sprintf("%d", monkeys["root"].Value()))
|
||||
showLog()
|
||||
}
|
||||
|
||||
func part2(inp []string) {
|
||||
fmt.Println()
|
||||
monkeys := loadMonkeys(inp)
|
||||
|
||||
m1 := monkeys["root"].inp1 // <- Human is in here
|
||||
m2 := monkeys["root"].inp2 // <- We need to make it match this
|
||||
monkeys["humn"].Value = func() int { return 0 }
|
||||
fmt.Println(m1.Value(), m2.Value())
|
||||
monkeys["humn"].Value = func() int { return 1000 }
|
||||
fmt.Println(m1.Value(), m2.Value())
|
||||
//v := monkeys["root"].Value()
|
||||
|
||||
/*
|
||||
i := 0
|
||||
var lastV int
|
||||
for {
|
||||
monkeys["humn"].Value = func() int {
|
||||
return i
|
||||
}
|
||||
v := monkeys["root"].Value()
|
||||
if v == 0 {
|
||||
output = append(output[:1], []string{"# Part 2", fmt.Sprintf("Found: %d", i)}...)
|
||||
showLog()
|
||||
return
|
||||
}
|
||||
output = append(output[:3], []string{
|
||||
"# Part 2",
|
||||
fmt.Sprintf("Scanning... [ humn: %d ]", i),
|
||||
fmt.Sprintf("CURR: %d =? 0", v),
|
||||
fmt.Sprintf("LAST: %d", lastV),
|
||||
}...)
|
||||
setLog(output...)
|
||||
showLog()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
type Operation int
|
||||
|
||||
const (
|
||||
opAdd = iota
|
||||
opSub
|
||||
opMlt
|
||||
opDiv
|
||||
)
|
||||
|
||||
type Monkey struct {
|
||||
name string
|
||||
op Operation
|
||||
inp1, inp2 *Monkey
|
||||
rawValue string
|
||||
Value func() int
|
||||
}
|
||||
|
||||
func NewMonkey(inp ...string) *Monkey { return &Monkey{name: inp[0], rawValue: inp[1]} }
|
||||
func (m *Monkey) init(monkeys map[string]*Monkey, part int) {
|
||||
pts := strings.Fields(m.rawValue)
|
||||
if len(pts) == 1 {
|
||||
m.Value = func() int { return h.Atoi(pts[0]) }
|
||||
var result int
|
||||
if *partOne {
|
||||
vprint("Running Part 1")
|
||||
result = part1(lines)
|
||||
} else {
|
||||
if part == 2 && m.name == "root" {
|
||||
pts[1] = "-"
|
||||
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)
|
||||
}
|
||||
m.inp1, m.inp2 = monkeys[pts[0]], monkeys[pts[2]]
|
||||
switch pts[1] {
|
||||
case "+":
|
||||
m.Value = func() int {
|
||||
return m.inp1.Value() + m.inp2.Value()
|
||||
}
|
||||
case "-":
|
||||
m.Value = func() int {
|
||||
return m.inp1.Value() - m.inp2.Value()
|
||||
}
|
||||
case "*":
|
||||
m.Value = func() int {
|
||||
return m.inp1.Value() * m.inp2.Value()
|
||||
}
|
||||
case "/":
|
||||
m.Value = func() int {
|
||||
return m.inp1.Value() / m.inp2.Value()
|
||||
}
|
||||
case "=":
|
||||
m.Value = func() int {
|
||||
if m.inp1.Value() == m.inp2.Value() {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
|
Reference in New Issue
Block a user