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.

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)

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.

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

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.

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.

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.

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