Skip to content

Instantly share code, notes, and snippets.

@kaustubh-karkare
Last active August 23, 2025 06:06
Show Gist options
  • Select an option

  • Save kaustubh-karkare/266c088f67fa0c216fe4464cf0def065 to your computer and use it in GitHub Desktop.

Select an option

Save kaustubh-karkare/266c088f67fa0c216fe4464cf0def065 to your computer and use it in GitHub Desktop.
Factorio / Library
local json = require("dkjson")
local data = require("space-age-data") -- https://wiki.factorio.com/Data.raw
local indent = " "
function to_json(item)
return json.encode(item, { indent = true })
end
function onlyx(items)
-- assert(#items == 1, string.format('Multiple items found in %s', to_json(items)))
for _, value in pairs(items) do
return value
end
end
function create_config(input)
productivity_module_bonus = input.productivity_module_bonus or 0
local category_to_productivity = {
["default"] = 4 * productivity_module_bonus,
["smelting"] = 2 * productivity_module_bonus,
["chemistry-or-cryogenics"] = (input.use_cryogenic_plant and 8 or 3) * productivity_module_bonus,
["chemistry"] = 3 * productivity_module_bonus,
["electronics"] = (input.use_em_plant and 5 or 4) * productivity_module_bonus,
["electronics-with-fluid"] = (input.use_em_plant and 5 or 4) * productivity_module_bonus,
["advanced-crafting"] = 4 * productivity_module_bonus,
["crafting-with-fluid"] = 4 * productivity_module_bonus,
["crafting"] = 4 * productivity_module_bonus,
["metallurgy"] = 4 * productivity_module_bonus,
}
local skipped_recipes = {}
local default_skipped_recipes = {} -- {"casting-iron-stick"}
for _, recipe_name in pairs(input.skipped_recipes or default_skipped_recipes) do
assert(data['recipe'][recipe_name] ~= nil, string.format('Recipe %s not found', recipe_name))
skipped_recipes[recipe_name] = true
end
local preferred_recipes = {}
if input.use_foundry then
for recipe_name, item in pairs(data['recipe']) do
if item.category ~= "metallurgy" then
-- not relevant for foundry
elseif string.find(recipe_name, "lava") then
-- not relevant for nauvis
elseif skipped_recipes[recipe_name] then
-- less efficient than casting to plates and then assembling into sticks.
-- in contrast, "casting-copper-cable" is less efficient at this stage.
else
preferred_recipes[onlyx(item.results).name] = recipe_name
end
end
end
return {
name = input.name,
category_to_productivity = category_to_productivity,
preferred_recipes = preferred_recipes,
use_foundry = input.use_foundry,
use_em_plant = input.use_em_plant,
productivity_bonus = input.productivity_bonus or {},
}
end
function create_result()
return {raw_items = {}, intermediate_products = {}}
end
function get_productivity(item_name, recipe, config)
local category = recipe.category or "default"
if recipe.allow_productivity and config.category_to_productivity[category] == nil then -- debugging
print(to_json(recipe))
end
local productivity = 1 -- base
if recipe.allow_productivity then -- modules
productivity = productivity + config.category_to_productivity[category] or 0
end
if config.use_foundry and recipe.category == "metallurgy" then -- buildings
productivity = productivity + 0.5
elseif config.use_em_plant and (recipe.category == "electronics" or recipe.category == "electronics-with-fluid") then
productivity = productivity + 0.5
end
productivity = productivity + (config.productivity_bonus[item_name] or 0) -- research
return productivity
end
function explore(item_name, item_amount, config, prefix, result)
local recipe_name = config.preferred_recipes[item_name] or item_name
local recipe = data['recipe'][recipe_name]
local productivity = recipe and get_productivity(item_name, recipe, config)
-- local preferred_suffix = (item_name ~= recipe_name and " using " .. recipe_name or "")
-- local productivity_suffix = productivity and string.format(" (productivity=%.2f)", productivity) or ""
-- print(string.format("%s%s (%.3f)%s%s", prefix, item_name, item_amount, preferred_suffix, productivity_suffix))
if recipe == nul then
result.raw_items[item_name] = (result.raw_items[item_name] or 0.0) + item_amount
return
else
result.intermediate_products[item_name] = (result.intermediate_products[item_name] or 0.0) + item_amount
end
local recipe_amount = onlyx(recipe.results).amount
for _, ingredient in ipairs(recipe.ingredients) do
ingredient_amount = ingredient.amount * item_amount / recipe_amount / productivity
explore(ingredient.name, ingredient_amount, config, prefix .. indent, result)
end
end
function print_table_divider(column_widths)
io.write("+")
for _, column_width in ipairs(column_widths) do
io.write(string.rep("-", column_width+2) .. "+")
end
io.write("\n")
end
function print_table(table_cols, table_rows)
local column_widths = {}
for index, column_name in ipairs(table_cols) do
column_widths[index] = #tostring(column_name)
for _, table_row in ipairs(table_rows) do
value = tostring(table_row[column_name])
column_widths[index] = math.max(column_widths[index], #value)
end
end
print_table_divider(column_widths)
io.write("|")
for index, column_name in ipairs(table_cols) do
prefix_length = column_widths[index] - #tostring(column_name)
io.write(string.rep(" ", prefix_length + 1) .. column_name .. " |")
end
io.write("\n")
print_table_divider(column_widths)
for _, table_row in ipairs(table_rows) do
io.write("|")
for index, column_name in ipairs(table_cols) do
value = tostring(table_row[column_name])
prefix_length = column_widths[index] - #value
io.write(string.rep(" ", prefix_length + 1) .. value .. " |")
end
io.write("\n")
end
print_table_divider(column_widths)
end
return {
to_json = to_json,
onlyx = onlyx,
create_config = create_config,
create_result = create_result,
explore = explore,
print_table = print_table,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment