testing
This commit is contained in:
131
main.py
131
main.py
@@ -5,10 +5,13 @@ from typing import Dict, List, Tuple, Optional, Any
|
|||||||
import networkx as nx
|
import networkx as nx
|
||||||
from flask import Flask, render_template, request
|
from flask import Flask, render_template, request
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from plus import Items, Machines, Recipes, Recipe
|
# from plus import Items, Machines, Recipes, Recipe
|
||||||
|
from vanilla import Items, Machines, Recipes, Recipe, RawResources
|
||||||
from rich import print
|
from rich import print
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Helpers to map item names to safe query parameter keys
|
# Helpers to map item names to safe query parameter keys
|
||||||
def _slugify(name: str) -> str:
|
def _slugify(name: str) -> str:
|
||||||
s = ''.join(ch.lower() if ch.isalnum() else '_' for ch in name)
|
s = ''.join(ch.lower() if ch.isalnum() else '_' for ch in name)
|
||||||
@@ -29,53 +32,88 @@ class Production(BaseModel):
|
|||||||
recipe: Recipe
|
recipe: Recipe
|
||||||
quantity: float
|
quantity: float
|
||||||
|
|
||||||
|
class ProductionChain:
|
||||||
|
def __init__(self):
|
||||||
|
self.recipe_map: Dict[Items, list[Recipes]] = defaultdict(list)
|
||||||
|
for r in Recipes:
|
||||||
|
for o in r.value.outputs:
|
||||||
|
self.recipe_map[o].append(r.value)
|
||||||
|
|
||||||
def get_recipe(item: Items, recipe_map: Dict[Items, list[Recipes]]) -> Optional[Recipe]:
|
def get_recipe(self, item: Items, recipe_map: Dict[Items, list[Recipes]]) -> Optional[Recipe]:
|
||||||
return recipe_map.get(item, (None,))[0]
|
# TODO: make logic for priority selection of recipes
|
||||||
|
return recipe_map.get(item, (None,))[0]
|
||||||
|
|
||||||
def compute_chain2(targets: Dict[Items, float]) -> Any:
|
def calculate_excess(self, production: Dict[Items, float], demand: Dict[Items, float], raw_resources: Dict[Items, float]):
|
||||||
if not targets:
|
excess = defaultdict(float)
|
||||||
return {}
|
for item, quantity in demand.items():
|
||||||
recipe_map: Dict[Items, list[Recipes]] = defaultdict(list)
|
total = production[item] - demand[item]
|
||||||
for r in Recipes:
|
if total != 0:
|
||||||
for o in r.value.outputs:
|
if item in RawResources:
|
||||||
recipe_map[o].append(r.value)
|
raw_resources[item] -= production[item]
|
||||||
# print(recipe_map)
|
|
||||||
demand = defaultdict(float)
|
|
||||||
for target in targets:
|
|
||||||
demand[target] = targets[target]
|
|
||||||
production = defaultdict(float)
|
|
||||||
# add demands to production
|
|
||||||
# find a recipe for that demand
|
|
||||||
# add inputs to demand
|
|
||||||
queue = deque(targets)
|
|
||||||
g = nx.DiGraph()
|
|
||||||
production_queue = []
|
|
||||||
raw_resources = defaultdict(float)
|
|
||||||
production_chain = defaultdict(float)
|
|
||||||
while queue:
|
|
||||||
item = queue.popleft()
|
|
||||||
recipe = get_recipe(item, recipe_map)
|
|
||||||
if recipe is None:
|
|
||||||
raw_resources[item] += demand[item] - raw_resources[item]
|
|
||||||
continue
|
|
||||||
levels = []
|
|
||||||
for out, quantity in recipe.outputs.items():
|
|
||||||
if out in demand:
|
|
||||||
target_quantity = demand[out] - production[out]
|
|
||||||
if target_quantity > 0:
|
|
||||||
levels.append(target_quantity / quantity)
|
|
||||||
else:
|
else:
|
||||||
levels.append(0)
|
excess[item] = total
|
||||||
production_level = max(levels) if max(levels) > 0 else 0
|
return excess
|
||||||
production_chain[recipe.name] = production_level
|
|
||||||
for out, quantity in recipe.outputs.items():
|
def calculate_byproduct(self, production: Dict[Items, float], demand: Dict[Items, float], raw_resources: Dict[Items, float]):
|
||||||
production[out] += production_level * quantity
|
byproduct = defaultdict(float)
|
||||||
for inp, quantity in recipe.inputs.items():
|
for item, quantity in production.items():
|
||||||
queue.append(inp)
|
if item not in demand:
|
||||||
demand[inp] += production_level * quantity
|
byproduct[item] = quantity
|
||||||
print(demand, production, raw_resources, production_chain)
|
return byproduct
|
||||||
return production
|
|
||||||
|
def compute_chain(self, targets: Dict[Items, float]) -> Any:
|
||||||
|
if not targets:
|
||||||
|
return {}
|
||||||
|
demand = defaultdict(float)
|
||||||
|
for target in targets:
|
||||||
|
demand[target] = targets[target]
|
||||||
|
|
||||||
|
queue = deque(targets)
|
||||||
|
production = defaultdict(float)
|
||||||
|
raw_resources = defaultdict(float)
|
||||||
|
production_chain = defaultdict(float)
|
||||||
|
while queue:
|
||||||
|
item = queue.popleft()
|
||||||
|
if item == Items.Silica:
|
||||||
|
print("silica")
|
||||||
|
recipe = self.get_recipe(item, self.recipe_map)
|
||||||
|
if item in RawResources:
|
||||||
|
raw_resources[item] += demand[item] - raw_resources[item]
|
||||||
|
continue
|
||||||
|
if item in production:
|
||||||
|
if production[item] == demand[item]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
levels = []
|
||||||
|
for out, quantity in recipe.outputs.items():
|
||||||
|
if out in demand:
|
||||||
|
target_quantity = demand[out] - production[out]
|
||||||
|
levels.append(target_quantity / quantity)
|
||||||
|
|
||||||
|
production_level = max(levels)
|
||||||
|
production_chain[recipe.name] += production_level
|
||||||
|
for out, quantity in recipe.outputs.items():
|
||||||
|
production[out] += production_level * quantity
|
||||||
|
for byproduct, quantity in recipe.byproducts.items():
|
||||||
|
production[byproduct] += production_level * quantity
|
||||||
|
queue.append(byproduct)
|
||||||
|
for inp, quantity in recipe.inputs.items():
|
||||||
|
queue.append(inp)
|
||||||
|
demand[inp] += production_level * quantity
|
||||||
|
|
||||||
|
excess = self.calculate_excess(production, demand, raw_resources)
|
||||||
|
byproduct =self.calculate_byproduct(production, demand, raw_resources)
|
||||||
|
|
||||||
|
|
||||||
|
print("demand:", demand)
|
||||||
|
print("production:", production)
|
||||||
|
print("excess:", excess)
|
||||||
|
print("byproduct:", byproduct)
|
||||||
|
print("raw resources:", raw_resources)
|
||||||
|
print("production chain:", production_chain)
|
||||||
|
|
||||||
|
|
||||||
|
return production_chain
|
||||||
|
|
||||||
|
|
||||||
def compute_chain(targets: Dict[Items, float], preferred_by_output: Optional[Dict[Items, str]] = None) -> Tuple[Dict[str, float], List[dict], Dict[str, float], Dict[str, float]]:
|
def compute_chain(targets: Dict[Items, float], preferred_by_output: Optional[Dict[Items, str]] = None) -> Tuple[Dict[str, float], List[dict], Dict[str, float], Dict[str, float]]:
|
||||||
@@ -388,4 +426,5 @@ def create_app():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# For local dev: python main.py
|
# For local dev: python main.py
|
||||||
# app.run(host="0.0.0.0", port=5000, debug=True)
|
# app.run(host="0.0.0.0", port=5000, debug=True)
|
||||||
compute_chain2({Items.ModularFrame: 45.0})
|
prod_chain = ProductionChain()
|
||||||
|
prod_chain.compute_chain({Items.FusedModularFrame: 1.5})
|
||||||
19
plus.py
19
plus.py
@@ -1,15 +1,9 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
from shared import Machine, Item
|
||||||
from pydantic import BaseModel, Field, ConfigDict
|
from pydantic import BaseModel, Field, ConfigDict
|
||||||
|
|
||||||
|
|
||||||
class Item(BaseModel):
|
|
||||||
name: str
|
|
||||||
|
|
||||||
class Machine(BaseModel):
|
|
||||||
name: str
|
|
||||||
|
|
||||||
class Machines(Enum):
|
class Machines(Enum):
|
||||||
Miner = Machine(name="Miner")
|
Miner = Machine(name="Miner")
|
||||||
Smelter = Machine(name="Smelter")
|
Smelter = Machine(name="Smelter")
|
||||||
@@ -80,12 +74,11 @@ class Items(Enum):
|
|||||||
|
|
||||||
class Recipe(BaseModel):
|
class Recipe(BaseModel):
|
||||||
model_config = ConfigDict(frozen=True)
|
model_config = ConfigDict(frozen=True)
|
||||||
name: str # Human-friendly name
|
name: str
|
||||||
building: Machines # e.g., "Smelter", "Constructor"
|
building: Machines
|
||||||
outputs: Dict[Items, float] # Produced item name
|
outputs: Dict[Items, float]
|
||||||
inputs: Dict[Items, float] = Field(default_factory=dict)
|
inputs: Dict[Items, float] = Field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
class Recipes(Enum):
|
class Recipes(Enum):
|
||||||
# Crusher
|
# Crusher
|
||||||
# - Crushing Ores
|
# - Crushing Ores
|
||||||
@@ -476,4 +469,6 @@ class Recipes(Enum):
|
|||||||
Items.CateriumPlate: 24.0,
|
Items.CateriumPlate: 24.0,
|
||||||
Items.TinnedWire: 18.0,
|
Items.TinnedWire: 18.0,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Recipe.model_rebuild()
|
||||||
12
shared.py
Normal file
12
shared.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from enum import Enum, EnumType
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
|
|
||||||
|
class Item(BaseModel):
|
||||||
|
name: str
|
||||||
|
|
||||||
|
class Machine(BaseModel):
|
||||||
|
name: str
|
||||||
|
|
||||||
193
vanilla.py
193
vanilla.py
@@ -1,20 +1,68 @@
|
|||||||
|
from enum import Enum
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from shared import Machine, Item
|
||||||
|
|
||||||
|
class Machines(Enum):
|
||||||
|
Smelter = Machine(name="Smelter")
|
||||||
|
Constructor = Machine(name="Constructor")
|
||||||
|
Assembler = Machine(name="Assembler")
|
||||||
|
Foundry = Machine(name="Foundry")
|
||||||
|
Manufacturer = Machine(name="Manufacturer")
|
||||||
|
Blender = Machine(name="Blender")
|
||||||
|
Refinery = Machine(name="Refinery")
|
||||||
|
|
||||||
|
|
||||||
|
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")
|
||||||
|
Screw = Item(name="Screw")
|
||||||
|
ReinforcedIronPlate = Item(name="Reinforced Iron Plate")
|
||||||
|
ModularFrame = Item(name="Modular Frame")
|
||||||
|
HeavyModularFrame = Item(name="Heavy Modular Frame")
|
||||||
|
EncasedIndustrialBeam = Item(name="Encased Industrial Beam")
|
||||||
|
SteelPipe = Item(name="Steel Pipe")
|
||||||
|
SteelBeam = Item(name="Steel Beam")
|
||||||
|
SteelIngot = Item(name="Steel Ingot")
|
||||||
|
Coal = Item(name="Coal")
|
||||||
|
FusedModularFrame = Item(name="Fused Modular Frame")
|
||||||
|
NitrogenGas = Item(name="Nitrogen Gas")
|
||||||
|
AluminumCasing = Item(name="Aluminum Casing")
|
||||||
|
AluminumIngot = Item(name="Aluminum Ingot")
|
||||||
|
Silica = Item(name="Silica")
|
||||||
|
AluminumScrap = Item(name="Aluminum Scrap")
|
||||||
|
AluminaSolution = Item(name="Alumina Solution")
|
||||||
|
Water = Item(name="Water")
|
||||||
|
RawQuartz = Item(name="Raw Quartz")
|
||||||
|
Bauxite = Item(name="Bauxite")
|
||||||
|
|
||||||
|
class RawResources(Enum):
|
||||||
|
Water = Items.Water
|
||||||
|
Coal = Items.Coal
|
||||||
|
IronOre = Items.IronOre
|
||||||
|
CopperOre = Items.CopperOre
|
||||||
|
Limestone = Items.Limestone
|
||||||
|
NitrogenGas = Items.NitrogenGas
|
||||||
|
RawQuartz = Items.RawQuartz
|
||||||
|
Bauxite = Items.Bauxite
|
||||||
|
|
||||||
|
class Recipe(BaseModel):
|
||||||
|
name: str
|
||||||
|
building: Machines
|
||||||
|
outputs: Dict[Items, float]
|
||||||
|
inputs: Dict[Items, float] = Field(default_factory=dict)
|
||||||
|
byproducts: Dict[Items, float] = Field(default_factory=dict)
|
||||||
|
|
||||||
class Recipes(Enum):
|
class Recipes(Enum):
|
||||||
# Mining
|
|
||||||
IronOre = Recipe(
|
|
||||||
name="Iron Ore",
|
|
||||||
building=Machines.Miner,
|
|
||||||
outputs={Items.IronOre: 60.0},
|
|
||||||
)
|
|
||||||
CopperOre = Recipe(
|
|
||||||
name="Copper Ore",
|
|
||||||
building=Machines.Miner,
|
|
||||||
outputs={Items.CopperOre: 60.0},
|
|
||||||
)
|
|
||||||
Limestone = Recipe(
|
|
||||||
name="Limestone",
|
|
||||||
building=Machines.Miner,
|
|
||||||
outputs={Items.Limestone: 60.0},
|
|
||||||
)
|
|
||||||
# Smelting
|
# Smelting
|
||||||
IronIngot = Recipe(
|
IronIngot = Recipe(
|
||||||
name="Iron Ingot",
|
name="Iron Ingot",
|
||||||
@@ -76,4 +124,117 @@ class Recipes(Enum):
|
|||||||
Items.IronPlate: 30.0,
|
Items.IronPlate: 30.0,
|
||||||
Items.Screw: 60.0,
|
Items.Screw: 60.0,
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
ModularFrame = Recipe(
|
||||||
|
name="Modular Frame",
|
||||||
|
building=Machines.Assembler,
|
||||||
|
outputs={Items.ModularFrame: 2.0},
|
||||||
|
inputs={
|
||||||
|
Items.IronRod: 12.0,
|
||||||
|
Items.ReinforcedIronPlate: 3.0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
HeavyModularFrame = Recipe(
|
||||||
|
name="Heavy Modular Frame",
|
||||||
|
building=Machines.Manufacturer,
|
||||||
|
outputs={Items.HeavyModularFrame: 2.0},
|
||||||
|
inputs={
|
||||||
|
Items.SteelPipe: 40.0,
|
||||||
|
Items.Screw: 240.0,
|
||||||
|
Items.EncasedIndustrialBeam: 10.0,
|
||||||
|
Items.ModularFrame: 10.0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
EncasedIndustrialBeam = Recipe(
|
||||||
|
name="Encased Industrial Beam",
|
||||||
|
building=Machines.Assembler,
|
||||||
|
outputs={Items.EncasedIndustrialBeam: 6.0},
|
||||||
|
inputs={
|
||||||
|
Items.Concrete: 36.0,
|
||||||
|
Items.SteelBeam: 18.0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
SteelPipe = Recipe(
|
||||||
|
name="Steel Pipe",
|
||||||
|
building=Machines.Constructor,
|
||||||
|
outputs={Items.SteelPipe: 20.0},
|
||||||
|
inputs={
|
||||||
|
Items.SteelIngot: 30.0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
SteelBeam = Recipe(
|
||||||
|
name="Steel Beam",
|
||||||
|
building=Machines.Constructor,
|
||||||
|
outputs={Items.SteelBeam: 15.0},
|
||||||
|
inputs={
|
||||||
|
Items.SteelIngot: 60.0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
SteelIngot = Recipe(
|
||||||
|
name="Steel Ingot",
|
||||||
|
building=Machines.Foundry,
|
||||||
|
outputs={Items.SteelIngot: 45.0},
|
||||||
|
inputs={
|
||||||
|
Items.Coal: 45.0,
|
||||||
|
Items.IronOre: 45.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
FusedModularFrame = Recipe(
|
||||||
|
name="Fused Modular Frame",
|
||||||
|
building=Machines.Blender,
|
||||||
|
outputs={Items.FusedModularFrame: 1.5},
|
||||||
|
inputs={
|
||||||
|
Items.NitrogenGas: 37.5,
|
||||||
|
Items.AluminumCasing: 75.0,
|
||||||
|
Items.HeavyModularFrame: 1.5,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
AluminumCasing = Recipe(
|
||||||
|
name="Aluminum Casing",
|
||||||
|
building=Machines.Constructor,
|
||||||
|
outputs={Items.AluminumCasing: 60.0},
|
||||||
|
inputs={
|
||||||
|
Items.AluminumIngot: 90.0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
AluminumIngot = Recipe(
|
||||||
|
name="Aluminum Ingot",
|
||||||
|
building=Machines.Foundry,
|
||||||
|
outputs={Items.AluminumIngot: 60.0},
|
||||||
|
inputs={
|
||||||
|
Items.Silica: 75.0,
|
||||||
|
Items.AluminumScrap: 90.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
AluminumScrap = Recipe(
|
||||||
|
name="Aluminum Scrap",
|
||||||
|
building=Machines.Refinery,
|
||||||
|
outputs={
|
||||||
|
Items.AluminumScrap: 360.0
|
||||||
|
},
|
||||||
|
inputs={
|
||||||
|
Items.Coal: 120.0,
|
||||||
|
Items.AluminaSolution: 240.0,
|
||||||
|
},
|
||||||
|
byproducts={Items.Water: 120.0}
|
||||||
|
)
|
||||||
|
Silica = Recipe(
|
||||||
|
name="Silica",
|
||||||
|
building=Machines.Constructor,
|
||||||
|
outputs={Items.Silica: 37.5},
|
||||||
|
inputs={
|
||||||
|
Items.RawQuartz: 22.5,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
AluminaSolution = Recipe(
|
||||||
|
name="Alumina Solution",
|
||||||
|
building=Machines.Refinery,
|
||||||
|
outputs={
|
||||||
|
Items.AluminaSolution: 120.0
|
||||||
|
},
|
||||||
|
inputs={
|
||||||
|
Items.Water: 180.0,
|
||||||
|
Items.Bauxite: 120.0,
|
||||||
|
},
|
||||||
|
byproducts={Items.Silica: 50.0}
|
||||||
)
|
)
|
||||||
Reference in New Issue
Block a user