Last active
January 1, 2018 23:46
-
-
Save EliseAv/e58cbd75fc7c549d8ea9a7eef2ec8c74 to your computer and use it in GitHub Desktop.
Factorio Rates
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from collections import namedtuple | |
from fractions import Fraction | |
from functools import partial | |
from itertools import chain | |
from math import log, floor | |
class FractionDict(dict): | |
zero = Fraction() | |
def __init__(self, contents: dict): | |
super().__init__(contents) | |
def __getitem__(self, item): | |
return self.get(item, self.zero) | |
def __setitem__(self, key, value): | |
if value: | |
if not isinstance(value, Fraction): | |
value = Fraction(value) | |
super().__setitem__(key, value) | |
elif key in self: | |
del self[key] | |
def __str__(self): | |
return ' + '.join(str(v) + ' ' + k for k, v in self.items() if v) | |
class Machine(namedtuple('Machine', ('time', 'inputs', 'outputs'))): | |
def __str__(self): | |
return f'{self.time} s + {FractionDict(self.inputs)} = {FractionDict(self.outputs)}' | |
def make(s, crafting_speed=1, **kwargs): | |
try: | |
return Machine(Fraction(s) / crafting_speed, | |
tuple((k, -Fraction(v)) for k, v in kwargs.items() if v < 0), | |
tuple((k, Fraction(v)) for k, v in kwargs.items() if v > 0)) | |
except TypeError: | |
print((s, crafting_speed)) | |
raise | |
am1 = partial(make, crafting_speed=Fraction(0.5)) | |
am2 = partial(make, crafting_speed=Fraction(0.75)) | |
am3 = partial(make, crafting_speed=Fraction(1.25)) | |
chemical_plant = am3 | |
centrifuge = am2 | |
cokery = partial(make, crafting_speed=2) | |
class BlueprintPlan: | |
def __init__(self, machine: Machine, *more_machines): | |
self.machines = FractionDict({machine: Fraction(1)}) | |
self.inputs = FractionDict({k: v / machine.time for k, v in machine.inputs}) | |
self.partways = FractionDict({}) | |
self.outputs = FractionDict({k: v / machine.time for k, v in machine.outputs}) | |
for other in more_machines: | |
self.add(other) | |
def add(self, machine: Machine, pivot: str = None): | |
if not pivot: | |
pivot = next(chain((p for p, _ in machine.inputs if p in self.outputs), | |
(p for p, _ in machine.outputs if p in self.inputs)), pivot) | |
# Calculate number of machines required to keep full use of pivot | |
if pivot in self.outputs: | |
machine_pivot_input = next(quantity for item, quantity in machine.inputs if item == pivot) | |
number_of_machines = self.outputs[pivot] * machine.time / machine_pivot_input | |
elif pivot in self.inputs: | |
machine_pivot_output = next(quantity for item, quantity in machine.outputs if item == pivot) | |
number_of_machines = self.inputs[pivot] * machine.time / machine_pivot_output | |
else: | |
raise ValueError("Cannot find pivot: " + str(pivot)) | |
# Find the rate of consumption relative to me | |
self.machines[machine] += number_of_machines | |
for item, quantity in machine.inputs: | |
self.inputs[item] += quantity * number_of_machines / machine.time | |
for item, quantity in machine.outputs: | |
self.outputs[item] += quantity * number_of_machines / machine.time | |
# Cancel out inputs and outputs | |
for item in set(i for i in self.outputs if i in self.inputs): | |
cancelled = min(self.inputs[item], self.outputs[item]) | |
self.inputs[item] -= cancelled | |
self.outputs[item] -= cancelled | |
self.partways[item] += cancelled | |
# Normalize number of machines | |
factor = number_of_machines.denominator | |
if factor != 1: | |
for k, v in self.machines.items(): | |
self.machines[k] = v * factor | |
for k, v in self.inputs.items(): | |
self.inputs[k] = v * factor | |
for k, v in self.partways.items(): | |
self.partways[k] = v * factor | |
for k, v in self.outputs.items(): | |
self.outputs[k] = v * factor | |
def report(self): | |
print('') | |
print(f'Have {len(self.machines)} machine types, totaling {sum(self.machines.values())} machines:') | |
for machine, quantity in self.machines.items(): | |
print(f'- {quantity} times: {machine}') | |
pairs = (('Inputs', self.inputs), | |
('Partways', self.partways), | |
('Outputs', self.outputs)) | |
for kind, dictionary in pairs: | |
print('') | |
print(f'{kind} ({len(dictionary)} types, total {fmtrate(sum(dictionary.values()))} per second):') | |
for item, rate in dictionary.items(): | |
print(f'- {fmtrate(rate)} {item} per second') | |
def fmtrate(value): | |
human_powers = ('', 'k', 'M') | |
if not isinstance(value, float): | |
value = float(value) | |
sign = suffix = '' | |
if value < 0: | |
sign = '-' | |
value = abs(value) | |
power = floor(log(value, 10)) | |
for current_suffix in human_powers: | |
suffix = current_suffix | |
if value < 1000: | |
break | |
else: | |
value /= 1000 | |
power -= 3 | |
rounded = round(value, 2 - power) | |
integer = int(rounded) | |
return sign + str(integer if integer == rounded else rounded) + suffix |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from calc1 import BlueprintPlan | |
from mods import BioIndustries, Base | |
def main(): | |
plastics_and_stuff() | |
fertilizer() | |
def nuclear_reactor(): | |
BlueprintPlan(Base.nuclear_reactor, | |
Base.fuel_reprocessing).report() | |
def circuits(): | |
BlueprintPlan(Base.bluec, | |
Base.redc, | |
Base.greenc, | |
Base.wire, | |
Base.acid, | |
Base.sulfur).report() | |
def wood_to_coal(): | |
BlueprintPlan(BioIndustries.char2coal, | |
BioIndustries.pulp2char, | |
BioIndustries.wood2pulp).report() | |
def fertilizer(): | |
BlueprintPlan(BioIndustries.fert2, | |
BioIndustries.fert1, | |
BioIndustries.nitrogen, | |
BioIndustries.pulp2ash, | |
BioIndustries.wood2pulp).report() | |
def plastics_and_stuff(): | |
plan = BlueprintPlan(BioIndustries.sulfur, | |
BioIndustries.acid) | |
plan.add(BioIndustries.cellulose, 'cellulose') | |
plan.report() | |
def uranium_ammo(): | |
BlueprintPlan(Base.ammo1, | |
Base.ammo2, | |
Base.ammo3).report() | |
if __name__ == '__main__': | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from calc1 import make, chemical_plant, cokery, am3, centrifuge | |
class Base: | |
wire = am3(0.5, copper=-1, wire=2) | |
greenc = am3(0.5, iron=-1, wire=-3, greenc=1) | |
redc = am3(6, wire=-4, greenc=-2, plastic=-2, redc=1) | |
bluec = am3(10, redc=-2, greenc=-20, acid=-5, bluec=1) | |
ammo1 = am3(1, iron=-4, ammo1=1) | |
ammo2 = am3(3, copper=-5, ammo1=-1, steel=-1, ammo2=1) | |
ammo3 = am3(10, ammo2=-1, uranium=-1, ammo3=1) | |
coal_liquefaction = make(5, coal=-10, steam=-50, heavy=10, light=15, gas=20) | |
offshore_pump = make(1, water=1200) | |
nuclear_reactor = make(200, cell=-1, depleted=1) | |
fuel_reprocessing = centrifuge(50, depleted=-5, uranium=3) | |
sulfur = chemical_plant(1, water=-30, gas=-30, sulfur=2) | |
acid = chemical_plant(1, sulfur=-5, iron=-1, water=-100, acid=50) | |
class BioIndustries: | |
wood2pulp = cokery(5, wood=-2, pulp=6) | |
pulp2char = cokery(12.5, pulp=-40, charcoal=18) | |
char2coal = cokery(18, charcoal=-12, coal=10) | |
pulp2ash = cokery(5, pulp=-10, ash=10) | |
cellulose = chemical_plant(20, pulp=-10, acid=-10, cellulose=10) | |
cellulose_steam = chemical_plant(5, pulp=-10, steam=-10, acid=-20, cellulose=10) | |
gas = make(5, biomass=-10, water=-10, gas=20) | |
light = make(5, biomass=-100, water=-10, light=80, cellulose=2) | |
plastic1 = chemical_plant(1, wood=-10, steam=-10, light=-20, plastic=2) | |
plastic2 = chemical_plant(1, cellulose=-1, gas=-10, plastic=2) | |
fert1 = chemical_plant(5, sulfur=-1, ash=-10, nitrogen=-10, fert1=5) | |
fert2 = chemical_plant(50, fert1=-20, pulp=-10, biomass=-10, fert2=20) | |
nitrogen = chemical_plant(10, air=-20, nitrogen=20) | |
air = chemical_plant(1, air=10) | |
biomass2 = make(10, ash=-10, water=-90, air=-10, biomass=90) | |
sulfur = chemical_plant(10, ash=-10, acid=-10, sulfur=10) | |
acid = chemical_plant(10, cellulose=-5, water=-90, biomass=-10, acid=50) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment