Loading...
{-# LANGUAGE CPP              #-}
{-# LANGUAGE FlexibleContexts #-}

-- Copyright   : (c) 2017 Harendra Kumar
--               (c) 2013, 2014 Gabriel Gonzalez
--
-- This example is adapted from Gabriel Gonzalez's pipes-concurrency package.
-- https://hackage.haskell.org/package/pipes-concurrency-2.0.8/docs/Pipes-Concurrent-Tutorial.html

import Control.Monad.IO.Class (MonadIO(liftIO))
import Control.Monad.State (MonadState, get, modify)
import Data.Function ((&))
import Streamly.Data.Stream.Prelude (MonadAsync, Stream)

import qualified Streamly.Data.Fold as Fold
import qualified Streamly.Data.Stream.Prelude as Stream

data Event = Quit | Harm Int | Heal Int deriving (Eq, Show)

userAction :: MonadAsync m => Stream m Event
userAction = Stream.repeatM $ liftIO askUser
    where
    askUser = do
        command <- getLine
        case command of
            "potion" -> return (Heal 10)
            "harm"   -> return (Harm 10)
            "quit"   -> return Quit
            _        -> putStrLn "Type potion or harm or quit" >> askUser

acidRain :: MonadAsync m => Stream m Event
acidRain = Stream.parRepeatM (Stream.constRate 1) (return $ Harm 1)

parallel :: MonadAsync m => [Stream m a] -> Stream m a
parallel = Stream.parList (Stream.eager True)

data Result = Check | Done

runEvents :: (MonadAsync m, MonadState Int m) => Stream m Result
runEvents =
    Stream.mapM f $ parallel [userAction, acidRain]

    where

    f event =
        case event of
            Harm n -> modify (\h -> h - n) >> return Check
            Heal n -> modify (\h -> h + n) >> return Check
            Quit -> return Done

data Status = Alive | GameOver deriving Eq

getStatus :: (MonadAsync m, MonadState Int m) => Result -> m Status
getStatus result =
    case result of
        Done  -> liftIO $ putStrLn "You quit!" >> return GameOver
        Check -> do
            h <- get
            liftIO
                $ if (h <= 0)
                  then putStrLn "You die!" >> return GameOver
                  else putStrLn ("Health = " <> show h) >> return Alive

main :: IO ()
main = do
    putStrLn "Your health is deteriorating due to acid rain,\\
             \ type \"potion\" or \"quit\""
    Stream.mapM getStatus runEvents  -- Stream (StateT Int IO) Status
        & Stream.runStateT (pure 60) -- Stream IO (Int, Status)
        & fmap snd                   -- Stream IO Status
        & Stream.fold (Fold.takeEndBy (/= Alive) Fold.drain) -- IO ()
    return ()