packages feed

werewolf-1.4.0.0: app/Game/Werewolf/Util.hs

{-|
Module      : Game.Werewolf.Util
Description : Utility functions for working in a ('MonadState' 'Game') environment.

Copyright   : (c) Henry J. Wylde, 2016
License     : BSD3
Maintainer  : public@hjwylde.com

Utility functions for woking in a ('MonadState' 'Game') environment.
-}

{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE Rank2Types            #-}

module Game.Werewolf.Util (
    -- * Game

    -- ** Manipulations
    killPlayer, removePlayer, setPlayerAllegiance, getRandomAllegiance,

    -- ** Searches
    findPlayerBy_, getAdjacentAlivePlayers, getFirstAdjacentAliveWerewolf, getPlayerVote,

    -- ** Queries
    isGameOver, isHuntersTurn, isNecromancersTurn, isOraclesTurn, isOrphansTurn, isProtectorsTurn,
    isScapegoatsTurn, isSeersTurn, isSunrise, isVillagesTurn, isWerewolvesTurn, isWitchsTurn,
    hasAnyoneWon, hasNecromancerWon, hasVillagersWon, hasWerewolvesWon, hasEveryoneLost,

    -- * Player

    -- ** Queries
    doesPlayerExist,
    isPlayerDullahan, isPlayerHunter, isPlayerJester, isPlayerNecromancer, isPlayerOracle,
    isPlayerOrphan, isPlayerProtector, isPlayerScapegoat, isPlayerSeer, isPlayerWitch,
    isPlayerZombie,
    isPlayerWerewolf,
    isPlayerAlive, isPlayerDead,
) where

import Control.Lens.Extra
import Control.Monad.Extra
import Control.Monad.Random
import Control.Monad.State  hiding (state)
import Control.Monad.Writer

import           Data.List
import qualified Data.Map   as Map
import           Data.Maybe
import           Data.Text  (Text)

import           Game.Werewolf.Game           hiding (hasAnyoneWon, hasEveryoneLost,
                                               hasVillagersWon, hasWerewolvesWon)
import qualified Game.Werewolf.Game           as Game
import           Game.Werewolf.Message.Engine
import           Game.Werewolf.Player
import           Game.Werewolf.Response
import           Game.Werewolf.Role           hiding (name)

import Prelude hiding (round)

killPlayer :: (MonadState Game m, MonadWriter [Message] m) => Text -> m ()
killPlayer name' = do
    tell [playerKilledMessage name']

    players . traverse . named name' . state .= Dead

    whenM (isPlayerSpitefulVillager name') $
        tell . (:[]) . spitefulVillagerKilledMessage name' =<< get

    whenM (isPlayerNecromancer name') $ do
        zombieNames <- toListOf (players . zombies . name) <$> get

        unless (null zombieNames) $ do
            mapM_ killPlayer zombieNames

            tell . (:[]) . zombiesReturnedToGraveMessage =<< get

removePlayer :: (MonadState Game m, MonadWriter [Message] m) => Text -> m ()
removePlayer name' = do
    killPlayer name'

    votes %= Map.delete name'

    player <- findPlayerBy_ name name'

    when (is orphan player)         $ roleModel .= Nothing
    when (is protector player)      $ do
        protect         .= Nothing
        priorProtect    .= Nothing
    when (is seer player)           $ see .= Nothing
    when (is witch player)          $ do
        healUsed    .= False
        poison      .= Nothing
        poisonUsed  .= False

-- | Fudges the player's allegiance. This function is useful for roles such as the Orphan where
--   they align themselves differently given some trigger.
setPlayerAllegiance :: MonadState Game m => Text -> Allegiance -> m ()
setPlayerAllegiance name allegiance' = modify $ players . traverse . named name . role . allegiance .~ allegiance'

-- | Get a random allegiance (either Villagers or Werewolves).
getRandomAllegiance :: MonadRandom m => m Allegiance
getRandomAllegiance = fromList [(Villagers, 0.5), (Werewolves, 0.5)]

findPlayerBy_ :: (Eq a, MonadState Game m) => Lens' Player a -> a -> m Player
findPlayerBy_ lens value = fromJust <$> preuse (players . traverse . filteredBy lens value)

getAdjacentAlivePlayers :: MonadState Game m => Text -> m [Player]
getAdjacentAlivePlayers name' = do
    alivePlayers    <- toListOf (players . traverse . alive) <$> get
    let index       = fromJust $ elemIndex name' (alivePlayers ^.. names)

    return $ adjacentElements index alivePlayers
    where
        adjacentElements 0 list     = last list : take 2 list
        adjacentElements index list = take 3 . drop (index - 1) $ cycle list

getFirstAdjacentAliveWerewolf :: MonadState Game m => Text -> m (Maybe Player)
getFirstAdjacentAliveWerewolf name = do
    players'            <- toListOf (players . traverse) <$> get
    let index           = fromJust $ elemIndex name (players' ^.. names)
    let filteredPlayers = dropWhile (isn't werewolf) (drop index (players' ++ players') ^.. traverse . alive)

    return $ listToMaybe filteredPlayers

getPlayerVote :: MonadState Game m => Text -> m (Maybe Text)
getPlayerVote playerName = use $ votes . at playerName

isGameOver :: MonadState Game m => m Bool
isGameOver = hasuse $ stage . _GameOver

isHuntersTurn :: MonadState Game m => m Bool
isHuntersTurn = orM
    [ hasuse $ stage . _HuntersTurn1
    , hasuse $ stage . _HuntersTurn2
    ]

isNecromancersTurn :: MonadState Game m => m Bool
isNecromancersTurn = hasuse $ stage . _NecromancersTurn

isOraclesTurn :: MonadState Game m => m Bool
isOraclesTurn = hasuse $ stage . _OraclesTurn

isOrphansTurn :: MonadState Game m => m Bool
isOrphansTurn = hasuse $ stage . _OrphansTurn

isProtectorsTurn :: MonadState Game m => m Bool
isProtectorsTurn = hasuse $ stage . _ProtectorsTurn

isScapegoatsTurn :: MonadState Game m => m Bool
isScapegoatsTurn = hasuse $ stage . _ScapegoatsTurn

isSeersTurn :: MonadState Game m => m Bool
isSeersTurn = hasuse $ stage . _SeersTurn

isSunrise :: MonadState Game m => m Bool
isSunrise = hasuse $ stage . _Sunrise

isVillagesTurn :: MonadState Game m => m Bool
isVillagesTurn = hasuse $ stage . _VillagesTurn

isWerewolvesTurn :: MonadState Game m => m Bool
isWerewolvesTurn = hasuse $ stage . _WerewolvesTurn

isWitchsTurn :: MonadState Game m => m Bool
isWitchsTurn = hasuse $ stage . _WitchsTurn

hasAnyoneWon :: MonadState Game m => m Bool
hasAnyoneWon = gets Game.hasAnyoneWon

hasVillagersWon :: MonadState Game m => m Bool
hasVillagersWon = gets Game.hasVillagersWon

hasWerewolvesWon :: MonadState Game m => m Bool
hasWerewolvesWon = gets Game.hasWerewolvesWon

hasEveryoneLost :: MonadState Game m => m Bool
hasEveryoneLost = gets Game.hasEveryoneLost

doesPlayerExist :: MonadState Game m => Text -> m Bool
doesPlayerExist name = hasuse $ players . traverse . named name

isPlayerDullahan :: MonadState Game m => Text -> m Bool
isPlayerDullahan name' = is dullahan <$> findPlayerBy_ name name'

isPlayerHunter :: MonadState Game m => Text -> m Bool
isPlayerHunter name' = is hunter <$> findPlayerBy_ name name'

isPlayerJester :: MonadState Game m => Text -> m Bool
isPlayerJester name' = is jester <$> findPlayerBy_ name name'

isPlayerNecromancer :: MonadState Game m => Text -> m Bool
isPlayerNecromancer name' = is necromancer <$> findPlayerBy_ name name'

isPlayerOracle :: MonadState Game m => Text -> m Bool
isPlayerOracle name' = is oracle <$> findPlayerBy_ name name'

isPlayerOrphan :: MonadState Game m => Text -> m Bool
isPlayerOrphan name' = is orphan <$> findPlayerBy_ name name'

isPlayerProtector :: MonadState Game m => Text -> m Bool
isPlayerProtector name' = is protector <$> findPlayerBy_ name name'

isPlayerScapegoat :: MonadState Game m => Text -> m Bool
isPlayerScapegoat name' = is scapegoat <$> findPlayerBy_ name name'

isPlayerSeer :: MonadState Game m => Text -> m Bool
isPlayerSeer name' = is seer <$> findPlayerBy_ name name'

isPlayerSpitefulVillager :: MonadState Game m => Text -> m Bool
isPlayerSpitefulVillager name' = is spitefulVillager <$> findPlayerBy_ name name'

isPlayerWitch :: MonadState Game m => Text -> m Bool
isPlayerWitch name' = is witch <$> findPlayerBy_ name name'

isPlayerZombie :: MonadState Game m => Text -> m Bool
isPlayerZombie name' = is zombie <$> findPlayerBy_ name name'

isPlayerWerewolf :: MonadState Game m => Text -> m Bool
isPlayerWerewolf name' = is werewolf <$> findPlayerBy_ name name'

isPlayerAlive :: MonadState Game m => Text -> m Bool
isPlayerAlive name' = is alive <$> findPlayerBy_ name name'

isPlayerDead :: MonadState Game m => Text -> m Bool
isPlayerDead name' = is dead <$> findPlayerBy_ name name'