Repository: jbradberry/universe

Issue:
Add mineral variables
Planets need to have mineral concentrations on them, in order for mining to work.

- [x] create a new mineral concentration component, consisting of ironium, boranium, and germanium
- [x] add the concentration component to the planet entity type
- [x] create a new minerals component to represent already mined minerals, consisting of the same set of minerals as above
- [x] add the new minerals component to both ships and planets
- [x] write a function or functions that randomly generates planet mineral concentrations, for use in generating an initial map.  These functions do not have to be hooked up yet.  Refer to Stars for the random distributions of these variables.

Discussion:

Patch:
diff --git a/universe/components.py b/universe/components.py
index 85c9ed2..f454a10 100644
--- a/universe/components.py
+++ b/universe/components.py
@@ -121,3 +121,27 @@ class EnvironmentComponent(Component):
             'temperature': random.choices(list(range(1, 100)), weights=weights)[0],
             'radiation': random.randint(1, 99),
         }
+
+
+class MineralConcentrationComponent(Component):
+    _name = 'mineral_concentrations'
+
+    ironium_conc = fields.IntField(min=0, max=100)
+    boranium_conc = fields.IntField(min=0, max=100)
+    germanium_conc = fields.IntField(min=0, max=100)
+
+    @classmethod
+    def random(cls):
+        return {
+            'ironium_conc': random.randint(1, 99),
+            'boranium_conc': random.randint(1, 99),
+            'germanium_conc': random.randint(1, 99),
+        }
+
+
+class MineralInventoryComponent(Component):
+    _name = 'minerals'
+
+    ironium = fields.IntField(min=0, required=False)
+    boranium = fields.IntField(min=0, required=False)
+    germanium = fields.IntField(min=0, required=False)
diff --git a/universe/engine.py b/universe/engine.py
index cd93175..f1d4246 100644
--- a/universe/engine.py
+++ b/universe/engine.py
@@ -104,10 +104,13 @@ class GameState:
             components.QueueComponent([Move, CargoTransfer, Scrap,]),
             components.OwnershipComponent(),
             components.PopulationComponent(),
+            components.MineralInventoryComponent(),
         ])
         self.manager.register_entity_type('planet', [
             components.PositionComponent(),
             components.EnvironmentComponent(),
+            components.MineralConcentrationComponent(),
+            components.MineralInventoryComponent(),
             components.QueueComponent([
                 BuildInstallation, Terraform, BuildStation, BuildShip, LaunchMassPacket,
             ]),


Test patch:
diff --git a/tests/test_components.py b/tests/test_components.py
index eb03b9e..7cfddbc 100644
--- a/tests/test_components.py
+++ b/tests/test_components.py
@@ -49,3 +49,20 @@ class EnvironmentComponentTestCase(unittest.TestCase):
         self.assertLessEqual(data['temperature'], 99)
         self.assertGreaterEqual(data['radiation'], 1)
         self.assertLessEqual(data['radiation'], 99)
+
+        # no ValidationError is raised
+        self.assertTrue(components.EnvironmentComponent().serialize(data))
+
+
+class MineralConcentrationComponentTestCase(unittest.TestCase):
+    def test_random(self):
+        data = components.MineralConcentrationComponent.random()
+        self.assertGreaterEqual(data['ironium_conc'], 1)
+        self.assertLessEqual(data['ironium_conc'], 99)
+        self.assertGreaterEqual(data['boranium_conc'], 1)
+        self.assertLessEqual(data['boranium_conc'], 99)
+        self.assertGreaterEqual(data['germanium_conc'], 1)
+        self.assertLessEqual(data['germanium_conc'], 99)
+
+        # no ValidationError is raised
+        self.assertTrue(components.MineralConcentrationComponent().serialize(data))
diff --git a/tests/test_engine.py b/tests/test_engine.py
index 6f1f1ad..518cee8 100644
--- a/tests/test_engine.py
+++ b/tests/test_engine.py
@@ -3,6 +3,124 @@ import unittest
 from universe import engine
 
 
+class PersistenceTestCase(unittest.TestCase):
+    def test_planet(self):
+        state = {
+            'turn': 2500,
+            'width': 1000,
+            'entities': {
+                0: {
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                },
+                1: {
+                    'type': 'planet',
+                    'x': 300,
+                    'y': 600,
+                    'gravity': 27,
+                    'temperature': 36,
+                    'radiation': 45,
+                    'ironium_conc': 67,
+                    'boranium_conc': 78,
+                    'germanium_conc': 82,
+                    'ironium': 20,
+                    'boranium': 30,
+                    'germanium': 40,
+                    'queue': [],
+                    'owner_id': 0,
+                    'population': 1000,
+                }
+            }
+        }
+
+        S = engine.GameState(state, {})
+        results = S.generate()
+
+        self.assertEqual(results['turn'], 2501)
+        self.assertIn(1, results['entities'])
+        self.assertEqual(results['entities'][1]['type'], 'planet')
+        self.assertEqual(results['entities'][1]['x'], 300)
+        self.assertEqual(results['entities'][1]['y'], 600)
+        self.assertEqual(results['entities'][1]['gravity'], 27)
+        self.assertEqual(results['entities'][1]['temperature'], 36)
+        self.assertEqual(results['entities'][1]['radiation'], 45)
+        self.assertEqual(results['entities'][1]['ironium_conc'], 67)
+        self.assertEqual(results['entities'][1]['boranium_conc'], 78)
+        self.assertEqual(results['entities'][1]['germanium_conc'], 82)
+        self.assertEqual(results['entities'][1]['ironium'], 20)
+        self.assertEqual(results['entities'][1]['boranium'], 30)
+        self.assertEqual(results['entities'][1]['germanium'], 40)
+        self.assertEqual(results['entities'][1]['queue'], [])
+        self.assertEqual(results['entities'][1]['owner_id'], 0)
+        self.assertGreater(results['entities'][1]['population'], 0)
+
+    def test_ship(self):
+        state = {
+            'turn': 2500,
+            'width': 1000,
+            'entities': {
+                0: {
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                },
+                1: {
+                    'type': 'ship',
+                    'x': 300,
+                    'y': 600,
+                    'ironium': 20,
+                    'boranium': 30,
+                    'germanium': 40,
+                    'queue': [],
+                    'owner_id': 0,
+                    'population': 1000,
+                }
+            }
+        }
+
+        S = engine.GameState(state, {})
+        results = S.generate()
+
+        self.assertEqual(results['turn'], 2501)
+        self.assertIn(1, results['entities'])
+        self.assertEqual(results['entities'][1]['type'], 'ship')
+        self.assertEqual(results['entities'][1]['x'], 300)
+        self.assertEqual(results['entities'][1]['y'], 600)
+        self.assertEqual(results['entities'][1]['ironium'], 20)
+        self.assertEqual(results['entities'][1]['boranium'], 30)
+        self.assertEqual(results['entities'][1]['germanium'], 40)
+        self.assertEqual(results['entities'][1]['queue'], [])
+        self.assertEqual(results['entities'][1]['owner_id'], 0)
+        self.assertGreater(results['entities'][1]['population'], 0)
+
+    def test_species(self):
+        state = {
+            'turn': 2500,
+            'width': 1000,
+            'entities': {
+                0: {
+                    'type': 'species',
+                    'name': 'Human',
+                    'plural_name': 'Humans',
+                    'growth_rate': 15,
+                },
+            }
+        }
+
+        S = engine.GameState(state, {})
+        results = S.generate()
+
+        self.assertEqual(results['turn'], 2501)
+        self.assertIn(0, results['entities'])
+        self.assertEqual(results['entities'][0]['type'], 'species')
+        self.assertEqual(results['entities'][0]['name'], 'Human')
+        self.assertEqual(results['entities'][0]['plural_name'], 'Humans')
+        self.assertEqual(results['entities'][0]['growth_rate'], 15)
+
+
 class MovementTestCase(unittest.TestCase):
     def test_empty_universe(self):
         state = {'turn': 2500, 'width': 1000, 'entities': {}}
diff --git a/tests/test_systems.py b/tests/test_systems.py
index 2877a9a..96ef1ce 100644
--- a/tests/test_systems.py
+++ b/tests/test_systems.py
@@ -24,6 +24,9 @@ class PopulationTestCase(unittest.TestCase):
                     'gravity': 50,
                     'temperature': 50,
                     'radiation': 50,
+                    'ironium_conc': 50,
+                    'boranium_conc': 50,
+                    'germanium_conc': 50,
                     'owner_id': 0,
                     'queue': [],
                 },
@@ -33,28 +36,34 @@ class PopulationTestCase(unittest.TestCase):
         S = engine.GameState(state, {})
         results = S.generate()
 
-        self.assertEqual(results,
-                         {'turn': 2501,
-                          'width': 1000,
-                          'entities': {
-                              0: {
-                                  'type': 'species',
-                                  'name': 'Human',
-                                  'plural_name': 'Humans',
-                                  'growth_rate': 15,
-                                  },
-                              1: {
-                                  'type': 'planet',
-                                  'population': 1150,
-                                  'x': 480,
-                                  'x_prev': 480,
-                                  'y': 235,
-                                  'y_prev': 235,
-                                  'gravity': 50,
-                                  'temperature': 50,
-                                  'radiation': 50,
-                                  'owner_id': 0,
-                                  'queue': [],
-                              },
-                          }
-                         })
+        self.assertEqual(
+            results,
+            {
+                'turn': 2501,
+                'width': 1000,
+                'entities': {
+                    0: {
+                        'type': 'species',
+                        'name': 'Human',
+                        'plural_name': 'Humans',
+                        'growth_rate': 15,
+                    },
+                    1: {
+                        'type': 'planet',
+                        'population': 1150,
+                        'x': 480,
+                        'x_prev': 480,
+                        'y': 235,
+                        'y_prev': 235,
+                        'gravity': 50,
+                        'temperature': 50,
+                        'radiation': 50,
+                        'ironium_conc': 50,
+                        'boranium_conc': 50,
+                        'germanium_conc': 50,
+                        'owner_id': 0,
+                        'queue': [],
+                    },
+                }
+            }
+        )