werewolf 1.0.1.0 → 1.0.2.0
raw patch · 20 files changed
+318/−115 lines, 20 filesdep −tostringPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies removed: tostring
API changes (from Hackage documentation)
- Game.Werewolf.Game: instance Data.String.ToString.ToString Game.Werewolf.Game.Stage
- Game.Werewolf.Player: filteredBy :: Eq b => Lens' a b -> b -> Traversal' a a
- Game.Werewolf.Player: is :: Getting Any s a -> s -> Bool
- Game.Werewolf.Player: isn't :: Getting All s a -> s -> Bool
- Game.Werewolf.Role: filteredBy :: Eq b => Lens' a b -> b -> Traversal' a a
- Game.Werewolf.Role: instance Data.String.ToString.ToString Game.Werewolf.Role.Allegiance
- Game.Werewolf.Role: is :: Getting Any s a -> s -> Bool
- Game.Werewolf.Role: isn't :: Getting All s a -> s -> Bool
+ Control.Lens.Extra: filteredBy :: Eq b => Lens' a b -> b -> Traversal' a a
+ Control.Lens.Extra: is :: Getting Any s a -> s -> Bool
+ Control.Lens.Extra: isn't :: Getting All s a -> s -> Bool
+ Data.String.Humanise: class Humanise a
+ Data.String.Humanise: humanise :: (Humanise a, IsString b) => a -> b
+ Game.Werewolf.Game: instance Data.String.Humanise.Humanise Game.Werewolf.Game.Stage
+ Game.Werewolf.Player: alphaWolf :: Traversal' Player ()
+ Game.Werewolf.Player: alphaWolves :: Traversable t => Traversal' (t Player) Player
+ Game.Werewolf.Player: beholder :: Traversal' Player ()
+ Game.Werewolf.Player: beholders :: Traversable t => Traversal' (t Player) Player
+ Game.Werewolf.Player: lycan :: Traversal' Player ()
+ Game.Werewolf.Player: lycans :: Traversable t => Traversal' (t Player) Player
+ Game.Werewolf.Role: alphaWolfRole :: Role
+ Game.Werewolf.Role: beholderRole :: Role
+ Game.Werewolf.Role: instance Data.String.Humanise.Humanise Game.Werewolf.Role.Allegiance
+ Game.Werewolf.Role: lycanRole :: Role
Files
- CHANGELOG.md +12/−0
- README.md +4/−1
- app/Werewolf/Command/Help.hs +1/−0
- src/Control/Lens/Extra.hs +41/−0
- src/Data/String/Humanise.hs +20/−0
- src/Game/Werewolf/Engine.hs +12/−11
- src/Game/Werewolf/Game.hs +20/−18
- src/Game/Werewolf/Messages.hs +20/−5
- src/Game/Werewolf/Player.hs +58/−12
- src/Game/Werewolf/Role.hs +108/−56
- src/Game/Werewolf/Util.hs +3/−2
- test/src/Game/Werewolf/Test/Arbitrary.hs +2/−1
- test/src/Game/Werewolf/Test/Command/Choose.hs +2/−1
- test/src/Game/Werewolf/Test/Command/Heal.hs +2/−1
- test/src/Game/Werewolf/Test/Command/Poison.hs +2/−1
- test/src/Game/Werewolf/Test/Command/Protect.hs +2/−1
- test/src/Game/Werewolf/Test/Command/See.hs +2/−1
- test/src/Game/Werewolf/Test/Command/Vote.hs +2/−1
- test/src/Game/Werewolf/Test/Engine.hs +2/−1
- werewolf.cabal +3/−2
CHANGELOG.md view
@@ -2,6 +2,18 @@ ### Upcoming +### v1.0.2.0++*Minor*++* Added the Alpha Wolf role. ([#149](https://github.com/hjwylde/werewolf/issues/149))+* Added the Beholder role. ([#168](https://github.com/hjwylde/werewolf/issues/168))+* Added the Lycan role. ([#169](https://github.com/hjwylde/werewolf/issues/169))++*Revisions*++* Moved Seer's player seen message to Sunrise. ([#184](https://github.com/hjwylde/werewolf/issues/184))+ ### v1.0.1.0 *Minor*
README.md view
@@ -24,7 +24,7 @@ For centuries no-one knew how to fight this scourge, however recently a theory has taken ahold that mayhaps the Werewolves walk among the Villagers themselves... -Objective of the Game: +Objective of the game: For the Loners: complete their own objective. For the Villagers: lynch all of the Werewolves. For the Werewolves: devour all of the Villagers.@@ -59,10 +59,12 @@ The Villagers must lynch all of the Werewolves. +* Beholder * Crooked Senator * Druid * Hunter * Jester+* Lycan * Protector * Scapegoat * Seer@@ -76,6 +78,7 @@ The Werewolves must devour all of the Villagers. +* Alpha Wolf * Simple Werewolf ### Installing
app/Werewolf/Command/Help.hs view
@@ -20,6 +20,7 @@ ) where import Control.Lens+import Control.Lens.Extra import Control.Monad.Extra import Control.Monad.IO.Class
+ src/Control/Lens/Extra.hs view
@@ -0,0 +1,41 @@+{-|+Module : Control.Lens.Extra+Description : Extra utility functions for working with lenses.++Copyright : (c) Henry J. Wylde, 2016+License : BSD3+Maintainer : public@hjwylde.com++Extra utility functions for working with lenses.+-}++{-# LANGUAGE Rank2Types #-}++module Control.Lens.Extra (+ -- * Folds+ is, isn't,++ -- * Traversals+ filteredBy,+) where++import Control.Lens hiding (isn't)++import Data.Monoid++-- | The counter-part to 'isn't', but more general as it takes a 'Getting' instead.+--+-- @'is' = 'has'@+is :: Getting Any s a -> s -> Bool+is = has++-- | A re-write of 'Control.Lens.Prism.isn't' to be more general by taking a 'Getting' instead.+--+-- @'isn't' = 'hasn't'@+isn't :: Getting All s a -> s -> Bool+isn't = hasn't++-- | A companion to 'filtered' that, rather than using a predicate, filters on the given lens for+-- matches.+filteredBy :: Eq b => Lens' a b -> b -> Traversal' a a+filteredBy lens value = filtered ((value ==) . view lens)
+ src/Data/String/Humanise.hs view
@@ -0,0 +1,20 @@+{-|+Module : Data.String.Humanise+Description : Humanise type class for pretty printing data structures.++Copyright : (c) Henry J. Wylde, 2016+License : BSD3+Maintainer : public@hjwylde.com++Humanise type class for displaying data structures to a human.+-}++module Data.String.Humanise (+ -- * Humanise+ Humanise(..),+) where++import Data.String++class Humanise a where+ humanise :: IsString b => a -> b
src/Game/Werewolf/Engine.hs view
@@ -22,10 +22,11 @@ ) where import Control.Lens hiding (cons, isn't)+import Control.Lens.Extra import Control.Monad.Except import Control.Monad.Extra import Control.Monad.Random-import Control.Monad.State hiding (state)+import Control.Monad.State import Control.Monad.Writer import Data.List.Extra@@ -70,9 +71,9 @@ checkStage' = use stage >>= \stage' -> case stage' of FerinasGrunt -> do druid <- findPlayerBy_ role druidRole- players' <- getAdjacentAlivePlayers (druid ^. name)+ players' <- filter (isn't alphaWolf) <$> getAdjacentAlivePlayers (druid ^. name) - when (has werewolves players') $ tell [ferinaGruntsMessage]+ when (has werewolves players' || has lycans players') $ tell [ferinaGruntsMessage] advanceStage @@ -113,13 +114,7 @@ SeersTurn -> do whenM (has (players . seers . dead) <$> get) advanceStage - whenJustM (use see) $ \targetName -> do- seer <- findPlayerBy_ role seerRole- target <- findPlayerBy_ name targetName-- tell [playerSeenMessage (seer ^. name) target]-- advanceStage+ whenM (isJust <$> use see) advanceStage Sunrise -> do round += 1@@ -130,6 +125,13 @@ setPlayerAllegiance (fallenAngel ^. name) Villagers + whenJustM (preuse $ players . seers . alive) $ \seer -> do+ target <- use see >>= findPlayerBy_ name . fromJust++ when (is alive target) $ tell [playerSeenMessage (seer ^. name) target]++ see .= Nothing+ advanceStage Sunset -> do@@ -221,7 +223,6 @@ stage .= nextStage boots .= Map.empty passed .= False- see .= Nothing tell . stageMessages =<< get
src/Game/Werewolf/Game.hs view
@@ -46,13 +46,15 @@ hasAnyoneWon, hasFallenAngelWon, hasVillagersWon, hasWerewolvesWon, ) where -import Control.Lens hiding (isn't)+import Control.Lens hiding (isn't)+import Control.Lens.Extra import Data.List.Extra import Data.Map (Map) import qualified Data.Map as Map import Data.Maybe-import Data.String.ToString+import Data.String+import Data.String.Humanise import Data.Text (Text) import Game.Werewolf.Player@@ -114,22 +116,22 @@ | VillagesTurn | WerewolvesTurn | WitchsTurn deriving (Eq, Read, Show) -instance ToString Stage where- toString FerinasGrunt = "Ferina's Grunt"- toString GameOver = "Game over"- toString HuntersTurn1 = "Hunter's turn"- toString HuntersTurn2 = "Hunter's turn"- toString Lynching = "Lynching"- toString OrphansTurn = "Orphan's turn"- toString ProtectorsTurn = "Protector's turn"- toString ScapegoatsTurn = "Scapegoat's turn"- toString SeersTurn = "Seer's turn"- toString Sunrise = "Sunrise"- toString Sunset = "Sunset"- toString VillageDrunksTurn = "Village Drunk's turn"- toString VillagesTurn = "village's turn"- toString WerewolvesTurn = "Werewolves' turn"- toString WitchsTurn = "Witch's turn"+instance Humanise Stage where+ humanise FerinasGrunt = fromString "Ferina's Grunt"+ humanise GameOver = fromString "Game over"+ humanise HuntersTurn1 = fromString "Hunter's turn"+ humanise HuntersTurn2 = fromString "Hunter's turn"+ humanise Lynching = fromString "Lynching"+ humanise OrphansTurn = fromString "Orphan's turn"+ humanise ProtectorsTurn = fromString "Protector's turn"+ humanise ScapegoatsTurn = fromString "Scapegoat's turn"+ humanise SeersTurn = fromString "Seer's turn"+ humanise Sunrise = fromString "Sunrise"+ humanise Sunset = fromString "Sunset"+ humanise VillageDrunksTurn = fromString "Village Drunk's turn"+ humanise VillagesTurn = fromString "village's turn"+ humanise WerewolvesTurn = fromString "Werewolves' turn"+ humanise WitchsTurn = fromString "Witch's turn" -- TODO (hjw): remove events -- | Events occur /after/ a 'Stage' is advanced. This is automatically handled in
src/Game/Werewolf/Messages.hs view
@@ -94,9 +94,10 @@ import Control.Arrow import Control.Lens+import Control.Lens.Extra import Data.List.Extra-import Data.String.ToString+import Data.String.Humanise import Data.Text (Text) import qualified Data.Text as T @@ -111,11 +112,15 @@ [ [newPlayersInGameMessage $ players' ^.. names] , [rolesInGameMessage Nothing $ players' ^.. roles] , map newPlayerMessage players'+ , beholderMessages , trueVillagerMessages , stageMessages game ] where players' = game ^. players+ beholderMessages = case (,) <$> players' ^? beholders <*> players' ^? seers of+ Just (beholder, seer) -> [beholderMessage (beholder ^. name) (seer ^. name)]+ _ -> [] trueVillagerMessages = case players' ^? trueVillagers of Just trueVillager -> [trueVillagerMessage $ trueVillager ^. name] _ -> []@@ -133,6 +138,12 @@ where playerRole = player ^. role +beholderMessage :: Text -> Text -> Message+beholderMessage to seerName = privateMessage to $ T.concat+ [ "The Seer has always been held in high regard among the Villagers. Few are as lucky as you to"+ , " know the Seer, ", seerName, ", personally."+ ]+ trueVillagerMessage :: Text -> Message trueVillagerMessage name = publicMessage $ T.unwords [ "Unguarded advice is seldom given, for advice is a dangerous gift, even from the wise to the"@@ -386,7 +397,7 @@ currentStageMessages _ Sunrise = [] currentStageMessages _ Sunset = [] currentStageMessages to turn = [privateMessage to $ T.concat- [ "It's currently the ", T.pack $ toString turn, "."+ [ "It's currently the ", humanise turn, "." ]] rolesInGameMessage :: Maybe Text -> [Role] -> Message@@ -475,10 +486,13 @@ playerSeenMessage :: Text -> Player -> Message playerSeenMessage to target = privateMessage to $ T.concat- [targetName, " is aligned with ", article, T.pack $ toString allegiance', "."]+ [targetName, " is aligned with ", article, humanise allegiance', "."] where targetName = target ^. name- allegiance' = target ^. role . allegiance+ allegiance'+ | is alphaWolf target = Villagers+ | is lycan target = Werewolves+ | otherwise = target ^. role . allegiance article = if allegiance' == NoOne then "" else "the " villageDrunkJoinedVillageMessage :: Text -> Message@@ -506,7 +520,8 @@ playerLynchedMessage :: Player -> Message playerLynchedMessage player- | is simpleWerewolf player = publicMessage $ T.concat+ | is simpleWerewolf player+ || is alphaWolf player = publicMessage $ T.concat [ playerName, " is tied up to a pyre and set alight. As they scream their body starts to " , "contort and writhe, transforming into ", article playerRole, " " , playerRole ^. Role.name, ".", " Thankfully they go limp before breaking free of their "
src/Game/Werewolf/Player.hs view
@@ -10,10 +10,9 @@ behaviour is handled in "Game.Werewolf.Command" and "Game.Werewolf.Engine". -} -{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE Rank2Types #-}-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TemplateHaskell #-} module Game.Werewolf.Player ( -- * Player@@ -26,24 +25,23 @@ newPlayer, -- ** Traversals- crookedSenator, druid, fallenAngel, hunter, jester, orphan, protector, scapegoat, seer,- simpleVillager, simpleWerewolf, trueVillager, villageDrunk, witch,+ alphaWolf, beholder, crookedSenator, druid, fallenAngel, hunter, jester, lycan, orphan,+ protector, scapegoat, seer, simpleVillager, simpleWerewolf, trueVillager, villageDrunk, witch, villager, werewolf, -- | These are provided just as a bit of sugar to avoid continually writing @'traverse' .@. names, roles, states, -- | N.B., these are not legal traversals for the same reason 'filtered' isn't!- crookedSenators, druids, fallenAngels, hunters, jesters, orphans, protectors, scapegoats,- seers, simpleVillagers, simpleWerewolves, trueVillagers, villageDrunks, witches,+ alphaWolves, beholders, crookedSenators, druids, fallenAngels, hunters, jesters, lycans,+ orphans, protectors, scapegoats, seers, simpleVillagers, simpleWerewolves, trueVillagers,+ villageDrunks, witches, villagers, werewolves, alive, dead,-- -- * Utility functions- is, isn't, filteredBy, ) where -import Control.Lens hiding (isn't)+import Control.Lens hiding (isn't)+import Control.Lens.Extra import Data.Function import Data.Text (Text)@@ -75,6 +73,22 @@ newPlayer :: Text -> Role -> Player newPlayer name role = Player name role Alive +-- | The traversal of 'Player's with a 'alphaWolfRole'.+--+-- @+-- 'alphaWolf' = 'role' . 'only' 'alphaWolfRole'+-- @+alphaWolf :: Traversal' Player ()+alphaWolf = role . only alphaWolfRole++-- | The traversal of 'Player's with a 'beholderRole'.+--+-- @+-- 'beholder' = 'role' . 'only' 'beholderRole'+-- @+beholder :: Traversal' Player ()+beholder = role . only beholderRole+ -- | The traversal of 'Player's with a 'crookedSenatorRole'. -- -- @@@ -115,6 +129,14 @@ jester :: Traversal' Player () jester = role . only jesterRole +-- | The traversal of 'Player's with a 'lycanRole'.+--+-- @+-- 'lycan' = 'role' . 'only' 'lycanRole'+-- @+lycan :: Traversal' Player ()+lycan = role . only lycanRole+ -- | The traversal of 'Player's with a 'orphanRole'. -- -- @@@ -227,6 +249,22 @@ states :: Traversable t => Traversal' (t Player) State states = traverse . state +-- | This 'Traversal' provides the traversal of 'alphaWolf' 'Player's.+--+-- @+-- 'alphaWolves' = 'traverse' . 'filtered' ('is' 'alphaWolf')+-- @+alphaWolves :: Traversable t => Traversal' (t Player) Player+alphaWolves = traverse . filtered (is alphaWolf)++-- | This 'Traversal' provides the traversal of 'beholder' 'Player's.+--+-- @+-- 'beholders' = 'traverse' . 'filtered' ('is' 'beholder')+-- @+beholders :: Traversable t => Traversal' (t Player) Player+beholders = traverse . filtered (is beholder)+ -- | This 'Traversal' provides the traversal of 'crookedSenator' 'Player's. -- -- @@@ -266,6 +304,14 @@ -- @ jesters :: Traversable t => Traversal' (t Player) Player jesters = traverse . filtered (is jester)++-- | This 'Traversal' provides the traversal of 'lycan' 'Player's.+--+-- @+-- 'lycans' = 'traverse' . 'filtered' ('is' 'lycan')+-- @+lycans :: Traversable t => Traversal' (t Player) Player+lycans = traverse . filtered (is lycan) -- | This 'Traversal' provides the traversal of 'orphan' 'Player's. --
src/Game/Werewolf/Role.hs view
@@ -47,25 +47,22 @@ -- certain few have learnt some tricks over the years that may turn out rather useful. -- The Villagers must lynch all of the Werewolves.- crookedSenatorRole, druidRole, hunterRole, jesterRole, protectorRole, scapegoatRole, seerRole,- simpleVillagerRole, trueVillagerRole, witchRole,+ beholderRole, crookedSenatorRole, druidRole, hunterRole, jesterRole, lycanRole, protectorRole,+ scapegoatRole, seerRole, simpleVillagerRole, trueVillagerRole, witchRole, -- *** The Werewolves -- | Hiding in plain sight, the Werewolves are not a small trifle. -- The Werewolves must devour all of the Villagers.- simpleWerewolfRole,-- -- * Utility functions- is, isn't, filteredBy,+ alphaWolfRole, simpleWerewolfRole, ) where -import Control.Lens hiding (isn't)+import Control.Lens import Data.Function import Data.List-import Data.Monoid-import Data.String.ToString+import Data.String+import Data.String.Humanise import Data.Text (Text) import qualified Data.Text as T @@ -92,10 +89,10 @@ data Allegiance = NoOne | Villagers | Werewolves deriving (Eq, Read, Show) -instance ToString Allegiance where- toString NoOne = "no-one"- toString Villagers = "Villagers"- toString Werewolves = "Werewolves"+instance Humanise Allegiance where+ humanise NoOne = fromString "no-one"+ humanise Villagers = fromString "Villagers"+ humanise Werewolves = fromString "Werewolves" makeLenses ''Role @@ -107,11 +104,14 @@ -- | A list containing all the roles defined in this file. allRoles :: [Role] allRoles =- [ crookedSenatorRole+ [ alphaWolfRole+ , beholderRole+ , crookedSenatorRole , druidRole , fallenAngelRole , hunterRole , jesterRole+ , lycanRole , orphanRole , protectorRole , scapegoatRole@@ -228,12 +228,32 @@ ] } +-- | /Awareness comes easy to the Beholder. They listen to their senses and trust their hunches./+-- /Over the years the Beholder has grown to know a certain few of the village just by paying/+-- /attention. Little cues here and there, the way someone talks, the way they move - it all/+-- /gives clues as to their true nature and role./+--+-- At the start of the game the Beholder is informed the Seer's identity.+beholderRole :: Role+beholderRole = Role+ { _name = "Beholder"+ , _allegiance = Villagers+ , _balance = 2+ , _description = T.unwords+ [ "Awareness comes easy to the Beholder. They listen to their senses and trust their"+ , "hunches. Over the years the Beholder has grown to know a certain few of the village just"+ , "by paying attention. Little cues here and there, the way someone talks, the way they"+ , "move - it all gives clues as to their true nature and role."+ ]+ , _rules = "At the start of the game the Beholder is informed the Seer's identity."+ }+ -- | /Never trust a politician. Nor a Crooked Senator for that matter. The Crooked Senator may seem/ -- /like he has the village's best interests at heart, but let's be honest, when put in a tough/ -- /situation he looks after no-one but himself. Even when safe, the Crooked Senator may decide/ -- /to toy with the Villagers' emotions and try pit them against one another./ ----- The Crooked Senator looks at the Villager votes as they come in.+-- The Crooked Senator looks at the village votes as they come in. crookedSenatorRole :: Role crookedSenatorRole = Role { _name = "Crooked Senator"@@ -245,7 +265,7 @@ , "a tough situation he looks after no-one but himself. Even when safe, the Crooked Senator" , "may decide to toy with the Villagers' emotions and try pit them against one another." ]- , _rules = "The Crooked Senator looks at the Villager votes as they come in."+ , _rules = "The Crooked Senator looks at the village votes as they come in." } -- | /How honoured we are to be in the presence of such a noble leader. The return of the Druid/@@ -297,6 +317,57 @@ ] } +-- | /Every village needs a Jester; they're so stupid, but provide so much entertainment! The/+-- /Jester may not have any special abilities, but at least no-one in the village wants to hurt/+-- /them./+--+-- If the village votes to lynch the Jester, their identity is revealed. The village realise+-- there's no point in burning them and so they are set free.+--+-- The Jester continues to play but may no longer vote as no-one can take them seriously.+jesterRole :: Role+jesterRole = Role+ { _name = "Jester"+ , _allegiance = Villagers+ , _balance = 0+ , _description = T.unwords+ [ "Every village needs a Jester; they're so stupid, but provide so much entertainment! The"+ , "Jester may not have any special abilities, but at least no-one in the village wants to"+ , "hurt them."+ ]+ , _rules = T.intercalate "\n"+ [ T.unwords+ [ "If the village votes to lynch the Jester, their identity is revealed. The village"+ , "realise there's no point in burning them and so they are set free."+ ]+ , "The Jester continues to play but may no longer vote as no-one can take them seriously."+ ]+ }++-- | /Traditionally a Werewolf once transformed loses all memories and personality. Over years of/+-- /transforming, the Lycan has slowly evolved and learnt how to retain themself. Night after/+-- /night of devouring with the other Werewolves took its toll. The screams alone were enough to/+-- /turn the Lycan and make them question their true nature./+--+-- The Lycan is aligned with the Villagers, but appears to nature-seeing roles (e.g., the Seer) as+-- a Werewolf.+lycanRole :: Role+lycanRole = Role+ { _name = "Lycan"+ , _allegiance = Villagers+ , _balance = 0+ , _description = T.unwords+ [ "Traditionally a Werewolf once transformed loses all memories and personality. Over years"+ , "of transforming, the Lycan has slowly evolved and learnt how to retain themself. Night"+ , "after night of devouring with the other Werewolves took its toll. The screams alone were"+ , "enough to turn the Lycan and make them question their true nature."+ ]+ , _rules = T.unwords+ [ "The Lycan is aligned with the Villagers, but appears to nature-seeing roles (e.g., the"+ , "Seer) as a Werewolf."+ ]+ }+ -- | /The Protector is one of the few pure of heart and altruistic Villagers; they are forever/ -- /putting others needs above their own. Each night they fight against the Werewolves with/ -- /naught but a sword and shield, potentially saving an innocents life./@@ -396,33 +467,6 @@ ] } --- | /Every village needs a Jester; they're so stupid, but provide so much entertainment! The/--- /Jester may not have any special abilities, but at least no-one in the village wants to hurt/--- /them./------ If the village votes to lynch the Jester, their identity is revealed. The village realise--- there's no point in burning them and so they are set free.------ The Jester continues to play but may no longer vote as no-one can take them seriously.-jesterRole :: Role-jesterRole = Role- { _name = "Jester"- , _allegiance = Villagers- , _balance = 0- , _description = T.unwords- [ "Every village needs a Jester; they're so stupid, but provide so much entertainment! The"- , "Jester may not have any special abilities, but at least no-one in the village wants to"- , "hurt them."- ]- , _rules = T.intercalate "\n"- [ T.unwords- [ "If the village votes to lynch the Jester, their identity is revealed. The village"- , "realise there's no point in burning them and so they are set free."- ]- , "The Jester continues to play but may no longer vote as no-one can take them seriously."- ]- }- -- | /The True Villager has a heart and soul as clear as day! Their allegiance and devotion to the/ -- /village are beyond reproach. If there is one person whom you should confide in, listen to and/ -- /trust, it is the True Villager./@@ -467,6 +511,27 @@ ] } +-- | /The Alpha Wolf leads the Werewolves in the raids against Fougères each night and not even the/+-- /Seer can see them coming. If the Werewolves caused the Villagers to question and accuse one/+-- /another beforehand, the Alpha Wolf eliminates any shred of humanity left. No-one can be/+-- /trusted anymore and no-one knows the truth./+--+-- The Alpha Wolf appears to nature-seeing roles (e.g., the Seer) as a Villager.+alphaWolfRole :: Role+alphaWolfRole = Role+ { _name = "Alpha Wolf"+ , _allegiance = Werewolves+ , _balance = -5+ , _description = T.unwords+ [ "The Alpha Wolf leads the Werewolves in the raids against Fougères each night and not"+ , "even the Seer can see them coming. If the Werewolves caused the Villagers to question"+ , "and accuse one another beforehand, the Alpha Wolf eliminates any shred of humanity left."+ , "No-one can be trusted anymore and no-one knows the truth."+ ]+ , _rules =+ "The Alpha Wolf appears to nature-seeing roles (e.g., the Seer) as a Villager."+ }+ -- | /The Simple Werewolf is a fearsome lupine, cunning like no other creature that roams the/ -- /forest. Their origin is unknown, but that matters little, for they present a grave threat to/ -- /Fougères. While each day they hide in plain sight as an ordinary Villager, each night they/@@ -487,16 +552,3 @@ ] , _rules = "A Werewolf may never devour another Werewolf." }---- | The counter-part to 'isn't', but more general as it takes a 'Getting' instead.-is :: Getting Any s a -> s -> Bool-is = has---- | A re-write of 'Control.Lens.Prism.isn't' to be more general by taking a 'Getting' instead.-isn't :: Getting All s a -> s -> Bool-isn't = hasn't---- | A companion to 'filtered' that, rather than using a predicate, filters on the given lens for--- matches.-filteredBy :: Eq b => Lens' a b -> b -> Traversal' a a-filteredBy lens value = filtered ((value ==) . view lens)
src/Game/Werewolf/Util.hs view
@@ -38,10 +38,11 @@ isPlayerAlive, isPlayerDead, ) where -import Control.Lens hiding (cons)+import Control.Lens+import Control.Lens.Extra import Control.Monad.Extra import Control.Monad.Random-import Control.Monad.State hiding (state)+import Control.Monad.State import Data.List import qualified Data.Map as Map
test/src/Game/Werewolf/Test/Arbitrary.hs view
@@ -36,7 +36,8 @@ arbitraryPlayer, arbitraryWerewolf, ) where -import Control.Lens hiding (elements, isn't)+import Control.Lens hiding (elements, isn't)+import Control.Lens.Extra import Data.List.Extra import Data.Maybe
test/src/Game/Werewolf/Test/Command/Choose.hs view
@@ -12,7 +12,8 @@ allChooseCommandTests, ) where -import Control.Lens hiding (elements, isn't)+import Control.Lens hiding (elements, isn't)+import Control.Lens.Extra import Game.Werewolf import Game.Werewolf.Command.Hunter as Hunter
test/src/Game/Werewolf/Test/Command/Heal.hs view
@@ -10,7 +10,8 @@ allHealCommandTests, ) where -import Control.Lens hiding (isn't)+import Control.Lens hiding (isn't)+import Control.Lens.Extra import Game.Werewolf import Game.Werewolf.Command.Witch
test/src/Game/Werewolf/Test/Command/Poison.hs view
@@ -10,7 +10,8 @@ allPoisonCommandTests, ) where -import Control.Lens hiding (isn't)+import Control.Lens hiding (isn't)+import Control.Lens.Extra import Game.Werewolf import Game.Werewolf.Command.Witch
test/src/Game/Werewolf/Test/Command/Protect.hs view
@@ -10,7 +10,8 @@ allProtectCommandTests, ) where -import Control.Lens hiding (isn't)+import Control.Lens hiding (isn't)+import Control.Lens.Extra import Data.Maybe
test/src/Game/Werewolf/Test/Command/See.hs view
@@ -10,7 +10,8 @@ allSeeCommandTests, ) where -import Control.Lens hiding (isn't)+import Control.Lens hiding (isn't)+import Control.Lens.Extra import Game.Werewolf import Game.Werewolf.Command.Seer
test/src/Game/Werewolf/Test/Command/Vote.hs view
@@ -10,7 +10,8 @@ allVoteCommandTests, ) where -import Control.Lens hiding (isn't)+import Control.Lens hiding (isn't)+import Control.Lens.Extra import Game.Werewolf import Game.Werewolf.Command.Villager as Villager
test/src/Game/Werewolf/Test/Engine.hs view
@@ -13,6 +13,7 @@ ) where import Control.Lens hiding (elements)+import Control.Lens.Extra import Control.Monad.Except import Control.Monad.Writer @@ -40,7 +41,7 @@ players' = game ^. players prop_startGameErrorsWhenLessThan7Players :: [Player] -> Property-prop_startGameErrorsWhenLessThan7Players players =+prop_startGameErrorsWhenLessThan7Players players = once $ length players < 7 ==> isLeft . runExcept . runWriterT $ startGame "" players
werewolf.cabal view
@@ -1,5 +1,5 @@ name: werewolf-version: 1.0.1.0+version: 1.0.2.0 author: Henry J. Wylde maintainer: public@hjwylde.com@@ -75,6 +75,8 @@ library hs-source-dirs: src/ exposed-modules:+ Control.Lens.Extra+ Data.String.Humanise Game.Werewolf Game.Werewolf.Command Game.Werewolf.Command.Global@@ -116,7 +118,6 @@ MonadRandom == 0.4.*, mtl == 2.2.*, text == 1.2.*,- tostring == 0.2.*, transformers == 0.4.* test-suite werewolf-test