packages feed

hstzaar-0.7: src/AI/Minimax.hs

module AI.Minimax( EvalFunc
                 , minimaxStrategy
                 , minimax
                 , minimax_ab
                 , minimaxPV
                 ) where

import AI.Utils
import Board

-- | type of static evaluation functions
type EvalFunc = Board -> Int  

-- | Minimax with alpha-beta and static depth prunning 
minimaxStrategy :: Int -> EvalFunc -> Strategy
minimaxStrategy n eval bt rndgen 
    | isEmptyTree bt = error "minimaxStrategy: empty tree"
minimaxStrategy n eval bt rndgen = ((m1,m2), rndgen)
    where (bestscore, m1:m2:_) = minimaxPV bt'
          bt' = pruneDepth n $        -- ^ prune to depth `n'
                mapTree eval bt       -- ^ apply static evaluation function



-- | Naive minimax algorithm (not used)
-- | nodes values are static evaluation scores
minimax ::  (Num a, Ord a) => GameTree a m -> a 
minimax = minimax' 0

minimax' :: (Num a, Ord a) => Int -> GameTree a m -> a 
minimax' depth (GameTree x []) = x
minimax' depth (GameTree _ branches) 
    | odd depth = - minimum vs
    | otherwise = maximum vs
    where vs = map (minimax' (1+depth) . snd) branches



-- | Minimax with alpha-beta prunning
minimax_ab ::  (Num a, Ord a) => a -> a -> GameTree a m -> a
minimax_ab = minimax_ab' 0

minimax_ab' :: (Num a, Ord a) => Int -> a -> a -> GameTree a m -> a
minimax_ab' depth a b (GameTree  x [])       = a `max` x `min` b
minimax_ab' depth a b (GameTree _ branches) = cmx a b (map snd branches)
    where cmx a b []  = a
          cmx a b (t:ts) | a'==b     = a'
                         | otherwise = cmx a' b ts
                         where a' | odd depth = -minimax_ab' (1+depth) (-b) (-a) t
                                  | otherwise =  minimax_ab' (1+depth) a b t

-- | Principal Variantions
data PV = PV !Int [Move] deriving (Show)

instance Eq PV where
    (PV x _) == (PV y _) = x==y

instance Ord PV where
    compare (PV x _) (PV y _) = compare x y

negatePV :: PV -> PV
negatePV (PV x ms) = PV (-x) ms


-- | Minimax with alpha-beta pruning
-- | extended with score and principal variation 
minimaxPV :: GameTree Int Move -> (Int, [Move])
minimaxPV bt 
    = case minimaxPV_ab' 0 [] (PV (-infinity-1) []) (PV (infinity+1) []) bt of
        PV v ms -> (v,ms)

-- | first parameter determines if we negate children scores
-- | minimaxPV_ab' :: (Num a, Ord a) => Int -> [m] -> a -> a  -> GameTree a m -> (a, [m])
minimaxPV_ab' depth ms a b (GameTree x []) = a `max` PV x (reverse ms) `min` b
minimaxPV_ab' depth ms a b (GameTree _ branches) = cmx a b branches
    where cmx a b [] = a
          cmx a b ((m,t) : branches) 
              | a'==b = a'
              | otherwise = cmx a' b branches
              where a'| odd depth = negatePV $ minimaxPV_ab' (1+depth) (m:ms) (negatePV b) (negatePV a) t
                      | otherwise = minimaxPV_ab' (1+depth) (m:ms) a b t