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.
- tilingpuzzles/__init__.py +6 -0
- tilingpuzzles/benchmark/README.md +10 -0
- tilingpuzzles/benchmark/__init__.py +0 -0
- tilingpuzzles/benchmark/data/timingResulsts.csv +193 -0
- tilingpuzzles/benchmark/git_state.py +22 -0
- tilingpuzzles/benchmark/run_benchmark.py +105 -0
- tilingpuzzles/examples/README.md +3 -0
- tilingpuzzles/examples/__init__.py +0 -0
- tilingpuzzles/examples/rectangularPentomino.py +40 -0
- tilingpuzzles/examples/scaledStones.py +163 -0
- tilingpuzzles/examples/tests/__init__.py +0 -0
- tilingpuzzles/examples/tests/test_rectangularPentomino.py +24 -0
- tilingpuzzles/examples/tests/test_scaledStones.py +8 -0
- tilingpuzzles/games/__init__.py +2 -0
- tilingpuzzles/games/game.py +20 -0
- tilingpuzzles/games/generic.py +7 -0
- tilingpuzzles/games/komino.py +147 -0
- tilingpuzzles/games/realisations.py +78 -0
- tilingpuzzles/games/stone.py +536 -0
- tilingpuzzles/games/stone_core.py +48 -0
- tilingpuzzles/games/tests/__init__.py +0 -0
- tilingpuzzles/games/tests/test_game.py +7 -0
- tilingpuzzles/games/tests/test_komino.py +30 -0
- tilingpuzzles/games/tests/test_realisations.py +28 -0
- tilingpuzzles/games/tests/test_stone.py +172 -0
- tilingpuzzles/games/tests/test_tile.py +19 -0
- tilingpuzzles/games/tile.py +39 -0
- tilingpuzzles/logUtils/__init__.py +0 -0
- tilingpuzzles/logUtils/callGraph.py +47 -0
- tilingpuzzles/logger.py +39 -0
- tilingpuzzles/solvers/__init__.py +0 -0
- tilingpuzzles/solvers/hights.py +3 -0
- tilingpuzzles/solvers/kominoSolver.py +191 -0
- tilingpuzzles/solvers/tests/test_komino_solver.py +30 -0
- tilingpuzzles/visualize/__init__.py +0 -0
- tilingpuzzles/visualize/visualize.py +61 -0
- tilingpuzzles-0.2.0.dist-info/METADATA +44 -0
- tilingpuzzles-0.2.0.dist-info/RECORD +40 -0
- tilingpuzzles-0.2.0.dist-info/WHEEL +4 -0
- 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
|
+
|
tilingpuzzles/logger.py
ADDED
@@ -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,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,,
|