hanabi-dealer 0.2.0.0 → 0.2.1.0
raw patch · 3 files changed
+50/−32 lines, 3 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Game.Hanabi: Discard :: Card -> Result
+ Game.Hanabi: Fail :: Card -> Result
+ Game.Hanabi: None :: Result
+ Game.Hanabi: Success :: Card -> Result
+ Game.Hanabi: [markPlayable] :: Verbosity -> Bool
+ Game.Hanabi: data Result
+ Game.Hanabi: instance GHC.Classes.Eq Game.Hanabi.EndGame
+ Game.Hanabi: instance GHC.Classes.Eq Game.Hanabi.GameSpec
+ Game.Hanabi: instance GHC.Classes.Eq Game.Hanabi.Move
+ Game.Hanabi: instance GHC.Classes.Eq Game.Hanabi.PrivateView
+ Game.Hanabi: instance GHC.Classes.Eq Game.Hanabi.PublicInfo
+ Game.Hanabi: instance GHC.Classes.Eq Game.Hanabi.Result
+ Game.Hanabi: instance GHC.Classes.Eq Game.Hanabi.Rule
+ Game.Hanabi: instance GHC.Classes.Eq Game.Hanabi.State
+ Game.Hanabi: instance GHC.Classes.Eq Game.Hanabi.Verbosity
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.Card
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.Color
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.EndGame
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.GameSpec
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.Move
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.Number
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.PrivateView
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.PublicInfo
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.Result
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.Rule
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.State
+ Game.Hanabi: instance GHC.Generics.Generic Game.Hanabi.Verbosity
+ Game.Hanabi: isPlayable :: PublicInfo -> Card -> Bool
+ Game.Hanabi: ithPlayerFromTheLast :: (Eq a, Num a, Show a) => a -> a -> [Char]
+ Game.Hanabi: prettyPI :: PublicInfo -> [Char]
+ Game.Hanabi: recentEvents :: Show a => (Int -> Int -> [Char]) -> [PrivateView] -> [a] -> String
+ Game.Hanabi: stdio :: Verbose Blind
+ Game.Hanabi: view :: State -> PrivateView
- Game.Hanabi: V :: Bool -> Bool -> Bool -> Verbosity
+ Game.Hanabi: V :: Bool -> Bool -> Bool -> Bool -> Verbosity
- Game.Hanabi: prettyEndGame :: (Show a1, Show a2) => (a2, [State], [a1]) -> String
+ Game.Hanabi: prettyEndGame :: (EndGame, [State], [Move]) -> String
Files
- ChangeLog.md +6/−0
- Game/Hanabi.hs +43/−31
- hanabi-dealer.cabal +1/−1
ChangeLog.md view
@@ -1,5 +1,11 @@ # Revision history for hanabi-dealer +## 0.2.1.0 -- 2020-01-04++* introduce isPlayable++* fix several misfeatures+ ## 0.2.0.0 -- 2019-12-20 * define the Verbosity option
Game/Hanabi.hs view
@@ -1,22 +1,22 @@-{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, Safe #-}+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, Safe, DeriveGeneric #-} module Game.Hanabi( -- * Functions for Dealing Games main, selfplay, start, createGame, run, prettyEndGame, isMoveValid, checkEndGame, help, -- * Datatypes -- ** The Class of Strategies- Strategies, Strategy(..), Verbose(..), STDIO, Blind, ViaHandles(..), Verbosity(..), verbose,+ Strategies, Strategy(..), Verbose(..), STDIO, stdio, Blind, ViaHandles(..), Verbosity(..), verbose, -- ** The Game Specification GameSpec(..), defaultGS, Rule(..), defaultRule, -- ** The Game State and Interaction History- Move(..), Index, State(..), PrivateView(..), PublicInfo(..), EndGame(..),+ Move(..), Index, State(..), PrivateView(..), PublicInfo(..), Result(..), EndGame(..), -- ** The Cards Card(..), Color(..), Number(..), cardToInt, intToCard, readsColorChar, readsNumberChar, -- * Utilities -- ** Hints- isCritical, isUseless, bestPossibleRank,+ isCritical, isUseless, bestPossibleRank, isPlayable, -- ** Minor ones- what'sUp, what'sUp1, ithPlayer) where+ what'sUp, what'sUp1, ithPlayer, recentEvents, prettyPI, ithPlayerFromTheLast, view) where -- module Hanabi where import qualified Data.IntMap as IM import System.Random@@ -26,11 +26,15 @@ import Data.List(isPrefixOf, group) import System.IO -data Number = Empty | K1 | K2 | K3 | K4 | K5 deriving (Eq, Ord, Show, Read, Enum, Bounded)+import GHC.Generics hiding (K1)++data Number = Empty | K1 | K2 | K3 | K4 | K5 deriving (Eq, Ord, Show, Read, Enum, Bounded, Generic) data Color = White | Yellow | Red | Green | Blue | Multicolor- deriving (Eq, Show, Read, Enum)+ deriving (Eq, Show, Read, Enum, Generic) readsColorChar :: ReadS Color-readsColorChar (c:str) = case lookup (toUpper c) [(head $ show i, i) | i <- [White, Yellow, Red, Green, Blue]] of+readsColorChar (c:str)+ | isSpace c = readsColorChar str+ | otherwise = case lookup (toUpper c) [(head $ show i, i) | i <- [White, Yellow, Red, Green, Blue]] of Nothing -> [] Just i -> [(i, str)] readsColorChar [] = []@@ -38,7 +42,7 @@ readsNumberChar ('0':rest) = [(Empty,rest)] readsNumberChar str = reads ('K':str) -data Card = C {color :: Color, number :: Number} deriving Eq+data Card = C {color :: Color, number :: Number} deriving (Eq, Generic) instance Show Card where showsPrec _ (C color number) = (head (show color) :) . (show (fromEnum number) ++) instance Read Card where@@ -50,17 +54,19 @@ data Move = Drop {index::Index} -- ^ drop the card (0-origin) | Play {index::Index} -- ^ play the card (0-origin) | Hint Int (Either Color Number) -- ^ give hint to the ith next player+ deriving (Eq, Generic) instance Show Move where showsPrec _ (Drop i) = ("Drop"++) . shows i showsPrec _ (Play i) = ("Play"++) . shows i- showsPrec _ (Hint i eith) = ("Hint"++) . shows i . (either shows (\k -> tail . shows k) eith)+ showsPrec _ (Hint i eith) = ("Hint"++) . shows i . (either (\i -> (take 1 (show i) ++)) (\k -> tail . shows k) eith) instance Read Move where readsPrec _ str- = let (cmd,other) = span (not.isSpace) str+ = let (cmd,other) = span (not.isSpace) str'+ str' = dropWhile isSpace str in case span (not . (`elem` "dDpP")) cmd of (tk, d:dr) | all (not.isAlphaNum) tkdr && null (drop 1 $ group tkdr) -> [((if toLower d == 'd' then Drop else Play) $ length tk, other)] where tkdr = tk++dr- _ -> case span isAlpha str of+ _ -> case span isAlpha str' of (kw, xs) | kwl `isPrefixOf` "hint" -> parseHint xs -- Since kwl can be "", "11" parses as "Hint11". | kwl `isPrefixOf` "drop" -> [(Drop i, rest) | (i, rest) <- reads xs] | kwl `isPrefixOf` "play" -> [(Play i, rest) | (i, rest) <- reads xs]@@ -94,17 +100,17 @@ -- -- See also the definition of 'checkEndGame'. data Rule = R { numBlackTokens :: Int -- ^ E.g., if this is 3, the third failure ends the game with failure.- , funPlayerHand :: [Int] -- ^ memoized function taking the number of players; the default is [5,5,4,4]++repeat 4+ , funPlayerHand :: [Int] -- ^ memoized function taking the number of players; the default is [5,5,4,4,4,4,4,4,4,4,4,4,4,4] , numColors :: Int -- ^ number of colors. 5 for the normal rule, and 6 for Variant 1-3 of the rule book. , prolong :: Bool -- ^ continue even after a round after the pile is exhausted. @True@ for Variant 4 of the rule book. , numMulticolors :: [Int] -- ^ number of each of multicolor cards. @[3,2,2,2,1]@ for Variant 1 (and Variant 3?), and @[1,1,1,1,1]@ for Variant 2. -- x , multicolor :: Bool -- ^ multicolor play, or Variant 3- } deriving (Show, Read)+ } deriving (Show, Read, Eq, Generic) -- | @defaultRule@ is the normal rule from the rule book of the original card game Hanabi. defaultRule = R { numBlackTokens = 3- , funPlayerHand = [5,5]++repeat 4+ , funPlayerHand = [5,5]++take 12 (repeat 4) , numColors = 5 , prolong = False , numMulticolors = replicate 5 0@@ -114,7 +120,7 @@ initialPileNum gs = sum (take (numColors $ rule gs) $ [10,10,10,10,10]++[sum (numMulticolors $ rule gs)]) - numPlayerHand gs * numPlayers gs numPlayerHand gs = funPlayerHand (rule gs) !! (numPlayers gs - 2)-data GameSpec = GS {numPlayers :: Int, rule :: Rule} deriving (Read, Show)+data GameSpec = GS {numPlayers :: Int, rule :: Rule} deriving (Read, Show, Eq, Generic) -- | State consists of all the information of the current game state, including public info, private info, and the hidden deck. data State = St { publicState :: PublicInfo@@ -122,7 +128,7 @@ , hands :: [[Card]] -- ^ partly invisible list of each player's hand. -- In the current implementation (arguably), this represents [current player's hand, next player's hand, second next player's hand, ...] -- and this is rotated every turn.- } deriving (Read, Show)+ } deriving (Read, Show, Eq, Generic) -- | PublicInfo is the info that is available to all players. data PublicInfo = PI { gameSpec :: GameSpec@@ -148,7 +154,7 @@ -- Negative hints should also be implemented, but they should be kept separate from givenHints, -- in order to guess the behavior of algorithms that do not use such information. , result :: Result -- ^ The result of the last move. This info may be separated from 'PublicInfo' in future.- } deriving (Read, Show)+ } deriving (Read, Show, Eq, Generic) @@ -170,12 +176,13 @@ isCritical pub card = not (isUseless pub card) && succ (discarded pub IM.! cardToInt card) == (numCards (gameSpec pub) (color card) !! (pred $ fromEnum $ number card)) -+isPlayable :: PublicInfo -> Card -> Bool+isPlayable pub card = pred (number card) == played pub IM.! fromEnum (color card) -- | 'Result' is the result of the last move. data Result = None -- ^ Hinted or at the beginning of the game- | Discard Card | Success Card | Fail Card deriving (Read, Show)+ | Discard Card | Success Card | Fail Card deriving (Read, Show, Eq, Generic) -- The view history [PrivateView] records the memory of what has been visible `as is'. That is, the info of the cards in the history is not updated by revealing them. -- I guess, sometimes, ignorance of other players might also be an important knowledge.@@ -189,7 +196,7 @@ -- and the view history @[PrivateView]@ must be from the same player's viewpoint (as the matter of course). , invisibleBag :: IM.IntMap Int -- ^ @'Card' -> Int@. This represents the bag of unknown cards (which are either in the pile or in the player's hand). -- This can be computed from publicView and handsSV.- } deriving (Read, Show)+ } deriving (Read, Show, Eq, Generic) prettyPV :: Verbosity -> PrivateView -> String prettyPV v pv@PV{publicView=pub} = prettyPI pub ++ "\nMy hand:\n" ++ concat (replicate (length myHand) " __") ++ "\n"@@ -200,13 +207,16 @@ ++ concat (zipWith3 (prettyHand v pub (ithPlayer $ numPlayers $ gameSpec pub)) [1..] (handsPV pv) $ tail $ givenHints pub) where myHand = head (givenHints pub) prettySt ithP st@St{publicState=pub} = prettyPI pub ++ concat (zipWith3 (prettyHand verbose pub (ithP $ numPlayers $ gameSpec pub)) [0..] (hands st) $ givenHints pub)-verbose = V{warnCritical=True,markUseless=True,markHints=True}+verbose = V{warnCritical=True,markUseless=True,markPlayable=True,markHints=True} prettyHand :: Verbosity -> PublicInfo -> (Int->String) -> Int -> [Card] -> [(Maybe Color, Maybe Number)] -> String prettyHand v pub ithPnumP i cards hl = "\n\n" ++ ithPnumP i ++ " hand:\n" -- ++ concat (replicate (length cards) " __") ++ " \n"- ++ concat [ if warnCritical v && tup==(Nothing,Nothing) && isCritical pub card then " !!"- else if markUseless v && isUseless pub card then " .."- else " __"+ ++ concat [ if markUseless v && isUseless pub card then " .."+ else case (warnCritical v && tup==(Nothing,Nothing) && isCritical pub card, markPlayable v && isPlayable pub card) of+ (True, True) -> " !^"+ (True, False) -> " !!"+ (False,True) -> " _^"+ (False,False) -> " __" | (card, tup) <- zip cards hl ] ++"\n" ++ concat [ '|':show card | card <- cards ] ++"|\n" ++ (if markHints v then showHintLine hl else "")@@ -216,10 +226,11 @@ showHintLine hl = '|' : concat [ maybe ' ' (head . show) mc : maybe ' ' (head . show . fromEnum) mk : "|" | (mc,mk) <- hl] ++ "\n" -- | 'Verbosity' is the set of options used by verbose 'Strategy's-data Verbosity = V { warnCritical :: Bool -- ^ mark unhinted critical cards with "!!"+data Verbosity = V { warnCritical :: Bool -- ^ mark unhinted critical cards with "!!" ("!^" if it is playable and markPlayable==True.) , markUseless :: Bool -- ^ mark useless cards with ".."+ , markPlayable :: Bool -- ^ mark playable cards with "_^". ("!^" if it is unhinted critical and warnCritical==True.) , markHints :: Bool -- ^ mark hints- } deriving (Read, Show)+ } deriving (Read, Show, Eq, Generic) prettyPI pub@@ -258,6 +269,7 @@ putStrLn $ prettyEndGame finalSituation -- | 'prettyEndGame' can be used to pretty print the final situation.+prettyEndGame :: (EndGame, [State], [Move]) -> String prettyEndGame (eg,sts@(st:_),mvs) = unlines $ recentEvents ithPlayerFromTheLast (map view sts) mvs : replicate 80 '!' :@@ -359,7 +371,7 @@ return $ if name == "Blind" then "STDIO" else "Verbose " ++ name move views@(v:_) moves (Verbose p verb) = let alg = move views moves p in do name <- strategyName (fmap (\a -> Verbose (snd a) verb) alg)- liftIO $ putStrLn $ what'sUp verb name views moves+ liftIO $ putStrLn $ what'sUp verb name views moves ++ "Your turn.\n" (mv,p') <- alg -- liftIO $ putStrLn $ "Move is " ++ show mv -- This is redundant because of echo back. return (mv, Verbose p' verb)@@ -370,7 +382,7 @@ recentEvents ithPlayer views moves ++ '\n' : replicate 20 '-' ++ '\n' : "Algorithm: " ++ name ++ '\n' :- prettyPV verb v ++ '\n' : "Your turn.\n"+ prettyPV verb v ++ "\n" what'sUp1 verb v m = replicate 20 '-' ++ '\n' : showTrial (const "") undefined v m ++ '\n' : replicate 20 '-' ++ '\n' :@@ -403,7 +415,7 @@ data ViaHandles = VH {hin :: Handle, hout :: Handle, verbVH :: Verbosity} instance (MonadIO m) => Strategy ViaHandles m where strategyName p = return "via handles"- move views@(v:_) moves vh = liftIO $ do hPutStrLn (hout vh) $ what'sUp (verbVH vh) "via handles" views moves+ move views@(v:_) moves vh = liftIO $ do hPutStrLn (hout vh) $ what'sUp (verbVH vh) "via handles" views moves ++ "Your turn.\n" mov <- repeatReadingAMoveUntilSuccess (hin vh) (hout vh) v return (mov, vh) @@ -520,7 +532,7 @@ -- | 'EndGame' represents the game score, along with the info of how the game ended. -- It is not just @Int@ in order to distinguish 'Failure' (disaster / no life) from @'Soso' 0@ (not playing any card), though @'Soso' 0@ does not look more attractive than 'Failure'.-data EndGame = Failure | Soso Int | Perfect deriving (Show,Read)+data EndGame = Failure | Soso Int | Perfect deriving (Show,Read,Eq,Generic) checkEndGame :: PublicInfo -> Maybe EndGame checkEndGame pub | lives pub == 0 = Just Failure
hanabi-dealer.cabal view
@@ -10,7 +10,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 0.2.0.0+version: 0.2.1.0 -- A short (one-line) description of the package. synopsis: Hanabi card game