-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Implementation of the rules of Love Letter -- -- Implementation of the rules of Love Letter @package haverer @version 0.2.0.0 module Haverer.Internal.Ring data Ring a makeRing :: [a] -> Maybe (Ring a) advance :: Ring a -> Ring a advance1 :: Ring a -> Either a (Ring a) dropItem :: (Eq a) => Ring a -> a -> Maybe (Ring a) dropItem1 :: (Eq a) => Ring a -> a -> Either a (Ring a) toList :: Ring a -> [a] nextItem :: Ring a -> a ringSize :: Ring a -> Int currentItem :: Ring a -> a instance GHC.Classes.Eq a => GHC.Classes.Eq (Haverer.Internal.Ring.Ring a) instance GHC.Show.Show a => GHC.Show.Show (Haverer.Internal.Ring.Ring a) module Haverer.Internal.Error -- | Assert that the value is Right. Throws error if it's Left. assertRight :: (Show a) => Text -> Either a b -> b -- | Assert that the value is Right. Throws error if it's Left. assertRight' :: Show a => Either a b -> b -- | error applied to Text -- -- Since 0.4.1 terror :: Text -> a module Haverer.Internal.Counter data Counter k a increment :: (Ord k, Num a) => Counter k a -> k -> Counter k a incrementMany :: (Ord k, Num a, Foldable m) => Counter k a -> m k -> Counter k a initialize :: (Ord k, Num a) => [k] -> Counter k a toList :: (Ord k) => Counter k a -> [(k, a)] top :: (Ord k, Ord a, Eq a) => Counter k a -> (a, [k]) topValue :: (Ord k, Ord a) => Counter k a -> a instance (GHC.Show.Show k, GHC.Show.Show a) => GHC.Show.Show (Haverer.Internal.Counter.Counter k a) module Haverer.CLI.Prompt class ConsoleText a toText :: ConsoleText a => a -> Text underline :: Char -> Text -> Text prompt :: ConsoleText e => Text -> (Text -> Either e a) -> IO (Either e a) repeatedlyPrompt :: ConsoleText e => Text -> (Text -> Either e a) -> IO a chooseItem :: ConsoleText a => Text -> [a] -> IO a chooseItem' :: ConsoleText a => Text -> Int -> [a] -> IO a instance Haverer.CLI.Prompt.ConsoleText Data.Text.Internal.Text instance Haverer.CLI.Prompt.ConsoleText GHC.Types.Int module Haverer.Deck allCards :: [Card] baseCards :: [Card] data Card Soldier :: Card Clown :: Card Knight :: Card Priestess :: Card Wizard :: Card General :: Card Minister :: Card Prince :: Card data DeckSize Incomplete :: DeckSize Complete :: DeckSize deal :: FullDeck -> Int -> Maybe (Card, [Card], Deck Incomplete) data Deck (a :: DeckSize) type FullDeck = Deck Complete makeDeck :: [Card] -> Maybe (Deck Complete) newDeck :: MonadRandom m => m (Deck Complete) pop :: Deck a -> (Maybe Card, Deck Incomplete) shuffleDeck :: MonadRandom m => Deck a -> m (Deck a) toList :: Deck a -> [Card] instance GHC.Classes.Ord (Haverer.Deck.Deck a) instance GHC.Show.Show (Haverer.Deck.Deck a) instance GHC.Classes.Eq (Haverer.Deck.Deck a) instance GHC.Enum.Enum Haverer.Deck.Card instance GHC.Classes.Ord Haverer.Deck.Card instance GHC.Show.Show Haverer.Deck.Card instance GHC.Classes.Eq Haverer.Deck.Card module Haverer.Player bust :: Player -> Card -> Player data Error a InvalidNumPlayers :: Int -> Error a DuplicatePlayers :: [a] -> Error a discardAndDraw :: Player -> Maybe Card -> Player eliminate :: Player -> Player getDiscards :: Player -> [Card] getHand :: Player -> Maybe Card isProtected :: Player -> Maybe Bool makePlayer :: Card -> Player data Player data PlayerSet a -- | Given a dealt and chosen card, update the hand to chosen, and chuck -- whatever wasn't played onto the discard pile. playCard :: Player -> Card -> Card -> Maybe Player protect :: Player -> Player swapHands :: Player -> Player -> (Player, Player) toPlayers :: PlayerSet a -> [a] toPlayerSet :: Ord a => [a] -> Either (Error a) (PlayerSet a) unprotect :: Player -> Player instance GHC.Classes.Eq Haverer.Player.Player instance GHC.Show.Show Haverer.Player.Player instance GHC.Classes.Eq a => GHC.Classes.Eq (Haverer.Player.PlayerSet a) instance GHC.Show.Show a => GHC.Show.Show (Haverer.Player.PlayerSet a) instance GHC.Classes.Eq a => GHC.Classes.Eq (Haverer.Player.Error a) instance GHC.Show.Show a => GHC.Show.Show (Haverer.Player.Error a) module Haverer.Action data BadPlay target -- | A thing that can be done with a card. data Play target NoEffect :: Play target Attack :: target -> Play target Guess :: target -> Card -> Play target -- | A validated card + play combination. Only guarantees that such a thing -- makes sense according to the rules, rather than the current state of -- the round. data Action target -- | If you're holding the Minister, there's a potential to "bust out" -- -- to have to immediately leave the round because you're holding another -- high card. bustingHand :: Card -> Card -> Bool getTarget :: Play target -> Maybe target -- | Return all valid plays for a hand. -- -- If the hand is one that would bust out (see bustingHand) then -- returns an empty list. getValidPlays :: player -> [player] -> Card -> Card -> [(Card, Play player)] -- | Given a player, a card, and a choice of play, decide whether it's a -- valid action. playToAction :: (Eq target, MonadError (BadPlay target) m) => target -> Card -> Play target -> m (Action target) viewAction :: Action target -> (target, Card, Play target) instance GHC.Show.Show target => GHC.Show.Show (Haverer.Action.BadPlay target) instance GHC.Show.Show target => GHC.Show.Show (Haverer.Action.Action target) instance GHC.Classes.Eq target => GHC.Classes.Eq (Haverer.Action.Action target) instance GHC.Show.Show target => GHC.Show.Show (Haverer.Action.Play target) instance GHC.Classes.Eq target => GHC.Classes.Eq (Haverer.Action.Play target) module Haverer.Round data Round playerId -- | Make a new round, given a complete Deck and a set of players. makeRound :: (Ord playerId, Show playerId) => FullDeck -> PlayerSet playerId -> Round playerId -- | Play a turn in a Round. -- -- This is the main function in this module. -- -- A turn has two steps. First, the player draws a card. If their hand -- "busts out" (due to holding the Minister and another high card), then -- they are eliminated and play proceeds to the next player. This is the -- Left return value, which returns the new Round and a Result -- indicating the player bust out. -- -- Second, the player plays one of these two cards. This is the -- Right return value, a function that takes the players chosen -- card and play, and returns either a BadAction or a new Round together -- with the Result of the play. playTurn :: (Ord playerId, Show playerId) => Round playerId -> Either (ActionM playerId (Result playerId, Round playerId)) (Card -> Play playerId -> ActionM playerId (Result playerId, Round playerId)) -- | Play a turn in a Round -- -- Similar to playTurn, except that instead of splitting the turn into -- two phases, there is a single, optional play. If the hand is a busting -- hand, then the play must be Nothing; if not, the play must be -- specified. playTurn' :: (Ord playerId, Show playerId) => Round playerId -> Maybe (Card, Play playerId) -> ActionM playerId (Result playerId, Round playerId) data BadAction playerId -- | The result of a turn. data Result playerId -- | The player whose turn it was "busted out", they held the Minister and -- another high card, and thus didn't get to play. BustedOut :: playerId -> Card -> Card -> Result playerId -- | The player performed an Action resulting in Event. Played :: (Action playerId) -> (Event playerId) -> Result playerId -- | A change to the Round that comes as result of a player's actions. data Event playerId -- | Nothing happened. What the player did had no effect. NoChange :: Event playerId -- | The player is now protected. Protected :: playerId -> Event playerId -- | The first player has been forced to swap hands with the second. SwappedHands :: playerId -> playerId -> Event playerId -- | The player has been eliminated from the round. Eliminated :: playerId -> Event playerId -- | The player has been forced to discard their hand. ForcedDiscard :: playerId -> Event playerId -- | The second player has been forced to show their hand to the first. ForcedReveal :: playerId -> playerId -> Card -> Event playerId -- | The ID of the current player. If the Round is over or not started, -- this will be Nothing. currentPlayer :: Round playerId -> Maybe playerId currentTurn :: Ord playerId => Round playerId -> Maybe (playerId, (Card, Card)) -- | The IDs of all of the active players. getActivePlayers :: Round playerId -> [playerId] -- | Get the burn card for the Round. Only possible when the Round is over. -- -- Since 0.1.1 getBurnCard :: Round playerId -> Maybe Card -- | Get the player with the given ID. Nothing if there is no such player. getPlayer :: Ord playerId => Round playerId -> playerId -> Maybe Player -- | A map of player IDs to players. getPlayerMap :: Round playerId -> Map playerId Player -- | The IDs of all of the players. getPlayers :: Round playerId -> [playerId] getWinners :: Victory playerId -> [playerId] nextPlayer :: Round playerId -> Maybe playerId -- | The number of cards remaining in the deck. remainingCards :: Round playerId -> Int data Victory playerId -- | The given player is the only survivor. SoleSurvivor :: playerId -> Card -> Victory playerId -- | These players have the highest card. HighestCard :: Card -> [playerId] -> [(playerId, Card)] -> Victory playerId -- | The currently surviving players in the round, with their cards. survivors :: Victory playerId -> [(playerId, Card)] -- | If the Round is Over, return the Victory data. Otherwise, Nothing. victory :: Round playerId -> Maybe (Victory playerId) -- | Are all the cards in the Round? prop_allCardsPresent :: Round playerId -> Bool prop_burnCardsSame :: [Round playerId] -> Bool prop_multipleActivePlayers :: Round playerId -> Bool prop_ringIsActivePlayers :: Eq playerId => Round playerId -> Bool instance GHC.Show.Show playerId => GHC.Show.Show (Haverer.Round.Victory playerId) instance GHC.Classes.Eq playerId => GHC.Classes.Eq (Haverer.Round.Victory playerId) instance GHC.Show.Show playerId => GHC.Show.Show (Haverer.Round.Result playerId) instance GHC.Classes.Eq playerId => GHC.Classes.Eq (Haverer.Round.Result playerId) instance GHC.Show.Show playerId => GHC.Show.Show (Haverer.Round.Event playerId) instance GHC.Classes.Eq playerId => GHC.Classes.Eq (Haverer.Round.Event playerId) instance GHC.Show.Show playerId => GHC.Show.Show (Haverer.Round.BadAction playerId) instance GHC.Show.Show playerId => GHC.Show.Show (Haverer.Round.Round playerId) instance GHC.Show.Show Haverer.Round.RoundState module Haverer.Game data Game playerId data Outcome playerId -- | Get the final scores at the end of the game. finalScores :: Ord playerId => Outcome playerId -> [(playerId, Int)] -- | Create a new game for the given set of players. makeGame :: Ord playerId => PlayerSet playerId -> Game playerId -- | Start a new round of the game, shuffling the deck cards ourselves. newRound :: (Functor m, MonadRandom m, Ord playerId, Show playerId) => Game playerId -> m (Round playerId) -- | Start a new round of the game with an already-shuffled deck of cards. newRound' :: (Ord playerId, Show playerId) => Game playerId -> FullDeck -> Round playerId -- | Return the set of all players players :: Game playerId -> PlayerSet playerId -- | Indicate that the specified players won. playersWon :: Ord playerId => Game playerId -> [playerId] -> Either (Outcome playerId) (Game playerId) -- | Return the number of rounds played. roundsPlayed :: Game playerId -> Int -- | Return the current scores of all the players. scores :: Ord playerId => Game playerId -> [(playerId, Int)] -- | Get the winners of the game. winners :: Ord playerId => Outcome playerId -> [playerId] instance GHC.Show.Show playerId => GHC.Show.Show (Haverer.Game.Outcome playerId) instance GHC.Show.Show playerId => GHC.Show.Show (Haverer.Game.Game playerId) module Haverer.CLI.CommandLine class ConsoleText a toText :: ConsoleText a => a -> Text formatScores :: ConsoleText playerId => [(playerId, Int)] -> Text pickNumPlayers :: IO Int pickCardToPlay :: (Card, Card) -> IO Card pickPlay :: ConsoleText playerId => Card -> PlayerSet playerId -> IO (Play playerId) instance Haverer.CLI.Prompt.ConsoleText Haverer.Deck.Card instance Haverer.CLI.Prompt.ConsoleText a => Haverer.CLI.Prompt.ConsoleText (Haverer.Round.Round a) instance (GHC.Classes.Eq a, Haverer.CLI.Prompt.ConsoleText a, GHC.Show.Show a) => Haverer.CLI.Prompt.ConsoleText (Haverer.Round.Result a) instance Haverer.CLI.Prompt.ConsoleText a => Haverer.CLI.Prompt.ConsoleText (Haverer.Round.Victory a) module Haverer.Engine class Monad m => MonadEngine m playerId where badPlay _ = return () gameStarted _ = return () gameOver _ = return () roundStarted _ _ = return () roundOver _ = return () handStarted _ = return () handOver _ = return () badPlay :: MonadEngine m playerId => BadAction playerId -> m () choosePlay :: MonadEngine m playerId => PlayerSet playerId -> playerId -> Card -> Card -> m (Card, Play playerId) gameStarted :: MonadEngine m playerId => Game playerId -> m () gameOver :: MonadEngine m playerId => Outcome playerId -> m () roundStarted :: MonadEngine m playerId => Game playerId -> Round playerId -> m () roundOver :: MonadEngine m playerId => Victory playerId -> m () handStarted :: MonadEngine m playerId => Round playerId -> m () handOver :: MonadEngine m playerId => Result playerId -> m () playGame :: (Ord playerId, Show playerId, MonadRandom m, MonadEngine m playerId) => PlayerSet playerId -> m (Outcome playerId) module Haverer.ValidMoves getValidMoves :: Ord playerId => Round playerId -> [(Card, Play playerId)] protectedPlayers :: Ord playerId => Round playerId -> [playerId] movesThatTargetPlayer :: Ord playerId => Round playerId -> playerId -> [(Card, Play playerId)] attacksOnProtectedPlayers :: Ord playerId => Round playerId -> [(Card, Play playerId)] module Haverer.Testing type PlayerId = Int -- | Generate an event that might come up in the course of play. inRoundEvent :: Gen (Result PlayerId) -- | For a Round and a known-good Card and Play, play the cards and return -- the round and event. If the hand busts out, Card and Play are ignored. playTurn' :: (Ord a, Show a) => Round a -> Card -> Play a -> (Result a, Round a) -- | Generate a random round that might come up in the course of play. randomRound :: Gen (Round PlayerId) -- | Generate a random number of consecutive rounds, starting from an -- initial round. randomRounds :: Gen [Round PlayerId] -- | Take a list and generate a shuffled version of it. shuffled :: [a] -> Gen [a] instance Test.QuickCheck.Arbitrary.Arbitrary Haverer.Deck.FullDeck instance Test.QuickCheck.Arbitrary.Arbitrary (Haverer.Player.PlayerSet Haverer.Testing.PlayerId) instance Test.QuickCheck.Arbitrary.Arbitrary (Haverer.Round.Round Haverer.Testing.PlayerId) -- | Implementation of the rules of Love Letter. module Haverer data Game playerId -- | Create a new game for the given set of players. makeGame :: Ord playerId => PlayerSet playerId -> Game playerId -- | Start a new round of the game, shuffling the deck cards ourselves. newRound :: (Functor m, MonadRandom m, Ord playerId, Show playerId) => Game playerId -> m (Round playerId) -- | Start a new round of the game with an already-shuffled deck of cards. newRound' :: (Ord playerId, Show playerId) => Game playerId -> FullDeck -> Round playerId -- | Return the set of all players players :: Game playerId -> PlayerSet playerId -- | Return the number of rounds played. roundsPlayed :: Game playerId -> Int -- | Get the final scores at the end of the game. finalScores :: Ord playerId => Outcome playerId -> [(playerId, Int)] -- | Return the current scores of all the players. scores :: Ord playerId => Game playerId -> [(playerId, Int)] -- | Get the winners of the game. winners :: Ord playerId => Outcome playerId -> [playerId] data Round playerId data BadAction playerId -- | The result of a turn. data Result playerId -- | The player whose turn it was "busted out", they held the Minister and -- another high card, and thus didn't get to play. BustedOut :: playerId -> Card -> Card -> Result playerId -- | The player performed an Action resulting in Event. Played :: (Action playerId) -> (Event playerId) -> Result playerId -- | A change to the Round that comes as result of a player's actions. data Event playerId -- | Nothing happened. What the player did had no effect. NoChange :: Event playerId -- | The player is now protected. Protected :: playerId -> Event playerId -- | The first player has been forced to swap hands with the second. SwappedHands :: playerId -> playerId -> Event playerId -- | The player has been eliminated from the round. Eliminated :: playerId -> Event playerId -- | The player has been forced to discard their hand. ForcedDiscard :: playerId -> Event playerId -- | The second player has been forced to show their hand to the first. ForcedReveal :: playerId -> playerId -> Card -> Event playerId -- | Play a turn in a Round. -- -- This is the main function in this module. -- -- A turn has two steps. First, the player draws a card. If their hand -- "busts out" (due to holding the Minister and another high card), then -- they are eliminated and play proceeds to the next player. This is the -- Left return value, which returns the new Round and a Result -- indicating the player bust out. -- -- Second, the player plays one of these two cards. This is the -- Right return value, a function that takes the players chosen -- card and play, and returns either a BadAction or a new Round together -- with the Result of the play. playTurn :: (Ord playerId, Show playerId) => Round playerId -> Either (ActionM playerId (Result playerId, Round playerId)) (Card -> Play playerId -> ActionM playerId (Result playerId, Round playerId)) -- | Play a turn in a Round -- -- Similar to playTurn, except that instead of splitting the turn into -- two phases, there is a single, optional play. If the hand is a busting -- hand, then the play must be Nothing; if not, the play must be -- specified. playTurn' :: (Ord playerId, Show playerId) => Round playerId -> Maybe (Card, Play playerId) -> ActionM playerId (Result playerId, Round playerId) -- | The IDs of all of the players. getPlayers :: Round playerId -> [playerId] -- | A map of player IDs to players. getPlayerMap :: Round playerId -> Map playerId Player -- | The ID of the current player. If the Round is over or not started, -- this will be Nothing. currentPlayer :: Round playerId -> Maybe playerId currentTurn :: Ord playerId => Round playerId -> Maybe (playerId, (Card, Card)) data Card Soldier :: Card Clown :: Card Knight :: Card Priestess :: Card Wizard :: Card General :: Card Minister :: Card Prince :: Card data Deck (a :: DeckSize) data DeckSize Incomplete :: DeckSize Complete :: DeckSize type FullDeck = Deck Complete newDeck :: MonadRandom m => m (Deck Complete) -- | A thing that can be done with a card. data Play target NoEffect :: Play target Attack :: target -> Play target Guess :: target -> Card -> Play target viewAction :: Action target -> (target, Card, Play target) data Player getDiscards :: Player -> [Card] getHand :: Player -> Maybe Card isProtected :: Player -> Maybe Bool toPlayers :: PlayerSet a -> [a] toPlayerSet :: Ord a => [a] -> Either (Error a) (PlayerSet a)