-
Notifications
You must be signed in to change notification settings - Fork 2
/
GameTypes.hs
132 lines (102 loc) · 4.85 KB
/
GameTypes.hs
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
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GADTs #-}
module Startups.GameTypes where
import Startups.Base
import Startups.Cards
import Startups.PrettyPrint
import Control.Lens
import qualified Data.Text as T
import qualified Data.Map.Strict as M
import Control.Monad.Operational
import Control.Monad.State.Strict
import Control.Monad.Error
import Data.List.NonEmpty
import Control.Applicative
import System.Random
type PlayerId = T.Text
showPlayerId :: PlayerId -> PrettyDoc
showPlayerId = emph . pe
data GameState = GameState { _playermap :: M.Map PlayerId PlayerState
, _discardpile :: [Card]
, _rnd :: StdGen
}
type Neighborhood = (PlayerId, PlayerId)
data PlayerState = PlayerState { _pCompany :: CompanyProfile
, _pCompanyStage :: CompanyStage
, _pCards :: [Card]
, _pFunds :: Funding
, _pNeighborhood :: Neighborhood
, _pPoachingResults :: [PoachingOutcome]
}
makeLenses ''GameState
makeLenses ''PlayerState
cardEffects :: Traversal' PlayerState Effect
cardEffects = pCards . traverse . cEffect . traverse
playerEffects :: PlayerId -> Traversal' GameState Effect
playerEffects pid = playermap . ix pid . cardEffects
neighbor :: Neighbor -> Lens' PlayerState PlayerId
neighbor NLeft = pNeighborhood . _1
neighbor NRight = pNeighborhood . _2
type Message = PrettyDoc
data PlayerAction = PlayerAction ActionType Card
deriving Eq
data ActionType = Play | Drop | BuildCompany
deriving Eq
_NonEmpty :: Prism' [a] (NonEmpty a)
_NonEmpty = prism' toList nonEmpty
-- | This describe the capabilities needed to write the rules, when no
-- interaction with the player is required.
type NonInteractive m = (MonadState GameState m, Monad m, MonadError Message m, Functor m, Applicative m)
type GameStateOnly m = (MonadState GameState m, Monad m, Functor m, Applicative m)
data CommunicationType = PlayerCom PlayerId Communication
| BroadcastCom Communication
data Communication = RawMessage PrettyDoc
| ActionRecapMsg GameState (M.Map PlayerId (PlayerAction, Exchange))
data GameInstr p a where
PlayerDecision :: Age -> Turn -> PlayerId -> NonEmpty Card -> GameInstr p (p (PlayerAction, Exchange))
AskCard :: Age -> PlayerId -> NonEmpty Card -> Message -> GameInstr p (p Card)
GetPromise :: p a -> GameInstr p a
Message :: CommunicationType -> GameInstr p ()
ThrowError :: Message -> GameInstr p a -- ^ Used for the error instance
CatchError :: GameMonad p a -> (Message -> GameMonad p a) -> GameInstr p a
type GameMonad p = ProgramT (GameInstr p) (State GameState)
-- | Ask the player which card he would like to play.
playerDecision :: Age -> Turn -> PlayerId -> NonEmpty Card -> GameMonad p (p (PlayerAction, Exchange))
playerDecision a t p c = singleton (PlayerDecision a t p c)
-- | Tell some information to a specific player
tellPlayer :: PlayerId -> Message -> GameMonad p ()
tellPlayer p = singleton . Message . PlayerCom p . RawMessage
-- | Broadcast some information
generalMessage :: Message -> GameMonad p ()
generalMessage = singleton . Message . BroadcastCom . RawMessage
-- | Awaits a promise
getPromise :: p a -> GameMonad p a
getPromise = singleton . GetPromise
-- | Gives a quick rundown of all actions
actionRecap :: M.Map PlayerId (PlayerAction, Exchange) -> GameMonad p ()
actionRecap mm = get >>= \s -> singleton . Message . BroadcastCom $ ActionRecapMsg s mm
instance MonadError PrettyDoc (ProgramT (GameInstr p) (State GameState)) where
throwError = singleton . ThrowError
catchError a handler = singleton (CatchError a handler)
-- | Ask the player to chose a card, along with a descriptive message.
-- This is used for the Recycling and CopyCommunity effects.
-- We define a "safe" version of the `askCard` function, that makes sure the
-- player doesn't introduce a new card in the game.
askCardSafe :: Age -> PlayerId -> NonEmpty Card -> Message -> GameMonad p Card
askCardSafe a p cl m = do
card <- singleton (AskCard a p cl m) >>= getPromise
when (card `notElem` (cl ^. re _NonEmpty)) (throwError (showPlayerId p <+> "tried to play a non proposed card"))
return card
instance PrettyE PlayerAction where
pe (PlayerAction a c) = a' <+> cardName c
where
a' = case a of
Play -> "played"
Drop -> "dropped"
BuildCompany -> "increase the company stage"