<p>Raw resources are integral part for most 4x games. Here’s one way of modeling them in Haskell. I wanted a system that is easy to use, doesn’t require too much typing and is type safe.</p>
<p>RawResource is basic building block:</p>
<pre><code>newtype RawResource a = RawResource { unRawResource :: Int }
    deriving (Show, Read, Eq)</code></pre>
<p>It can be parametrised with anything, but I’m using three different types:</p>
<pre><code>data Biological = Biological
data Mechanical = Mechanical
data Chemical = Chemical</code></pre>
<p>Example of defining harvest being 100 units of biological raw resources:</p>
<pre><code>  harvest :: RawResource Biological
  harvest = RawResource 100</code></pre>
<p>Raw resources are often manipulated (added and subtracted mostly). Defining Num instance allows us to use them as numbers:</p>
<pre><code>instance Num (RawResource t) where
    (+) (RawResource a) (RawResource b) = RawResource $ a + b
    (-) (RawResource a) (RawResource b) = RawResource $ a - b
    (*) (RawResource a) (RawResource b) = RawResource $ a * b
    abs (RawResource a) = RawResource $ abs a
    signum (RawResource a) = RawResource $ signum a
    fromInteger a = RawResource $ fromInteger a</code></pre>
<p>For example, adding harvest to stock pile:</p>
<pre><code>  stock :: RawResource Biological
  stock = RawResource 1000

  harvest :: RawResource Biological
  harvest = RawResource 100

  newStock = stock + harvest</code></pre>
<p>Comparing size of two resource piles is common operation. Ord instance has methods we need for comparing:</p>
<pre><code>instance Ord (RawResource t) where
    (&lt;=) (RawResource a) (RawResource b) = a &lt;= b</code></pre>
<p>One function is enough, as rest is defined in terms of it. Sometimes (usually for reasons of optimization), one might want to define other functions too.</p>
<p>Another way to add bunch of resources of same type together is defining Monoid instance:</p>
<pre><code>instance Semigroup (RawResource t) where
    (&lt;&gt;) a b = a + b

instance Monoid (RawResource t) where
    mempty = RawResource 0</code></pre>
<p>For example, combining harvests of many fields can be achieved as:</p>
<pre><code>  harvests :: [RawResource Biological]
  harvests = [RawResource 20, RawResource 50, RawResource 25]

  total :: RawResource Biological
  total = mappend harvests</code></pre>
<p>All these functions keep track of type of resources being manipulated. Compiler will emit an error if two different types of resources are being mixed together.</p>
<p>Raw resources are often grouped together for specific purpose. This again uses phantom types to keep track the intended usage:</p>
<pre><code>data RawResources a = RawResources
    { ccdMechanicalCost :: RawResource Mechanical
    , ccdBiologicalCost :: RawResource Biological
    , ccdChemicalCost :: RawResource Chemical
    } deriving (Show, Read, Eq)

data ResourceCost = ResourceCost
data ConstructionSpeed = ConstructionSpeed
data ConstructionLeft = ConstructionLeft
data ConstructionDone = ConstructionDone
data ResourcesAvailable = ResourcesAvailable</code></pre>
<p>And in order to be able to combine piles of RawResources, we’ll define Semigroup and Monoid instances. Notice how both instances make use of Semigroup and Monoid instances of RawResource:</p>
<pre><code>instance Semigroup (RawResources t) where
    (&lt;&gt;) a b = RawResources
        { ccdMechanicalCost = ccdMechanicalCost a &lt;&gt; ccdMechanicalCost b
        , ccdBiologicalCost = ccdBiologicalCost a &lt;&gt; ccdBiologicalCost b
        , ccdChemicalCost = ccdChemicalCost a &lt;&gt; ccdChemicalCost b
        }

instance Monoid (RawResources t) where
    mempty = RawResources
        { ccdMechanicalCost = mempty
        , ccdBiologicalCost = mempty
        , ccdChemicalCost = mempty
        }</code></pre>
<p>For those interested seeing some code, source is available at <a href="https://github.com/Tuula/deep-sky/" class="uri">https://github.com/Tuula/deep-sky/</a> (<a href="https://github.com/Tuula/deep-sky/tree/baa0807dd36b61fd02174b17c10013862af4ec18" class="uri">https://github.com/Tuula/deep-sky/tree/baa0807dd36b61fd02174b17c10013862af4ec18</a> is situation before lots of Elm related changes that I mentioned in passing in the previous episode)</p>
