pywargame 0.3.1__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 (150) hide show
  1. pywargame/__init__.py +2 -0
  2. pywargame/common/__init__.py +3 -0
  3. pywargame/common/collector.py +87 -0
  4. pywargame/common/dicedraw.py +363 -0
  5. pywargame/common/drawdice.py +40 -0
  6. pywargame/common/singleton.py +22 -0
  7. pywargame/common/test.py +25 -0
  8. pywargame/common/verbose.py +59 -0
  9. pywargame/common/verboseguard.py +53 -0
  10. pywargame/cyberboard/__init__.py +18 -0
  11. pywargame/cyberboard/archive.py +283 -0
  12. pywargame/cyberboard/base.py +63 -0
  13. pywargame/cyberboard/board.py +462 -0
  14. pywargame/cyberboard/cell.py +200 -0
  15. pywargame/cyberboard/collect.py +49 -0
  16. pywargame/cyberboard/collectgbx0pwd.py +30 -0
  17. pywargame/cyberboard/collectgbxext.py +30 -0
  18. pywargame/cyberboard/collectgsnexp.py +32 -0
  19. pywargame/cyberboard/collectgsnext.py +30 -0
  20. pywargame/cyberboard/draw.py +396 -0
  21. pywargame/cyberboard/exporter.py +1132 -0
  22. pywargame/cyberboard/extractor.py +240 -0
  23. pywargame/cyberboard/features.py +17 -0
  24. pywargame/cyberboard/gamebox.py +81 -0
  25. pywargame/cyberboard/gbxexp.py +76 -0
  26. pywargame/cyberboard/gbxext.py +64 -0
  27. pywargame/cyberboard/gsnexp.py +147 -0
  28. pywargame/cyberboard/gsnext.py +59 -0
  29. pywargame/cyberboard/head.py +111 -0
  30. pywargame/cyberboard/image.py +76 -0
  31. pywargame/cyberboard/main.py +47 -0
  32. pywargame/cyberboard/mark.py +102 -0
  33. pywargame/cyberboard/palette.py +36 -0
  34. pywargame/cyberboard/piece.py +169 -0
  35. pywargame/cyberboard/player.py +36 -0
  36. pywargame/cyberboard/scenario.py +115 -0
  37. pywargame/cyberboard/testgrid.py +156 -0
  38. pywargame/cyberboard/tile.py +121 -0
  39. pywargame/cyberboard/tray.py +68 -0
  40. pywargame/cyberboard/windows.py +41 -0
  41. pywargame/cyberboard/zeropwd.py +45 -0
  42. pywargame/cyberboard.py +2728 -0
  43. pywargame/gbx0pwd.py +2776 -0
  44. pywargame/gbxextract.py +2795 -0
  45. pywargame/gsnexport.py +16499 -0
  46. pywargame/gsnextract.py +2790 -0
  47. pywargame/latex/__init__.py +2 -0
  48. pywargame/latex/collect.py +34 -0
  49. pywargame/latex/latexexporter.py +4010 -0
  50. pywargame/latex/main.py +184 -0
  51. pywargame/vassal/__init__.py +66 -0
  52. pywargame/vassal/base.py +139 -0
  53. pywargame/vassal/board.py +243 -0
  54. pywargame/vassal/buildfile.py +60 -0
  55. pywargame/vassal/chart.py +79 -0
  56. pywargame/vassal/chessclock.py +197 -0
  57. pywargame/vassal/collect.py +98 -0
  58. pywargame/vassal/collectpatch.py +28 -0
  59. pywargame/vassal/command.py +21 -0
  60. pywargame/vassal/documentation.py +322 -0
  61. pywargame/vassal/dumpcollect.py +28 -0
  62. pywargame/vassal/dumpvsav.py +28 -0
  63. pywargame/vassal/element.py +439 -0
  64. pywargame/vassal/exporter.py +89 -0
  65. pywargame/vassal/extension.py +101 -0
  66. pywargame/vassal/folder.py +103 -0
  67. pywargame/vassal/game.py +940 -0
  68. pywargame/vassal/gameelements.py +1091 -0
  69. pywargame/vassal/globalkey.py +127 -0
  70. pywargame/vassal/globalproperty.py +433 -0
  71. pywargame/vassal/grid.py +573 -0
  72. pywargame/vassal/map.py +1061 -0
  73. pywargame/vassal/mapelements.py +1020 -0
  74. pywargame/vassal/merge.py +57 -0
  75. pywargame/vassal/merger.py +460 -0
  76. pywargame/vassal/moduledata.py +275 -0
  77. pywargame/vassal/mrgcollect.py +31 -0
  78. pywargame/vassal/patch.py +44 -0
  79. pywargame/vassal/patchcollect.py +28 -0
  80. pywargame/vassal/player.py +83 -0
  81. pywargame/vassal/save.py +495 -0
  82. pywargame/vassal/skel.py +380 -0
  83. pywargame/vassal/trait.py +224 -0
  84. pywargame/vassal/traits/__init__.py +36 -0
  85. pywargame/vassal/traits/area.py +50 -0
  86. pywargame/vassal/traits/basic.py +35 -0
  87. pywargame/vassal/traits/calculatedproperty.py +22 -0
  88. pywargame/vassal/traits/cargo.py +29 -0
  89. pywargame/vassal/traits/click.py +41 -0
  90. pywargame/vassal/traits/clone.py +28 -0
  91. pywargame/vassal/traits/delete.py +24 -0
  92. pywargame/vassal/traits/deselect.py +32 -0
  93. pywargame/vassal/traits/dynamicproperty.py +112 -0
  94. pywargame/vassal/traits/globalcommand.py +55 -0
  95. pywargame/vassal/traits/globalhotkey.py +26 -0
  96. pywargame/vassal/traits/globalproperty.py +54 -0
  97. pywargame/vassal/traits/hide.py +67 -0
  98. pywargame/vassal/traits/label.py +76 -0
  99. pywargame/vassal/traits/layer.py +105 -0
  100. pywargame/vassal/traits/mark.py +20 -0
  101. pywargame/vassal/traits/mask.py +85 -0
  102. pywargame/vassal/traits/mat.py +26 -0
  103. pywargame/vassal/traits/moved.py +35 -0
  104. pywargame/vassal/traits/movefixed.py +51 -0
  105. pywargame/vassal/traits/nonrect.py +95 -0
  106. pywargame/vassal/traits/nostack.py +55 -0
  107. pywargame/vassal/traits/place.py +104 -0
  108. pywargame/vassal/traits/prototype.py +20 -0
  109. pywargame/vassal/traits/report.py +34 -0
  110. pywargame/vassal/traits/restrictaccess.py +28 -0
  111. pywargame/vassal/traits/restrictcommand.py +32 -0
  112. pywargame/vassal/traits/return.py +40 -0
  113. pywargame/vassal/traits/rotate.py +62 -0
  114. pywargame/vassal/traits/sendto.py +59 -0
  115. pywargame/vassal/traits/sheet.py +129 -0
  116. pywargame/vassal/traits/skel.py +9 -0
  117. pywargame/vassal/traits/stack.py +28 -0
  118. pywargame/vassal/traits/submenu.py +27 -0
  119. pywargame/vassal/traits/trail.py +61 -0
  120. pywargame/vassal/traits/trigger.py +72 -0
  121. pywargame/vassal/turn.py +272 -0
  122. pywargame/vassal/upgrade.py +191 -0
  123. pywargame/vassal/vmod.py +323 -0
  124. pywargame/vassal/vsav.py +100 -0
  125. pywargame/vassal/widget.py +358 -0
  126. pywargame/vassal/withtraits.py +634 -0
  127. pywargame/vassal/xml.py +4 -0
  128. pywargame/vassal/zone.py +399 -0
  129. pywargame/vassal.py +12500 -0
  130. pywargame/vmodpatch.py +12548 -0
  131. pywargame/vsavdump.py +12533 -0
  132. pywargame/vslmerge.py +13015 -0
  133. pywargame/wgexport.py +16689 -0
  134. pywargame/ztexport.py +14351 -0
  135. pywargame/zuntzu/__init__.py +5 -0
  136. pywargame/zuntzu/base.py +82 -0
  137. pywargame/zuntzu/collect.py +38 -0
  138. pywargame/zuntzu/countersheet.py +250 -0
  139. pywargame/zuntzu/dicehand.py +48 -0
  140. pywargame/zuntzu/exporter.py +936 -0
  141. pywargame/zuntzu/gamebox.py +154 -0
  142. pywargame/zuntzu/map.py +36 -0
  143. pywargame/zuntzu/piece.py +37 -0
  144. pywargame/zuntzu/scenario.py +208 -0
  145. pywargame/zuntzu/ztexp.py +115 -0
  146. pywargame-0.3.1.dist-info/METADATA +353 -0
  147. pywargame-0.3.1.dist-info/RECORD +150 -0
  148. pywargame-0.3.1.dist-info/WHEEL +5 -0
  149. pywargame-0.3.1.dist-info/licenses/LICENSE +5 -0
  150. pywargame-0.3.1.dist-info/top_level.txt +1 -0
pywargame/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ '''Top-level of module'''
2
+ version = '0.3.0'
@@ -0,0 +1,3 @@
1
+ from . singleton import Singleton
2
+ from . verbose import Verbose
3
+ from . verboseguard import VerboseGuard
@@ -0,0 +1,87 @@
1
+ ## BEGIN_IMPORT
2
+ from . verboseguard import VerboseGuard
3
+ from . verbose import Verbose
4
+ ## END_IMPORT
5
+
6
+
7
+ class Collector:
8
+ def __init__(self,executable=True):
9
+ '''This could be done with SED, but we don't need to rely on that'''
10
+ self._executable = executable
11
+
12
+ def readFile(self,filename):
13
+ '''Reads lines from a file and returns them.
14
+
15
+ Lines fenced by with `##` are not returned.
16
+
17
+ Lines starting with `#!` are not returned
18
+ '''
19
+ with VerboseGuard(f'Reading lines from {filename}') as g:
20
+ ret = [f'# {"="*68}\n'
21
+ f'# From {filename}\n' ]
22
+ with open(filename,'r') as file:
23
+ lines = file.readlines()
24
+ nlines = len(lines)
25
+ lineno = 0
26
+
27
+ g(f'{filename} has {nlines} lines')
28
+ while lineno < nlines:
29
+ line = lines[lineno]
30
+ if line.strip().startswith('##'):
31
+ start = line
32
+ startno = lineno
33
+
34
+ while lineno < nlines:
35
+ lineno += 1
36
+ if lineno >= nlines:
37
+ raise RuntimeError(f'At {filename}:{lineno}:'
38
+ f' No end to {start} in '
39
+ f'line {startno}')
40
+
41
+ line = lines[lineno]
42
+ if line.strip().startswith('##'):
43
+ g(f'Skipped lines {startno} to {lineno}')
44
+ break
45
+ lineno += 1
46
+ continue
47
+
48
+ if line.startswith('#!'):
49
+ g(f'Skipping line {lineno}')
50
+ lineno += 1
51
+ continue
52
+
53
+ ret.append(line)
54
+ lineno += 1
55
+
56
+ return ret
57
+
58
+ def run(self,output,*filenames):
59
+ lines = ['#!/usr/bin/env python\n',
60
+ '# Script collected from other scripts\n',
61
+ '#\n']
62
+ lines.extend([f'# {filename}\n' for filename in filenames])
63
+ lines.append('#\n')
64
+
65
+
66
+ for filename in filenames:
67
+ lines.extend(self.readFile(filename))
68
+
69
+ lines.extend(['##\n',
70
+ '# End of generated script\n'
71
+ '##\n'])
72
+
73
+
74
+ output.writelines(lines)
75
+
76
+ if not self._executable:
77
+ return
78
+
79
+ from os import chmod
80
+ chmod(output.name,0o755)
81
+
82
+
83
+ #
84
+ # EOF
85
+ #
86
+
87
+
@@ -0,0 +1,363 @@
1
+ # --------------------------------------------------------------------
2
+ class DiceContext:
3
+ def __init__(self,draw,darker=None):
4
+ self._draw = draw
5
+ self._darker = darker
6
+
7
+
8
+
9
+ def __enter__(self):
10
+ from wand.color import Color
11
+ self._draw._draw.push()
12
+
13
+ if self._darker:
14
+ r, g, b = (self._draw._draw.fill_color.red_int8,
15
+ self._draw._draw.fill_color.green_int8,
16
+ self._draw._draw.fill_color.blue_int8)
17
+ r *= self._darker
18
+ g *= self._darker
19
+ b *= self._darker
20
+ #print(r,g,b)
21
+ self._draw._draw.fill_color = \
22
+ Color(f'srgb({int(r)},{int(g)},{int(b)})')
23
+
24
+ return self._draw
25
+
26
+ def __exit__(self,*e):
27
+ self._draw._draw.pop()
28
+
29
+ # --------------------------------------------------------------------
30
+ class DicePath:
31
+ def __init__(self,draw):
32
+ self._draw = draw
33
+
34
+ def __enter__(self):
35
+ self._draw._draw.path_start()
36
+ return self
37
+
38
+ def __exit__(self,*e):
39
+ self._draw._draw.path_finish()
40
+
41
+ def move(self,to):
42
+ self._draw._draw.path_move(to=(self._draw.x(to[0]),
43
+ self._draw.y(to[1])))
44
+
45
+ def line(self,to):
46
+ self._draw._draw.path_line(to=(self._draw.x(to[0]),
47
+ self._draw.y(to[1])))
48
+
49
+ def arc(self,to,r,cw=True):
50
+ self._draw._draw.path_elliptic_arc(to=(self._draw.x(to[0]),
51
+ self._draw.y(to[1])),
52
+ radius=(self._draw.x(r),
53
+ self._draw.y(r)),
54
+ clockwise=cw)
55
+
56
+ # --------------------------------------------------------------------
57
+ class DiceDraw:
58
+ def __init__(self,width=100,height=100,fg='black',bg='white'):
59
+ from wand.drawing import Drawing
60
+ from wand.color import Color
61
+
62
+ self._width = width
63
+ self._height = height
64
+ self._fg = fg if isinstance(fg,Color) else Color(fg)
65
+ self._bg = bg if isinstance(bg,Color) else Color(bg)
66
+ self._draw = Drawing()
67
+ self._size = min(self._width,self._height)
68
+
69
+ @property
70
+ def size(self):
71
+ return self._size
72
+
73
+ def x(self,xx):
74
+ return int((xx + 0.5) * self.size)
75
+
76
+ def y(self,yy):
77
+ return int((0.5 - yy) * self.size)
78
+
79
+ def __enter__(self):
80
+ self._draw.stroke_width = max(1,self.size//75)
81
+ self._draw.stroke_color = self._fg
82
+ self._draw.fill_color = self._bg
83
+ return self
84
+
85
+ def number(self,num,yoff=0,scale=1):
86
+ if num is None or num == '':
87
+ return
88
+
89
+ with DiceContext(self):
90
+ self._draw.stroke_width = 1
91
+ self._draw.stroke_color = self._fg
92
+ self._draw.fill_color = self._fg
93
+ self._draw.text_alignment = 'center'
94
+ self._draw.font_size = scale * self.size / 2
95
+ self._draw.text(self.x(0),
96
+ int(self.y(yoff)+self._draw.font_size//2),
97
+ f'{num}')
98
+
99
+ def image(self):
100
+ from wand.image import Image as WImage
101
+
102
+ image = WImage(width=self._width,height=self._height,format='png')
103
+ image.alpha_channel = True
104
+ self._draw(image)
105
+
106
+ off = min(self._width,self._height) // 30
107
+ copy = image.clone()
108
+ copy.shadow(50,off,0,0)
109
+ copy.negate(channel='rgb')
110
+ copy.composite(image,int(off/4),int(off/4))
111
+
112
+ #copy.save(filename='d4.png')
113
+ return copy
114
+
115
+ def __exit__(self,*e):
116
+ pass
117
+
118
+ # --------------------------------------------------------------------
119
+ class DiceDrawer:
120
+ def __init__(self,nsides,width,height,fg='red',bg='white'):
121
+ from wand.color import Color
122
+ self._nsides = nsides
123
+ self._width = width
124
+ self._height = height
125
+ self._fg = Color(fg if isinstance(fg,str) else f'#{fg:06x}')
126
+ self._bg = Color(bg if isinstance(bg,str) else f'#{bg:06x}')
127
+ if self._nsides not in [4,6,8,10,12,20]:
128
+ raise RuntimeError(f'Unknown number of sides: {self._nsides}')
129
+
130
+
131
+ def draw(self,num):
132
+ if self._nsides == 4: return self.draw_d4 (num)
133
+ if self._nsides == 6: return self.draw_d6 (num)
134
+ if self._nsides == 8: return self.draw_d8 (num)
135
+ if self._nsides == 10: return self.draw_d10(num)
136
+ if self._nsides == 12: return self.draw_d12(num)
137
+ if self._nsides == 20: return self.draw_d20(num)
138
+ return None
139
+
140
+ def draw_d4(self,num):
141
+ with DiceDraw(self._width,self._height,
142
+ fg=self._fg,bg=self._bg) as draw:
143
+ with DicePath(draw) as path:
144
+ path.move(to=( 0.000, 0.40))
145
+ path.line(to=( 0.433,-0.35))
146
+ path.line(to=(-0.433,-0.35))
147
+ path.line(to=( 0.000, 0.40))
148
+
149
+ draw.number(num)
150
+ return draw.image()
151
+
152
+ def draw_d6(self,num):
153
+ with DiceDraw(self._width,self._height,
154
+ fg=self._fg,bg=self._bg) as draw:
155
+ with DicePath(draw) as path:
156
+ r = 0.05
157
+ w = .4 - r
158
+ path.move(to=( w, 0.40))
159
+ path.arc (to=( 0.40, w), r=r)
160
+ path.line(to=( 0.40,- w))
161
+ path.arc (to=( w,-0.40), r=r)
162
+ path.line(to=(- w,-0.40))
163
+ path.arc (to=(-0.40,- w), r=r)
164
+ path.line(to=(-0.40, w))
165
+ path.arc (to=(- w, 0.40), r=r)
166
+ path.line(to=( w, 0.40))
167
+
168
+ draw.number(num,yoff=.1)
169
+ return draw.image()
170
+
171
+ def draw_d8(self,num):
172
+ with DiceDraw(self._width,self._height,
173
+ fg=self._fg,bg=self._bg) as draw:
174
+ with DiceContext(draw,darker=.9):
175
+ with DicePath(draw) as path:
176
+ path.move(to=(0.0000,0.5000))
177
+ path.line(to=(0.4330,0.2500))
178
+ path.line(to=(0.4330,-0.2500))
179
+ path.line(to=(0.0000,-0.5000))
180
+ path.line(to=(-0.4330,-0.2500))
181
+ path.line(to=(-0.4330,0.2500))
182
+ path.line(to=(0.0000,0.5000))
183
+
184
+ with DicePath(draw) as path:
185
+ path.move(to=(0.0000,0.5000))
186
+ path.line(to=(0.4330,-0.2500))
187
+ path.line(to=(-0.4330,-0.2500))
188
+ path.line(to=(0.0000,0.5000))
189
+
190
+ draw.number(num,yoff=.1)
191
+ return draw.image()
192
+
193
+ def draw_d10(self,num):
194
+ with DiceDraw(self._width,self._height,
195
+ fg=self._fg,bg=self._bg) as draw:
196
+ with DiceContext(draw,darker=.9):
197
+ with DicePath(draw) as path:
198
+ path.move(to=(0.0000,0.5000))
199
+ path.line(to=(0.4750,0.1000))
200
+ path.line(to=(0.4750,-0.1000))
201
+ path.line(to=(0.0000,-0.5000))
202
+ path.line(to=(-0.4750,-0.1000))
203
+ path.line(to=(-0.4750,0.1000))
204
+ path.line(to=(0.0000,0.5000))
205
+ path.move(to=(0.2940,-0.1540))
206
+ path.line(to=(0.4750,-0.1000))
207
+ path.move(to=(-0.4750,-0.1000))
208
+ path.line(to=(-0.2940,-0.1540))
209
+ path.move(to=(0.0000,-0.5000))
210
+ path.line(to=(0.0000,-0.3000))
211
+ with DicePath(draw) as path:
212
+ path.move(to=(0.0000,0.5000))
213
+ path.line(to=(0.2940,-0.1540))
214
+ path.line(to=(0.0000,-0.3000))
215
+ path.line(to=(-0.2940,-0.1540))
216
+ path.line(to=(0.0000,0.5000))
217
+
218
+ draw.number(num,yoff=.1)
219
+ return draw.image()
220
+
221
+ def draw_d12(self,num):
222
+ with DiceDraw(self._width,self._height,
223
+ fg=self._fg,bg=self._bg) as draw:
224
+ with DiceContext(draw,darker=.9):
225
+ with DicePath(draw) as path:
226
+ path.move(to=( 0.0000, 0.5000))
227
+ path.line(to=( 0.2940, 0.4050))
228
+ path.line(to=( 0.4750, 0.1730))
229
+ path.line(to=( 0.4750,-0.1730))
230
+ path.line(to=( 0.2940,-0.4050))
231
+ path.line(to=( 0.0000,-0.5000))
232
+ path.line(to=(-0.2940,-0.4050))
233
+ path.line(to=(-0.4750,-0.1730))
234
+ path.line(to=(-0.4750, 0.1730))
235
+ path.line(to=(-0.2940, 0.4050))
236
+ path.line(to=( 0.0000, 0.5000))
237
+ path.line(to=( 0.0000, 0.3490))
238
+ path.move(to=( 0.4750, 0.1730))
239
+ path.line(to=( 0.3320, 0.1080))
240
+ path.move(to=( 0.2940,-0.4050))
241
+ path.line(to=( 0.2050,-0.2820))
242
+ path.move(to=(-0.2940,-0.4050))
243
+ path.line(to=(-0.2050,-0.2820))
244
+ path.move(to=(-0.4750, 0.1730))
245
+ path.line(to=(-0.3320, 0.1080))
246
+ with DicePath(draw) as path:
247
+ path.move(to=(0.0000,0.3490))
248
+ path.line(to=(0.3320,0.1080))
249
+ path.line(to=(0.2050,-0.2820))
250
+ path.line(to=(-0.2050,-0.2820))
251
+ path.line(to=(-0.3320,0.1080))
252
+ path.line(to=(0.0000,0.3490))
253
+
254
+
255
+ draw.number(num,yoff=.1)
256
+ return draw.image()
257
+
258
+ def draw_d20(self,num):
259
+ with DiceDraw(self._width,self._height,
260
+ fg=self._fg,bg=self._bg) as draw:
261
+ with DiceContext(draw,darker=.85):
262
+ with DicePath(draw) as path:
263
+ path.move(to=(0.0000,0.5000))
264
+ path.line(to=(0.4540,0.2620))
265
+ path.line(to=(0.4540,-0.2620))
266
+ path.line(to=(0.0000,-0.5000))
267
+ path.line(to=(-0.4540,-0.2620))
268
+ path.line(to=(-0.4540,0.2620))
269
+ path.line(to=(0.0000,0.5000))
270
+ path.line(to=(0.0000,0.2920))
271
+ with DiceContext(draw,darker=.95):
272
+ with DicePath(draw) as path:
273
+ path.move(to=(0.0000,0.2920))
274
+ path.line(to=(0.4540,0.2620))
275
+ path.line(to=(0.2530,-0.1460))
276
+ path.line(to=(0.0000,-0.5000))
277
+ path.line(to=(-0.2530,-0.1460))
278
+ path.line(to=(-0.4540,0.2620))
279
+ path.line(to=(0.0000,0.2920))
280
+ path.move(to=(0.4540,-0.2620))
281
+ path.line(to=(0.2530,-0.1460))
282
+ path.move(to=(-0.4540,-0.2620))
283
+ path.line(to=(-0.2530,-0.1460))
284
+ with DicePath(draw) as path:
285
+ path.move(to=(0.0000,0.2920))
286
+ path.line(to=(0.2530,-0.1460))
287
+ path.line(to=(-0.2530,-0.1460))
288
+ path.line(to=(0.0000,0.2920))
289
+
290
+
291
+ draw.number(num,yoff=.07,scale=.7)
292
+ return draw.image()
293
+
294
+
295
+ # --------------------------------------------------------------------
296
+ class DiceAnimator:
297
+ def __init__(self,nsides,width,height,fg='red',bg='white'):
298
+ drawer = DiceDrawer(nsides = nsides,
299
+ width = width,
300
+ height = height,
301
+ fg = fg,
302
+ bg = bg)
303
+ self._pool = [drawer.draw(v) for v in range(1,nsides+1)]
304
+
305
+ def draw(self,num,n,dt=10):
306
+
307
+ '''
308
+ delay : int
309
+ 1 / 100 of a second as base delay
310
+ '''
311
+ from random import sample
312
+ from wand.image import Image
313
+
314
+ if num > len(self._pool):
315
+ raise RuntimeError(f'Final value {num} not possible for '
316
+ f'd{len(self._pool)}')
317
+
318
+ if n >= len(self._pool) - 1:
319
+ raise RuntimeError(f'Pre-steps {n} not possible for '
320
+ f'd{len(self._pool)}')
321
+
322
+ end = self._pool[num-1]
323
+
324
+ # Round-about, but for debug
325
+ nums = list(range(1,len(self._pool)+1))
326
+ nums.remove(num)
327
+ pool = sample(nums, k=n)
328
+ other = [self._pool[i-1] for i in pool]
329
+ print(num,pool)
330
+
331
+ # More direct
332
+ # pool = self._pool[:num] + self._pool[num+1:]
333
+ # other = sample(pool, k=n)
334
+
335
+ faces = other + [end]
336
+ dang = 360 / (len(faces))
337
+ img = Image(format='gif')
338
+ ang = 360
339
+ wait = dt
340
+ for i, face in enumerate(faces):
341
+ ang -= dang
342
+ wait += dt
343
+
344
+ with face.clone() as rotated:
345
+ w, h = rotated.size
346
+ rotated.rotate(ang, reset_coords=False)
347
+ rotated.crop(0,0,w,h)
348
+ rotated.dispose = 'previous'
349
+ rotated.delay = wait # dt * (i + 1)
350
+ rotated.loop = 1
351
+ img.sequence.append(rotated)
352
+
353
+ # Make sure we dispose of the previous image altogether
354
+ #img.dispose = 'previous'
355
+ img.coalesce()
356
+ #img.deconstruct()
357
+
358
+
359
+ return img
360
+
361
+ #
362
+ # EOF
363
+ #
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env python
2
+
3
+ if __name__ == '__main__':
4
+ from sys import path
5
+
6
+ from argparse import ArgumentParser
7
+ from dicedraw import DiceDrawer
8
+
9
+ ap = ArgumentParser(description='Make a series of dice images')
10
+ ap.add_argument('-n','--sides', choices=[4,6,8,10,12,20],
11
+ default=6, type=int,
12
+ help='Number of sides')
13
+ ap.add_argument('-f','--foreground', type=str, default='white',
14
+ help='Foreground color')
15
+ ap.add_argument('-b','--background', type=str, default='#333333',
16
+ help='Background color')
17
+ ap.add_argument('-W','--width',type=int,default=75,
18
+ help='Width of images')
19
+ ap.add_argument('-H','--height',type=int,default=75,
20
+ help='Height of images')
21
+ ap.add_argument('-B','--base',type=str,default='d{sides}-{value}.png',
22
+ help='Format of file names')
23
+
24
+ args = ap.parse_args()
25
+
26
+ dd = DiceDrawer(args.sides,
27
+ args.width,
28
+ args.height,
29
+ fg = args.foreground,
30
+ bg = args.background)
31
+
32
+ base = args.base
33
+
34
+ vals = list(range(0,args.sides) if args.sides == 10 else
35
+ range(1,args.sides+1))
36
+ for val in vals:
37
+ dd.draw(val).save(filename=base.format(sides=args.sides,value=val))
38
+
39
+
40
+
@@ -0,0 +1,22 @@
1
+ # ====================================================================
2
+ class Singleton(type):
3
+ '''Meta base class for singletons'''
4
+ _instances = {}
5
+ def __call__(cls, *args, **kwargs):
6
+ '''Create the singleton object or returned existing
7
+
8
+ Parameters
9
+ ----------
10
+ args : tuple
11
+ Arguments
12
+ kwargs : dict
13
+ Keyword arguments
14
+ '''
15
+ if cls not in cls._instances:
16
+ cls._instances[cls] = \
17
+ super(Singleton, cls).__call__(*args, **kwargs)
18
+ return cls._instances[cls]
19
+
20
+ #
21
+ # EOF
22
+ #
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env python
2
+
3
+ from verboseguard import VerboseGuard
4
+ from verbose import Verbose
5
+
6
+ def inner():
7
+ with VerboseGuard('Entering inner') as g:
8
+ g('Message from inner')
9
+
10
+ def test():
11
+ with VerboseGuard('Entering test'):
12
+ inner()
13
+
14
+
15
+ if __name__ == '__main__':
16
+ from argparse import ArgumentParser
17
+
18
+ ap = ArgumentParser('test verbose')
19
+ ap.add_argument('-v','--verbose',help='Be verbose',action='store_true')
20
+
21
+ args = ap.parse_args()
22
+
23
+ Verbose().setVerbose(args.verbose)
24
+
25
+ test()
@@ -0,0 +1,59 @@
1
+ # --------------------------------------------------------------------
2
+ ## BEGIN_IMPORT
3
+ from . singleton import Singleton
4
+ ## END_IMPORT
5
+
6
+ class Verbose(metaclass=Singleton):
7
+ def __init__(self,verbose=False):
8
+ '''Singleton for writing message to screen, contigent on setting
9
+
10
+ Parameters
11
+ ----------
12
+ verbose : bool
13
+ Whether to show messages or not
14
+ '''
15
+ self._indent = ''
16
+ self._verbose = verbose
17
+
18
+ def setVerbose(self,verbose):
19
+ '''Set whether to print or not
20
+
21
+ Parameters
22
+ ----------
23
+ verbose : bool
24
+ Whether to show messages or not
25
+ '''
26
+ self._verbose = verbose
27
+
28
+ @property
29
+ def verbose(self):
30
+ '''Test if this is verbose'''
31
+ return self._verbose
32
+
33
+ def message(self,*args,**kwargs):
34
+ '''Write messsage if verbose
35
+
36
+ Parameters
37
+ ----------
38
+ args : tuple
39
+ Arguments
40
+ kwargs : dict
41
+ Keyword arguments
42
+ '''
43
+ if not self._verbose: return
44
+ if not kwargs.pop('noindent', False):
45
+ print(self._indent,end='')
46
+ print(*args,**kwargs)
47
+
48
+ def incr(self):
49
+ '''Increment indention'''
50
+ self._indent += ' '
51
+
52
+ def decr(self):
53
+ '''Decrement indention'''
54
+ if len(self._indent) > 0:
55
+ self._indent = self._indent[:-1]
56
+
57
+ #
58
+ # EOF
59
+ #
@@ -0,0 +1,53 @@
1
+ # --------------------------------------------------------------------
2
+ ## BEGIN_IMPORT
3
+ from . verbose import Verbose
4
+ ## END_IMPORT
5
+
6
+ class VerboseGuard:
7
+ def __init__(self,*args,**kwargs):
8
+ '''A guard pattern that increases verbose indention
9
+
10
+ This is a context manager. The arguments passed are used for
11
+ an initial message, before increasinig indention.
12
+
13
+ Parameters
14
+ ----------
15
+ args : tuple
16
+ Arguments
17
+ kwargs : dict
18
+ Keyword arguments
19
+ '''
20
+ Verbose().message(*args,**kwargs)
21
+
22
+ def __bool_(self):
23
+ '''Test if verbose'''
24
+ return Verbose().verbose
25
+
26
+ def __enter__(self):
27
+ '''Enter context'''
28
+ Verbose().incr()
29
+ return self
30
+
31
+ def __exit__(self,*args):
32
+ '''Exit context'''
33
+ Verbose().decr()
34
+
35
+ @property
36
+ def i(self):
37
+ return Verbose()._indent
38
+
39
+ def __call__(self,*args,**kwargs):
40
+ '''Write a message at current indention level
41
+
42
+ Parameters
43
+ ----------
44
+ args : tuple
45
+ Arguments
46
+ kwargs : dict
47
+ Keyword arguments
48
+ '''
49
+ Verbose().message(*args,**kwargs)
50
+
51
+ #
52
+ # EOF
53
+ #
@@ -0,0 +1,18 @@
1
+ from . archive import *
2
+ from . base import *
3
+ from . head import *
4
+ from . image import *
5
+ from . tile import *
6
+ from . piece import *
7
+ from . mark import *
8
+ from . draw import *
9
+ from . cell import *
10
+ from . board import *
11
+ from . gamebox import *
12
+ from . scenario import *
13
+ from . player import *
14
+ from . windows import *
15
+ from . palette import *
16
+ from . tray import *
17
+ from . extractor import *
18
+