Created
November 4, 2024 03:26
-
-
Save apocalyptech/cc99a7467e4a10f7543d41c4a924a729 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
#!/usr/bin/env python3 | |
# vim: set expandtab tabstop=4 shiftwidth=4: | |
import os | |
import sys | |
import math | |
import argparse | |
# I spent a bit of time banging my head against these without really | |
# getting to the nub of the final Recycled loop. This post helped: | |
# | |
# https://www.reddit.com/r/SatisfactoryGame/comments/x94t7i/could_use_help_with_the_math_behind_the_oil_loop/ | |
# | |
# ... but in the end it was Haxton from the Discord who pointed out | |
# what I missed. | |
# | |
# Specifically: | |
# | |
# Fp = (P * 17/27) + (R * 8/27) - (F * 1/27) | |
# Fr = (P * 7/27) + (R * 16/27) - (F * 2/27) | |
# | |
# F = Desired fuel output | |
# R = Desired Rubber Output | |
# P = Desired Plastic Output | |
# Fr = Fuel sent to the Recycled Rubber refineries | |
# Fp = Fuel sent to the Recycled Plastic refineries | |
# R0 = "seed" rubber created by the Residual Rubber refineries | |
# C = Amount of crude oil | |
# | |
# First up, some ratios and "basic" info that we know: | |
# | |
# HOR: 3 Crude Oil -> 4 HOR + 2 Resin (3:4:2) | |
# Residual: 4 Resin + 4 Water -> 2 Rubber (2:2:1) | |
# Diluted: 5 HOR + 10 Water -> 10 Fuel (1:2:2) | |
# Recycled Plastic: 6 Rubber + 6 Fuel -> 12 Plastic (1:1:2) | |
# Recycled Rubber: 6 Plastic + 6 Fuel -> 12 Rubber (1:1:2) | |
# | |
# C = (F + R + P)/3 | |
# Resin output: (2/3)C | |
# HOR output: (4/3)C | |
# R0: 1/2 * (2/3)C -> (1/3)C | |
# Total fuel: 2 * (4/3)C -> (8/3)C | |
# | |
# Now the basic equations we'll use to solve: | |
# | |
# R = 2Fr - Fp + R0 | |
# P = 2Fp - Fr | |
# R0 = (1/3)C | |
# F + Fr + Fp = (8/3)C | |
# | |
# First we'll solve for Fr; we start by getting an equation | |
# giving us R in terms of Fr, Fp, and F: | |
# | |
# C = (3/8)(F + Fr + Fp) | |
# R0 = (1/8)(F + Fr + Fp) | |
# R = 2Fr - Fp + (1/8)(F + Fr + Fp) | |
# R = ((16+1)/8)Fr + ((-8+1)/8)Fp + (1/8)F | |
# R = (17/8)Fr - (7/8)Fp + (1/8)F | |
# | |
# Now get Fp in terms of P and Fr, and sub it in to that one, | |
# which will eventually let us write Fr in terms of R, P, and F. | |
# | |
# 2Fp = P + Fr | |
# Fp = (1/2)P + (1/2)Fr | |
# R = (17/8)Fr - (7/8)((1/2)P + (1/2)Fr) + (1/8)F | |
# R = ((34-7)/16)Fr - (7/16)P + (1/8)F | |
# R = (27/16)Fr - (7/16)P + (2/16)F | |
# 16R = 27Fr - 7P + 2F | |
# 27Fr = 16R + 7P - 2F | |
# Fr = (16/27)R + (7/27)P - (2/27)F | |
# | |
# Technically that's all we need! There'd be a couple of ways | |
# to get Fp. One is that we know the total Fuel count, so just | |
# subtract Fr from that. We *could* get an equation for Fp | |
# basically the exact same way we did Fr; take our initial P=foo | |
# equation and sub in Fr: | |
# | |
# P = 2Fp - (16/27)R - (7/27)P + (2/27)F | |
# 2Fp = ((27+7)/27)P + (16/27)R - (2/27)F | |
# 2FP = (34/27)P + (16/27)R - (2/27)F | |
# Fp = (17/27)P + (8/27)R - (1/27)F | |
# | |
# Alternatively, could sub Fr into the `F + Fr + Fp = (8/3)C` | |
# equation. Not sure which is simpler: | |
# | |
# Fp = (8/3)C - Fr -F | |
# Fp = (8/3)((F+R+P)/3) - Fr - F | |
# Fp = (8/9)F + (8/9)R + (8/9)P - Fr - F | |
# Fp = (24/27)F + (24/27)R + (24/27)P - (16/27)R - (7/27)R + (2/27)R - (27/27)F | |
# Fp = ((24-16)/27)R + ((24-7)/27)P + ((24+2-27)/27)F | |
# Fp = (8/27)R + (17/27)P - (1/27)F | |
# | |
# (oops, put things in a slightly different order for that one.) | |
# | |
# Anyway, nice to have equations for it! | |
def num(value): | |
rounded = round(value, 4) | |
if round(value, 4) == int(value): | |
return int(value) | |
else: | |
return rounded | |
def normalize(label, value): | |
new_value = math.ceil(value/10.125) * 10.125 | |
if round(value, 4) != round(new_value, 4): | |
print(f'NOTICE: Normalized {label} to: {num(new_value)}') | |
return new_value | |
parser = argparse.ArgumentParser( | |
description='Satisfactory Petrochemical Solver', | |
) | |
parser.add_argument('-p', '--plastic', | |
type=float, | |
default=0, | |
help='Amount of Plastic to consume', | |
) | |
parser.add_argument('-r', '--rubber', | |
type=float, | |
default=0, | |
help='Amount of Rubber to consume', | |
) | |
parser.add_argument('-f', '--fuel', | |
type=float, | |
default=0, | |
help='Amount of Fuel to consume', | |
) | |
parser.add_argument('-n', '--normalize', | |
action='store_true', | |
help='Normalize to "clean" numbers', | |
) | |
args = parser.parse_args() | |
if all([ | |
args.plastic == 0, | |
args.rubber == 0, | |
args.fuel == 0, | |
]): | |
parser.error('At least one resource must be specified') | |
if args.plastic == 0 and args.rubber == 0 and args.fuel > 0: | |
print('ERROR: This utility does not currently support *only* fuel output.') | |
print('The math works out differently. Just send Crude Oil to HOR and then') | |
print("over to Diluted (packaged or otherwise), can't *quite* get 3x with") | |
print('that.') | |
print('') | |
sys.exit(1) | |
if args.normalize: | |
args.plastic = normalize('plastic', args.plastic) | |
args.rubber = normalize('rubber', args.rubber) | |
args.fuel = normalize('fuel', args.fuel) | |
total_resources = sum([ | |
args.plastic, | |
args.rubber, | |
args.fuel, | |
]) | |
crude_oil = total_resources/3 | |
### | |
### HOR Refineries | |
### | |
# This should really be abstracted, but whatever | |
hor_in_crude = 30 | |
hor_out_hor = 40 | |
hor_out_resin = 20 | |
num_hor = crude_oil/hor_in_crude | |
oil_input = num_hor*hor_in_crude | |
hor_output = num_hor*hor_out_hor | |
resin_output = num_hor*hor_out_resin | |
### | |
### Diluted Fuel | |
### | |
# Using the blender version for the math, but we'll normalize later | |
# if needed. The ratios are the same. | |
diluted_in_hor = 50 | |
diluted_in_water = 100 | |
diluted_out_fuel = 100 | |
num_diluted = hor_output/diluted_in_hor | |
water_input_diluted = num_diluted*diluted_in_water | |
fuel_output = num_diluted*diluted_out_fuel | |
fuel_output_loop = fuel_output - args.fuel | |
fuel_output_rubber = (args.plastic * (7/27)) + (args.rubber * (16/27)) - (args.fuel * (2/27)) | |
fuel_output_plastic = (args.plastic * (17/27)) + (args.rubber * (8/27)) - (args.fuel * (1/27)) | |
### | |
### Residual Rubber | |
### | |
res_in_resin = 40 | |
res_in_water = 40 | |
res_out_rubber = 20 | |
num_res = resin_output/res_in_resin | |
water_input_res = num_res*res_in_water | |
rubber_output_res = num_res*res_out_rubber | |
water_input_total = water_input_diluted + water_input_res | |
### | |
### Recycled Rubber | |
### | |
recycled_in_mat = 30 | |
recycled_in_fuel = 30 | |
recycled_out_mat = 60 | |
num_recycled_rubber = fuel_output_rubber/recycled_in_fuel | |
recycled_rubber_output = num_recycled_rubber*recycled_out_mat | |
recycled_rubber_loop = recycled_rubber_output - args.rubber | |
### | |
### Recycled Plastic | |
### | |
num_recycled_plastic = fuel_output_plastic/recycled_in_fuel | |
recycled_plastic_output = num_recycled_plastic*recycled_out_mat | |
recycled_plastic_loop = recycled_plastic_output - args.plastic | |
### | |
### Report! | |
### | |
print(f'Crude Oil Input: {num(oil_input)}') | |
print(f'Water Input: {num(water_input_total)}') | |
print(f' -> {num(water_input_diluted)} to Diluted Fuel') | |
print(f' -> {num(water_input_res)} to Residual Rubber') | |
print('') | |
print(f'Number of HOR refineries: {num(num_hor)}') | |
print(f' -> HOR to Diluted Fuel: {num(hor_output)}') | |
print(f' -> Resin to Residual Rubber: {num(resin_output)}') | |
print('') | |
print(f'Number of Residual Rubber refineries: {num(num_res)}') | |
print(f' -> Rubber to Recycled Plastic: {num(rubber_output_res)}') | |
print('') | |
print(f'Number of Diluted Fuel blenders: {num(num_diluted)}') | |
num_packaged = num_diluted*(100/60) | |
print(f'(Or, number of Diluted Packaged Fuel loops: {num(num_packaged)})') | |
if args.fuel > 0: | |
print(f' -> FUEL OUTPUT: {num(args.fuel)}') | |
print(f' -> Total fuel to Recycling loop: {num(fuel_output_loop)}') | |
print(f' -> Fuel to Recycled Rubber: {num(fuel_output_rubber)}') | |
print(f' -> Fuel to Recycled Plastic: {num(fuel_output_plastic)}') | |
print('') | |
print(f'Number of Recycled Rubber refineries: {num(num_recycled_rubber)}') | |
print(f' -> Total rubber: {num(recycled_rubber_output)}') | |
print(f' -> Rubber looped to Recycled Plastic: {num(recycled_rubber_loop)}') | |
if args.rubber > 0: | |
print(f' -> RUBBER OUTPUT: {num(args.rubber)}') | |
print('') | |
print(f'Number of Recycled Plastic refineries: {num(num_recycled_plastic)}') | |
print(f' -> Total plastic: {num(recycled_plastic_output)}') | |
print(f' -> Plastic looped to Recycled Rubber: {num(recycled_plastic_loop)}') | |
if args.plastic > 0: | |
print(f' -> PLASTIC OUTPUT: {num(args.plastic)}') | |
print('') | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment