Browse Source

supply cart

raylu 1 tháng trước cách đây
mục cha
commit
4818b21fbb
1 tập tin đã thay đổi với 64 bổ sung9 xóa
  1. 64 9
      supply.py

+ 64 - 9
supply.py

@@ -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()