Separate material and trade logic
This commit is contained in:
parent
4e2a1691a3
commit
c8b6563d17
4 changed files with 287 additions and 221 deletions
171
elite_engineering/materials.py
Normal file
171
elite_engineering/materials.py
Normal file
|
@ -0,0 +1,171 @@
|
|||
import dataclasses
|
||||
import enum
|
||||
import fractions
|
||||
import typing
|
||||
|
||||
from returns.maybe import Maybe, Some, Nothing
|
||||
|
||||
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
|
||||
|
||||
MAX_CAPACITY: int = 300
|
||||
|
||||
def trade_ratio(self, other: "Material") -> Maybe[fractions.Fraction]:
|
||||
if self.type_ != other.type_:
|
||||
return Nothing
|
||||
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)
|
||||
}
|
|
@ -1,182 +1,21 @@
|
|||
import dataclasses
|
||||
import enum
|
||||
import fractions
|
||||
import typing
|
||||
|
||||
from returns.maybe import Maybe, Some, Nothing
|
||||
from returns.maybe import Maybe, Nothing, Some
|
||||
|
||||
MAX_CAPACITY: int = 300
|
||||
|
||||
|
||||
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
|
||||
|
||||
MAX_CAPACITY: int = 300
|
||||
|
||||
def trade_ratio(self, other: "Material") -> Maybe[fractions.Fraction]:
|
||||
if self.type_ != other.type_:
|
||||
return Nothing
|
||||
grade_base = 3 if self.grade <= other.grade else 6
|
||||
grade_ratio = fractions.Fraction(
|
||||
grade_base ** self.grade, grade_base ** other.grade
|
||||
)
|
||||
def ratio(
|
||||
grade_in: int, grade_out: int, across_categories: bool
|
||||
) -> Maybe[fractions.Fraction]:
|
||||
trading_up = grade_out > grade_in
|
||||
grade_base = 6 if trading_up else 3
|
||||
grade_ratio = fractions.Fraction(grade_base ** grade_out, grade_base ** grade_in)
|
||||
category_ratio = (
|
||||
fractions.Fraction(1, 1)
|
||||
if self.category == other.category
|
||||
else fractions.Fraction(6, 1)
|
||||
fractions.Fraction(6, 1) if across_categories else fractions.Fraction(1, 1)
|
||||
)
|
||||
ratio = grade_ratio * category_ratio
|
||||
if ratio.numerator > self.MAX_CAPACITY or ratio.denominator > self.MAX_CAPACITY:
|
||||
result = grade_ratio * category_ratio
|
||||
if max(result.numerator, result.denominator) > MAX_CAPACITY:
|
||||
return Nothing
|
||||
else:
|
||||
return Some(ratio)
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
return Some(result)
|
||||
|
|
93
tests/test_materials.py
Normal file
93
tests/test_materials.py
Normal file
|
@ -0,0 +1,93 @@
|
|||
import fractions
|
||||
import unittest
|
||||
|
||||
from elite_engineering import materials
|
||||
|
||||
|
||||
class TradeCalculationTests(unittest.TestCase):
|
||||
def format_ratio(self, ratio: fractions.Fraction) -> str:
|
||||
return "{} → {}".format(ratio.numerator, ratio.denominator)
|
||||
|
||||
def test_same_category_exchange(self) -> None:
|
||||
expected = [
|
||||
["1 → 1", "1 → 3", "1 → 9", "1 → 27", "1 → 81"],
|
||||
["6 → 1", "1 → 1", "1 → 3", "1 → 9", "1 → 27"],
|
||||
["36 → 1", "6 → 1", "1 → 1", "1 → 3", "1 → 9"],
|
||||
["216 → 1", "36 → 1", "6 → 1", "1 → 1", "1 → 3"],
|
||||
["-", "216 → 1", "36 → 1", "6 → 1", "1 → 1"],
|
||||
]
|
||||
|
||||
actual = [
|
||||
[
|
||||
materials.Material(
|
||||
"name", materials.MaterialType.raw, "category", grade_in
|
||||
)
|
||||
.trade_ratio(
|
||||
materials.Material(
|
||||
"name", materials.MaterialType.raw, "category", grade_out
|
||||
)
|
||||
)
|
||||
.map(self.format_ratio)
|
||||
.value_or("-")
|
||||
for grade_in in [1, 2, 3, 4, 5]
|
||||
]
|
||||
for grade_out in [1, 2, 3, 4, 5]
|
||||
]
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_different_category_exchange(self) -> None:
|
||||
expected = [
|
||||
["6 → 1", "2 → 1", "2 → 3", "2 → 9", "2 → 27"],
|
||||
["36 → 1", "6 → 1", "2 → 1", "2 → 3", "2 → 9"],
|
||||
["216 → 1", "36 → 1", "6 → 1", "2 → 1", "2 → 3"],
|
||||
["-", "216 → 1", "36 → 1", "6 → 1", "2 → 1"],
|
||||
["-", "-", "216 → 1", "36 → 1", "6 → 1"],
|
||||
]
|
||||
|
||||
actual = [
|
||||
[
|
||||
materials.Material(
|
||||
"name", materials.MaterialType.raw, "category", grade_in
|
||||
)
|
||||
.trade_ratio(
|
||||
materials.Material(
|
||||
"name", materials.MaterialType.raw, "other", grade_out
|
||||
)
|
||||
)
|
||||
.map(self.format_ratio)
|
||||
.value_or("-")
|
||||
for grade_in in [1, 2, 3, 4, 5]
|
||||
]
|
||||
for grade_out in [1, 2, 3, 4, 5]
|
||||
]
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_different_type_exchange(self) -> None:
|
||||
expected = [
|
||||
["-", "-", "-", "-", "-"],
|
||||
["-", "-", "-", "-", "-"],
|
||||
["-", "-", "-", "-", "-"],
|
||||
["-", "-", "-", "-", "-"],
|
||||
["-", "-", "-", "-", "-"],
|
||||
]
|
||||
|
||||
actual = [
|
||||
[
|
||||
materials.Material(
|
||||
"name", materials.MaterialType.raw, "category", grade_in
|
||||
)
|
||||
.trade_ratio(
|
||||
materials.Material(
|
||||
"name", materials.MaterialType.encoded, "other", grade_out
|
||||
)
|
||||
)
|
||||
.map(self.format_ratio)
|
||||
.value_or("-")
|
||||
for grade_in in [1, 2, 3, 4, 5]
|
||||
]
|
||||
for grade_out in [1, 2, 3, 4, 5]
|
||||
]
|
||||
|
||||
self.assertEqual(expected, actual)
|
|
@ -1,9 +1,6 @@
|
|||
import fractions
|
||||
import unittest
|
||||
|
||||
from returns.curry import partial
|
||||
from returns.maybe import Maybe
|
||||
|
||||
from elite_engineering import trade
|
||||
|
||||
|
||||
|
@ -20,22 +17,15 @@ class TradeCalculationTests(unittest.TestCase):
|
|||
["-", "216 → 1", "36 → 1", "6 → 1", "1 → 1"],
|
||||
]
|
||||
|
||||
material = partial(
|
||||
trade.Material,
|
||||
"name",
|
||||
trade.MaterialType.raw,
|
||||
"category",
|
||||
)
|
||||
actual = [
|
||||
[
|
||||
material(grade_in)
|
||||
.trade_ratio(material(grade_out))
|
||||
trade.ratio(grade_in, grade_out, across_categories=False)
|
||||
.map(self.format_ratio)
|
||||
.value_or("-")
|
||||
for grade_out in [1, 2, 3, 4, 5]
|
||||
]
|
||||
for grade_in in [1, 2, 3, 4, 5]
|
||||
]
|
||||
for grade_out in [1, 2, 3, 4, 5]
|
||||
]
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
@ -48,41 +38,14 @@ class TradeCalculationTests(unittest.TestCase):
|
|||
["-", "-", "216 → 1", "36 → 1", "6 → 1"],
|
||||
]
|
||||
|
||||
type_ = trade.MaterialType.raw
|
||||
material = partial(trade.Material, "name", trade.MaterialType.raw)
|
||||
actual = [
|
||||
[
|
||||
material("category", grade_in)
|
||||
.trade_ratio(material("other", grade_out))
|
||||
trade.ratio(grade_in, grade_out, across_categories=True)
|
||||
.map(self.format_ratio)
|
||||
.value_or("-")
|
||||
for grade_out in [1, 2, 3, 4, 5]
|
||||
]
|
||||
for grade_in in [1, 2, 3, 4, 5]
|
||||
]
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_different_type_exchange(self) -> None:
|
||||
expected = [
|
||||
["-", "-", "-", "-", "-"],
|
||||
["-", "-", "-", "-", "-"],
|
||||
["-", "-", "-", "-", "-"],
|
||||
["-", "-", "-", "-", "-"],
|
||||
["-", "-", "-", "-", "-"],
|
||||
]
|
||||
|
||||
type_ = trade.MaterialType.raw
|
||||
material = partial(trade.Material, "name")
|
||||
actual = [
|
||||
[
|
||||
material(trade.MaterialType.raw, "category", grade_in)
|
||||
.trade_ratio(material(trade.MaterialType.encoded, "other", grade_out))
|
||||
.map(self.format_ratio)
|
||||
.value_or("-")
|
||||
for grade_out in [1, 2, 3, 4, 5]
|
||||
]
|
||||
for grade_in in [1, 2, 3, 4, 5]
|
||||
]
|
||||
for grade_out in [1, 2, 3, 4, 5]
|
||||
]
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
|
Loading…
Reference in a new issue