{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE LambdaCase #-}
Module : Dragons.Options
Copyright By PowCoder代写 加微信 powcoder
Description : Command-line parsing
Copyright : (c) 2020 The Australian National University
License : AllRightsReserved
This module uses the excellent
library to recognise command-line arguments.
Essentially, each command-line flag is a @’Parser’ a@ for some
and we use the ‘Applicative’ instance to construct a parser for our
‘Options’ structure, which “Dragons.Main” uses to set up the program.
module Dragons.Options
( — * Overview
— $overview
— * Types
, Options(..)
, DisplayUI(..)
, Course(..)
, NetworkMode(..)
— * Entry Point
, parseOptions
import Data.ByteString (ByteString)
import Data.Foldable (asum)
import Data.Functor ((<&>))
import Data.Proxy (Proxy(..))
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8)
import Dragons.Game (DebugFlag(..), GenericAIFunc, MoveSource(..))
import Options.Applicative
import System.Exit (exitFailure)
import System.IO (hPutStrLn, stderr)
import Text.Read (readMaybe)
— | Parse command-line arguments, and either return the parsed config
— or bail out of the program.
parseOptions :: AITable st mv -> IO (Options st mv)
parseOptions ais = do
opts <- execParser . info (helper <*> config ais) $ mconcat
[ fullDesc
, progDesc “COMP1100 Assignment 3”
, header “game – a two-player strategy game for COMP1100 assignment 3”
validateOptions opts
validateOptions opts = case (optPlayer1 opts, optPlayer2 opts) of
(Network _, Network _) ->
hPutStrLn stderr “Cannot have two network players.” *> exitFailure
(Network _, _) -> checkNetworkMode opts
(_, Network _) -> checkNetworkMode opts
_ -> pure ()
checkNetworkMode opts = case optNetworkMode opts of
Just _ -> pure ()
hPutStrLn stderr “Need to provide either the –host\
\ or –connect if using network players.”
exitFailure
— | Type for the list of registered AIs.
type AITable st mv = [(String, GenericAIFunc st mv)]
— | Type representing all config information collected from the command line.
data Options st mv = Options
{ optUI :: DisplayUI — ^ Which UI type to use.
, optMode :: Course — ^ Run the COMP1100 or COMP1130 version of the game?
, optN :: Int — ^ Number of consecutive pieces needed to win the game
, optHeight :: Int — ^ Board Height
, optWidth :: Int — ^ Board Width
, optTimeout :: Double — ^ How long to wait for AI moves?
, optPlayer1 :: MoveSource st mv Proxy — ^ Player 1’s moves.
, optPlayer2 :: MoveSource st mv Proxy — ^ Player 2’s moves.
, optDebugFlags :: [DebugFlag]
, optNetworkMode :: Maybe NetworkMode
— ^ If we’re using the network, are we connecting or hosting?
data DisplayUI = Text | CodeWorld | Json deriving (Eq, Show)
data Course = COMP1100 deriving (Eq, Show)
data NetworkMode = Host Int | Connect ByteString Int deriving (Eq, Show)
config :: AITable st mv -> Parser (Options st mv)
config ais = Options
<$> displayUI
<*> course
<*> height
<*> timeout
<*> player ais “p1”
<*> player ais “p2”
<*> debugFlags
<*> networkMode
course :: Parser Course
course = pure COMP1100
len :: Parser Int
len = option auto $ mconcat
[ long “n”
, metavar “LENGTH”
, help “How many subsequent pieces wins the game”
, showDefault
height :: Parser Int
height = option auto $ mconcat
[ long “height”
, metavar “LENGTH”
, help “Height of the board”
, showDefault
width :: Parser Int
width = option auto $ mconcat
[ long “width”
, metavar “LENGTH”
, help “Width of the board”
, showDefault
timeout :: Parser Double
timeout = option auto $ mconcat
[ long “timeout”
, metavar “DURATION”
, help “How long to wait for AI moves, in decimal seconds”
, value 4.0
, showDefault
displayUI :: Parser DisplayUI
displayUI = option readDisplayUI $ mconcat
[ long “ui”
, metavar “TYPE”
, help “Which UI to run. Valid options: text, codeworld”
, value CodeWorld
, showDefault
readDisplayUI = maybeReader $ \case
“text” -> Just Text
“codeworld” -> Just CodeWorld
“json” -> Just Json
_ -> Nothing
:: forall st mv . AITable st mv
-> Parser (MoveSource st mv Proxy)
player ais argname = option readMoveSource $ mconcat
[ long argname
, metavar “PLAYER”
, help “Who is playing as this player. Valid options: human, ai, ai:AINAME”
readMoveSource = maybeReader $ \case
“human” -> Just Human
“network” -> Just $ Network Proxy
“ai” -> findAI “default”
‘a’:’i’:’:’:name -> findAI name
_ -> Nothing
findAI :: String -> Maybe (MoveSource st mv Proxy)
findAI n = AI n <$> lookup n ais
debugFlags :: Parser [DebugFlag]
debugFlags = many $ asum [debugLookahead]
debugLookahead :: Parser DebugFlag
debugLookahead = flag’ DebugLookahead $ mconcat
[ long “debug-lookahead”
, help “Show how far the AI looks ahead, and what moves it considers.”
networkMode :: Parser (Maybe NetworkMode)
networkMode = asum [ host, connect, pure Nothing ]
host = Just . Host <$> option auto (mconcat
[ long “host”
, metavar “PORT”
, help “What port to listen on.”
connect = option readConnect $ mconcat
[ long “connect”
, metavar “HOST:PORT”
, help “Connect to another running game.”
readConnect = maybeReader $ \s -> case break (== ‘:’) s of
(remoteHost, ‘:’:portStr) -> readMaybe portStr <&> \port ->
Just $ Connect (encodeUtf8 $ T.pack remoteHost) port
_ -> Nothing
程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com