OpenML - Machine Learning

So I think I got it but after 6 it gets the same error:
Code:

local OpenML = require(script.OpenML)
local Ragdoll = require(script.Ragdoll)

local States = { "Distance", "Health", "Angle", "DirX", "DirZ", "Random", }
local Actions = { "Idle", "TurnLeft", "TurnRight", "MoveX", "MoveZ", "Jump" }

local turnSpeed = 20

local ActivationFunction = OpenML.ActivationFunctions.TanH

game:GetService("Players").PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		character:AddTag("Character")
		local ragdoll = Ragdoll.new(character)
		for _, v in next, character:GetDescendants() do
			if v:IsA("BasePart") then
				v.CollisionGroup = "Fighter"
			end
		end
		character.Humanoid.BreakJointsOnDeath = false
		character.Humanoid.Died:Once(function()
			character:RemoveTag("Character")
			character.Humanoid.AutoRotate = false
			ragdoll:Toggle(true)
			character.HumanoidRootPart:SetNetworkOwner(player)
			for _, v in next, character:GetDescendants() do
				if v:IsA("BasePart") then
					v.CollisionGroup = "Ragdoll"
				end
			end
		end)
	end)
end)

local tpCooldown = {}
for _, v in next, workspace.Box:GetChildren() do
	if v:IsA("BasePart") then
		v.Touched:Connect(function(otherPart)
			if otherPart.Parent:HasTag("Character") then
				if tpCooldown[otherPart.Parent.Name] then return end
				otherPart.Parent:MoveTo(workspace.SpawnLocation.Position) --otherPart.Parent:PivotTo(script.Rig:GetPivot() * CFrame.new((math.random() * 2 - 1) * 35, 0, (math.random() * 2 - 1) * 35))
				tpCooldown[otherPart.Parent.Name] = true
				task.wait(1)
				tpCooldown[otherPart.Parent.Name] = false
			end
			print(tpCooldown)
		end)
	end
end

local function perform360Raycast(humanoidRootPart)
	local debug = true
	local rayLength = 50 -- Set the length of the rays
	local numberOfRays = 36 -- Number of rays to cast (360 degrees / 10 degrees per ray)

	local detectedObjects = {}

	for i = 0, numberOfRays - 1 do
		local angle = math.rad(i * (360 / numberOfRays))
		local direction = CFrame.fromEulerAnglesYXZ(0, angle, 0).LookVector

		local rayOrigin = humanoidRootPart.Position
		local rayDirection = direction * rayLength

		local raycastParams = RaycastParams.new()
		raycastParams.FilterType = Enum.RaycastFilterType.Exclude
		raycastParams.FilterDescendantsInstances = {humanoidRootPart.Parent, humanoidRootPart.Parent:FindFirstChild("RaycastParts")} -- Ignore the NPC itself
		raycastParams.RespectCanCollide = true

		local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

		if raycastResult then
			table.insert(detectedObjects, {
				Part = raycastResult.Instance,
				Position = raycastResult.Position,
				Normal = raycastResult.Normal,
				Distance = raycastResult.Distance,
				Direction = direction -- Store the direction of the ray
			})
		end

		if debug then
			for _, object in humanoidRootPart.Parent.RaycastParts:GetChildren() do
				if object:IsA("Part") then
					if object.Name == tostring(i) then
						object:Destroy()
					end
				end
			end

			if raycastResult then
				local debugPart = Instance.new("Part", humanoidRootPart.Parent.RaycastParts)
				debugPart.Name = i
				debugPart.Anchored = true
				debugPart.CanCollide = false
				debugPart.CanQuery = false
				debugPart.CanTouch = false
				debugPart.Size = Vector3.one * 1.5
				debugPart.Position = raycastResult.Position
				debugPart.Material = raycastResult.Material
				debugPart.BrickColor = BrickColor.new("Lime green")
				debugPart.Shape = Enum.PartType.Ball
			end
		end
		
		if i < 6 then
			break
		end
	end

	return detectedObjects
end

for i = 1, 1 do
	task.spawn(function()

		while task.wait() do
			local rig = script.Rig:Clone()
			rig.Name = "AI-"..i
			rig:AddTag("Character")
			rig:MoveTo(workspace.SpawnLocation.Position) --rig:PivotTo(rig:GetPivot() * CFrame.new((math.random() * 2 - 1) * 35, 0, (math.random() * 2 - 1) * 35))
			rig.Parent = workspace

			local humanoid = rig.Humanoid
			local humanoidRootPart = rig.HumanoidRootPart

			local ragdoll = Ragdoll.new(rig)

			humanoid.Died:Once(function()
				rig:RemoveTag("Character")
				rig.HumanoidRootPart.AngularVelocity.Enabled = false
				--rig.ClassicSword.SwordScript.Enabled = false
				ragdoll:Toggle(true)
				for _, v in next, rig:GetDescendants() do
					if v:IsA("BasePart") then
						v.CollisionGroup = "Ragdoll"
					end
				end
				task.wait(3)
				rig:Destroy()
			end)

			local function avoidObstacles(humanoidRootPart, detectedObjects)
				local avoidanceThreshold = 10 -- Distance threshold for obstacle avoidance
				local avoidanceForce = Vector3.zero -- Initialize the avoidance force

				for _, obj in ipairs(detectedObjects) do
					if obj.Distance < avoidanceThreshold then
						-- Calculate the opposite direction to move away from the obstacle
						local avoidanceDirection = -obj.Direction / obj.Distance
						avoidanceForce = avoidanceForce + avoidanceDirection
					end
				end

				if avoidanceForce.Magnitude > 0 then
					avoidanceForce = avoidanceForce.Unit -- Normalize the force vector
					humanoid:Move(avoidanceForce) -- Move the NPC in the avoidance direction
				end
			end

			-- Integrate this into your existing loop
			while true and rig.Parent and humanoid.Health > 0 do
				local detectedObjects = perform360Raycast(humanoidRootPart)
				
				-- Process detected objects here for obstacle avoidance
				avoidObstacles(humanoidRootPart, detectedObjects)
				
				local NeuralNetwork = setmetatable(OpenML.Resources.MLP.new({ #States + #detectedObjects, 10, 10, 10, #Actions }, function()
					return (math.random() * 2 - 1) * 1.5
				end), { __index = OpenML.Algorithms.Propagator })
												
				local DQL = OpenML.Resources.DQL.new()

				local startingLearningRate = 0.00015
				local maxLearningRate = 0.0015

				local learningRateMultiplier = 1.00015

				local learningRate = startingLearningRate

				DQL.OnForwardPropagation = function(state) return NeuralNetwork:ForwardPropagation(state, ActivationFunction) end
				DQL.OnBackPropagation = function(activations, target) return NeuralNetwork:BackPropagation(activations, target, { ActivationFunction = ActivationFunction, LearningRate = learningRate }) end

				-- Continue with your existing code
				learningRate = math.max(math.min(learningRate * learningRateMultiplier, maxLearningRate) % maxLearningRate, startingLearningRate)

				local deltaTime = task.wait()
				local characters = game:GetService("CollectionService"):GetTagged("Character")
				local character, closestDistance = nil, math.huge

				for _, v in next, characters do
					--if v == rig or not v:FindFirstChild("HumanoidRootPart") then continue end

					local distance = (rig:GetPivot().Position - v.HumanoidRootPart.Position).Magnitude
					if distance < closestDistance then
						character, closestDistance = v, distance
					end
				end

				if character then
					local positionDifference: Vector3 = (character:GetPivot().Position - workspace.Goal.Position) --(character.HumanoidRootPart.Position - humanoidRootPart.Position)
					local distance = positionDifference.Magnitude / 50

					local angleDot = positionDifference.Unit:Dot(humanoidRootPart.CFrame.LookVector)

					local unitPositionDifferenceXZ = (positionDifference * Vector3.new(1, 0, 1)).Unit
					local environment = { distance, 1 - humanoid.Health/humanoid.MaxHealth, angleDot, unitPositionDifferenceXZ.X, unitPositionDifferenceXZ.Z, math.random() }
					for _, object in detectedObjects do
						if #detectedObjects < 6 then
							if object.Part.Parent == workspace then
								table.insert(environment, tonumber(1))
							elseif object.Part.Parent == character or object.Part.Parent == character:FindFirstChild("RaycastParts") then
								table.insert(environment, tonumber(-1))
							elseif object.Part.Parent == nil then
								table.insert(environment, tonumber(0))
							end
						end
						--print(environment)
					end
					
					print(environment)
					
					local activations = NeuralNetwork:ForwardPropagation(environment, ActivationFunction)
					local lastActivationLayer = activations[#activations]

					humanoidRootPart:PivotTo(
						humanoidRootPart:GetPivot() 
							* CFrame.fromEulerAnglesXYZ(0, deltaTime * turnSpeed * lastActivationLayer[2], 0)
							* CFrame.fromEulerAnglesXYZ(0, -deltaTime * turnSpeed * lastActivationLayer[3], 0)
					)

					humanoidRootPart.AssemblyAngularVelocity = --.AngularVelocity.AngularVelocity = 
						Vector3.yAxis * (turnSpeed * lastActivationLayer[2] - turnSpeed * lastActivationLayer[3])

					if lastActivationLayer[1] < 0.5 then
						humanoid:Move(Vector3.new(lastActivationLayer[4] - 0.5, 0, lastActivationLayer[5] - 0.5).Unit)
					end

					if lastActivationLayer[6] > 0.5 then
						humanoid.Jump = true
					end

					DQL:Learn{
						State = environment,
						Action = 1,
						Reward = (distance > 3 and lastActivationLayer[1] > 0.5 and 1 or -1)
					}

					local rightAngleDot = positionDifference.Unit:Dot(humanoidRootPart.CFrame.RightVector)

					DQL:Learn{
						State = environment,
						Action = 2,
						Reward = ((-rightAngleDot) + 0.05)
					}; DQL:Learn{
						State = environment,
						Action = 3,
						Reward = ((rightAngleDot) + 0.05)
					};

					DQL:Learn{
						State = environment,
						Action = 4,
						Reward = (positionDifference.Unit.X - humanoid.MoveDirection.X) - 0.5,
					}; DQL:Learn{
						State = environment,
						Action = 5,
						Reward = (positionDifference.Unit.Z - humanoid.MoveDirection.Z) - 0.5,
					};

					DQL:Learn{
						State = environment,
						Action = 6,
						Reward = lastActivationLayer[6] > 0.5 and humanoid.Health/humanoid.MaxHealth < 0.2 and 0.5 or -0.5,
					};
				end
			end
		
			task.wait(3)
		end
	end)
end
1 Like

what does this run with? did you put torch? also what neural networks does it support, CNN’s would be fire :skull:

1 Like

What does it run with? wdym, it’s just a custom neural network module thats easy to use. No it doesnt use torch or any of its similarities. It does support CNN.

can you help me? I don’t know how to add more inputs.

1 Like

If your code your stating that the inputs will have lets say 8, but your giving it 6. You need to edit the parameters of :ForwardPropagation or :Learn function whichever is erroring. If you’re sending a table with the length of 6 but it needs 8 then it’ll cause the error. You need to correctly send the “environment” variable you made correctly. Its not a module problem, its something in your code that you added, you tell it your gonna send this amount (8) but actually send this amount (6) those numbers are examples.

1 Like

well I’m going to be honest its not really inserting the write amount of numbers when its being inserted.

1 Like

Nice library, I like how you featured compressing and showed how to save the network.
I also like the opp usage since it’s similar to my workflow, great work overall going to try making something cool out of it.

When performing a 360 raycast you have to account what if it doesnt find anything in that 360 raycast, like what if you raycast 10 times and only 6 of them hit? now you have 6 inputs when it expected 10. That’s the problem with your code. Your not accounting all the possible outcomes. So if it doesnt hit anything it shoukd still inserts in the table but put like the max distance if your raycasts depend on distances

Really? Holy Im impressed this needs more love bro hop off roblox start earning money I know some people who work at ai companies bro u need to join them. I thought you used torch which is basically PyTorch but in lua, it came before PyTorch

Hi. Is the Developer still Answering questions?

So i want to make a car that reaches a target(chasing car) and i would like it to be able to avoid walls as well as to know to reverse/unfree when getting stuck or walled. Is it posible to do it with this machine learning code?

I mean I tried that on an NPC.

Im looking for car since i need for car

yes it is possible, you can use either DQL (Deep Q-Learning) or just give it training data and it’ll learn from regular back propagation.

You could use the Deep Learning Cars I made as a base. I say it’s deprecated though the way its made. It could be made way better.

Hi! Can you publish this to GitHub as source code, so that I can use this in my workflow? That would be much appreciated.