summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2016-06-08 22:45:07 +0200
committerGregor Kleen <gkleen@yggdrasil.li>2016-06-08 22:45:07 +0200
commit85486c4838e23ca6d8b643c759d4e2a3035ef61d (patch)
treeca642cb38eae17ba88002c97973d79a9f34bf457
parent73c24db325e741ca9402152d934bd28c7ac89fed (diff)
download2017-01-16_17:13:37-85486c4838e23ca6d8b643c759d4e2a3035ef61d.tar
2017-01-16_17:13:37-85486c4838e23ca6d8b643c759d4e2a3035ef61d.tar.gz
2017-01-16_17:13:37-85486c4838e23ca6d8b643c759d4e2a3035ef61d.tar.bz2
2017-01-16_17:13:37-85486c4838e23ca6d8b643c759d4e2a3035ef61d.tar.xz
2017-01-16_17:13:37-85486c4838e23ca6d8b643c759d4e2a3035ef61d.zip
framework for damage tracking
-rw-r--r--src/Main.hs8
-rw-r--r--src/Sequence/Contact/Archetypes.hs29
-rw-r--r--src/Sequence/Contact/Tests.hs32
-rw-r--r--src/Sequence/Contact/Types.hs218
-rw-r--r--src/Sequence/Contact/Types/Internal.hs200
-rw-r--r--src/Sequence/Formula.hs25
6 files changed, 377 insertions, 135 deletions
diff --git a/src/Main.hs b/src/Main.hs
index 145df7a..b7c6a6e 100644
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -15,7 +15,7 @@ import System.Directory
15 15
16import Data.Default 16import Data.Default
17import Data.CaseInsensitive (CI) 17import Data.CaseInsensitive (CI)
18import qualified Data.CaseInsensitive 18import qualified Data.CaseInsensitive as CI
19 19
20import Data.Map.Strict (Map) 20import Data.Map.Strict (Map)
21import qualified Data.Map.Strict as Map 21import qualified Data.Map.Strict as Map
@@ -183,7 +183,8 @@ rollTest = withArg $ enactTest' >=> maybe (return ()) (shellPutStrLn . ppResult)
183 183
184enactTest' :: (FormulaM Stats Test) -> Sh GameState (Maybe TestResult) 184enactTest' :: (FormulaM Stats Test) -> Sh GameState (Maybe TestResult)
185enactTest' test = withFocus' $ \focus -> do 185enactTest' test = withFocus' $ \focus -> do
186 (newFocus, result) <- evalFormula focus (enactTest =<< test) 186 focusName <- use gFocus >>= toName . fromJust
187 (newFocus, result) <- evalFormula focusName focus (enactTest =<< test)
187 gFocus' .= newFocus 188 gFocus' .= newFocus
188 return result 189 return result
189 190
@@ -200,5 +201,6 @@ entitySeqVal' ident = do
200 case (,) <$> entity <*> sVal of 201 case (,) <$> entity <*> sVal of
201 Nothing -> return () 202 Nothing -> return ()
202 Just (entity, sVal) -> do 203 Just (entity, sVal) -> do
203 (newEntity, view (seqVal . re _Just) -> val) <- evalFormula entity sVal 204 name <- toName ident
205 (newEntity, view (seqVal . re _Just) -> val) <- evalFormula name entity sVal
204 gEntities . at ident .= Just (newEntity & set eSeqVal val) 206 gEntities . at ident .= Just (newEntity & set eSeqVal val)
diff --git a/src/Sequence/Contact/Archetypes.hs b/src/Sequence/Contact/Archetypes.hs
index 193892a..3e9b416 100644
--- a/src/Sequence/Contact/Archetypes.hs
+++ b/src/Sequence/Contact/Archetypes.hs
@@ -85,6 +85,17 @@ human = Humanoid
85 , _sSeqVal = vReflexes * 2 + vMobility + vPerception + d 10 85 , _sSeqVal = vReflexes * 2 + vMobility + vPerception + d 10
86 , _sPainTolerance = vMass `quot'` 2 + vWillpower 86 , _sPainTolerance = vMass `quot'` 2 + vWillpower
87 , _sFatigueTolerance = vWillpower `quot'` 2 + vEndurance 87 , _sFatigueTolerance = vWillpower `quot'` 2 + vEndurance
88
89 , _sHitzones = [ ("Kopf", 0.05)
90 , ("Torso", 0.49)
91 , ("Rechter Arm", 0.08)
92 , ("Linker Arm", 0.08)
93 , ("Unterleib", 0.10)
94 , ("Rechtes Bein", 0.10)
95 , ("Linkes Bein", 0.10)
96 ]
97 , _sDamage = const 0
98 , _sArmor = const def
88 } 99 }
89 100
90dog = Quadruped 101dog = Quadruped
@@ -109,6 +120,17 @@ dog = Quadruped
109 , _sSeqVal = vReflexes * 2 + vMobility + vPerception + 2 * d 10 120 , _sSeqVal = vReflexes * 2 + vMobility + vPerception + 2 * d 10
110 , _sPainTolerance = vMass `quot'` 2 + vWillpower 121 , _sPainTolerance = vMass `quot'` 2 + vWillpower
111 , _sFatigueTolerance = vWillpower `quot'` 2 + vEndurance 122 , _sFatigueTolerance = vWillpower `quot'` 2 + vEndurance
123
124 , _sHitzones = [ ("Kopf", 0.05)
125 , ("Torso", 0.49)
126 , ("Rechter Vorderlauf", 0.08)
127 , ("Linker Vorderlauf", 0.08)
128 , ("Hinterteil", 0.10)
129 , ("Rechter Hinterlauf", 0.10)
130 , ("Linker Hinterlauf", 0.10)
131 ]
132 , _sDamage = const 0
133 , _sArmor = const def
112 } 134 }
113 135
114dolphin = Dolphin 136dolphin = Dolphin
@@ -133,6 +155,13 @@ dolphin = Dolphin
133 , _sSeqVal = vReflexes * 2 + vMobility + vPerception + 2 * d 10 155 , _sSeqVal = vReflexes * 2 + vMobility + vPerception + 2 * d 10
134 , _sPainTolerance = vMass `quot'` 2 + vWillpower 156 , _sPainTolerance = vMass `quot'` 2 + vWillpower
135 , _sFatigueTolerance = vWillpower `quot'` 2 + vEndurance 157 , _sFatigueTolerance = vWillpower `quot'` 2 + vEndurance
158
159 , _sHitzones = [ ("Kopf", 0.10)
160 , ("Rumpf", 0.65)
161 , ("Schwanz", 0.25)
162 ]
163 , _sDamage = const 0
164 , _sArmor = const def
136 } 165 }
137 166
138childOfMu = human 167childOfMu = human
diff --git a/src/Sequence/Contact/Tests.hs b/src/Sequence/Contact/Tests.hs
index 8aa072a..82b1cf3 100644
--- a/src/Sequence/Contact/Tests.hs
+++ b/src/Sequence/Contact/Tests.hs
@@ -1,9 +1,7 @@
1{-# LANGUAGE TemplateHaskell, OverloadedStrings, OverloadedLists, ViewPatterns, MultiParamTypeClasses, FlexibleContexts, RankNTypes, TypeSynonymInstances, FlexibleInstances, ImpredicativeTypes #-} 1{-# LANGUAGE TemplateHaskell, OverloadedStrings, OverloadedLists, ViewPatterns, MultiParamTypeClasses, FlexibleContexts, RankNTypes, TypeSynonymInstances, FlexibleInstances #-}
2 2
3module Sequence.Contact.Tests 3module Sequence.Contact.Tests
4 ( TestResult(..), rWith, rBy 4 ( enactTest
5 , Test, tCritSuccessMod, tCritFailureMod, tBaseDifficulty, tMod
6 , enactTest
7 ) where 5 ) where
8 6
9import Sequence.Formula 7import Sequence.Formula
@@ -13,6 +11,7 @@ import Sequence.Contact.Types
13 11
14import Control.Monad 12import Control.Monad
15import Control.Monad.Reader 13import Control.Monad.Reader
14import Control.Monad.Base
16import Control.Lens 15import Control.Lens
17 16
18import Data.Default 17import Data.Default
@@ -29,31 +28,6 @@ import Data.Traversable (mapM)
29 28
30import Prelude hiding (mapM) 29import Prelude hiding (mapM)
31 30
32
33data TestResult = CritSuccess { _rWith, _rBy :: Int }
34 | Success { _rWith, _rBy :: Int }
35 | Failure { _rWith, _rBy :: Int }
36 | CritFailure { _rWith, _rBy :: Int }
37 deriving (Eq, Ord, Show)
38makeLenses ''TestResult
39
40data Test = Test
41 { _tCritSuccessMod
42 , _tCritFailureMod
43 , _tBaseDifficulty
44 , _tMod :: Int
45 }
46 deriving (Eq, Ord)
47makeLenses ''Test
48
49instance Default Test where
50 def = Test
51 { _tCritSuccessMod = 0
52 , _tCritFailureMod = 0
53 , _tBaseDifficulty = 50
54 , _tMod = 0
55 }
56
57tests :: MonadReader Stats m => m (Map (CI String) (FormulaM Stats Test)) 31tests :: MonadReader Stats m => m (Map (CI String) (FormulaM Stats Test))
58tests = mconcat <$> sequence [ test "Stärke" $ sAStrength . attributeTest 32tests = mconcat <$> sequence [ test "Stärke" $ sAStrength . attributeTest
59 33
diff --git a/src/Sequence/Contact/Types.hs b/src/Sequence/Contact/Types.hs
index c00a60d..cd1bc02 100644
--- a/src/Sequence/Contact/Types.hs
+++ b/src/Sequence/Contact/Types.hs
@@ -1,6 +1,9 @@
1{-# LANGUAGE TemplateHaskell, RecordWildCards, OverloadedStrings, OverloadedLists, ViewPatterns, RankNTypes #-} 1{-# LANGUAGE TemplateHaskell, RecordWildCards, OverloadedStrings, OverloadedLists, ViewPatterns, RankNTypes, ImpredicativeTypes #-}
2 2
3module Sequence.Contact.Types where 3module Sequence.Contact.Types
4 ( module Sequence.Contact.Types
5 , module Sequence.Contact.Types.Internal
6 ) where
4 7
5import Sequence.Formula 8import Sequence.Formula
6 9
@@ -9,105 +12,103 @@ import Control.Lens
9 12
10import Data.Default 13import Data.Default
11 14
12data Stats = Prop 15import Data.Map (Map)
13 | Humanoid 16import qualified Data.Map as Map
14 { _sAStrength 17
15 , _sAEndurance 18import Data.CaseInsensitive (CI)
16 , _sAMass 19import qualified Data.CaseInsensitive as CI
17 , _sAReflexes 20
18 , _sAMobility 21import Data.String (IsString(..))
19 , _sADexterity 22import Data.Function (on)
20 , _sAIntelligence 23import Data.Maybe
21 , _sACharisma 24import Data.Ratio
22 , _sAPerception 25
23 , _sAWillpower 26import Control.Monad.Reader (ask)
24 27import Control.Monad.State
25 , _sSArchaicRanged 28
26 , _sSFirearms 29import Sequence.Contact.Types.Internal
27 , _sSHeavyWeapons 30
28 , _sSEnergyWeapons 31hitzone :: Iso' String Hitzone
29 , _sSUnarmedMelee 32hitzone = iso (Hitzone . CI.mk) (CI.original . _hitzone)
30 , _sSArmedMelee 33
31 , _sSThrownWeapons 34instance IsString Hitzone where
32 , _sSStealth 35 fromString = view hitzone
33 , _sSThievery 36
34 , _sSLockpicking 37makeLenses ''Armor
35 , _sSTrapping 38
36 , _sSSciences 39instance Default Armor where
37 , _sSFirstAid 40 def = Armor
38 , _sSMedicine 41 { _aBallistic = 0
39 , _sSHumanities 42 , _aPiercing = 0
40 , _sSEngineering 43 , _aBlunt = 0
41 , _sSCraft 44 , _aHeat = 0
42 , _sSInterface 45 , _aCold = 0
43 , _sSSpeech 46 , _aToxic = 0
44 , _sSLeadership 47 , _aExplosive = 0
45 , _sSHomeEconomics 48 }
46 , _sSSurvival 49
47 , _sSMotorcycle 50makeLenses ''TestResult
48 , _sSWheeled 51
49 , _sSHovercraft 52makeLenses ''Test
50 , _sSAircraft 53
51 , _sSSpacecraft 54instance Default Test where
52 , _sSWatercraft 55 def = Test
53 , _sSTracked 56 { _tName = ""
54 , _sSExoskeleton 57 , _tCritSuccessMod = 0
55 58 , _tCritFailureMod = 0
56 , _sMaxVitality 59 , _tBaseDifficulty = 50
57 , _sSeqVal 60 , _tMod = 0
58 , _sPainTolerance 61 }
59 , _sFatigueTolerance :: Formula Stats 62
60 } 63makePrisms ''Modifier
61 | Quadruped 64
62 { _sAStrength 65instance Eq Modifier where
63 , _sAEndurance 66 (==) = (==) `on` (view $ _Modifier . _1)
64 , _sAMass 67
65 , _sAReflexes 68instance Ord Modifier where
66 , _sAMobility 69 compare = compare `on` (view $ _Modifier . _1)
67 , _sADexterity 70
68 , _sAIntelligence 71instance Default Modifier where
69 , _sACharisma 72 def = Modifier "" pure
70 , _sAPerception 73
71 , _sAWillpower 74makePrisms ''Effect
72 75
73 , _sSBiting 76instance Eq Effect where
74 , _sSSearching 77 (==) = (==) `on` (view $ _Effect . _1)
75 , _sSStealth 78
76 , _sSLeadership 79instance Ord Effect where
77 , _sSDemeanour 80 compare = compare `on` (view $ _Effect . _1)
78 81
79 , _sMaxVitality 82instance Default Effect where
80 , _sSeqVal 83 def = Effect "" pure
81 , _sPainTolerance 84
82 , _sFatigueTolerance :: Formula Stats 85makeLenses ''ShockEffect
83 } 86
84 | Dolphin 87instance Default ShockEffect where
85 { _sAStrength 88 def = ShockEffect { _seApplied = False
86 , _sAEndurance 89 , _seVal = pure Nothing
87 , _sAMass 90 , _seBar = pure Nothing
88 , _sAReflexes 91 , _seEffect = def
89 , _sAMobility 92 }
90 , _sADexterity 93
91 , _sAIntelligence
92 , _sACharisma
93 , _sAPerception
94 , _sAWillpower
95
96 , _sSRamming
97 , _sSTargeting
98 , _sSSearching
99 , _sSStealth
100 , _sSComprehension
101
102 , _sMaxVitality
103 , _sSeqVal
104 , _sPainTolerance
105 , _sFatigueTolerance :: Formula Stats
106 }
107makeLenses ''Stats 94makeLenses ''Stats
108 95
109instance Default Stats where 96instance Default Stats where
110 def = Prop 97 def = Prop
98 { _sHitzones = [("Volumen", 1)]
99 , _sDamage = const 0
100 , _sFatigue = 0
101 , _sCripple = const def
102 , _sArmor = const def
103
104 , _sExtraSkills = []
105 , _sModifiers = []
106 }
107
108applyModifier :: String -> (Test -> FormulaM Stats Test) -> Effect
109applyModifier effectName modifier = Effect (CI.mk effectName) $ return . apply
110 where
111 apply = sModifiers <>~ [Modifier (CI.mk $ effectName ++ " (modifier)") modifier]
111 112
112vStrength = val sAStrength "Stärke?" True 113vStrength = val sAStrength "Stärke?" True
113vEndurance = val sAEndurance "Ausdauer?" True 114vEndurance = val sAEndurance "Ausdauer?" True
@@ -119,3 +120,26 @@ vIntelligence = val sAIntelligence "Intelligenz?" True
119vCharisma = val sACharisma "Charisma?" True 120vCharisma = val sACharisma "Charisma?" True
120vPerception = val sAPerception "Wahrnehmung?" True 121vPerception = val sAPerception "Wahrnehmung?" True
121vWillpower = val sAWillpower "Entschlossenheit?" True 122vWillpower = val sAWillpower "Entschlossenheit?" True
123
124scaled :: (Real a, Fractional a, Integral b) => Ratio b -> Iso' a a
125scaled (realToFrac -> ratio) = iso (* ratio) (/ ratio)
126
127sDamage' :: String -> Traversal' Stats Int
128sDamage' (view hitzone -> zone) = sDamage . ix zone
129
130sCripple' :: String -> Traversal' Stats ShockEffect
131sCripple' (view hitzone -> zone) = sCripple . ix zone
132
133sTotalDamage :: Lens' Stats Int
134sTotalDamage = lens retrieve undefined
135 where
136 retrieve = do
137 hitzones <- Map.keys <$> view sHitzones
138 damageMap <- view sDamage
139 return . sum $ pure damageMap <*> hitzones
140
141sDead :: Fold Stats (FormulaM Stats Bool)
142sDead = folding $ do
143 maxVitality <- preview sMaxVitality
144 damage <- view sTotalDamage
145 return $ liftM2 (>) <$> Just (return damage) <*> maxVitality
diff --git a/src/Sequence/Contact/Types/Internal.hs b/src/Sequence/Contact/Types/Internal.hs
new file mode 100644
index 0000000..9929801
--- /dev/null
+++ b/src/Sequence/Contact/Types/Internal.hs
@@ -0,0 +1,200 @@
1{-# LANGUAGE RankNTypes #-}
2
3module Sequence.Contact.Types.Internal where
4
5import Sequence.Formula (Formula, FormulaM, Table)
6
7import Data.Map (Map)
8import Data.Set (Set)
9import Data.Ratio
10
11import Control.Lens
12
13import Data.CaseInsensitive (CI)
14
15newtype Hitzone = Hitzone { _hitzone :: CI String }
16 deriving (Eq, Ord)
17
18data Armor = Armor
19 { _aBallistic
20 , _aPiercing
21 , _aBlunt
22 , _aHeat
23 , _aCold
24 , _aToxic
25 , _aExplosive :: Int
26 }
27
28data TestResult = CritSuccess { _rWith, _rBy :: Int }
29 | Success { _rWith, _rBy :: Int }
30 | Failure { _rWith, _rBy :: Int }
31 | CritFailure { _rWith, _rBy :: Int }
32 deriving (Eq, Ord, Show)
33
34data Test = Test
35 { _tName :: CI String
36 , _tCritSuccessMod
37 , _tCritFailureMod
38 , _tBaseDifficulty
39 , _tMod :: Int
40 }
41 deriving (Eq, Ord)
42
43data Modifier = Modifier (CI String) (Test -> FormulaM Stats Test)
44
45data Effect = Effect (CI String) (Stats -> FormulaM Stats Stats)
46
47data ShockEffect = ShockEffect
48 { _seApplied :: Bool
49 , _seVal :: FormulaM Stats (Maybe Int)
50 , _seBar :: FormulaM Stats (Maybe Int)
51 , _seEffect :: Table Effect
52 }
53
54data Stats = Prop
55 { _sHitzones :: Table Hitzone
56 , _sArmor :: Hitzone -> Armor
57 , _sCripple :: Hitzone -> ShockEffect
58
59 , _sDamage :: Hitzone -> Int
60 , _sFatigue :: Int
61
62 , _sPainShock :: ShockEffect
63 , _sFatigueShock :: ShockEffect
64
65 , _sExtraSkills :: Map (CI String) (FormulaM Stats Test)
66 , _sModifiers :: Set Modifier
67 }
68 | Humanoid
69 { _sAStrength
70 , _sAEndurance
71 , _sAMass
72 , _sAReflexes
73 , _sAMobility
74 , _sADexterity
75 , _sAIntelligence
76 , _sACharisma
77 , _sAPerception
78 , _sAWillpower
79
80 , _sSArchaicRanged
81 , _sSFirearms
82 , _sSHeavyWeapons
83 , _sSEnergyWeapons
84 , _sSUnarmedMelee
85 , _sSArmedMelee
86 , _sSThrownWeapons
87 , _sSStealth
88 , _sSThievery
89 , _sSLockpicking
90 , _sSTrapping
91 , _sSSciences
92 , _sSFirstAid
93 , _sSMedicine
94 , _sSHumanities
95 , _sSEngineering
96 , _sSCraft
97 , _sSInterface
98 , _sSSpeech
99 , _sSLeadership
100 , _sSHomeEconomics
101 , _sSSurvival
102 , _sSMotorcycle
103 , _sSWheeled
104 , _sSHovercraft
105 , _sSAircraft
106 , _sSSpacecraft
107 , _sSWatercraft
108 , _sSTracked
109 , _sSExoskeleton
110
111 , _sMaxVitality
112 , _sSeqVal
113 , _sPainTolerance
114 , _sFatigueTolerance :: Formula Stats
115
116 , _sHitzones :: Table Hitzone
117 , _sArmor :: Hitzone -> Armor
118 , _sCripple :: Hitzone -> ShockEffect
119
120 , _sDamage :: Hitzone -> Int
121 , _sFatigue :: Int
122
123 , _sPainShock :: ShockEffect
124 , _sFatigueShock :: ShockEffect
125
126 , _sExtraSkills :: Map (CI String) (FormulaM Stats Test)
127 , _sModifiers :: Set Modifier
128 }
129 | Quadruped
130 { _sAStrength
131 , _sAEndurance
132 , _sAMass
133 , _sAReflexes
134 , _sAMobility
135 , _sADexterity
136 , _sAIntelligence
137 , _sACharisma
138 , _sAPerception
139 , _sAWillpower
140
141 , _sSBiting
142 , _sSSearching
143 , _sSStealth
144 , _sSLeadership
145 , _sSDemeanour
146
147 , _sMaxVitality
148 , _sSeqVal
149 , _sPainTolerance
150 , _sFatigueTolerance :: Formula Stats
151
152 , _sHitzones :: Table Hitzone
153 , _sArmor :: Hitzone -> Armor
154 , _sCripple :: Hitzone -> ShockEffect
155
156 , _sDamage :: Hitzone -> Int
157 , _sFatigue :: Int
158
159 , _sPainShock :: ShockEffect
160 , _sFatigueShock :: ShockEffect
161
162 , _sExtraSkills :: Map (CI String) (FormulaM Stats Test)
163 , _sModifiers :: Set Modifier
164 }
165 | Dolphin
166 { _sAStrength
167 , _sAEndurance
168 , _sAMass
169 , _sAReflexes
170 , _sAMobility
171 , _sADexterity
172 , _sAIntelligence
173 , _sACharisma
174 , _sAPerception
175 , _sAWillpower
176
177 , _sSRamming
178 , _sSTargeting
179 , _sSSearching
180 , _sSStealth
181 , _sSComprehension
182
183 , _sMaxVitality
184 , _sSeqVal
185 , _sPainTolerance
186 , _sFatigueTolerance :: Formula Stats
187
188 , _sHitzones :: Table Hitzone
189 , _sArmor :: Hitzone -> Armor
190 , _sCripple :: Hitzone -> ShockEffect
191
192 , _sDamage :: Hitzone -> Int
193 , _sFatigue :: Int
194
195 , _sPainShock :: ShockEffect
196 , _sFatigueShock :: ShockEffect
197
198 , _sExtraSkills :: Map (CI String) (FormulaM Stats Test)
199 , _sModifiers :: Set Modifier
200 }
diff --git a/src/Sequence/Formula.hs b/src/Sequence/Formula.hs
index d486bab..ca945f8 100644
--- a/src/Sequence/Formula.hs
+++ b/src/Sequence/Formula.hs
@@ -2,10 +2,11 @@
2 2
3module Sequence.Formula 3module Sequence.Formula
4 ( FormulaM, Formula, quot' 4 ( FormulaM, Formula, quot'
5 , (:<:)(..), Context(..) 5 , (:<:)(..), Context(..), ctx
6 , evalFormula 6 , evalFormula
7 , val 7 , val
8 , d, z 8 , d, z
9 , Table, table
9 ) where 10 ) where
10 11
11import Control.Lens hiding (Context(..)) 12import Control.Lens hiding (Context(..))
@@ -31,6 +32,9 @@ import Data.Either
31import Data.Set (Set) 32import Data.Set (Set)
32import qualified Data.Set as Set 33import qualified Data.Set as Set
33 34
35import Data.Map (Map)
36import qualified Data.Map as Map
37
34class (:<:) small large where 38class (:<:) small large where
35 ctx' :: Traversal' large small 39 ctx' :: Traversal' large small
36 40
@@ -89,16 +93,20 @@ instance Integral a => Num (FormulaM input a) where
89quot' :: Integral a => FormulaM input a -> FormulaM input a -> FormulaM input a 93quot' :: Integral a => FormulaM input a -> FormulaM input a -> FormulaM input a
90quot' = liftM2 quot 94quot' = liftM2 quot
91 95
92askQuestion :: (MonadIO m, sInput :<: lInput) => lInput -> Question sInput -> m lInput 96askQuestion :: (MonadIO m, sInput :<: lInput) => String -> lInput -> Question sInput -> m lInput
93askQuestion input q@(Question{..}) = flip (set $ ctx' . answer) input . maybe (throwError q) return <$> askQ prompt (join . fmap readMaybe) 97askQuestion promptPref input q@(Question{..}) = flip (set $ ctx' . answer) input . maybe (throwError q) return <$> askQ (promptPref' ++ prompt) (join . fmap readMaybe)
98 where
99 promptPref'
100 | null promptPref = ""
101 | otherwise = promptPref ++ " » "
94 102
95evalFormula :: (MonadIO m, sInput :<: lInput) => lInput -> FormulaM sInput a -> m (lInput, a) 103evalFormula :: (MonadIO m, sInput :<: lInput) => String -> lInput -> FormulaM sInput a -> m (lInput, a)
96evalFormula = evalFormula' [] 104evalFormula = evalFormula' []
97 where 105 where
98 evalFormula' finalChanges input formula = do 106 evalFormula' finalChanges promptPref input formula = do
99 result <- liftIO . enact . runExceptT . (runReaderT ?? (Context input)) . (evalStateT ?? Set.empty) $ formula 107 result <- liftIO . enact . runExceptT . (runReaderT ?? (Context input)) . (evalStateT ?? Set.empty) $ formula
100 case result of 108 case result of
101 Left q@(Question{..}) -> askQuestion input q >>= flip (evalFormula' $ bool (pure . set (ctx' . answer) $ throwError q) mempty keepResult ++ finalChanges) formula 109 Left q@(Question{..}) -> askQuestion promptPref input q >>= flip (flip evalFormula' promptPref $ bool (pure . set (ctx' . answer) $ throwError q) mempty keepResult ++ finalChanges) formula
102 Right result -> return (foldr ($) input finalChanges, result) 110 Right result -> return (foldr ($) input finalChanges, result)
103 111
104val :: Integral a => Traversal' input (Formula input) -> String -> Bool -> Formula input 112val :: Integral a => Traversal' input (Formula input) -> String -> Bool -> Formula input
@@ -109,3 +117,8 @@ val answer prompt keepResult = do
109d, z :: Integral a => Int -> FormulaM input a 117d, z :: Integral a => Int -> FormulaM input a
110d n = liftBase . fmap fromIntegral $ D.d n 118d n = liftBase . fmap fromIntegral $ D.d n
111z n = liftBase . fmap fromIntegral $ D.z n 119z n = liftBase . fmap fromIntegral $ D.z n
120
121type Table a = Map a Rational
122
123table :: Ord a => Table a -> FormulaM input a
124table = liftBase . makeEventProb . Map.assocs