To install `mistlecode` yourself, run `devtools::install_github('guslipkin/mistlecode')`.
Also loading: cipheR data.table dplyr purrr slider stringr tidyverse glue
2022-21: Monkey Math
This…doesn’t actually look too bad. Part 2 will probably be a nightmare.
Part 1
Let’s take a chance to use S7 for the first time! I can build on the bits I learned with S3 in 2022-11. The idea to use a vector of monkey names (index
) made it really easy to make sure I was pulling info from the right monkeys, rather than needing to iterate over the entire list. There’s probably a better approach overall with a more recursive solution, but this seems to work fine for Part 1, although I’m worried about Part 2.
Note (2023-04-23): I just updated to R 4.3 and the latest version of R7 which is now S7 and I was unable to run the previous version which used recursion due to a C-stack error. I swapped over to a while loop based version and that seems to be working fine.
monkey <- new_class(
name = "monkey",
properties = list(
name = class_character,
val = class_any,
op = class_character
)
)
new_monkey <- function(x) {
if (!is.na(suppressWarnings(as.numeric(x[2])))) {
val <- as.numeric(x[2])
op <- NA_character_
} else {
val <- NA
op <- x[2]
}
monkey(
name = x[1],
val = val,
op = op
)
}
monkeys <- lapply(dt, new_monkey)
masterMonkeys <- monkeys
index <- sapply(monkeys, \(x) x@name)
monkey_math <- function(monkeys, op) {
m1 <- monkeys[[which(index == op[1])]]@val
m2 <- monkeys[[which(index == op[3])]]@val
if (!is.na(m1) & !is.na(m2)) {
if (op[2] == "+") { m1 + m2 }
else if (op[2] == "-") { m1 - m2 }
else if (op[2] == "*") { m1 * m2 }
else if (op[2] == "/") { m1 / m2 }
} else { return(NA_integer_) }
}
m <- monkeys
breakFlag <- FALSE
while(!breakFlag) {
n <- 1
while (n <= length(monkeys)) {
if (is.na(monkeys[[n]]@val)) {
val <- str_split_1(monkeys[[n]]@op, " ")
monkeys[[n]]@val <- monkey_math(monkeys, val)
}
if (monkeys[[n]]@name == "root" & !is.na(monkeys[[n]]@val)) {
breakFlag <- TRUE
break
}
n <- n + 1
}
}
monkeys[[n]]@val
[1] 93813115694560
Part 2
I tried brute-forcing it… It took a while to fully figure out what I wanted to do, but the idea behind just making a giant equation then solving for humn
was there early on. I started by expanding the input by replacing monkeys with their value if they had one or operation if they didn’t. I then replaced all the monkeys with their values, just like in Part 1. This left me with a giant expression where the only variable was humn
. I just wasn’t sure how to actually solve equations in R so I kept trying to brute-force it, which kept on not working. I eventually found the Ryacas
package which made quick work of everything.
monkeys <- lapply(dt, new_monkey)
root_op <-
str_extract(monkeys[[which(index == "root")]]@op, "(\\+|\\-|\\*|\\/)")
monkeys[[which(index == "root")]]@op <-
gsub(paste0("\\", root_op), "=", monkeys[[which(index == "root")]]@op)
m1 <- str_split_1(monkeys[[which(index == "root")]]@op, " = ")
m2 <- m1[2]
m1 <- m1[1]
replace_monkey <- function(m) {
w <- which(index == m)
if (length(w) == 0) { return(m) }
v <- monkeys[[w]]@val
if (is.na(v)) { return(paste0("( ", monkeys[[which(index == m)]]@op, " )")) }
return(paste0("( ", v, " )"))
}
# https://stackoverflow.com/a/14838753
quotemeta <- function(string) { str_replace_all(string, "(\\W)", "\\\\\\1") }
reduce_string <- function(mm, pattern) {
r <-
str_extract_all(mm, pattern) |>
unlist() |>
sapply(\(r) {
if (grepl("h", r)) { return(r) }
else { eval(parse(text = r)) }
})
for (i in 1:length(r)) {
mm <- str_replace_all(mm, quotemeta(names(r)[i]), as.character(r[i]))
}
return(mm)
}
process_string <- function(m) {
while (any(grepl("([^(h)][a-z])", m))) {
m <-
sapply(m, \(mm) { replace_monkey(mm) }) |>
paste0(collapse = " ") |>
str_split_1(" ")
}
m <- paste0(m, collapse = "")
oldM <- 0
while (length(m) == 1 && oldM != m) {
oldM <- m
m <- reduce_string(m, "\\((\\d+|h)\\)[\\+|\\-|\\*|\\/]\\((\\d+|h)\\)")
}
return(oldM)
}
monkeys[[which(index == "humn")]]@val <- "h"
mm1 <- process_string(m1)
mm2 <- process_string(m2)
paste(
reduce_string(mm1, "\\((\\d+|h)\\)"),
"==",
reduce_string(mm2, "\\((\\d+|h)\\)")
) %>%
Ryacas::y_fn("Solve", "h") |>
Ryacas::yac_str() |>
str_extract("\\d+") |>
as.numeric()
[1] 3910938071092