import collections import dataclasses import enum import fractions import typing from elite_engineering import trade class MaterialType(enum.Enum): raw = "Raw" manufactured = "Manufactured" encoded = "Encoded" categorized = { MaterialType.raw: { "Raw Material Category 1": ["Carbon", "Vanadium", "Niobium", "Yttrium"], "Raw Material Category 2": [ "Phosphorus", "Chromium", "Molybdenum", "Technetium", ], "Raw Material Category 3": ["Sulphur", "Manganese", "Cadmium", "Ruthenium"], "Raw Material Category 4": ["Iron", "Zinc", "Tin", "Selenium"], "Raw Material Category 5": ["Nickel", "Germanium", "Tungsten", "Tellurium"], "Raw Material Category 6": ["Rhenium", "Arsenic", "Mercury", "Polonium"], "Raw Material Category 7": ["Lead", "Zirconium", "Boron", "Antimony"], }, MaterialType.manufactured: { "Chemical": [ "Chemical Storage Units", "Chemical Processors", "Chemical Distillery", "Chemical Manipulators", "Pharmaceutical Isolators", ], "Thermic": [ "Tempered Alloys", "Heat Resistant Ceramics", "Precipitated Alloys", "Thermic Alloys", "Military Grade Alloys", ], "Heat": [ "Heat Conduction Wiring", "Heat Dispersion Plate", "Heat Exchangers", "Heat Vanes", "Proto Heat Radiators", ], "Conductive": [ "Basic Conductors", "Conductive Components", "Conductive Ceramics", "Conductive Polymers", "Biotech Conductors", ], "Mechanical Components": [ "Mechanical Scrap", "Mechanical Equipment", "Mechanical Components", "Configurable Components", "Improvised Components", ], "Capacitors": [ "Grid Resistors", "Hybrid Capacitors", "Electrochemical Arrays", "Polymer Capacitors", "Military Supercapacitors", ], "Shielding": [ "Worn Shield Emitters", "Shield Emitters", "Shielding Sensors", "Compound Shielding", "Imperial Shielding", ], "Composite": [ "Compact Composites", "Filament Composites", "High Density Composites", "Proprietary Composites", "Core Dynamics Composites", ], "Crystals": [ "Crystal Shards", "Flawed Focus Crystals", "Focus Crystals", "Refined Focus Crystals", "Exquisite Focus Crystals", ], "Alloys": [ "Salvaged Alloys", "Galvanising Alloys", "Phase Alloys", "Proto Light Alloys", "Proto Radiolic Alloys", ], }, MaterialType.encoded: { "Emission Data": [ "Exceptional Scrambled Emission Data", "Irregular Emission Data", "Unexpected Emission Data", "Decoded Emission Data", "Abnormal Compact Emissions Data", ], "Wake Scans": [ "Atypical Disrupted Wake Echoes", "Anomalous FSD Telemetry", "Strange Wake Solutions", "Eccentric Hyperspace Trajectories", "Datamined Wake Exceptions", ], "Shield Data": [ "Distorted Shield Cycle Recordings", "Inconsistent Shield Soak Analysis", "Untypical Shield Scans", "Aberrant Shield Pattern Analysis", "Peculiar Shield Frequency Data", ], "Encryption Files": [ "Unusual Encrypted Files", "Tagged Encryption Codes", "Open Symmetric Keys", "Atypical Encryption Archives", "Adaptive Encryptors Capture", ], "Data Archives": [ "Anomalous Bulk Scan Data", "Unidentified Scan Archives", "Classified Scan Databanks", "Divergent Scan Data", "Classified Scan Fragment", ], "Encoded Firmware": [ "Specialised Legacy Firmware", "Modified Consumer Firmware", "Cracked Industrial Firmware", "Security Firmware Patch", "Modified Embedded Firmware", ], }, } @dataclasses.dataclass class Material: name: str type_: MaterialType category: str grade: int def trade_ratio(self, other: "Material") -> typing.Optional[fractions.Fraction]: if self.type_ != other.type_: return None return trade.ratio(self.grade, other.grade, self.category != other.category) materials: typing.Dict[str, Material] = { name: Material(name, type_, category, grade) for type_, categories in categorized.items() for category, names in categories.items() for grade, name in enumerate(names) } class Inventory(collections.UserDict): """A mapping of every available material with an associated quantity.""" def __init__( self, items: typing.Optional[typing.Union[typing.Dict[str, int], "Inventory"]] = None, ) -> None: self.data = {name: 0 for name in materials} if items: self.update(items) def __setitem__(self, key, value) -> None: if key not in materials: raise KeyError(key) if value < 0: raise ValueError(value) super().__setitem__(key, value) def has(self, items: typing.Union[typing.Dict[str, int], "Inventory"]) -> bool: return all([self[name] >= quantity for name, quantity in items.items()]) def __add__(self, other: "Inventory") -> "Inventory": return Inventory({name: self[name] + qty for name, qty in other.items()}) def __sub__(self, other: "Inventory") -> "Inventory": return Inventory({name: self[name] - qty for name, qty in other.items()}) class Materials(Inventory): """Same as Inventory, but omits zeroed materials.""" def __init__( self, items: typing.Optional[typing.Union[typing.Dict[str, int], "Inventory"]] = None, ) -> None: self.data: typing.Dict[str, int] = {} if items: self.update(items) def __setitem__(self, key: str, value: int) -> None: if key not in materials: raise KeyError(key) if value < 0: raise ValueError(value) if value == 0: return super().__setitem__(key, value) def __getitem__(self, key: str) -> int: if key not in materials: raise KeyError(key) return self.data.get(key, 0) def __add__(self, other: "Inventory") -> "Materials": return Materials({name: self[name] + other[name] for name in materials}) def __sub__(self, other: "Inventory") -> "Materials": return Materials({name: self[name] - other[name] for name in materials})