Help with Ratios

Alright, so for my game, metallurgy reactions have a certain ratio they follow, and from all present parts within the ratio, how can I get the output?

There are two inputs to this function: ratio: {[string]: number}, and resource: {[string]: number}

So for example, when the resources are 90% copper and 10% tin for example, the output would be 100% bronze (technically other side products have also been produced, but those are not important, what’s important is the sum of in = the sum of out)

But if 99% copper and 1% tin, only 10% bronze has created and the 90% of copper keeps on existing.

Now I know how to implement this with ratios of two parts, but here, the ratios can range between two and posibly even 5, so I’m really stuck on how to implement this.

2 Likes

This sounds like it would be easier to implement with absolute volumes, as you can calculate the percentages for info only.

So if you have a product requiring 9 copper, 1 tin and 2 gold (metallurgy is not my strong point !)
You could take the input volumes for each element let’s say 90 copper, 6 tin and 3 gold divide them by the requirements, then get the product amount produced using the lowest whole.
Use this as a multiplier with the requirements to get the amounts consumed and subtract from the inputs to give the remaining amounts.

local required = {Copper = 9, Tin = 1, Gold = 2}
local function smelt(inputs:{}):{}
    local products = {}
    local potential = {}
    local amount = 0
    for Elem, vol in required do
        table.insert(potential, math.floor(inputs[Elem] / vol))
    end
    amount = math.min(table.unpack(potential))
    for Elem, vol in potential do
        products[Elem] = inputs[Elem] - (required[Elem] * amount)
    end
    products["GoldenBronze"] = amount
    return products
end

(Not tested: Written on phone, you would need to adapt the table of requirements and add error checking for absent keys)

1 Like

My reaction data-base is based on percentages, so having an absolute value will not work with my current data type and with absolute values, it would make it hard to make alloys without them being a certain range of values.

1 Like

I think it’s the same principle, just multiplying out the numbers to ensure the math functions work as expected.

local Reactions = {
    Inputs = {Copper = 0.85, Tin = 0.15},
    Outputs = {Bronze = 0.95, CopperOxide = 0.05}
}

local function smelt(inputs:{}):{}
    local products = {}
    local potential = {}
    local amount = 0
    for Elem, vol in Reactions.Inputs do
        table.insert(potential, math.floor((inputs[Elem] / vol)*100))
    end
    amount = math.min(table.unpack(potential))/100
    for Elem, vol in potential do
        products[Elem] = inputs[Elem] - (Reactions.Inputs[Elem] * amount)
    end
    for Elem, vol in Reactions.Outputs do
        products[Elem] = amount * vol
    end
    return products
end

Edited to ensure all inputs are returned (in case they are not an ingredient or output

local function smelt(inputs:{}):{}
    local potential = {}
    local amount = 0
    for Elem, vol in Reactions.Inputs do
        table.insert(potential, math.floor((inputs[Elem] / vol)*100))
    end
    amount = math.min(table.unpack(potential))/100
    for Elem, vol in potential do
        inputs[Elem] -=  (Reactions.Inputs[Elem] * amount)
    end
    for Elem, vol in Reactions.Outputs do
        inputs[Elem] = amount * vol
    end
    return inputs
end
1 Like

so this assumes for example if it is 15% tin, that there would surely be 85% copper, since 15% < 85%. Also this code assums the input has all the ingredients for the alloy, which it might not.

An example edge case for this would be: (15% tin, 17% copper, 68% other materials), with tin being the smallest of the ratio, it would assume that per 15/100 parts of tin, there is 85/100 parts of copper, which here it clearly isnt.

So it would output 1 part of bronze, when the ingredients clearly aren’t all there

Not exactly.
15 / 15 = 1 (100% of tin is provided)
17 / 85 = less than 1… (Mental arithmetic not great, but this would be less
If the amount is less the amounts consumed and produced would be less.

(Got distracted whilst writing this)

1 Like

If the ratio of the ingredients are disproportionate, but still all elements are present in some magnitude, then the amount of output should be the maximum amount of the sum of elements that comply with this ratio.

Aka if 25% of A and 75% of B creates 100% E, is there is 25% of A and 75/2% of B, it would produce 50% of E and and 25/2% amount of A would still be in the mixture.

1 Like

I think that is what it does

local function smelt(inputs:{}):{}
--[[
inputs ={ copper = 0.17, tin = 0.15, other =0.68}
]]
    local potential = {}
    local amount = 0
    for Elem, vol in Reactions.Inputs do
        table.insert(potential, inputs[Elem] / vol)
    end
--[[
 potential={1, 0.2}
]]
    amount = math.min(table.unpack(potential))
--amount = 0.2
    for Elem, vol in potential do
        inputs[Elem] -=  (Reactions.Inputs[Elem] * amount)
    end
    for Elem, vol in Reactions.Outputs do
        inputs[Elem] = amount * vol
    end
    return inputs
--[[
inputs ={
  copper = 0,
  tin = 0.12,
  other = 0.68,
  bronze = 0.19,
  CopperOxide= 0.01
]]
end

Edited out the multipliers and math.floor as they are not really required if not using production units. Will also improve accuracy.

1 Like

Alright, I’ll try implementing this after I’m complete with a cross-server thingy, thanks!

image
resourced it to this, and it seems to work. nice

2 Likes

Hey, turns out there is an issue!

For this example, the reaction that happened was:
@ 950°C: 85% copper + 15% tin95% bronze + 5% copper oxide

For context, the input resource was ~50% tin, ~50% copper and a trace amount of silica.

But for some reason, the outputs are as such:
image
ISSUES:
copper is -346.9% of the element, which is imposible as its negative & above 50%

Weird…
The copper amount is actually much smaller than that as it has e-18 at the end, although it should be positive at least (like silica is e-38).
I’m thinking as the amounts are so small it is suffering from a floating point error.

You may need to force values below a certain amount to be 0

bro wants to cook on roblox too

2 Likes

BTW i’ve implemented this mechanic in a test version of my game: METALLURGIA [BETA] - Roblox

1 Like