summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Main.hs77
-rw-r--r--src/Sequence/Types.hs101
-rw-r--r--src/Sequence/Utils.hs46
3 files changed, 223 insertions, 1 deletions
diff --git a/src/Main.hs b/src/Main.hs
index e9e1deb..639673c 100644
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -1,2 +1,77 @@
1{-# LANGUAGE ViewPatterns #-}
2
3import Control.Monad
4
5import Control.Lens
6
7import System.Console.Shell
8import System.Console.Shell.ShellMonad
9import System.Console.Shell.Backend.Haskeline
10
11import System.Environment.XDG.BaseDir
12import System.FilePath
13import System.Directory
14
15import Data.Default
16import Data.CaseInsensitive (CI)
17import qualified Data.CaseInsensitive
18
19import Data.Map.Strict (Map)
20import qualified Data.Map.Strict as Map
21
22import Data.List
23import Data.Maybe
24
25import Data.Function
26
27import Control.Monad.State.Strict
28
29import Sequence.Types
30import Sequence.Utils
31
32import Text.Layout.Table
33
1main :: IO () 34main :: IO ()
2main = undefined 35main = do
36 historyFile <- getUserCacheFile "sequence" "history"
37 createDirectoryIfMissing True $ takeDirectory historyFile
38 let
39 description = initialShellDescription
40 { historyFile = Just historyFile
41 , prompt = \st -> return $ maybe "" (++ " ") ((evalState ?? st) . toName . snd <$> (listToMaybe $ view priorityQueue st)) ++ "→ "
42 , beforePrompt = gets stateOutline >>= (\str -> if null str then return () else shellPutStrLn str)
43 , commandStyle = OnlyCommands
44 , shellCommands = [ exitCommand "exit"
45 , helpCommand "help"
46 , cmd "factions" listFactions "List all inhabited factions"
47 , cmd "members" listFaction "List all members of a faction"
48 , cmd "entities" listEntities "List all entities"
49 ]
50 }
51 void $ runShell description haskelineBackend (def :: GameState)
52
53stateOutline :: GameState -> String
54stateOutline st
55 | null pQueue = ""
56 | otherwise = layoutTableToString rowGs (Just ("" : factions, repeat def)) (repeat def) unicodeBoldHeaderS
57 where
58 factions = map (view faction') $ st ^. inhabitedFactions
59 pQueue = st ^. priorityQueue
60 protoRows = groupBy ((==) `on` fst) pQueue
61 faction id = fromJust $ view eFaction <$> Map.lookup id (st ^. gEntities)
62 factionIndex id = fromJust $ elemIndex (view faction' $ faction id) factions
63 rowGs = do
64 rowGroup'@((seqVal', _):_) <- protoRows
65 let
66 rowGroup = map snd rowGroup'
67 factionColumn i = [evalState ?? st $ toName x | x <- rowGroup, factionIndex x == i ]
68 return . colsAllG top $ [show (seqVal' ^. seqVal)] : map factionColumn [0..(length factions - 1)]
69
70listFaction :: Completable Faction -> Sh GameState ()
71listFaction (Completable (flip (set faction') def -> qFaction)) = use gEntities >>= mapM_ (shellPutStrLn <=< toName) . Map.keys . Map.filter ((==) qFaction . view eFaction)
72
73listFactions :: Sh GameState ()
74listFactions = use inhabitedFactions >>= mapM_ (shellPutStrLn . view faction')
75
76listEntities :: Sh GameState ()
77listEntities = use (gEntities . to Map.keys) >>= mapM_ (shellPutStrLn <=< toName)
diff --git a/src/Sequence/Types.hs b/src/Sequence/Types.hs
new file mode 100644
index 0000000..afe1060
--- /dev/null
+++ b/src/Sequence/Types.hs
@@ -0,0 +1,101 @@
1{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances, ViewPatterns, OverloadedStrings, TemplateHaskell, GeneralizedNewtypeDeriving #-}
2
3module Sequence.Types
4 ( GameState(..), gEntities, gEntityNames
5 , Faction, faction, faction'
6 , SeqVal(..), seqVal
7 , Entity(..), eFaction, eSeqVal
8 , EntityName(..), entityName
9 , EntityIdentifier(..), entityId
10 , inhabitedFactions, priorityQueue
11 ) where
12
13import Control.Lens
14
15import System.Console.Shell
16
17import Data.Default
18import Data.CaseInsensitive (CI)
19import qualified Data.CaseInsensitive as CI
20
21import Data.Map.Strict (Map)
22import qualified Data.Map.Strict as Map
23
24import Data.Bimap (Bimap)
25import qualified Data.Bimap as Bimap
26
27import Data.List
28import Data.Maybe
29import Data.Tuple
30import Data.Ord
31
32
33newtype Faction = Faction { _lFaction :: Maybe (CI String) }
34 deriving (Show, Eq, Ord)
35makeLenses ''Faction
36
37instance Default Faction where
38 def = Faction Nothing
39
40unaligned :: CI String
41unaligned = "Unaligned"
42
43faction :: Getter Faction (Maybe (CI String))
44faction = lFaction
45
46faction' :: Lens' Faction String
47faction' = lens (CI.original . fromMaybe unaligned . view faction) (\s a -> s { _lFaction = parseFaction a })
48 where
49 parseFaction str@(CI.mk -> str')
50 | str' == unaligned = Nothing
51 | null str = Nothing
52 | otherwise = Just str'
53
54
55newtype SeqVal = SeqVal { _seqVal :: Integer }
56 deriving (Show, Ord, Eq, Num, Integral, Enum, Real)
57makeLenses ''SeqVal
58
59
60data Entity = Entity
61 { _eFaction :: Faction
62 , _eSeqVal :: Maybe SeqVal
63 }
64 deriving (Show)
65makeLenses ''Entity
66
67instance Default Entity where
68 def = Entity
69 { _eFaction = def
70 , _eSeqVal = def
71 }
72
73newtype EntityName = EntityName { _entityName :: CI String }
74 deriving (Show, Eq, Ord)
75makeLenses ''EntityName
76
77newtype EntityIdentifier = EntityIdentifier { _entityId :: Integer }
78 deriving (Show, Eq, Ord)
79makeLenses ''EntityIdentifier
80
81data GameState = GameState
82 { _gEntities :: Map EntityIdentifier Entity
83 , _gEntityNames :: Bimap EntityIdentifier EntityName
84 }
85makeLenses ''GameState
86
87instance Default GameState where
88 def = GameState
89 { _gEntities = def
90 , _gEntityNames = Bimap.empty
91 }
92
93inhabitedFactions :: Getter GameState [Faction]
94inhabitedFactions = to $ nub . sort . Map.elems . fmap (view eFaction) . view gEntities
95
96priorityQueue :: Getter GameState [(SeqVal, EntityIdentifier)]
97priorityQueue = to priorityQueue'
98 where
99 priorityQueue' (Map.toAscList . view gEntities -> entities) = sortBy (comparing $ Down . snd) . concat . map filter . map (over _1 $ view eSeqVal) . map swap $ entities
100 filter (Nothing, _) = mempty
101 filter (Just val, id) = pure (val, id)
diff --git a/src/Sequence/Utils.hs b/src/Sequence/Utils.hs
new file mode 100644
index 0000000..7d28b83
--- /dev/null
+++ b/src/Sequence/Utils.hs
@@ -0,0 +1,46 @@
1{-# LANGUAGE ViewPatterns, FlexibleContexts, MultiParamTypeClasses #-}
2
3module Sequence.Utils
4 ( toName, fromName
5 ) where
6
7import Sequence.Types
8
9import Control.Monad.State.Strict
10
11import Control.Lens
12
13import Data.Bimap (Bimap)
14import qualified Data.Bimap as Bimap
15
16import Data.CaseInsensitive (CI)
17import qualified Data.CaseInsensitive as CI
18
19import Data.Map.Strict (Map)
20import qualified Data.Map.Strict as Map
21
22import Data.Function
23import Data.Default
24import Data.Maybe
25import Text.Read
26
27import Data.List
28
29import System.Console.Shell
30
31unaligned = view faction' def
32
33toName :: MonadState GameState m => EntityIdentifier -> m String
34toName id = fromMaybe (show $ id ^. entityId) . fmap (CI.original . view entityName) . Bimap.lookup id <$> use gEntityNames
35
36fromName :: MonadState GameState m => String -> m (Maybe EntityIdentifier)
37fromName (readMaybe -> n@(Just _)) = return $ EntityIdentifier <$> n
38fromName (EntityName . CI.mk -> name) = Bimap.lookupR name <$> use gEntityNames
39
40instance Completion EntityIdentifier GameState where
41 completableLabel _ = "<entity>"
42 complete _ st prefix = return . filter ((isPrefixOf `on` CI.foldCase) prefix) . map ((evalState ?? st) . toName) . Map.keys $ st ^. gEntities
43
44instance Completion Faction GameState where
45 completableLabel _ = "<faction>"
46 complete _ st prefix = return . filter ((isPrefixOf `on` CI.foldCase) prefix) . nub . sort $ unaligned : map (view faction') (st ^. inhabitedFactions)