summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--default.nix56
-rw-r--r--sequence.cabal17
-rw-r--r--sequence.nix11
-rw-r--r--src/Main.hs77
-rw-r--r--src/Sequence/Types.hs101
-rw-r--r--src/Sequence/Utils.hs46
6 files changed, 302 insertions, 6 deletions
diff --git a/default.nix b/default.nix
index ac19354..c96e43f 100644
--- a/default.nix
+++ b/default.nix
@@ -3,6 +3,60 @@
3}: 3}:
4 4
5rec { 5rec {
6 haskellPackages = pkgs.haskell.packages."${compilerName}"; 6 haskellPackages = pkgs.haskell.packages."${compilerName}".override {
7 overrides = self: super: {
8 Shellac = pkgs.haskell.lib.appendPatch super.Shellac (pkgs.writeText "build.patch" ''
9 From 29c78ed6549525fefb04ae6f7cab8316ac59f3c4 Mon Sep 17 00:00:00 2001
10 From: Gregor Kleen <gkleen@yggdrasil.li>
11 Date: Thu, 2 Jun 2016 17:14:15 +0200
12 Subject: [PATCH 1/2] Hiding <$> provided by newer versions of Prelude
13
14 ---
15 src/System/Console/Shell/PPrint.hs | 1 +
16 1 file changed, 1 insertion(+)
17
18 diff --git a/src/System/Console/Shell/PPrint.hs b/src/System/Console/Shell/PPrint.hs
19 index e8ec520..8d271e1 100644
20 --- a/src/System/Console/Shell/PPrint.hs
21 +++ b/src/System/Console/Shell/PPrint.hs
22 @@ -48,6 +48,7 @@ module System.Console.Shell.PPrint
23 ) where
24
25 import System.IO (Handle,hPutStr,hPutChar,stdout)
26 +import Prelude hiding ((<$>))
27
28 infixr 5 </>,<//>,<$>,<$$>
29 infixr 6 <>,<+>
30 --
31 2.8.0
32
33
34 From 74cb07ccfa92fdcdd6eb3c5871289796ea4981d5 Mon Sep 17 00:00:00 2001
35 From: Gregor Kleen <gkleen@yggdrasil.li>
36 Date: Thu, 2 Jun 2016 17:28:17 +0200
37 Subject: [PATCH 2/2] Applicative instance for Sh
38
39 ---
40 src/System/Console/Shell/Types.hs | 2 +-
41 1 file changed, 1 insertion(+), 1 deletion(-)
42
43 diff --git a/src/System/Console/Shell/Types.hs b/src/System/Console/Shell/Types.hs
44 index 4ec47a9..9efe4b4 100644
45 --- a/src/System/Console/Shell/Types.hs
46 +++ b/src/System/Console/Shell/Types.hs
47 @@ -74,7 +74,7 @@ type OutputCommand = BackendOutput -> IO ()
48 -- The type parameter @st@ allows the monad to carry around a package of
49 -- user-defined state.
50 newtype Sh st a = Sh { unSh :: StateT (CommandResult st) (ReaderT OutputCommand IO) a }
51 - deriving (Monad, MonadIO, MonadFix, Functor)
52 + deriving (Monad, MonadIO, MonadFix, Functor, Applicative)
53
54 ------------------------------------------------------------------------
55 -- The shell description and utility functions
56 --
57 2.8.0
58 '');
59 };
60 };
7 sequence = haskellPackages.callPackage ./sequence.nix {}; 61 sequence = haskellPackages.callPackage ./sequence.nix {};
8} 62}
diff --git a/sequence.cabal b/sequence.cabal
index 0cea160..3d9e66f 100644
--- a/sequence.cabal
+++ b/sequence.cabal
@@ -17,8 +17,21 @@ cabal-version: >=1.10
17 17
18executable sequence 18executable sequence
19 main-is: Main.hs 19 main-is: Main.hs
20 -- other-modules: 20 other-modules: Sequence.Types
21 -- other-extensions: 21 -- other-extensions:
22 build-depends: base >=4.8 && <4.9 22 build-depends: base >=4.8 && <5
23 , Shellac
24 , Shellac-haskeline
25 , data-default
26 , xdg-basedir
27 , filepath
28 , directory
29 , containers
30 , case-insensitive
31 , lens
32 , bimap
33 , mtl
34 , table-layout
35 , game-probability
23 hs-source-dirs: src 36 hs-source-dirs: src
24 default-language: Haskell2010 \ No newline at end of file 37 default-language: Haskell2010 \ No newline at end of file
diff --git a/sequence.nix b/sequence.nix
index 612eb30..4533ad2 100644
--- a/sequence.nix
+++ b/sequence.nix
@@ -1,10 +1,17 @@
1{ mkDerivation, base, stdenv }: 1{ mkDerivation, base, bimap, case-insensitive, containers
2, data-default, directory, filepath, game-probability, lens, mtl
3, Shellac, Shellac-haskeline, stdenv, table-layout, xdg-basedir
4}:
2mkDerivation { 5mkDerivation {
3 pname = "sequence"; 6 pname = "sequence";
4 version = "0.0.0"; 7 version = "0.0.0";
5 src = ./.; 8 src = ./.;
6 isLibrary = false; 9 isLibrary = false;
7 isExecutable = true; 10 isExecutable = true;
8 executableHaskellDepends = [ base ]; 11 executableHaskellDepends = [
12 base bimap case-insensitive containers data-default directory
13 filepath game-probability lens mtl Shellac Shellac-haskeline
14 table-layout xdg-basedir
15 ];
9 license = stdenv.lib.licenses.mit; 16 license = stdenv.lib.licenses.mit;
10} 17}
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)