hstzaar-0.9: src/AI/Minimax.hs
{-# LANGUAGE BangPatterns #-}
module AI.Minimax( negamaxStrategy
, negamax
, negamax_ab
, negamaxPV
) where
import AI.Tree
import Board
-- import Debug.Trace
-- | Negamax with alpha-beta and static depth prunning
negamaxStrategy :: Int -> Eval -> Strategy
negamaxStrategy depth eval bt rndgen
| isEmptyTree bt = error "negamaxStrategy: empty tree"
negamaxStrategy depth eval bt rndgen
= (score, m, rndgen)
where (score, m:_) = negamaxPV $
pruneDepth depth $ -- ^ prune evaluation tree
mapTree eval 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 (GameTree x []) = x
negamax (GameTree _ branches) = - minimum vs
where vs = map (negamax . snd) branches
-- | Negamax with alpha-beta prunning;
-- computes the minimax value but not the best move
negamax_ab :: (Num a, Ord a) => a -> a -> GameTree a m -> a
negamax_ab a b (GameTree x []) = a `max` x `min` b
negamax_ab 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' = - negamax_ab (-b) (-a) t
-- | pair a evaluation score with something
newtype PV a = PV (Int,a) deriving (Show)
instance Eq (PV a) where
(PV (x,_)) == (PV (y,_)) = x==y
instance Ord (PV a) where
compare (PV (x,_)) (PV (y,_)) = compare x y
instance Show a => Num (PV a) where
(+) = undefined
(-) = undefined
(*) = undefined
fromInteger = undefined
signum = undefined
abs = undefined
negate (PV (x,m)) = PV (negate x,m)
-- | Negamax with alpha-beta pruning
-- computes both minimax value and the best move (start of principal variation)
negamaxPV :: GameTree Int Move -> (Int, [Move])
negamaxPV bt
= case negamaxPV_ab [] lo hi bt of
PV (v,ms) -> (v,reverse ms)
where lo = PV (-infinity, []) -- dummy bounds
hi = PV ( infinity, [])
-- m = fst (head branches)
negamaxPV_ab :: [Move] -> PV [Move] -> PV [Move] -> GameTree Int Move -> PV [Move]
negamaxPV_ab ms a b (GameTree x []) = a `max` (PV (x,ms)) `min` b
negamaxPV_ab ms a b (GameTree _ branches) = cmx a b branches
where
cmx x y [] = x
cmx x y ((m,t) : rest)
| x'==y = x'
| otherwise = cmx x' y rest
where x' = - negamaxPV_ab (m:ms) (-y) (-x) t