packages feed

Nomyx-Language 0.6.1 → 0.6.2

raw patch · 7 files changed

+148/−311 lines, 7 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Language.Nomyx.Examples: gameMasterElections :: Rule
- Language.Nomyx.Examples: referendum :: String -> Nomex () -> Rule
- Language.Nomyx.Examples: referendumOnKickPlayer :: Rule
- Language.Nomyx.Rules: activateOrReject :: RuleInfo -> Bool -> Nomex ()
- Language.Nomyx.Vote: Election :: String -> Election
- Language.Nomyx.Vote: ExclusiveVote :: (Maybe (Alts a)) -> VoteType a
- Language.Nomyx.Vote: NonExclusiveVote :: [Alts a] -> VoteType a
- Language.Nomyx.Vote: Referendum :: String -> Referendum
- Language.Nomyx.Vote: VoteData :: Msg [Alts a] -> ArrayVar PlayerNumber (Alts a) -> [EventNumber] -> VoteResult a -> Maybe UTCTime -> VoteData a
- Language.Nomyx.Vote: alts :: Votable a => [Alts a]
- Language.Nomyx.Vote: assessFunction :: VoteData a -> VoteResult a
- Language.Nomyx.Vote: assessOnEveryVote :: Votable a => Assessor a
- Language.Nomyx.Vote: assessOnTimeDelay :: Votable a => NominalDiffTime -> Assessor a
- Language.Nomyx.Vote: assessOnTimeLimit :: Votable a => UTCTime -> Assessor a
- Language.Nomyx.Vote: assessWhenEverybodyVoted :: Votable a => Assessor a
- Language.Nomyx.Vote: class (Eq (Alts a), Show (Alts a), Ord (Alts a), Typeable a) => Votable a where data family Alts a exclusiveWinner _ = Nothing
- Language.Nomyx.Vote: cleanVote :: Votable a => VoteData a -> Nomex EventNumber
- Language.Nomyx.Vote: data Election
- Language.Nomyx.Vote: data Referendum
- Language.Nomyx.Vote: data VoteData a
- Language.Nomyx.Vote: data VoteType a
- Language.Nomyx.Vote: displayVoteResult :: Votable a => String -> VoteData a -> Nomex OutputNumber
- Language.Nomyx.Vote: displayVoteVar :: Votable a => (Maybe PlayerNumber) -> String -> ArrayVar PlayerNumber (Alts a) -> Nomex EventNumber
- Language.Nomyx.Vote: elections :: String -> [PlayerInfo] -> (PlayerNumber -> Nomex ()) -> Nomex ()
- Language.Nomyx.Vote: exclusiveVoteQuota :: Votable a => Int -> VoteStats a -> (Alts a, Alts a) -> Maybe (Alts a)
- Language.Nomyx.Vote: exclusiveWinner :: Votable a => a -> Maybe (Alts a, Alts a)
- Language.Nomyx.Vote: firstBest :: Votable a => VoteStats a -> [Alts a]
- Language.Nomyx.Vote: firstXBest :: Votable a => Int -> VoteStats a -> [Alts a]
- Language.Nomyx.Vote: inputNumbers :: VoteData a -> [EventNumber]
- Language.Nomyx.Vote: instance Bounded (Alts Referendum)
- Language.Nomyx.Vote: instance Bounded (Alts RuleInfo)
- Language.Nomyx.Vote: instance Enum (Alts Referendum)
- Language.Nomyx.Vote: instance Enum (Alts RuleInfo)
- Language.Nomyx.Vote: instance Eq (Alts Election)
- Language.Nomyx.Vote: instance Eq (Alts Referendum)
- Language.Nomyx.Vote: instance Eq (Alts RuleInfo)
- Language.Nomyx.Vote: instance Ord (Alts Election)
- Language.Nomyx.Vote: instance Ord (Alts Referendum)
- Language.Nomyx.Vote: instance Ord (Alts RuleInfo)
- Language.Nomyx.Vote: instance Read (Alts Referendum)
- Language.Nomyx.Vote: instance Read (Alts RuleInfo)
- Language.Nomyx.Vote: instance Show (Alts Election)
- Language.Nomyx.Vote: instance Show (Alts Referendum)
- Language.Nomyx.Vote: instance Show (Alts RuleInfo)
- Language.Nomyx.Vote: instance Typeable Election
- Language.Nomyx.Vote: instance Typeable Referendum
- Language.Nomyx.Vote: instance Typeable1 Alts
- Language.Nomyx.Vote: instance Votable Election
- Language.Nomyx.Vote: instance Votable Referendum
- Language.Nomyx.Vote: instance Votable RuleInfo
- Language.Nomyx.Vote: msgEnd :: VoteData a -> Msg [Alts a]
- Language.Nomyx.Vote: name :: Votable a => a -> String
- Language.Nomyx.Vote: onRuleProposed :: (RuleInfo -> Nomex (Msg [ForAgainst])) -> Rule
- Language.Nomyx.Vote: quorum :: Votable a => Int -> VoteStats a -> Bool
- Language.Nomyx.Vote: quota :: Votable a => Alts a -> Int -> Int -> Int
- Language.Nomyx.Vote: referendum :: String -> Nomex () -> Rule
- Language.Nomyx.Vote: showChoice :: Votable a => Maybe (Alts a) -> String
- Language.Nomyx.Vote: showChoices :: Votable a => [Alts a] -> String
- Language.Nomyx.Vote: showFinishedVote :: Votable a => [(PlayerNumber, Maybe (Alts a))] -> NomexNE String
- Language.Nomyx.Vote: showOnGoingVote :: Votable a => String -> [(PlayerNumber, Maybe (Alts a))] -> NomexNE String
- Language.Nomyx.Vote: showVote :: Votable a => (PlayerNumber, Maybe (Alts a)) -> NomexNE (String, String)
- Language.Nomyx.Vote: sortWith :: Ord b => (a -> b) -> [a] -> [a]
- Language.Nomyx.Vote: takeInGroups :: Int -> [[a]] -> [a]
- Language.Nomyx.Vote: timeLimit :: VoteData a -> Maybe UTCTime
- Language.Nomyx.Vote: type Assessor a = StateT (VoteData a) Nomex ()
- Language.Nomyx.Vote: type ForAgainst = Alts RuleInfo
- Language.Nomyx.Vote: type Vote a = (PlayerNumber, Maybe (Alts a))
- Language.Nomyx.Vote: type VoteResult a = VoteStats a -> [Alts a]
- Language.Nomyx.Vote: unanimity' :: [Maybe Bool] -> Bool
- Language.Nomyx.Vote: vote :: UTCTime -> [PlayerNumber] -> Event Bool
- Language.Nomyx.Vote: voteEvent :: UTCTime -> [PlayerNumber] -> Event ([Maybe Bool])
- Language.Nomyx.Vote: voteVar :: VoteData a -> ArrayVar PlayerNumber (Alts a)
- Language.Nomyx.Vote: voteWith_ :: Votable a => VoteResult a -> Assessor a -> a -> Nomex (Msg [Alts a])
+ Language.Nomyx.Events: shortcutEvents :: [Event a] -> ([a] -> Maybe b) -> Event b
+ Language.Nomyx.Expression: EventEnv :: (Field e) -> e -> EventEnv
+ Language.Nomyx.Expression: EventInfo :: EventNumber -> RuleNumber -> Event e -> EventHandler e -> Status -> [EventEnv] -> EventInfo
+ Language.Nomyx.Expression: GetEvents :: NomexNE [EventInfo]
+ Language.Nomyx.Expression: SActive :: Status
+ Language.Nomyx.Expression: SDeleted :: Status
+ Language.Nomyx.Expression: ShortcutEvents :: [Event a] -> ([a] -> Maybe b) -> Event b
+ Language.Nomyx.Expression: _env :: EventInfo -> [EventEnv]
+ Language.Nomyx.Expression: _evStatus :: EventInfo -> Status
+ Language.Nomyx.Expression: _eventNumber :: EventInfo -> EventNumber
+ Language.Nomyx.Expression: _ruleNumber :: EventInfo -> RuleNumber
+ Language.Nomyx.Expression: data EventEnv
+ Language.Nomyx.Expression: data EventInfo
+ Language.Nomyx.Expression: data Status
+ Language.Nomyx.Expression: env :: Lens EventInfo [EventEnv]
+ Language.Nomyx.Expression: evStatus :: Lens EventInfo Status
+ Language.Nomyx.Expression: event :: EventInfo -> Event e
+ Language.Nomyx.Expression: eventNumber :: Lens EventInfo EventNumber
+ Language.Nomyx.Expression: handler :: EventInfo -> EventHandler e
+ Language.Nomyx.Expression: instance Eq EventInfo
+ Language.Nomyx.Expression: instance Eq Status
+ Language.Nomyx.Expression: instance Ord EventInfo
+ Language.Nomyx.Expression: instance Show EventEnv
+ Language.Nomyx.Expression: instance Show Status
+ Language.Nomyx.Expression: ruleNumber :: Lens EventInfo RuleNumber
+ Language.Nomyx.Expression: type EventHandler e = (EventNumber, e) -> Nomex ()
+ Language.Nomyx.Rules: activateOrRejectRule :: RuleInfo -> Bool -> Nomex ()
+ Language.Nomyx.Rules: onRuleProposed :: (RuleInfo -> Nomex ()) -> Nomex ()
+ Language.Nomyx.Vote: callVoteRule :: AssessFunction -> NominalDiffTime -> RuleInfo -> Nomex ()
+ Language.Nomyx.Vote: callVoteRule' :: AssessFunction -> UTCTime -> RuleInfo -> Nomex ()
+ Language.Nomyx.Vote: instance Show VoteStats
+ Language.Nomyx.Vote: nbParticipants :: VoteStats -> Int
+ Language.Nomyx.Vote: type AssessFunction = VoteStats -> Maybe Bool
+ Language.Nomyx.Vote: unanimityVote :: Nomex ()
+ Language.Nomyx.Vote: voteEvents :: UTCTime -> String -> [PlayerNumber] -> [Event (Maybe Bool)]
- Language.Nomyx.Inputs: inputRadio :: (Eq c, Show c, Typeable c) => PlayerNumber -> String -> [c] -> c -> Event c
+ Language.Nomyx.Inputs: inputRadio :: (Eq c, Show c, Typeable c) => PlayerNumber -> String -> [(c, String)] -> Event c
- Language.Nomyx.Vote: VoteStats :: Map (Maybe (Alts a)) Int -> Bool -> VoteStats a
+ Language.Nomyx.Vote: VoteStats :: Map (Maybe Bool) Int -> Int -> Bool -> VoteStats
- Language.Nomyx.Vote: callVote :: UTCTime -> Nomex ()
+ Language.Nomyx.Vote: callVote :: AssessFunction -> UTCTime -> String -> (Bool -> Nomex ()) -> Nomex ()
- Language.Nomyx.Vote: data VoteStats a
+ Language.Nomyx.Vote: data VoteStats
- Language.Nomyx.Vote: getVoteStats :: Votable a => [Vote a] -> Bool -> VoteStats a
+ Language.Nomyx.Vote: getVoteStats :: Int -> [Maybe Bool] -> VoteStats
- Language.Nomyx.Vote: majority :: Votable a => VoteStats a -> [Alts a]
+ Language.Nomyx.Vote: majority :: AssessFunction
- Language.Nomyx.Vote: majorityWith :: Votable a => Int -> VoteStats a -> [Alts a]
+ Language.Nomyx.Vote: majorityWith :: Int -> AssessFunction
- Language.Nomyx.Vote: nbVoters :: Votable a => VoteStats a -> Int
+ Language.Nomyx.Vote: nbVoters :: VoteStats -> Int
- Language.Nomyx.Vote: notVoted :: Votable a => VoteStats a -> Int
+ Language.Nomyx.Vote: notVoted :: VoteStats -> Int
- Language.Nomyx.Vote: numberVotes :: Votable a => Int -> VoteStats a -> [Alts a]
+ Language.Nomyx.Vote: numberVotes :: Int -> AssessFunction
- Language.Nomyx.Vote: singleVote :: UTCTime -> PlayerNumber -> Event (Maybe Bool)
+ Language.Nomyx.Vote: singleVote :: UTCTime -> String -> PlayerNumber -> Event (Maybe Bool)
- Language.Nomyx.Vote: totalVoters :: Votable a => VoteStats a -> Int
+ Language.Nomyx.Vote: totalVoters :: VoteStats -> Int
- Language.Nomyx.Vote: unanimity :: Votable a => VoteStats a -> [Alts a]
+ Language.Nomyx.Vote: unanimity :: AssessFunction
- Language.Nomyx.Vote: voteCounts :: VoteStats a -> Map (Maybe (Alts a)) Int
+ Language.Nomyx.Vote: voteCounts :: VoteStats -> Map (Maybe Bool) Int
- Language.Nomyx.Vote: voteFinished :: VoteStats a -> Bool
+ Language.Nomyx.Vote: voteFinished :: VoteStats -> Bool
- Language.Nomyx.Vote: voteQuota :: Votable a => Int -> VoteStats a -> [Alts a]
+ Language.Nomyx.Vote: voteQuota :: Int -> VoteStats -> Maybe Bool
- Language.Nomyx.Vote: voteWith :: Votable a => VoteResult a -> Assessor a -> a -> [Alts a] -> Nomex (Msg [Alts a])
+ Language.Nomyx.Vote: voteWith :: UTCTime -> [PlayerNumber] -> AssessFunction -> String -> Event Bool
- Language.Nomyx.Vote: voted :: Votable a => VoteStats a -> Int
+ Language.Nomyx.Vote: voted :: VoteStats -> Int
- Language.Nomyx.Vote: withQuorum :: Votable a => VoteResult a -> Int -> VoteResult a
+ Language.Nomyx.Vote: withQuorum :: AssessFunction -> Int -> AssessFunction

Files

Nomyx-Language.cabal view
@@ -1,5 +1,5 @@ name: Nomyx-Language-version: 0.6.1+version: 0.6.2 cabal-version: >=1.6 ---Warning: to use "cabal sdist", this patch is needed in cabal: https://github.com/haskell/cabal/pull/1110 ---This allows cabal to work with exported generated modules (Paths_Nomyx_Language)@@ -10,7 +10,7 @@ maintainer: Corentin Dupont stability: Experimental synopsis: Language to express rules for Nomic-description: Provide a DSL to express rules for a Nomic game, with evaluation engine. See package Nomyx for a full game implementation.+description: Provide a DSL to express rules in a Nomic game. See package Nomyx for a full game implementation. category: Language Homepage: http://www.nomyx.net author: Corentin Dupont
src/Language/Nomyx/Events.hs view
@@ -10,7 +10,8 @@    getCurrentTime,    oneWeek, oneDay, oneHour, oneMinute,    timeEvent, messageEvent, victoryEvent, playerEvent, ruleEvent,-   baseEvent, baseInputEvent+   baseEvent, baseInputEvent,+   shortcutEvents    ) where 
 import Language.Nomyx.Expression@@ -110,13 +111,23 @@  timeEvent :: UTCTime -> Event UTCTime timeEvent = BaseEvent . Time+ messageEvent :: (Typeable a) => Msg a -> Event a messageEvent = BaseEvent . Message++victoryEvent :: Event VictoryCond victoryEvent = BaseEvent Victory+ playerEvent :: Player -> Event PlayerInfo playerEvent = BaseEvent . Player+ ruleEvent :: RuleEvent -> Event RuleInfo ruleEvent re = BaseEvent $ RuleEv re  baseInputEvent :: (Typeable a) => PlayerNumber -> String -> (InputForm a) -> Field a baseInputEvent pn s iform = Input Nothing pn s iform++shortcutEvents :: [Event a] -> ([a] -> Maybe b) -> Event b+shortcutEvents = ShortcutEvents++
src/Language/Nomyx/Examples.hs view
@@ -25,9 +25,9 @@    victoryXEcu,    --noGroupVictory,    banPlayer,-   referendum,-   referendumOnKickPlayer,-   gameMasterElections,+--   referendum,+--   referendumOnKickPlayer,+--   gameMasterElections,    gameMaster,    bravoButton,    enterHaiku,@@ -112,7 +112,7 @@ monarchy :: Rule monarchy = void $ onEvent_ (ruleEvent Proposed) $ \rule -> do     k <- readMsgVar_ king-    void $ onInputRadioOnce ("Your Royal Highness, do you accept rule " ++ (show $ _rNumber rule) ++ "?") [True, False] (activateOrReject rule) k+    void $ onInputRadioOnce ("Your Royal Highness, do you accept rule " ++ (show $ _rNumber rule) ++ "?") [True, False] (activateOrRejectRule rule) k  -- | Revolution! Hail to the king! -- This rule suppresses the democracy (usually rules 1 and 2), installs the king and activates monarchy.@@ -161,7 +161,7 @@ -- the vote is assessed after every vote in case the winner is already known -- the vote will finish anyway after one day voteWithMajority :: Rule-voteWithMajority = onRuleProposed $ voteWith_ (majority `withQuorum` 2) $ assessOnEveryVote >> assessOnTimeDelay oneDay+voteWithMajority = onRuleProposed $ callVoteRule (majority `withQuorum` 2) oneDay  -- | Change current system (the rules passed in parameter) to absolute majority (half participants plus one) returnToDemocracy :: [RuleNumber] -> Rule@@ -180,14 +180,14 @@ -- * Referendum & elections  -- | triggers a referendum, if the outcome is yes player 2 will be kicked-referendumOnKickPlayer :: Rule-referendumOnKickPlayer = referendum " kick player 2" (void $ delPlayer 2)---- | triggers elections (all players are candidates), the winner becomes game master-gameMasterElections :: Rule-gameMasterElections = do-   pls <- liftEffect getPlayers-   elections "Game Master" pls makeGM+--referendumOnKickPlayer :: Rule+--referendumOnKickPlayer = referendum " kick player 2" (void $ delPlayer 2)+--+---- | triggers elections (all players are candidates), the winner becomes game master+--gameMasterElections :: Rule+--gameMasterElections = do+--   pls <- liftEffect getPlayers+--   elections "Game Master" pls makeGM  makeGM :: PlayerNumber -> Nomex () makeGM pn = do
src/Language/Nomyx/Expression.hs view
@@ -54,7 +54,8 @@    DelVar         :: (V a) -> Nomex Bool
    --Events management    OnEvent        :: (Typeable e, Show e) => Event e -> ((EventNumber, e) -> Nomex ()) -> Nomex EventNumber
-   DelEvent       :: EventNumber -> Nomex Bool
+   DelEvent       :: EventNumber -> Nomex Bool+   GetEvents      :: NomexNE [EventInfo]
    SendMessage    :: (Typeable a, Show a) => Msg a -> a -> Nomex ()
    --Rules management
    ProposeRule    :: RuleInfo -> Nomex Bool
@@ -129,11 +130,12 @@  -- | Composable events data Event a where-   SumEvent   :: Event a -> Event a -> Event a        -- The first event to fire will be returned-   AppEvent   :: Event (a -> b) -> Event a -> Event b -- Both events should fire, and then the result is returned-   PureEvent  :: a -> Event a                         -- Create a fake event. The result is useable with no delay.-   EmptyEvent :: Event a                              -- An event that is never fired.-   BaseEvent  :: (Typeable a) => Field a -> Event a   -- Embed a base event+   SumEvent       :: Event a -> Event a -> Event a            -- The first event to fire will be returned+   AppEvent       :: Event (a -> b) -> Event a -> Event b     -- Both events should fire, and then the result is returned+   PureEvent      :: a -> Event a                             -- Create a fake event. The result is useable with no delay.+   EmptyEvent     :: Event a                                  -- An event that is never fired.+   ShortcutEvents :: [Event a] -> ([a] -> Maybe b) -> Event b -- a result is returned as soon as it can be computed, dismissing the events that hasn't fired yet+   BaseEvent      :: (Typeable a) => Field a -> Event a       -- Embed a base event    deriving Typeable  -- | Base events@@ -181,6 +183,31 @@    (<|>) = SumEvent    empty = EmptyEvent +-- EventInfo++data EventInfo = forall e. (Typeable e, Show e) =>+   EventInfo {_eventNumber :: EventNumber,+              _ruleNumber  :: RuleNumber,+              event        :: Event e,+              handler      :: EventHandler e,+              _evStatus    :: Status,+              _env         :: [EventEnv]}++data EventEnv = forall e. (Typeable e, Show e) => EventEnv (Field e) e++type EventHandler e = (EventNumber, e) -> Nomex ()++deriving instance Show EventEnv++data Status = SActive | SDeleted deriving (Eq, Show)++instance Eq EventInfo where+   (EventInfo {_eventNumber=e1}) == (EventInfo {_eventNumber=e2}) = e1 == e2++instance Ord EventInfo where+   (EventInfo {_eventNumber=e1}) <= (EventInfo {_eventNumber=e2}) = e1 <= e2++ -- * Rule
 
 -- | Type of a rule function.@@ -234,5 +261,5 @@ concatMapM        :: (Monad m) => (a -> m [b]) -> [a] -> m [b] concatMapM f xs   =  liftM concat (mapM f xs)
 -$( makeLenses [''RuleInfo, ''PlayerInfo] )
+$( makeLenses [''RuleInfo, ''PlayerInfo, ''EventInfo] )
 
src/Language/Nomyx/Inputs.hs view
@@ -28,21 +28,21 @@ 
 -- | triggers a choice input to the user. The result will be sent to the callback
 onInputRadio :: (Typeable a, Eq a,  Show a) => String -> [a] -> (EventNumber -> a -> Nomex ()) -> PlayerNumber -> Nomex EventNumber
-onInputRadio title choices handler pn = onEvent (inputRadioHead pn title choices) (\(en, a) -> handler en a)
+onInputRadio title choices handler pn = onEvent (inputRadio' pn title choices) (\(en, a) -> handler en a)
 
 -- | the same, disregard the event number
 onInputRadio_ :: (Typeable a, Eq a, Show a) => String -> [a] -> (a -> Nomex ()) -> PlayerNumber -> Nomex EventNumber
-onInputRadio_ title choices handler pn = onEvent_ (inputRadioHead pn title choices) handler
+onInputRadio_ title choices handler pn = onEvent_ (inputRadio' pn title choices) handler
 
 -- | the same, suppress the event after first trigger
 onInputRadioOnce :: (Typeable a, Eq a, Show a) => String -> [a] -> (a -> Nomex ()) -> PlayerNumber -> Nomex EventNumber
-onInputRadioOnce title choices handler pn = onEventOnce (inputRadioHead pn title choices) handler
-
-inputRadioHead :: (Eq c, Show c, Typeable c) => PlayerNumber -> String -> [c] -> Event c
-inputRadioHead pn title choices = inputRadio pn title choices (head choices)
+onInputRadioOnce title choices handler pn = onEventOnce (inputRadio' pn title choices) handler
 -inputRadio :: (Eq c, Show c, Typeable c) => PlayerNumber -> String -> [c] -> c -> Event c
-inputRadio pn title cs _ = baseEvent $ baseInputRadio pn title (zip cs (show <$> cs))+inputRadio :: (Eq c, Show c, Typeable c) => PlayerNumber -> String -> [(c, String)] -> Event c
+inputRadio pn title cs = baseEvent $ baseInputRadio pn title cs++inputRadio' :: (Eq c, Show c, Typeable c) => PlayerNumber -> String -> [c] -> Event c
+inputRadio' pn title cs = inputRadio pn title (zip cs (show <$> cs))  baseInputRadio :: (Eq c, Show c, Typeable c) => PlayerNumber -> String -> [(c, String)] -> Field c
 baseInputRadio pn title cs = baseInputEvent pn title (Radio cs)
src/Language/Nomyx/Rules.hs view
@@ -21,13 +21,14 @@    suppressRule, suppressRule_, suppressAllRules,    modifyRule,    autoActivate,-   activateOrReject,+   activateOrRejectRule,    simulate,    metaruleVar, createMetaruleVar, addMetarule, testWithMetaRules, displayMetarules,    legal, illegal, noPlayPlayer, immutableRule,    autoDelete,    eraseAllRules,    getSelfRuleNumber, getSelfRule,+   onRuleProposed,    showRule    ) where @@ -124,8 +125,8 @@    return rs  -- | activate or reject a rule-activateOrReject :: RuleInfo -> Bool -> Nomex ()-activateOrReject r b = if b then activateRule_ (_rNumber r) else rejectRule_ (_rNumber r)+activateOrRejectRule :: RuleInfo -> Bool -> Nomex ()+activateOrRejectRule r b = if b then activateRule_ (_rNumber r) else rejectRule_ (_rNumber r)  -- | a rule can autodelete itself (generaly after having performed some actions) autoDelete :: Nomex ()@@ -197,7 +198,9 @@ simulate :: Nomex a -> NomexNE Bool -> NomexNE Bool simulate sim test = Simu sim test -+-- | sets a callback called for each rule proposed+onRuleProposed :: (RuleInfo -> Nomex ()) -> Nomex ()
+onRuleProposed f = void $ onEvent_ (ruleEvent Proposed) f  -- | a default rule
 defaultRule = RuleInfo  {
src/Language/Nomyx/Vote.hs view
@@ -11,315 +11,111 @@ import Prelude hiding (foldr)
 import Language.Nomyx.Expression
 import Language.Nomyx.Events-import Language.Nomyx.Variables-import Language.Nomyx.Outputs import Language.Nomyx.Inputs import Language.Nomyx.Players import Language.Nomyx.Rules
-import Data.Typeable
 import Control.Monad.State hiding (forM_)
 import Data.Maybe import Data.Time hiding (getCurrentTime)-import Data.Traversable hiding (mapM) import Control.Arrow
 import Control.Applicative
 import Data.List-import Data.Function import qualified Data.Map as M-import Control.Monad.Error (MonadError(..)) -+-- | a vote assessing function (such as unanimity, majority...)+type AssessFunction = VoteStats -> Maybe Bool -data VoteType a = ExclusiveVote (Maybe (Alts a))-                | NonExclusiveVote [Alts a]+-- | the vote statistics, including the number of votes per choice (Nothing means abstention),+-- the number of persons called to vote, and if the vote if finished (timeout or everybody voted)+data VoteStats = VoteStats { voteCounts     :: M.Map (Maybe Bool) Int,+                             nbParticipants :: Int,+                             voteFinished   :: Bool}+                             deriving (Show) -class (Eq (Alts a), Show (Alts a), Ord (Alts a), Typeable a) => Votable a where-   data Alts a-   alts :: [Alts a]                                -- The alternatives-   quota :: Alts a -> Int -> Int -> Int            -- "quota alts q total" gives the quota to reach for each alternatives-   name :: a -> String-   exclusiveWinner :: a ->  Maybe (Alts a, Alts a) --The Votable as only two alternatives, one excluding the other-   exclusiveWinner _ = Nothing+-- | vote at unanimity every incoming rule+unanimityVote :: Nomex ()+unanimityVote = onRuleProposed $ callVoteRule unanimity oneDay -type ForAgainst = Alts RuleInfo
-instance Votable RuleInfo where-   data Alts RuleInfo = For | Against deriving (Typeable, Enum, Show, Eq, Bounded, Read, Ord)-   alts = [For, Against]-   quota For q _ = q-   quota Against q vs = vs - q + 1-   name r = "rule " ++ (show $ _rNumber r)-   exclusiveWinner _ = Just (For, Against)+-- | call a vote on a rule for every players, with an assessing function and a delay+callVoteRule :: AssessFunction -> NominalDiffTime -> RuleInfo -> Nomex ()+callVoteRule assess delay ri = do+   endTime <- addUTCTime delay <$> liftEffect getCurrentTime+   callVoteRule' assess endTime ri -
-type Vote a = (PlayerNumber, Maybe (Alts a))-type VoteResult a = VoteStats a -> [Alts a]
-data VoteStats a = VoteStats { voteCounts :: M.Map (Maybe (Alts a)) Int,-                               voteFinished :: Bool}+callVoteRule' :: AssessFunction -> UTCTime -> RuleInfo -> Nomex ()+callVoteRule' assess endTime ri = do+   let title = "Vote for rule: \"" ++ (_rName ri) ++ "\" (#" ++ (show $ _rNumber ri) ++ "):"+   callVote assess endTime title (activateOrRejectRule ri) -data VoteData a = VoteData { msgEnd :: Msg [Alts a],                   -- message sent when the vote is finished with the winners
-                             voteVar :: ArrayVar PlayerNumber (Alts a),-- variable containing the votes
-                             inputNumbers :: [EventNumber],            -- numbers of the toggle inputs of the players (to delete them at the end of vote)
-                             assessFunction :: VoteResult a,           -- function used to count the votes-                             timeLimit :: Maybe UTCTime}               -- end of the vote-                             
-type Assessor a = StateT (VoteData a) Nomex ()
+-- | call a vote for every players, with an assessing function, a delay and a function to run on the result+callVote :: AssessFunction -> UTCTime -> String -> (Bool -> Nomex ()) -> Nomex ()+callVote assess endTime title payload = do+   pns <- liftEffect getAllPlayerNumbers+   void $ onEventOnce (voteWith endTime pns assess title) payload ---TODO: decorelate effects and non effects--- | Perform a vote.-voteWith :: (Votable a) => VoteResult a         -- ^ the function used to count the votes.-                        -> Assessor a           -- ^ assessors: when and how to perform the vote assessment (several assessors can be chained).-                        -> a                    -- ^ toVote: the matter to be voted.-                        -> [Alts a]             -- ^ the vote alternatives.-                        -> Nomex (Msg [Alts a]) -- ^ return value: a message containing the result of the vote. 
-voteWith countVotes assessors toVote als = do
-    pns <- liftEffect getAllPlayerNumbers
-    let toVoteName = name toVote
-    let msgEnd = Msg ("Result of votes for " ++ toVoteName) :: Msg [Alts a]
-    --create an array variable to store the votes
-    (voteVar :: ArrayVar PlayerNumber (Alts a)) <- newArrayVar_ ("Votes for " ++ toVoteName) pns
-    --create the voting buttons-    let askPlayer pn = onInputRadioOnce ("Vote for " ++ toVoteName ++ ":") als (putArrayVar_ voteVar pn) pn
-    inputs <- mapM askPlayer pns
-    let voteData = VoteData msgEnd voteVar inputs countVotes Nothing
-    --set the assessors-    evalStateT assessors voteData-    --display the vote-    displayVoteVar Nothing ("On-going vote for " ++ toVoteName ++ ":") voteVar-    displayVoteResult toVoteName voteData-    --clean the vote at the end-    cleanVote voteData-    return msgEnd+-- vote with a function able to assess the ongoing votes.+-- the vote can be concluded as soon as the result is known.+voteWith :: UTCTime -> [PlayerNumber] -> AssessFunction -> String -> Event Bool+voteWith timeLimit pns assess title = shortcutEvents (voteEvents timeLimit title pns) (assess . getVoteStats (length pns)) --- | Performs a vote, all the possible alternatives are selected.-voteWith_ :: (Votable a) => VoteResult a -> Assessor a -> a -> Nomex (Msg [Alts a]) 
-voteWith_ assessFunction assessors toVote = voteWith assessFunction assessors toVote alts
+-- list of vote events+voteEvents :: UTCTime -> String -> [PlayerNumber] -> [Event (Maybe Bool)]+voteEvents time title pns = map (singleVote time title) pns -
--- | assess the vote on every new vote with the assess function, and as soon as the vote has an issue (positive of negative), sends a signal
-assessOnEveryVote :: (Votable a) => Assessor a
-assessOnEveryVote = do
-   (VoteData msgEnd voteVar _ assess _) <- get
-   lift $ void $ onMsgVarEvent voteVar $ f assess msgEnd where-      f assess msgEnd (VUpdated votes) = do-         let res = assess $ getVoteStats votes False-         unless (null res) $ sendMessage msgEnd res-      f _ _ _ = return ()   
-
-
--- | assess the vote with the assess function when time is reached, and sends a signal with the issue (positive of negative)
-assessOnTimeLimit :: (Votable a) => UTCTime -> Assessor a
-assessOnTimeLimit time = do
-   (VoteData msgEnd voteVar i assess _) <- get-   put (VoteData msgEnd voteVar i assess (Just time))
-   lift $ void $ onEvent_ (timeEvent time) $ \_ -> do
-      votes <- getMsgVarData_ voteVar
-      sendMessage msgEnd (assess $ getVoteStats votes True)
+-- trigger the display of a radio button choice on the player screen, yelding either Just True or Just False.+-- after the time limit, the value sent back is Nothing.+singleVote :: UTCTime -> String -> PlayerNumber -> Event (Maybe Bool)+singleVote timeLimit title pn = (Just <$> inputRadio pn title [(True, "For"), (False, "Against")]) <|> (Nothing <$ timeEvent timeLimit) -   
--- | assess the vote with the assess function when time is elapsed, and sends a signal with the issue (positive of negative)
-assessOnTimeDelay :: (Votable a) => NominalDiffTime -> Assessor a
-assessOnTimeDelay delay = do
-   t <- addUTCTime delay <$> lift (liftEffect getCurrentTime)
-   assessOnTimeLimit t
-
--- | assess the vote only when every body voted. An error is generated if the assessing function returns Nothing.
-assessWhenEverybodyVoted :: (Votable a) => Assessor a
-assessWhenEverybodyVoted = do
-   (VoteData msgEnd voteVar _ assess _) <- get
-   lift $ void $ onMsgVarEvent voteVar $ f assess msgEnd where-      f assess msgEnd (VUpdated votes) = when (all isJust (map snd votes)) $ sendMessage msgEnd $ assess $ getVoteStats votes True
-      f _ _ _ = return ()
-
--- | clean events and variables necessary for the vote
-cleanVote :: (Votable a) => VoteData a -> Nomex EventNumber
-cleanVote (VoteData msgEnd voteVar inputsNumber _ _) = onMessageOnce msgEnd $ \_ -> do
-   delMsgVar voteVar
-   mapM_ delEvent inputsNumber
-
--- | a quorum is the neccessary number of voters for the validity of the vote
-quorum :: (Votable a) => Int -> VoteStats a -> Bool
-quorum q vs = (voted vs) >= q
-
--- | adds a quorum to an assessing function
-withQuorum :: (Votable a) => VoteResult a -> Int -> VoteResult a
-withQuorum assess q vs = if quorum q vs then assess vs else []
+-- | assess the vote results according to a unanimity+unanimity :: AssessFunction+unanimity voteStats = voteQuota (nbVoters voteStats) voteStats --- | assess the vote results according to a unanimity (everybody votes for)
-unanimity :: (Votable a) => VoteStats a -> [Alts a]
-unanimity vs = voteQuota (nbVoters vs) vs
-  
--- | assess the vote results according to an absolute majority (half voters plus one, no quorum is needed)
-majority :: (Votable a) => VoteStats a -> [Alts a]
-majority vs = voteQuota ((nbVoters vs) `div` 2 + 1) vs
-
+-- | assess the vote results according to an absolute majority (half voters plus one)
+majority :: AssessFunction
+majority voteStats = voteQuota ((nbVoters voteStats) `div` 2 + 1) voteStats+                       
 -- | assess the vote results according to a majority of x (in %)
-majorityWith :: (Votable a) => Int -> VoteStats a -> [Alts a]
-majorityWith x vs = voteQuota ((nbVoters vs) * x `div` 100 + 1) vs
-
--- | assess the vote results according to a necessary number of positive votes
-numberVotes :: (Votable a) => Int -> VoteStats a -> [Alts a]
-numberVotes = voteQuota
---- | the winners are the x vote alternatives with the more votes
-firstXBest :: forall a. (Votable a) => Int -> VoteStats a -> [Alts a]
-firstXBest x votes = map fst $ takeInGroups x sortedVotesWithExAequoGroups where-   voted (Just a, n) = Just (a, n)-   voted (Nothing, _) = Nothing-   sortedVotesWithExAequoGroups :: [[(Alts a, Int)]]-   sortedVotesWithExAequoGroups = groupBy ((==) `on` snd) $ sortWith snd $ mapMaybe voted $ M.assocs (voteCounts votes)+majorityWith :: Int -> AssessFunction
+majorityWith x voteStats = voteQuota ((nbVoters voteStats) * x `div` 100 + 1) voteStats ---take n elements from the first lists, but do not break groups-takeInGroups :: Int -> [[a]] -> [a]-takeInGroups 0 _ = []-takeInGroups n grps = if grpsSize <= n then head grps ++ takeInGroups (n - grpsSize) (tail grps) else head grps where-   grpsSize = length $ head grps+-- | assess the vote results according to a fixed number of positive votes+numberVotes :: Int -> AssessFunction+numberVotes x voteStats = voteQuota x voteStats+
+-- | adds a quorum to an assessing function
+withQuorum :: AssessFunction -> Int -> AssessFunction
+withQuorum f minNbVotes = \voteStats -> if (voted voteStats) >= minNbVotes+                                        then f voteStats+                                        else if voteFinished voteStats then Just False else Nothing --- | the winner is the vote alternative with the more votes
-firstBest :: (Votable a) => VoteStats a -> [Alts a]
-firstBest = firstXBest 1+getVoteStats :: Int -> [Maybe Bool] -> VoteStats+getVoteStats npPlayers votes = VoteStats+   {voteCounts   = M.fromList $ counts votes,+    nbParticipants = npPlayers,+    voteFinished = length votes == npPlayers} -sortWith :: Ord b => (a -> b) -> [a] -> [a]-sortWith f = sortBy (\x y -> compare (f x) (f y))-
--- | return the vote alternatives that are above threshold
-voteQuota :: forall a. (Votable a) => Int -> VoteStats a -> [Alts a]
-voteQuota q votes = case exclusiveWinner (undefined :: a) of-   Nothing -> catMaybes $ M.keys $ M.filter (>= q) (voteCounts votes)-   Just a -> maybeToList $ exclusiveVoteQuota q votes a+counts :: (Eq a, Ord a) => [a] -> [(a, Int)]+counts as = map (head &&& length) (group $ sort as) --- | in case of exclusive winner-exclusiveVoteQuota :: (Votable a) => Int -> VoteStats a -> (Alts a, Alts a) -> Maybe (Alts a)-exclusiveVoteQuota q votes (for, against)-   | M.findWithDefault 0 (Just for) vs     >= q                   = Just for-   | M.findWithDefault 0 (Just against) vs > (nbVoters votes) - q = Just against+-- | Compute a result based on a quota of positive votes.+-- the result can be positive if the quota if reached, negative if the quota cannot be reached anymore at that point, or still pending.+voteQuota :: Int -> VoteStats -> Maybe Bool+voteQuota q voteStats+   | M.findWithDefault 0 (Just True)  vs >= q                       = Just True+   | M.findWithDefault 0 (Just False) vs > (nbVoters voteStats) - q = Just False    | otherwise = Nothing-   where vs = voteCounts votes+   where vs = voteCounts voteStats   -- | number of people that voted if the voting is finished, -- total number of people that should vote otherwise-nbVoters :: (Votable a) => VoteStats a -> Int+nbVoters :: VoteStats -> Int nbVoters vs-   | voteFinished vs = (totalVoters vs) - (notVoted vs)-   | otherwise = totalVoters vs+   | voteFinished vs = (nbParticipants vs) - (notVoted vs)+   | otherwise = nbParticipants vs -totalVoters, voted, notVoted :: (Votable a) => VoteStats a -> Int+totalVoters, voted, notVoted :: VoteStats -> Int totalVoters vs = M.foldr (+) 0 (voteCounts vs) notVoted    vs = fromMaybe 0 $ M.lookup Nothing (voteCounts vs) voted       vs = (totalVoters vs) - (notVoted vs)---getVoteStats :: (Votable a) => [Vote a] -> Bool -> VoteStats a-getVoteStats vs voteFinished = VoteStats-   {voteCounts = M.fromList $ counts (snd <$> vs),-    voteFinished = voteFinished}--counts :: (Eq a, Ord a) => [a] -> [(a, Int)]-counts as = map (head &&& length) (group $ sort as)---displayVoteVar :: (Votable a) => (Maybe PlayerNumber) -> String -> ArrayVar PlayerNumber (Alts a) -> Nomex EventNumber-displayVoteVar mpn title mv = displayVar' mpn mv (showOnGoingVote title)--showChoice :: (Votable a) => Maybe (Alts a) -> String-showChoice (Just a) = show a-showChoice Nothing  = "Not Voted"--showChoices :: (Votable a) => [Alts a] -> String-showChoices [] = "no result"-showChoices cs = intercalate ", " $ map show cs--showOnGoingVote :: (Votable a) => String -> [(PlayerNumber, Maybe (Alts a))] -> NomexNE String-showOnGoingVote title listVotes = do-   list <- mapM showVote listVotes-   return $ title ++ "\n" ++ concatMap (\(name, vote) -> name ++ "\t" ++ vote ++ "\n") list--showFinishedVote :: (Votable a) =>  [(PlayerNumber, Maybe (Alts a))] -> NomexNE String-showFinishedVote l = do-   let voted = filter (\(_, r) -> isJust r) l-   votes <- mapM showVote voted-   return $ intercalate ", " $ map (\(name, vote) -> name ++ ": " ++ vote) votes--showVote :: (Votable a) => (PlayerNumber, Maybe (Alts a)) -> NomexNE (String, String)-showVote (pn, v) = do-   name <- showPlayer pn-   return (name, showChoice v)-                                              
-displayVoteResult :: (Votable a) => String -> VoteData a -> Nomex OutputNumber-displayVoteResult toVoteName (VoteData msgEnd voteVar _ _ _) = onMessage msgEnd $ \result -> do
-   vs <- getMsgVarData_ voteVar-   votes <- liftEffect $ showFinishedVote vs-   void $ outputAll_ $ "Vote result for " ++ toVoteName ++ ": " ++ showChoices result ++ " (" ++ votes ++ ")"---- | any new rule will be activate if the rule in parameter returns For
-onRuleProposed :: (RuleInfo -> Nomex (Msg [ForAgainst]) ) -> Rule
-onRuleProposed f = void $ onEvent_ (ruleEvent Proposed) $ \rule -> do
-    resp <- f rule-    void $ onMessageOnce resp $ (activateOrReject rule) . (== [For])---- * Referendum & elections--data Referendum = Referendum String deriving (Typeable)-instance Votable Referendum where-   data Alts Referendum = Yes | No deriving (Enum, Show, Eq, Bounded, Read, Ord)-   alts = [Yes, No]-   quota Yes q _ = q-   quota No q vs = vs - q + 1-   name (Referendum n) = "referendum on " ++ n--referendum :: String -> Nomex () -> Rule-referendum name action = do-   msg <- voteWith_ (majority `withQuorum` 2) (assessOnEveryVote >> assessOnTimeDelay oneDay) (Referendum name)-   void $ onMessageOnce msg resolution where-      resolution [Yes] = do-            outputAll_ "Positive result of referendum"-            action-      resolution [No] = void $ outputAll_ "Negative result of referendum"-      resolution []   = void $ outputAll_ "No result for referendum"-      resolution _    = throwError "Impossible result for referendum"---data Election = Election String deriving (Typeable)-instance Votable Election where-   data Alts Election = Candidate {candidate :: PlayerInfo}-   alts = map (\n -> Candidate (PlayerInfo n "" Nothing)) [1..]-   quota _ q _ = q-   name (Election n) = "elections on " ++ n--instance Show (Alts Election) where-   show (Candidate (PlayerInfo _ name _)) = name--instance Eq (Alts Election) where-   (Candidate (PlayerInfo pn1 _ _)) == (Candidate (PlayerInfo pn2 _ _)) = pn1 == pn2--instance Ord (Alts Election) where-   compare (Candidate (PlayerInfo pn1 _ _)) (Candidate (PlayerInfo pn2 _ _)) = compare pn1 pn2--elections :: String -> [PlayerInfo] -> (PlayerNumber -> Nomex ()) -> Nomex ()-elections name pns action = do-   msg <- voteWith majority (assessOnTimeDelay oneDay) (Election name) (Candidate <$> pns)-   void $ onMessageOnce msg resolution where-      resolution [Candidate pi] = do-         outputAll_ $ "Result of elections: player(s) " ++ (show $ _playerName pi) ++ " won!"-         action $ _playerNumber pi-      resolution [] = void $ outputAll_ "Result of elections: nobody won!"-      resolution _  = throwError "Impossible result for elections"--voteEvent :: UTCTime -> [PlayerNumber] -> Event ([Maybe Bool])-voteEvent time pns = sequenceA $ map (singleVote time) pns--singleVote :: UTCTime -> PlayerNumber -> Event (Maybe Bool)-singleVote timeLimit pn = (Just <$> inputRadio pn "Vote for "[True, False] True) <|> (Nothing <$ timeEvent timeLimit)--vote :: UTCTime -> [PlayerNumber] -> Event Bool-vote timeLimit pns = unanimity' <$> (voteEvent timeLimit pns)--unanimity' :: [Maybe Bool] -> Bool-unanimity' = all (== Just True)--callVote :: UTCTime -> Nomex ()-callVote t = do-   pns <- liftEffect getAllPlayerNumbers-   void $ onEventOnce (vote t pns) (outputAll_ . show)--