Making a Script Solve a Math Problem

After my other devforum post (which still hasn’t been solved), I’ve decided to work on something else. It is an educational horror game like Advanced Education with Viktor Strobovski (which is inspired by Baldi’s Basics) and I’ve made a successful, but REALLY expansive, math problem generator which doesn’t even have parenthesis yet. I wanted to have the pluses colored green, minuses orange, multiplications yellow and divisions blue but that’s a whole other problem.

Okay, what do I want? I want the “AnswerChecker” script to solve the equation and when the player clicks the “next” button, it checks the textbox to see if it’s the same thing. If it is (for now) print “yes” and otherwise, “no”. But… I don’t know how to do this. I’ve tried, yet nothing. I even resorted to Assistant, Bard and ChatGPT. Here’s the madness I call the script for the problem generator:

--CONFIG--
local autoChangeOnDifficulty = true --The below values change automatically depending on the difficulty selected.
local autoChangeOnCollected = true  --Questions get harder the more notebooks you collect.

--VALUES--
local division = true
local multiplication = true
local subtraction = true
local addition = true
local parenthesis = true

local longDivision = true
local longMultiplication = true

local numOfNums = 2        --How many numbers are in the equation
local numOfChars = 2       --How many digits are in the current number
local numOfSymbols = 1     --How many symbols are in the equation
local numOfSymbolsDone = 0 --How many symbols have been generated in the equation

local equation = ""
local nextSymbol = ""
local prevSymbol = ""

--FUNCTIONS--
function autoChangeOnCollectedFnc(difficulty, collectedNotebooks)
	--Making the equations harder depending on how many notebooks are collected
	if difficulty == "Weakling" and collectedNotebooks == 1 then
		numOfNums = math.random(1,2)
		numOfChars = math.random(1,2)
	elseif difficulty == "Weakling" and collectedNotebooks >= 5 then
		multiplication = true
		longMultiplication = false
	elseif difficulty == "Easy" and collectedNotebooks >= 6 then
		division = true
	elseif difficulty == "Harder" and collectedNotebooks >= 5 then
		longDivision = true
		longMultiplication = true
	elseif difficulty == "Braniac" and collectedNotebooks >= 3 then
		longDivision = true
	end
	
	--The main part of the function: making equations harder depending on how many notebooks you have
	if collectedNotebooks > 0 then
		if difficulty == "Weakling" or difficulty == "Easy" then
			if collectedNotebooks / 7 >= 1 then --To avoid errors
				numOfChars += math.random(1,math.round(collectedNotebooks / 7))
				numOfNums += math.random(1,math.round(collectedNotebooks / 7))
			end
		elseif difficulty == "Harder" or difficulty == "Braniac" then
			if collectedNotebooks / 4 >= 1 then --To avoid errors
				numOfNums += math.random(1,math.round(collectedNotebooks / 4))
			end
		end
	end
end

function checkDifficulty(difficulty)
	if difficulty == "Weakling" then
		numOfChars = math.random(1,1)
		numOfNums = math.random(1,1)
		division = false
		parenthesis = false
		longMultiplication = false
	elseif difficulty == "Easy" then
		numOfChars = math.random(1,2)
		numOfNums = math.random(1,2)
		division = false
		longMultiplication = false
	elseif difficulty == "Harder" then
		--These chances are to make it more random.. I guess..
		local charsChance = math.random(1,2)
		local charsChance2 = math.random(1,2)
		if charsChance2 >= charsChance then
			numOfChars = charsChance
		end
		numOfNums = math.random(1,2)
		longDivision = false
		longMultiplication = false
	elseif difficulty == "Braniac" then
		--These chances are to make it more random.. I guess..
		local charsChance = math.random(2,2)
		local charsChance2 = math.random(2,2)
		if charsChance2 >= charsChance then
			numOfChars = charsChance
		end
		numOfNums = math.random(2,3)
		longDivision = false
	end
end

function generateDigit(difficulty, collectedNotebooks)
	--Generating negative numbers
	local negativeAllowed = true
	if difficulty == "Weakling" and collectedNotebooks < 9 then
		negativeAllowed = false
	elseif difficulty == "Easy" and collectedNotebooks < 8 then
		negativeAllowed = false
	elseif difficulty == "Harder" and collectedNotebooks < 5 then
		negativeAllowed = false
	elseif difficulty == "Braniac" and collectedNotebooks < 2 then
		negativeAllowed = false
	end
	
	if negativeAllowed == true then
		local chance1 = math.random(1,5)
		local chance2 = math.random(1,5)
		if chance2 < chance1 then
			negativeAllowed = false
		else
			equation = equation .. "-"
		end
	end
	
	--Generating the digits
	local newNumber = ""
	
	if nextSymbol ~= "*" and longMultiplication == false or prevSymbol ~= "*" and longMultiplication == false then
		for i = 1, numOfChars do
			local num = math.random(0,9)
			newNumber = newNumber .. tostring(num)
		end
	else --Making sure longMultiplication being deactivated results in 1 digit per number
		numOfChars = 1
		local num = math.random(0,9)
		newNumber = newNumber .. tostring(num)
	end

	--Make sure the first digits AREN'T 0 (ex: 069; 0022; 0007)
	if numOfChars > 1 then
		repeat
			if tonumber(string.sub(newNumber, 1,1)) == 0 then
				local num = math.random(0,9)
				newNumber = tostring(num)..string.sub(newNumber, 2)
			end
		until tonumber(string.sub(newNumber, 1,1)) ~= 0
	end
	
	--Add the number to the equation
	equation = equation .. newNumber
	
	--Reroll number of digits to avoid always having the same amount of digits (ex: 7+7+7, 863-638)
	if difficulty == "Weakling" then
		numOfChars = math.random(1,1)
	elseif difficulty == "Easy" then
		numOfChars = math.random(1,2)
	elseif difficulty == "Harder" then
		local charsChance = math.random(1,2)
		local charsChance2 = math.random(1,2)
		if charsChance2 >= charsChance then
			numOfChars = charsChance
		end
	elseif difficulty == "Braniac" then
		local charsChance = math.random(2,2)
		local charsChance2 = math.random(2,2)
		if charsChance2 >= charsChance then
			numOfChars = charsChance
		end
	end
end

function generateSymbol()
	if numOfSymbolsDone ~= numOfSymbols then --This is to avoid stuff like 5+=, 3-4/=
		prevSymbol = nextSymbol
		
		local chance --Chance of which symbol is chosen.
		if multiplication == true then
			chance = math.random(1,3)
		elseif division == true then
			chance = math.random(1,4)
		end
		
		--Setting the next symbol
		if chance == 1 then
			nextSymbol = "+"
		elseif chance == 2 then
			nextSymbol = "-"
		elseif chance == 3 then
			nextSymbol = "*"
		elseif chance == 4 then
			nextSymbol = "/"
		end
		
		--This is to also avoid stuff like 5+=, 3-4/=
		numOfSymbolsDone += 1
	else
		--If it is the last symbol, always write an equals sign
		nextSymbol = "="
	end
end

--MAIN--
function generateEquation(difficulty, collectedNotebooks)
	if autoChangeOnDifficulty == true then
		checkDifficulty(difficulty)
	end
	if autoChangeOnCollected == true then
		autoChangeOnCollectedFnc(difficulty, collectedNotebooks)
	end
	
	numOfSymbols = numOfNums - 1
	numOfSymbolsDone = 0
	
	--GENERATING THE EQUATION--
	equation = ""
	nextSymbol = ""
	for i = 1, numOfNums do
		generateSymbol()
		generateDigit(difficulty, collectedNotebooks)
		equation = equation .. nextSymbol
	end
	
	--Finally, write down the equation! WE'RE DONE!!!
	script.Parent.Equation.Text = equation
end

game.ReplicatedStorage.Remotes.EnterEquationizer.OnClientEvent:Connect(function()
	generateEquation(game.ReplicatedStorage.Values.Difficulty.Value, game.ReplicatedStorage.Values.CollectedNotebooks.Value)
end)

script.Parent.Next.MouseButton1Click:Connect(function()
	generateEquation(game.ReplicatedStorage.Values.Difficulty.Value, game.ReplicatedStorage.Values.CollectedNotebooks.Value)
end)

(don’t worry, there’s a method to my madness) and here’s a screenshot, just for comprehension’s sake:


Any help on this really appreciated. I’m gonna continue trying to do this.

you can use loadstring()* for that:

local eq = "9 + 10"
local ans = loadstring("return " .. eq)()
print(ans) --> 21

*you have to set LoadStringEnabled in ServerScriptService to true first
**loadstring() only works in server scripts

1 Like

you can use a LR parser to parse them into AST and interpret them. If you want a example I can show it.

after reading the problem more, instead of using a parser you can generate a random AST for it and then solve it by interpreting it, check if its same with the answer. Or just use loadstring("return "…expr)

1 Like

It keeps giving me this error:
attempt to call a nil value
And if I keep the problem generator script as a local one, the answer is always 19 because the text on the TextBox is being changed locally.

1 Like

then generate the problems on the server and send it to the player, once the player enters his answer send that answer to the server and verify it.

can you show the script and the line where it throws the error?

1 Like

What is an AST exactly? Also, the loadstring("return “…eq”) doesn’t work.

1 Like

With the server script in the ScreenGui, doesn’t it still generate on the server? By the way, the error is happening on the “local ans” line.

Just tested removing the empty parenthesis and it just prints nil now. Also, I saved the problem to a value in ReplicatedStorage yet still doesn’t work.

Nevermind, it’s because I included the “=” in the equation so it broke. I just needed to remove it from the equation. Now it works fine. Thanks!

There seems to be some inconsistencies with equations that have negative numbers, like “-7–4*-0*2±4=”, giving -7 on Roblox but -11 on a scientific calculator. Maybe Roblox confuses minuses with negatives.

Nevermind, I just needed to add parenthesis to surround the negative number like this: 7+(-8)*2

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.