Lua already has Torch, the original library that was ported to Python and is now known as PyTorch. My old and new module are still child’s play compared to what is on there.
Is there a way to make neural networks in roblox work with the driveseat .throttle and .steer property? How would this go in to a script?
Use the outputs the AI returns and scale and set them, with a output value being a higher value.
What power limitations does roblox have with CNNs?
They tend to require a ton of computations and roblox lua is not very good at doing a lot of math very quickly.
Why does it have to do with roblox lua, and not with the machines hardware?
Because you do not run code directly on the hardware. To use the hardware, you need an interface that lets you use it. This interface is machine code. The problem is that machine code is impossible to write complex programs with due to it’s lack of readability (binary) and lack of features, so we use programming languages. These languages interpret our code and turn it into machine code. Every language is different and has different features, but all of them have the same result. Lua is an old and rather unsupported scripting language. Due to lack of support and frequent updates, Lua does not tend to be very fast at all. Lua 5.1, which we’re using, is a snail compared to newer modified versions like LuaJIT. Anywho, point is, with the way everything works, Lua is the bottleneck here. Due it’s low speed, you cannot do the same things that you could do in Java for example.
Lua is actually compiled to bytecode (not machine code), so it can be safely sandboxed. Roblox also forked from Lua earlier this year with “Luau”, which has a new VM with a lot of great optimizations that can compete with LuaJIT in some specific cases.
If course it’s still quite a bit slower than machine code. If we identify the fundamental bottlenecks we hit with AI implementations in Lua, perhaps new API’s can be introduced to speed up training/runtime calculations.
When giving a raycast as an input, assuming that the ray’s direction relative to the bot is always the same, the distance to the object the ray hit is used as an input, capped at a reasonable max distance.
For example, if the raycast hit something 134 studs away and the maximum is 1000, 134 is the input. If it hit something 1458 studs away, 1000 is the input instead.
Dude this is awesome! Recently got into python and started making a NN in that language, I wondered, imagine an AI in roblox. Truly interesting, this really shows how far roblox has come as a game engine.
Im getting this error
[18:52:08.923 - Workspace.KNNLibrary:350: Wrong Arg#1: Network must be a string or StringValue.]
[18:52:08.924 - Script ‘Workspace.KNNLibrary’, Line 350 - function loadNet]
[18:52:08.924 - Script ‘Workspace.KNNLibrary’, Line 495 - function runGenNet]
[18:52:08.925 - Script ‘Workspace.Script’, Line 115]
Heres the script
local module=require(workspace.KNNLibrary) --Activating and getting the library
local nets = module.createGenNet(workspace.Networks,20,9,2,6,5,“LeakyReLU”) --Creating a generation of 20 networks with 2 inputs, 2 hidden layers, 2 nodes per hidden layer,
–1 output node, and with ReLU as the activation function
local vis = module.getVisual(nets[1])
vis.Parent = game.StarterGuilocal function visualizeRay(ray, name, E)
if game.Workspace:FindFirstChild(name) then
game.Workspace[name]:Destroy()
end
local hitpart, hitposition = workspace:FindPartOnRay(ray)
local p = Instance.new(“Part”, workspace) – creating a part to visualize the ray)
p.Name = name
p.Position = hitposition
p.Anchored = true
p.CanCollide = false
p.Size = Vector3.new(1,1,1)
p.Shape = “Ball”
p.BrickColor = BrickColor.new(“Really red”)
return (p.Position - E.Position).Magnitude – magnitude from Origin part and the part that visualizes the ray(i could use a better system)
end
function getray(part, food)
local forwardray = Ray.new(part.Position, part.CFrame.LookVector * 500)
local backwardray =Ray.new(part.Position, part.CFrame.LookVector * 500*(-1))
local rightray = Ray.new(part.Position, part.CFrame.RightVector * 500)
local leftray = Ray.new(part.Position, (part.CFrame.RightVector * 500)-1)
local forwardleftray = Ray.new(part.Position, ((part.CFrame.LookVector * 500) -(part.CFrame.RightVector * 500)-1))
local forwardrightray = Ray.new(part.Position, ((part.CFrame.LookVector * 500) -(part.CFrame.RightVector * 500)1))
local backwardleftray = Ray.new(part.Position, ((part.CFrame.LookVector * 500(-1)) -(part.CFrame.RightVector500)-1))
local backwardrightray = Ray.new(part.Position, ((part.CFrame.LookVector * 500*(-1)) -(part.CFrame.RightVector*500)*1))
local foodray = Ray.new(part.Position, (food.Position - part.Position).unit)
local f = visualizeRay(forwardray, “forwardray”, part)
local r = visualizeRay(rightray, “rightray”, part)local l = visualizeRay(leftray, “leftray”, part)
local fl = visualizeRay(forwardleftray, “forwardleftray”, part)
local fr = visualizeRay(forwardrightray, “forwardrightray”, part)
local bl = visualizeRay(backwardleftray, “backwardleftray”, part)
local br = visualizeRay(backwardrightray, “backwardrightray”,part)
local b = visualizeRay(backwardray, “backwardray”, part)
local fd = visualizeRay(foodray, “foodray”, part)
return {f, r, l, fl, fr, bl, br, b, fd} – table for input
endfor g=1, 1000 do --We will run for 1 million generations, which is basically forever
local i = 0–print("Generation: "…g) --Print the generation number
local scores = {} --Array storing the scores for every network
local step = 8 --step value used for lowering the resolution of the scoring. Lower step means higher resolution but far slower
local neg = 1
local tim=tick()
for z=1,#nets do --Looping through every network in the generation
local network = module.loadNet(nets[z]) --We load up the StringValue of the network in question
local wins=0
local part = game.ReplicatedStorage.Jaco:Clone()
local food = game.ReplicatedStorage.Food:Clone()
local previousWin = game.ServerScriptService.PreviousGen
part.Parent = workspace
food.Parent = workspace
game.ServerScriptService.StopGeneration.Value = false
game.ServerScriptService.Touching.Value = false
game.ServerScriptService.Food.Value = false
while game.ServerScriptService.StopGeneration.Value ~= true do
local answer=module.forwardNet(network,getray(part, food)) --We run the network and get a response from it
local ra = unpack(answer)
print(g, nets[z])
i = i + 1if ra >= 0.9 then part.CFrame = CFrame.new(part.CFrame.X+1, part.CFrame.Y, part.CFrame.Z) elseif ra > 0.6 and ra < 0.9 then part.CFrame = CFrame.new(part.CFrame.X, part.CFrame.Y, part.CFrame.Z-1) elseif ra < 0.6 and ra > 0.4 then part.CFrame = CFrame.new(part.CFrame.X-1, part.CFrame.Y, part.CFrame.Z) elseif ra < 0.4 then part.CFrame = CFrame.new(part.CFrame.X, part.CFrame.Y, part.CFrame.Z+1) end if game.ServerScriptService.Food.Value == true then wins = wins + 1 game.ServerScriptService.Food.Value = false i=i-50 end if tick()-tim>=0.1 then --To avoid timeouts, we add a wait() every tenth of a second the script runs. This is to make sure even potato processors will work tim=tick() --If you want a little more speed over the pretty effects, switch the 0.1 into a 0.5 end if game.ServerScriptService.StopGeneration.Value == true then i = 0 break end if game.ServerScriptService.Touching.Value == true then previousWin.Value = g wins = wins - 1 wait() i = 0 break end if i == 100 then wait() wins = wins - 1 i = 0 break end game.StarterGui.bruhmoment.Wins.Text = "Wins: "..wins game.StarterGui.bruhmoment.Moves.Text = "Move Left: ".. 100-i game.StarterGui.bruhmoment.Answer.Text = ra game.StarterGui.bruhmoment.TextLabel.Text = "Generation: "..g.." Part: "..z wait() table.insert(scores,wins) --With the score calculated, we insert it into the scores array end local best = module.runGenNet(nets, scores) module.updateVisualActive(network,vis, getray(part, food)) part:Destroy() food:Destroy()
end
–With all of the networks scored, we run the next generation
–For every new generation, we show how the best network looks
–for a split second
table.sort(scores) --Purely for demo purposes, I sort the scores to find the best one
–We calculate the best possible score using our step value and print out the success rate %
print(“Best network success rate: “…scores[#scores]/(800/step)^2*(100)…”%”)
end
Same, i dont know what causes it
i would just use tostring the table i guess
For whatever reason, the network provided isn’t a string or a StringValue. My guess is that somewhere the entry is replaced with a table version of a network. Its a bit difficult to check the code with the format its in so could you post the entire script in a code bubble?
With all the problems caused by my odd design choices and poor coding practices when I made this abomination of a library, I am really glad that I am working on a new one that will deprecate this one. If you would like to keep your sanity, please, don’t look into the code behind this thing.
I tried using tostring but its not working
sry for the bad scripting format
here’s the script
local module=require(workspace.KNNLibrary) --Activating and getting the library
local nets = module.createGenNet(workspace.Networks,20,9,2,6,5,"LeakyReLU") --Creating a generation of 20 networks with 2 inputs, 2 hidden layers, 2 nodes per hidden layer,
--1 output node, and with ReLU as the activation function
local vis = module.getVisual(nets[1])
vis.Parent = game.StarterGui
local function visualizeRay(ray, name, E)
--Visualize Ray
if game.Workspace:FindFirstChild(name) then
game.Workspace[name]:Destroy()
end
local hitpart, hitposition = workspace:FindPartOnRay(ray)
local p = Instance.new("Part", workspace) -- creating a part to visualize the ray)
p.Name = name
p.Position = hitposition
p.Anchored = true
p.CanCollide = false
p.Size = Vector3.new(1,1,1)
p.Shape = "Ball"
p.BrickColor = BrickColor.new("Really red")
return (p.Position - E.Position).Magnitude -- magnitude from Origin part and the part that visualizes the ray(i could use a better system)
end
function getray(part, food)
--Raycasting for inputs
local forwardray = Ray.new(part.Position, part.CFrame.LookVector * 500)
local backwardray =Ray.new(part.Position, part.CFrame.LookVector * 500*(-1))
local rightray = Ray.new(part.Position, part.CFrame.RightVector * 500)
local leftray = Ray.new(part.Position, (part.CFrame.RightVector * 500)*-1)
local forwardleftray = Ray.new(part.Position, ((part.CFrame.LookVector * 500) -(part.CFrame.RightVector * 500)*-1))
local forwardrightray = Ray.new(part.Position, ((part.CFrame.LookVector * 500) -(part.CFrame.RightVector * 500)*1))
local backwardleftray = Ray.new(part.Position, ((part.CFrame.LookVector * 500*(-1)) -(part.CFrame.RightVector*500)*-1))
local backwardrightray = Ray.new(part.Position, ((part.CFrame.LookVector * 500*(-1)) -(part.CFrame.RightVector*500)*1))
local foodray = Ray.new(part.Position, (food.Position - part.Position).unit)
local f = visualizeRay(forwardray, "forwardray", part)
local r = visualizeRay(rightray, "rightray", part)
local l = visualizeRay(leftray, "leftray", part)
local fl = visualizeRay(forwardleftray, "forwardleftray", part)
local fr = visualizeRay(forwardrightray, "forwardrightray", part)
local bl = visualizeRay(backwardleftray, "backwardleftray", part)
local br = visualizeRay(backwardrightray, "backwardrightray",part)
local b = visualizeRay(backwardray, "backwardray", part)
local fd = visualizeRay(foodray, "foodray", part)
return {f, r, l, fl, fr, bl, br, b, fd} -- table for input
end
for g=1, 1000 do --We will run for 1 million generations, which is basically forever
local i = 0
--print("Generation: "..g) --Print the generation number
local scores = {} --Array storing the scores for every network
local step = 8 --step value used for lowering the resolution of the scoring. Lower step means higher resolution but far slower
local neg = 1
local tim=tick()
for z=1,#nets do --Looping through every network in the generation
local network = module.loadNet(nets[z]) --We load up the StringValue of the network in question
local wins=0
local part = game.ReplicatedStorage.Jaco:Clone()
local food = game.ReplicatedStorage.Food:Clone()
local previousWin = game.ServerScriptService.PreviousGen
part.Parent = workspace
food.Parent = workspace
game.ServerScriptService.StopGeneration.Value = false
game.ServerScriptService.Touching.Value = false
game.ServerScriptService.Food.Value = false
while game.ServerScriptService.StopGeneration.Value ~= true do
local answer=module.forwardNet(network,getray(part, food))
local ra = unpack(answer)
print(g, nets[z])
i = i + 1
if ra >= 0.9 then
part.CFrame = CFrame.new(part.CFrame.X+1, part.CFrame.Y, part.CFrame.Z)
elseif ra > 0.6 and ra < 0.9 then
part.CFrame = CFrame.new(part.CFrame.X, part.CFrame.Y, part.CFrame.Z-1)
elseif ra < 0.6 and ra > 0.4 then
part.CFrame = CFrame.new(part.CFrame.X-1, part.CFrame.Y, part.CFrame.Z)
elseif ra < 0.4 then
part.CFrame = CFrame.new(part.CFrame.X, part.CFrame.Y, part.CFrame.Z+1)
end
if game.ServerScriptService.Food.Value == true then
wins = wins + 1
game.ServerScriptService.Food.Value = false
i=i-50
end
if tick()-tim>=0.1 then --To avoid timeouts, we add a wait() every tenth of a second the script runs. This is to make sure even potato processors will work
tim=tick() --If you want a little more speed over the pretty effects, switch the 0.1 into a 0.5
end
if game.ServerScriptService.StopGeneration.Value == true then
i = 0
break
end
if game.ServerScriptService.Touching.Value == true then
previousWin.Value = g
wins = wins - 1
wait()
i = 0
break
end
if i == 100 then
wait()
wins = wins - 1
i = 0
break
end
game.StarterGui.bruhmoment.Wins.Text = "Wins: "..wins
game.StarterGui.bruhmoment.Moves.Text = "Move Left: ".. 100-i
game.StarterGui.bruhmoment.Answer.Text = ra
game.StarterGui.bruhmoment.TextLabel.Text = "Generation: "..g.." Part: "..z
wait()
table.insert(scores,wins) --With the score calculated, we insert it into the scores array
end
local best = module.runGenNet(nets, scores)
module.updateVisualActive(network,vis, getray(part, food))
part:Destroy()
food:Destroy()
end
--With all of the networks scored, we run the next generation
--For every new generation, we show how the best network looks
--for a split second
table.sort(scores) --Purely for demo purposes, I sort the scores to find the best one
--We calculate the best possible score using our step value and print out the success rate %
print("Best network success rate: "..scores[#scores]/(800/step)^2*(100).."%")
end
btw my things are named VERY badly. i could reformat and make it easier to understand
so the network not being a tostring i went into the module, and the line in the module the error was is 495, and then i did
print(tostring(nets[best[1]]))
and it printed nil, so somewhere i mustve messed it up where it would return nil
SOLVED
Here is a script I had a problem with, but not anymore. its supposed to make a part, “Goobee,” play tag with you; this is basically the finished product for those who want to see it or use it. (If you see something wrong, feel free to tell me!)
-- CONFIGURATIONS --------------------------------------------------------------------------------
local totalGenerations, step, netsPerGen = 10000, 16, 60 --Generation Amount, Generation length, Generation Size
local inputs, layers, nodes, outputs = 3,2,6,3 --Nerual network configurations
local yourName = "qwert347" --Player name for tagging
-- VARIABLES --------------------------------------------------------------------------------
local module=require(workspace.KNNLibrary) --Activating and getting the library
local nets = module.createGenNet(workspace.Networks,netsPerGen, inputs, layers, nodes, outputs,"LeakyReLU")
local vis = module.getVisual(nets[1]); vis.Parent = game.StarterGui
local player = game.Players.PlayerAdded:Wait()
local character = player.CharacterAdded:Wait()
local rootPart = character.HumanoidRootPart
--- CODE -------------------------------------------------------------------------------------
--Generations
for g=1, totalGenerations do
--Setup Gen
print("Generation: "..g)
local scores = {}
local updater = Instance.new("NumberValue",script)
local botFolder = Instance.new("Folder",workspace)
botFolder.Name = "BotFolder"
updater.Name = "Updater"
--Networks
for z=1,#nets do
print("Network: "..z)
--Setup Net
local network = module.loadNet(nets[z])
local bot = game:GetService("ReplicatedStorage").Goobee:Clone()
bot.Parent = botFolder
--Scoring player hits
local wins = 10000
bot.Touched:Connect(function(hit)
if hit.Parent.Name == yourName then wins=wins+100 end
end)
--Updater setup
script.Updater.Changed:Connect(function()
if script.Updater.Value ~= 0 then
--Normal update
local cac = math.tanh(rootPart.CFrame:ToObjectSpace(bot.CFrame).X/50)
local cac2 = math.tanh(rootPart.CFrame:ToObjectSpace(bot.CFrame).Z/50)
local cac3 = bot.Velocity.Magnitude/50
local answer=module.forwardNet(network,{cac,cac2,cac3})
bot.Speed.Force = Vector3.new(0,0,(answer[1]-.5)*10000)
bot.Steering.AngularVelocity = Vector3.new(0,answer[2]-answer[3],0)
else --Final update
wins= wins - math.floor((rootPart.Position-bot.Position).Magnitude + .5) --scoring distance from player
table.insert(scores,wins) -- logging score
end
end)
end
--Updater Running
for d=0,399,step do updater.Value=d; wait(.2) end
--Ending Gen
botFolder:Destroy()
updater.Value = 0
updater:Destroy()
local best = module.runGenNet(nets,scores)
module.updateVisualState(nets[best],vis)
print("Best network success: " ,module.hardCode(nets[best]))
end