Brilliant script, thank you!
works ingame too, and i added a "time" entry in the return table, which should be 100% correct
call it by rawingredients(recipe)
when you call it on "nuclear fuel reprocessing" it returns the entry ERROR_INFINITE_LOOP = "used-up-uranium-fuel-cell", otherwise it works like a charm
Code: Select all
local exclude = {
["used-up-uranium-fuel-cell"] = true,
}
function rawingredients(recipe)
ret = getRawIngredients(recipe,exclude)
ret.time = (ret.time or 0)+recipe.energy
return ret
end
function getIngredients(recipe)
local ingredients = {}
for i,ingredient in pairs(recipe.ingredients) do
if (ingredient.name and ingredient.amount) then
ingredients[ingredient.name] = ingredient.amount
elseif (ingredient[1] and ingredient[2]) then
ingredients[ingredient[1]] = ingredient[2]
end
end
return ingredients
end
function getProducts(recipe)
local products = {}
if (recipe.products) then
for i,product in pairs(recipe.products) do
if (product.name and product.amount) then
products[product.name] = product.amount
end
end
elseif (recipe.products) then
local amount = 1
if (recipe.result_count) then
amount = recipe.result_count
end
products[recipe.products] = amount
end
return products
end
function getRecipes(item)
local recipes = {}
for i,recipe in pairs(game.recipe_prototypes) do
local products = getProducts(recipe)
for product,amount in pairs(products) do
if (product == item) then
table.insert(recipes, recipe)
end
end
end
return recipes
end
function getRawIngredients(recipe,excluded)
local raw_ingredients = {
["time"] = 0,}
for name,amount in pairs(getIngredients(recipe)) do
-- Do not use an item as its own ingredient
if (excluded[name]) then
return {ERROR_INFINITE_LOOP = name}
end
local excluded_ingredients = {[name] = true}
for k,v in pairs(excluded) do
excluded_ingredients[k] = true
end
-- Recursively find the sub-ingredients for each ingredient
-- There might be more than one recipe to choose from
local subrecipes = {}
local loop_error = nil
for i,subrecipe in pairs(getRecipes(name)) do
local subingredients = getRawIngredients(subrecipe, excluded_ingredients)
if (subingredients.ERROR_INFINITE_LOOP) then
loop_error = subingredients.ERROR_INFINITE_LOOP
else
local value = 0
for subproduct,subamount in pairs(getProducts(subrecipe)) do
value = value + subamount
end
local divisor = 0
for subingredient,subamount in pairs(subingredients) do
divisor = divisor + subamount
end
if (divisor == 0) then divisor = 1 end
table.insert(subrecipes, {recipe = subrecipe, ingredients = subingredients, value = value / divisor})
end
end
if (#subrecipes == 0) then
if (loop_error and loop_error ~= name) then
-- This branch of the recipe tree is invalid
return {ERROR_INFINITE_LOOP = loop_error}
else
-- This is a raw resource
if (raw_ingredients[name]) then
raw_ingredients[name] = raw_ingredients[name] + amount
else
raw_ingredients[name] = amount
end
end
else
-- Pick the cheapest recipe
local best_recipe = nil
local best_value = 0
for i,subrecipe in pairs(subrecipes) do
if (best_value < subrecipe.value) then
best_value = subrecipe.value
best_recipe = subrecipe
end
end
local multiple = 0
for subname,subamount in pairs(getProducts(best_recipe.recipe)) do
multiple = multiple + subamount
end
for subname,subamount in pairs(best_recipe.ingredients) do
if (raw_ingredients[subname]) then
raw_ingredients[subname] = raw_ingredients[subname] + amount * subamount / multiple
else
raw_ingredients[subname] = amount * subamount / multiple
end
end
raw_ingredients["time"] = raw_ingredients["time"] + (best_recipe.recipe.energy or 1)*amount/multiple
end
end
return raw_ingredients
end
Edit: NEW VERSION!
- Now calculates the time based on the most energy-efficient crafting machine
- Also provides information about the energy required
- Breaks down liquids into crude oil
- Takes into account probabilities of products (doesn't work well with covarex enrichment process - so 1 fuel cell will cost 200 uranium ore)
Code: Select all
exclude = {
["used-up-uranium-fuel-cell"] = true,
}
function rawingredients(recipe) -- call this function
if not global.best_machine then
global.best_machine = {}
for name, proto in pairs(game.entity_prototypes) do
if proto.crafting_categories and proto.name ~= "player" and proto.name ~= "escape-pod-assembler" and proto.energy_usage and proto.energy_usage > 0 then
for cat, _ in pairs(proto.crafting_categories) do
if not global.best_machine[cat] then
global.best_machine[cat]={speed = proto.crafting_speed or 1, energy = (proto.energy_usage or 1), name = name}
elseif (proto.crafting_speed or 1)/(proto.energy_usage or 1) > global.best_machine[cat].speed / global.best_machine[cat].energy then
global.best_machine[cat]={speed = proto.crafting_speed or 1, energy = (proto.energy_usage or 1), name = name}
end
end
end
end
end
ret = getRawIngredients(recipe,exclude)
ret.time = (ret.time or 0)+(recipe.energy or 1)/global.best_machine[recipe.category].speed
--ret.time = (ret.time or 0)+(recipe.energy or 1)
ret.energy = (ret.energy or 0)+(recipe.energy or 1)/global.best_machine[recipe.category].speed*global.best_machine[recipe.category].energy
return ret
end
function getIngredients(recipe)
local ingredients = {}
for i,ingredient in pairs(recipe.ingredients) do
if (ingredient.name and ingredient.amount) then
ingredients[ingredient.name] = ingredient.amount
elseif (ingredient[1] and ingredient[2]) then
ingredients[ingredient[1]] = ingredient[2]
end
end
return ingredients
end
function getProducts(recipe)
local products = {}
if (recipe.products) then
for i,product in pairs(recipe.products) do
if (product.name and product.amount) then
products[product.name] = product.amount
elseif product.amount_min and product.amount_max then
products[product.name] = (product.amount_min+product.amount_max)/2 * (product.probability or 1)
end
end
end
return products
end
function getRecipes(item)
local recipes = {}
for i,recipe in pairs(game.recipe_prototypes) do
if i ~="coal-liquefaction" and not (string.sub(item,-7) ~= "-barrel" and string.sub(i,-7) == "-barrel" and (string.sub(i,1,5) == "fill-" or string.sub(i,1,6) == "empty-")) then
local products = getProducts(recipe)
for product,amount in pairs(products) do
if (product == item) then
table.insert(recipes, recipe)
end
end
end
end
return recipes
end
function getRawIngredients(recipe,excluded)
local raw_ingredients = {
["time"] = 0,
["energy"] = 0}
for name,amount in pairs(getIngredients(recipe)) do
-- Do not use an item as its own ingredient
if (excluded[name]) then
return {ERROR_INFINITE_LOOP = name}
end
local excluded_ingredients = {[name] = true}
for k,v in pairs(excluded) do
excluded_ingredients[k] = true
end
-- Recursively find the sub-ingredients for each ingredient
-- There might be more than one recipe to choose from
local subrecipes = {}
local loop_error = nil
for i,subrecipe in pairs(getRecipes(name)) do
local subingredients = getRawIngredients(subrecipe, excluded_ingredients)
if (subingredients.ERROR_INFINITE_LOOP) then
loop_error = subingredients.ERROR_INFINITE_LOOP
else
local value = 0
for subproduct,subamount in pairs(getProducts(subrecipe)) do
value = value + subamount
end
local divisor = 0
for subingredient,subamount in pairs(subingredients) do
if subingredient ~= "intermediates" then
divisor = divisor + subamount
end
end
if (divisor == 0) then divisor = 1 end
table.insert(subrecipes, {recipe = subrecipe, ingredients = subingredients, value = value / divisor})
end
end
if (#subrecipes == 0) then
if (loop_error and loop_error ~= name) then
-- This branch of the recipe tree is invalid
return {ERROR_INFINITE_LOOP = loop_error}
else
-- This is a raw resource
if (raw_ingredients[name]) then
raw_ingredients[name] = raw_ingredients[name] + amount
else
raw_ingredients[name] = amount
end
end
else
-- Pick the cheapest recipe
local best_recipe = nil
local best_value = 0
for i,subrecipe in pairs(subrecipes) do
if (best_value < subrecipe.value) then
best_value = subrecipe.value
best_recipe = subrecipe
end
end
local multiple = 0
for subname,subamount in pairs(getProducts(best_recipe.recipe)) do
multiple = multiple + subamount
end
if not raw_ingredients["intermediates"] then raw_ingredients["intermediates"] = {} end
for subname,subamount in pairs(best_recipe.ingredients) do
if subname == "intermediates" then
for a, b in pairs(subamount) do
raw_ingredients["intermediates"][a] = (raw_ingredients["intermediates"][a] or 0)+b*amount/multiple
end
elseif (raw_ingredients[subname]) then
raw_ingredients[subname] = raw_ingredients[subname] + amount * subamount / multiple
else
raw_ingredients[subname] = amount * subamount / multiple
end
end
for a, b in pairs(getProducts(best_recipe.recipe)) do
raw_ingredients["intermediates"][a] = (raw_ingredients["intermediates"][a] or 0)+b*amount/multiple
end
raw_ingredients["time"] = raw_ingredients["time"] + (best_recipe.recipe.energy or 1)/global.best_machine[best_recipe.recipe.category].speed*amount/multiple
--raw_ingredients["time"] = raw_ingredients["time"] + (best_recipe.recipe.energy or 1)*amount/multiple
raw_ingredients["energy"] = raw_ingredients["energy"] + (best_recipe.recipe.energy or 1)/global.best_machine[best_recipe.recipe.category].speed*global.best_machine[best_recipe.recipe.category].energy*amount/multiple
end
end
return raw_ingredients
end