Cheat tool for Scholar Bear's Math Puzzles

#1KhatharrPosted 10/23/2012 1:57:58 PM
I had written a nice little solver for Rydia's Mathemagical Solutions (FF4 DS) some time ago. That script was nice and short, using recursion to iterate through the number set and test different possible operations until it found the first one that worked. When I tried to use it for Scholar Bear's math game in Invidia I found that it usually spat out a pretty sub-optimal solution, so I sat down and monkied with it for a little while and then just threw it aside and created this brute-force solver, which works for both since they're so similar.
Basically this script tests every possible solution for a given set of 4 numbers and then spits out the one that will give the highest score in Scholar Bear's game. I've only tested it on Win XP using Ruby 1.9.3p194, but there's nothing really complex here, so it should work on any Ruby ver or platform provided there's stdin and stdout. (If you're a Rubyist you should be able to port it very easily.)

If the score that gets spit out by the script isn't high enough for you then just skip the set on the DS and try the next one. The skip penalty is something like 3 points, but you should be able to produce really high scores pretty regularly.

Anyway, I thought I'd share it with everyone. I don't post on GF often, so I'm sort of surprised there's no quote or code tags or whatever, but I guess I'll try posting it like this and then also upload it and post a link.

Here's the link:

http://www.filedropper.com/fholbearmath

I'll post the script itself in the next post.
#2Khatharr(Topic Creator)Posted 10/23/2012 1:59:13 PM
#############################################
# FINAL FANTASY - THE 4 HEROES OF LIGHT
# Ruby script to produce the best solutions to Scholar Bear's Number Puzzles
# Version 1.0(final) - written by Khatharr (Aug 2012)
#############################################
# No rights reserved - enjoy!
#############################################

#global array with all operator set permutations
$operationSets = []
operators = ['+','-','*','/']
for a in operators
for b in operators
for c in operators
$operationSets.push([a,b,c])
end
end
end

#takes an input value array and spits out a 2D array with all combinations
def composeCombinations(ary)
a = ary[0]; b = ary[1]; c = ary[2]; d = ary[3]
set = [
[a,b,c,d], [a,b,d,c], [a,c,b,d], [a,c,d,b], [a,d,b,c], [a,d,c,b],
[b,a,c,d], [b,a,d,c], [b,c,a,d], [b,c,d,a], [b,d,a,c], [b,d,c,a],
[c,b,a,d], [c,b,d,a], [c,a,b,d], [c,a,d,b], [c,d,b,a], [c,d,a,b],
[d,b,c,a], [d,b,a,c], [d,c,b,a], [d,c,a,b], [d,a,b,c], [d,a,c,b]
]
return set
end

#determines the score of the operation ((A [op] B) [op] C) [op] D
def scoreSequential(valAry, opAry)
vals = valAry.clone
ops = opAry.clone
sult = vals.shift
retval = 0
3.times do
return 0 if (ops[0] == '/') and (sult % vals[0] != 0)
eval "retval += (sult = #{sult} #{ops.shift} #{vals.shift})"
end
return retval if sult == 10
return 0
end

#determines the score of the operation (A [op] B) [op] (C [op] D)
def scorePaired(vals, ops)
sult = []
return 0 if (ops[0] == '/') and (vals[0] % vals[1] != 0)
eval "sult[0] = vals[0] #{ops[0]} vals[1]"
return 0 if (ops[2] == '/') and (vals[2] % vals[3] != 0)
eval "sult[1] = vals[2] #{ops[2]} vals[3]"
if (ops[1] == '/')
return 0 if sult[1] == 0
return 0 if (sult[0] % sult[1] != 0)
end
eval "sult[2] = sult[0] #{ops[1]} sult[1]"
return 0 if sult[2] != 10
return (sult[0] + sult[1] + sult[2])
end

# POLAR BEARS ARE GOOD AT MATHS
def findBestSolution(ary)
results = {}
combos = composeCombinations(ary)
for combo in combos
for ops in $operationSets
score = scoreSequential(combo, ops)
results[score] = [combo.clone, ops.clone, false]
score = scorePaired(combo, ops)
results[score] = [combo.clone, ops.clone, true]
end
end
results.delete(0)
return results.max
end
#3Khatharr(Topic Creator)Posted 10/23/2012 1:59:33 PM
#returns a string explaining the winning solution
def composeResultString(winner)
#'winner' is in the format: [score, [[A,B,C,D], [x,y,z], paired]]
#score is an Integer
#A,B,C and D are Integers
#x,y and z are each one of '+', '-', '*' or '/'
#if the boolean value paired is true then:
# (A x B) y (C z D) == score
#else
# ((A x B) y C) z D == score
outstr = ""
if winner == nil
outstr << "Unsolvable set! Skip this one for some free points.\n"
else
score = winner[0]
combo = winner[1][0]
ops = winner[1][1]
asPair = winner[1][2]
outstr << "#{score} Point Solution:\n"
if asPair
valA = 0
valB = 0
opStr = "#{combo[0]} #{ops[0]} #{combo[1]}"
outstr << " > #{opStr} = #{valA = eval opStr}\n"
opStr = "#{combo[2]} #{ops[2]} #{combo[3]}"
outstr << " > #{opStr} = #{valB = eval opStr}\n"
opStr = "#{valA} #{ops[1]} #{valB}"
outstr << " > #{opStr} = #{eval opStr}\n"
else
val = combo.shift
3.times do
opStr = "#{val} #{ops.shift} #{combo.shift}"
outstr << " > #{opStr} = #{val = eval opStr}\n"
end
end
end
return outstr.chomp
end

#returns true for a valid input array or false otherwise
def arrayIsValid?(ary)
return false if ary.size != 4
return ((ary.min >= 1) and (ary.max <= 9))
end

############################################
#Beyond this point there's use of stdin and stdout. If you want to port to
#something that doesn't have them then you need to replace the I/O stuff.
#
#Basically what needs to happen for a single pass is this:
# * get 4 values from the user
# * put them in an Array as Integers
# * pass the array to arrayIsValid? and make sure it returns true
# - if it's not then the user gave you bad values, so ask them again
# * pass the array to findBestSolution which returns the best result
# * pass the result to composeResultString which returns a string
# * show the string to the user
#The rest is just window dressing.
############################################

LINE_WIDTH = 60
HR = ("-" * LINE_WIDTH)

#print intro blurb
puts HR, "Final Fantasy - The 4 Heroes of Light".center(LINE_WIDTH)
puts "Scholar Bear's Math Game".center(LINE_WIDTH), HR

loop do
ary = []
#get a valid input array
until arrayIsValid?(ary)
print "Enter comma delimited start values ['q' to quit]: "
input = gets
exit if input.downcase == "q\n"
ary = input.split(",")
#this .collect! converts the string-form numbers to integers
ary.collect! {|v| v.to_i}
end
#get the best solution
winner = findBestSolution(ary)
#put it in English
text = composeResultString(winner)
#print it
puts HR, text, HR
end
#4Khatharr(Topic Creator)Posted 10/23/2012 2:01:04 PM
That's the end of it. It took two posts, lol.

Anyway, I hope some people enjoy it.

Like the comments say, I don't reserve any kind of copyright for crap like this, so feel free to do what you like with it.

Enjoy! :)
#5reb8Posted 10/26/2012 5:09:16 PM
Thanks. It may be a while before I play this again, but I'll try this at that time.
---
Numbers don't lie, but beware of the person using them.
Politicians always get the facts so they can distort them as they please.
#6MikaZoid416Posted 10/31/2012 6:00:59 AM
....Are you a wizard? :o
---
The dumber people think you are, the more surprised they will be when you kill them.
#7Khatharr(Topic Creator)Posted 11/1/2012 4:24:16 PM
I prefer not to disclose my magical alignment preferences. ;)
#8Khatharr(Topic Creator)Posted 11/6/2012 6:27:51 PM
It occurred to me that not many people use Ruby at the consumer level, so since some folks actually responded here I figured I'd throw together a nice little windows version.

http://www.filedropper.com/scholarbearwin32

Source code is included (C++ in VS2008 project) in case you don't trust executables from random internet people (which is wise).
#9mmike265Posted 11/7/2012 4:05:15 AM
"The requested file doesn't exist. Details: HTTP/1.1 404 Not Found"

Can you upload it again, please? I couldn't download it.
---
Read my lips mercy is for wimps! There's a reason "oppose" rhymes with "dispose"... If they get in your way, kill them! - Kefka Palazzo
#10Khatharr(Topic Creator)Posted 11/7/2012 7:50:58 PM
?

I just now copy/pasted the link and it downloaded.

Are you sure you entered it correctly?