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,147 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
#from .game import Game
|
3
|
+
#from .stone import Stone
|
4
|
+
from random import choice
|
5
|
+
#from .tile import Tile
|
6
|
+
from logging import info, warning
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
#from logger import Logger
|
11
|
+
#
|
12
|
+
from . import stone, tile ,game
|
13
|
+
|
14
|
+
class Komino(game.Game):
|
15
|
+
|
16
|
+
|
17
|
+
def __init__(self,TilesToFill,k=5):
|
18
|
+
super().__init__(TilesToFill)
|
19
|
+
self.k=k
|
20
|
+
|
21
|
+
@classmethod
|
22
|
+
def generate(cls,M,k=5,return_solution=False):
|
23
|
+
# TODO return used stones Dictionary
|
24
|
+
from . import tile # Why ???
|
25
|
+
|
26
|
+
boundary={tile.Tile((0,0))}
|
27
|
+
T=set()
|
28
|
+
solution:list[stone.Stone]=[]
|
29
|
+
while boundary and M:
|
30
|
+
c=choice(list(boundary))
|
31
|
+
boundary.remove(c)
|
32
|
+
toCheck={c}
|
33
|
+
|
34
|
+
tilesFound :set[tile.Tile]=set()
|
35
|
+
|
36
|
+
while toCheck and len(tilesFound)<k:
|
37
|
+
front : tile.Tile= toCheck.pop()
|
38
|
+
#toCheck.remove(front)
|
39
|
+
if front in T or front in tilesFound:
|
40
|
+
continue
|
41
|
+
toCheck.update(front.get_neighbores())
|
42
|
+
tilesFound.add(front)
|
43
|
+
|
44
|
+
tilesFound=stone.Stone(tilesFound)
|
45
|
+
if len(tilesFound)==k:
|
46
|
+
T|=tilesFound
|
47
|
+
solution.append(tilesFound)
|
48
|
+
|
49
|
+
neig: set[tile.Tile]=set()
|
50
|
+
for tile in tilesFound:
|
51
|
+
neig.update(tile.get_neighbores())
|
52
|
+
neig -= T
|
53
|
+
neig -= tilesFound
|
54
|
+
boundary.update(neig)
|
55
|
+
M-=1
|
56
|
+
|
57
|
+
usedStones={}
|
58
|
+
for st in solution:
|
59
|
+
norm=st.normalize()
|
60
|
+
if norm in usedStones:
|
61
|
+
usedStones[st.normalize()]+=1
|
62
|
+
else:
|
63
|
+
usedStones[norm]=1
|
64
|
+
|
65
|
+
if return_solution:
|
66
|
+
return Komino(TilesToFill=T,k=k),usedStones,solution
|
67
|
+
else:
|
68
|
+
return Komino(TilesToFill=T,k=k),usedStones
|
69
|
+
|
70
|
+
pass
|
71
|
+
|
72
|
+
|
73
|
+
def unique_stones(self,_k=None) -> set[stone.Stone]:
|
74
|
+
if _k is None:
|
75
|
+
_k=self.k
|
76
|
+
if _k==1:
|
77
|
+
return {stone.Stone(((0,0),))}
|
78
|
+
|
79
|
+
prev=self.unique_stones(_k-1)
|
80
|
+
|
81
|
+
res=set()
|
82
|
+
|
83
|
+
for s in prev:
|
84
|
+
s:stone.Stone
|
85
|
+
bound=s.outer_bound()
|
86
|
+
|
87
|
+
for t in bound:
|
88
|
+
s_new=stone.Stone(s | {t})
|
89
|
+
s_new=s_new.normalize()
|
90
|
+
res.add(s_new)
|
91
|
+
return res
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
def unique_stones_dict(self,N):
|
97
|
+
"""
|
98
|
+
dictionary that says >>all stones are N times available<<
|
99
|
+
"""
|
100
|
+
res={}
|
101
|
+
for s in self.unique_stones():
|
102
|
+
res[s]=N
|
103
|
+
return res
|
104
|
+
|
105
|
+
pass
|
106
|
+
|
107
|
+
def find_solution(self,limits:dict[stone.Stone]|int=None,display=True):
|
108
|
+
from tilingpuzzles.solvers import kominoSolver
|
109
|
+
from tilingpuzzles.visualize import visualize
|
110
|
+
|
111
|
+
if limits is None:
|
112
|
+
#TODO
|
113
|
+
assert False, "not implemented"
|
114
|
+
if isinstance(limits,int):
|
115
|
+
limits=self.unique_stones_dict(limits)
|
116
|
+
|
117
|
+
solver=kominoSolver.KominoSolverLimited(self,limits)
|
118
|
+
res=solver.solve()
|
119
|
+
|
120
|
+
if display:
|
121
|
+
vz =visualize.Visualize()
|
122
|
+
vz.add_stone(self.T)
|
123
|
+
|
124
|
+
for st in res:
|
125
|
+
vz.add_stone(st)
|
126
|
+
|
127
|
+
vz.render()
|
128
|
+
|
129
|
+
|
130
|
+
return res
|
131
|
+
|
132
|
+
pass
|
133
|
+
|
134
|
+
def count_solutions(self,limits:dict[stone.Stone]|int=None,progressLevel=1):
|
135
|
+
from tilingpuzzles.solvers import kominoSolver
|
136
|
+
|
137
|
+
if limits is None:
|
138
|
+
solver = kominoSolver.KominoSolverUnlimted(self, k=self.k)
|
139
|
+
res = solver.solve(progressLevel)
|
140
|
+
return res
|
141
|
+
|
142
|
+
else:
|
143
|
+
#TODO count limited
|
144
|
+
assert False, "counting with limitations not implemented yet"
|
145
|
+
|
146
|
+
|
147
|
+
pass
|
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
from __future__ import annotations
|
3
|
+
import logging
|
4
|
+
|
5
|
+
from . import stone
|
6
|
+
|
7
|
+
class Realisations():
|
8
|
+
"""
|
9
|
+
Used to genrate Mixed Integer Program for solving with highs Algorithm
|
10
|
+
not needed for now
|
11
|
+
"""
|
12
|
+
# FIXME outdated
|
13
|
+
|
14
|
+
def __init__(self,msk):
|
15
|
+
assert msk
|
16
|
+
self.msk=stone.Stone(msk)
|
17
|
+
self.indexToReal={}
|
18
|
+
self.stoneToReal={}
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
def add_stone(self,stone: stone.Stone):
|
23
|
+
|
24
|
+
# if realisation already exists do nothing
|
25
|
+
if stone in self.stoneToReal:
|
26
|
+
return
|
27
|
+
|
28
|
+
|
29
|
+
stone=stone.shift_positive()
|
30
|
+
logging.info(f"{self.stoneToReal = }")
|
31
|
+
self.stoneToReal[stone]=[]
|
32
|
+
logging.info(f"{ stone = }")
|
33
|
+
|
34
|
+
# FIXME use actual Symetries, stone.get_symetries
|
35
|
+
|
36
|
+
symetries=[stone]
|
37
|
+
|
38
|
+
symetries.sort()
|
39
|
+
|
40
|
+
|
41
|
+
(X_min,Y_min),(X_max,Y_max)=self.msk.bounding_Box
|
42
|
+
|
43
|
+
logging.info(f"\n mask bounding box = {self.msk.bounding_Box}")
|
44
|
+
|
45
|
+
|
46
|
+
for sym in symetries:
|
47
|
+
sym=sym.shift_positive()
|
48
|
+
(x_min,y_min),(x_max,y_max)=sym.bounding_Box
|
49
|
+
|
50
|
+
logging.info(f"\nBounding box symetrie = {sym.bounding_Box}")
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
for dx in range(X_min -x_min,X_max-x_max+1):
|
55
|
+
for dy in range(Y_min-y_min,Y_max-y_max+1):
|
56
|
+
new_stone=sym.shift(dx,dy)
|
57
|
+
if not new_stone <= self.msk:
|
58
|
+
continue
|
59
|
+
n=len(self.indexToReal)
|
60
|
+
self.indexToReal[n]=new_stone
|
61
|
+
self.stoneToReal[stone].append(new_stone)
|
62
|
+
return
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
def to_matrix(self):
|
67
|
+
pass
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|