<commit_before>module Jakway.Blackjack.Interface.Options
(
Config,
getConfig
) where

import Jakway.Blackjack.AI
import Jakway.Blackjack.Util
import System.Console.GetOpt
import Data.Maybe (fromJust, catMaybes)
import System.Exit (die)

data Flag = Verbose |
            WhichDealer AI |
            NumBasicPlayer Int |
            NumGames Integer |
            TableNameSuffix String
            deriving (Show, Eq)

type Config = (Bool, AI, [AI], Integer, String)

--extract the number of BasicPlayer or return Nothing
getNumBasicPlayer :: Flag -> Maybe Int
getNumBasicPlayer (NumBasicPlayer num) = Just num
getNumBasicPlayer _ = Nothing

getWhichDealer :: Flag -> Maybe AI
getWhichDealer (WhichDealer whichAI) = Just whichAI
getWhichDealer _ = Nothing

getNumGames :: Flag -> Maybe Integer
getNumGames (NumGames num) = Just num
getNumGames _ = Nothing

flagsToConfig :: [Flag] -> Either String Config
flagsToConfig [] = Left "No flags passed."
flagsToConfig flags
                --No dealer!
                | (whichDealer == Nothing) = Left "No dealer AI found, perhaps you passed a player AI by mistake?"
                | (numPlayerAIs == 0) = Left "Must have >0 players!"
                | numGames == Nothing = Left "Specify how many games to run."
                | suffixes == [] = Left "You must specify a table name suffix."
                | otherwise = Right (hasVerbose, (fromJust whichDealer), playerAIs, fromJust numGames, tableNameSuffix)
            --Make sure a player AI hasn't been passed for a dealer AI
            -- TODO: rewrite using filter?
            where whichDealer = case (catMaybes $ map getWhichDealer flags) of [] -> Nothing
                                                                               [x] -> Just x
                  getNum f = length . catMaybes $ map f flags
                  --need at least 1 player besides the dealer
                  numBasicPlayerAIs = let n = catMaybes $ map getNumBasicPlayer flags
                                          in case n of [] -> 0
                                                       _ -> head n
                  numPlayerAIs = numBasicPlayerAIs -- TODO: modify as we add more AI types
                  suffixes = catMaybes $ map (\a -> case a of (TableNameSuffix suff) -> Just suff
                                                              _ -> Nothing) flags
                  numGames = case catMaybes (map getNumGames flags) of [] -> Nothing
                                                                       [x] -> Just x
                  hasVerbose = True `elem` (map (== Verbose) flags)
                  --build the list of player AIs from the passed flags
                  -- TODO: concat other ais as we add more types
                  playerAIs = replicate numBasicPlayerAIs BasicPlayer
                  tableNameSuffix = head suffixes

--flag converters
dealerOpt, ngOpt, nbpOpt, sfOpt :: String -> Flag
dealerOpt = WhichDealer . read
nbpOpt = NumBasicPlayer . read
ngOpt = NumGames . (read :: String -> Integer)
sfOpt = TableNameSuffix


options :: [OptDescr Flag]
options =
    [ Option ['v']     ["verbose"] (NoArg Verbose)       "Be verbose."
    , Option []        ["with-dealer"]  (ReqArg dealerOpt "DealerAI")  "Which dealer AI to use."
    , Option []        ["num-BasicPlayer"]  (ReqArg nbpOpt  "NUM")  "Number of BasicPlayer AI's."
    , Option ['g']     ["num-games"]  (ReqArg ngOpt "NUM") "Number of games to run."
    , Option ['s']     ["tablename-suffix"] (ReqArg sfOpt "SUFFIX") "Table name suffix."
    ]


parseOptions :: [String] -> IO ([Flag], [String])
parseOptions argv = 
    case getOpt Permute options argv of
        (o,n,[]  ) -> return (o,n)
        (_,_,errs) -> ioError (userError (concat errs ++ usageInfo header options))
    where header = "Usage: blackjack-simulator [OPTION...]"


--returns a valid configuration or prints an error and exits
getConfig :: [String] -> IO Config
getConfig argv = parseOptions argv >>= \(flags, _) -> return (flagsToConfig flags) >>=
                                \res -> case res of (Left x) -> die $ "Error processing options: " ++ x
                                                    (Right conf) -> return conf<commit_msg>Added postgresql connection string option, Options.hs typechecks<commit_after>{-# LANGUAGE ScopedTypeVariables #-}
module Jakway.Blackjack.Interface.Options
(
Config,
getConfig
) where

import Jakway.Blackjack.AI
import Jakway.Blackjack.Util
import System.Console.GetOpt
import Data.Maybe (fromJust, catMaybes)
import System.Exit (die)

data Flag = Verbose |
            WhichDealer AI |
            NumBasicPlayer Int |
            NumGames Integer |
            TableNameSuffix String |
            PostgresqlConnectString String
            deriving (Show, Eq)

type Config = (Bool, AI, [AI], Integer, String, Maybe String)

--extract the number of BasicPlayer or return Nothing
getNumBasicPlayer :: Flag -> Maybe Int
getNumBasicPlayer (NumBasicPlayer num) = Just num
getNumBasicPlayer _ = Nothing

getWhichDealer :: Flag -> Maybe AI
getWhichDealer (WhichDealer whichAI) = Just whichAI
getWhichDealer _ = Nothing

getNumGames :: Flag -> Maybe Integer
getNumGames (NumGames num) = Just num
getNumGames _ = Nothing

getPostgresqlConnectString :: Flag -> Maybe String
getPostgresqlConnectString (PostgresqlConnectString str) = Just str
getPostgresqlConnectString _ = Nothing


flagsToConfig :: [Flag] -> Either String Config
flagsToConfig [] = Left "No flags passed."
flagsToConfig flags
                --No dealer!
                | (whichDealer == Nothing) = Left "No dealer AI found, perhaps you passed a player AI by mistake?"
                | (numPlayerAIs == 0) = Left "Must have >0 players!"
                | numGames == Nothing = Left "Specify how many games to run."
                | suffixes == [] = Left "You must specify a table name suffix."
                | otherwise = Right (hasVerbose, (fromJust whichDealer), playerAIs, fromJust numGames, tableNameSuffix, pConnStr)
            --Make sure a player AI hasn't been passed for a dealer AI
            -- TODO: rewrite using filter?
            where whichDealer = case (catMaybes $ map getWhichDealer flags) of [] -> Nothing
                                                                               [x] -> Just x
                  getNum f = length . catMaybes $ map f flags
                  --need at least 1 player besides the dealer
                  numBasicPlayerAIs = let n = catMaybes $ map getNumBasicPlayer flags
                                          in case n of [] -> 0
                                                       _ -> head n
                  numPlayerAIs = numBasicPlayerAIs -- TODO: modify as we add more AI types
                  suffixes = catMaybes $ map (\a -> case a of (TableNameSuffix suff) -> Just suff
                                                              _ -> Nothing) flags
                  numGames = case catMaybes (map getNumGames flags) of [] -> Nothing
                                                                       [x] -> Just x
                  hasVerbose = True `elem` (map (== Verbose) flags)
                  --build the list of player AIs from the passed flags
                  -- TODO: concat other ais as we add more types
                  playerAIs = replicate numBasicPlayerAIs BasicPlayer
                  tableNameSuffix = head suffixes

                  pConnStr :: Maybe String
                  pConnStr = extractFlags (getPostgresqlConnectString) flags


extractFlags :: (Flag -> Maybe b) -> [Flag] -> Maybe b
extractFlags f flags = case (catMaybes $ map f flags) of [] -> Nothing
                                                         [x] -> Just x


--flag converters
dealerOpt, ngOpt, nbpOpt :: String -> Flag
dealerOpt = WhichDealer . read
nbpOpt = NumBasicPlayer . read
ngOpt = NumGames . (read :: String -> Integer)

options :: [OptDescr Flag]
options =
    [ Option ['v']     ["verbose"] (NoArg Verbose)       "Be verbose."
    , Option []        ["with-dealer"]  (ReqArg dealerOpt "DealerAI")  "Which dealer AI to use."
    , Option []        ["num-BasicPlayer"]  (ReqArg nbpOpt  "NUM")  "Number of BasicPlayer AI's."
    , Option ['g']     ["num-games"]  (ReqArg ngOpt "NUM") "Number of games to run."
    , Option ['s']     ["tablename-suffix"] (ReqArg TableNameSuffix "SUFFIX") "Table name suffix."
    , Option []        ["postgres-string"] (ReqArg PostgresqlConnectString "CONNSTR")  "Postgresql connection string (used instead of config file if both are present)"
    ]


parseOptions :: [String] -> IO ([Flag], [String])
parseOptions argv = 
    case getOpt Permute options argv of
        (o,n,[]  ) -> return (o,n)
        (_,_,errs) -> ioError (userError (concat errs ++ usageInfo header options))
    where header = "Usage: blackjack-simulator [OPTION...]"


--returns a valid configuration or prints an error and exits
getConfig :: [String] -> IO Config
getConfig argv = parseOptions argv >>= \(flags, _) -> return (flagsToConfig flags) >>=
                                \res -> case res of (Left x) -> die $ "Error processing options: " ++ x
                                                    (Right conf) -> return conf
{'fraction_non_alphanumeric': 0.0927209705372617, 'fraction_numerical': 0.0008665511265164644, 'mean_word_length': 1.9034591194968553, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 30, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3618683', 'n_tokens_mistral': 2416, 'n_tokens_neox': 2341, 'n_words': 871}