You can do this on your own in a customized fashion (i.e. you control when to run the engine or to use the book) with a bit of scripting in python, using the python-chess module and a polyglot opening book of your choice. In this example I'm using gm2001.bin
, which you can get from here.
As dummy example I have chosen two positions:
fen = 'rnbqkb1r/ppp1pppp/5n2/3p4/3P4/5NP1/PPP1PP1P/RNBQKB1R b KQkq - 0 3'
A basic Catalan position for which I know the book still has a move.
fen2 = 'rnbqkb1r/ppp1ppp1/5n2/3p3p/3P4/5NP1/PPP1PP1P/RNBQKB1R w KQkq - 0 4'
A position for which there are no book moves left in the chosen opening book.
What the code does is very basic: if it finds an opening move, then it makes that move (if there are more opening branches, you need to figure out how you want to choose the line to play), otherwise, it calls Stockfish with 10 seconds of movetime and makes the top engine move.
import chess
import chess.uci
import chess.polyglot
fen = 'rnbqkb1r/ppp1pppp/5n2/3p4/3P4/5NP1/PPP1PP1P/RNBQKB1R b KQkq - 0 3'
fen2 = 'rnbqkb1r/ppp1ppp1/5n2/3p3p/3P4/5NP1/PPP1PP1P/RNBQKB1R w KQkq - 0 4'
board = chess.Board(fen)
print board
handler = chess.uci.InfoHandler()
engine = chess.uci.popen_engine('stockfish_8_x64') #Provide correct address to where your engine is stored
evaltime = 10000
reader = chess.polyglot.open_reader('gm2001.bin') #Provide correct address to where your bin file is stored
n = sum(1 for _ in reader.find_all(board))
if n==0:
print 'empty, using SF top move with 10 sec movetime'
engine.info_handlers.append(handler)
if board.is_checkmate():
print 'Game has ended: checkmate'
else:
engine.position(board)
eval = engine.go(movetime=evaltime)
nextmove = board.san(eval[0])
if handler.info["score"][1].cp != None:
print 'Top move: ', nextmove, ' Evaluation: ',round(handler.info["score"][1].cp/100.0,2)
else:
print nextmove
print board.variation_san(handler.info["pv"][1])
print 'nodes: ', handler.info["nodes"], ' depth: ', handler.info["depth"], ' time: ', handler.info["time"]
board.push_san(nextmove)
print '---- Updated position:'
print board
else:
for entry in reader.find_all(board):
print 'Found book moves:'
nextmove = board.san(entry.move())
print str(nextmove)
board.push_san(nextmove)
print '---- Updated position:'
print board
break
Output for the first fen
:
r n b q k b . r
p p p . p p p p
. . . . . n . .
. . . p . . . .
. . . P . . . .
. . . . . N P .
P P P . P P . P
R N B Q K B . R
Found book moves:
c6
---- Updated position:
r n b q k b . r
p p . . p p p p
. . p . . n . .
. . . p . . . .
. . . P . . . .
. . . . . N P .
P P P . P P . P
R N B Q K B . R
and output for the 2nd fen (fen2
):
r n b q k b . r
p p p . p p p .
. . . . . n . .
. . . p . . . p
. . . P . . . .
. . . . . N P .
P P P . P P . P
R N B Q K B . R
empty, using SF top move with 10 sec movetime
Top move: Bg2 Evaluation: 0.24
4. Bg2 c5 5. c4 cxd4 6. cxd5 Qxd5 7. Qxd4 Nc6 8. Qxd5 Nxd5 9. Bd2 Ndb4 10. Na3 Be6 11. Ng5 Bd5 12. Bxd5 Nxd5 13. e4 Ndb4 14. Nb5 Nd3+ 15. Ke2
nodes: 14946976 depth: 21 time: 10001
---- Updated position:
r n b q k b . r
p p p . p p p .
. . . . . n . .
. . . p . . . p
. . . P . . . .
. . . . . N P .
P P P . P P B P
R N B Q K . . R
You should be able to take it from here and extend the code however you want.