tilingPuzzles 0.2.0__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 (40) hide show
  1. tilingpuzzles/__init__.py +6 -0
  2. tilingpuzzles/benchmark/README.md +10 -0
  3. tilingpuzzles/benchmark/__init__.py +0 -0
  4. tilingpuzzles/benchmark/data/timingResulsts.csv +193 -0
  5. tilingpuzzles/benchmark/git_state.py +22 -0
  6. tilingpuzzles/benchmark/run_benchmark.py +105 -0
  7. tilingpuzzles/examples/README.md +3 -0
  8. tilingpuzzles/examples/__init__.py +0 -0
  9. tilingpuzzles/examples/rectangularPentomino.py +40 -0
  10. tilingpuzzles/examples/scaledStones.py +163 -0
  11. tilingpuzzles/examples/tests/__init__.py +0 -0
  12. tilingpuzzles/examples/tests/test_rectangularPentomino.py +24 -0
  13. tilingpuzzles/examples/tests/test_scaledStones.py +8 -0
  14. tilingpuzzles/games/__init__.py +2 -0
  15. tilingpuzzles/games/game.py +20 -0
  16. tilingpuzzles/games/generic.py +7 -0
  17. tilingpuzzles/games/komino.py +147 -0
  18. tilingpuzzles/games/realisations.py +78 -0
  19. tilingpuzzles/games/stone.py +536 -0
  20. tilingpuzzles/games/stone_core.py +48 -0
  21. tilingpuzzles/games/tests/__init__.py +0 -0
  22. tilingpuzzles/games/tests/test_game.py +7 -0
  23. tilingpuzzles/games/tests/test_komino.py +30 -0
  24. tilingpuzzles/games/tests/test_realisations.py +28 -0
  25. tilingpuzzles/games/tests/test_stone.py +172 -0
  26. tilingpuzzles/games/tests/test_tile.py +19 -0
  27. tilingpuzzles/games/tile.py +39 -0
  28. tilingpuzzles/logUtils/__init__.py +0 -0
  29. tilingpuzzles/logUtils/callGraph.py +47 -0
  30. tilingpuzzles/logger.py +39 -0
  31. tilingpuzzles/solvers/__init__.py +0 -0
  32. tilingpuzzles/solvers/hights.py +3 -0
  33. tilingpuzzles/solvers/kominoSolver.py +191 -0
  34. tilingpuzzles/solvers/tests/test_komino_solver.py +30 -0
  35. tilingpuzzles/visualize/__init__.py +0 -0
  36. tilingpuzzles/visualize/visualize.py +61 -0
  37. tilingpuzzles-0.2.0.dist-info/METADATA +44 -0
  38. tilingpuzzles-0.2.0.dist-info/RECORD +40 -0
  39. tilingpuzzles-0.2.0.dist-info/WHEEL +4 -0
  40. tilingpuzzles-0.2.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,172 @@
1
+
2
+ import sys
3
+ sys.path.insert(0, '.')
4
+ from tqdm import tqdm
5
+
6
+ from tilingpuzzles.games.stone import Stone, Stone_Symetries
7
+ from tilingpuzzles.games.tile import Tile
8
+ from tilingpuzzles.logUtils.callGraph import GTracker
9
+ from tilingpuzzles.games.komino import Komino
10
+ from tilingpuzzles.visualize.visualize import Visualize
11
+
12
+ from logging import info
13
+ from time import sleep
14
+
15
+
16
+ def test_stone():
17
+ n=10
18
+ A=set([(i//2,i) for i in range(10)])
19
+ s=Stone(A)
20
+ msk=s.to_mask(n,n)
21
+
22
+ assert msk.any()
23
+ assert s==A
24
+
25
+ s=Stone(((1,1),))
26
+ s2=s.shift(4,4)
27
+
28
+ assert s.bounding_Box == ((1,1),(1,1))
29
+ assert s.bounding_Box == ((1,1),(1,1))
30
+
31
+
32
+
33
+ def test_boundary():
34
+
35
+ s=Stone(((0,0),))
36
+
37
+ assert s.outer_bound() == frozenset(((1,0),(-1,0),(0,1),(0,-1)))
38
+ assert s.inner_bound()==s
39
+
40
+
41
+
42
+ def test_substones():
43
+ seed=Tile((0,0))
44
+ s=Stone((seed,))
45
+
46
+ for i in range(2):
47
+ s=Stone(s | s.outer_bound())
48
+ n=len(s)
49
+ subs=s.get_k_stone_on_tile(seed,n)
50
+ assert s == list(subs)[0]
51
+
52
+
53
+ def test_normalize():
54
+ st=Stone(
55
+ [
56
+ (2,3),
57
+ (4,5),
58
+ (3,4),
59
+ (22,5)
60
+ ]
61
+ )
62
+
63
+ norm=st.normalize()
64
+ assert len(st) == len(norm)
65
+
66
+ for i in range(1,10):
67
+ norm2=st.shift(i,i).normalize()
68
+ assert norm==norm2
69
+
70
+
71
+ def test_symetries():
72
+ st=Stone([(0,0)])
73
+ get_symetries=Stone_Symetries()
74
+
75
+ sym=get_symetries(st)
76
+
77
+ assert len(sym)==1
78
+
79
+ st=Stone([(0,1),(0,0)])
80
+ sym=get_symetries(st)
81
+ assert len(sym)==2
82
+
83
+ st=Stone([(2,1),(1,1),(0,1),(0,0)])
84
+ sym=get_symetries(st)
85
+ assert len(sym)==8
86
+
87
+
88
+
89
+ def test_clip_to_Bounds():
90
+ N=10
91
+ s=Stone(((i,i) for i in range(N)))
92
+
93
+ s2=s.clip_to_bounds(0,N,0,N)
94
+ assert s == s2
95
+
96
+ for i in range(N):
97
+
98
+ s3=s.clip_to_bounds(0,i-1,0,i-1)
99
+ assert len(s3)==i
100
+
101
+
102
+ def test_split_point():
103
+ # test if method finds the split point
104
+ sstring="""
105
+ #########################
106
+ ######splitpoint#########
107
+ v
108
+ #########################
109
+ #########################
110
+ """
111
+ s:Stone=Stone.from_string(sstring)
112
+ #s.display()
113
+ split_tile= s.good_cut_point()
114
+ new_stone=Stone(s-{split_tile})
115
+ componets=new_stone.ConectedComponents()
116
+ assert len(componets)==2
117
+
118
+
119
+ def test_split_point_random():
120
+ for i in tqdm(range(100)):
121
+
122
+ k,_=Komino.generate(30,5)
123
+ s=k.T
124
+ assert s
125
+ assert s.isConected()
126
+ loop_runs=False
127
+ while(s and s.isConected()):
128
+ last_size=len(s)
129
+ p=s.good_cut_point(perc=0.5,offset=3)
130
+
131
+ match p:
132
+ case (int(), int()):
133
+ pass
134
+ case _:
135
+ assert False
136
+
137
+ s=Stone(s-{p})
138
+ assert len(s)==last_size -1
139
+ loop_runs=True
140
+ assert loop_runs
141
+ #return
142
+ #visual
143
+ # test spliting of random generated stones
144
+ for i in tqdm(range(10)):
145
+
146
+ k,_=Komino.generate(100,5)
147
+ s=k.T
148
+ assert s
149
+ assert s.isConected()
150
+ loop_runs=False
151
+ vz=Visualize()
152
+ vz.add_stone(s)
153
+ while(s and s.isConected()):
154
+ last_size=len(s)
155
+ p=s.good_cut_point(perc=0.5,offset=3)
156
+ vz.add_stone(Stone({p}))
157
+
158
+
159
+ match p:
160
+ case (int(), int()):
161
+ pass
162
+ case _:
163
+ assert False
164
+
165
+ s=Stone(s-{p})
166
+ assert len(s)==last_size -1
167
+ loop_runs=True
168
+ vz.render()
169
+ assert loop_runs
170
+
171
+
172
+
@@ -0,0 +1,19 @@
1
+
2
+ from tilingpuzzles.games.tile import Tile
3
+ import pytest
4
+
5
+ def test_tile():
6
+
7
+ t=Tile((0,1))
8
+ n=t.get_neighbores()
9
+ assert len(n)==4, "this tile should have 4 neighbores"
10
+
11
+ # 3D
12
+ t=Tile((1,2,3))
13
+ n=t.get_neighbores()
14
+ assert len(n)==6, "3D => 6 faces"
15
+
16
+ # trap
17
+ t=Tile((0,0))
18
+ n=t.get_neighbores(lowerB=(0,0),upperB=(1,1))
19
+ assert len(n)==0, f"All blocked\n {n =}"
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+ import logging
3
+
4
+ #from .stone import Stone as Stone
5
+
6
+ from . import stone
7
+
8
+
9
+
10
+ class Tile(tuple):
11
+ #WARNING performance, remove this use plain tuples
12
+
13
+ def __new__(cls,*cords):
14
+ cords=tuple(map(int,*cords))
15
+
16
+ return super(Tile,cls).__new__(cls,cords)
17
+
18
+
19
+ def __init__(self,*cords):
20
+ pass
21
+
22
+ #TODO change to `stone.Stone.get_neigbores(tuple)-> Stone`
23
+ def get_neighbores(self,lowerB=None,upperB=None) -> stone.Stone:
24
+ res=[]
25
+ n=len(self)
26
+
27
+ for i in range(n):
28
+ new_cords=[ self[j]+ (i==j) for j in range(n)]
29
+ if upperB is None or new_cords[i]<upperB[i]:
30
+ res.append(Tile(new_cords))
31
+ new_cords=[ self[j]- (i==j) for j in range(n)]
32
+ if lowerB is None or new_cords[i]>=lowerB[i]:
33
+ res.append(Tile(new_cords))
34
+
35
+ return stone.Stone(res)
36
+
37
+
38
+
39
+
File without changes
@@ -0,0 +1,47 @@
1
+
2
+ from graphviz import Digraph
3
+ from functools import wraps
4
+
5
+
6
+
7
+ class GTracker():
8
+
9
+ call_graph = Digraph(comment='Function Call Graph')
10
+
11
+ stk=["__main__"]
12
+
13
+ MAX_CNT=200
14
+ cnt=0
15
+
16
+
17
+ #@classmethod
18
+ def track_calls(func):
19
+ def wrapper(*args, **kwargs):
20
+ caller = GTracker.stk[-1]
21
+ callee = func.__name__ +f"\n{args =}\n {kwargs =}"
22
+ GTracker.stk.append(callee)
23
+
24
+ # Add nodes and edges to graph
25
+ GTracker.call_graph.node(caller)
26
+ GTracker.call_graph.node(callee)
27
+ GTracker.call_graph.edge(caller, callee)
28
+ #break after a certain number of iterations
29
+
30
+ GTracker.cnt+=1
31
+ if GTracker.cnt>=GTracker.MAX_CNT:
32
+ GTracker.render()
33
+ assert False, 'Maximum number of tracked calls reached'
34
+ res= func(*args,**kwargs)
35
+ GTracker.stk.pop()
36
+ return res
37
+ return wrapper
38
+
39
+ #@classmethod
40
+ def render():
41
+ GTracker.call_graph.render(view=True,engine='dot')
42
+
43
+ def clear():
44
+ GTracker.call_graph = Digraph(comment='Function Call Graph')
45
+
46
+ GTracker.stk=["__main__"]
47
+
@@ -0,0 +1,39 @@
1
+
2
+ import logging
3
+
4
+ class Logger():
5
+
6
+
7
+
8
+ def __init__(self,other):
9
+ self.LogOff=True
10
+ self.other=other
11
+ pass
12
+
13
+ def WARN(self,*args,**kwargs):
14
+ if self.LogOff:
15
+ return
16
+ else:
17
+ msg,*args=args
18
+ logging.warning(f"obj {self.other} says:\n\t{msg}")
19
+
20
+ def INFO(self,*args,**kwargs):
21
+ if self.LogOff:
22
+ return
23
+ else:
24
+ msg,*args=args
25
+ logging.INFO(f"obj {self.other} says:\n\t{msg}")
26
+
27
+ def with_loging(f):
28
+
29
+ def new_f(other,*args,**kwarsg):
30
+ other.LOG.OFF=False
31
+ res=f(other,*args,**kwarsg)
32
+ other.LOG.OFF=True
33
+ return res
34
+ return new_f
35
+
36
+
37
+
38
+
39
+
File without changes
@@ -0,0 +1,3 @@
1
+
2
+ # CPP backend on ints own should make this faster
3
+
@@ -0,0 +1,191 @@
1
+
2
+
3
+ from ..games import komino
4
+ from ..games import stone
5
+ from logging import info
6
+ from ..visualize import visualize
7
+ from tqdm.notebook import tqdm
8
+
9
+
10
+ class KominoSolverLimited():
11
+
12
+ def __init__(self,komino:komino.Komino,stoneDict:dict[stone.Stone,int]):
13
+ r"""
14
+ sets up a Solver
15
+ - komino
16
+ komino game
17
+ - stoneDict
18
+ dictionary of `stones` to `int`
19
+ how often a stone of a certain kind can be placed.
20
+ """
21
+
22
+ self.T = komino.T
23
+ self.stoneDict= stoneDict
24
+ k=None
25
+ for st in stoneDict:
26
+ k=len(st)
27
+ break
28
+ assert k
29
+ self.k=k
30
+ self.solved = False
31
+ self.solution: list[stone.Stone]=[ ]
32
+
33
+
34
+ def solve(self):
35
+ """
36
+ finds a solution fot the problem if one exists
37
+ """
38
+ stone.Stone_config.MaxCacheStoneSize=self.k
39
+ self.solved=self._get_solution(self.T,self.stoneDict)
40
+ return self.solution.copy()
41
+ pass
42
+
43
+ def _get_solution(self,T:stone.Stone,availableStones:dict[stone.Stone,int]) -> bool:
44
+ #info(f"{self.solution = }")
45
+ components=list(T.ConectedComponents())
46
+ components.sort(key=len)
47
+ expPoint=components[0].good_cut_point()
48
+ candidates =T.get_k_stone_on_tile(expPoint,self.k)
49
+
50
+ for candidate in candidates:
51
+ norm=candidate.normalize()
52
+ if norm not in availableStones:
53
+ continue
54
+ next_T=stone.Stone(T-candidate)
55
+ self.solution.append(candidate)
56
+ if not next_T:
57
+ return True
58
+
59
+ comp=next_T.ConectedComponents()
60
+ compSizesModK=map(lambda x:len(x) % self.k,comp)
61
+ allZero=all(size==0 for size in compSizesModK)
62
+ if not allZero:
63
+ self.solution.pop()
64
+ continue
65
+
66
+ next_dict=availableStones.copy()
67
+ next_dict[norm]-=1
68
+ if not next_dict[norm]:
69
+ del(next_dict[norm])
70
+ res=self._get_solution(next_T,next_dict)
71
+ if res:
72
+ return True
73
+ self.solution.pop()
74
+
75
+ return False
76
+
77
+
78
+ def get_solution_viz(self):
79
+ """
80
+ visualization of the solution process
81
+ """
82
+ #WARNING outdatet
83
+ self.solved=self._get_solution_viz(self.T,self.stoneDict)
84
+ return self.solution.copy()
85
+ pass
86
+
87
+ def _get_solution_viz(self,T:stone.Stone,availableStones:dict[stone.Stone,int]) -> bool:
88
+ #info(f"{self.solution = }")
89
+
90
+ expPoint=T.getMinTile()
91
+ assert expPoint in T,f" {expPoint = } should be in T"
92
+ print(f"{expPoint = }")
93
+ print(f"{ availableStones = }")
94
+ candidates =T.get_k_stone_on_tile(expPoint,self.k)
95
+
96
+ print(f"{candidates = }")
97
+
98
+ vz = visualize.Visualize()
99
+
100
+ vz.add_stone(self.T)
101
+
102
+ for st in self.solution:
103
+ vz.add_stone(st)
104
+ vz.render()
105
+
106
+ for candidate in candidates:
107
+ norm=candidate.normalize()
108
+ if norm not in availableStones:
109
+ continue
110
+ next_T=stone.Stone(T-candidate)
111
+ self.solution.append(candidate)
112
+ if not next_T:
113
+ return True
114
+
115
+ comp=next_T.ConectedComponents()
116
+ compSizesModK=map(lambda x:len(x) % self.k,comp)
117
+ allZero=all(size==0 for size in compSizesModK)
118
+ if not allZero:
119
+ self.solution.pop()
120
+ continue
121
+
122
+ next_dict=availableStones.copy()
123
+ next_dict[norm]-=1
124
+ if not next_dict[norm]:
125
+ del(next_dict[norm])
126
+ res=self._get_solution_viz(next_T,next_dict)
127
+ if res:
128
+ return True
129
+ self.solution.pop()
130
+
131
+ return False
132
+
133
+
134
+ def count_solutions(self):
135
+ pass
136
+
137
+ class KominoSolverUnlimted():
138
+
139
+ def __init__(self,kom:komino.Komino,k=5):
140
+ """
141
+ counts number of solutions if unlimited numbers of stones are given
142
+
143
+ uses Dynammic Programming
144
+ """
145
+ #TODO
146
+ self.T = kom.T
147
+ self.k=kom.k
148
+ stone.Stone_config.MaxCacheStoneSize=self.k
149
+ self.DP={}
150
+
151
+ def solve(self,ProgressLevel=1,displayBelow=0) -> int:
152
+ #TODO
153
+ self.ProgressLevel=ProgressLevel
154
+ self.displayBelow=displayBelow
155
+ return self._solve(self.T)
156
+
157
+ def _solve(self,st:stone.Stone,curLevel=0):
158
+ #TODO
159
+ """
160
+ responsible for normalization
161
+ """
162
+ # Base Case
163
+ if not st:
164
+ return 1
165
+
166
+ st=st.normalize()
167
+ if st in self.DP:
168
+ return self.DP[st]
169
+ expPoint=st.good_cut_point()
170
+ candidates=st.get_k_stone_on_tile(expPoint,self.k)
171
+
172
+ if curLevel<self.ProgressLevel:
173
+ candidates=tqdm(candidates,desc=f"Level {curLevel}",position=curLevel,leave=(curLevel==0))
174
+ if curLevel<self.displayBelow:
175
+ st.display()
176
+ res=0
177
+ for candidate in candidates:
178
+ remainder=stone.Stone(st-candidate)
179
+ components=remainder.ConectedComponents()
180
+ compSizesModK=map(lambda x:len(x) % self.k,components)
181
+ allZero=all(mod==0 for mod in compSizesModK)
182
+ if not allZero:
183
+ continue
184
+ base=1
185
+ for component in components:
186
+ base*=self._solve(component,curLevel=curLevel+1)
187
+ res+=base
188
+ self.DP[st]=res
189
+ return res
190
+ pass
191
+
@@ -0,0 +1,30 @@
1
+
2
+ from tilingpuzzles.games import komino as _komio
3
+ from src.tilingpuzzles.solvers.kominoSolver import KominoSolverLimited
4
+ from tilingpuzzles.games.stone import Stone
5
+
6
+ from logging import info
7
+
8
+
9
+
10
+ def test_KominoSolverLimited():
11
+ Komino=_komio.Komino
12
+
13
+ N=15
14
+
15
+ for k in range(2,6):
16
+ komino,stonesAllowed=Komino.generate(N,k)
17
+
18
+ solver=KominoSolverLimited(komino,stonesAllowed)
19
+ solution = solver.solve()
20
+ info(f"{solution = }")
21
+ assert solution
22
+
23
+ res=set()
24
+ for st in solver.solution:
25
+ res |= st
26
+ res = Stone(res)
27
+ assert res == komino.T
28
+
29
+ def test_KominSolverUnlimited():
30
+ assert False, "not implementet test !"
File without changes
@@ -0,0 +1,61 @@
1
+
2
+ import matplotlib.pyplot as plt
3
+ from matplotlib.axes import Axes
4
+ from matplotlib.patches import Rectangle
5
+ #from tilingPuzzles.games.stone import Stone
6
+ import numpy as np
7
+
8
+ class Visualize():
9
+
10
+ def __init__(self,figure=None,ax=None):
11
+ self.n =0
12
+ if ax is None:
13
+ ax=plt.subplot()
14
+ ax.set_aspect("equal")
15
+
16
+ self.figure=figure
17
+ self.ax: Axes=ax
18
+
19
+
20
+ def add_stone(self,stone,fill=None):
21
+ if fill is None:
22
+ fill=self.get_nth_color()
23
+
24
+ for tile in stone:
25
+
26
+ rc=Rectangle(tile,1,1,edgecolor="black",facecolor=fill,lw=1.5)
27
+ self.ax.add_patch(rc)
28
+ self.ax.autoscale_view()
29
+
30
+ def update_stones(self,stones):
31
+ for stone in stones:
32
+ self.add_stone(stone=stone)
33
+
34
+ def get_nth_color(self,n=None):
35
+ if n is None:
36
+ n=self.n
37
+ self.n+=1
38
+
39
+ pi=np.pi
40
+ sin=np.sin
41
+ shift=2
42
+ color=[ sin(shift*n+pi/4*i)**2 for i in range(3) ]
43
+ s=sum(color)
44
+ color = [c/s for c in color]
45
+ color= "#"+"".join(f"{int(c*16**2):02x}" for c in color)
46
+ return color
47
+
48
+
49
+
50
+ def draw_stone(st):
51
+ st=st.shift_positive()
52
+ msk=st.to_mask()
53
+
54
+ plt.imshow(msk)
55
+ plt.show()
56
+
57
+ pass
58
+
59
+ def render(self):
60
+ plt.show()
61
+
@@ -0,0 +1,44 @@
1
+ Metadata-Version: 2.4
2
+ Name: tilingPuzzles
3
+ Version: 0.2.0
4
+ Summary: Add your description here
5
+ Author-email: jonathan graf <jonathangraf@outlook.de>
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: billiard>=4.2.1
8
+ Requires-Dist: graphviz>=0.20.3
9
+ Requires-Dist: jupyter>=1.1.1
10
+ Requires-Dist: matplotlib>=3.10.3
11
+ Requires-Dist: numpy>=2.2.6
12
+ Requires-Dist: pandas>=2.2.3
13
+ Requires-Dist: py-cpuinfo>=9.0.0
14
+ Requires-Dist: pytest>=8.3.5
15
+ Requires-Dist: python-cpuid>=0.1.1
16
+ Requires-Dist: scipy>=1.15.3
17
+ Requires-Dist: timeout-decorator>=0.5.0
18
+ Requires-Dist: tqdm>=4.67.1
19
+ Description-Content-Type: text/markdown
20
+
21
+ # TilingPuzzles
22
+
23
+ ## Requirements
24
+
25
+ ```bash
26
+ pip install uv
27
+ uv sync
28
+ ```
29
+
30
+ ## Test
31
+
32
+ ```bash
33
+ pytest
34
+ ```
35
+
36
+ ## Build
37
+
38
+ ```bash
39
+ uv build
40
+ ```
41
+
42
+ # Demo
43
+
44
+ [notebook](Demo.ipynb)
@@ -0,0 +1,40 @@
1
+ tilingpuzzles/__init__.py,sha256=U5flfDCld7tYtZ9374eCWQ65UGLMndptYQkqw8xOuwM,56
2
+ tilingpuzzles/logger.py,sha256=66fX7Tb8WkbOP-FqImiT2UXGGWyXnIw2gJT5IkGsWVA,771
3
+ tilingpuzzles/benchmark/README.md,sha256=icAWDmRCJmdfuX4ps7HtHjR1QqvhYSeX9JQ4M2UwMB4,200
4
+ tilingpuzzles/benchmark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ tilingpuzzles/benchmark/git_state.py,sha256=IUp0IWOzlC_84JFrxlvl0DtP0l-U0QRzHCFrhLG1yMc,584
6
+ tilingpuzzles/benchmark/run_benchmark.py,sha256=8PMpi0JOe1Vm9JYdByUBlaCbOagS3krmO4AebgfHX8E,2977
7
+ tilingpuzzles/benchmark/data/timingResulsts.csv,sha256=A3cXQuqonxhDuZzJN1uSyJuIXJOrrLfOgTO-R8djBi0,42278
8
+ tilingpuzzles/examples/README.md,sha256=vq6Qjppx96HhAnCpDm2U0298Wy7t-KQf6H1Or2sPmeQ,52
9
+ tilingpuzzles/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ tilingpuzzles/examples/rectangularPentomino.py,sha256=xpYQtmjMpF6XFL-oPYWVQRcOZqgWAR5xqQ6M9ajik_g,591
11
+ tilingpuzzles/examples/scaledStones.py,sha256=qep-waU6_9TrA1zBjfRR9-Xsj9VcehScvEzhCqGFAC8,1876
12
+ tilingpuzzles/examples/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ tilingpuzzles/examples/tests/test_rectangularPentomino.py,sha256=gjYluiqrRGDMIVYY3OX8cQ5zvRpOJLGzgqjxX1ROBMk,406
14
+ tilingpuzzles/examples/tests/test_scaledStones.py,sha256=vS1p6ErY-_SqjmNnU2uOsDP1FBA1Ki6bFM4894XbdR4,160
15
+ tilingpuzzles/games/__init__.py,sha256=oH3fystDA1fw_Fz7grKfAqrZFPoz68aoRyewopgqSHM,32
16
+ tilingpuzzles/games/game.py,sha256=C7KBYbJ41M0yCCaLfldlRoip7Us5dXeHTQPqc6AULlc,261
17
+ tilingpuzzles/games/generic.py,sha256=fEDg3lTLXnQUbW0arxOyOz8TEneLaq9blN8VuKxER4s,57
18
+ tilingpuzzles/games/komino.py,sha256=Cb-wk0u3pVe1gVq5eL8e2HPwjvt0PXFg4M9A2ERRghs,3771
19
+ tilingpuzzles/games/realisations.py,sha256=C3ztLBuj23Ei0_isqteStIdSCS6e0kc2IMjYTqXU_iE,1714
20
+ tilingpuzzles/games/stone.py,sha256=HQSGl5sO-AVuithElP0110q-sS-qq8VhnANOjCSi0Vs,14295
21
+ tilingpuzzles/games/stone_core.py,sha256=naKrJERMthm21N6Yu_q65gBDFcisO_oEyU0ZCkmE664,642
22
+ tilingpuzzles/games/tile.py,sha256=8-JqOOYybqR8SBDkT4SYPLYLYPf7j14KrqqeOJPxcEI,930
23
+ tilingpuzzles/games/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ tilingpuzzles/games/tests/test_game.py,sha256=6ngLGqjqdGs0KEgMX5gLfNOI3ll69Ax3EBmr1kkVoGg,95
25
+ tilingpuzzles/games/tests/test_komino.py,sha256=E-K_gKANkbY-5u3PWbJe7NuR2_R6jpvTom5qsrMoavw,540
26
+ tilingpuzzles/games/tests/test_realisations.py,sha256=mFbsUBhAetyjCuS5qAW3sO3iWkRiwPpMKjXCaX_TrBw,578
27
+ tilingpuzzles/games/tests/test_stone.py,sha256=5p2KyWH-s_DprCmdBJSx9L6t2OLcRkf4NvGkS1u40a8,3600
28
+ tilingpuzzles/games/tests/test_tile.py,sha256=9JgSLKbevFORNbPuR2tPko97rYy1PgVdbT3qhGUZNg8,395
29
+ tilingpuzzles/logUtils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ tilingpuzzles/logUtils/callGraph.py,sha256=g8tsEGL3MH4Alv1f1EUuv3jhPUj6KJ2eowoJEFbdC9M,1175
31
+ tilingpuzzles/solvers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ tilingpuzzles/solvers/hights.py,sha256=bCrBCvM_jwUmh7G5KjgIzzCy3Zjo2KRflW5fLDW_N40,52
33
+ tilingpuzzles/solvers/kominoSolver.py,sha256=xfXAGGTsZ0K5TesDADmHuA4a8RyL-j_bcSQumBHyn6Y,5485
34
+ tilingpuzzles/solvers/tests/test_komino_solver.py,sha256=IVDGm1lVmKwafNpSYQ6ja9s_K48MWdGbsGoOXumrlUQ,692
35
+ tilingpuzzles/visualize/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
+ tilingpuzzles/visualize/visualize.py,sha256=uuyi2FRvqujrphwfx0uz1UOQzpXgGCGyJSv_RQvQulI,1349
37
+ tilingpuzzles-0.2.0.dist-info/METADATA,sha256=li40u-BrCmwSTwTwqcwlRQjzYRKoB8K2ZQl_5vAFeS8,750
38
+ tilingpuzzles-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
39
+ tilingpuzzles-0.2.0.dist-info/entry_points.txt,sha256=76zo3sy1IWctxF6HC3cMy1ANDDzewI-6eYeO1CTnyHU,53
40
+ tilingpuzzles-0.2.0.dist-info/RECORD,,