Needing help with weird probability math involving sizes of parts

Hello everyone,

So, I’m running a script that will suck up parts from around the world, using a “Destruction Level” to determine

  1. the size limit of the parts it can pick up given its current destruction level (done)
  2. how common it is for the script to give the a-ok for running the checks based on current destruction level (done)
    And lastly, 3) make smaller parts have a higher chance than larger parts to be picked up.

The last one is what I’m having issues with.

For the first listed, I hard-coded this formula to check if the size is okay. In this check, the “sizemagnitude” is the magnitude of the size (x + y + z of size all added together), and the “DestructionLevel” is the level of destruction, which can range to 1-1000. When a part is absorbed, their size magnitude is added to the DestructionLevel value.

sizemagnitude <= math.max(DestructionLevel.Value/(DestructionLevel.Value/(DestructionLevel.Value/3)),6.5)
-- given this, 58/(58/(58/3)) = at MOST around 18 size magnitude for the check to give the a-ok.

Now, this works well. However, after this and doing the second one, my issue is with making the chance of actually eating the part be HIGHER if the part is SMALLER, whilst also using the Destruction level in the equation as to not completely shut out bigger parts from being picked up. Think of the script as sort of a black hole functionality–smaller parts will be much more frequently picked up than larger parts.

I’m completely lost on this after trying for about an hour. I’ve tried subtracting the sizemagnitude from the DestructionLevel, I’ve tried switching that equation around, and I’ve tried doing a few inequalities, though not many. Any help with figuring this out would be appreciated.

2 Likes

i would love to help, could you add more of your script so i can understand the question better. thanks!

1 Like

Why don’t you use Size.Magnitude?

1 Like

Of course. Here’s the whole loop for it:

	while true do
		local eatenpart = false
		wait(math.random(.3,1.2)/7.5) --This is the wait for the while loop; don't worry about this
		local randomize = math.random(1,math.max(10+(DestructionLevel.Value*.5),10)) --this is for deciding if it should try to pick up a part or not
		if randomize == 1 then
			for i,v in pairs(ListofParts) do if v:IsA("Part") or v:IsA("UnionOperation") or v:IsA("MeshPart") or v:IsA("WedgePart") or v:IsA("CornerWedgePart") then -- ListofParts is a table of every part, union, etc in workspace.
					local sizemagnitude = (v.Size.X+v.Size.Y+v.Size.Z)
					if sizemagnitude <= math.max(DestructionLevel.Value/(DestructionLevel.Value/(DestructionLevel.Value/3)),6.5) and v.Parent and not v.Parent:FindFirstChildOfClass("Humanoid") and not v:FindFirstAncestor(Character.Name) then
						if math.random(1,3) == 1 and eatenpart == false then -- This is the chance I'd like to have be higher if the part is a SMALLER part.
							eatenpart = true
							EatPart(v); eatenpart = true -- Bool to ensure that the script does not eat multiple parts at once
							print("SUCCESS!")
						end
					end
				end end
		else print(randomize)
		end
	end
1 Like

Unrelated, but I didn’t know exactly what Size.Magnitude was, so I went with what I knew I wanted.

Surely you could just factor in size.Magnitude when doing

if math.random(1,3) == 1

which is what the other person said first. Am i missing something?

However, after this and doing the second one, my issue is with making the chance of actually eating the part be HIGHER if the part is SMALLER…

Perhaps the use of a rational function would help here? The behavior you are describing with regards to smaller objects being sucked in more forcefully works well for functions whose variables are in the denominator, i.e. functions of the class f(x) = 1/x.

image

You can see that as the value of the x-axis approaches zero, the value of the y-axis approaches infinity…

…which leads to the fun part: if you change the value of x by subtracting a value from it, it will shift the function to the right, which means larger values on the x-axis will result in a higher y-axis:

image

The above function has the same domain as the first example: the function is now just f(x) = 1/(x-3). You can use this fact to factor in your destruction level: a higher level will shift the function more to the right, which means objects of a larger magnitude (the value of the x-axis) will feel the pick-up more (the value of the y-axis).

If you normalize the output of this function on [0, 1], you could compare that value to a random check to determine if an object should be destroyed.

2 Likes

Yes; in the original post, I stated that I’d like to have a higher chance of smaller parts, but also factoring the DestructionLevel to not completely stop larger parts from being moved.

Thanks. I’ll do some testing with this.

1 Like

that makes perfect sense, beautiful response

1 Like

I just wanted to point out that you are using the wrong formula for magnitude.

If you want the right magnitude you can do the square root of the sum of the sizes squared:
local sizemagnitude = math.sqrt(v.Size.X^2+v.Size.Y^2+v.Size.Z^2)
which is the same as v.Size.Magnitude

1 Like

Hey - I forgot to mention that you will have to account for negative values when you factor in the shifting of the function. 1/x has some interesting behavior when the denominator takes on negative values.

1 Like

After a bit of experimenting, this is definitely on the right track, but I’m stumped on how I would implement the destruction value with the size magnitude.

More on this, I found that this formula works for returning a higher decimal for smaller parts–the only issue being how to plug it into a probability/math.random function.

1/(sizemagnitude-(DestructionLevel/100)) --Returns a lower decimal the higher that the size magnitude (x) is

Simply use the destruction level as an argument for the function that determines the probability a part of magnitude m will be destroyed.

I have created a Desmos graph that illustrates this here.

In code:

function check_probability(magnitude, dfactor)
  return 1/math.max(1, magnitude-dfactor)
end

-- When a part is being prompted for destruction
local rnd = Random.new()

local magnitude = part.Size.Magnitude
local destruction_factor = 10
local prob = check_probability(magnitude, destruction_factor)

-- If the random roll on (0, 1) is less than the output of check_probability, destroy the part
-- and add its magnitude to destruction_factor
if rnd:NextNumber() <= prob then 
  destruction_factor += magnitude
  part:Destroy() 
end
1 Like

This is nice, but where does the (0,1) come from? Is that the Random.new?

Otherwise, this looks great. I’ll make some modifications to this for suiting what I’m looking for.

1 Like

The (0, 1) is just the domain (it means 0 to 1 exclusive) of what the call to a Random object’s NextNumber method returns. In other words, it is always going to give you a random number between but not including 0 and 1.

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