|
|
@@ -8,19 +8,62 @@ import httpx
|
|
|
|
|
|
def main() -> None:
|
|
|
planet = sys.argv[1].casefold()
|
|
|
- burn = planet_burn(planet)
|
|
|
+ burn, raw_materials = fio_data(planet)
|
|
|
|
|
|
inventory = {item['MaterialTicker']: item['MaterialAmount'] for item in burn['Inventory']}
|
|
|
+ materials = {mat['Ticker']: mat for mat in raw_materials}
|
|
|
producing = frozenset(item['MaterialTicker'] for item in burn['OrderProduction'])
|
|
|
- for consumption in burn['OrderConsumption'] + burn['WorkforceConsumption']:
|
|
|
+ consumables = [c for c in burn['OrderConsumption'] + burn['WorkforceConsumption']
|
|
|
+ if c['MaterialTicker'] not in producing]
|
|
|
+
|
|
|
+ vol_per_day = 0.0
|
|
|
+ weight_per_day = 0.0
|
|
|
+ target_days = float('inf')
|
|
|
+ for consumption in consumables:
|
|
|
+ ticker = consumption['MaterialTicker']
|
|
|
+ vol_per_day += materials[ticker]['Volume'] * consumption['DailyAmount']
|
|
|
+ weight_per_day += materials[ticker]['Weight'] * consumption['DailyAmount']
|
|
|
+ days = inventory.get(ticker, 0) / consumption['DailyAmount']
|
|
|
+ if days < target_days:
|
|
|
+ target_days = days
|
|
|
+
|
|
|
+ print(f'consuming {vol_per_day:.1f}㎥/d')
|
|
|
+ print(f'consuming {weight_per_day:.1f}t/d')
|
|
|
+ limiting = 'Volume'
|
|
|
+ if weight_per_day > vol_per_day:
|
|
|
+ limiting = 'Weight'
|
|
|
+
|
|
|
+ target_days = round(target_days + 0.05, 1)
|
|
|
+ while True:
|
|
|
+ space_used = 0
|
|
|
+ buy: dict[str, float] = {}
|
|
|
+ for consumption in consumables:
|
|
|
+ ticker = consumption['MaterialTicker']
|
|
|
+ avail = inventory.get(ticker, 0)
|
|
|
+ daily_consumption = consumption['DailyAmount']
|
|
|
+ days = avail / daily_consumption
|
|
|
+ if days < target_days:
|
|
|
+ buy[ticker] = (target_days - days) * daily_consumption
|
|
|
+ space_used += buy[ticker] * materials[ticker][limiting]
|
|
|
+
|
|
|
+ if space_used > 500:
|
|
|
+ break
|
|
|
+ optimal = buy
|
|
|
+ target_days += 0.1
|
|
|
+ print('supply for', round(target_days, 1), 'days')
|
|
|
+
|
|
|
+ for consumption in consumables:
|
|
|
ticker = consumption['MaterialTicker']
|
|
|
- if ticker in producing:
|
|
|
- continue
|
|
|
avail = inventory.get(ticker, 0)
|
|
|
- days = avail / consumption['DailyAmount']
|
|
|
- print(f'{ticker}: {avail} ({consumption['DailyAmount']:.2f}/d) → {days:.2f} d')
|
|
|
+ daily_consumption = consumption['DailyAmount']
|
|
|
+ days = avail / daily_consumption
|
|
|
+ print(f'{ticker:>3}: {avail:5d} ({daily_consumption:8.2f}/d) {days:4.1f} d', end='')
|
|
|
+ if need := optimal.get(ticker): # pyright: ignore[reportPossiblyUnboundVariable]
|
|
|
+ print(f' | {need:8.1f}')
|
|
|
+ else:
|
|
|
+ print()
|
|
|
|
|
|
-def planet_burn(planet: str) -> PlanetData:
|
|
|
+def fio_data(planet: str) -> tuple[PlanetData, typing.Sequence[Material]]:
|
|
|
with open('config.toml', 'rb') as f:
|
|
|
config = tomllib.load(f)
|
|
|
client = httpx.Client(headers={'Authorization': config['fio_api_key']})
|
|
|
@@ -32,8 +75,15 @@ def planet_burn(planet: str) -> PlanetData:
|
|
|
name = planet_data['PlanetName']
|
|
|
if name.casefold() == planet:
|
|
|
assert planet_data['Error'] is None
|
|
|
- return planet_data
|
|
|
- raise ValueError(planet + ' not found')
|
|
|
+ break
|
|
|
+ else:
|
|
|
+ raise ValueError(planet + ' not found')
|
|
|
+
|
|
|
+ r = client.get('https://rest.fnar.net/material/allmaterials')
|
|
|
+ materials: list[Material] = r.json()
|
|
|
+ r.raise_for_status()
|
|
|
+
|
|
|
+ return planet_data, materials
|
|
|
|
|
|
class PlanetData(typing.TypedDict):
|
|
|
PlanetName: str
|
|
|
@@ -51,5 +101,10 @@ class Inventory(typing.TypedDict):
|
|
|
MaterialTicker: str
|
|
|
MaterialAmount: int
|
|
|
|
|
|
+class Material(typing.TypedDict):
|
|
|
+ Ticker: str
|
|
|
+ Weight: float
|
|
|
+ Volume: float
|
|
|
+
|
|
|
if __name__ == '__main__':
|
|
|
main()
|