haverer 0.2.0.0 → 0.3.0.0
raw patch · 12 files changed
+190/−31 lines, 12 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Haverer.Player: DuplicatePlayers :: [a] -> Error a
- Haverer.Player: InvalidNumPlayers :: Int -> Error a
- Haverer.Player: data Error a
- Haverer.Player: data PlayerSet a
- Haverer.Player: instance GHC.Classes.Eq a => GHC.Classes.Eq (Haverer.Player.Error a)
- Haverer.Player: instance GHC.Classes.Eq a => GHC.Classes.Eq (Haverer.Player.PlayerSet a)
- Haverer.Player: instance GHC.Show.Show a => GHC.Show.Show (Haverer.Player.Error a)
- Haverer.Player: instance GHC.Show.Show a => GHC.Show.Show (Haverer.Player.PlayerSet a)
- Haverer.Player: toPlayerSet :: Ord a => [a] -> Either (Error a) (PlayerSet a)
- Haverer.Player: toPlayers :: PlayerSet a -> [a]
- Haverer.Testing: instance Test.QuickCheck.Arbitrary.Arbitrary (Haverer.Player.PlayerSet Haverer.Testing.PlayerId)
+ Haverer: DuplicatePlayers :: [a] -> PlayerSetError a
+ Haverer: InvalidNumPlayers :: Int -> PlayerSetError a
+ Haverer: data PlayerSet a
+ Haverer: data PlayerSetError a
+ Haverer.PlayerSet: DuplicatePlayers :: [a] -> PlayerSetError a
+ Haverer.PlayerSet: InvalidNumPlayers :: Int -> PlayerSetError a
+ Haverer.PlayerSet: data PlayerSet a
+ Haverer.PlayerSet: data PlayerSetError a
+ Haverer.PlayerSet: instance Data.Foldable.Foldable Haverer.PlayerSet.PlayerSet
+ Haverer.PlayerSet: instance Data.Traversable.Traversable Haverer.PlayerSet.PlayerSet
+ Haverer.PlayerSet: instance GHC.Base.Functor Haverer.PlayerSet.PlayerSet
+ Haverer.PlayerSet: instance GHC.Classes.Eq a => GHC.Classes.Eq (Haverer.PlayerSet.PlayerSet a)
+ Haverer.PlayerSet: instance GHC.Classes.Eq a => GHC.Classes.Eq (Haverer.PlayerSet.PlayerSetError a)
+ Haverer.PlayerSet: instance GHC.Show.Show a => GHC.Show.Show (Haverer.PlayerSet.PlayerSet a)
+ Haverer.PlayerSet: instance GHC.Show.Show a => GHC.Show.Show (Haverer.PlayerSet.PlayerSetError a)
+ Haverer.PlayerSet: randomize :: MonadRandom m => PlayerSet a -> m (PlayerSet a)
+ Haverer.PlayerSet: rotate :: PlayerSet a -> PlayerSet a
+ Haverer.PlayerSet: toPlayerSet :: Ord a => [a] -> Either (PlayerSetError a) (PlayerSet a)
+ Haverer.PlayerSet: toPlayers :: PlayerSet a -> [a]
+ Haverer.Testing: instance Test.QuickCheck.Arbitrary.Arbitrary (Haverer.PlayerSet.PlayerSet Haverer.Testing.PlayerId)
- Haverer: toPlayerSet :: Ord a => [a] -> Either (Error a) (PlayerSet a)
+ Haverer: toPlayerSet :: Ord a => [a] -> Either (PlayerSetError a) (PlayerSet a)
Files
- Changelog.md +19/−0
- README.md +74/−0
- haverer.cabal +4/−1
- lib/Haverer.hs +8/−2
- lib/Haverer/CLI/CommandLine.hs +2/−1
- lib/Haverer/Engine.hs +1/−1
- lib/Haverer/Game.hs +7/−2
- lib/Haverer/Player.hs +0/−19
- lib/Haverer/PlayerSet.hs +67/−0
- lib/Haverer/Round.hs +5/−2
- lib/Haverer/Testing.hs +1/−1
- tests/test.hs +2/−2
+ Changelog.md view
@@ -0,0 +1,19 @@+# 0.3.0.0 - 2015-06-08++* `PlayerSet` moved to new module, `Haverer.PlayerSet`+* `PlayerSet` now implements `Foldable` and `Traversable`+* New `rotate` and `randomize` methods for `PlayerSet`++# 0.2.0.0 - 2015-06-08++* `Deck` uses associated types for whether or not it is complete+* New `FullType` alias for `Deck 'Complete`+* Burn card is the *first* card from the deck, rather than the first card+ after players have been dealt.+* Burn card is now available for finished rounds (`Haverer.Round.getBurnCard`)+* A list of survivors of each round can be obtained, once the round is over+ (`Haverer.Round.survivors`)++# 0.1.0.0 - 2015-06-07++Initial release.
+ README.md view
@@ -0,0 +1,74 @@+# Haverer++Haverer is a simple implementation of the excellent card game,+[Love Letter](http://boardgamegeek.com/boardgame/129622/love-letter).++It follows the original Japanese rules, rather than the newer American ones.++## Running++At the moment, you'll need to compile from source, which means you'll need to+get set up with `cabal`. The easiest way to do this is to install the+[Haskell Platform](https://www.haskell.org/platform/).++Once you've done that, run the following commands:++```+$ git clone https://github.com/jml/haverer.git+$ cd haverer+$ cabal sandbox init+$ cabal configure+$ cabal install --dependencies-only+$ cabal run+```++That last command, `cabal run`, will run a command-line version of the game.++## License++This code is copyright Jonathan M. Lange, 2014-2015, and is made available+under the Apache License, version 2.0. See LICENSE for details.++## Roadmap++0. Procrastinate endlessly learning new Haskell tricks+1. Wrap this whole thing in an API server+2. Make that server available to the public+3. Put a nice-ish web frontend in front++## Bugs++Please file issues on the+[Github issue tracker](https://github.com/jml/haverer/issues)++## Patches++Are most welcome.++## History++The original implementation of this game is called+[loveletter](https://github.com/jml/loveletter) and is written in+[Rust](http://www.rust-lang.org/).++## Thanks++Thanks to the following people, who have helped with advice, kindness, or by patiently+smiling and nodding while I've rambled on about this project:++* metagnome+* RAOF+* @mithrandi+* pjdelport+* tomprince+* radix+* teh+* simpson+* joliette++Any infelicities are my own.++If you are one of these people and would like to be referred to in another way+(perhaps you'd like me to link to your Github page?), please contact me. You+know how.+
haverer.cabal view
@@ -16,7 +16,7 @@ -- see http://haskell.org/cabal/users-guide/ name: haverer-version: 0.2.0.0+version: 0.3.0.0 synopsis: Implementation of the rules of Love Letter description: Implementation of the rules of Love Letter license: Apache-2.0@@ -27,6 +27,8 @@ category: Game build-type: Simple cabal-version: >=1.10+extra-source-files: README.md+ , Changelog.md source-repository head type: git@@ -56,6 +58,7 @@ , Haverer.Internal.Error , Haverer.Internal.Ring , Haverer.Player+ , Haverer.PlayerSet , Haverer.Round , Haverer.Testing , Haverer.ValidMoves
lib/Haverer.hs view
@@ -47,6 +47,8 @@ getDiscards, getHand, isProtected,+ PlayerSet,+ PlayerSetError(..), toPlayers, toPlayerSet, ) where@@ -68,9 +70,13 @@ Player, getDiscards, getHand,- isProtected,+ isProtected)+import Haverer.PlayerSet (+ PlayerSetError(..),+ PlayerSet, toPlayers,- toPlayerSet)+ toPlayerSet+ ) import Haverer.Round ( BadAction, Result(..),
lib/Haverer/CLI/CommandLine.hs view
@@ -33,7 +33,8 @@ import Haverer.Action (Play(..), viewAction) import Haverer.Deck (Card(..))-import Haverer.Player (PlayerSet, getDiscards, isProtected, toPlayers)+import Haverer.Player (getDiscards, isProtected)+import Haverer.PlayerSet (PlayerSet, toPlayers) import Haverer.Round ( Event(..), Result(..),
lib/Haverer/Engine.hs view
@@ -31,7 +31,7 @@ import Haverer.Deck (Card) import qualified Haverer.Game as Game import qualified Haverer.Round as Round-import Haverer.Player (PlayerSet)+import Haverer.PlayerSet (PlayerSet) class Monad m => MonadEngine m playerId where
lib/Haverer/Game.hs view
@@ -33,8 +33,9 @@ import Control.Monad.Random (MonadRandom) import Haverer.Deck (FullDeck, newDeck)-import Haverer.Player (+import Haverer.PlayerSet ( PlayerSet,+ rotate, toPlayers, ) import Haverer.Round (Round)@@ -75,11 +76,15 @@ newRound game = newRound' game <$> newDeck -- | Indicate that the specified players won.+--+-- Since 0.3, will also rotate the order of play, so the person to the left of+-- the previous first person is now the first. playersWon :: Ord playerId => Game playerId -> [playerId] -> Either (Outcome playerId) (Game playerId) playersWon game ps =- bumpRoundsPlayed <$> onCounter game (`Counter.incrementMany` ps)+ rotatePlayers . bumpRoundsPlayed <$> onCounter game (`Counter.incrementMany` ps) where bumpRoundsPlayed g = g { _roundsPlayed = _roundsPlayed g + 1 }+ rotatePlayers g = g { _playerSet = rotate (_playerSet g) } -- | Return the number of rounds played. roundsPlayed :: Game playerId -> Int
lib/Haverer/Player.hs view
@@ -17,7 +17,6 @@ module Haverer.Player ( bust,- Error(..), discardAndDraw, eliminate, getDiscards,@@ -25,35 +24,17 @@ isProtected, makePlayer, Player,- PlayerSet, playCard, protect, swapHands,- toPlayers,- toPlayerSet, unprotect ) where import BasicPrelude import Control.Lens hiding (chosen)-import Control.Monad.Except import Haverer.Deck (Card) --data Error a = InvalidNumPlayers Int | DuplicatePlayers [a] deriving (Show, Eq)---newtype PlayerSet a = PlayerSet { toPlayers :: [a] } deriving (Show, Eq)---toPlayerSet :: Ord a => [a] -> Either (Error a) (PlayerSet a)-toPlayerSet playerIds- | numPlayers /= numDistinctPlayers = throwError (DuplicatePlayers playerIds)- | numPlayers < 2 || numPlayers > 4 = throwError (InvalidNumPlayers numPlayers)- | otherwise = (return . PlayerSet) playerIds- where numPlayers = length playerIds- numDistinctPlayers = (length . nub . sort) playerIds data Player = Active {
+ lib/Haverer/PlayerSet.hs view
@@ -0,0 +1,67 @@+-- Copyright (c) 2014-2015 Jonathan M. Lange <jml@mumak.net>+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Haverer.PlayerSet (+ PlayerSet+ , PlayerSetError(..)+ , toPlayerSet+ , toPlayers+ , randomize+ , rotate+ ) where+++import BasicPrelude+import Control.Monad.Except+import Control.Monad.Random+import Data.Foldable (Foldable)+import Data.Traversable (Traversable)+import System.Random.Shuffle+++data PlayerSetError a = InvalidNumPlayers Int | DuplicatePlayers [a] deriving (Show, Eq)+++newtype PlayerSet a = PlayerSet { toPlayers :: [a] } deriving (Show, Eq, Functor, Traversable, Foldable)+++toPlayerSet :: Ord a => [a] -> Either (PlayerSetError a) (PlayerSet a)+toPlayerSet playerIds+ | numPlayers /= numDistinctPlayers = throwError (DuplicatePlayers playerIds)+ | numPlayers < 2 || numPlayers > 4 = throwError (InvalidNumPlayers numPlayers)+ | otherwise = (return . PlayerSet) playerIds+ where numPlayers = length playerIds+ numDistinctPlayers = (length . nub . sort) playerIds+++-- | Rotate the order of the PlayerSet+--+-- The player who was first is now last, whoever was second is now third,+-- whoever was third is now second, etc.+--+-- Since 0.3+rotate :: PlayerSet a -> PlayerSet a+rotate (PlayerSet (x:xs)) = PlayerSet (xs ++ [x])+rotate _ = error "Empty PlayerSet is impossible"+++-- | Randomize the order of the PlayerSet+--+-- Since 0.3+randomize :: MonadRandom m => PlayerSet a -> m (PlayerSet a)+randomize = map PlayerSet . shuffleM . toPlayers+
lib/Haverer/Round.hs view
@@ -84,12 +84,15 @@ makePlayer, playCard, Player,- PlayerSet, protect, swapHands,- toPlayers, unprotect )+import Haverer.PlayerSet (+ PlayerSet,+ toPlayers+ )+ import Haverer.Internal.Error (assertRight) import Haverer.Internal.Ring (Ring, advance1, currentItem, dropItem1, makeRing, nextItem)
lib/Haverer/Testing.hs view
@@ -34,7 +34,7 @@ import Haverer.Action (Play(..)) import Haverer.Deck (baseCards, Card(..), FullDeck, makeDeck)-import Haverer.Player (PlayerSet, toPlayerSet)+import Haverer.PlayerSet (PlayerSet, toPlayerSet) import Haverer.Round ( Round , Result(..)
tests/test.hs view
@@ -19,7 +19,7 @@ import Test.Tasty import qualified CLI-import qualified Player+import qualified PlayerSet import qualified Ring import qualified Round @@ -28,7 +28,7 @@ suite = testGroup "Test Suite" [ CLI.suite- , Player.suite+ , PlayerSet.suite , Ring.suite , Round.suite ]