implement sequencing of GameActions with a Monoid interface

This commit is contained in:
Jesse D. McDonald 2014-04-13 01:54:20 -05:00
parent fe3c7e54e7
commit 4898dea404
4 changed files with 103 additions and 26 deletions

View File

@ -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
} }

View File

@ -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 ()

View File

@ -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

8
src/Waterdeep/Util.hs Normal file
View File

@ -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