-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathPrettyPrint.hs
More file actions
244 lines (211 loc) · 9.22 KB
/
PrettyPrint.hs
File metadata and controls
244 lines (211 loc) · 9.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Some pretty printing, with specific constructors for the game
module Startups.PrettyPrint where
import Startups.Base
import Startups.Cards
import Data.Monoid
import Data.Maybe (fromMaybe)
import Data.String
import Control.Monad.Error
import qualified Data.Sequence as Seq
import Data.Sequence (Seq)
import qualified Data.Text as T
import qualified Data.Foldable as F
import Data.List (intersperse)
import Control.Lens
import qualified Text.PrettyPrint.ANSI.Leijen as PP
newtype PrettyDoc = PrettyDoc { getDoc :: Seq PrettyElement }
deriving (Eq, Monoid)
data PrettyElement = RawText T.Text
| NewLine
| Space
| Emph PrettyDoc
| Colorize PColor PrettyDoc
| Indent Int PrettyDoc
| PDirection EffectDirection
| PNeighbor Neighbor
| PFund Funding
| PPoach Poacher
| PVictory VictoryPoint
| PPlayerCount PlayerCount
| PTurn Turn
| PResource Resource
| PAge Age
| PCompanyStage CompanyStage
| PConflict PoachingOutcome
| PCompany CompanyProfile
| PResearch ResearchType
| PCardType CardType
deriving Eq
data PColor = PColorCard CardType
| PColorVictory VictoryType
deriving Eq
instance IsString PrettyDoc where
fromString = PrettyDoc . Seq.singleton . RawText . T.pack
instance Error PrettyDoc where
noMsg = mempty
strMsg = fromString
class PrettyE a where
pe :: a -> PrettyDoc
instance PrettyE PrettyDoc where
pe = id
instance PrettyE T.Text where
pe = PrettyDoc . Seq.singleton . RawText
instance PrettyE EffectDirection where
pe = PrettyDoc . Seq.singleton . PDirection
instance PrettyE Neighbor where
pe = PrettyDoc . Seq.singleton . PNeighbor
instance PrettyE Funding where
pe = PrettyDoc . Seq.singleton . PFund
instance PrettyE Poacher where
pe = PrettyDoc . Seq.singleton . PPoach
instance PrettyE VictoryPoint where
pe = PrettyDoc . Seq.singleton . PVictory
instance PrettyE PlayerCount where
pe = PrettyDoc . Seq.singleton . PPlayerCount
instance PrettyE Turn where
pe = PrettyDoc . Seq.singleton . PTurn
instance PrettyE Resource where
pe = PrettyDoc . Seq.singleton . PResource
instance PrettyE Age where
pe = PrettyDoc . Seq.singleton . PAge
instance PrettyE CompanyStage where
pe = PrettyDoc . Seq.singleton . PCompanyStage
instance PrettyE PoachingOutcome where
pe = PrettyDoc . Seq.singleton . PConflict
instance PrettyE CompanyProfile where
pe = PrettyDoc . Seq.singleton . PCompany
instance PrettyE ResearchType where
pe = PrettyDoc . Seq.singleton . PResearch
instance PrettyE CardType where
pe = PrettyDoc . Seq.singleton . PCardType
instance (PrettyE a, F.Foldable f) => PrettyE (f a) where
pe l = brackets $ sepBy (pchar ',') (map pe (F.toList l))
victory :: VictoryPoint -> VictoryType -> PrettyDoc
victory v vc = withVictoryColor vc (pe v)
brackets :: PrettyDoc -> PrettyDoc
brackets e = pchar '[' <> e <> pchar ']'
space :: PrettyDoc
space = PrettyDoc (Seq.singleton Space)
sepBy :: F.Foldable f => PrettyDoc -> f PrettyDoc -> PrettyDoc
sepBy sep = mconcat . intersperse sep . F.toList
pchar :: Char -> PrettyDoc
pchar = pe . T.singleton
withCardColor :: CardType -> PrettyDoc -> PrettyDoc
withCardColor c = PrettyDoc . Seq.singleton . Colorize (PColorCard c)
withVictoryColor :: VictoryType -> PrettyDoc -> PrettyDoc
withVictoryColor v = PrettyDoc . Seq.singleton . Colorize (PColorVictory v)
numerical :: Integral n => n -> PrettyDoc
numerical = fromString . (show :: Integer -> String) . fromIntegral
emph :: PrettyDoc -> PrettyDoc
emph = PrettyDoc . Seq.singleton . Emph
pcost :: Cost -> PrettyDoc
pcost (Cost r m) = F.foldMap pe r <> pe m
indent :: Int -> PrettyDoc -> PrettyDoc
indent d = PrettyDoc . Seq.singleton . Indent d
(<+>) :: PrettyDoc -> PrettyDoc -> PrettyDoc
a <+> b = a <> PrettyDoc (Seq.singleton Space) <> b
(</>) :: PrettyDoc -> PrettyDoc -> PrettyDoc
a </> b = a <> newline <> b
newline :: PrettyDoc
newline = PrettyDoc (Seq.singleton NewLine)
vcat :: [PrettyDoc] -> PrettyDoc
vcat = mconcat . intersperse newline
cardEffectShort :: Effect -> PrettyDoc
cardEffectShort c = case c of
ProvideResource r n _ -> "+" <> mconcat (replicate n (pe r))
ResourceChoice rs _ -> "+" <> sepBy "/" (map pe (F.toList rs))
CheapExchange rs t -> "Exch." <+> F.foldMap pe (F.toList rs) <+> F.foldMap pe t
AddVictory vc v cond -> "+" <> victory v vc <+> conditionShort cond
GainFunding m cond -> "+" <> pe m <+> conditionShort cond
RnD s -> pe s
Poaching p -> "+" <> pe p
ScientificBreakthrough -> "+" <> sepBy "/" (map pe [Scaling, Programming, CustomSolution])
Recycling -> "Play a discarded card"
Opportunity _ -> "Build for free once/age"
Efficiency -> "Play the last card of the age"
CopyCommunity -> "Copy a neighbor's community"
conditionShort :: Condition -> PrettyDoc
conditionShort cond = case cond of
HappensOnce -> mempty
PerCard t c -> sepBy "/" $ F.foldMap pe t : map pe (F.toList c)
ByPoachingResult t o -> sepBy "/" $ F.foldMap pe t : map pe (F.toList o)
ByStartupStage t -> "per stage" <+> brackets (F.foldMap pe t)
cardName :: Card -> PrettyDoc
cardName card = case card of
Card cn _ _ ct _ _ _ -> withCardColor ct (pe cn)
CompanyCard c s _ _ -> pe c <+> pe s
shortCard :: Card -> PrettyDoc
shortCard card = cardName card <+> pcost (card ^. cCost) <+> pe (map cardEffectShort (card ^. cEffect))
longCard :: Card -> PrettyDoc
longCard c = shortCard c <> page
<> if null grt
then mempty
else mempty <+> "- Free:" <+> pe grt
where
grt = c ^.. cFree . traverse . to pe
page = fromMaybe mempty (c ^? cAge . to pe . to brackets)
instance PP.Pretty PrettyDoc where
pretty = F.foldMap PP.pretty . getDoc
prettyColor :: PColor -> PP.Doc -> PP.Doc
prettyColor (PColorCard c) = case c of
BaseResource -> id
AdvancedResource -> id
Infrastructure -> PP.dullcyan
ResearchDevelopment -> PP.green
Commercial -> PP.dullyellow
HeadHunting -> PP.red
Community -> PP.magenta
prettyColor (PColorVictory v) = case v of
PoachingVictory -> PP.red
FundingVictory -> PP.dullyellow
CompanyVictory -> id
InfrastructureVictory -> PP.dullblue
RnDVictory -> PP.dullgreen
CommercialVictory -> PP.dullred
CommunityVictory -> PP.magenta
instance PP.Pretty PrettyElement where
pretty e = case e of
RawText t -> PP.string (T.unpack t)
NewLine -> PP.linebreak
Space -> PP.space
Emph d -> PP.bold (PP.pretty d)
Colorize c d -> prettyColor c (PP.pretty d)
Indent n d -> PP.indent n (PP.pretty d)
PDirection Own -> "⇓"
PDirection (Neighboring n) -> PP.pretty (PNeighbor n)
PNeighbor NLeft -> "◀"
PNeighbor NRight -> "▶"
PFund (Funding 0) -> mempty
PFund (Funding n) -> PP.yellow (PP.pretty (numerical n) <> "$")
PPoach (Poacher 0) -> mempty
PPoach (Poacher p) -> PP.red $ if p > 5
then fromString (replicate (fromIntegral p) '⚔')
else PP.pretty (numerical p) <> "⚔"
PVictory vp -> PP.pretty $ numerical vp
PPlayerCount pc -> PP.pretty $ numerical pc
PTurn t -> PP.pretty $ numerical t
PResource Youthfulness -> PP.cyan "Y"
PResource Adoption -> PP.dullwhite "A"
PResource Vision -> PP.magenta "V"
PResource Development -> PP.dullyellow "D"
PResource Marketing -> PP.dullgreen "M"
PResource Finance -> PP.dullwhite "F"
PResource Operations -> PP.white "O"
PAge Age1 -> "Ⅰ"
PAge Age2 -> "Ⅱ"
PAge Age3 -> "Ⅲ"
PCompanyStage Project -> "."
PCompanyStage Stage1 -> "_"
PCompanyStage Stage2 -> "="
PCompanyStage Stage3 -> "Δ"
PCompanyStage Stage4 -> "☥"
PConflict Defeat -> PP.red "-1"
PConflict (Victory Age1) -> PP.red "+1"
PConflict (Victory Age2) -> PP.red "+3"
PConflict (Victory Age3) -> PP.red "+5"
PCardType t -> prettyColor (PColorCard t) (fromString (show t))
PResearch r -> PP.dullgreen $ PP.char (head (show r))
PCompany (CompanyProfile c s) -> PP.string (show c) <> PP.string (show s)