Principal variation search
From HaskellWiki
(Difference between revisions)
m |
m (category) |
||
| (One intermediate revision not shown.) | |||
| Line 147: | Line 147: | ||
--test24 = pvs (-1000) 1000 [] | --test24 = pvs (-1000) 1000 [] | ||
</haskell> | </haskell> | ||
| + | |||
| + | [[Category:Code]] | ||
Current revision
{-# OPTIONS -fglasgow-exts #-} module PVS (pvs) where import Data.Tree import Debug.Trace import Test.QuickCheck import System.Random import Control.Monad -- Top node reflects the current position b x = Node x [] testt = Node ("p1_1",1) [Node ("p2_2_1",2) [b $ ("p1_3_1",-7), b $ ("p1_3_2",-2)], Node ("p2_2_1",3) [b $ ("p1_3_1",-4), b $ ("p1_3_2",-6)], Node ("p2_2_2",4) [b $ ("p1_3_3",-2), b $ ("p1_3_4",-5)] ] showTree x = putStr $ drawTree $ fmap show x test2 = fmap show testt test3 = putStr $ drawTree test2 -- straightforward minimax (not even with alpha beta) negamax node = case node of Node (move,v) [] -> ([move],v) Node (move,_) (n:nn) -> (move:pvm, pvv) where (pvm, pvv) = negaLevel (neg (negamax n)) nn where negaLevel prev_best@(_,old_v) (n:nn) = negaLevel best4 nn where best4 = case neg $ negamax n of value@(_,v) | v > old_v -> value | otherwise -> prev_best negaLevel best _ = best neg (m,v) = (m,-v) -- Normal alpha beta alpha_beta alpha beta node = case node of Node (move,v) [] -> ([move],v) Node (move,_) nn -> (move:pvm, pvv) where (pvm, pvv) = negaLevel ([1010101],-1000) alpha beta nn where negaLevel prev_best@(_,v1) prev_alpha beta (n:nn) | v1 < beta = negaLevel best4 alpha beta nn where best4 = case neg $ alpha_beta (-beta) (-alpha) n of value@(_,v2) | (v2 > v1) -> value | otherwise -> prev_best alpha = if v1 > prev_alpha then v1 else prev_alpha negaLevel best alpha beta _ = best neg (m,v) = (m,-v) -- Principle variation search -- the search continues as long as alpha < pvs < beta -- as soon pvs hits one these bounds the search stops and returns best pvs :: (Num a1, Ord a1) => a1 -> a1 -> [Tree (a, a1)] -> ([a], a1) pvs alpha beta (n:nn) = case negpvs (-beta) (-alpha) n of best -> negaLevel best alpha beta nn where negaLevel prev_best@(_,v1) prev_alpha beta (n:nn) | v1 < beta = negaLevel best4 alpha beta nn where best4 = case negpvs (-alpha - 1) (-alpha) n of value@(_,v2) | (alpha < v2) && (v2 < beta) -> negpvs (-beta) (-v2) n | (v2 > v1) -> value | otherwise -> prev_best alpha = if v1 > prev_alpha then v1 else prev_alpha negaLevel best alpha beta _ = best negpvs alpha beta node = case node of Node (move,v) [] -> ([move], -v) Node (move,_) nn -> (move:pvm, -pvv) where (pvm, pvv) = pvs alpha beta nn pvs _ _ _ = error "PV Search called with empty list" pvs_topnode (Node (move,v) []) = ([move],v) pvs_topnode (Node (move,_) nn) = case pvs (-10000) 10000 nn of (pvm, pvv) -> (move:pvm, pvv) test5 = pvs_topnode testt test6 = negamax testt rtest n = generate 1000 (mkStdGen n) test7 = rtest 4 $ choose (1, 10) test8 = rtest 4 $ (vector 5 :: Gen [Int]) -- Twenty numbers from 1 to 10. test9 = rtest 4 $ sequence [ choose (1,10) | i <- [1..20] ] instance (Arbitrary x) => Arbitrary (Tree x) where arbitrary = sized tree' where tree' 0 = liftM leaf arbitrary tree' n | n>0 = oneof [liftM leaf arbitrary, liftM2 Node arbitrary leaves] where subtree = tree' (n `div` 2) leaves = do n <- choose (1,5) sequence [ subtree | i <- [1..n] ] leaf = flip Node [] coarbitrary (Node v nodes) = variant 0 . coarbitrary v .coarbitrary nodes test10 :: Tree (Int, Int) test10 = rtest 14 $ arbitrary test11 = showTree test10 test12 = pvs_topnode test10 test13 = negamax test10 prop_SameResult :: Tree (Int,Int) -> Bool prop_SameResult node = case pvs' == negamax' of True -> True False -> trace (show (pvs', negamax')) False where pvs' = pvs_topnode node negamax' = negamax node test14 = quickCheck prop_SameResult test15 = verboseCheck prop_SameResult test16 = test prop_SameResult -- beta sets the maximum best score. -- test17 returns -6 because there is a lower node that returns -6, which is good enough -- we don't need to search further. -- i.e. test17 <= beta if there is such a score. test17 = pvs (-1000) (-6) nodes where Node _ nodes = testt -- alpha sets the minimum best score. -- presumably alpha <= test17 <= beta if there is a valid path for this test18 = pvs (-1000) 1000 nodes where Node _ nodes = testt test19 = pvs (-4) 1000 nodes where Node _ nodes = testt test20 = pvs (-100001) (-100000) nodes where Node _ nodes = testt test21 = pvs 100000 100001 nodes where Node _ nodes = testt testt1 = Node ("p1_1",1) [Node ("p2_2_1",2) [b $ ("p1_3_1",-7)], Node ("p2_2_2",4) [b $ ("p1_3_3",-2), b $ ("p1_3_4",-5)] ] test22 = pvs (-1000) 1000 nodes where Node _ nodes = testt1 testt2 = Node ("p1_1",1) [Node ("p2_2_2",4) [b $ ("p1_3_3",-2)] ] test23 = pvs (-1000) 1000 nodes where Node _ nodes = testt2 --the only failing test, and it makes sense that this fails. --test24 = pvs (-1000) 1000 []
