hanabi-dealer-0.15.1.1: Game/Hanabi/Strategies/StatefulStrategy.hs
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
module Game.Hanabi.Strategies.StatefulStrategy where
import Game.Hanabi hiding (main)
import Game.Hanabi.Strategies.SimpleStrategy
import System.Random
import Data.Maybe(isNothing)
import Data.List(sortOn, tails)
import Data.Bits(bit, (.&.), complement)
import qualified Data.IntMap as IM
-- An example of a strategy with state.
data Stateful s = SF {guessColorToPlay::Bool, guessPositionalDrop::Bool, baseStrategy::s, anns::[Annotation]}
sfs smpl = SF True True smpl []
lookupOn :: Eq b => (a -> b) -> a -> [a] -> [a]
lookupOn fun key xs = [ result | result <- xs, fun key == fun result ]
instance (Monad m, Strategy s m) => Strategy (Stateful s) m where
strategyName ms = ms >>= \sf -> strategyName (return $ baseStrategy sf) >>= \name -> return ("Stateful strategy of "++name)
move pvs@(pv:tlpvs) mvs (SF ctp pd s lastGuess) = let
numP = numPlayers $ gameSpec pub
pub = publicView pv
lG | turn pub < numP = []
| otherwise = lastGuess
consistentGuesses = [ [ case lookupOn ixDeck realAnn $ (if ctp then colorToPlay numP pvs mvs else []) ++ (if pd then positionalDrop numP pvs mvs else []) ++ lG of
Ann{possibilities=guessedPos}:_ | not $ isObviouslyInconsistent pub newPos -> newAnn
where newAnn = realAnn{possibilities = newPos}
newPos = realPos .&. guessedPos
_ -> realAnn
| realAnn@Ann{possibilities=realPos} <- anns ]
| anns <- annotations pub ]
in move (pv{publicView=pub{annotations=consistentGuesses}} : tlpvs) mvs s >>= \(mov, s') -> return (mov, SF ctp pd s' $ concat consistentGuesses)
colorToPlay numP pvs mvs = [ focusedAnn{possibilities = rankToQit (succ $ achievedRank pub hintedColor)} -- This is undefined when achievedRank pub c == K5, but then isDefinitelyUnplayable pv ann should be True.
| (p, pv, Hint q (Left hintedColor)) <- zip3 [1..] pvs $ take numP mvs,
let pub = publicView pv,
let ix = (q-p) `mod` numP
anns = annotations pub !! ix
cards = (repeat undefined:handsPV pv) !! ix,
let hintedTups@((focusedAnn,focusedCard):_) = [ tup | tup@(Ann{marks=(Just i, _)}, _) <- zip anns cards, hintedColor==i ],
not $ any (isObviouslyPlayable pub . possibilities . fst) hintedTups, -- Exclude if there is a playable card with the color.
if p==q then not $ isDefinitelyUnplayable pv focusedAnn else isPlayable pub focusedCard -- Exclude if the focused card is definitely unplayable.
]
positionalDrop numP pvs mvs = concat $ zipWith3 (positionalDrop' numP) [0,-1..1-numP] (tails pvs) mvs
positionalDrop' numP rot (pv:lastpv:_) mv = case mv of
Drop i | i `notElem` unusualChops || length anns <= i -> [] -- It is not a positional drop if nothing is unusual or the corresponding card does not exist.
| if rot==0 then not (isDefinitelyUnplayable pv focused)
else isPlayable pub (hand !! i) -> [focused{possibilities=nextToPlay pub}]
| otherwise -> [focused{possibilities = complement $ critical pub}] -- Suggest dropping the card by telling that it is not critical when almost everything is marked.
where pub = publicView pv
lastpub = publicView lastpv
unusualChops = drop 1 $ concat $ map reverse $ obviousChopss lastpub lastAnns
lastAnns = annotations lastpub !! (pred rot `mod` numP)
anns = annotations pub !! player
hand = handsPV pv !! pred player
player = rot `mod` numP
focused = anns!!i
_ -> []
main = do g <- newStdGen
((eg,_),_) <- start defaultGS [] ([sfs (S False)],[stdio]) g -- Play it with standard I/O (human player).
-- ((eg,_),_) <- start defaultGS [peek] [sfs (S False), sfs (S False)] g -- Play it with itself.
putStrLn $ prettyEndGame eg