#+title: Factorio Recipe Analyzer #+author: Phil Bajsicki #+PROPERTY: header-args :tangle fra.raku * Intro This is a simple Raku script which generates an analysis of each recipe into its component parts, and delivers insight into the balance and progression of a mod. The .csv files used as input are generated in Factorio, by loading a new game with only ~base~ and your chosen mod enabled, and running the following Lua code: #+begin_src lua :tangle no /c local whitelist = {} for _, k in pairs({ "iron-plate", "copper-plate", "steel-plate", "copper-wire", "iron-gear-wheel", "iron-stick", "pipe", }) do whitelist[k] = true end local parts = {} for name, recipe in pairs(game.recipe_prototypes) do local history = script.get_prototype_history("recipe", name) if history.created == "base" then local add = false local ingredients = {} for _, ingredient in pairs(recipe.ingredients) do if whitelist[ingredient.name] then add = true end ingredients[#ingredients+1] = ingredient.name .. "," .. ingredient.amount end if add then parts[#parts+1] = name .. "," .. table.concat(ingredients, ",") end end end game.write_file("recipes.csv", table.concat(parts, "\n"), false) #+end_src * This script This is a literate script. The source code is embedded in these code blocks, and tangled into the Raku script using org-babel. This allows me to write a description of what I want to do, and comment on it without resorting to ~// /* */~ ugly comments. * License I don't own the source csv files generated by Factorio, nor the mods the script is pulling from. The Raku script itself is GPLv3. ** TODO: include GPLv3 in the repo ** Credits: The following are mods from which .csv files have been generated. The recipe .csv files are included in the ~mod-recipes~ directory. * The Code ** Setup ** Open csv file ** Open (create if needed) output org file /I like org-mode, sue me. Also pseudocode is in Lisp./ For this, we'll likely want to include some metadata, like creation date, mod name, number of ingredients, maybe the total amount of raw mats needed to make one of everything? ** Make a list of products That's the first column in the .csv file. Read first column of the csv file and insert it into the .org (output) file. *** For each product, create a templated section Ideally we'd end up with Something like: #+begin_example ,* product ,** direct inputs - input and amount - input and amount ,** raw ingredients + raw ingredient and amount + raw ingredient and amount #+end_example **** Parse the csv file: For each line: 1. First column becomes the top header 2. Insert second header 3. Insert each ingredient and its amount as a separate item **** Parse the output.org file, filling it out recursively 1. Open .org (output file) 2. Loop over output.org: 1. Find *product section.* 2. Find (next) ingredient lines in this product section. 3. Pass the product and each direct input item and its number to ~raw-ingredients~. /(We can distinguish direct inputs from raw ingredients easily because org-mode supports multiple characters for defining lists. So we can just look for lines beginning with ~-~ and not really think about anything else.)/ This would look something like: #+begin_example (raw-ingredients iron-gear-wheel iron-plate 2) (raw-ingredients yellow-belt iron-plate 1) #+end_example 3. ~(raw-ingredients (product item number))~: 1. Store in variables for clarity: - product - item - number 2. Read .csv file: 1. If a recipe for this item exists in the csv file: 1. Go to the line with the recipe (first column. 2. For each ~(item number)~ pair, call ~(raw-ingredients (product new-item (* new-number old-number)))~. 3. If a recipe does not exist: 1. Find ~* product~ section in the .org file. 2. If the ingredient item already exists: 1. Add new number we just got to the existing number. 3. Else: write new raw ingredient line and number in this section.