ankigammon 1.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. ankigammon/__init__.py +7 -0
  2. ankigammon/__main__.py +6 -0
  3. ankigammon/analysis/__init__.py +13 -0
  4. ankigammon/analysis/score_matrix.py +391 -0
  5. ankigammon/anki/__init__.py +6 -0
  6. ankigammon/anki/ankiconnect.py +216 -0
  7. ankigammon/anki/apkg_exporter.py +111 -0
  8. ankigammon/anki/card_generator.py +1325 -0
  9. ankigammon/anki/card_styles.py +1054 -0
  10. ankigammon/gui/__init__.py +8 -0
  11. ankigammon/gui/app.py +192 -0
  12. ankigammon/gui/dialogs/__init__.py +10 -0
  13. ankigammon/gui/dialogs/export_dialog.py +594 -0
  14. ankigammon/gui/dialogs/import_options_dialog.py +201 -0
  15. ankigammon/gui/dialogs/input_dialog.py +762 -0
  16. ankigammon/gui/dialogs/note_dialog.py +93 -0
  17. ankigammon/gui/dialogs/settings_dialog.py +420 -0
  18. ankigammon/gui/dialogs/update_dialog.py +373 -0
  19. ankigammon/gui/format_detector.py +377 -0
  20. ankigammon/gui/main_window.py +1611 -0
  21. ankigammon/gui/resources/down-arrow.svg +3 -0
  22. ankigammon/gui/resources/icon.icns +0 -0
  23. ankigammon/gui/resources/icon.ico +0 -0
  24. ankigammon/gui/resources/icon.png +0 -0
  25. ankigammon/gui/resources/style.qss +402 -0
  26. ankigammon/gui/resources.py +26 -0
  27. ankigammon/gui/update_checker.py +259 -0
  28. ankigammon/gui/widgets/__init__.py +8 -0
  29. ankigammon/gui/widgets/position_list.py +166 -0
  30. ankigammon/gui/widgets/smart_input.py +268 -0
  31. ankigammon/models.py +356 -0
  32. ankigammon/parsers/__init__.py +7 -0
  33. ankigammon/parsers/gnubg_match_parser.py +1094 -0
  34. ankigammon/parsers/gnubg_parser.py +468 -0
  35. ankigammon/parsers/sgf_parser.py +290 -0
  36. ankigammon/parsers/xg_binary_parser.py +1097 -0
  37. ankigammon/parsers/xg_text_parser.py +688 -0
  38. ankigammon/renderer/__init__.py +5 -0
  39. ankigammon/renderer/animation_controller.py +391 -0
  40. ankigammon/renderer/animation_helper.py +191 -0
  41. ankigammon/renderer/color_schemes.py +145 -0
  42. ankigammon/renderer/svg_board_renderer.py +791 -0
  43. ankigammon/settings.py +315 -0
  44. ankigammon/thirdparty/__init__.py +7 -0
  45. ankigammon/thirdparty/xgdatatools/__init__.py +17 -0
  46. ankigammon/thirdparty/xgdatatools/xgimport.py +160 -0
  47. ankigammon/thirdparty/xgdatatools/xgstruct.py +1032 -0
  48. ankigammon/thirdparty/xgdatatools/xgutils.py +118 -0
  49. ankigammon/thirdparty/xgdatatools/xgzarc.py +260 -0
  50. ankigammon/utils/__init__.py +13 -0
  51. ankigammon/utils/gnubg_analyzer.py +590 -0
  52. ankigammon/utils/gnuid.py +577 -0
  53. ankigammon/utils/move_parser.py +204 -0
  54. ankigammon/utils/ogid.py +326 -0
  55. ankigammon/utils/xgid.py +387 -0
  56. ankigammon-1.0.6.dist-info/METADATA +352 -0
  57. ankigammon-1.0.6.dist-info/RECORD +61 -0
  58. ankigammon-1.0.6.dist-info/WHEEL +5 -0
  59. ankigammon-1.0.6.dist-info/entry_points.txt +2 -0
  60. ankigammon-1.0.6.dist-info/licenses/LICENSE +21 -0
  61. ankigammon-1.0.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1032 @@
1
+ #
2
+ # xgstruct.py - classes to read XG file structures
3
+ # Copyright (C) 2013,2014 Michael Petch <mpetch@gnubg.org>
4
+ # <mpetch@capp-sysware.com>
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+ #
20
+ # This code is based upon Delphi data structures provided by
21
+ # Xavier Dufaure de Citres <contact@extremegammon.com> for purposes
22
+ # of interacting with the ExtremeGammon XG file formats. Field
23
+ # descriptions derived from xg_format.pas. The file formats are
24
+ # published at http://www.extremegammon.com/xgformat.aspx
25
+ #
26
+
27
+ from . import xgutils as _xgutils
28
+ import struct as _struct
29
+ import os as _os
30
+ import uuid as _uuid
31
+ import binascii as _binascii
32
+
33
+
34
+ class GameDataFormatHdrRecord(dict):
35
+ SIZEOFREC = 8232
36
+
37
+ def __init__(self, **kw):
38
+ defaults = {
39
+ 'MagicNumber': 0, # $484D4752, RM_MAGICNUMBER
40
+ 'HeaderVersion': 0, # version
41
+ 'HeaderSize': 0, # size of the header
42
+ 'ThumbnailOffset': 0, # location of the thumbnail (jpg)
43
+ 'ThumbnailSize': 0, # size in bye of the thumbnail
44
+ 'GameGUID': None, # game id (GUID)
45
+ 'GameName': None, # Unicode game name
46
+ 'SaveName': None, # Unicode save name
47
+ 'LevelName': None, # Unicode level name
48
+ 'Comments': None # Unicode comments
49
+ }
50
+ super(GameDataFormatHdrRecord, self).__init__(defaults, **kw)
51
+
52
+ def __setattr__(self, key, value):
53
+ self[key] = value
54
+
55
+ def __getattr__(self, key):
56
+ return self[key]
57
+
58
+ def fromstream(self, stream):
59
+ try:
60
+ unpacked_data = \
61
+ _struct.unpack('<4BiiQiLHHBB6s1024H1024H1024H1024H',
62
+ stream.read(self.SIZEOFREC))
63
+ except:
64
+ return None
65
+
66
+ self.MagicNumber = bytearray(unpacked_data[0:4][::-1]).decode('ascii')
67
+ self.HeaderVersion = unpacked_data[4]
68
+ if self.MagicNumber != 'HMGR' or self.HeaderVersion != 1:
69
+ return None
70
+
71
+ self.HeaderSize = unpacked_data[5]
72
+ self.ThumbnailOffset = unpacked_data[6]
73
+ self.ThumbnailSize = unpacked_data[7]
74
+
75
+ # Convert Delphi 4 component GUID to the 6 components
76
+ # of a Python GUID.
77
+ guidp1, guidp2, guidp3, guidp4, guidp5 = unpacked_data[8:13]
78
+ guidp6 = int(_binascii.b2a_hex(unpacked_data[13]), 16)
79
+ self.GameGUID = str(_uuid.UUID(fields=(guidp1, guidp2, guidp3,
80
+ guidp4, guidp5, guidp6)))
81
+
82
+ self.GameName = _xgutils.utf16intarraytostr(unpacked_data[14:1038])
83
+ self.SaveName = _xgutils.utf16intarraytostr(unpacked_data[1038:2062])
84
+ self.LevelName = _xgutils.utf16intarraytostr(unpacked_data[2062:3086])
85
+ self.Comments = _xgutils.utf16intarraytostr(unpacked_data[3086:4110])
86
+ return self
87
+
88
+
89
+ class TimeSettingRecord(dict):
90
+
91
+ SIZEOFREC = 32
92
+
93
+ def __init__(self, **kw):
94
+ defaults = {
95
+ 'ClockType': 0, # 0=None,0=Fischer,0=Bronstein
96
+ 'PerGame': False, # time is for session reset after each game
97
+ 'Time1': 0, # initial time in sec
98
+ 'Time2': 0, # time added (fisher) or reverved (bronstrein) per move in sec
99
+ 'Penalty': 0, # point penalty when running our of time (in point)
100
+ 'TimeLeft1': 0, # current time left
101
+ 'TimeLeft2': 0, # current time left
102
+ 'PenaltyMoney': 0 # point penalty when running our of time (in point)
103
+ }
104
+ super(TimeSettingRecord, self).__init__(defaults, **kw)
105
+
106
+ def __setattr__(self, key, value):
107
+ self[key] = value
108
+
109
+ def __getattr__(self, key):
110
+ return self[key]
111
+
112
+ def fromstream(self, stream):
113
+ unpacked_data = _struct.unpack(
114
+ '<lBxxxllllll',
115
+ stream.read(self.SIZEOFREC))
116
+ self.ClockType = unpacked_data[0]
117
+ self.PerGame = bool(unpacked_data[1])
118
+ self.Time1 = unpacked_data[2]
119
+ self.Time2 = unpacked_data[3]
120
+ self.Penalty = unpacked_data[4]
121
+ self.TimeLeft1 = unpacked_data[5]
122
+ self.TimeLeft2 = unpacked_data[6]
123
+ self.PenaltyMoney = unpacked_data[7]
124
+ return self
125
+
126
+
127
+ class EvalLevelRecord(dict):
128
+
129
+ SIZEOFREC = 4
130
+
131
+ def __init__(self, **kw):
132
+ defaults = {
133
+ 'Level': 0, # Level used see PLAYERLEVEL table
134
+ 'isDouble': False # The analyze assume double for the very next move
135
+ }
136
+ super(EvalLevelRecord, self).__init__(defaults, **kw)
137
+
138
+ def __setattr__(self, key, value):
139
+ self[key] = value
140
+
141
+ def __getattr__(self, key):
142
+ return self[key]
143
+
144
+ def fromstream(self, stream):
145
+ unpacked_data = _struct.unpack(
146
+ '<hBb',
147
+ stream.read(self.SIZEOFREC))
148
+ self.Level = unpacked_data[0]
149
+ self.isDouble = bool(unpacked_data[1])
150
+
151
+ return self
152
+
153
+
154
+ class EngineStructBestMoveRecord(dict):
155
+
156
+ SIZEOFREC = 2184
157
+
158
+ def __init__(self, **kw):
159
+ defaults = {
160
+ 'Pos': None, # Current position
161
+ 'Dice': None, # Dice
162
+ 'Level': 0, # analyze level requested
163
+ 'Score': None, # current score
164
+ 'Cube': 0, # cube value 1,2,4, etcc.
165
+ 'CubePos': 0, # 0: Center 1: Player owns cube -1 Opponent owns cube
166
+ 'Crawford': 0, # 1 = Crawford 0 = No Crawford
167
+ 'Jacoby': 0, # 1 = Jacoby 0 = No Jacoby
168
+ 'NMoves': 0, # number of move (max 32)
169
+ 'PosPlayed': None, # position played
170
+ 'Moves': None, # move list as From1,dice1, from2,dice2 etc.. -1 show termination of list
171
+ 'EvalLevel': None, # evaluation level of each move
172
+ 'Eval': None, # eval value of each move
173
+ 'Unused': 0, # if 1 does not count as a decision
174
+ 'met': 0, # unused
175
+ 'Choice0': 0, # 1-ply choice (index to PosPlayed)
176
+ 'Choice3': 0 # 3-ply choice (index to PosPlayed)
177
+ }
178
+ super(EngineStructBestMoveRecord, self).__init__(defaults, **kw)
179
+
180
+ def __setattr__(self, key, value):
181
+ self[key] = value
182
+
183
+ def __getattr__(self, key):
184
+ return self[key]
185
+
186
+ def fromstream(self, stream):
187
+ unpacked_data = _struct.unpack(
188
+ '<26bxx2ll2llllll',
189
+ stream.read(68))
190
+ self.Pos = unpacked_data[0:26]
191
+ self.Dice = unpacked_data[26:28]
192
+ self.Level = unpacked_data[28]
193
+ self.Score = unpacked_data[29:31]
194
+ self.Cube = unpacked_data[31]
195
+ self.Cubepos = unpacked_data[32]
196
+ self.Crawford = unpacked_data[33]
197
+ self.Jacoby = unpacked_data[34]
198
+ self.NMoves = unpacked_data[35]
199
+
200
+ self.PosPlayed = ()
201
+ for row in range(32):
202
+ unpacked_data = _struct.unpack('<26b', stream.read(26))
203
+ self.PosPlayed += (unpacked_data[0:26],)
204
+
205
+ self.Moves = ()
206
+ for row in range(32):
207
+ self.Moves += (_struct.unpack('<8b', stream.read(8))[0:8],)
208
+
209
+ self.EvalLevel = ()
210
+ for row in range(32):
211
+ self.EvalLevel += (EvalLevelRecord().fromstream(stream),)
212
+
213
+ self.Eval = ()
214
+ for row in range(32):
215
+ unpacked_data = _struct.unpack('<7f', stream.read(28))
216
+ self.Eval += (unpacked_data,)
217
+
218
+ unpacked_data = _struct.unpack('<bbbb', stream.read(4))
219
+ self.Unused = unpacked_data[0]
220
+ self.met = unpacked_data[1]
221
+ self.Choice0 = unpacked_data[2]
222
+ self.Choice3 = unpacked_data[3]
223
+
224
+ return self
225
+
226
+
227
+ class EngineStructDoubleAction(dict):
228
+
229
+ SIZEOFREC = 132
230
+
231
+ def __init__(self, **kw):
232
+ defaults = {
233
+ 'Pos': None, # Current position
234
+ 'Level': 0, # analyze level performed
235
+ 'Score': None, # current score
236
+ 'Cube': 0, # cube value 1,2,4, etcc.
237
+ 'CubePos': 0, # 0: Center 1: Player owns cube -1 Opponent owns cube
238
+ 'Jacoby': 0, # 1 = Jacoby 0 = No Jacoby
239
+ 'Crawford': 0, # 1 = Crawford 0 = No Crawford
240
+ 'met': 0, # unused
241
+ 'FlagDouble': 0, # 0: Dont double 1: Double
242
+ 'isBeaver': 0, # is it a beaver if doubled
243
+ 'Eval': None, # eval value for No double
244
+ 'equB': 0.0, # equity No Double
245
+ 'equDouble': 0.0, # equity Double/take
246
+ 'equDrop': 0.0, # equity double/drop (-1)
247
+ 'LevelRequest': 0, # analyze level requested
248
+ 'DoubleChoice3': 0, # 3-ply choice as double+take*2
249
+ 'EvalDouble': None # eval value for Double/Take
250
+ }
251
+ super(EngineStructDoubleAction, self).__init__(defaults, **kw)
252
+
253
+ def __setattr__(self, key, value):
254
+ self[key] = value
255
+
256
+ def __getattr__(self, key):
257
+ return self[key]
258
+
259
+ def fromstream(self, stream):
260
+ unpacked_data = _struct.unpack(
261
+ '<26bxxl2llllhhhh7ffffhh7f',
262
+ stream.read(132))
263
+ self.Pos = unpacked_data[0:26]
264
+ self.Level = unpacked_data[26]
265
+ self.Score = unpacked_data[27:29]
266
+ self.Cube = unpacked_data[29]
267
+ self.CubePos = unpacked_data[30]
268
+ self.Jacoby = unpacked_data[31]
269
+ self.Crawford = unpacked_data[32]
270
+ self.met = unpacked_data[33]
271
+ self.FlagDouble = unpacked_data[34]
272
+ self.isBeaver = unpacked_data[35]
273
+ self.Eval = unpacked_data[36:43]
274
+ self.equB = unpacked_data[43]
275
+ self.equDouble = unpacked_data[44]
276
+ self.equDrop = unpacked_data[45]
277
+ self.LevelRequest = unpacked_data[46]
278
+ self.DoubleChoice3 = unpacked_data[47]
279
+ self.EvalDouble = unpacked_data[48:55]
280
+
281
+ return self
282
+
283
+ class HeaderMatchEntry(dict):
284
+
285
+ SIZEOFREC = 2560
286
+
287
+ def __init__(self, version=0, **kw):
288
+ defaults = {
289
+ 'Name': 'MatchInfo',
290
+ 'EntryType': GameFileRecord.ENTRYTYPE_HEADERMATCH,
291
+ 'SPlayer1': None, # player name in ANSI string for XG1 compatbility see "Player1" and "Player2" below for unicode
292
+ 'SPlayer2': None,
293
+ 'MatchLength': 0, # Match length, 99999 for unlimited
294
+ 'Variation': 0, # 0:backgammon, 1: Nack, 2: Hyper, 3: Longgammon
295
+ 'Crawford': False, # Crawford in use
296
+ 'Jacoby': False, # Jacoby in use
297
+ 'Beaver': False, # Beaver in use
298
+ 'AutoDouble': False, # Automatic double in use
299
+ 'Elo1': 0.0, # player1 elo
300
+ 'Elo2': 0.0, # player2 experience
301
+ 'Exp1': 0, # player1 elo
302
+ 'Exp2': 0, # player2 experience
303
+ 'Date': 0, # game date
304
+ 'SEvent': None, # event name, in ANSI string for XG1 compatbility see "event" below for unicode
305
+ 'GameId': 0, # game ID, if player are swap make gameid:=-GameID
306
+ 'CompLevel1': -1, # Player level: see table at the end (PLAYERLEVEL TABLE)
307
+ 'CompLevel2': -1,
308
+ 'CountForElo': False, # outcome of the session will affect elo
309
+ 'AddtoProfile1': False, # outcome of the session will affect player 1 profile
310
+ 'AddtoProfile2': False, # outcome of the session will affect player 2 profile
311
+ 'SLocation': None, # location name, in ANSI string for XG1 compatbility see "location" below for unicode
312
+ 'GameMode': 0, # game mode : see table at the end (GAMEMODE TABLE)
313
+ 'Imported': False, # game was imported from an site (MAT, CBG etc..)
314
+ 'SRound': None, # round name, in ANSI string for XG1 compatbility see "round" below for unicode
315
+ 'Invert': 0, # If the board is swap then invert=-invert and MatchID=-MatchID
316
+ 'Version': version, # file version, currently SaveFileVersion
317
+ 'Magic': 0x494C4D44, # must be MagicNumber = $494C4D44;
318
+ 'MoneyInitG': 0, # initial game played from the profile against that opp in money
319
+ 'MoneyInitScore': [0, 0], # initial score from the profile against that opp in money
320
+ 'Entered': False, # entered in profile
321
+ 'Counted': False, # already accounted in the profile elo
322
+ 'UnratedImp': False, # game was unrated on the site it was imported from
323
+ 'CommentHeaderMatch': -1, # index of the match comment header in temp.xgc
324
+ 'CommentFooterMatch': -1, # index of the match comment footer in temp.xgc
325
+ 'isMoneyMatch': False, # was player for real money
326
+ 'WinMoney': 0.0, # amount of money for the winner
327
+ 'LoseMoney': 0.0, # amount of money for the looser
328
+ 'Currency': 0, # currency code from Currency.ini
329
+ 'FeeMoney': 0.0, # amount of rake
330
+ 'TableStake': 0, # max amount that can be lost -- NOT IMPLEMENTED
331
+ 'SiteId': -1, # site id from siteinfo.ini
332
+ 'CubeLimit': 0, # v8: maximum cube value
333
+ 'AutoDoubleMax': 0, # v8: maximum c# of time the autodouble can be used
334
+ 'Transcribed': False, # v24: game was transcribed
335
+ 'Event': None, # v24: Event name (unicode)
336
+ 'Player1': None, # v24: Player1 name (unicode)
337
+ 'Player2': None, # v24: Player2 name (unicode)
338
+ 'Location': None, # v24: Location (unicode)
339
+ 'Round': None, # v24: Round (unicode)
340
+ 'TimeSetting': None, # v25: Time setting for the game
341
+ 'TotTimeDelayMove': 0, # v26: # of checker play marked for delayed RO
342
+ 'TotTimeDelayCube': 0, # v26: # of checker play marked for delayed RO done
343
+ 'TotTimeDelayMoveDone': 0, # v26: # of checker Cube action marked for delayed RO
344
+ 'TotTimeDelayCubeDone': 0, # v26: # of checker Cube action marked for delayed RO Done
345
+ 'Transcriber': None # v30: Name of the Transcriber (unicode)
346
+ }
347
+ super(HeaderMatchEntry, self).__init__(defaults, **kw)
348
+
349
+ def __setattr__(self, key, value):
350
+ self[key] = value
351
+
352
+ def __getattr__(self, key):
353
+ return self[key]
354
+
355
+ def fromstream(self, stream):
356
+
357
+ unpacked_data = _struct.unpack(
358
+ '<9x41B41BxllBBBBddlld129BxxxlllBBB129BlB129BxxllLl2lBBB'
359
+ 'xllBxxxfflfll', stream.read(612))
360
+ self.SPlayer1 = _xgutils.delphishortstrtostr(unpacked_data[0:41])
361
+ self.SPlayer2 = _xgutils.delphishortstrtostr(unpacked_data[41:82])
362
+ self.MatchLength = unpacked_data[82]
363
+ self.Variation = unpacked_data[83]
364
+ self.Crawford = bool(unpacked_data[84])
365
+ self.Jacoby = bool(unpacked_data[85])
366
+ self.Beaver = bool(unpacked_data[86])
367
+ self.AutoDouble = bool(unpacked_data[87])
368
+ self.Elo1 = unpacked_data[88]
369
+ self.Elo2 = unpacked_data[89]
370
+ self.Exp1 = unpacked_data[90]
371
+ self.Exp2 = unpacked_data[91]
372
+ self.Date = str(_xgutils.delphidatetimeconv(unpacked_data[92]))
373
+ self.SEvent = _xgutils.delphishortstrtostr(unpacked_data[93:222])
374
+ self.GameId = unpacked_data[222]
375
+ self.CompLevel1 = unpacked_data[223]
376
+ self.CompLevel2 = unpacked_data[224]
377
+ self.CountForElo = bool(unpacked_data[225])
378
+ self.AddtoProfile1 = bool(unpacked_data[226])
379
+ self.AddtoProfile2 = bool(unpacked_data[227])
380
+ self.SLocation = _xgutils.delphishortstrtostr(unpacked_data[228:357])
381
+ self.GameMode = unpacked_data[357]
382
+ self.Imported = bool(unpacked_data[358])
383
+ self.SRound = _xgutils.delphishortstrtostr(unpacked_data[359:487])
384
+ self.Invert = unpacked_data[488]
385
+ self.Version = unpacked_data[489]
386
+ self.Magic = unpacked_data[490]
387
+ self.MoneyInitG = unpacked_data[491]
388
+ self.MoneyInitScore = unpacked_data[492:494]
389
+ self.Entered = bool(unpacked_data[494])
390
+ self.Counted = bool(unpacked_data[495])
391
+ self.UnratedImp = bool(unpacked_data[496])
392
+ self.CommentHeaderMatch = unpacked_data[497]
393
+ self.CommentFooterMatch = unpacked_data[498]
394
+ self.isMoneyMatch = bool(unpacked_data[499])
395
+ self.WinMoney = unpacked_data[500]
396
+ self.LoseMoney = unpacked_data[501]
397
+ self.Currency = unpacked_data[502]
398
+ self.FeeMoney = unpacked_data[503]
399
+ self.TableStake = unpacked_data[504]
400
+ self.SiteId = unpacked_data[505]
401
+ if self.Version >= 8:
402
+ unpacked_data = _struct.unpack('<ll', stream.read(8))
403
+ self.CubeLimit = unpacked_data[0]
404
+ self.AutoDoubleMax = unpacked_data[1]
405
+ if self.Version >= 24:
406
+ unpacked_data = _struct.unpack('<Bx129H129H129H129H129H',
407
+ stream.read(1292))
408
+ self.Transcribed = bool(unpacked_data[0])
409
+ self.Event = _xgutils.utf16intarraytostr(unpacked_data[1:130])
410
+ self.Player1 = _xgutils.utf16intarraytostr(unpacked_data[130:259])
411
+ self.Player2 = _xgutils.utf16intarraytostr(unpacked_data[259:388])
412
+ self.Location = _xgutils.utf16intarraytostr(unpacked_data[388:517])
413
+ self.Round = _xgutils.utf16intarraytostr(unpacked_data[517:646])
414
+ if self.Version >= 25:
415
+ self.TimeSetting = TimeSettingRecord().fromstream(stream)
416
+ if self.Version >= 26:
417
+ unpacked_data = _struct.unpack('<llll', stream.read(16))
418
+ self.TotTimeDelayMove = unpacked_data[0]
419
+ self.TotTimeDelayCube = unpacked_data[1]
420
+ self.TotTimeDelayMoveDone = unpacked_data[2]
421
+ self.TotTimeDelayCubeDone = unpacked_data[3]
422
+ if self.Version >= 30:
423
+ unpacked_data = _struct.unpack('<129H', stream.read(258))
424
+ self.Transcriber = _xgutils.utf16intarraytostr(
425
+ unpacked_data[0:129])
426
+
427
+ return self
428
+
429
+
430
+ class FooterGameEntry(dict):
431
+
432
+ SIZEOFREC = 2560
433
+
434
+ def __init__(self, **kw):
435
+ defaults = {
436
+ 'Name': 'GameFooter',
437
+ 'EntryType': GameFileRecord.ENTRYTYPE_FOOTERGAME,
438
+ 'Score1g': 0, # Final score
439
+ 'Score2g': 0, # Final score
440
+ 'CrawfordApplyg': False, # will crawford apply next game
441
+ 'Winner': 0, # who win +1=player1, -1 player 2
442
+ 'PointsWon': 0, # point scored
443
+ 'Termination': 0, # 0=Drop 1=single 2=gammon 3=Backgamon
444
+ # (0,1,2)+100=Resign (0,1,2)+1000 settle
445
+ 'ErrResign': 0.0, # error made by resigning (-1000 if not analyze)
446
+ 'ErrTakeResign': 0.0, # error made by accepting the resign (-1000 if not analyze)
447
+ 'Eval': None, # evaluation of the final position
448
+ 'EvalLevel': 0
449
+ }
450
+ super(FooterGameEntry, self).__init__(defaults, **kw)
451
+
452
+ def __setattr__(self, key, value):
453
+ self[key] = value
454
+
455
+ def __getattr__(self, key):
456
+ return self[key]
457
+
458
+ def fromstream(self, stream):
459
+ unpacked_data = _struct.unpack('<9xxxxllBxxxlllxxxxdd7dl',
460
+ stream.read(116))
461
+ self.Score1g = unpacked_data[0]
462
+ self.Score2g = unpacked_data[1]
463
+ self.CrawfordApplyg = bool(unpacked_data[2])
464
+ self.Winner = unpacked_data[3]
465
+ self.PointsWon = unpacked_data[4]
466
+ self.Termination = unpacked_data[5]
467
+ self.ErrResign = unpacked_data[6]
468
+ self.ErrTakeResign = unpacked_data[7]
469
+ self.Eval = unpacked_data[8:15]
470
+ self.EvalLevel = unpacked_data[15]
471
+ return self
472
+
473
+
474
+ class MissingEntry(dict):
475
+
476
+ SIZEOFREC = 2560
477
+
478
+ def __init__(self, **kw):
479
+ defaults = {
480
+ 'Name': 'Missing',
481
+ 'EntryType': GameFileRecord.ENTRYTYPE_MISSING,
482
+ 'MissingErrLuck': 0.0,
483
+ 'MissingWinner': 0,
484
+ 'MissingPoints': 0
485
+ }
486
+ super(MissingEntry, self).__init__(defaults, **kw)
487
+
488
+ def __setattr__(self, key, value):
489
+ self[key] = value
490
+
491
+ def __getattr__(self, key):
492
+ return self[key]
493
+
494
+ def fromstream(self, stream):
495
+ unpacked_data = _struct.unpack('<9xxxxxxxxdll', stream.read(32))
496
+ self.MissingErrLuck = unpacked_data[0]
497
+ self.MissingWinner = unpacked_data[1]
498
+ self.MissingPoints = unpacked_data[2]
499
+ return self
500
+
501
+
502
+ class FooterMatchEntry(dict):
503
+
504
+ SIZEOFREC = 2560
505
+
506
+ def __init__(self, **kw):
507
+ defaults = {
508
+ 'Name': 'MatchFooter',
509
+ 'EntryType': GameFileRecord.ENTRYTYPE_FOOTERMATCH,
510
+ 'Score1m': 0, # Final score of the match
511
+ 'Score2m': 0, # Final score of the match
512
+ 'WinnerM': 0, # who win +1=player1, -1 player 2
513
+ 'Elo1m': 0.0, # resulting elo, player1
514
+ 'Elo2m': 0.0, # resulting elo, player2
515
+ 'Exp1m': 0, # resulting exp, player1
516
+ 'Exp2m': 0, # resulting exp, player2
517
+ 'Datem': 0.0 # Date time of the match end
518
+ }
519
+ super(FooterMatchEntry, self).__init__(defaults, **kw)
520
+
521
+ def __setattr__(self, key, value):
522
+ self[key] = value
523
+
524
+ def __getattr__(self, key):
525
+ return self[key]
526
+
527
+ def fromstream(self, stream):
528
+ unpacked_data = _struct.unpack('<9xxxxlllddlld', stream.read(56))
529
+ self.Score1m = unpacked_data[0]
530
+ self.Score2m = unpacked_data[1]
531
+ self.WinnerM = unpacked_data[2]
532
+ self.Elo1m = unpacked_data[3]
533
+ self.Elo2m = unpacked_data[4]
534
+ self.Exp1m = unpacked_data[5]
535
+ self.Exp2m = unpacked_data[6]
536
+ self.Datem = str(_xgutils.delphidatetimeconv(unpacked_data[7]))
537
+
538
+ return self
539
+
540
+
541
+ class HeaderGameEntry(dict):
542
+
543
+ SIZEOFREC = 2560
544
+
545
+ def __init__(self, **kw):
546
+ defaults = {
547
+ 'Name': 'GameHeader',
548
+ 'EntryType': GameFileRecord.ENTRYTYPE_HEADERGAME,
549
+ 'Score1': 0, # initial score player1
550
+ 'Score2': 0, # initial score player1
551
+ 'CrawfordApply': False, # iDoes Crawford apply on that game
552
+ 'PosInit': (0,) * 26, # initial position
553
+ 'GameNumber': 0, # Game number (start at 1)
554
+ 'InProgress': False, # Game is still in progress
555
+ 'CommentHeaderGame': -1, # index of the game comment header in temp.xgc
556
+ 'CommentFooterGame': -1, # index of the game comment footer in temp.xgc
557
+ 'NumberOfAutoDoubles': 0 # v26: Number of Autodouble that happen in that game
558
+ # note that in the rest of the game the cube still start at 1.
559
+ # For display purpose or point calculation add the 2^NumberOfAutoDouble
560
+ }
561
+ super(HeaderGameEntry, self).__init__(defaults, **kw)
562
+
563
+ def __setattr__(self, key, value):
564
+ self[key] = value
565
+
566
+ def __getattr__(self, key):
567
+ return self[key]
568
+
569
+ def fromstream(self, stream):
570
+ unpacked_data = _struct.unpack('<9xxxxllB26bxlBxxxlll',
571
+ stream.read(68))
572
+ self.Score1 = unpacked_data[0]
573
+ self.Score2 = unpacked_data[1]
574
+ self.CrawfordApply = bool(unpacked_data[2])
575
+ self.PosInit = unpacked_data[3:29]
576
+ self.GameNumber = unpacked_data[29]
577
+ self.InProgress = bool(unpacked_data[30])
578
+ self.CommentHeaderGame = unpacked_data[31]
579
+ self.CommentFooterGame = unpacked_data[32]
580
+ if self.Version >= 26:
581
+ self.NumberOfAutoDoubles = unpacked_data[33]
582
+
583
+ return self
584
+
585
+
586
+ class CubeEntry(dict):
587
+
588
+ SIZEOFREC = 2560
589
+
590
+ def __init__(self, **kw):
591
+ defaults = {
592
+ 'Name': 'Cube',
593
+ 'EntryType': GameFileRecord.ENTRYTYPE_CUBE,
594
+ 'ActiveP': 0, # Active player (1 or 2)
595
+ 'Double': 0, # player double (0= no, 1=yes)
596
+ 'Take': 0, # opp take (0= no, 1=yes, 2=beaver )
597
+ 'BeaverR': 0, # player accept beaver (0= no, 1=yes, 2=raccoon)
598
+ 'RaccoonR': 0, # player accept raccoon (0= no, 1=yes)
599
+ 'CubeB': 0, # Cube value 0=center, +1=2 own, +2=4 own ... -1=2 opp, -2=4 opp
600
+ 'Position': None, # initial position
601
+ 'Doubled': None, # Analyze result
602
+ 'ErrCube': 0.0, # error made on doubling (-1000 if not analyze)
603
+ 'DiceRolled': None, # dice rolled
604
+ 'ErrTake': 0.0, # error made on taking (-1000 if not analyze)
605
+ 'RolloutIndexD': 0, # index of the Rollout in temp.xgr
606
+ 'CompChoiceD': 0, # 3-ply choice as Double+2*take
607
+ 'AnalyzeC': 0, # Level of the analyze
608
+ 'ErrBeaver': 0.0, # error made on beavering (-1000 if not analyze)
609
+ 'ErrRaccoon': 0.0, # error made on racconning (-1000 if not analyze)
610
+ 'AnalyzeCR': 0, # requested Level of the analyze (sometime a XGR+ request will stop at 4-ply when obivous)
611
+ 'isValid': 0, # invalid decision 0=Ok, 1=error, 2=invalid
612
+ 'TutorCube': 0, # player initial double in tutor mode (0= no, 1=yes)
613
+ 'TutorTake': 0, # player initial take in tutor mode (0= no, 1=yes)
614
+ 'ErrTutorCube': 0.0, # error initialy made on doubling (-1000 if not analyze)
615
+ 'ErrTutorTake': 0.0, # error initialy made on taking (-1000 if not analyze)
616
+ 'FlaggedDouble': False, # cube has been flagged
617
+ 'CommentCube': -1, # index of the cube comment in temp.xgc
618
+ 'EditedCube': False, # v24: Position was edited at this point
619
+ 'TimeDelayCube': False, # v26: position is marked for later RO
620
+ 'TimeDelayCubeDone': False, # v26: position later RO has been done
621
+ 'NumberOfAutoDoubleCube': 0, # v27: Number of Autodouble that happen in that game
622
+ 'TimeBot': 0, # v28: time left for both players
623
+ 'TimeTop': 0
624
+ }
625
+ super(CubeEntry, self).__init__(defaults, **kw)
626
+
627
+ def __setattr__(self, key, value):
628
+ self[key] = value
629
+
630
+ def __getattr__(self, key):
631
+ return self[key]
632
+
633
+ def fromstream(self, stream):
634
+ unpacked_data = _struct.unpack('<9xxxxllllll26bxx',
635
+ stream.read(64))
636
+ self.ActiveP = unpacked_data[0]
637
+ self.Double = unpacked_data[1]
638
+ self.Take = unpacked_data[2]
639
+ self.BeaverR = unpacked_data[3]
640
+ self.RaccoonR = unpacked_data[4]
641
+ self.CubeB = unpacked_data[5]
642
+ self.Position = unpacked_data[6:32]
643
+ self.Doubled = EngineStructDoubleAction().fromstream(stream)
644
+ unpacked_data = _struct.unpack('<xxxxd3Bxxxxxdlllxxxx' \
645
+ 'ddllbbxxxxxxddBxxxlBBBxlll',
646
+ stream.read(116))
647
+ self.ErrCube = unpacked_data[0]
648
+ self.DiceRolled = _xgutils.delphishortstrtostr(unpacked_data[1:4])
649
+ self.ErrTake = unpacked_data[4]
650
+ self.RolloutIndexD = unpacked_data[5]
651
+ self.CompChoiceD = unpacked_data[6]
652
+ self.AnalyzeC = unpacked_data[7]
653
+ self.ErrBeaver = unpacked_data[8]
654
+ self.ErrRaccoon = unpacked_data[9]
655
+ self.AnalyzeCR = unpacked_data[10]
656
+ self.isValid = unpacked_data[11]
657
+ self.TutorCube = unpacked_data[12]
658
+ self.TutorTake = unpacked_data[13]
659
+ self.ErrTutorCube = unpacked_data[14]
660
+ self.ErrTutorTake = unpacked_data[15]
661
+ self.FlaggedDouble = bool(unpacked_data[16])
662
+ self.CommentCube = unpacked_data[17]
663
+ if self.Version >= 24:
664
+ self.EditedCube = bool(unpacked_data[18])
665
+ if self.Version >= 26:
666
+ self.TimeDelayCube = bool(unpacked_data[19])
667
+ self.TimeDelayCubeDone = bool(unpacked_data[20])
668
+ if self.Version >= 27:
669
+ self.NumberOfAutoDoubleCube = unpacked_data[21]
670
+ if self.Version >= 28:
671
+ self.TimeBot = unpacked_data[22]
672
+ self.TimeTop = unpacked_data[23]
673
+ return self
674
+
675
+
676
+ class MoveEntry(dict):
677
+
678
+ SIZEOFREC = 2560
679
+
680
+ def __init__(self, **kw):
681
+ defaults = {
682
+ 'Name:': 'Move',
683
+ 'EntryType': GameFileRecord.ENTRYTYPE_MOVE,
684
+ 'PositionI': None, # Initial position
685
+ 'PositionEnd': None, # Final Position
686
+ 'ActiveP': 0, # active player (1,2)
687
+ 'Moves': None, # list of move as From1,dice1, from2,dice2 etc.. -1 show termination of list
688
+ 'Dice': None, # dice rolled
689
+ 'CubeA': 0, # Cube value 0=center, +1=2 own, +2=4 own ... -1=2 opp, -2=4 opp
690
+ 'ErrorM': 0, # Not used anymore (not sure)
691
+ 'NMoveEval': 0, # Number of candidate (max 32)
692
+ 'DataMoves': None, # analyze
693
+ 'Played': False, # move was played
694
+ 'ErrMove': 0.0, # error made (-1000 if not analyze)
695
+ 'ErrLuck': 0.0, # luck of the roll
696
+ 'CompChoice': 0, # computer choice (index to DataMoves.moveplayed)
697
+ 'InitEq': 0.0, # Equity before the roll (for luck purposes)
698
+ 'RolloutIndexM': None, # index of the Rollout in temp.xgr
699
+ 'AnalyzeM': 0, # level of analyze of the move
700
+ 'AnalyzeL': 0, # level of analyze for the luck
701
+ 'InvalidM': 0, # invalid decision 0=Ok, 1=error, 2=invalid
702
+ 'PositionTutor': None, # Position resulting of the initial move
703
+ 'Tutor': 0, # index of the move played dataMoves.moveplayed
704
+ 'ErrTutorMove': 0.0, # error initialy made (-1000 if not analyze)
705
+ 'Flagged': False, # move has been flagged
706
+ 'CommentMove': -1, # index of the move comment in temp.xgc
707
+ 'EditedMove': False, # v24: Position was edited at this point
708
+ 'TimeDelayMove': 0, # v26: Bit list: position is marked for later RO
709
+ 'TimeDelayMoveDone': 0, # v26: Bit list: position later RO has been done
710
+ 'NumberOfAutoDoubleMove': 0 # v27: Number of Autodouble that happen in that game
711
+ }
712
+ super(MoveEntry, self).__init__(defaults, **kw)
713
+
714
+ def __setattr__(self, key, value):
715
+ self[key] = value
716
+
717
+ def __getattr__(self, key):
718
+ return self[key]
719
+
720
+ def fromstream(self, stream):
721
+ unpacked_data = _struct.unpack('<9x26b26bxxxl8l2lldl',
722
+ stream.read(124))
723
+ self.PositionI = unpacked_data[0:26]
724
+ self.PositionEnd = unpacked_data[26:52]
725
+ self.ActiveP = unpacked_data[52]
726
+ self.Moves = unpacked_data[53:61]
727
+ self.Dice = unpacked_data[61:63]
728
+ self.CubeA = unpacked_data[63]
729
+ self.ErrorM = unpacked_data[64] # Not used
730
+ self.NMoveEval = unpacked_data[65]
731
+ self.DataMoves = EngineStructBestMoveRecord().fromstream(stream)
732
+
733
+ unpacked_data = _struct.unpack('<Bxxxddlxxxxd32llll26bbxdBxxxl',
734
+ stream.read(220))
735
+ self.Played = bool(unpacked_data[0])
736
+ self.ErrMove = unpacked_data[1]
737
+ self.ErrLuck = unpacked_data[2]
738
+ self.CompChoice = unpacked_data[3]
739
+ self.InitEq = unpacked_data[4]
740
+ self.RolloutIndexM = unpacked_data[5:37]
741
+ self.AnalyzeM = unpacked_data[37]
742
+ self.AnalyzeL = unpacked_data[38]
743
+ self.InvalidM = unpacked_data[39]
744
+ self.PositionTutor = unpacked_data[40:66]
745
+ self.Tutor = unpacked_data[66]
746
+ self.ErrTutorMove = unpacked_data[67]
747
+ self.Flagged = bool(unpacked_data[68])
748
+ self.CommentMove = unpacked_data[69]
749
+ if self.Version >= 24:
750
+ unpacked_data = _struct.unpack('<B', stream.read(1))
751
+ self.EditedMove = bool(unpacked_data[0])
752
+ if self.Version >= 26:
753
+ unpacked_data = _struct.unpack('<xxxLL', stream.read(11))
754
+ self.TimeDelayMove = unpacked_data[0]
755
+ self.TimeDelayMoveDone = unpacked_data[1]
756
+ if self.Version >= 27:
757
+ unpacked_data = _struct.unpack('<l', stream.read(4))
758
+ self.NumberOfAutoDoubleMove = unpacked_data[0]
759
+
760
+ return self
761
+
762
+
763
+ class UnimplementedEntry(dict):
764
+
765
+ """ Class for record types we have yet to implement
766
+ """
767
+
768
+ SIZEOFREC = 2560
769
+
770
+ def __init__(self, **kw):
771
+ defaults = {
772
+ 'Name': 'Unimplemented'
773
+ }
774
+ super(UnimplementedEntry, self).__init__(defaults, **kw)
775
+
776
+ def __setattr__(self, key, value):
777
+ self[key] = value
778
+
779
+ def __getattr__(self, key):
780
+ return self[key]
781
+
782
+ def fromstream(self, stream):
783
+ return self
784
+
785
+
786
+ class GameFileRecord(dict):
787
+
788
+ __SIZEOFSRHDR = 9
789
+ __REC_CLASSES = [HeaderMatchEntry, HeaderGameEntry,
790
+ CubeEntry, MoveEntry,
791
+ FooterGameEntry, FooterMatchEntry,
792
+ UnimplementedEntry, MissingEntry]
793
+
794
+ ENTRYTYPE_HEADERMATCH, ENTRYTYPE_HEADERGAME, ENTRYTYPE_CUBE, \
795
+ ENTRYTYPE_MOVE, ENTRYTYPE_FOOTERGAME, ENTRYTYPE_FOOTERMATCH, \
796
+ ENTRYTYPE_MISSING, ENTRYTYPE_UNIMPLEMENTED = range(8)
797
+
798
+ def __init__(self, version=-1, **kw):
799
+ """ Create a game file record based upon the given file version
800
+ number. The file version is first found in a HeaderMatchEntry
801
+ object. The version needs to be propogated to all other game
802
+ file objects within the same archive.
803
+ """
804
+ defaults = {
805
+ 'Name': 'GameFileRecord',
806
+ 'EntryType': -1,
807
+ 'Record': None,
808
+ 'Version': version
809
+ }
810
+ super(GameFileRecord, self).__init__(defaults, **kw)
811
+
812
+ def __setattr__(self, key, value):
813
+ self[key] = value
814
+
815
+ def __getattr__(self, key):
816
+ return self[key]
817
+
818
+ def fromstream(self, stream):
819
+ # Read the header. First 8 bytes are unused. 9th byte is record type
820
+ # The record type determines what object to create and load.
821
+ # If we catch a struct.error we have hit the EOF.
822
+ startpos = stream.tell()
823
+ try:
824
+ unpacked_data = _struct.unpack('<8xB',
825
+ stream.read(self.__SIZEOFSRHDR))
826
+ except _struct.error:
827
+ return None
828
+ self.EntryType = unpacked_data[0]
829
+
830
+ # Back up to the beginning of the record after getting the record
831
+ # type and feed the entire stream back into the corresponding
832
+ # record object.
833
+ stream.seek(-self.__SIZEOFSRHDR, _os.SEEK_CUR)
834
+
835
+ # Using the appropriate class, read the data stream
836
+ self.Record = self.__REC_CLASSES[self.EntryType]()
837
+ self.Record.Version = self.Version
838
+ self.Record.fromstream(stream)
839
+ realrecsize = stream.tell() - startpos
840
+
841
+ # Each record is actually 2560 bytes long. We need to advance past
842
+ # the unused filler data to be at the start of the next record
843
+ stream.seek(self.Record.SIZEOFREC - realrecsize, _os.SEEK_CUR)
844
+
845
+ return self.Record
846
+
847
+
848
+ class RolloutContextEntry(dict):
849
+
850
+ SIZEOFREC = 2184
851
+
852
+ def __init__(self, **kw):
853
+ defaults = {
854
+ 'Name': 'Rollout',
855
+ 'EntryType': RolloutFileRecord.ROLLOUTCONTEXT,
856
+ 'Truncated': False, # is truncated
857
+ 'ErrorLimited': False, # stop when CI under "ErrorLimit"
858
+ 'Truncate': 0, # truncation level
859
+ 'MinRoll': 0, # minimum games to roll
860
+ 'ErrorLimit': 0.0, # CI to stop the RO
861
+ 'MaxRoll': 0, # maximum games to roll
862
+ 'Level1': 0, # checker play Level used before "LevelCut"
863
+ 'Level2': 0, # checker play Level used on and after "LevelCut"
864
+ 'LevelCut': 0, # Cutoff for level1 and level2
865
+ 'Variance': False, # use variance reduction
866
+ 'Cubeless': False, # is a cubeless ro
867
+ 'Time': False, # is time limited
868
+ 'Level1C': 0, # cube Level used before "LevelCut"
869
+ 'Level2C': 0, # cube Level used on and after "LevelCut"
870
+ 'TimeLimit': 0, # limit in time (min)
871
+ 'TruncateBO': 0, # what do do when reaching BO db: 0=nothing; 1=?
872
+ 'RandomSeed': 0, # caculated seed=RandomSeedI+hashpos
873
+ 'RandomSeedI': 0, # used entered seed
874
+ 'RollBoth': False, # roll both line (ND and D/T)
875
+ 'SearchInterval': 0.0, # Search interval used (1=normal, 1.5=large, 2=huge, 4=gigantic)
876
+ 'met': 0, # unused
877
+ 'FirstRoll': False, # is it a first roll rollout
878
+ 'DoDouble': False, # roll both line (ND and D/T) in multiple rollout
879
+ 'Extent': False, # if the ro is extended
880
+ 'Rolled': 0, # game rolled
881
+ 'DoubleFirst': False, # a double happens immediatly.
882
+ 'Sum1': None, # sum of equities for all 36 1st roll
883
+ 'SumSquare1': None, # sum of square equities for all 36 1st roll
884
+ 'Sum2': None, # D/T sum of equities for all 36 1st roll
885
+ 'SumSquare2': None, # D/T sum of square equities for all 36 1st roll
886
+ 'Stdev1': None, # Standard deviation for all 36 1st roll
887
+ 'Stdev2': None, # D/T Stand deviation for all 36 1st roll
888
+ 'RolledD': None, # number of game rolled for all 36 1st roll
889
+ 'Error1': 0.0, # 95% CI
890
+ 'Error2': 0.0, # D/T 95% CI
891
+ 'Result1': None, # evaluation of the position
892
+ 'Result2': None, # D/T evaluation of the position
893
+ 'Mwc1': 0.0, # ND mwc equivalent of result1[1,6]
894
+ 'Mwc2': 0.0, # D/T mwc equivalent of result2[1,6]
895
+ 'PrevLevel': 0, # store the previous analyze level (for deleting RO)
896
+ 'PrevEval': None, # store the previous analyze result (for deleting RO)
897
+ 'PrevND': 0, # store the previous analyze equities (for deleting RO)
898
+ 'PrevD': 0,
899
+ 'Duration': 0, # duration in seconds
900
+ 'LevelTrunc': 0, # level used at truncation
901
+ 'Rolled2': 0, # D/T number of game rolled
902
+ 'MultipleMin': 0, # Multiple RO minimum # of game
903
+ 'MultipleStopAll': False, # Multiple RO stop all if one move reach MultipleStopAllValue
904
+ 'MultipleStopOne': False, # Multiple RO stop one move is reach under MultipleStopOneValue
905
+ 'MultipleStopAllValue': 0.0, # value to stop all RO (for instance 99.9%)
906
+ 'MultipleStopOneValue': 0.0, # value to stop one move(for instance 0.01%)
907
+ 'AsTake': False, # when running ND and D/T if AsTake is true, checker decision are made using the cube position in the D/T line
908
+ 'Rotation': 0, # 0=36 dice, 1=21 dice (XG1), 2=30 dice (for 1st pos)
909
+ 'UserInterrupted': False, # RO was interrupted by user
910
+ 'VerMaj': 0, # Major version use for the RO, currently (2.20): 2
911
+ 'VerMin': 0 # Minor version use for the RO, currently (2.10): 10 (no change in RO or engine between 2.10 and 2.20)
912
+ }
913
+ super(RolloutContextEntry, self).__init__(defaults, **kw)
914
+
915
+ def __setattr__(self, key, value):
916
+ self[key] = value
917
+
918
+ def __getattr__(self, key):
919
+ return self[key]
920
+
921
+ def fromstream(self, stream):
922
+ unpacked_data = _struct.unpack('<BBxxllxxxxdllllBBBxllLlllBxxx' \
923
+ 'flBBBxlBxxxxxxx37d37d37d37d37d37d37l' \
924
+ 'ff7f7fffl7fllllllBBxxffBxxxlBxHH',
925
+ stream.read(2174))
926
+
927
+ self.Truncated = bool(unpacked_data[0])
928
+ self.ErrorLimited = bool(unpacked_data[1])
929
+ self.Truncate = unpacked_data[2]
930
+ self.MinRoll = unpacked_data[3]
931
+ self.ErrorLimit = unpacked_data[4]
932
+ self.MaxRoll = unpacked_data[5]
933
+ self.Level1 = unpacked_data[6]
934
+ self.Level2 = unpacked_data[7]
935
+ self.LevelCut = unpacked_data[8]
936
+ self.Variance = bool(unpacked_data[9])
937
+ self.Cubeless = bool(unpacked_data[10])
938
+ self.Time = bool(unpacked_data[11])
939
+ self.Level1C = unpacked_data[12]
940
+ self.Level2C = unpacked_data[13]
941
+ self.TimeLimit = unpacked_data[14]
942
+ self.TruncateBO = unpacked_data[15]
943
+ self.RandomSeed = unpacked_data[16]
944
+ self.RandomSeedI = unpacked_data[17]
945
+ self.RollBoth = bool(unpacked_data[18])
946
+ self.SearchInterval = unpacked_data[19]
947
+ self.met = unpacked_data[20]
948
+ self.FirstRoll = bool(unpacked_data[21])
949
+ self.DoDouble = bool(unpacked_data[22])
950
+ self.Extent = bool(unpacked_data[23])
951
+ self.Rolled = unpacked_data[24]
952
+ self.DoubleFirst = bool(unpacked_data[25])
953
+ self.Sum1 = unpacked_data[26:63]
954
+ self.SumSquare1 = unpacked_data[63:100]
955
+ self.Sum2 = unpacked_data[100:137]
956
+ self.SumSquare2 = unpacked_data[137:174]
957
+ self.Stdev1 = unpacked_data[174:211]
958
+ self.Stdev2 = unpacked_data[211:248]
959
+ self.RolledD = unpacked_data[248:285]
960
+ self.Error1 = unpacked_data[285]
961
+ self.Error2 = unpacked_data[286]
962
+ self.Result1 = unpacked_data[287:294]
963
+ self.Result2 = unpacked_data[294:301]
964
+ self.Mwc1 = unpacked_data[301]
965
+ self.Mwc2 = unpacked_data[302]
966
+ self.PrevLevel = unpacked_data[303]
967
+ self.PrevEval = unpacked_data[304:311]
968
+ self.PrevND = unpacked_data[311]
969
+ self.PrevD = unpacked_data[312]
970
+ self.Duration = unpacked_data[313]
971
+ self.LevelTrunc = unpacked_data[314]
972
+ self.Rolled2 = unpacked_data[315]
973
+ self.MultipleMin = unpacked_data[316]
974
+ self.MultipleStopAll = bool(unpacked_data[317])
975
+ self.MultipleStopOne = bool(unpacked_data[318])
976
+ self.MultipleStopAllValue = unpacked_data[319]
977
+ self.MultipleStopOneValue = unpacked_data[320]
978
+ self.AsTake = bool(unpacked_data[321])
979
+ self.Rotation = unpacked_data[322]
980
+ self.UserInterrupted = bool(unpacked_data[323])
981
+ self.VerMaj = unpacked_data[324]
982
+ self.VerMin = unpacked_data[325]
983
+
984
+ return self
985
+
986
+
987
+ class RolloutFileRecord(dict):
988
+
989
+ ROLLOUTCONTEXT = 0
990
+
991
+ def __init__(self, version=-1, **kw):
992
+ """ Create a game file record based upon the given file version
993
+ number. The file version is first found in a HeaderMatchEntry
994
+ object. The version needs to be propogated to all other game
995
+ file objects within the same archive.
996
+ """
997
+ defaults = {
998
+ 'Name': 'RolloutFileRecord',
999
+ 'EntryType': 0,
1000
+ 'Record': None,
1001
+ 'Version': version
1002
+ }
1003
+ super(RolloutFileRecord, self).__init__(defaults, **kw)
1004
+
1005
+ def __setattr__(self, key, value):
1006
+ self[key] = value
1007
+
1008
+ def __getattr__(self, key):
1009
+ return self[key]
1010
+
1011
+ def fromstream(self, stream):
1012
+ # If we are at EOF then return
1013
+ if len(stream.read(1)) <= 0:
1014
+ return None
1015
+
1016
+ stream.seek(-1, _os.SEEK_CUR)
1017
+ startpos = stream.tell()
1018
+
1019
+ # Using the appropriate class, read the data stream
1020
+ self.Record = RolloutContextEntry()
1021
+ self.Record.Version = self.Version
1022
+ self.Record.fromstream(stream)
1023
+ realrecsize = stream.tell() - startpos
1024
+ # Each record is actually 2184 bytes long. We need to advance past
1025
+ # the unused filler data to be at the start of the next record
1026
+ stream.seek(self.Record.SIZEOFREC - realrecsize, _os.SEEK_CUR)
1027
+
1028
+ return self.Record
1029
+
1030
+
1031
+ if __name__ == '__main__':
1032
+ pass