------------------------------------------------------------------------
-- |
-- Module      :  ALife.Creatur.Genetics.Reproduction.SimplifiedSexual
-- Copyright   :  (c) 2012-2021 Amy de Buitléir
-- License     :  BSD-style
-- Maintainer  :  amy@nualeargais.ie
-- Stability   :  experimental
-- Portability :  portable
--
-- A reproduction method for artificial lifeforms where:
--
-- * Each agent has a /single/ strand of genetic information.
--
-- * Each child has two parents.
--
-- * Each parent contributes approximately half of its genetic
--   information to the offspring.
--
------------------------------------------------------------------------
{-# LANGUAGE TypeFamilies #-}
module ALife.Creatur.Genetics.Reproduction.SimplifiedSexual
  (
    Reproductive(..)
  ) where

import ALife.Creatur (AgentId)
import Control.Monad.Random (Rand, RandomGen)

-- | A species that reproduces, transmitting genetic information to
--   its offspring. Minimal complete definition: all except @mate@.
class Reproductive a where

  -- | A sequence of hereditary information for an agent.
  type Strand a

  -- | Recombines the genetic information from two parents, creating
  --   genetic information for potential offspring.
  --
  --   Typically this involves the following steps:
  --
  --   1. Recombine the two strands of genetic information (one from
  --      each parent) to obtain two new strands.
  --
  --   1. Discard one strand, and return the remaining one.
  recombine :: RandomGen r => a -> a -> Rand r (Strand a)

  -- | Builds an agent based on the genome provided, if it is possible
  --   to do so.
  build :: AgentId -> Strand a -> Either [String] a

  -- | @'makeOffspring' (parent1, parent2) name@ uses the genetic
  --   information from @parent1@ and @parent2@ to produce a child with
  --   the agent ID @name@. The default implementation:
  --
  --   1. Calls @'recombine'@ to create a genome for the child.
  --
  --   2. Calls @'build'@ to construct a child with this genome.
  makeOffspring
    :: RandomGen r
      => a -> a -> AgentId -> Rand r (Either [String] a)
  makeOffspring a
a a
b AgentId
name = do
    Strand a
g <- a -> a -> Rand r (Strand a)
forall a r.
(Reproductive a, RandomGen r) =>
a -> a -> Rand r (Strand a)
recombine a
a a
b
    Either [AgentId] a -> Rand r (Either [AgentId] a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [AgentId] a -> Rand r (Either [AgentId] a))
-> Either [AgentId] a -> Rand r (Either [AgentId] a)
forall a b. (a -> b) -> a -> b
$ AgentId -> Strand a -> Either [AgentId] a
forall a.
Reproductive a =>
AgentId -> Strand a -> Either [AgentId] a
build AgentId
name Strand a
g