@@ -15,6 +15,7 @@ module AI.AlphaBeta where
15
15
import Control.Monad
16
16
import Control.Monad.State
17
17
import Control.Monad.Except
18
+ import Control.Monad.Catch
18
19
import qualified Control.Monad.Metrics as Metrics
19
20
import Control.Concurrent.STM
20
21
import Data.Maybe
@@ -69,22 +70,33 @@ scoreMove (ai@(AlphaBeta params rules eval), var, side, dp, board, pm) = do
69
70
score <- Metrics. timed " ai.score.move" $ do
70
71
let board' = applyMoveActions (pmResult pm) board
71
72
score <- doScore rules eval var params (opposite side) dp board'
73
+ `catchError` (\ (e :: Error ) -> do
74
+ $ info " doScore: move {}, depth {}: {}" (show pm, dpTarget dp, show e)
75
+ throwError e
76
+ )
72
77
$ info " Check: {} (depth {}) => {}" (show pm, dpTarget dp, show score)
73
78
return score
74
79
75
80
return (pmMove pm, score)
76
81
77
- runAI :: (GameRules rules , Evaluator eval ) => AlphaBeta rules eval -> AICacheHandle rules eval -> Side -> Board -> Checkers ([Move ], Score )
78
- runAI ai@ (AlphaBeta params rules eval) handle side board = iterator
82
+ type AiIterationInput = (AlphaBetaParams , Maybe AiIterationOutput )
83
+ type AiIterationOutput = [(Move , Score )]
84
+ type AiOutput = ([Move ], Score )
85
+
86
+ runAI :: (GameRules rules , Evaluator eval ) => AlphaBeta rules eval -> AICacheHandle rules eval -> Side -> Board -> Checkers AiOutput
87
+ runAI ai@ (AlphaBeta params rules eval) handle side board = do
88
+ options <- controller
89
+ select options
79
90
where
80
- iterator = case abBaseTime params of
91
+ controller :: Checkers AiIterationOutput
92
+ controller = case abBaseTime params of
81
93
Nothing -> do
82
94
(result, _) <- go (params, Nothing )
83
95
return result
84
96
Just time -> repeatTimed' " runAI" time goTimed (params, Nothing )
85
97
86
- goTimed :: ( AlphaBetaParams , Maybe ([ Move ], Score ))
87
- -> Checkers (([ Move ], Score ), Maybe ( AlphaBetaParams , Maybe ([ Move ], Score )) )
98
+ goTimed :: AiIterationInput
99
+ -> Checkers (AiIterationOutput , Maybe AiIterationInput )
88
100
goTimed (params, prevResult) = do
89
101
ret <- tryC $ go (params, prevResult)
90
102
case ret of
@@ -94,11 +106,11 @@ runAI ai@(AlphaBeta params rules eval) handle side board = iterator
94
106
Just result -> return (result, Nothing )
95
107
Nothing -> do
96
108
let moves = map pmMove $ possibleMoves rules side board
97
- return ((moves , 0 ), Nothing )
109
+ return ([(move , 0 ) | move <- moves] , Nothing )
98
110
Left err -> throwError err
99
111
100
- go :: ( AlphaBetaParams , Maybe ([ Move ], Score ))
101
- -> Checkers (([ Move ], Score ), Maybe ( AlphaBetaParams , Maybe ([ Move ], Score )) )
112
+ go :: AiIterationInput
113
+ -> Checkers (AiIterationOutput , Maybe AiIterationInput )
102
114
go (params, prevResult) = do
103
115
let depth = abDepth params
104
116
let moves = possibleMoves rules side board
@@ -108,7 +120,7 @@ runAI ai@(AlphaBeta params rules eval) handle side board = iterator
108
120
-- currently we do not use results of evaluating of all moves
109
121
-- when evaluating deeper parts of the tree (it is hard due to alpha-beta restrictions).
110
122
-- It means we are not going to use that Score value anyway.
111
- return (( map pmMove moves , 0 ), Nothing )
123
+ return ([( pmMove move , 0 ) | move <- moves] , Nothing )
112
124
113
125
else do
114
126
let var = aichData handle
@@ -121,13 +133,31 @@ runAI ai@(AlphaBeta params rules eval) handle side board = iterator
121
133
, dpMin = fromMaybe depth (abStartDepth params)
122
134
}
123
135
let inputs = [(ai, handle, side, dp, board, move) | move <- moves]
124
- scores <- process processor inputs
125
- let select = if side == First then maximum else minimum
126
- maxScore = select $ map snd scores
127
- goodMoves = [move | (move, score) <- scores, score == maxScore]
128
- let result = (goodMoves, maxScore)
129
- params' = params {abDepth = depth + 1 , abStartDepth = Nothing }
130
- return (result, Just (params', Just result))
136
+ results <- process' processor inputs
137
+ let params' = params {abDepth = depth + 1 , abStartDepth = Nothing }
138
+ joined <- joinResults prevResult results
139
+ return (joined, Just (params', Just joined))
140
+
141
+ joinResults :: Maybe AiIterationOutput -> [Either Error (Move , Score )] -> Checkers AiIterationOutput
142
+ joinResults Nothing results =
143
+ case sequence results of
144
+ Right result -> return result
145
+ Left err -> throwError err
146
+ joinResults (Just prevResults) results = zipWithM joinResult prevResults results
147
+
148
+ joinResult :: (Move , Score ) -> Either Error (Move , Score ) -> Checkers (Move , Score )
149
+ joinResult prev@ (move, score) (Left TimeExhaused ) = do
150
+ $ info " Time exhaused while checking move {}, use result from previous depth: {}" (show move, score)
151
+ return prev
152
+ joinResult _ (Left err) = throwError err
153
+ joinResult _ (Right result) = return result
154
+
155
+ select :: AiIterationOutput -> Checkers AiOutput
156
+ select pairs = do
157
+ let best = if side == First then maximum else minimum
158
+ maxScore = best $ map snd pairs
159
+ goodMoves = [move | (move, score) <- pairs, score == maxScore]
160
+ return (goodMoves, maxScore)
131
161
132
162
-- type ScoreMemo = M.Map Side (M.Map Int (M.Map Score (M.Map Score Score)))
133
163
@@ -365,7 +395,7 @@ scoreAB var params side dp alpha beta board
365
395
iterateMoves (move : moves) dp' = do
366
396
timeout <- isTimeExhaused
367
397
when timeout $ do
368
- $ info " Timeout exhaused for depth {}." (Single $ dpCurrent dp)
398
+ -- $info "Timeout exhaused for depth {}." (Single $ dpCurrent dp)
369
399
throwError TimeExhaused
370
400
$ trace " {}|+Check move of side {}: {}" (indent, show side, show move)
371
401
evaluator <- gets ssEvaluator
0 commit comments