1#
2# gnubg.py
3#
4# This file is read by GNU Backgammon during startup.
5# You can add your own user specified functions, if you wish.
6# Below are a few examples for inspiration.
7#
8# Exercise: write a shorter function for calculating pip count!
9#
10# by Joern Thyssen <jth@gnubg.org>, 2003
11#
12# This program is free software: you can redistribute it and/or modify
13# it under the terms of the GNU General Public License as published by
14# the Free Software Foundation, either version 3 of the License, or
15# (at your option) any later version.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20# GNU General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License
23# along with this program.  If not, see <http://www.gnu.org/licenses/>.
24#
25# $Id: gnubg.py,v 1.18 2017/11/13 21:07:43 plm Exp $
26#
27
28# Add the scripts directory to the module path to allow
29# for modules from this directory to be imported
30import sys
31import os
32
33sys.path.insert(1, './scripts')
34if sys.platform == 'win32':
35    os.environ['TCL_LIBRARY']='PythonLib/lib/tcl8'
36
37if sys.version_info >= (3, 0):
38    import builtins as bi
39else:
40    import __builtin__ as bi
41
42
43def setinterpreterquit():
44    class interpreterquit(object):
45        def __repr__(self):
46            self()
47        def __call__(self, code=None):
48            if not ('idlelib' in sys.stdin.__class__.__module__):
49                raise SystemExit(0)
50            else:
51                print('Press Ctrl-D to exit')
52
53    bi.quit = interpreterquit()
54    bi.exit = interpreterquit()
55
56setinterpreterquit()
57
58
59def gnubg_find_msvcrt():
60    return 'msvcr100.dll'
61
62# This is a workaround for a pyreadline c runtime conflict
63# on Win32 platforms. Replace ctypes.util.find_msvcrt with
64# our own. Readline not properly supported on Win2000 or
65# WinXP with a Service Pack earlier than SP2
66
67supports_readline = True
68
69try:
70    from ctypes.util import find_msvcrt
71    import ctypes.util
72    ctypes.util.find_msvcrt = gnubg_find_msvcrt
73    import platform
74    winver = platform.win32_ver()
75    try:
76        sp_ver = int(winver[2][2])
77    except:
78        sp_ver = 0
79
80    ver_split = winver[1].split('.')
81    major = int(ver_split[0])
82    minor = int(ver_split[1])
83
84    if ((major < 5) or (major == 5 and minor == 0) or (major == 5 and minor == 1 and sp_ver < 2)):
85        supports_readline = False
86except:
87    pass
88
89
90def gnubg_InteractivePyShell_tui(argv=[''], banner=None):
91    global supports_readline
92    import sys
93    import traceback
94    import code
95
96    try:
97        sys.argv = argv
98
99        # Check for IPython as it is generally the best cmdline interpreter
100        from IPython import version_info as ipy_version_info
101        if ipy_version_info[0] >= 1:
102            from IPython.terminal.embed import InteractiveShellEmbed
103        else:
104            from IPython.frontend.terminal.embed import InteractiveShellEmbed
105
106        from IPython import __version__ as ipyversion
107        if ipy_version_info[0] >= 4:
108            from traitlets.config.loader import Config
109        else:
110            from IPython.config.loader import Config
111
112    except:
113        # Otherwise use standard interpreter
114        if (banner == None):
115            banner = 'Python ' + sys.version
116
117        if (supports_readline):
118            try:
119                # See if we can use readline support
120                import readline
121            except:
122                # Might be Win32 so check for pyreadline
123                try:
124                    import pyreadline as readline
125                except:
126                    pass
127            try:
128                # See if we can add tab completion
129                import rlcompleter
130                readline.parse_and_bind('tab: complete')
131            except:
132                pass
133
134            try:
135                code.interact(banner=banner, local=globals())
136            except SystemExit:
137                # Ignore calls to exit() and quit()
138                pass
139
140            return True
141
142        else:
143            # If we get this far we are on Win32 and too early
144            # a version to support the embedded interpreter so
145            # we simulate one
146            print(banner)
147            print('<Control-Z> and <Return> to exit')
148            while True:
149                print('>>> ',)
150                line = sys.stdin.readline()
151                if not line:
152                    break
153
154                try:
155                    exec(line)
156                except SystemExit:
157                    # Ignore calls to exit() and quit()
158                    break
159
160            return True
161
162    try:
163        # Launch IPython interpreter
164        if ipy_version_info[0] <= 4:
165            cfg = Config()
166            prompt_config = cfg.PromptManager
167            prompt_config.in_template = 'In <\\#> > '
168            prompt_config.in2_template = '   .\\D. > '
169            prompt_config.out_template = 'Out<\\#> > '
170            cfg.InteractiveShell.confirm_exit = False
171        else:
172            # FIXME:
173            # As of IPython 5.0 `PromptManager` config will have no effect and
174            # has been replaced by TerminalInteractiveShell.prompts_class
175            cfg = None
176
177        if banner == None:
178            banner = 'IPython ' + ipyversion + ', Python ' + sys.version
179
180        # We want to execute in the name space of the CALLER of this function,
181        # not within the namespace of THIS function.
182        # This allows us to have changes made in the IPython environment
183        # visible to the CALLER of this function
184
185        # Go back one frame and get the locals.
186        call_frame = sys._getframe(0).f_back
187        calling_ns = call_frame.f_locals
188
189        ipshell = InteractiveShellEmbed(
190            config=cfg, user_ns=calling_ns, banner1=banner)
191
192        try:
193            ipshell()
194        except SystemExit:
195            # Ignore calls to exit() and quit()
196            pass
197
198        # Cleanup the sys environment (including exception handlers)
199        ipshell.restore_sys_module_state()
200
201        return True
202
203    except:
204        traceback.print_exc()
205
206    return False
207
208
209def gnubg_InteractivePyShell_gui(argv=['', '-n']):
210    import sys
211    sys.argv = argv
212
213    try:
214        import idlelib.PyShell
215        try:
216            idlelib.PyShell.main()
217            return True
218        except SystemExit:
219            # Ignore calls to exit() and quit()
220            return True
221        except:
222            traceback.print_exc()
223
224    except:
225        pass
226
227    return False
228
229
230def swapboard(board):
231    """Swap the board"""
232
233    return [board[1], board[0]]
234
235
236def pipcount(board):
237    """Calculate pip count"""
238
239    sum = [0, 0]
240    for i in range(2):
241        for j in range(25):
242            sum[i] += (j + 1) * board[i][j]
243
244    return sum
245
246
247# Following code is intended as an example on the usage of the match command.
248# It illustrates how to iterate over matches and do something useful with the
249# navigate command.
250import os.path
251
252
253def skillBad(s):
254    return s and (s == "very bad" or s == "bad" or s == "doubtful")
255
256
257def exportBad(baseName):
258    """ For current analyzed match, export all moves/cube decisions marked
259    doubtful or bad"""
260
261    # Get current match
262    m = gnubg.match()
263
264    # Go to match start
265    gnubg.navigate()
266
267    # Skill of previous action, to avoid exporting double actions twice
268    prevSkill = None
269
270    # Exported position number, used in file name
271    poscount = 0
272
273    for game in m["games"]:
274        for action in game["game"]:
275
276            analysis = action.get("analysis", None)
277            if analysis:
278                type = action["action"]
279                skill = analysis.get("skill", None)
280                bad = skillBad(skill)
281
282                if type == "move":
283                    if skillBad(analysis.get("cube-skill", None)):
284                        bad = True
285                elif type == "take" or type == "drop":
286                    if skillBad(prevSkill):
287                        # Already exported
288                        bad = False
289
290                if bad:
291                    exportfile = "%s__%d.html" % (
292                        os.path.splitext(baseName)[0], poscount)
293                    gnubg.command(
294                        "export position html " + "\"" + exportfile + "\"")
295                    poscount += 1
296
297            # Advance to next record
298            gnubg.navigate(1)
299
300        # Advance to next game
301        gnubg.navigate(game=1)
302