| 1 | # coding=utf-8 |
| 2 | r""" |
| 3 | Family Games America's Quantumino solver |
| 4 | |
| 5 | This module allows to solve the Quantumino puzzle made by Family Games |
| 6 | America (see [1] and [2]). This puzzle was left at the dinner room of the |
| 7 | Laboratoire de Combinatoire Informatique Mathématique in Montréal by Franco |
| 8 | Saliola during winter 2011. |
| 9 | |
| 10 | The solution uses the dancing links code which is in Sage. Dancing links |
| 11 | were originally introduced by Donald Knuth in 2000 [3]. In particular, |
| 12 | Knuth used dancing links to solve tilings of a region by 2D pentaminos. |
| 13 | Here we extend the method for 3D pentaminos. |
| 14 | |
| 15 | AUTHOR: |
| 16 | |
| 17 | - Sébastien Labbé, April 28th, 2011 |
| 18 | |
| 19 | DESCRIPTION (from [1]): |
| 20 | |
| 21 | " |
| 22 | Pentamino games have been taken to a whole different level; a 3-D |
| 23 | level, with this colorful creation! Using the original pentamino |
| 24 | arrangements of 5 connected squares which date from 1907, players are |
| 25 | encouraged to «think inside the box» as they try to fit 16 of the 17 |
| 26 | 3-D pentamino pieces inside the playing perimeters. Remove a different |
| 27 | piece each time you play for an entirely new challenge! Thousands of |
| 28 | solutions to be found! |
| 29 | Quantumino™ hands-on educational tool where players learn how shapes |
| 30 | can be transformed or arranged into predefined shapes and spaces. |
| 31 | Includes: |
| 32 | 1 wooden frame, 17 wooden blocks, instruction booklet. |
| 33 | Age: 8+ |
| 34 | " |
| 35 | |
| 36 | EXAMPLES: |
| 37 | |
| 38 | Here are the 17 wooden blocks of the Quantumino puzzle numbered from 0 to |
| 39 | 16 in the following 3d picture:: |
| 40 | |
| 41 | sage: from sage.games.quantumino import quantumino_solver |
| 42 | sage: quantumino_solver.show_pentaminos() |
| 43 | |
| 44 | To solve the puzzle without using the block numbered 12:: |
| 45 | |
| 46 | sage: s = quantumino_solver.get_solution(12) |
| 47 | sage: s |
| 48 | Quantumino solution without the following pentamino : |
| 49 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (2, 1, 1)], Color: blue |
| 50 | sage: s.show3d() # long time (<1s) |
| 51 | |
| 52 | To solve the puzzle without using the block numbered 7:: |
| 53 | |
| 54 | sage: s = quantumino_solver.get_solution(7) |
| 55 | sage: s |
| 56 | Quantumino solution without the following pentamino : |
| 57 | 3D Polyomino: [(0, 0, 0), (0, 1, 0), (0, 2, 0), (0, 2, 1), (1, 0, 0)], Color: orange |
| 58 | sage: s.show3d() # long time (<1s) |
| 59 | |
| 60 | To get all the solutions, use the iterator. Note that finding the first |
| 61 | solution is the most time consuming because it needs to creates the |
| 62 | complete data to describe the problem:: |
| 63 | |
| 64 | sage: it = quantumino_solver.solutions_iterator(7) |
| 65 | sage: it.next() # (1s) |
| 66 | Quantumino solution without the following pentamino : |
| 67 | 3D Polyomino: [(0, 0, 0), (0, 1, 0), (0, 2, 0), (0, 2, 1), (1, 0, 0)], Color: orange |
| 68 | sage: it.next() # (0.001s) |
| 69 | Quantumino solution without the following pentamino : |
| 70 | 3D Polyomino: [(0, 0, 0), (0, 1, 0), (0, 2, 0), (0, 2, 1), (1, 0, 0)], Color: orange |
| 71 | sage: it.next() # (0.001s) |
| 72 | Quantumino solution without the following pentamino : |
| 73 | 3D Polyomino: [(0, 0, 0), (0, 1, 0), (0, 2, 0), (0, 2, 1), (1, 0, 0)], Color: orange |
| 74 | |
| 75 | To get the solution inside other boxes:: |
| 76 | |
| 77 | sage: s = quantumino_solver.get_solution(7, box=(4,4,5)) # long time (2s) |
| 78 | sage: s.show3d() # long time (<1s) |
| 79 | |
| 80 | :: |
| 81 | |
| 82 | sage: s = quantumino_solver.get_solution(7, box=(2,2,20)) # long time (1s) |
| 83 | sage: s.show3d() # long time (<1s) |
| 84 | |
| 85 | EXAMPLES: |
| 86 | |
| 87 | One can also solve similar problems by defining 3D polyominos directly. The |
| 88 | following is a 2d puzzle owned by Florent Hivert:: |
| 89 | |
| 90 | sage: from sage.games.quantumino import Polyomino3d, solve_3d_puzzle |
| 91 | sage: L = [] |
| 92 | sage: L.append(Polyomino3d([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0),(1,2,0),(1,3,0)])) |
| 93 | sage: L.append(Polyomino3d([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0),(1,2,0)])) |
| 94 | sage: L.append(Polyomino3d([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0),(1,3,0)])) |
| 95 | sage: L.append(Polyomino3d([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,3,0)])) |
| 96 | sage: L.append(Polyomino3d([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0)])) |
| 97 | sage: L.append(Polyomino3d([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,1,0),(1,2,0)])) |
| 98 | sage: L.append(Polyomino3d([(0,0,0),(0,1,0),(0,2,0),(0,3,0),(1,1,0),(1,3,0)])) |
| 99 | sage: L.append(Polyomino3d([(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0),(1,3,0)])) |
| 100 | sage: L.append(Polyomino3d([(0,1,0),(0,2,0),(0,3,0),(1,0,0),(1,1,0),(1,2,0)])) |
| 101 | sage: L.append(Polyomino3d([(0,0,0),(0,1,0),(0,2,0),(1,0,0),(1,1,0),(1,2,0)])) |
| 102 | |
| 103 | Solve the puzzle and show the solution:: |
| 104 | |
| 105 | sage: it = solve_3d_puzzle(L, (8,8,1)) |
| 106 | sage: solution = it.next() |
| 107 | sage: G = sum([piece.show3d() for piece in solution], Graphics()) |
| 108 | sage: G.show(aspect_ratio=1, viewer='tachyon') |
| 109 | |
| 110 | REFERENCES: |
| 111 | |
| 112 | - [1] http://familygamesamerica.com/mainsite/consumers/productview.php?pro_id=274&search=quantumino |
| 113 | - [2] Quantumino - How to Play, http://www.youtube.com/watch?v=jX_VKzakZi8 |
| 114 | - [3] Knuth, Donald (2000). "Dancing links". arXiv:cs/0011047. |
| 115 | |
| 116 | """ |
| 117 | #***************************************************************************** |
| 118 | # Copyright (C) 2011 Sebastien Labbe <slabqc@gmail.com> |
| 119 | # |
| 120 | # Distributed under the terms of the GNU General Public License (GPL) |
| 121 | # as published by the Free Software Foundation; either version 2 of |
| 122 | # the License, or (at your option) any later version. |
| 123 | # http://www.gnu.org/licenses/ |
| 124 | #***************************************************************************** |
| 125 | import itertools |
| 126 | from sage.combinat.all import WeylGroup |
| 127 | from sage.plot.plot import Graphics |
| 128 | from sage.modules.free_module_element import vector |
| 129 | from sage.plot.plot3d.platonic import cube |
| 130 | from sage.plot.plot3d.shapes2 import text3d |
| 131 | from sage.structure.sage_object import SageObject |
| 132 | |
| 133 | ############################## |
| 134 | # Orthogonal Rotation matrices |
| 135 | ############################## |
| 136 | rotation_matrices = [w.matrix() for w in WeylGroup(['B',3]) if w.matrix().det() == 1] |
| 137 | |
| 138 | ############################## |
| 139 | # Class Polyomino3d |
| 140 | ############################## |
| 141 | class Polyomino3d(SageObject): |
| 142 | r""" |
| 143 | Return the 3D polyomino defined by a set of 3d coordinates. |
| 144 | |
| 145 | INPUT: |
| 146 | |
| 147 | - ``coords`` - iterable of 3d tuple |
| 148 | - ``color`` - string (optional, default: ``'gray'``), the color |
| 149 | |
| 150 | EXAMPLES:: |
| 151 | |
| 152 | sage: from sage.games.quantumino import Polyomino3d |
| 153 | sage: Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') |
| 154 | 3D Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue |
| 155 | """ |
| 156 | def __init__(self, coords, color='gray'): |
| 157 | r""" |
| 158 | INPUT: |
| 159 | |
| 160 | - ``coords`` - iterable of 3d tuple |
| 161 | - ``color`` - string (optional, default: ``'gray'``), the color |
| 162 | |
| 163 | EXAMPLES:: |
| 164 | |
| 165 | sage: from sage.games.quantumino import Polyomino3d |
| 166 | sage: Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') |
| 167 | 3D Polyomino: [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)], Color: blue |
| 168 | """ |
| 169 | assert all(len(a) == 3 for a in coords), "coord must be in dimension 3" |
| 170 | assert isinstance(color, str) |
| 171 | self._blocs = frozenset(tuple(c) for c in coords) |
| 172 | self._color = color |
| 173 | |
| 174 | def __repr__(self): |
| 175 | r""" |
| 176 | EXAMPLES:: |
| 177 | |
| 178 | sage: from sage.games.quantumino import Pentamino3D |
| 179 | sage: p = Pentamino3D(0) |
| 180 | sage: p |
| 181 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink |
| 182 | """ |
| 183 | s = "3D Polyomino: %s, " % sorted(self._blocs) |
| 184 | s += "Color: %s" % self._color |
| 185 | return s |
| 186 | |
| 187 | def __hash__(self): |
| 188 | r""" |
| 189 | EXAMPLES:: |
| 190 | |
| 191 | sage: from sage.games.quantumino import Polyomino3d |
| 192 | sage: p = Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') |
| 193 | sage: hash(p) |
| 194 | 2059134902 |
| 195 | """ |
| 196 | return hash(self._blocs) |
| 197 | |
| 198 | def __eq__(self, other): |
| 199 | r""" |
| 200 | Return whether self is equal to other. |
| 201 | |
| 202 | INPUT: |
| 203 | |
| 204 | - ``other`` - a polyomino |
| 205 | |
| 206 | EXAMPLES:: |
| 207 | |
| 208 | sage: from sage.games.quantumino import Polyomino3d |
| 209 | sage: p = Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') |
| 210 | sage: q = Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='red') |
| 211 | sage: p == q |
| 212 | True |
| 213 | sage: r = Polyomino3d([(0,0,0), (0,1,0), (1,1,0)], color='blue') |
| 214 | sage: p == r |
| 215 | False |
| 216 | """ |
| 217 | return self._blocs == other._blocs |
| 218 | |
| 219 | def __ne__(self, other): |
| 220 | r""" |
| 221 | Return whether self is not equal to other. |
| 222 | |
| 223 | INPUT: |
| 224 | |
| 225 | - ``other`` - a polyomino |
| 226 | |
| 227 | EXAMPLES:: |
| 228 | |
| 229 | sage: from sage.games.quantumino import Polyomino3d |
| 230 | sage: p = Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') |
| 231 | sage: q = Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='red') |
| 232 | sage: p != q |
| 233 | False |
| 234 | sage: r = Polyomino3d([(0,0,0), (0,1,0), (1,1,0)], color='blue') |
| 235 | sage: p != r |
| 236 | True |
| 237 | """ |
| 238 | return self._blocs != other._blocs |
| 239 | |
| 240 | def color(self): |
| 241 | r""" |
| 242 | Return the color of the polyomino. |
| 243 | |
| 244 | EXAMPLES:: |
| 245 | |
| 246 | sage: from sage.games.quantumino import Polyomino3d |
| 247 | sage: p = Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') |
| 248 | sage: p.color() |
| 249 | 'blue' |
| 250 | """ |
| 251 | return self._color |
| 252 | |
| 253 | def __iter__(self): |
| 254 | r""" |
| 255 | EXAMPLES:: |
| 256 | |
| 257 | sage: from sage.games.quantumino import Polyomino3d |
| 258 | sage: p = Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') |
| 259 | sage: it = iter(p) |
| 260 | sage: it.next() |
| 261 | (1, 1, 0) |
| 262 | """ |
| 263 | return iter(self._blocs) |
| 264 | |
| 265 | def rotated(self): |
| 266 | r""" |
| 267 | Iterator over the 24 orthogonal rotated image of self. |
| 268 | |
| 269 | EXAMPLES:: |
| 270 | |
| 271 | sage: from sage.games.quantumino import Polyomino3d |
| 272 | sage: p = Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') |
| 273 | sage: L = list(p.rotated()) |
| 274 | sage: len(L) |
| 275 | 24 |
| 276 | """ |
| 277 | for m in rotation_matrices: |
| 278 | L = [m * vector(p) for p in self] |
| 279 | yield Polyomino3d(L, color=self._color) |
| 280 | |
| 281 | def rotated_canonical(self): |
| 282 | r""" |
| 283 | Iterator over the 24 orthogonal rotated image of self where the |
| 284 | coordinates are all positive and minimal. |
| 285 | |
| 286 | EXAMPLES:: |
| 287 | |
| 288 | sage: from sage.games.quantumino import Polyomino3d |
| 289 | sage: p = Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') |
| 290 | sage: L = list(p.rotated_canonical()) |
| 291 | sage: len(L) |
| 292 | 24 |
| 293 | |
| 294 | They might not be all different:: |
| 295 | |
| 296 | sage: s = set(p.rotated_canonical()) |
| 297 | sage: len(s) |
| 298 | 12 |
| 299 | """ |
| 300 | for q in self.rotated(): |
| 301 | yield q.canonical() |
| 302 | |
| 303 | def canonical(self): |
| 304 | r""" |
| 305 | Returns the translated copy of self having minimal and positive |
| 306 | coordinates |
| 307 | |
| 308 | EXAMPLES:: |
| 309 | |
| 310 | sage: from sage.games.quantumino import Pentamino3D |
| 311 | sage: p = Pentamino3D(0) |
| 312 | sage: p |
| 313 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink |
| 314 | sage: p.canonical() |
| 315 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink |
| 316 | |
| 317 | TESTS:: |
| 318 | |
| 319 | sage: p |
| 320 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink |
| 321 | sage: p + (3,4,5) |
| 322 | 3D Polyomino: [(3, 4, 5), (4, 4, 5), (4, 5, 5), (4, 5, 6), (4, 6, 5)], Color: deeppink |
| 323 | sage: (p + (3,4,5)).canonical() |
| 324 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink |
| 325 | """ |
| 326 | minxyz,maxxyz = self.bounding_box() |
| 327 | mx, my, mz = minxyz |
| 328 | return self - (mx,my,mz) |
| 329 | |
| 330 | def is_higher_than(self, k): |
| 331 | r""" |
| 332 | EXAMPLES:: |
| 333 | |
| 334 | sage: from sage.games.quantumino import Pentamino3D |
| 335 | sage: p = Pentamino3D(15) |
| 336 | sage: p.is_higher_than(2) |
| 337 | False |
| 338 | """ |
| 339 | [(a,b,c), (aa,bb,cc)] = self.bounding_box() |
| 340 | return cc - c > k-1 |
| 341 | |
| 342 | def __sub__(self, arg): |
| 343 | r""" |
| 344 | EXAMPLES:: |
| 345 | |
| 346 | sage: from sage.games.quantumino import Pentamino3D |
| 347 | sage: p = Pentamino3D(0) |
| 348 | sage: p - (2,2,2) |
| 349 | 3D Polyomino: [(-2, -2, -2), (-1, -2, -2), (-1, -1, -2), (-1, -1, -1), (-1, 0, -2)], Color: deeppink |
| 350 | |
| 351 | """ |
| 352 | x,y,z = arg |
| 353 | return Polyomino3d([(a-x, b-y, c-z) for a,b,c in self], color=self._color) |
| 354 | |
| 355 | def __add__(self, arg): |
| 356 | r""" |
| 357 | EXAMPLES:: |
| 358 | |
| 359 | sage: from sage.games.quantumino import Pentamino3D |
| 360 | sage: p = Pentamino3D(0) |
| 361 | sage: p + (2,2,2) |
| 362 | 3D Polyomino: [(2, 2, 2), (3, 2, 2), (3, 3, 2), (3, 3, 3), (3, 4, 2)], Color: deeppink |
| 363 | """ |
| 364 | x,y,z = arg |
| 365 | return Polyomino3d([(a+x, b+y, c+z) for a,b,c in self], color=self._color) |
| 366 | |
| 367 | def bounding_box(self): |
| 368 | r""" |
| 369 | EXAMPLES:: |
| 370 | |
| 371 | sage: from sage.games.quantumino import Pentamino3D |
| 372 | sage: p = Pentamino3D(15) |
| 373 | sage: p.bounding_box() |
| 374 | [(0, 0, 0), (1, 2, 1)] |
| 375 | """ |
| 376 | xx,yy,zz = zip(*self) |
| 377 | minx = min(xx) |
| 378 | miny = min(yy) |
| 379 | minz = min(zz) |
| 380 | maxx = max(xx) |
| 381 | maxy = max(yy) |
| 382 | maxz = max(zz) |
| 383 | return [(minx,miny,minz), (maxx,maxy,maxz)] |
| 384 | |
| 385 | def translated(self, box): |
| 386 | r""" |
| 387 | Returns an iterator over the translated images of self inside a |
| 388 | box. |
| 389 | |
| 390 | INPUT: |
| 391 | |
| 392 | - ``box`` - tuple of size three, size of the box |
| 393 | |
| 394 | EXAMPLES:: |
| 395 | |
| 396 | sage: from sage.games.quantumino import Pentamino3D |
| 397 | sage: p = Pentamino3D(0) |
| 398 | sage: for t in p.translated(box=(5,8,2)): t |
| 399 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink |
| 400 | 3D Polyomino: [(0, 1, 0), (1, 1, 0), (1, 2, 0), (1, 2, 1), (1, 3, 0)], Color: deeppink |
| 401 | 3D Polyomino: [(0, 2, 0), (1, 2, 0), (1, 3, 0), (1, 3, 1), (1, 4, 0)], Color: deeppink |
| 402 | 3D Polyomino: [(0, 3, 0), (1, 3, 0), (1, 4, 0), (1, 4, 1), (1, 5, 0)], Color: deeppink |
| 403 | 3D Polyomino: [(0, 4, 0), (1, 4, 0), (1, 5, 0), (1, 5, 1), (1, 6, 0)], Color: deeppink |
| 404 | 3D Polyomino: [(0, 5, 0), (1, 5, 0), (1, 6, 0), (1, 6, 1), (1, 7, 0)], Color: deeppink |
| 405 | 3D Polyomino: [(1, 0, 0), (2, 0, 0), (2, 1, 0), (2, 1, 1), (2, 2, 0)], Color: deeppink |
| 406 | 3D Polyomino: [(1, 1, 0), (2, 1, 0), (2, 2, 0), (2, 2, 1), (2, 3, 0)], Color: deeppink |
| 407 | 3D Polyomino: [(1, 2, 0), (2, 2, 0), (2, 3, 0), (2, 3, 1), (2, 4, 0)], Color: deeppink |
| 408 | 3D Polyomino: [(1, 3, 0), (2, 3, 0), (2, 4, 0), (2, 4, 1), (2, 5, 0)], Color: deeppink |
| 409 | 3D Polyomino: [(1, 4, 0), (2, 4, 0), (2, 5, 0), (2, 5, 1), (2, 6, 0)], Color: deeppink |
| 410 | 3D Polyomino: [(1, 5, 0), (2, 5, 0), (2, 6, 0), (2, 6, 1), (2, 7, 0)], Color: deeppink |
| 411 | 3D Polyomino: [(2, 0, 0), (3, 0, 0), (3, 1, 0), (3, 1, 1), (3, 2, 0)], Color: deeppink |
| 412 | 3D Polyomino: [(2, 1, 0), (3, 1, 0), (3, 2, 0), (3, 2, 1), (3, 3, 0)], Color: deeppink |
| 413 | 3D Polyomino: [(2, 2, 0), (3, 2, 0), (3, 3, 0), (3, 3, 1), (3, 4, 0)], Color: deeppink |
| 414 | 3D Polyomino: [(2, 3, 0), (3, 3, 0), (3, 4, 0), (3, 4, 1), (3, 5, 0)], Color: deeppink |
| 415 | 3D Polyomino: [(2, 4, 0), (3, 4, 0), (3, 5, 0), (3, 5, 1), (3, 6, 0)], Color: deeppink |
| 416 | 3D Polyomino: [(2, 5, 0), (3, 5, 0), (3, 6, 0), (3, 6, 1), (3, 7, 0)], Color: deeppink |
| 417 | 3D Polyomino: [(3, 0, 0), (4, 0, 0), (4, 1, 0), (4, 1, 1), (4, 2, 0)], Color: deeppink |
| 418 | 3D Polyomino: [(3, 1, 0), (4, 1, 0), (4, 2, 0), (4, 2, 1), (4, 3, 0)], Color: deeppink |
| 419 | 3D Polyomino: [(3, 2, 0), (4, 2, 0), (4, 3, 0), (4, 3, 1), (4, 4, 0)], Color: deeppink |
| 420 | 3D Polyomino: [(3, 3, 0), (4, 3, 0), (4, 4, 0), (4, 4, 1), (4, 5, 0)], Color: deeppink |
| 421 | 3D Polyomino: [(3, 4, 0), (4, 4, 0), (4, 5, 0), (4, 5, 1), (4, 6, 0)], Color: deeppink |
| 422 | 3D Polyomino: [(3, 5, 0), (4, 5, 0), (4, 6, 0), (4, 6, 1), (4, 7, 0)], Color: deeppink |
| 423 | """ |
| 424 | box_x, box_y, box_z = box |
| 425 | cano = self.canonical() |
| 426 | x,y,z = zip(*cano) |
| 427 | mx = max(x) |
| 428 | my = max(y) |
| 429 | mz = max(z) |
| 430 | for i in range(box_x-mx): |
| 431 | for j in range(box_y-my): |
| 432 | for k in range(box_z-mz): |
| 433 | yield self + (i,j,k) |
| 434 | |
| 435 | def translated_rotated(self, box): |
| 436 | r""" |
| 437 | Return the translated and rotated of self that lies in the box. |
| 438 | |
| 439 | INPUT: |
| 440 | |
| 441 | - ``box`` - tuple of size three, size of the box |
| 442 | |
| 443 | EXAMPLES:: |
| 444 | |
| 445 | sage: from sage.games.quantumino import Pentamino3D |
| 446 | sage: p = Pentamino3D(0) |
| 447 | sage: L = list(p.translated_rotated(box=(5,8,2))) |
| 448 | sage: len(L) |
| 449 | 360 |
| 450 | |
| 451 | :: |
| 452 | |
| 453 | sage: p = Pentamino3D(6) |
| 454 | sage: L = list(p.translated_rotated(box=(5,8,2))) |
| 455 | sage: len(L) |
| 456 | 180 |
| 457 | """ |
| 458 | height = box[2] |
| 459 | all_distinct_cano = set(self.rotated_canonical()) |
| 460 | for cano in all_distinct_cano: |
| 461 | if cano.is_higher_than(height): |
| 462 | continue |
| 463 | for t in cano.translated(box=box): |
| 464 | yield t |
| 465 | |
| 466 | def middle_of_neighbor_coords(self): |
| 467 | r""" |
| 468 | Return the list of middle of neighbor coords. |
| 469 | |
| 470 | This is use to draw cube in between two neighbor cubes. |
| 471 | |
| 472 | EXAMPLES:: |
| 473 | |
| 474 | sage: from sage.games.quantumino import Polyomino3d |
| 475 | sage: p = Polyomino3d([(0,0,0),(0,0,1)]) |
| 476 | sage: list(p.middle_of_neighbor_coords()) |
| 477 | [(0.0, 0.0, 0.5)] |
| 478 | |
| 479 | :: |
| 480 | |
| 481 | sage: from sage.games.quantumino import Pentamino3D |
| 482 | sage: L = sorted(Pentamino3D(0).middle_of_neighbor_coords()) |
| 483 | sage: for a in L: a |
| 484 | (0.5, 0.0, 0.0) |
| 485 | (1.0, 0.5, 0.0) |
| 486 | (1.0, 1.0, 0.5) |
| 487 | (1.0, 1.5, 0.0) |
| 488 | """ |
| 489 | for (a,b,c), (p,q,r) in itertools.combinations(self, 2): |
| 490 | if [0,0,1] == sorted(map(abs, (p-a, q-b, r-c))): |
| 491 | yield ( (a+p)/2.0, (b+q)/2.0, (c+r)/2.0 ) |
| 492 | |
| 493 | def show3d(self, size=0.75): |
| 494 | r""" |
| 495 | INPUT: |
| 496 | |
| 497 | - ``size`` - number (optional, default: ``0.75``), the size of the |
| 498 | ``1 \times 1 \times 1`` cubes |
| 499 | |
| 500 | EXAMPLES:: |
| 501 | |
| 502 | sage: from sage.games.quantumino import Polyomino3d |
| 503 | sage: p = Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') |
| 504 | sage: p.show3d() |
| 505 | """ |
| 506 | #bug trac #11272 |
| 507 | G = Graphics() |
| 508 | for p in self: |
| 509 | G += cube(color=self._color, size=size).translate(p) |
| 510 | for m in self.middle_of_neighbor_coords(): |
| 511 | G += cube(color=self._color, size=size).translate(m) |
| 512 | return G |
| 513 | |
| 514 | ####################### |
| 515 | # General puzzle solver |
| 516 | ####################### |
| 517 | def solve_3d_puzzle(pieces, box=(5,8,2), verbose=False): |
| 518 | r""" |
| 519 | Solve the 3d puzzle, that is find the partition of the box into translated |
| 520 | and rotated pieces where each pieces is used exactly once. |
| 521 | |
| 522 | INPUT: |
| 523 | |
| 524 | - ``pieces`` - iterable of Polyominos3d |
| 525 | - ``box`` - tuple of size three (optional, default: ``(5,8,2)``), |
| 526 | size of the box |
| 527 | - ``verbose`` - bool |
| 528 | |
| 529 | OUTPUT: |
| 530 | |
| 531 | iterator of list of translated and rotated polyominos 3d that partition the box |
| 532 | |
| 533 | EXAMPLES:: |
| 534 | |
| 535 | sage: from sage.games.quantumino import Pentamino3D, solve_3d_puzzle |
| 536 | sage: L = map(Pentamino3D, range(17)) |
| 537 | sage: all_solutions = solve_3d_puzzle(L[:16], verbose=True) |
| 538 | sage: K = all_solutions.next() |
| 539 | Number of rows : 5664 |
| 540 | Number of distinct rows : 5664 |
| 541 | Row indices of the solution : [24, 696, 815, 1172, 1699, 1969, 5318, 5277, 4024, 3271, 4851, 2484, 4748, 4555, 2248, 2659] |
| 542 | sage: for k in K: k |
| 543 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink |
| 544 | 3D Polyomino: [(0, 0, 1), (0, 1, 0), (0, 1, 1), (0, 2, 1), (1, 2, 1)], Color: deeppink |
| 545 | 3D Polyomino: [(0, 2, 0), (0, 3, 0), (0, 4, 0), (1, 4, 0), (1, 4, 1)], Color: green |
| 546 | 3D Polyomino: [(0, 3, 1), (1, 3, 1), (2, 2, 0), (2, 2, 1), (2, 3, 1)], Color: green |
| 547 | 3D Polyomino: [(1, 3, 0), (2, 3, 0), (2, 4, 0), (2, 4, 1), (3, 4, 0)], Color: red |
| 548 | 3D Polyomino: [(1, 0, 1), (2, 0, 1), (2, 1, 0), (2, 1, 1), (3, 1, 1)], Color: red |
| 549 | 3D Polyomino: [(2, 0, 0), (3, 0, 0), (3, 0, 1), (4, 0, 1), (4, 1, 1)], Color: purple |
| 550 | 3D Polyomino: [(3, 1, 0), (3, 2, 0), (3, 2, 1), (4, 0, 0), (4, 1, 0)], Color: purple |
| 551 | 3D Polyomino: [(0, 4, 1), (0, 5, 0), (0, 5, 1), (0, 6, 1), (1, 5, 0)], Color: midnightblue |
| 552 | 3D Polyomino: [(3, 3, 0), (3, 3, 1), (4, 2, 0), (4, 2, 1), (4, 3, 1)], Color: yellow |
| 553 | 3D Polyomino: [(3, 4, 1), (3, 5, 1), (4, 3, 0), (4, 4, 0), (4, 4, 1)], Color: blue |
| 554 | 3D Polyomino: [(0, 7, 0), (0, 7, 1), (1, 7, 1), (2, 6, 1), (2, 7, 1)], Color: orange |
| 555 | 3D Polyomino: [(0, 6, 0), (1, 5, 1), (1, 6, 0), (1, 6, 1), (2, 5, 1)], Color: blue |
| 556 | 3D Polyomino: [(1, 7, 0), (2, 7, 0), (3, 6, 0), (3, 7, 0), (3, 7, 1)], Color: darkblue |
| 557 | 3D Polyomino: [(2, 5, 0), (2, 6, 0), (3, 5, 0), (4, 5, 0), (4, 5, 1)], Color: orange |
| 558 | 3D Polyomino: [(3, 6, 1), (4, 6, 0), (4, 6, 1), (4, 7, 0), (4, 7, 1)], Color: yellow |
| 559 | """ |
| 560 | box_x, box_y, box_z = box |
| 561 | number_of_pieces = len(pieces) |
| 562 | |
| 563 | # Bijection column indices <-> coordinates |
| 564 | SPACE = list(itertools.product(range(box_x), range(box_y), range(box_z))) |
| 565 | COORD_TO_INT = dict( (c,i+number_of_pieces) for i,c in enumerate(SPACE) ) |
| 566 | INT_TO_COORD = dict( (i+number_of_pieces,c) for i,c in enumerate(SPACE) ) |
| 567 | |
| 568 | # Creation of the rows |
| 569 | rows = [] |
| 570 | for i,p in enumerate(pieces): |
| 571 | for q in p.translated_rotated(box): |
| 572 | rows += [[i] + [COORD_TO_INT[coord] for coord in q]] |
| 573 | if verbose: |
| 574 | print "Number of rows : %s" % len(rows) |
| 575 | print "Number of distinct rows : %s" % len(set(map(tuple,rows))) |
| 576 | |
| 577 | # Solve using dancing links |
| 578 | from sage.combinat.matrices.dancing_links import dlx_solver |
| 579 | x = dlx_solver(rows) |
| 580 | |
| 581 | while True: |
| 582 | a = x.search() |
| 583 | solution = x.get_solution() |
| 584 | if verbose: |
| 585 | print "Row indices of the solution : %s" % solution |
| 586 | |
| 587 | #Recover the pentos from the solution |
| 588 | pentos = [] |
| 589 | for row_number in solution: |
| 590 | row = rows[row_number] |
| 591 | no = row[0] |
| 592 | coords = [INT_TO_COORD[i] for i in row[1:]] |
| 593 | p = Polyomino3d(coords, color=pieces[no].color()) |
| 594 | pentos.append(p) |
| 595 | yield pentos |
| 596 | |
| 597 | ################################################ |
| 598 | # Example: The family games america: Quantumino |
| 599 | ################################################ |
| 600 | _list_pentaminos = [] |
| 601 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (1,1,0), (1,2,0), (1,1,1)], color='deeppink')) |
| 602 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (1,1,0), (-1,0,0), (0,0,1)], color='deeppink')) |
| 603 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (1,1,0), (1,2,0), (0,0,1)], color='green')) |
| 604 | _list_pentaminos.append(Polyomino3d([(0,0,0), (0,1,0), (0,2,0), (1,0,0), (1,0,1)], color='green')) |
| 605 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (1,1,0), (1,0,1), (1,-1,1)], color='red')) |
| 606 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (1,1,0), (1,0,1), (2,0,1)], color='red')) |
| 607 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (1,1,0), (1,2,0), (1,2,1)], color='orange')) |
| 608 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (0,1,0), (0,2,0), (0,2,1)], color='orange')) |
| 609 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (0,1,0), (1,1,0), (0,0,1)], color='yellow')) |
| 610 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (1,1,0), (1,1,1), (0,0,1)], color='yellow')) |
| 611 | _list_pentaminos.append(Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (0,2,0), (1,1,1)], color='midnightblue')) |
| 612 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (1,1,0), (1,0,1), (1,2,0)], color='darkblue')) |
| 613 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (1,1,0), (1,1,1), (2,1,1)], color='blue')) |
| 614 | _list_pentaminos.append(Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,1,1), (1,2,1)], color='blue')) |
| 615 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (1,1,0), (2,1,0), (2,1,1)], color='purple')) |
| 616 | _list_pentaminos.append(Polyomino3d([(0,0,0), (0,1,0), (1,1,0), (1,2,0), (1,2,1)], color='purple')) |
| 617 | _list_pentaminos.append(Polyomino3d([(0,0,0), (1,0,0), (1,1,0), (1,0,1), (1,-1,0)], color='gray')) |
| 618 | |
| 619 | def Pentamino3D(i): |
| 620 | r""" |
| 621 | Return one of the seventeen 3D Pentamino pieces included in the game. |
| 622 | |
| 623 | INPUT: |
| 624 | |
| 625 | - ``i`` - integer, from 0 to 16. |
| 626 | |
| 627 | EXAMPLES:: |
| 628 | |
| 629 | sage: from sage.games.quantumino import Pentamino3D |
| 630 | sage: Pentamino3D(3) |
| 631 | 3D Polyomino: [(0, 0, 0), (0, 1, 0), (0, 2, 0), (1, 0, 0), (1, 0, 1)], Color: green |
| 632 | """ |
| 633 | return _list_pentaminos[i] |
| 634 | |
| 635 | |
| 636 | ############################## |
| 637 | # Class QuantuminoSolultion |
| 638 | ############################## |
| 639 | class QuantuminoSolution(SageObject): |
| 640 | r""" |
| 641 | A solution of the Quantumino puzzle. |
| 642 | |
| 643 | INPUT: |
| 644 | |
| 645 | - ``pentos`` - list of 16 3D pentamino representing the solution |
| 646 | - ``aside`` - the unused 3D pentamino |
| 647 | |
| 648 | EXAMPLES:: |
| 649 | |
| 650 | sage: from sage.games.quantumino import Pentamino3D, QuantuminoSolution |
| 651 | sage: p = Pentamino3D(0) |
| 652 | sage: q = Pentamino3D(5) |
| 653 | sage: r = Pentamino3D(11) |
| 654 | sage: S = QuantuminoSolution([p,q], r) |
| 655 | sage: S |
| 656 | Quantumino solution without the following pentamino : |
| 657 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 2, 0)], Color: darkblue |
| 658 | |
| 659 | :: |
| 660 | |
| 661 | sage: from sage.games.quantumino import quantumino_solver |
| 662 | sage: quantumino_solver.get_solution(3) # long time (1.5s) |
| 663 | Quantumino solution without the following pentamino : |
| 664 | 3D Polyomino: [(0, 0, 0), (0, 1, 0), (0, 2, 0), (1, 0, 0), (1, 0, 1)], Color: green |
| 665 | """ |
| 666 | def __init__(self, pentos, aside): |
| 667 | r""" |
| 668 | EXAMPLES:: |
| 669 | |
| 670 | sage: from sage.games.quantumino import Pentamino3D, QuantuminoSolution |
| 671 | sage: p = Pentamino3D(0) |
| 672 | sage: q = Pentamino3D(5) |
| 673 | sage: QuantuminoSolution([p], q) |
| 674 | Quantumino solution without the following pentamino : |
| 675 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 0, 1), (1, 1, 0), (2, 0, 1)], Color: red |
| 676 | """ |
| 677 | self._pentos = pentos |
| 678 | self._aside = aside |
| 679 | |
| 680 | def __repr__(self): |
| 681 | r""" |
| 682 | EXAMPLES:: |
| 683 | |
| 684 | sage: from sage.games.quantumino import Pentamino3D, QuantuminoSolution |
| 685 | sage: p = Pentamino3D(0) |
| 686 | sage: q = Pentamino3D(5) |
| 687 | sage: QuantuminoSolution([p], q) |
| 688 | Quantumino solution without the following pentamino : |
| 689 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 0, 1), (1, 1, 0), (2, 0, 1)], Color: red |
| 690 | """ |
| 691 | return "Quantumino solution without the following pentamino :\n%s" % self._aside |
| 692 | |
| 693 | def __iter__(self): |
| 694 | r""" |
| 695 | EXAMPLES:: |
| 696 | |
| 697 | sage: from sage.games.quantumino import Pentamino3D, QuantuminoSolution |
| 698 | sage: p = Pentamino3D(0) |
| 699 | sage: q = Pentamino3D(5) |
| 700 | sage: r = Pentamino3D(11) |
| 701 | sage: S = QuantuminoSolution([p,q], r) |
| 702 | sage: for a in S: a |
| 703 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink |
| 704 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 0, 1), (1, 1, 0), (2, 0, 1)], Color: red |
| 705 | """ |
| 706 | return iter(self._pentos) |
| 707 | |
| 708 | def show3d(self): |
| 709 | r""" |
| 710 | Show the solution in 3D. |
| 711 | |
| 712 | EXAMPLES:: |
| 713 | |
| 714 | sage: from sage.games.quantumino import quantumino_solver |
| 715 | sage: s = quantumino_solver.get_solution(0) # long time (1.5s) |
| 716 | sage: s.show3d() # long time (<1s) |
| 717 | |
| 718 | """ |
| 719 | G = Graphics() |
| 720 | for p in self: |
| 721 | G += p.show3d() |
| 722 | aside_pento = self._aside.canonical() + (2.5,-4,0) |
| 723 | G += aside_pento.show3d() |
| 724 | G.show(aspect_ratio=1, frame=False) |
| 725 | #return G |
| 726 | |
| 727 | |
| 728 | ############################## |
| 729 | # Class QuantuminoSolver |
| 730 | ############################## |
| 731 | class QuantuminoSolver(SageObject): |
| 732 | r""" |
| 733 | """ |
| 734 | def list_pentaminos(self): |
| 735 | r""" |
| 736 | Return the list of the 17 3-D pentaminos included in the game. |
| 737 | |
| 738 | OUTPUT: |
| 739 | |
| 740 | list |
| 741 | |
| 742 | EXAMPLES:: |
| 743 | |
| 744 | sage: from sage.games.quantumino import quantumino_solver |
| 745 | sage: L = quantumino_solver.list_pentaminos() |
| 746 | sage: len(L) |
| 747 | 17 |
| 748 | """ |
| 749 | return _list_pentaminos |
| 750 | |
| 751 | def show_pentaminos(self, box=(5,8,2)): |
| 752 | r""" |
| 753 | Show the 17 3-D pentaminos included in the game and the `5 \times 8 |
| 754 | \times 2` box where 16 of them must fit. |
| 755 | |
| 756 | INPUT: |
| 757 | |
| 758 | - ``box`` - tuple of size three (optional, default: ``(5,8,2)``), |
| 759 | size of the box |
| 760 | |
| 761 | OUTPUT: |
| 762 | |
| 763 | 3D Graphic object |
| 764 | |
| 765 | EXAMPLES:: |
| 766 | |
| 767 | sage: from sage.games.quantumino import quantumino_solver |
| 768 | sage: quantumino_solver.show_pentaminos() # long time (1s) |
| 769 | """ |
| 770 | G = Graphics() |
| 771 | for i,p in enumerate(self.list_pentaminos()): |
| 772 | x = 3.5 * (i%4) |
| 773 | y = 3.5 * (i/4) |
| 774 | q = p + (x, y, 0) |
| 775 | G += q.show3d() |
| 776 | G += text3d(str(i), (x,y,2)) |
| 777 | G += cube(color='gray',opacity=0.5).scale(box).translate((17,6,0)) |
| 778 | a,b = G.bounding_box() |
| 779 | a,b = map(vector, (a,b)) |
| 780 | G.frame_aspect_ratio(tuple(b-a)) |
| 781 | G.show(frame=False) |
| 782 | #return G |
| 783 | |
| 784 | def get_solution(self, without, box=(5,8,2), verbose=False): |
| 785 | r""" |
| 786 | Return a solution without one of the block. |
| 787 | |
| 788 | INPUT: |
| 789 | |
| 790 | - ``without`` - integer, from 0 to 16. |
| 791 | - ``box`` - tuple of size three (optional, default: ``(5,8,2)``), |
| 792 | size of the box |
| 793 | - ``verbose`` - bool (optional, default: ``False``) |
| 794 | |
| 795 | OUTPUT: |
| 796 | |
| 797 | QuantuminoSolution |
| 798 | |
| 799 | EXAMPLES:: |
| 800 | |
| 801 | sage: from sage.games.quantumino import quantumino_solver |
| 802 | sage: s = quantumino_solver.get_solution(8) |
| 803 | sage: s |
| 804 | Quantumino solution without the following pentamino : |
| 805 | 3D Polyomino: [(0, 0, 0), (0, 0, 1), (0, 1, 0), (1, 0, 0), (1, 1, 0)], Color: yellow |
| 806 | sage: for p in s: p |
| 807 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink |
| 808 | 3D Polyomino: [(0, 0, 1), (0, 1, 0), (0, 1, 1), (0, 2, 1), (1, 2, 1)], Color: deeppink |
| 809 | 3D Polyomino: [(0, 2, 0), (0, 3, 0), (0, 4, 0), (1, 4, 0), (1, 4, 1)], Color: green |
| 810 | 3D Polyomino: [(0, 3, 1), (1, 3, 1), (2, 2, 0), (2, 2, 1), (2, 3, 1)], Color: green |
| 811 | 3D Polyomino: [(1, 3, 0), (2, 3, 0), (2, 4, 0), (2, 4, 1), (3, 4, 0)], Color: red |
| 812 | 3D Polyomino: [(1, 0, 1), (2, 0, 0), (2, 0, 1), (2, 1, 0), (3, 0, 1)], Color: midnightblue |
| 813 | 3D Polyomino: [(0, 4, 1), (0, 5, 0), (0, 5, 1), (0, 6, 0), (1, 5, 0)], Color: red |
| 814 | 3D Polyomino: [(2, 1, 1), (3, 0, 0), (3, 1, 0), (3, 1, 1), (4, 0, 0)], Color: blue |
| 815 | 3D Polyomino: [(3, 2, 0), (4, 0, 1), (4, 1, 0), (4, 1, 1), (4, 2, 0)], Color: purple |
| 816 | 3D Polyomino: [(3, 2, 1), (3, 3, 0), (4, 2, 1), (4, 3, 0), (4, 3, 1)], Color: yellow |
| 817 | 3D Polyomino: [(3, 3, 1), (3, 4, 1), (4, 4, 0), (4, 4, 1), (4, 5, 0)], Color: blue |
| 818 | 3D Polyomino: [(0, 6, 1), (0, 7, 0), (0, 7, 1), (1, 5, 1), (1, 6, 1)], Color: purple |
| 819 | 3D Polyomino: [(1, 6, 0), (1, 7, 0), (1, 7, 1), (2, 7, 0), (3, 7, 0)], Color: darkblue |
| 820 | 3D Polyomino: [(2, 5, 0), (2, 6, 0), (3, 6, 0), (4, 6, 0), (4, 6, 1)], Color: orange |
| 821 | 3D Polyomino: [(2, 5, 1), (3, 5, 0), (3, 5, 1), (3, 6, 1), (4, 5, 1)], Color: gray |
| 822 | 3D Polyomino: [(2, 6, 1), (2, 7, 1), (3, 7, 1), (4, 7, 0), (4, 7, 1)], Color: orange |
| 823 | |
| 824 | Generalizations of the game inside different boxes:: |
| 825 | |
| 826 | sage: quantumino_solver.get_solution(7, (4,4,5)) # long time (2s) |
| 827 | Quantumino solution without the following pentamino : |
| 828 | 3D Polyomino: [(0, 0, 0), (0, 1, 0), (0, 2, 0), (0, 2, 1), (1, 0, 0)], Color: orange |
| 829 | sage: quantumino_solver.get_solution(7, (2,2,20)) # long time (1s) |
| 830 | Quantumino solution without the following pentamino : |
| 831 | 3D Polyomino: [(0, 0, 0), (0, 1, 0), (0, 2, 0), (0, 2, 1), (1, 0, 0)], Color: orange |
| 832 | sage: quantumino_solver.get_solution(3, (2,2,20)) # long time (1s) |
| 833 | Quantumino solution without the following pentamino : |
| 834 | 3D Polyomino: [(0, 0, 0), (0, 1, 0), (0, 2, 0), (1, 0, 0), (1, 0, 1)], Color: green |
| 835 | """ |
| 836 | return next(self.solutions_iterator(without, box, verbose)) |
| 837 | |
| 838 | def solutions_iterator(self, without, box=(5,8,2), verbose=False): |
| 839 | r""" |
| 840 | Return an iterator over the solutions without the block. |
| 841 | |
| 842 | INPUT: |
| 843 | |
| 844 | - ``without`` - integer, from 0 to 16. |
| 845 | - ``box`` - tuple of size three (optional, default: ``(5,8,2)``), |
| 846 | size of the box |
| 847 | - ``verbose`` - bool (optional, default: ``False``) |
| 848 | |
| 849 | OUTPUT: |
| 850 | |
| 851 | iterator of QuantuminoSolution |
| 852 | |
| 853 | EXAMPLES:: |
| 854 | |
| 855 | sage: from sage.games.quantumino import quantumino_solver |
| 856 | sage: it = quantumino_solver.solutions_iterator(0) |
| 857 | sage: it.next() |
| 858 | Quantumino solution without the following pentamino : |
| 859 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink |
| 860 | sage: it.next() |
| 861 | Quantumino solution without the following pentamino : |
| 862 | 3D Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink |
| 863 | """ |
| 864 | if not 0 <= without < 17: |
| 865 | raise ValueError, "without (=%s) must be between 0 and 16" % without |
| 866 | all_pieces = self.list_pentaminos() |
| 867 | pieces = all_pieces[:without] + all_pieces[without+1:] |
| 868 | aside = all_pieces[without] |
| 869 | for pentos in solve_3d_puzzle(pieces, box=box, verbose=verbose): |
| 870 | yield QuantuminoSolution(pentos, aside) |
| 871 | |
| 872 | |
| 873 | |
| 874 | quantumino_solver = QuantuminoSolver() |
| 875 | |
| 876 | |