smcdel-1.2.0: src/SMCDEL/Examples/Toynabi.hs
module SMCDEL.Examples.Toynabi where
import Data.List ((\\),sort)
import SMCDEL.Language
import SMCDEL.Symbolic.S5
import SMCDEL.Explicit.S5
import SMCDEL.Translations.S5
import SMCDEL.Other.Planning
-- | Given nPlayers and nCards in total, this proposition encodes that player i has card c in position p.
-- Note that we start counting players with 1, but cards and positions with 0.
cardIsAt :: Int -> Int -> Int -> Int -> Int -> Prp
cardIsAt nPlayers nCards i c p = P ((i * nCards * nPositions) + (c * nPositions) + p) where
nPositions = ceiling (fromIntegral nCards / fromIntegral nPlayers :: Double)
-- | A proposition to say that the card c should be played next.
toPlay :: Int -> Int -> Int -> Prp
toPlay nPlayers nCards c = toEnum . (+(1+c)) . fromEnum $ cardIsAt nPlayers nCards nPlayers (nCards-1) (nPositions-1) where
nPositions = ceiling (fromIntegral nCards / fromIntegral nPlayers :: Double)
-- | This proposition says that the game has ended.
done :: Int -> Int -> Prp
done nPlayers nCards = toPlay nPlayers nCards nCards
-- | Print and translate the vocabulary in both directions, just for debugging.
vocab :: Int -> Int -> IO ()
vocab nPlayers nCards = do
mapM_ printExplain (vocabOf $ toynabiStartFor nPlayers nCards)
putStrLn "--"
mapM_ putStrLn [ show i ++ " has " ++ show c ++ " @ " ++ show p ++ "\t " ++ show (cardIsAt nPlayers nCards i c p) | i <- players, c <- cards, p <- positions ]
mapM_ putStrLn [ "toPlay: " ++ show c ++ "\t" ++ show (toPlay nPlayers nCards c) | c <- cards ]
mapM_ putStrLn [ "done! \t" ++ show (toPlay nPlayers nCards nCards) ]
where
printExplain p = putStrLn $ show p ++ "\t" ++ explain nPlayers nCards p
players = [1..nPlayers]
cards = [0..(nCards-1)]
nPositions = ceiling (fromIntegral nCards / fromIntegral nPlayers :: Double)
positions = [0..(nPositions-1)]
explain :: Int -> Int -> Prp -> String
explain nPlayers nCards (P k)
| P k == toPlay nPlayers nCards nCards = "done!"
| P k >= toPlay nPlayers nCards 0 = "to play: " ++ show (k +1 - fromEnum (toPlay nPlayers nCards 1)) -- TODO test and check this!
| otherwise = show i ++ " has " ++ show c ++ " @ " ++ show p where
nPositions = ceiling (fromIntegral nCards / fromIntegral nPlayers :: Double)
i = k `div` (nCards * nPositions)
c = (k - (i * nCards * nPositions)) `div` nPositions
p = k - (i * nCards * nPositions) - (c * nPositions)
explainState :: Int -> Int -> State -> String
explainState nPlayers nCards state = show [ [ c | p <- positions, c <- cards, cardIsAt nPlayers nCards i c p `elem` state ] | i <- players ] ++ tP where
players = [1..nPlayers]
cards = [0..(nCards-1)]
nPositions = ceiling (fromIntegral nCards / fromIntegral nPlayers :: Double)
positions = [0..(nPositions-1)]
tP = concat $ [ " to play: " ++ show c | c <- cards, toPlay nPlayers nCards c `elem` state ]
++ [ "done!" | toPlay nPlayers nCards nCards `elem` state ]
-- | The starting knowledge structure.
toynabiStartFor :: Int -> Int -> MultipointedKnowScene
toynabiStartFor nPlayers nCards = (KnS voc law obs, cur) where
players = [1..nPlayers]
cards = [0..(nCards-1)]
nPositions = ceiling (fromIntegral nCards / fromIntegral nPlayers :: Double)
positions = [0..(nPositions-1)]
voc = sort $ [ cardIsAt nPlayers nCards i c p | i <- players, c <- cards, p <- positions ]
++ [ toPlay nPlayers nCards c | c <- cards ]
++ [ done nPlayers nCards ]
law = boolBddOf $ Conj $
-- The next card to be played is 0:
[ (if c==0 then id else Neg) $ PrpF $ toPlay nPlayers nCards c | c <- cards ++ [nCards] ]
-- Each card must be at someone in some position, but not two:
++ [ Disj [ PrpF $ cardIsAt nPlayers nCards i c p | i <- players, p <- positions ] | c <- cards ]
++ [ Neg $ Conj [ PrpF $ cardIsAt nPlayers nCards i c p
, PrpF $ cardIsAt nPlayers nCards j c q ] | c <- cards
, i <- players, j <- players
, p <- positions, q <- positions
, i /= j || p /= q ]
-- For each player each position can only hold one card:
++ [ Neg $ Conj [ PrpF $ cardIsAt nPlayers nCards i c p
, PrpF $ cardIsAt nPlayers nCards i d p ] | c <- cards, d <- cards
, c /= d
, i <- players
, p <- positions ]
-- If nPlayers * nPositions > nCards then there are empty positions:
++ [ Conj [ Neg $ PrpF $ cardIsAt nPlayers nCards i c p ] | (i,p) <- drop nCards [ (i,p) | i <- players, p <- positions ], c <- cards ]
obs = [ (show i, voc \\ [ cardIsAt nPlayers nCards i c p | c <- cards, p <- positions ]) | i <- players ]
cur = boolBddOf $ Conj $
-- distribute all the cards:
[ PrpF $ cardIsAt nPlayers nCards i c p | (c,(i,p)) <- zip cards [ (i,p) | i <- players, p <- positions ] ] ++
-- set toPlay to 0:
[ (if c == 0 then id else Neg) $ PrpF $ toPlay nPlayers nCards c | c <- cards ]
-- | Tell agent i that they have card c at position p.
tell :: Int -> Int -> Int -> Int -> Int -> Labelled MultipointedEvent
tell nPlayers nCards i c p = (label, (KnTrf addprops addlaw changeLaw addObs, boolBddOf Top)) where
label = "tell " ++ show i ++ " card " ++ show c ++ " @ " ++ show p
addprops = [ ]
addlaw = PrpF $ cardIsAt nPlayers nCards i c p
changeLaw = [ ]
addObs = [ (show j, []) | j <- players ]
players = [1..nPlayers]
-- | Let agent i play the card at position p.
play :: Int -> Int -> Int -> Int -> Labelled MultipointedEvent
play nPlayers nCards i p = (label, (KnTrf addprops addlaw changeLaw addObs, boolBddOf Top)) where
label = show i ++ " plays position " ++ show p
addprops = [ ]
addlaw = Disj [ Conj [ PrpF $ toPlay nPlayers nCards c
, PrpF $ cardIsAt nPlayers nCards i c p ]
| c <- cards ]
changeLaw = [ (cardIsAt nPlayers nCards i c p, boolBddOf Bot) | c <- cards ] -- remove card from that position
++ [ ( toPlay nPlayers nCards 0, boolBddOf $ ite (tP 0) Bot (tP 0) ) ] -- if first card is played
++ [ ( toPlay nPlayers nCards c, boolBddOf $ ite (tP c) Bot (ite (tP (c-1)) Top (tP c)) ) | c <- cards, c > 0 ] -- increase toPlay
++ [ ( toPlay nPlayers nCards nCards, boolBddOf $ ite (tP (nCards-1)) Top Bot ) | c <- cards, c > 0 ] -- set done
addObs = [ (show j, []) | j <- players ]
players = [1..nPlayers]
tP c = PrpF $ toPlay nPlayers nCards c
cards = [0..(nCards-1)]
-- | The goal.
toynabiGoal :: Int -> Int -> Form
toynabiGoal nPlayers nCards = PrpF $ done nPlayers nCards
-- | The whole cooperative planning task.
toynabi :: Int -> Int -> CoopTask MultipointedKnowScene MultipointedEvent
toynabi nPlayers nCards = CoopTask (toynabiStartFor nPlayers nCards) actions (toynabiGoal nPlayers nCards) where
actions = [ (show i, tell nPlayers nCards j c p) | i <- players, j <- players, i /= j, c <- cards, p <- positions ] ++
[ (show i, play nPlayers nCards i p) | i <- players, p <- positions ]
players = [1..nPlayers]
cards = [0..(nCards-1)]
nPositions = ceiling (fromIntegral nCards / fromIntegral nPlayers :: Double)
positions = [0..(nPositions-1)]
-- | An example result to test whether updating works, translated to a Kripke model.
exampleResult :: PointedModelS5
exampleResult = generatedSubmodel $ knsToKripke (fst theKNS, head $ statesOf $ fst theKNS) where
theKNS = toynabiStartFor 2 4
`update` snd (tell 2 4 1 0 0)
`update` snd (play 2 4 1 0)
`update` snd (play 2 4 1 1)