1
2import os
3from struct import Struct
4from collections import namedtuple
5
6from pychess.System import conf
7from pychess.Utils.lutils.lmove import parsePolyglot
8from pychess.System.Log import log
9
10path = conf.get("opening_file_entry")
11
12if os.path.isfile(path):
13    bookfile = True
14else:
15    bookfile = False
16    log.warning("Could not find %s" % path)
17
18# The book probing code is based on that of PolyGlot by Fabien Letouzey.
19# PolyGlot is available under the GNU GPL from http://wbec-ridderkerk.nl
20
21BookEntry = namedtuple('BookEntry', 'key move weight learn')
22# 'key' c_uint64      the position's hash
23# 'move' c_uint16     the candidate move
24# 'weight' c_uint16   proportional to prob. we should play it
25# The following terms are not always available:
26# 'learn' c_uint32    we use this NOT the polyglot way but as in
27#                     https://github.com/mcostalba/chess_db
28
29entrystruct = Struct(">QHHI")
30entrysize = entrystruct.size
31
32
33def getOpenings(board):
34    """ Return a tuple (move, weight, learn) for each opening move
35        in the given position. The weight is proportional to the probability
36        that a move should be played. By convention, games is the number of
37        times a move has been tried, and score the number of points it has
38        scored (with 2 per victory and 1 per draw). However, opening books
39        aren't required to keep this information. """
40
41    openings = []
42    if not bookfile:
43        return openings
44
45    with open(path, "rb") as bookFile:
46        key = board.hash
47        # Find the first entry whose key is >= the position's hash
48        bookFile.seek(0, os.SEEK_END)
49        low, high = 0, bookFile.tell() // 16 - 1
50        if high < 0:
51            return openings
52        while low < high:
53            mid = (low + high) // 2
54            bookFile.seek(mid * 16)
55            entry = bookFile.read(entrysize)
56            if len(entry) != entrysize:
57                return openings
58            entry = BookEntry._make(entrystruct.unpack(entry))
59            if entry.key < key:
60                low = mid + 1
61            else:
62                high = mid
63
64        bookFile.seek(low * 16)
65        while True:
66            entry = bookFile.read(entrysize)
67            if len(entry) != entrysize:
68                return openings
69            entry = BookEntry._make(entrystruct.unpack(entry))
70            if entry.key != key:
71                break
72            move = parsePolyglot(board, entry.move)
73            openings.append((move, entry.weight, entry.learn))
74    return openings
75