hstzaar-0.8.2: src/AI/Minimax.hs
{-# LANGUAGE BangPatterns #-}
module AI.Minimax( EvalFunc
, negamaxStrategy
, negamax
, negamax_ab
, negamaxPV
) where
import AI.Utils
import Board
-- import Debug.Trace
-- | type of static evaluation functions
type EvalFunc = Board -> Int
-- | Negamax with alpha-beta and static depth prunning
negamaxStrategy :: Int -> EvalFunc -> Strategy
negamaxStrategy n evf bt rndgen
| isEmptyTree bt = error "negamaxStrategy: empty tree"
negamaxStrategy n evf bt rndgen
= ((m1,m2), rndgen)
where (bestscore, m1:m2:_) = negamaxPV bt'
bt' = pruneDepth n $ -- ^ prune to depth `n'
mapTree evf bt -- ^ apply static evaluation function
-- | Naive negamax algorithm (not used)
-- | nodes values are static evaluation scores
negamax :: (Num a, Ord a) => GameTree a m -> a
negamax = negamax' 0
negamax' :: (Num a, Ord a) => Int -> GameTree a m -> a
negamax' depth (GameTree x []) = x
negamax' depth (GameTree _ branches)
| odd depth = - minimum vs
| otherwise = maximum vs
where vs = map (negamax' (1+depth) . snd) branches
-- | Negamax with alpha-beta prunning
negamax_ab :: (Num a, Ord a) => a -> a -> GameTree a m -> a
negamax_ab = negamax_ab' 0
negamax_ab' :: (Num a, Ord a) => Int -> a -> a -> GameTree a m -> a
negamax_ab' depth a b (GameTree x []) = a `max` x `min` b
negamax_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 = -negamax_ab' (1+depth) (-b) (-a) t
| otherwise = negamax_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
instance Num PV where
(+) = undefined
(-) = undefined
(*) = undefined
fromInteger = undefined
signum = undefined
abs = undefined
negate (PV x ms) = PV (-x) ms
{-
negatePV :: PV -> PV
negatePV (PV x ms) = PV (-x) ms
-}
-- | Negamax with alpha-beta pruning
-- | extended with score and principal variation
negamaxPV :: GameTree Int Move -> (Int, [Move])
negamaxPV bt
= case negamaxPV_ab 0 [] lo hi bt of
PV v ms -> (v, reverse ms)
where lo = PV (-maxBound) []
hi = PV maxBound []
-- | depth parameter determines if we negate children scores
-- | negamaxPV_ab :: (Num a, Ord a) => Int -> [m] -> a -> a -> GameTree a m -> (a, [m])
negamaxPV_ab depth ms a b (GameTree x []) = a `max` PV x ms `min` b
negamaxPV_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' = if odd depth
then - negamaxPV_ab (1+depth) (m:ms) (-b) (-a) t
else negamaxPV_ab (1+depth) (m:ms) a b t