Skip to content

Commit 48a50b5

Browse files
committed
Merge branch 'timing1' into future
2 parents ef4795c + 2d94e91 commit 48a50b5

File tree

3 files changed

+58
-21
lines changed

3 files changed

+58
-21
lines changed

src/AI/AlphaBeta.hs

+47-17
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module AI.AlphaBeta where
1515
import Control.Monad
1616
import Control.Monad.State
1717
import Control.Monad.Except
18+
import Control.Monad.Catch
1819
import qualified Control.Monad.Metrics as Metrics
1920
import Control.Concurrent.STM
2021
import Data.Maybe
@@ -69,22 +70,33 @@ scoreMove (ai@(AlphaBeta params rules eval), var, side, dp, board, pm) = do
6970
score <- Metrics.timed "ai.score.move" $ do
7071
let board' = applyMoveActions (pmResult pm) board
7172
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+
)
7277
$info "Check: {} (depth {}) => {}" (show pm, dpTarget dp, show score)
7378
return score
7479

7580
return (pmMove pm, score)
7681

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
7990
where
80-
iterator = case abBaseTime params of
91+
controller :: Checkers AiIterationOutput
92+
controller = case abBaseTime params of
8193
Nothing -> do
8294
(result, _) <- go (params, Nothing)
8395
return result
8496
Just time -> repeatTimed' "runAI" time goTimed (params, Nothing)
8597

86-
goTimed :: (AlphaBetaParams, Maybe ([Move], Score))
87-
-> Checkers (([Move], Score), Maybe (AlphaBetaParams, Maybe ([Move], Score)))
98+
goTimed :: AiIterationInput
99+
-> Checkers (AiIterationOutput, Maybe AiIterationInput)
88100
goTimed (params, prevResult) = do
89101
ret <- tryC $ go (params, prevResult)
90102
case ret of
@@ -94,11 +106,11 @@ runAI ai@(AlphaBeta params rules eval) handle side board = iterator
94106
Just result -> return (result, Nothing)
95107
Nothing -> do
96108
let moves = map pmMove $ possibleMoves rules side board
97-
return ((moves, 0), Nothing)
109+
return ([(move, 0) | move <- moves], Nothing)
98110
Left err -> throwError err
99111

100-
go :: (AlphaBetaParams, Maybe ([Move], Score))
101-
-> Checkers (([Move], Score), Maybe (AlphaBetaParams, Maybe ([Move], Score)))
112+
go :: AiIterationInput
113+
-> Checkers (AiIterationOutput, Maybe AiIterationInput)
102114
go (params, prevResult) = do
103115
let depth = abDepth params
104116
let moves = possibleMoves rules side board
@@ -108,7 +120,7 @@ runAI ai@(AlphaBeta params rules eval) handle side board = iterator
108120
-- currently we do not use results of evaluating of all moves
109121
-- when evaluating deeper parts of the tree (it is hard due to alpha-beta restrictions).
110122
-- 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)
112124

113125
else do
114126
let var = aichData handle
@@ -121,13 +133,31 @@ runAI ai@(AlphaBeta params rules eval) handle side board = iterator
121133
, dpMin = fromMaybe depth (abStartDepth params)
122134
}
123135
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)
131161

132162
-- type ScoreMemo = M.Map Side (M.Map Int (M.Map Score (M.Map Score Score)))
133163

@@ -365,7 +395,7 @@ scoreAB var params side dp alpha beta board
365395
iterateMoves (move : moves) dp' = do
366396
timeout <- isTimeExhaused
367397
when timeout $ do
368-
$info "Timeout exhaused for depth {}." (Single $ dpCurrent dp)
398+
-- $info "Timeout exhaused for depth {}." (Single $ dpCurrent dp)
369399
throwError TimeExhaused
370400
$trace "{}|+Check move of side {}: {}" (indent, show side, show move)
371401
evaluator <- gets ssEvaluator

src/Core/Parallel.hs

+9-4
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,19 @@ runProcessor nThreads getKey fn = do
3232
writeChan outChan (getKey input, output)
3333

3434
process :: Ord key => Processor key input output -> [input] -> Checkers [output]
35-
process (Processor getKey inChan outChan) inputs = do
35+
process processor inputs = do
36+
results <- process' processor inputs
37+
case sequence results of
38+
Right outputs -> return outputs
39+
Left err -> throwError err
40+
41+
process' :: Ord key => Processor key input output -> [input] -> Checkers [Either Error output]
42+
process' (Processor getKey inChan outChan) inputs = do
3643
let n = length inputs
3744
forM_ inputs $ \input ->
3845
liftIO $ writeChan inChan input
3946
results <- replicateM n $ liftIO $ readChan outChan
4047
let m = M.fromList results
4148
let results = [fromJust $ M.lookup (getKey input) m | input <- inputs]
42-
case sequence results of
43-
Right outputs -> return outputs
44-
Left err -> throwError err
49+
return results
4550

src/Core/Types.hs

+2
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,8 @@ data Error =
578578
| Unhandled String
579579
deriving (Eq, Show, Typeable, Generic)
580580

581+
instance Exception Error
582+
581583
-- | Checkers monad
582584
newtype Checkers a = Checkers {
583585
runCheckers :: ExceptT Error (ReaderT CheckersState IO) a

0 commit comments

Comments
 (0)