hanabi-dealer 0.5.0.0 → 0.6.0.0
raw patch · 8 files changed
+200/−61 lines, 8 filesdep ~basedep ~timenew-component:exe:hanabibnew-component:exe:hanabifnew-component:exe:hanabiqPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, time
API changes (from Hackage documentation)
+ Game.Hanabi: peek :: Peeker IO
+ Game.Hanabi: prettySt :: (Int -> Int -> String) -> State -> [Char]
+ Game.Hanabi: type Peeker m = State -> m ()
- Game.Hanabi: run :: (Monad m, Strategies ps m) => [State] -> [Move] -> ps -> m ((EndGame, [State], [Move]), ps)
+ Game.Hanabi: run :: (Monad m, Strategies ps m) => [Peeker m] -> [State] -> [Move] -> ps -> m ((EndGame, [State], [Move]), ps)
- Game.Hanabi: start :: (RandomGen g, Monad m, Strategies ps m) => GameSpec -> ps -> g -> m (((EndGame, [State], [Move]), ps), g)
+ Game.Hanabi: start :: (RandomGen g, Monad m, Strategies ps m) => GameSpec -> [Peeker m] -> ps -> g -> m (((EndGame, [State], [Move]), ps), g)
Files
- ChangeLog.md +8/−0
- Game/Hanabi.hs +15/−8
- Game/Hanabi/Backend.lhs +47/−24
- Game/Hanabi/Client.hs +54/−17
- Game/Hanabi/Msg.hs +2/−1
- Game/Hanabi/Strategies/SimpleStrategy.hs +2/−2
- all.hs +34/−0
- hanabi-dealer.cabal +38/−9
ChangeLog.md view
@@ -1,5 +1,13 @@ # Revision history for hanabi-dealer + ## 0.6.0.0 -- 2020-02-08++ * introduce the observation (or audience) mode++ * rename the command names from server and client to hanabib and hanabif respectively.++ * Flag jsaddle builds hanabiq that is quickly-built executable including the functionalities of both hanabib and hanabif (but it is not stable.)+ ## 0.5.0.0 -- 2020-02-05 * implement possibility marks which represent what colors and what numbers are possible for each card
Game/Hanabi.hs view
@@ -6,6 +6,8 @@ -- * Datatypes -- ** The Class of Strategies Strategies, Strategy(..), StrategyDict(..), mkSD, DynamicStrategy, mkDS, mkDS', Verbose(..), STDIO, stdio, Blind, ViaHandles(..), Verbosity(..), verbose,+ -- ** Audience+ Peeker, peek, -- ** The Game Specification GameSpec(..), defaultGS, Rule(..), defaultRule, isRuleValid, colors, -- ** The Game State and Interaction History@@ -17,7 +19,7 @@ isCritical, isUseless, bestPossibleRank, achievedRank, isPlayable, isHinted, isMoreObviouslyUseless, isObviouslyUseless, isDefinitelyUseless, isMoreObviouslyPlayable, isObviouslyPlayable, isDefinitelyPlayable, obviousChopss, definiteChopss, isDoubleDrop, -- ** Minor ones- what'sUp, what'sUp1, ithPlayer, recentEvents, prettyPI, ithPlayerFromTheLast, view, replaceNth, shuffle, showPossibilities, showColorPossibilities, showNumberPossibilities) where+ what'sUp, what'sUp1, ithPlayer, recentEvents, prettyPI, prettySt, ithPlayerFromTheLast, view, replaceNth, shuffle, showPossibilities, showColorPossibilities, showNumberPossibilities) where -- module Hanabi where import qualified Data.IntMap as IM import System.Random@@ -414,7 +416,7 @@ -- (where 1<n<10) starts selfplay with youselves:D selfplay gs = do g <- newStdGen- ((finalSituation,_),_) <- start gs [stdio] g+ ((finalSituation,_),_) <- start gs [] [stdio] g putStrLn $ prettyEndGame finalSituation -- | 'prettyEndGame' can be used to pretty print the final situation.@@ -430,15 +432,20 @@ len2 =len `div` 2 in reverse (drop len2 ys) ++ xs ++ drop (len - len2) ys +type Peeker m = State -> m ()+peek :: Peeker IO+peek = putStrLn . prettySt ithPlayerFromTheLast+ -- | 'start' creates and runs a game. This is just the composition of 'createGame' and 'run'. start :: (RandomGen g, Monad m, Strategies ps m) =>- GameSpec -> ps -> g -> m (((EndGame, [State], [Move]), ps), g)-start gs players gen = let+ GameSpec -> [Peeker m] -> ps -> g -> m (((EndGame, [State], [Move]), ps), g)+start gs audience players gen = let (st, g) = createGame gs gen- in fmap (\e -> (e,g)) $ run [st] [] players-run :: (Monad m, Strategies ps m) => [State] -> [Move] -> ps -> m ((EndGame, [State], [Move]), ps)-run states moves players = do ((mbeg, sts, mvs), ps) <- runARound (\sts@(st:_) mvs -> let myOffset = turn (publicState st) in broadcast (zipWith rotate [-myOffset, 1-myOffset ..] sts) mvs players (myOffset `mod` numPlayers (gameSpec $ publicState st)) >> return ()) states moves players- case mbeg of Nothing -> run sts mvs ps+ in fmap (\e -> (e,g)) $ run audience [st] [] players+run :: (Monad m, Strategies ps m) => [Peeker m] -> [State] -> [Move] -> ps -> m ((EndGame, [State], [Move]), ps)+run audience states moves players = do+ ((mbeg, sts, mvs), ps) <- runARound (\sts@(st:_) mvs -> let myOffset = turn (publicState st) in mapM_ ($ st) audience >> broadcast (zipWith rotate [-myOffset, 1-myOffset ..] sts) mvs players (myOffset `mod` numPlayers (gameSpec $ publicState st)) >> return ()) states moves players+ case mbeg of Nothing -> run audience sts mvs ps Just eg -> return ((eg, sts, mvs), ps) -- | The 'Strategy' class is exactly the interface that
Game/Hanabi/Backend.lhs view
@@ -4,7 +4,11 @@ -- -- x #define SNAP-module Game.Hanabi.Backend(server, Options(..), defaultOptions, module Game.Hanabi) where+module Game.Hanabi.Backend(server,+#ifdef WARP+ hanabiApp,+#endif+ Options(..), defaultOptions, module Game.Hanabi) where import Game.Hanabi hiding (main) import Game.Hanabi.Msg@@ -50,7 +54,7 @@ # ifdef WARP import Network.Wai.Handler.WebSockets import Network.Wai.Handler.Warp as Warp-import Network.Wai(responseLBS)+import Network.Wai(Application, responseLBS) import Network.HTTP.Types(status400) # endif #endif@@ -95,24 +99,25 @@ hPutStrLn stderr $ "hanabi-dealer server " ++ version options beginCT <- getCurrentTime hPutStrLn stderr ("started at " ++ show beginCT)-#ifdef TFRANDOM- g <- newTFGen+#ifdef WARP+ app <- hanabiApp options $ \_ resp -> resp $ responseLBS status400 [] "Not a WebSocket request."+ Warp.run (port options) app #else- g <- newStdGen-#endif-- mv <- newMVar (IntMap.empty::IntMap.IntMap ([MVar ViaWebSocket], MVar (Maybe (EndGame,[State],[Move]))))- let params = Params{gen = g, mvTIDToMVH = mv, versionString = version options, gsConstructorMap = Map.fromList $ strategies options, conn = error "Game.Hanabi.Backend.server: should not happen"}-#ifdef SNAP+ params <- mkParams options+# ifdef SNAP httpServe (setPort (port options) defaultConfig) $ runWebSocketsSnap $ loop params-#else-# ifdef WARP- Warp.run (port options) $ websocketsOr defaultConnectionOptions (loop params) $ \_ resp -> resp $ responseLBS status400 [] "Not a WebSocket request." # else runServer "127.0.0.1" (port options) $ loop params # endif #endif +#ifdef WARP+hanabiApp :: Options -> Application -> IO Application+hanabiApp options fallbackApp = do+ params <- mkParams options+ return $ websocketsOr defaultConnectionOptions (loop params) fallbackApp+#endif+ data Params g = Params{gen :: g, mvTIDToMVH :: MVar (IntMap.IntMap ([MVar ViaWebSocket], MVar (Maybe (EndGame,[State],[Move])))), conn :: Connection,@@ -120,6 +125,18 @@ gsConstructorMap :: Map.Map String (IO (DynamicStrategy IO)) } +#ifdef TFRANDOM+mkParams :: Options -> IO (Params TFGen)+mkParams options = do+ g <- newTFGen+#else+mkParams :: Options -> IO (Params StdGen)+mkParams options = do+ g <- newStdGen+#endif+ mv <- newMVar (IntMap.empty::IntMap.IntMap ([MVar ViaWebSocket], MVar (Maybe (EndGame,[State],[Move]))))+ return Params{gen = g, mvTIDToMVH = mv, versionString = version options, gsConstructorMap = Map.fromList $ strategies options, conn = error "Game.Hanabi.Backend.server: should not happen"}+ -- Well, this is not a loop any longer.... loop :: RandomGen g => Params g -> PendingConnection -> IO ()@@ -136,7 +153,7 @@ # endif let p = params{gen = g, conn = c} #endif- withPingThread c 30 (return ()) $ fmap (const ()) $ answerHIO p+ withPingThread c 25 (return ()) $ fmap (const ()) $ answerHIO p endecodeX Nothing = endecode@@ -172,6 +189,8 @@ observe (v:_) (m:_) vh = liftIO $ sendTextData (connection vh) $ endecodeX (verbVWS vh) $ WhatsUp1 v m +watch conn mbVerb st = sendTextData conn $ endecodeX mbVerb $ Watch st+ answerHIO :: RandomGen g => Params g -> IO () answerHIO params = do available Nothing (mvTIDToMVH params) (conn params)@@ -200,13 +219,13 @@ interpret mbVerb inp params = let sender :: String -> IO () sender = sendTextData (conn params) . endecodeX mbVerb . Str- createR creater args = case reads args of [(rule,rest)] | isRuleValid rule -> create creater rule rest- _ -> create creater defaultRule args- create :: Maybe Int -> Rule -> String -> IO ()- create from rule args =+ createR observe creater args = case reads args of [(rule,rest)] | isRuleValid rule -> create observe creater rule rest+ _ -> create observe creater defaultRule args+ create :: Bool -> Maybe Int -> Rule -> String -> IO ()+ create observe from rule args = case wordsBy (==',') args of is | numAllies > 0 -> if numAllies >= 9- then sender "Too many teemmates!\n"+ then sender "Too many teammates!\n" else case dropWhile (\s -> isWS s || s `Map.member` gsConstructorMap params) is of alg:_ -> sender $ "Algorithm " ++ shows (maximum is) " not implemented yet.\n" [] -> do@@ -224,11 +243,13 @@ constructor _ algIx = fromJust $ Map.lookup algIx $ gsConstructorMap params ixSs <- sequence $ zipWith constructor [0..] is sender "starting the game\n"- let playerList = mkDS "via WebSocket" (VWS (conn params) mbVerb sendFullHistoryInFact) : ixSs+ let playerList | observe = ixSs+ |otherwise= mkDS "via WebSocket" (VWS (conn params) mbVerb sendFullHistoryInFact) : ixSs (playOrder,g) = case from of Just n -> (dr++tk, gen params) where (tk,dr) = splitAt n playerList Nothing -> shuffle playerList $ gen params- eithFinalSituation <- try $ start (GS (succ numAllies) rule) playOrder g+ eithFinalSituation <- try $ if observe then start (GS numAllies rule) [watch (conn params) mbVerb] playOrder g+ else start (GS (succ numAllies) rule) [] playOrder g finalSituation <- case eithFinalSituation of Left e -> do hPutStrLn stderr $ displayException (e::SomeException) return Nothing@@ -240,10 +261,12 @@ _ -> sender "The arguments of the `create' command could not be parsed." in case lex inp of [("version", _)] -> sender $ "hanabi-dealer server " ++ versionString params- [("create", args)] -> createR (Just 0) args- [("from", args)] -> case reads args of [(n, ars)] -> createR (Just n) ars+ [("create", args)] -> createR False (Just 0) args+ [("from", args)] -> case reads args of [(n, ars)] -> createR False (Just n) ars _ -> sender "Parse error. The player number expected."- [("shuffle",args)] -> createR Nothing args+ [("observe",args)] -> case reads args of [(n, ars)] -> createR True (Just n) ars+ _ -> createR True Nothing args+ [("shuffle",args)] -> createR False Nothing args [("available", s)] | all isSpace s -> when (isJust mbVerb) $ available mbVerb (mvTIDToMVH params) (conn params) [("attend", arg)] -> case reads arg of [(gid, s)] | all isSpace s -> do tidToMVH <- readMVar $ mvTIDToMVH params
Game/Hanabi/Client.hs view
@@ -3,7 +3,11 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ExtendedDefaultRules #-} {-# LANGUAGE CPP #-}-module Game.Hanabi.Client(client, Game.Hanabi.Msg.Options(..), Game.Hanabi.Msg.defaultOptions, mkDS) where+module Game.Hanabi.Client(client,+#if !defined ghcjs_HOST_OS && !defined IOS+ clientApp,+#endif+ Game.Hanabi.Msg.Options(..), Game.Hanabi.Msg.defaultOptions, mkDS) where import Game.Hanabi hiding (main, rule) import qualified Game.Hanabi(rule)@@ -22,24 +26,45 @@ import Miso.String (MisoString) import qualified Miso.String as S -#ifdef IOS+#ifdef ghcjs_HOST_OS+client :: Game.Hanabi.Msg.Options -> IO ()+client options = clientJSM options+#else+# ifdef IOS import Language.Javascript.JSaddle.WKWebView as JSaddle -runApp :: JSM () -> IO ()-runApp = JSaddle.run-#else-import Language.Javascript.JSaddle.Warp as JSaddle+client :: Game.Hanabi.Msg.Options -> IO ()+client options = JSaddle.run $ clientJSM options+# else+import Language.Javascript.JSaddle.WebSockets+import Network.Wai.Handler.Warp+import Network.Wai+import Network.WebSockets(defaultConnectionOptions) -runApp :: JSM () -> IO ()-runApp = JSaddle.run 8080 -- 8720-#endif+{- This doesn't work.+client :: Game.Hanabi.Msg.Options -> IO ()+client options = runSettings (setPort 8080 (setTimeout 3600 defaultSettings)) $ clientApp options -- Maybe the port number should be taken from options, and it should be correctly set.+clientApp :: Game.Hanabi.Msg.Options -> Application+clientApp options request respond = do+ app <- jsmToApp $ clientJSM options+ app request respond+-} +client :: Game.Hanabi.Msg.Options -> IO ()+client options = runSettings (setPort 8080 (setTimeout 3600 defaultSettings)) =<< clientApp options -- Maybe the port number should be taken from options, and it should be correctly set.+clientApp :: Game.Hanabi.Msg.Options -> IO Application+clientApp = jsmToApp . clientJSM++jsmToApp :: JSM () -> IO Application+jsmToApp f = jsaddleOr defaultConnectionOptions (f >> syncPoint) jsaddleApp+# endif+#endif -- Miso's implementation of WebSockets uses global IORef. -- https://github.com/dmjio/miso/blob/master/frontend-src/Miso/Subscription/WebSocket.hs -client :: Game.Hanabi.Msg.Options -> IO ()-client options = runApp $ startApp App{- model = Model{tboxval = Message "available", players = ["via WebSocket"], from = Just 0, rule = defaultRule, received = [], fullHistory = False, showVerbosity = False, verbosity = verbose},+clientJSM :: Game.Hanabi.Msg.Options -> JSM ()+clientJSM options = startApp App{+ model = Model{tboxval = Message "available", players = ["via WebSocket"], from = Just 0, rule = defaultRule, received = [], fullHistory = False, showVerbosity = False, verbosity = verbose, play = True}, update = updateModel, view = appView strNames $ version options, subs = [ websocketSub uri protocols HandleWebSocket ],@@ -77,6 +102,8 @@ updateModel (UpdateVerbosity v) model = noEff model{verbosity = v} updateModel Toggle model = noEff model{fullHistory = not $ fullHistory model} updateModel ToggleVerbosity model = noEff model{showVerbosity = not $ showVerbosity model}+updateModel TogglePlay model | play model && length (players model) < 2 = noEff model{play = False, players = "via WebSocket" : players model}+ | otherwise = noEff model{play = not $ play model} #ifdef DEBUG updateModel (HandleWebSocket act) model = noEff model{received = Str (show act) : received model } #endif@@ -100,6 +127,7 @@ | UpdateVerbosity Verbosity | Toggle | ToggleVerbosity+ | TogglePlay | Id data Model = Model {@@ -111,6 +139,7 @@ , fullHistory :: Bool , showVerbosity :: Bool , verbosity :: Verbosity+ , play :: Bool } deriving (Show, Eq) lenShownHistory = 10@@ -187,6 +216,7 @@ renderMsg _ verb _ (WhatsUp1 p m) = renderWhatsUp1 verb p m renderMsg _ _ _ (PrettyEndGame Nothing) = pre_ [] [ text $ S.pack $ prettyMbEndGame Nothing] renderMsg _ verb _ (PrettyEndGame (Just tup)) = renderEndGame verb tup -- pre_ [] [ text $ S.pack $ prettyEndGame tup]+renderMsg _ verb _ (Watch st) = renderSt verb ithPlayerFromTheLast st renderMsg strategies _ mdl (PrettyAvailable games) = div_ [style_ $ M.fromList [("overflow","auto")]] [ hr_ [],@@ -203,9 +233,10 @@ label_ [for_ "shuffle"] [text "shuffle on startup"] ] : -- table_ [solid] $- [ tr_ [] [td_ [solid] [x], td_ [] [input_ [type_ "radio", id_ iD, onClick (From $ Just n), checked_ $ from mdl == Just n],- label_ [for_ iD] [text "Turn 0"]]]- | (n,x) <- zip [0..] $ text "You" : reverse (zipWith (renderPlayer strategies) [0..] (players mdl))+ [ tr_ [] [td_ [solid] [x], td_ [] (if n>=0 then [input_ [type_ "radio", id_ iD, onClick (From $ Just n), checked_ $ from mdl == Just n],+ label_ [for_ iD] [text "Turn 0"]]+ else [])]+ | (n,x) <- zip [if play mdl then 0 else -1 ..] $ you : reverse (zipWith (renderPlayer strategies) [0..] (players mdl)) , let iD = S.pack $ "radio"++show n ] -- x ++ [tr_ [] [td_ [] [], td_ [] [input_ [type_ "radio", id_ "shuffle", onClick (From Nothing), checked_ $ from mdl == Nothing], -- label_ [for_ "shuffle"] [text "shuffle the player list before the game"]]]]@@ -234,10 +265,16 @@ mkTR "funPlayerHand" (\n -> (rule mdl){funPlayerHand=n}) funPlayerHand ], - button_ [onClick $ SendMessage $ Message $ S.pack $ (case from mdl of Just n -> "from " ++ shows n " "- Nothing-> "shuffle ") ++ show (rule mdl) ++ concat (intersperse "," $ map S.unpack $ reverse $ players mdl)] [text $ S.pack "create a game"]+ button_ [onClick $ SendMessage $ Message $ S.pack $ (case from mdl of Just n | play mdl -> "from " ++ shows n " "+ | otherwise -> "observe " ++ shows n " "+ Nothing | play mdl -> "shuffle "+ | otherwise -> "observe ") ++ show (rule mdl) ++ concat (intersperse "," $ map S.unpack $ reverse $ players mdl)] [text $ S.pack "create a game"] ] ]+ where you = span_[][+ input_ [ type_ "checkbox", id_ "you", onClick TogglePlay, checked_ $ play mdl]+ , label_ [for_ "you"] [if play mdl then text "You" else s_ [] [text "You"]]+ ] renderPlayer :: [MisoString] -> Int -> MisoString -> View Action --renderPlayer _ i p = text p renderPlayer strategies i p = dropdown strategies (UpdatePlayer i) p
Game/Hanabi/Msg.hs view
@@ -33,12 +33,13 @@ #endif -- WhatsUp and WhatsUp1 should be minimized after better clients are implemented. 効率上はminimizeすべきだが、テスト目的ではとりあえずこのままの方がやりやすい。-data Msg = Str String | WhatsUp String [PrivateView] [Move] | WhatsUp1 PrivateView Move | PrettyEndGame (Maybe (EndGame, [State], [Move])) | PrettyAvailable [(Int, (Int, Int))] deriving (Show, Read, Eq, Generic)+data Msg = Str String | WhatsUp String [PrivateView] [Move] | WhatsUp1 PrivateView Move | PrettyEndGame (Maybe (EndGame, [State], [Move])) | Watch State | PrettyAvailable [(Int, (Int, Int))] deriving (Show, Read, Eq, Generic) prettyMsg :: Verbosity -> Msg -> String prettyMsg _ (Str xs) = xs prettyMsg verb (WhatsUp name ps ms) = what'sUp verb name ps ms prettyMsg verb (WhatsUp1 p m) = what'sUp1 verb p m prettyMsg _ (PrettyEndGame tup) = prettyMbEndGame tup+prettyMsg _ (Watch st) = prettySt ithPlayerFromTheLast st prettyMsg _ (PrettyAvailable available) = unlines $ map prettyAvailableGame available prettyMbEndGame Nothing = "Game ended abnormally, possibly by connection failure.\n" prettyMbEndGame (Just tup) = prettyEndGame tup
Game/Hanabi/Strategies/SimpleStrategy.hs view
@@ -30,6 +30,6 @@ sndOf3 (_,b,_) = b main = do g <- newStdGen--- ((eg,_),_) <- start defaultGS ([S],[stdio]) g -- Play it with standard I/O (human player).- ((eg,_),_) <- start defaultGS [S,S] g -- Play it with itself.+-- ((eg,_),_) <- start defaultGS [] ([S],[stdio]) g -- Play it with standard I/O (human player).+ ((eg,_),_) <- start defaultGS [peek] [S,S] g -- Play it with itself. putStrLn $ prettyEndGame eg
+ all.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE OverloadedStrings, CPP #-}+import Network.Wai+import Network.HTTP.Types+import Network.Wai.Handler.Warp (run)++import Game.Hanabi.Client(clientApp)+import Game.Hanabi.Backend(hanabiApp, mkDS)+import Game.Hanabi.Msg(defaultOptions, Options(..))++import Game.Hanabi.Strategies.SimpleStrategy hiding (main)++main = do putStrLn "localhost:8080" -- Seemingly other port than 8080 does not work.+ let opt = defaultOptions{version="hanabi-dealer quickbuilt server", strategies=strs}+ capp <- clientApp opt+ bapp <- hanabiApp opt $ \_ resp -> resp $ responseLBS status400 [("Content-Type", "text/plain")] "400 Not a WebSocket request."+ run 8080 $ route capp bapp+route :: Application -> Application -> Application+route capp bapp request respond = case rawPathInfo request of+ "/ws/" -> bapp request respond+ _ -> capp request respond+-- _ -> respond $ responseLBS status404 [("Content-Type", "text/plain")] "404 Not found"++{-+import Game.Hanabi.Client(client)++-- main = client defaultOptions{strategies=strs, version="foo"}+main = do capp <- clientApp defaultOptions{strategies=strs, version="foo"}+ run 8080 capp+-}+-- strs :: [(String, IO (DynamicStrategy IO))]+strs = [+ ("Stupid example strategy", return $ mkDS "Stupid example strategy" $ S)+ -- x , ("Sontakki", return $ mkDS "Sontakki" (Sontakki emptyDefault))+ ]
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.5.0.0+version: 0.6.0.0 -- A short (one-line) description of the package. synopsis: Hanabi card game@@ -73,11 +73,18 @@ Description: Use template-haskell just for obtaining the compilation time. Default: True Flag jsaddle- Description: Build the client with JSaddle even when compiling with GHC.+-- Description: Build the client with JSaddle even when compiling with GHC.+-- (Package miso must have been built with --flags="jsaddle".)+-- Default: False+-- Flag all+ Description: Quickbuild all-in-one server, using JSaddle. (Package miso must have been built with --flags="jsaddle".) Default: False library+ if flag(jsaddle)+ buildable: False+ else -- Modules exported by the library. exposed-modules: Game.Hanabi @@ -97,7 +104,8 @@ -- Base language which the package is written in. default-language: Haskell2010 - if impl(ghcjs) || flag(jsaddle) || flag(server)+ if impl(ghcjs) || flag(server)+-- || flag(jsaddle) exposed-modules: Game.Hanabi.VersionInfo other-modules: Game.Hanabi.Msg -- cpp-options: -DCABAL@@ -120,16 +128,34 @@ if flag(WARP) build-depends: wai-websockets, warp, wai, http-types cpp-options: -DWARP- if impl(ghcjs) || flag(jsaddle)+ if impl(ghcjs)+-- || flag(jsaddle) exposed-modules: Game.Hanabi.Client ghcjs-options: -dedupe -O build-depends: base >=4.12 && <4.13, jsaddle-warp >=0.9, aeson, miso, time >=1.6 if flag(official) cpp-options: -DURI="ws://133.54.228.39:8720"+-- if flag(jsaddle)+-- build-depends: warp, wai, websockets -executable server+executable hanabiq+ main-is: all.hs+ other-modules: Game.Hanabi, Game.Hanabi.Msg, Game.Hanabi.Backend, Game.Hanabi.Client, Game.Hanabi.Strategies.SimpleStrategy+-- if !flag(all)+ if !flag(jsaddle)+ buildable: False+ else+ ghc-options: -O0 -rtsopts+ cpp-options: -DURI="ws://localhost:8080/ws/" -DWARP+ build-depends: base >=4.12 && <4.13, containers >=0.5, random >=1.1, jsaddle-warp >=0.9, aeson, miso, websockets >=0.12 && <0.13, network >=2.6 && <3.2, hashable >=1.3, time >=1.6 && <1.9, text >=1.2, utf8-string, wai-websockets, warp, wai, http-types+ if flag(TFRANDOM)+ build-depends: tf-random+ cpp-options: -DTFRANDOM+ default-language: Haskell2010++executable hanabib main-is: server.hs- other-modules: Game.Hanabi.Backend, Game.Hanabi.Msg, Game.Hanabi.VersionInfo, Game.Hanabi.Strategies.SimpleStrategy+ other-modules: Game.Hanabi.Strategies.SimpleStrategy if impl(ghcjs) || !flag(server) buildable: False else@@ -151,10 +177,11 @@ build-depends: wai-websockets, warp, wai, http-types cpp-options: -DWARP -executable client+executable hanabif main-is: client.hs- other-modules: Game.Hanabi.Msg, Game.Hanabi.Client, Game.Hanabi.VersionInfo, Game.Hanabi.Strategies.SimpleStrategy- if !impl(ghcjs) && !flag(jsaddle)+ other-modules: Game.Hanabi.Strategies.SimpleStrategy+ if !impl(ghcjs)+-- && !flag(jsaddle) buildable: False else ghcjs-options: -dedupe -O@@ -165,3 +192,5 @@ cpp-options: -DURI="ws://133.54.228.39:8720" if flag(TH) build-depends: template-haskell+-- if flag(jsaddle)+-- build-depends: warp, wai, websockets