packages feed

hanabi-dealer-0.15.1.1: Game/Hanabi/Strategies/EndGameOld.hs

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
module Game.Hanabi.Strategies.EndGameOld(endGameMoveOld, EndGameOld(..), EndGameMirrorOld(..), egmo) where
import Game.Hanabi
import Data.List(maximumBy, delete)
import Data.Function(on)

-- | @'EGS' f p ps@ usually behaves based on @p@, but it conducts the exhaustive search assuming that others behave based on @ps@ when the deck size is @f@ or below @f@.
--   @move pvs mvs (EGO f p ps)@ may cause an error if @p@ can choose an invalid move.
data EndGameOld p ps = EGO {fromWhen::PublicInfo->Bool, myUsualStrategy::p, otherPlayers::ps}

instance (Monad m, Strategy p m, Strategies ps m) => Strategy (EndGameOld p ps) m where
  strategyName ms = return "EndGameOld"
  move pvs@(pv:_) mvs str@(EGO f p ps) | f (publicView pv)            = do (defaultMove, _) <- move pvs mvs p
                                                                           m <- endGameMoveOld pvs mvs (ps, [EGO f p ps]) $ defaultMove : delete defaultMove (effectiveMoves pv)
                                                                           return (m,str)
                                       | otherwise                    = do (m,_) <- move pvs mvs p
                                                                           return (m,str)

-- | 'EndGameMirrorStrategy' assumes that other players think in the same way as itself during endgame.
--   @move pvs mvs (EGMO (EGO f p ps))@ may cause an error if @p@ can choose an invalid move.
data EndGameMirrorOld p = EGMO (EndGameOld p [EndGameMirrorOld p])
egmo :: (PublicInfo -> Bool) -- ^ from when to start the endgame search
     -> p                    -- ^ the default strategy used until endgame
     -> Int                  -- ^ number of players, including the resulting player
     -> EndGameMirrorOld p
egmo from p nump = egmo where egmo = EGMO (EGO from p $ replicate (pred nump) egmo)

instance (Monad m, Strategy p m) => Strategy (EndGameMirrorOld p) m where
  strategyName ms = return "EndGameMirrorOld"
  move pvs mvs (EGMO egs) = do (m, egs') <- move pvs mvs egs
                               return (m, EGMO egs')

endGameMoveOld :: (Monad m, Strategies ps m) =>
               [PrivateView] -- ^ view history
               -> [Move]     -- ^ move history
               -> ps
               -> [Move]     -- ^ move candidates. More promising moves appear earlier.
               -> m Move
endGameMoveOld pvs@(pv:tlpvs) mvs ps candidates = do
                                        let states = possibleStates pv
                                        scores <- mapM (evalMove states (map publicView tlpvs) mvs ps) candidates
                                        let asc = zip scores candidates
                                            pub = publicView pv
                                            achievable = moreStrictlyAchievableScore pub
                                            --  ToDo:  Also consider critical cards at the bottom deck.
                                        return $ case lookup (achievable * length states) asc of Nothing -> snd $ maximumBy (compare `on` fst) $ reverse asc
                                                                                                 Just k  -> k -- Stop search when the best possible score is found.
evalMove :: (Monad m, Strategies ps m) => [(State, Int)] -> [PublicInfo] -> [Move] -> ps -> Move -> m Int
evalMove states pubs@(pub:_) mvs ps mv = fmap (sum . map (\(((eg,st:_,_),_),n) -> n * egToInt st eg)) $ mapM (\(st,n) -> fmap (\a->(a,n)) $ tryAMove (stateToStateHistory pubs mvs st) mvs ps mv) states

-- | 'tryAMove' tries a 'Move' and then simulate the game to the end, using given 'Strategies'. Running this with empty history, such as @tryAMove [st] [] strs m@ is possible, but that assumes other strategies does not depend on the history.
tryAMove :: (Monad m, Strategies ps m) => [State] -> [Move] -> ps -> Move -> m ((EndGame, [State], [Move]),ps)
tryAMove states@(st:_) mvs strs mov = case proceed st mov of Nothing -> error $ show mov ++ ": invalid move!"
                                                             Just st -> let nxt = rotate 1 st
                                                                        in case checkEndGame $ publicState nxt of Nothing -> runSilently (nxt:states) (mov:mvs) strs
                                                                                                                  Just eg -> return ((eg, nxt:states, mov:mvs), strs)