Separate material and trade logic

This commit is contained in:
Correl Roush 2022-01-22 00:21:09 -05:00
parent 4e2a1691a3
commit c8b6563d17
4 changed files with 287 additions and 221 deletions

View 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)
}

View file

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

View file

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