creatur-5.9.8: src/ALife/Creatur/Task.hs
------------------------------------------------------------------------
-- |
-- Module : ALife.Creatur.Task
-- Copyright : (c) Amy de Buitléir 2012-2015
-- License : BSD-style
-- Maintainer : amy@nualeargais.ie
-- Stability : experimental
-- Portability : portable
--
-- Provides tasks that you can use with a daemon. These tasks handle
-- reading and writing agents, and various other housekeeping chores,
-- which reduces the amount of code you need to write.
--
-- It’s also easy to write your own tasks, using these as a guide.)
--
------------------------------------------------------------------------
{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
module ALife.Creatur.Task
(
AgentProgram,
AgentsProgram,
withAgent,
withAgents,
runNoninteractingAgents,
runInteractingAgents,
simpleJob,
startupHandler,
shutdownHandler,
doNothing,
exceptionHandler,
checkPopSize,
requestShutdown
) where
import ALife.Creatur.Daemon (Job(..))
import qualified ALife.Creatur.Daemon as D
import ALife.Creatur.Universe (Universe, Agent, AgentProgram,
AgentsProgram, writeToLog, lineup, refreshLineup, markDone, endOfRound,
withAgent, withAgents, incTime, popSize)
import Control.Conditional (whenM)
import Control.Exception (SomeException)
import Control.Monad (when)
import qualified Control.Monad.Catch as C
import Control.Monad.State (StateT, execStateT, evalStateT)
import Control.Monad.Trans.Class (lift)
import Data.Serialize (Serialize)
simpleJob :: Universe u => Job u
simpleJob = Job
{
onStartup = startupHandler,
onShutdown = shutdownHandler,
onException = exceptionHandler,
task = undefined,
sleepTime = 100
}
startupHandler :: Universe u => u -> IO u
startupHandler = execStateT (writeToLog $ "Starting")
shutdownHandler :: Universe u => u -> IO ()
shutdownHandler u = evalStateT (writeToLog "Shutdown requested") u
exceptionHandler :: Universe u => u -> SomeException -> IO u
exceptionHandler u x = execStateT (writeToLog ("WARNING: " ++ show x)) u
-- | Can be used as a startupHandler, shutdownHandler,
-- startRoundProgram, or endRoundProgram
doNothing :: Monad m => m ()
doNothing = return ()
runNoninteractingAgents
:: (Universe u, Serialize (Agent u))
=> AgentProgram u -> StateT u IO () -> StateT u IO ()
-> StateT u IO ()
runNoninteractingAgents agentProgram startRoundProgram
endRoundProgram = do
atStartOfRound startRoundProgram
as <- lineup
when (not . null $ as) $ do
let a = head as
C.onException
(withAgent agentProgram a)
(writeToLog "Continuing after exception")
markDone a
atEndOfRound endRoundProgram
-- The input parameter is a list of agents. The first agent in the
-- list is the agent whose turn it is to use the CPU. The rest of
-- the list contains agents it could interact with. For example, if
-- agents reproduce sexually, the program might check if the first
-- agent in the list is female, and the second one is male, and if so,
-- mate them to produce offspring. The input list is generated in a
-- way that guarantees that every possible sequence of agents has an
-- equal chance of occurring.
runInteractingAgents
:: (Universe u, Serialize (Agent u))
=> AgentsProgram u -> StateT u IO () -> StateT u IO ()
-> StateT u IO ()
runInteractingAgents agentsProgram startRoundProgram
endRoundProgram = do
atStartOfRound startRoundProgram
as <- lineup
C.onException
(withAgents agentsProgram as)
(writeToLog "Continuing after exception")
markDone (head as)
atEndOfRound endRoundProgram
checkPopSize :: Universe u => (Int, Int) -> StateT u IO ()
checkPopSize (minAgents, maxAgents) = do
n <- popSize
writeToLog $ "Pop. size=" ++ show n
when (n < minAgents) $ requestShutdown "population too small"
when (n > maxAgents) $ requestShutdown "population too big"
requestShutdown :: Universe u => String -> StateT u IO ()
requestShutdown s = do
writeToLog $ "Requesting shutdown: " ++ s
lift D.requestShutdown
atStartOfRound :: Universe u => StateT u IO () -> StateT u IO ()
atStartOfRound program = do
whenM endOfRound $ do
refreshLineup
incTime
writeToLog "Beginning of round"
program
atEndOfRound :: Universe u => StateT u IO () -> StateT u IO ()
atEndOfRound program = do
whenM endOfRound $ do
writeToLog "End of round"
program
-- nothing :: StateT u IO ()
-- nothing = return ()
-- Note: There's no reason for the checklist type to be a parameter of
-- the Universe type. Users don't interact directly with it, so they
-- won't have any reason to want to use a different checklist
-- implementation.