implement sequencing of GameActions with a Monoid interface
This commit is contained in:
parent
fe3c7e54e7
commit
4898dea404
36
src/Test.hs
36
src/Test.hs
|
|
@ -4,6 +4,7 @@
|
||||||
import Waterdeep.Types
|
import Waterdeep.Types
|
||||||
import Waterdeep.Monad
|
import Waterdeep.Monad
|
||||||
import Waterdeep.Logic
|
import Waterdeep.Logic
|
||||||
|
import Waterdeep.Util (mrepeat)
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
|
|
@ -11,6 +12,7 @@ import Control.Monad.Prompt
|
||||||
import Control.Monad.Random
|
import Control.Monad.Random
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import Data.List
|
import Data.List
|
||||||
|
import Data.Monoid
|
||||||
import Text.Printf
|
import Text.Printf
|
||||||
import System.IO
|
import System.IO
|
||||||
import System.Random
|
import System.Random
|
||||||
|
|
@ -29,59 +31,57 @@ p2 = ("Ned", f2, l2)
|
||||||
q1 = Quest { _questType = Arcana
|
q1 = Quest { _questType = Arcana
|
||||||
, _questTitle = "Research Palantirs"
|
, _questTitle = "Research Palantirs"
|
||||||
, _questQuote = ""
|
, _questQuote = ""
|
||||||
, _questAction = Transaction $
|
, _questAction = ReturnResource [Cleric]
|
||||||
[ Transaction $
|
<> ReturnResource [Rogue]
|
||||||
map (\r -> ReturnResource [r]) [Cleric,Rogue,Rogue,Wizard]
|
<> ReturnResource [Rogue]
|
||||||
, Repeat 4 $ ReturnResource [Gold]
|
<> ReturnResource [Wizard]
|
||||||
, ScorePoints 15
|
<> mrepeat 4 (ReturnResource [Gold])
|
||||||
, Repeat 8 $ TakeResource [Gold]
|
<> ScorePoints 15
|
||||||
]
|
<> mrepeat 8 (TakeResource [Gold])
|
||||||
, _questPlotActions = []
|
, _questPlotActions = []
|
||||||
}
|
}
|
||||||
|
|
||||||
q2 = Quest { _questType = Skullduggery
|
q2 = Quest { _questType = Skullduggery
|
||||||
, _questTitle = "Rob Waterdeep Bank"
|
, _questTitle = "Rob Waterdeep Bank"
|
||||||
, _questQuote = ""
|
, _questQuote = ""
|
||||||
, _questAction = Transaction $
|
, _questAction = mrepeat 8 (ReturnResource [Rogue])
|
||||||
[ Repeat 8 $ ReturnResource [Rogue]
|
<> ScorePoints 10
|
||||||
, ScorePoints 10
|
<> mrepeat 16 (TakeResource [Gold])
|
||||||
, Repeat 16 $ TakeResource [Gold]
|
|
||||||
]
|
|
||||||
, _questPlotActions = []
|
, _questPlotActions = []
|
||||||
}
|
}
|
||||||
|
|
||||||
b1 = Building { _buildingCost = 6
|
b1 = Building { _buildingCost = 6
|
||||||
, _buildingTitle = "Monastary"
|
, _buildingTitle = "Monastary"
|
||||||
, _buildingAction = Group [TakeResource [Cleric], TakeResource [Cleric]]
|
, _buildingAction = mrepeat 2 (TakeResource [Cleric])
|
||||||
, _buildingOwnerAction = TakeResource [Cleric]
|
, _buildingOwnerAction = TakeResource [Cleric]
|
||||||
, _buildingAccumType = NoAccumulation
|
, _buildingAccumType = NoAccumulation
|
||||||
}
|
}
|
||||||
|
|
||||||
b2 = Building { _buildingCost = 4
|
b2 = Building { _buildingCost = 4
|
||||||
, _buildingTitle = "Training Hall"
|
, _buildingTitle = "Training Hall"
|
||||||
, _buildingAction = Group [TakeResource [Fighter], TakeResource [Fighter]]
|
, _buildingAction = mrepeat 2 (TakeResource [Fighter])
|
||||||
, _buildingOwnerAction = TakeResource [Fighter]
|
, _buildingOwnerAction = TakeResource [Fighter]
|
||||||
, _buildingAccumType = NoAccumulation
|
, _buildingAccumType = NoAccumulation
|
||||||
}
|
}
|
||||||
|
|
||||||
b3 = Building { _buildingCost = 4
|
b3 = Building { _buildingCost = 4
|
||||||
, _buildingTitle = "Prison Yard"
|
, _buildingTitle = "Prison Yard"
|
||||||
, _buildingAction = Group [TakeResource [Rogue], TakeResource [Rogue]]
|
, _buildingAction = mrepeat 2 (TakeResource [Rogue])
|
||||||
, _buildingOwnerAction = TakeResource [Rogue]
|
, _buildingOwnerAction = TakeResource [Rogue]
|
||||||
, _buildingAccumType = NoAccumulation
|
, _buildingAccumType = NoAccumulation
|
||||||
}
|
}
|
||||||
|
|
||||||
b4 = Building { _buildingCost = 6
|
b4 = Building { _buildingCost = 6
|
||||||
, _buildingTitle = "Wizard School"
|
, _buildingTitle = "Wizard School"
|
||||||
, _buildingAction = Group [TakeResource [Wizard], TakeResource [Wizard]]
|
, _buildingAction = mrepeat 2 (TakeResource [Wizard])
|
||||||
, _buildingOwnerAction = TakeResource [Wizard]
|
, _buildingOwnerAction = TakeResource [Wizard]
|
||||||
, _buildingAccumType = NoAccumulation
|
, _buildingAccumType = NoAccumulation
|
||||||
}
|
}
|
||||||
|
|
||||||
b5 = Building { _buildingCost = 4
|
b5 = Building { _buildingCost = 4
|
||||||
, _buildingTitle = "Gold Mine"
|
, _buildingTitle = "Gold Mine"
|
||||||
, _buildingAction = Group $ replicate 4 $ TakeResource [Gold]
|
, _buildingAction = mrepeat 4 (TakeResource [Gold])
|
||||||
, _buildingOwnerAction = Group $ replicate 2 $ TakeResource [Gold]
|
, _buildingOwnerAction = mrepeat 4 (TakeResource [Gold])
|
||||||
, _buildingAccumType = NoAccumulation
|
, _buildingAccumType = NoAccumulation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import Control.Monad.Random
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
import Data.Monoid
|
||||||
import System.Random.Shuffle
|
import System.Random.Shuffle
|
||||||
import Waterdeep.Types
|
import Waterdeep.Types
|
||||||
import Waterdeep.Monad
|
import Waterdeep.Monad
|
||||||
|
|
@ -97,8 +98,25 @@ waterdeepGame = do
|
||||||
replicateM_ (3 + p) $ performAction p $ TakeResource [Gold]
|
replicateM_ (3 + p) $ performAction p $ TakeResource [Gold]
|
||||||
forM_ [1..8] $ \round -> do
|
forM_ [1..8] $ \round -> do
|
||||||
beginRound round
|
beginRound round
|
||||||
|
fix $ \loop -> do
|
||||||
|
p <- use gameCurrentPlayer
|
||||||
|
success <- filterChoices $ performAction p AssignAgent
|
||||||
|
case success of
|
||||||
|
True -> do
|
||||||
|
filterChoices $ performAction p CompleteQuest
|
||||||
|
gameCurrentPlayer .= (p `mod` length ps) + 1
|
||||||
|
gameConsecutivePasses .= 0
|
||||||
|
loop
|
||||||
|
False -> do
|
||||||
|
passes <- gameConsecutivePasses <+= 1
|
||||||
|
when (passes < length ps) loop
|
||||||
performAction 1 $ TakeResource [Cleric, Fighter, Rogue, Wizard, Gold]
|
performAction 1 $ TakeResource [Cleric, Fighter, Rogue, Wizard, Gold]
|
||||||
performAction 2 $ ChooseQuest
|
filterChoices $ performAction 1 $
|
||||||
|
ReturnResource [Wizard]
|
||||||
|
<> ReturnResource [Cleric, Fighter, Gold]
|
||||||
|
<> ReturnResource [Cleric, Fighter]
|
||||||
|
<> ReturnResource [Cleric]
|
||||||
|
-- performAction 2 $ ChooseQuest
|
||||||
scoreFinalPoints
|
scoreFinalPoints
|
||||||
notifyState
|
notifyState
|
||||||
determineWinners
|
determineWinners
|
||||||
|
|
@ -124,15 +142,54 @@ initialAgents players round =
|
||||||
if round >= 5 then startingAgents + 1 else startingAgents
|
if round >= 5 then startingAgents + 1 else startingAgents
|
||||||
where startingAgents = 6 - players
|
where startingAgents = 6 - players
|
||||||
|
|
||||||
|
data ContWD a where
|
||||||
|
Done :: (a, WaterdeepState) -> ContWD a
|
||||||
|
Cont :: WaterdeepPrompt b -> (b -> ContWD a) -> ContWD a
|
||||||
|
|
||||||
|
-- |Permit only choices which lead to a True result.
|
||||||
|
-- Returns False if and only if no such choice exists.
|
||||||
|
-- If result is False, state remains unchanged.
|
||||||
|
filterChoices :: Waterdeep Bool -> Waterdeep Bool
|
||||||
|
filterChoices m = do
|
||||||
|
mw' <- filteredChoice =<< (runWaterdeepC Done Cont m <$> get)
|
||||||
|
case mw' of
|
||||||
|
Just w' -> put w' >> return True
|
||||||
|
Nothing -> return False
|
||||||
|
where
|
||||||
|
tryChoice :: ContWD Bool -> Bool
|
||||||
|
tryChoice (Done a) = fst a
|
||||||
|
tryChoice (Cont (NotifyState w) cont) = tryChoice $ cont ()
|
||||||
|
tryChoice (Cont (SolicitChoice w p t cs) cont) =
|
||||||
|
or $ map (tryChoice . cont . snd) cs
|
||||||
|
filteredChoice :: ContWD Bool -> Waterdeep (Maybe WaterdeepState)
|
||||||
|
filteredChoice (Done (a,w)) = if a then return (Just w) else return Nothing
|
||||||
|
filteredChoice (Cont (NotifyState w) cont) = filteredChoice $ cont ()
|
||||||
|
filteredChoice (Cont (SolicitChoice w p t cs) cont) = do
|
||||||
|
let cs' = filter (tryChoice . cont . snd) cs
|
||||||
|
if null cs'
|
||||||
|
then return Nothing
|
||||||
|
else filteredChoice =<< (cont <$> (put w >> solicitChoice p t cs'))
|
||||||
|
|
||||||
performAction :: PlayerID -> GameAction -> Waterdeep Bool
|
performAction :: PlayerID -> GameAction -> Waterdeep Bool
|
||||||
performAction p a = case a of
|
performAction p a = case a of
|
||||||
|
NoAction -> return True
|
||||||
ScorePoints n -> do
|
ScorePoints n -> do
|
||||||
gamePlayer p . playerScore += n
|
gamePlayer p . playerScore += n
|
||||||
return True
|
return True
|
||||||
TakeResource rs -> do
|
TakeResource rs -> do
|
||||||
r <- solicitChoice p "Please choose one:" $ map (show &&& id) rs
|
r <- solicitChoice p "Take one item:" $ map (show &&& id) rs
|
||||||
gamePlayer p . playerTavern %= flip (M.unionWith (+)) (M.fromList [(r, 1)])
|
gamePlayer p . playerTavern %= flip (M.unionWith (+)) (M.fromList [(r, 1)])
|
||||||
return True
|
return True
|
||||||
|
ReturnResource rs -> do
|
||||||
|
tavern <- use $ gamePlayer p . playerTavern
|
||||||
|
let rs' = filter ((> 0) . maybe 0 id . flip M.lookup tavern) rs
|
||||||
|
if null rs'
|
||||||
|
then return False
|
||||||
|
else do
|
||||||
|
r <- solicitChoice p "Return one item:" $ map (show &&& id) rs'
|
||||||
|
let removeOne x = if x > 1 then Just (x-1) else Nothing
|
||||||
|
gamePlayer p . playerTavern %= M.update removeOne r
|
||||||
|
return True
|
||||||
ChooseQuest -> do
|
ChooseQuest -> do
|
||||||
qs <- use gameCliffwatchInn
|
qs <- use gameCliffwatchInn
|
||||||
if null qs
|
if null qs
|
||||||
|
|
@ -158,6 +215,8 @@ performAction p a = case a of
|
||||||
Just i -> do
|
Just i -> do
|
||||||
gamePlayer p . playerIntrigueCards %= (++[i])
|
gamePlayer p . playerIntrigueCards %= (++[i])
|
||||||
return True
|
return True
|
||||||
|
a1 `Then` a2 -> do
|
||||||
|
(&&) <$> performAction p a1 <*> performAction p a2
|
||||||
_ -> return False
|
_ -> return False
|
||||||
|
|
||||||
scoreFinalPoints :: Waterdeep ()
|
scoreFinalPoints :: Waterdeep ()
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ module Waterdeep.Types
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
import Data.IntMap (IntMap)
|
import Data.IntMap (IntMap)
|
||||||
import Data.Map (Map)
|
import Data.Map (Map)
|
||||||
|
import Data.Monoid
|
||||||
import System.Random (StdGen)
|
import System.Random (StdGen)
|
||||||
|
|
||||||
type PlayerID = Int
|
type PlayerID = Int
|
||||||
|
|
@ -192,7 +193,8 @@ data AccumulationType = NoAccumulation
|
||||||
| AccumulateResource Resource Int
|
| AccumulateResource Resource Int
|
||||||
deriving (Eq,Show)
|
deriving (Eq,Show)
|
||||||
|
|
||||||
data GameAction = ScorePoints Int
|
data GameAction = NoAction
|
||||||
|
| ScorePoints Int
|
||||||
| TakeResource [Resource]
|
| TakeResource [Resource]
|
||||||
| ReturnResource [Resource]
|
| ReturnResource [Resource]
|
||||||
| GiveResource [Resource]
|
| GiveResource [Resource]
|
||||||
|
|
@ -201,6 +203,7 @@ data GameAction = ScorePoints Int
|
||||||
| DrawQuest
|
| DrawQuest
|
||||||
| DrawNamedQuestType
|
| DrawNamedQuestType
|
||||||
| DistributeQuests
|
| DistributeQuests
|
||||||
|
| CompleteQuest
|
||||||
| ChooseAndCompleteQuest
|
| ChooseAndCompleteQuest
|
||||||
| DiscardUncompletedQuest
|
| DiscardUncompletedQuest
|
||||||
| BuyBuilding
|
| BuyBuilding
|
||||||
|
|
@ -220,18 +223,18 @@ data GameAction = ScorePoints Int
|
||||||
| TakeAccumulated
|
| TakeAccumulated
|
||||||
| TakeResourceFromOpponent [Resource]
|
| TakeResourceFromOpponent [Resource]
|
||||||
| AssignMandatoryQuest Quest
|
| AssignMandatoryQuest Quest
|
||||||
| Group [GameAction]
|
|
||||||
| Optional GameAction
|
| Optional GameAction
|
||||||
| ChooseFrom [GameAction]
|
| ChooseOne [GameAction]
|
||||||
| Transaction [GameAction]
|
| Then GameAction GameAction
|
||||||
| Repeat Int GameAction
|
| OrElse GameAction GameAction
|
||||||
| IfThenElse GameAction GameAction GameAction
|
|
||||||
| ForEachBuilding GameAction
|
| ForEachBuilding GameAction
|
||||||
| ForEachControlledBuilding GameAction
|
| ForEachControlledBuilding GameAction
|
||||||
| OneOpponent GameAction
|
| OneOpponent GameAction
|
||||||
| EachOpponent GameAction
|
| EachOpponent GameAction
|
||||||
| CurrentPlayer GameAction
|
| CurrentPlayer GameAction
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
infixr `Then`
|
||||||
|
infixr `OrElse`
|
||||||
|
|
||||||
data PlotQualifier = ActionProvides [Resource]
|
data PlotQualifier = ActionProvides [Resource]
|
||||||
| CompletesQuest [QuestType]
|
| CompletesQuest [QuestType]
|
||||||
|
|
@ -275,3 +278,10 @@ instance Eq Building where
|
||||||
|
|
||||||
instance Eq IntrigueCard where
|
instance Eq IntrigueCard where
|
||||||
a == b = a^.intrigueTitle == b^.intrigueTitle
|
a == b = a^.intrigueTitle == b^.intrigueTitle
|
||||||
|
|
||||||
|
instance Monoid GameAction where
|
||||||
|
mempty = NoAction
|
||||||
|
NoAction `mappend` x = x
|
||||||
|
x `mappend` NoAction = x
|
||||||
|
(x `Then` y) `mappend` z = x `Then` (y `mappend` z)
|
||||||
|
x `mappend` y = x `Then` y
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
module Waterdeep.Util
|
||||||
|
( mrepeat
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Monoid
|
||||||
|
|
||||||
|
mrepeat :: (Monoid m) => Int -> m -> m
|
||||||
|
mrepeat n m = mconcat $ replicate n m
|
||||||
Loading…
Reference in New Issue