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,
             ]),

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': [],
+                    },
+                }
+            }
+        )

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.
tests/test_components.py::MineralConcentrationComponentTestCase::test_random tests/test_engine.py::PersistenceTestCase::test_planet tests/test_engine.py::PersistenceTestCase::test_ship tests/test_systems.py::PopulationTestCase::test_population_growth
tests/test_components.py::MetadataComponentTestCase::test_missing_required_field tests/test_components.py::MetadataComponentTestCase::test_simple tests/test_components.py::EnvironmentComponentTestCase::test_component_display tests/test_components.py::EnvironmentComponentTestCase::test_display_fields tests/test_components.py::EnvironmentComponentTestCase::test_random tests/test_engine.py::PersistenceTestCase::test_species tests/test_engine.py::MovementTestCase::test_add_to_queued_moves tests/test_engine.py::MovementTestCase::test_empty_universe tests/test_engine.py::MovementTestCase::test_four_way_cycle_intercept tests/test_engine.py::MovementTestCase::test_multi_turn_single_move tests/test_engine.py::MovementTestCase::test_mutual_intercept tests/test_engine.py::MovementTestCase::test_one_object_single_turn_move tests/test_engine.py::MovementTestCase::test_one_stationary_object tests/test_engine.py::MovementTestCase::test_previously_queued_move tests/test_engine.py::MovementTestCase::test_replace_queued_move tests/test_engine.py::MovementTestCase::test_slow_motion tests/test_engine.py::MovementTestCase::test_target_can_barely_reach tests/test_engine.py::MovementTestCase::test_target_moving_object tests/test_engine.py::MovementTestCase::test_target_stationary_object tests/test_engine.py::MovementTestCase::test_target_stops_short tests/test_engine.py::MovementTestCase::test_three_way_cycle_intercept