From 562b33dff95bcd5214a8add5e3cf80354af8228d Mon Sep 17 00:00:00 2001 From: Ajurna Date: Thu, 6 Nov 2025 20:36:26 +0000 Subject: [PATCH] Introduce enums for items, machines, and recipes, and integrate recipe selection logic. Add `.gitignore` and improve Flask app for query-based input handling. --- .gitignore | 164 ++++++++++++ .idea/dictionaries/project.xml | 4 + .idea/pysatcalc.iml | 8 + main.py | 290 +++++--------------- plus.py | 477 +++++++++++++++++++++++++++++++++ templates/index.html | 2 +- 6 files changed, 721 insertions(+), 224 deletions(-) create mode 100644 .gitignore create mode 100644 plus.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dda4f8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,164 @@ +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml index ecab1d5..5765eb4 100644 --- a/.idea/dictionaries/project.xml +++ b/.idea/dictionaries/project.xml @@ -1,7 +1,11 @@ + aurovite callanite + caterium + larrussite + quickwire siterite diff --git a/.idea/pysatcalc.iml b/.idea/pysatcalc.iml index 8593391..fedc597 100644 --- a/.idea/pysatcalc.iml +++ b/.idea/pysatcalc.iml @@ -7,4 +7,12 @@ + + + \ No newline at end of file diff --git a/main.py b/main.py index 1ce95a9..e128a83 100644 --- a/main.py +++ b/main.py @@ -1,201 +1,12 @@ -from __future__ import annotations - -from dataclasses import dataclass from enum import Enum from math import ceil from typing import Dict, List, Tuple, Optional from flask import Flask, render_template, request from pydantic import BaseModel, Field - +from plus import Items, Machines, Recipes, Recipe app = Flask(__name__) -class Item(BaseModel): - name: str - -class Machine(BaseModel): - name: str - -class Machines(Enum): - Miner = Machine(name="Miner") - Smelter = Machine(name="Smelter") - Constructor = Machine(name="Constructor") - Assembler = Machine(name="Assembler") - Sorter = Machine(name="Sorter") - Crusher = Machine(name="Crusher") - Foundry = Machine(name="Foundry") - -class Items(Enum): - IronIngot = Item(name="Iron Ingot") - CopperIngot = Item(name="Copper Ingot") - Limestone = Item(name="Limestone") - IronOre = Item(name="Iron Ore") - CopperOre = Item(name="Copper Ore") - IronPlate = Item(name="Iron Plate") - IronRod = Item(name="Iron Rod") - Wire = Item(name="Wire") - Cable = Item(name="Cable") - Concrete = Item(name="Concrete") - ReinforcedIronPlate = Item(name="Reinforced Iron Plate") - ModularFrame = Item(name="Modular Frame") - BronzeBeam = Item(name="Bronze Beam") - TinPlate = Item(name="Tin Plate") - TinIngot = Item(name="Tin Ingot") - CrushedTin = Item(name="Crushed Tin") - CrushedIron = Item(name="Crushed Iron") - CrushedGangue = Item(name="Crushed Gangue") - CrushedSiterite = Item(name="Crushed Siterite") - SiteriteOre = Item(name="Siterite Ore") - Screws = Item(name="Screws") - BronzeIngot = Item(name="Bronze Ingot") - CrushedCopper = Item(name="Crushed Copper") - CrushedMagnesium = Item(name="Crushed Magnesium") - CrushedCallanite = Item(name="Crushed Callanite") - CallaniteOre = Item(name="Callanite Ore") - -class RawResources(Enum): - IronOre = Items.IronOre - CopperOre = Items.CopperOre - Limestone = Items.Limestone - - -# --- Domain model (minimal, extensible) --- -class Recipe(BaseModel): - name: str # Human-friendly name - building: Machines # e.g., "Smelter", "Constructor" - outputs: Dict[Items, float] # Produced item name - inputs: Dict[Items, float] = Field(default_factory=dict) - - -# A very small starter dataset (default, non-alternate recipes) -# Rates are per building per minute, matching Satisfactory default recipes. -class Recipes(Enum): - ModularFrame = Recipe( - name="Modular Frame", - building=Machines.Assembler, - outputs={Items.ModularFrame: 6.0}, - inputs={ - Items.ReinforcedIronPlate: 7.5, - Items.BronzeBeam: 7.5, - }, - ) - ReinforcedIronPlate = Recipe( - name="Reinforced Iron Plate", - building=Machines.Assembler, - outputs={Items.ReinforcedIronPlate: 5.0}, - inputs={ - Items.TinPlate: 20.0, - Items.Screws: 37.5, - }, - ) - TinPlate = Recipe( - name="Tin Plate", - building=Machines.Assembler, - outputs={Items.TinPlate: 40.0}, - inputs={ - Items.IronPlate: 20.0, - Items.TinIngot: 30.0, - }, - ) - TinIngot = Recipe( - name="Tin Ingot", - building=Machines.Smelter, - outputs={Items.TinIngot: 15.0}, - inputs={Items.CrushedTin: 15.0}, - ) - CrushedIron = Recipe( - name="Crushed Iron", - building=Machines.Sorter, - outputs={ - Items.CrushedIron: 90.0, - Items.CrushedTin: 60.0, - Items.CrushedGangue: 40.0, - }, - inputs={Items.CrushedSiterite: 120.0}, - ) - CrushedSiterite = Recipe( - name="Crushed Siterite", - building=Machines.Crusher, - outputs={ - Items.CrushedSiterite: 60.0, - Items.CrushedGangue: 25.0, - }, - inputs={Items.SiteriteOre: 60.0}, - ) - IronPlate = Recipe( - name="Iron Plate", - building=Machines.Constructor, - outputs={Items.IronPlate: 20.0}, - inputs={Items.IronIngot: 30.0}, - ) - IronIngot = Recipe( - name="Iron Ingot", - building=Machines.Smelter, - outputs={Items.IronIngot: 30.0}, - inputs={Items.CrushedIron: 30.0}, - ) - Screws = Recipe( - name="Screws", - building=Machines.Constructor, - outputs={Items.Screws: 50.0}, - inputs={Items.IronRod: 20.0}, - ) - IronRod = Recipe( - name="Iron Rod", - building=Machines.Constructor, - outputs={Items.IronRod: 15.0}, - inputs={Items.IronIngot: 15.0}, - ) - BronzeBeam = Recipe( - name="Bronze Beam", - building=Machines.Constructor, - outputs={Items.BronzeBeam: 7.5}, - inputs={Items.BronzeIngot: 22.5}, - ) - BronzeIngot = Recipe( - name="Bronze Ingot", - building=Machines.Foundry, - outputs={Items.BronzeIngot: 45.0}, - inputs={ - Items.CopperIngot: 36.0, - Items.TinIngot: 15.0, - }, - ) - CopperIngot = Recipe( - name="Copper Ingot", - building=Machines.Smelter, - outputs={Items.CopperIngot: 48.0}, - inputs={Items.CrushedCopper: 48.0}, - ) - CrushedCopper = Recipe( - name="Crushed Copper", - building=Machines.Sorter, - outputs={ - Items.CrushedCopper: 96.0, - Items.CrushedMagnesium: 80.0, - Items.CrushedGangue: 40.0, - }, - inputs={Items.CrushedCallanite: 120.0}, - ) - CrushedCallanite = Recipe( - name="Crushed Callanite", - building=Machines.Sorter, - outputs={ - Items.CrushedCallanite: 60.0, - Items.CrushedGangue: 25.0, - }, - inputs={Items.CallaniteOre: 60.0}, - ) - - -# Items which are considered raw resources (mined); they have no crafting recipes here -RAW_RESOURCES = { - Items.IronOre, - Items.CopperOre, - Items.Limestone, - Items.SiteriteOre, -} - def compute_chain(targets: Dict[Items, float]) -> Tuple[Dict[str, float], List[dict], Dict[str, float], Dict[str, float]]: """ @@ -205,14 +16,43 @@ def compute_chain(targets: Dict[Items, float]) -> Tuple[Dict[str, float], List[d - total production rates (echo of targets summed if multiple entries per item) - unused byproducts (items/min) produced by multi-output recipes but not consumed or targeted Uses the Recipes enum and Items objects. + + Now supports alternate recipes: when multiple recipes produce the same output item, + a selection heuristic is used unless an explicit preference is configured. """ - # Build a mapping from output item -> recipe that produces it (default recipe set) - output_to_recipe: Dict[Items, Recipe] = {} + # Build a mapping from output item -> list of recipes that produce it + output_to_recipes: Dict[Items, List[Recipe]] = {} for r in Recipes: recipe = r.value for out_item in recipe.outputs.keys(): - # prefer the first seen; can be extended to handle alternates later - output_to_recipe.setdefault(out_item, recipe) + output_to_recipes.setdefault(out_item, []).append(recipe) + + # Optional explicit preferences: map output Item -> recipe name to prefer + # Users can populate/modify this mapping elsewhere if desired. + PREFERRED_RECIPE_BY_OUTPUT: Dict[Items, str] = {} + + # Heuristic to select a recipe when multiple alternatives exist + def select_recipe_for(item: Items) -> Optional[Recipe]: + candidates = output_to_recipes.get(item, []) + if not candidates: + return None + # If explicit preference exists and matches a candidate, use it + pref_name = PREFERRED_RECIPE_BY_OUTPUT.get(item) + if pref_name: + for c in candidates: + if c.name == pref_name: + return c + # Otherwise pick the candidate with the highest per-building output for this item + # Tie-breaker 1: smallest total input per unit of this output + # Tie-breaker 2: deterministic by name + def score(c: Recipe) -> Tuple[float, float, str]: + per_build_out = c.outputs.get(item, 0.0) + total_input = sum(c.inputs.values()) + # Lower input per unit is better; we express as (total_input/per_build_out) + # Protect against division by zero + eff = float('inf') if per_build_out <= 0 else (total_input / per_build_out) + return (per_build_out, -eff, c.name) + return sorted(candidates, key=score, reverse=True)[0] # Aggregate demands for each item demand: Dict[Items, float] = {} @@ -236,18 +76,24 @@ def compute_chain(targets: Dict[Items, float]) -> Tuple[Dict[str, float], List[d # Expand demanded craftable items into their inputs until only raw remain while True: craftable_item = next( - (i for i, r in demand.items() if r > 1e-9 and i in output_to_recipe), + (i for i, r in demand.items() if r > 1e-9 and i in output_to_recipes), None, ) if craftable_item is None: break needed_rate = demand[craftable_item] - recipe = output_to_recipe[craftable_item] + recipe = select_recipe_for(craftable_item) + if recipe is None: + # Should not happen because craftable_item is in output_to_recipes, + # but guard anyway: treat as raw if selection failed. + demand[craftable_item] = 0.0 + raw_requirements[craftable_item.value.name] = raw_requirements.get(craftable_item.value.name, 0.0) + needed_rate + continue per_building_output = recipe.outputs[craftable_item] # Buildings needed - buildings = needed_rate / per_building_output + buildings = needed_rate / per_building_output if per_building_output > 0 else 0.0 buildings_ceiled = ceil(buildings - 1e-9) utilization = 0.0 if buildings_ceiled == 0 else buildings / buildings_ceiled @@ -282,11 +128,7 @@ def compute_chain(targets: Dict[Items, float]) -> Tuple[Dict[str, float], List[d for item, rate in demand.items(): if rate <= 1e-9: continue - if item in RAW_RESOURCES or item not in output_to_recipe: - raw_requirements[item.value.name] = raw_requirements.get(item.value.name, 0.0) + rate - else: - # Shouldn't happen, but guard - raw_requirements[item.value.name] = raw_requirements.get(item.value.name, 0.0) + rate + raw_requirements[item.value.name] = raw_requirements.get(item.value.name, 0.0) + rate # Merge steps for same item/building merged: Dict[Tuple[str, str], dict] = {} @@ -321,7 +163,7 @@ def compute_chain(targets: Dict[Items, float]) -> Tuple[Dict[str, float], List[d return raw_requirements, merged_steps, total_outputs, unused_byproducts -@app.route("/", methods=["GET", "POST"]) +@app.route("/", methods=["GET"]) def index(): # Build selectable items list from Items enum (display names) item_names = sorted([i.value.name for i in Items]) @@ -332,9 +174,11 @@ def index(): selected_item = item_names[0] if item_names else "" selected_rate = 60.0 - if request.method == "POST": - item_name = request.form.get("item") or selected_item - rate_str = request.form.get("rate") + # Read from query parameters for bookmarkable URLs + item_name = request.args.get("item") or selected_item + rate_str = request.args.get("rate") + rate = None + if rate_str is not None and rate_str != "": try: rate = float(rate_str) if rate < 0: @@ -343,24 +187,24 @@ def index(): error = "Please enter a valid non-negative number for rate (items per minute)." rate = None - selected_item = item_name - if rate is not None: - selected_rate = rate + selected_item = item_name + if rate is not None: + selected_rate = rate - if not error and item_name and rate is not None: - item_obj = name_to_item.get(item_name) - if item_obj is None: - error = "Unknown item selected." - else: - targets = {item_obj: rate} - raw, steps, outputs, unused = compute_chain(targets) - result = { - "targets": {item_name: rate}, - "raw": raw, - "steps": steps, - "outputs": outputs, - "unused": unused, - } + if not error and item_name and rate is not None: + item_obj = name_to_item.get(item_name) + if item_obj is None: + error = "Unknown item selected." + else: + targets = {item_obj: rate} + raw, steps, outputs, unused = compute_chain(targets) + result = { + "targets": {item_name: rate}, + "raw": raw, + "steps": steps, + "outputs": outputs, + "unused": unused, + } return render_template( "index.html", diff --git a/plus.py b/plus.py new file mode 100644 index 0000000..f609ce3 --- /dev/null +++ b/plus.py @@ -0,0 +1,477 @@ +from enum import Enum +from typing import Dict + +from pydantic import BaseModel, Field + + +class Item(BaseModel): + name: str + +class Machine(BaseModel): + name: str + +class Machines(Enum): + Miner = Machine(name="Miner") + Smelter = Machine(name="Smelter") + Constructor = Machine(name="Constructor") + Assembler = Machine(name="Assembler") + Sorter = Machine(name="Sorter") + Crusher = Machine(name="Crusher") + Foundry = Machine(name="Foundry") + +class Items(Enum): + IronIngot = Item(name="Iron Ingot") + CopperIngot = Item(name="Copper Ingot") + Limestone = Item(name="Limestone") + IronOre = Item(name="Iron Ore") + CopperOre = Item(name="Copper Ore") + IronPlate = Item(name="Iron Plate") + IronRod = Item(name="Iron Rod") + Wire = Item(name="Wire") + Cable = Item(name="Cable") + Concrete = Item(name="Concrete") + ReinforcedIronPlate = Item(name="Reinforced Iron Plate") + ModularFrame = Item(name="Modular Frame") + BronzeBeam = Item(name="Bronze Beam") + TinPlate = Item(name="Tin Plate") + TinIngot = Item(name="Tin Ingot") + CrushedTin = Item(name="Crushed Tin") + CrushedIron = Item(name="Crushed Iron") + CrushedGangue = Item(name="Crushed Gangue") + CrushedSiterite = Item(name="Crushed Siterite") + SiteriteOre = Item(name="Siterite Ore") + Screws = Item(name="Screws") + BronzeIngot = Item(name="Bronze Ingot") + CrushedCopper = Item(name="Crushed Copper") + CrushedMagnesium = Item(name="Crushed Magnesium") + CrushedCallanite = Item(name="Crushed Callanite") + CallaniteOre = Item(name="Callanite Ore") + CrushedLarrussite = Item(name="Crushed Larrussite") + CrushedAurovite = Item(name="Crushed Aurovite") + AuroviteOre = Item(name="Aurovite Ore") + Sand = Item(name="Sand") + MagnesiumGranules = Item(name="Magnesium Granules") + CrushedZinc = Item(name="Crushed Zinc") + LarrussiteOre = Item(name="Larrussite Ore") + ZincIngot = Item(name="Zinc Ingot") + Glass = Item(name="Glass") + IronSheet = Item(name="Iron Sheet") + CopperRod = Item(name="Copper Rod") + CopperSheet = Item(name="Copper Sheet") + BronzePlates = Item(name="Bronze Plates") + CateriumRod = Item(name="Caterium Rod") + CateriumIngot = Item(name="Caterium Ingot") + CateriumPlate = Item(name="Caterium Plate") + TinRod = Item(name="Tin Rod") + ZincPlates = Item(name="Zinc Plates") + IronWire = Item(name="Iron Wire") + CopperBusbars = Item(name="Copper Busbars") + Quickwire = Item(name="Quickwire") + TinnedWire = Item(name="Tinned Wire") + SolarCell = Item(name="Solar Cell") + Silica = Item(name="Silica") + BronzePipes = Item(name="Bronze Pipes") + SmoothBeltDrive = Item(name="Smooth Belt Drive") + TinnedSheet = Item(name="Tinned Sheet") + Rotor = Item(name="Rotor") + BronzeFrame = Item(name="Bronze Frame") + AILimiter = Item(name="AI Limiter") + +class Recipe(BaseModel): + name: str # Human-friendly name + building: Machines # e.g., "Smelter", "Constructor" + outputs: Dict[Items, float] # Produced item name + inputs: Dict[Items, float] = Field(default_factory=dict) + + +class Recipes(Enum): + # Crusher + # - Crushing Ores + CrushedSiterite = Recipe( + name="Crushed Siterite", + building=Machines.Crusher, + outputs={ + Items.CrushedSiterite: 60.0, + Items.CrushedGangue: 25.0, + }, + inputs={Items.SiteriteOre: 60.0}, + ) + CrushedLarrussite = Recipe( + name="Crushed Larrussite", + building=Machines.Crusher, + outputs={ + Items.CrushedLarrussite: 40.0, + Items.CrushedGangue: 20.0, + }, + inputs={Items.LarrussiteOre: 60.0}, + ) + CrushedCallanite = Recipe( + name="Crushed Callanite", + building=Machines.Crusher, + outputs={ + Items.CrushedCallanite: 60.0, + Items.CrushedGangue: 25.0, + }, + inputs={Items.CallaniteOre: 60.0}, + ) + CrushedAurovite = Recipe( + name="Crushed Aurovite", + building=Machines.Crusher, + outputs={ + Items.CrushedAurovite: 60.0, + Items.CrushedGangue: 22.5, + }, + inputs={Items.AuroviteOre: 60.0}, + ) + # - Crushing Powders + CoarseSand = Recipe( + name="Coarse Sand", + building=Machines.Crusher, + outputs={ + Items.Sand: 30.0 + }, + inputs={Items.CrushedGangue: 60.0}, + ) + MagnesiumGranules = Recipe( + name="Magnesium Granules", + building=Machines.Crusher, + outputs={ + Items.MagnesiumGranules: 72.0 + }, + inputs={Items.CrushedMagnesium: 80.0}, + ) + + # Sorter + # - Simple Sorting + CrushedIron = Recipe( + name="Crushed Iron", + building=Machines.Sorter, + outputs={ + Items.CrushedIron: 90.0, + Items.CrushedTin: 60.0, + Items.CrushedGangue: 40.0, + }, + inputs={Items.CrushedSiterite: 120.0}, + ) + CrushedCopper = Recipe( + name="Crushed Copper", + building=Machines.Sorter, + outputs={ + Items.CrushedCopper: 96.0, + Items.CrushedMagnesium: 80.0, + Items.CrushedGangue: 40.0, + }, + inputs={Items.CrushedCallanite: 120.0}, + ) + CrushedZinc = Recipe( + name="Crushed Zinc", + building=Machines.Sorter, + outputs={ + Items.CrushedZinc: 96.0, + Items.CrushedMagnesium: 48.0, + Items.CrushedGangue: 60.0, + }, + inputs={Items.CrushedLarrussite: 120.0}, + ) + # Smelter + # - smelting + IronIngot = Recipe( + name="Iron Ingot", + building=Machines.Smelter, + outputs={Items.IronIngot: 30.0}, + inputs={Items.CrushedIron: 30.0}, + ) + CopperIngot = Recipe( + name="Copper Ingot", + building=Machines.Smelter, + outputs={Items.CopperIngot: 48.0}, + inputs={Items.CrushedCopper: 48.0}, + ) + TinIngot = Recipe( + name="Tin Ingot", + building=Machines.Smelter, + outputs={Items.TinIngot: 15.0}, + inputs={Items.CrushedTin: 15.0}, + ) + # - Ingots + ImpureIronIngot = Recipe( + name="Impure Iron Ingot", + building=Machines.Smelter, + outputs={Items.IronIngot: 30.0}, + inputs={Items.CrushedSiterite: 30.0}, + ) + ImpureCopperIngot = Recipe( + name="Impure Copper Ingot", + building=Machines.Smelter, + inputs={Items.CrushedCallanite: 24.0}, + outputs={Items.CopperIngot: 24.0}, + ) + ImpureTinIngot = Recipe( + name="Impure Tin Ingot", + building=Machines.Smelter, + inputs={Items.CrushedSiterite: 30.0}, + outputs={Items.TinIngot: 15.0}, + ) + ImpureCateriumIngot = Recipe( + name="Impure Caterium Ingot", + building=Machines.Smelter, + inputs={Items.CrushedAurovite: 40.0}, + outputs={Items.CopperIngot: 24.0}, + ) + ZincIngot = Recipe( + name="Zinc Ingot", + building=Machines.Smelter, + outputs={Items.ZincIngot: 15.0}, + inputs={Items.CrushedZinc: 24.0}, + ) + # - Standard Parts + SloppyGlass = Recipe( + name="Sloppy Glass", + building=Machines.Smelter, + outputs={Items.Glass: 20.0}, + inputs={Items.Sand: 30.0}, + ) + # Constructor + # - Standard Parts + IronPlate = Recipe( + name="Iron Plate", + building=Machines.Constructor, + outputs={Items.IronPlate: 20.0}, + inputs={Items.IronIngot: 30.0}, + ) + Screws = Recipe( + name="Screws", + building=Machines.Constructor, + outputs={Items.Screws: 50.0}, + inputs={Items.IronRod: 20.0}, + ) + IronRod = Recipe( + name="Iron Rod", + building=Machines.Constructor, + outputs={Items.IronRod: 15.0}, + inputs={Items.IronIngot: 15.0}, + ) + IronSheet = Recipe( + name="Iron Sheet", + building=Machines.Constructor, + outputs={Items.IronSheet: 50.0}, + inputs={Items.IronPlate: 40.0}, + ) + CopperRod = Recipe( + name="Copper Rod", + building=Machines.Constructor, + outputs={Items.CopperRod: 18.0}, + inputs={Items.CopperIngot: 12.0}, + ) + CopperSheet = Recipe( + name="Copper Sheet", + building=Machines.Constructor, + outputs={Items.CopperSheet: 30.0}, + inputs={Items.CopperIngot: 24.0}, + ) + BronzeBeam = Recipe( + name="Bronze Beam", + building=Machines.Constructor, + outputs={Items.BronzeBeam: 7.5}, + inputs={Items.BronzeIngot: 22.5}, + ) + BronzePlates = Recipe( + name="Bronze Plates", + building=Machines.Constructor, + outputs={Items.BronzePlates: 24.0}, + inputs={Items.BronzeIngot: 30.0}, + ) + CateriumRod = Recipe( + name="Caterium Rod", + building=Machines.Constructor, + outputs={Items.CateriumRod: 32.0}, + inputs={Items.CateriumIngot: 16.0}, + ) + CateriumPlate = Recipe( + name="Caterium Plate", + building=Machines.Constructor, + outputs={Items.CateriumPlate: 36.0}, + inputs={Items.CateriumIngot: 60.0}, + ) + TinRod = Recipe( + name="Tin Rod", + building=Machines.Constructor, + outputs={Items.TinRod: 15.0}, + inputs={Items.TinIngot: 30.0}, + ) + ZincPlates = Recipe( + name="Zinc Plates", + building=Machines.Constructor, + outputs={Items.ZincPlates: 15.0}, + inputs={Items.ZincIngot: 12.5}, + ) + # - Electronics + IronWire = Recipe( + name="Iron Wire", + building=Machines.Constructor, + outputs={Items.IronWire: 30.0}, + inputs={Items.IronRod: 20.0}, + ) + Wire = Recipe( + name="Wire", + building=Machines.Constructor, + outputs={Items.Wire: 30.0}, + inputs={Items.CopperRod: 18.0}, + ) + CopperBusbars = Recipe( + name="Copper Busbars", + building=Machines.Constructor, + outputs={Items.CopperBusbars: 15.0}, + inputs={Items.CopperRod: 24.0}, + ) + Cable = Recipe( + name="Cable", + building=Machines.Constructor, + outputs={Items.Cable: 30.0}, + inputs={Items.Wire: 60.0}, + ) + Quickwire = Recipe( + name="Quickwire", + building=Machines.Constructor, + outputs={Items.Quickwire: 80.0}, + inputs={Items.CateriumRod: 24.0}, + ) + PureTinWire = Recipe( + name="Pure Tin Wire", + building=Machines.Constructor, + outputs={Items.TinnedWire: 11.25}, + inputs={Items.TinRod: 15.0}, + ) + # - Compounds + Concrete = Recipe( + name="Concrete", + building=Machines.Constructor, + outputs={Items.Concrete: 15.0}, + inputs={Items.CrushedGangue: 45.0}, + ) + + # Foundry + # - Building Parts + SolarCell = Recipe( + name="Solar Cell", + building=Machines.Foundry, + outputs={Items.SolarCell: 11.25}, + inputs={ + Items.ZincPlates: 22.5, + Items.Silica: 67.5, + }, + ) + # - Alloys + BronzePipes = Recipe( + name="Bronze Pipes", + building=Machines.Foundry, + outputs={Items.BronzePipes: 36.0}, + inputs={ + Items.CopperIngot: 24.0, + Items.TinIngot: 15.0, + }, + ) + # - ingots + BronzeIngot = Recipe( + name="Bronze Ingot", + building=Machines.Foundry, + outputs={Items.BronzeIngot: 45.0}, + inputs={ + Items.CopperIngot: 36.0, + Items.TinIngot: 15.0, + }, + ) + # - Other + ThermalSilica = Recipe( + name="Thermal Silica", + building=Machines.Foundry, + outputs={Items.Silica: 45.0}, + inputs={ + Items.Sand: 30.0, + Items.MagnesiumGranules: 27.0, + }, + ) + # Assembler + # - Building Parts + SmoothBeltDrive = Recipe( + name="Smooth Belt Drive", + building=Machines.Assembler, + outputs={Items.SmoothBeltDrive: 18.75}, + inputs={ + Items.TinnedSheet: 15.0, + Items.Rotor: 3.75, + }, + ) + # - Standard Parts + TinPlate = Recipe( + name="Tin Plate", + building=Machines.Assembler, + outputs={Items.TinPlate: 40.0}, + inputs={ + Items.TinIngot: 30.0, + Items.IronPlate: 20.0, + }, + ) + TinnedWire = Recipe( + name="Tinned Wire", + building=Machines.Assembler, + outputs={Items.TinnedWire: 90}, + inputs={ + Items.Wire: 60.0, + Items.TinRod: 45.0, + }, + ) + TinnedSheet = Recipe( + name="Tinned Sheet", + building=Machines.Assembler, + outputs={Items.TinnedSheet: 40.0}, + inputs={ + Items.TinIngot: 20.0, + Items.CopperSheet: 20.0, + }, + ) + Rotor = Recipe( + name="Rotor", + building=Machines.Assembler, + outputs={Items.Rotor: 7.5}, + inputs={ + Items.CopperBusbars: 22.5, + Items.IronWire: 60.0, + }, + ) + ReinforcedIronPlate = Recipe( + name="Reinforced Iron Plate", + building=Machines.Assembler, + outputs={Items.ReinforcedIronPlate: 5.0}, + inputs={ + Items.TinPlate: 20.0, + Items.Screws: 37.5, + }, + ) + BronzeFrame = Recipe( + name="Bronze Frame", + building=Machines.Assembler, + outputs={Items.BronzeFrame: 5.0}, + inputs={ + Items.BronzeBeam: 10.0, + Items.BronzePipes: 24.0, + }, + ) + ModularFrame = Recipe( + name="Modular Frame", + building=Machines.Assembler, + outputs={Items.ModularFrame: 6.0}, + inputs={ + Items.ReinforcedIronPlate: 7.5, + Items.BronzeBeam: 7.5, + }, + ) + AILimiter = Recipe( + name="AI Limiter", + building=Machines.Assembler, + outputs={Items.AILimiter: 6.0}, + inputs={ + Items.CateriumPlate: 24.0, + Items.TinnedWire: 18.0, + }, + ) \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 79a7002..5d68690 100644 --- a/templates/index.html +++ b/templates/index.html @@ -33,7 +33,7 @@

Satisfactory Production Calculator

Compute buildings and raw inputs for a target output rate (items per minute). Data set includes a few early-game default recipes; you can extend it in main.py.

-
+