{-# LANGUAGE NoMonomorphismRestriction #-} module Cargo(Cargo, Market, randomCargo, randomMarket, showCargo, showMarket, fromMarket, fromCargo, withdraw, tradeScreen, takeScreen) where import System.Random import Text.Printf import Data.List import Control.Monad import Control.Monad.State as State import qualified Data.Edison.Assoc.StandardMap as M import Graphics.Rendering.OpenGL as OpenGL import Graphics.UI.SDL as SDL import Graphics.Rendering.FTGL as FTGL import Statistics import TextScreen import Space import SDLUtils type Cargo = M.FM String Int type Market = M.FM String (Int, Int) cargonames = sort ["Grain", "Fruit", "Gem stones", "Firearms"] randomCargo :: IO String randomCargo = chooseIO cargonames showCargo :: Cargo -> String showCargo c | M.null c = "No cargo\n" | otherwise = concatMap (\(k, v) -> printf "%-20s-%4d\n" k v) (M.toOrdSeq c) showMarket :: Market -> String showMarket m = title ++ infos where title = printf "%-16s%8s%8s\n" "Good" "Quantity" "Price" infos = concatMap (\(n, (q, p)) -> printf "%-20s%10d%10d\n" n q p) (M.toOrdSeq m) numCargoItems = length cargonames randomMarket :: IO Market randomMarket = do let names = cargonames prices <- replicateM numCargoItems $ randomRIO (5, 15) quantities <- replicateM numCargoItems $ randomRIO (0, 30) return $ M.fromSeq $ zip names (zip quantities prices) fitCargo :: Cargo -> [(String, Int)] fitCargo c = map (\s -> (s, M.lookupWithDefault 0 s c)) cargonames cargoToMarket :: Cargo -> Market cargoToMarket cargo = foldl' (\acc x -> M.insert x (M.lookupWithDefault 0 x cargo, 0) acc) M.empty cargonames fromMarket :: Int -> String -> Market -> Market fromMarket q = toMarket (-q) fromCargo :: Int -> String -> Cargo -> Cargo fromCargo q = toCargo (-q) toMarket :: Int -> String -> Market -> Market toMarket q = M.adjust (\(q', p) -> (q' + q, p)) toCargo :: Int -> String -> Cargo -> Cargo toCargo q n = M.insertWith (+) n q showMarketAndCargo :: Bool -> Market -> Cargo -> String showMarketAndCargo t m c = title ++ infos where title = if t then printf "%-14s%9s%6s%6s\n" "Good" "Quantity" "Price" "Cargo" else printf "%-17s%-12s%-9s\n" "Good" "Captured" "Cargo" infos = if t then concatMap (\((n, (q, p)), (_, q')) -> printf "%-14s%9d%6d%6d\n" n q p q') (zip (M.toOrdSeq m) (fitCargo c)) else concatMap (\((n, (q, _)), (_, q')) -> printf "%-17s%-12d%-9d\n" n q q') (zip (M.toOrdSeq m) (fitCargo c)) type TradeState = (Market, Cargo, Int, Int) type TakeState = (Cargo, Cargo, Int, Int) buy :: Int -> String -> StateT TradeState IO () buy q n = do (market, cargo, cash, holdspace) <- State.get let mval = M.lookupM n market case mval of Nothing -> return () Just (q', p) -> do let totalq = minimum [max 0 q', q, holdspace] let totalp = totalq * p if totalp > cash || totalq == 0 then return () else do State.put (fromMarket totalq n market, toCargo totalq n cargo, subtract totalp cash, holdspace - totalq) sell :: Int -> String -> StateT TradeState IO () sell q n = do (_, cargo, _, _) <- State.get let mval = M.lookupM n cargo case mval of Nothing -> return () Just q' -> buy (negate (min q' q)) n tradeScreen :: String -> Font -> Font -> StateT TradeState IO () tradeScreen = screenGeneric True withdraw :: Int -> String -> StateT TradeState IO () withdraw q n = withStateT (\(m, c, _, hold) -> (m, c, maxBound, hold)) (buy q n) screenGeneric :: Bool -> String -> Font -> Font -> StateT TradeState IO () screenGeneric trade str f1 f2 = do (market, _, _, _) <- State.get let exitb = ((100, 100), (100, 30)) buybuttons = map (\i -> ((550, 440 - 50 * fromIntegral i), (100, 30))) [1..numCargoItems] sellbuttons = map (\i -> ((680, 440 - 50 * fromIntegral i), (100, 30))) [1..numCargoItems] buyactions = map (\(n, (_, _)) -> buy 1 n >> return Nothing) (M.toOrdSeq market) sellactions = map (\(n, (_, _)) -> sell 1 n >> return Nothing) (M.toOrdSeq market) allbuttons = exitb : (buybuttons ++ sellbuttons) allactions = return (Just ()) : (buyactions ++ sellactions) bttoaction = zip allbuttons allactions let handleInput = do events <- liftIO $ pollAllSDLEvents let mbutton = mouseClickInAny height [ButtonLeft] allbuttons events case mbutton of Nothing -> return Nothing Just n -> case lookup n bttoaction of Just act -> act Nothing -> return Nothing loopTextScreen (do (market', cargo, cash, holdspace) <- State.get liftIO $ makeTextScreen (10, 500) [(f1, Color4 1.0 1.0 1.0 1.0, str), (f2, Color4 1.0 1.0 0.0 1.0, showMarketAndCargo trade market' cargo), (f1, Color4 1.0 1.0 1.0 1.0, if trade then "Cash: " ++ show cash else ""), (f1, Color4 1.0 1.0 1.0 1.0, "Hold space: " ++ show holdspace)] (drawButton (Just ("Exit", f1)) exitb >> mapM_ (drawButton (Just (if trade then "Buy" else "Take", f1))) buybuttons >> mapM_ (drawButton (Just (if trade then "Sell" else "Leave", f1))) sellbuttons)) handleInput takeScreen :: String -> Font -> Font -> StateT TakeState IO () takeScreen str f1 f2 = do (stuff, cargo, cash, hold) <- State.get (_, cargo', _, hold') <- liftIO $ execStateT (screenGeneric False str f1 f2) (cargoToMarket stuff, cargo, cash, hold) State.put (stuff, cargo', cash, hold')