Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/groups/perm_gps/cubegroup.py
4096 views
1
r"""
2
Rubik's cube group functions
3
4
.. _sec-rubik:
5
6
.. note::
7
8
"Rubiks cube" is trademarked. We shall omit the trademark
9
symbol below for simplicity.
10
11
NOTATION: B denotes a clockwise quarter turn of the back face D
12
denotes a clockwise quarter turn of the down face and similarly for
13
F (front), L (left), R (right), U (up) Products of moves are read
14
right to left, so for example, R\*U means move U first and then R.
15
16
See ``CubeGroup.parse()`` for all possible input
17
notations.
18
19
The "Singmaster notation":
20
21
- moves: U, D, R, L, F, B as in the
22
diagram below,
23
24
- corners: xyz means the facet is on face x (in R,F,L,U,D,B) and the
25
clockwise rotation of the corner sends x-y-z
26
27
- edges: xy means the facet is on face x and a flip of the edge sends
28
x-y.
29
30
::
31
32
sage: rubik = CubeGroup()
33
sage: rubik.display2d("")
34
+--------------+
35
| 1 2 3 |
36
| 4 top 5 |
37
| 6 7 8 |
38
+------------+--------------+-------------+------------+
39
| 9 10 11 | 17 18 19 | 25 26 27 | 33 34 35 |
40
| 12 left 13 | 20 front 21 | 28 right 29 | 36 rear 37 |
41
| 14 15 16 | 22 23 24 | 30 31 32 | 38 39 40 |
42
+------------+--------------+-------------+------------+
43
| 41 42 43 |
44
| 44 bottom 45 |
45
| 46 47 48 |
46
+--------------+
47
48
AUTHORS:
49
50
- David Joyner (2006-10-21): first version
51
52
- David Joyner (2007-05): changed faces, added legal and solve
53
54
- David Joyner(2007-06): added plotting functions
55
56
- David Joyner (2007, 2008): colors corrected, "solve" rewritten
57
(again),typos fixed.
58
59
- Robert Miller (2007, 2008): editing, cleaned up display2d
60
61
- Robert Bradshaw (2007, 2008): RubiksCube object, 3d
62
plotting.
63
64
- David Joyner (2007-09): rewrote docstring for CubeGroup's
65
"solve".
66
67
- Robert Bradshaw (2007-09): Versatile parse function for
68
all input types.
69
70
- Robert Bradshaw (2007-11): Cleanup.
71
72
REFERENCES:
73
74
- Cameron, P., Permutation Groups. New York: Cambridge
75
University Press, 1999.
76
77
- Wielandt, H., Finite Permutation Groups.
78
New York: Academic Press, 1964.
79
80
- Dixon, J. and Mortimer, B.,
81
Permutation Groups, Springer-Verlag, Berlin/New York, 1996.
82
83
- Joyner,D., Adventures in Group Theory, Johns Hopkins Univ Press,
84
2002.
85
"""
86
87
#**************************************************************************************
88
# Copyright (C) 2006 David Joyner <[email protected]>
89
#
90
# Distributed under the terms of the GNU General Public License (GPL)
91
# http://www.gnu.org/licenses/
92
#**************************************************************************************
93
94
from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic
95
import random
96
97
from sage.structure.sage_object import SageObject
98
99
from sage.rings.all import RationalField, Integer, RDF
100
#from sage.matrix.all import MatrixSpace
101
from sage.interfaces.all import gap
102
from sage.groups.perm_gps.permgroup_element import PermutationGroupElement
103
from sage.plot.polygon import polygon
104
from sage.plot.text import text
105
pi = RDF.pi()
106
107
108
from sage.plot.plot3d.shapes import Box
109
from sage.plot.plot3d.texture import Texture
110
111
####################### predefined colors ##################
112
113
named_colors = {
114
'red': (1,0,0), ## F face
115
'green': (0,1,0), ## R face
116
'blue': (0,0,1), ## D face
117
'yellow': (1,1,0), ## L face
118
'white': (1,1,1), ## none
119
'orange': (1,0.6,0.3), ## B face
120
'purple': (1,0,1), ## none
121
'lpurple': (1,0.63,1), ## U face
122
'lightblue': (0,1,1), ## none
123
'lgrey': (0.75,0.75,0.75), ## sagemath.org color
124
}
125
globals().update(named_colors)
126
127
#########################################################
128
#written by Tom Boothby, placed in the public domain
129
130
def xproj(x,y,z,r):
131
return (y*r[1] - x*r[3])*r[2]
132
133
def yproj(x,y,z,r):
134
return z*r[2] - (x*r[1] + y*r[2])*r[0]
135
136
def rotation_list(tilt,turn):
137
from sage.functions.all import sin, cos
138
return [ sin(tilt*pi/180.0), sin(turn*pi/180.0), cos(tilt*pi/180.0), cos(turn*pi/180.0) ]
139
140
def polygon_plot3d(points, tilt=30, turn=30, **kwargs):
141
"""
142
Plots a polygon viewed from an angle determined by tilt, turn, and
143
vertices points.
144
145
.. warning::
146
147
The ordering of the points is important to get "correct"
148
and if you add several of these plots together, the one added first
149
is also drawn first (ie, addition of Graphics objects is not
150
commutative).
151
152
The following example produced a green-colored square with vertices
153
at the points indicated.
154
155
EXAMPLES::
156
157
sage: from sage.groups.perm_gps.cubegroup import *
158
sage: P = polygon_plot3d([[1,3,1],[2,3,1],[2,3,2],[1,3,2],[1,3,1]],rgbcolor=green)
159
"""
160
rot = rotation_list(tilt,turn)
161
points2 = [(xproj(x,y,z,rot), yproj(x,y,z,rot)) for (x,y,z) in points ]
162
return polygon(points2, **kwargs)
163
164
###########################################################
165
166
############# lots of "internal" utility plot functions #########
167
168
169
170
def inv_list(lst):
171
"""
172
Input a list of ints 1, ..., m (in any order), outputs inverse
173
perm.
174
175
EXAMPLES::
176
177
sage: from sage.groups.perm_gps.cubegroup import *
178
sage: L = [2,3,1]
179
sage: inv_list(L)
180
[3, 1, 2]
181
"""
182
return [lst.index(i)+1 for i in range(1,1+len(lst))]
183
184
face_polys = {
185
### bottom layer L, F, R, B
186
'ldb': [[-3,0],[-2,0], [-2,1], [-3,1]], #square labeled 14
187
'ld': [[-2,0],[-1,0], [-1,1], [-2,1]], #square labeled 15
188
'lfd': [[-1,0],[0,0], [0,1], [-1,1]], #square labeled 16
189
'fdl': [[0,0],[1,0], [1,1], [0,1]], #square labeled 22
190
'fd': [[1,0],[2,0], [2,1], [1,1]], #square labeled 23
191
'frd': [[2,0],[3,0], [3,1], [2,1]], #square labeled 24
192
'rdf': [[3,0],[4,0], [4,1], [3,1]], #square labeled 30
193
'rd': [[4,0],[5,0], [5,1], [4,1]], #square labeled 31
194
'rbd': [[5,0],[6,0], [6,1], [5,1]], #square labeled 32
195
'bdr': [[6,0],[7,0], [7,1], [6,1]], #square labeled 38
196
'bd': [[7,0],[8,0], [8,1], [7,1]], #square labeled 39
197
'bld': [[8,0],[9,0], [9,1], [8,1]], #square labeled 40
198
### middle layer L,F,R, B
199
'lb': [[-3,1],[-2,1], [-2,2], [-3,2]], #square labeled 12
200
'l_center': [[-2,1],[-1,1], [-1,2], [-2,2]], #center square
201
'lf': [[-1,1],[0,1], [0,2], [-1,2]], #square labeled 13
202
'fl': [[0,1],[1,1], [1,2], [0,2]], #square labeled 20
203
'f_center': [[1,1],[2,1], [2,2], [1,2]], #center square
204
'fr': [[2,1],[3,1], [3,2], [2,2]], #square labeled 21
205
'rf': [[3,1],[4,1], [4,2], [3,2]], #square labeled 28
206
'r_center': [[4,1],[5,1], [5,2], [4,2]], #center square
207
'rb': [[5,1],[6,1], [6,2], [5,2]], #square labeled 29
208
'br': [[6,1],[7,1], [7,2], [6,2]], #square labeled 36
209
'b_center': [[7,1],[8,1], [8,2], [7,2]], #center square
210
'bl': [[8,1],[9,1], [9,2], [8,2]], #square labeled 37
211
## top layer L, F, R, B
212
'lbu': [[-3,2],[-2,2], [-2,3], [-3,3]], #square labeled 9
213
'lu': [[-2,2],[-1,2], [-1,3], [-2,3]], #square labeled 10
214
'luf': [[-1,2],[0,2], [0,3], [-1,3]], #square labeled 11
215
'flu': [[0,2],[1,2], [1,3], [0,3]], #square labeled 17
216
'fu': [[1,2],[2,2], [2,3], [1,3]], #square labeled 18
217
'fur': [[2,2],[3,2], [3,3], [2,3]], #square labeled 19
218
'ruf': [[3,2],[4,2], [4,3], [3,3]], #square labeled 25
219
'ru': [[4,2],[5,2], [5,3], [4,3]], #square labeled 26
220
'rub': [[5,2],[6,2], [6,3], [5,3]], #square labeled 27
221
'bur': [[6,2],[7,2], [7,3], [6,3]], #square labeled 33
222
'bu': [[7,2],[8,2], [8,3], [7,3]], #square labeled 34
223
'bul': [[8,2],[9,2], [9,3], [8,3]], #square labeled 35
224
# down face
225
'dlf': [[0,-1],[1,-1], [1,0], [0,0]], #square labeled 41
226
'df': [[1,-1],[2,-1], [2,0], [1,0]], #square labeled 42
227
'dfr': [[2,-1],[3,-1], [3,0], [2,0]], #square labeled 43
228
'dl': [[0,-2],[1,-2], [1,-1], [0,-1]], #square labeled 44
229
'd_center': [[1,-2],[2,-2], [2,-1], [1,-1]], #center square
230
'dr': [[2,-2],[3,-2], [3,-1], [2,-1]], #square labeled 45
231
'dlb': [[0,-3],[1,-3], [1,-2], [0,-2]], #square labeled 46
232
'db': [[1,-3],[2,-3], [2,-2], [1,-2]], #square labeled 47
233
'drb': [[2,-3],[3,-3], [3,-2], [2,-2]], #square labeled 48
234
# up face
235
'ufl': [[0,3],[1,3], [1,4], [0,4]], #square labeled 6
236
'uf': [[1,3],[2,3], [2,4], [1,4]], #square labeled 7
237
'urf': [[2,3],[3,3], [3,4], [2,4]], #square labeled 8
238
'ul': [[0,4],[1,4], [1,5], [0,5]], #square labeled 4
239
'u_center': [[1,4],[2,4], [2,5], [1,5]], #center square
240
'ur': [[2,4],[3,4], [3,5], [2,5]], #square labeled 5
241
'ulb': [[0,6],[1,6], [1,5], [0,5]], #square labeled 1
242
'ub': [[1,6],[2,6], [2,5], [1,5]], #square labeled 2
243
'ubr': [[2,6],[3,6], [3,5], [2,5]], #square labeled 3
244
}
245
246
def create_poly(face, color):
247
return polygon(face_polys[face], rgbcolor=color)
248
249
####################################################
250
251
singmaster_indices = {
252
1: "ulb",
253
2: "ub",
254
3: "ubr",
255
4: "ul",
256
5: "ur",
257
6: "ufl",
258
7: "uf",
259
8: "urf",
260
14: "ldb",
261
15: "ld",
262
16: "lfd",
263
12: "lb",
264
13: "lf",
265
9: "lbu",
266
10: "lu",
267
11: "luf",
268
17: "flu",
269
18: "fu",
270
19: "fur",
271
20: "fl",
272
21: "fr",
273
22: "fdl",
274
23: "fd",
275
24: "frd",
276
41: "dlf",
277
42: "df",
278
43: "dfr",
279
44: "dl",
280
45: "dr",
281
46: "dlb",
282
47: "db",
283
48: "drb",
284
33: "bur",
285
34: "bu",
286
35: "bul",
287
36: "br",
288
37: "bl",
289
38: "bdr",
290
39: "bd",
291
40: "bld",
292
25: "ruf",
293
26: "ru",
294
27: "rub",
295
28: "rf",
296
29: "rb",
297
30: "rdf",
298
31: "rd",
299
32: "rbd",
300
}
301
302
def index2singmaster(facet):
303
"""
304
Translates index used (eg, 43) to Singmaster facet notation (eg,
305
fdr).
306
307
EXAMPLES::
308
309
sage: from sage.groups.perm_gps.cubegroup import *
310
sage: index2singmaster(41)
311
'dlf'
312
"""
313
return singmaster_indices[facet]
314
315
def color_of_square(facet, colors=['lpurple', 'yellow', 'red', 'green', 'orange', 'blue']):
316
"""
317
Returns the color the facet has in the solved state.
318
319
EXAMPLES::
320
321
sage: from sage.groups.perm_gps.cubegroup import *
322
sage: color_of_square(41)
323
'blue'
324
"""
325
return colors[(facet-1) // 8]
326
327
cubie_center_list = {
328
# centers of the cubies on the F,U, R faces
329
1: [1/2,1/2,5/2], # ulb
330
2: [1/2,3/2,5/2], # ub
331
3: [1/2,5/2,5/2], # ubr
332
4: [3/2,1/2,5/2], # ul
333
5: [3/2,5/2,5/2], # ur
334
6: [5/2,1/2,5/2], # ufl
335
7: [5/2,3/2,5/2], # uf
336
8: [5/2,5/2,5/2], # urf
337
17: [5/2,1/2,5/2], # flu
338
18: [5/2,3/2,5/2], # fu
339
19: [5/2,5/2,5/2], # fur
340
20: [5/2,1/2,3/2], # fl
341
21: [5/2,5/2,3/2], # fr
342
22: [5/2,1/2,1/2], # fdl
343
23: [5/2,3/2,1/2], # fd
344
24: [5/2,5/2,1/2], # frd
345
25: [5/2,5/2,5/2], # rfu
346
26: [3/2,5/2,5/2], # ru
347
27: [1/2,5/2,5/2], # rub
348
28: [5/2,5/2,3/2], # rf
349
29: [1/2,5/2,3/2], # rb
350
30: [5/2,5/2,1/2], # rdf
351
31: [3/2,5/2,1/2], # rd
352
32: [1/2,5/2,1/2], # rbd
353
}
354
355
def cubie_centers(label):
356
return cubie_center_list[label]
357
358
def cubie_colors(label,state0):
359
# colors of the cubies on the F,U, R faces
360
clr_any = named_colors['white']
361
state = inv_list(state0)
362
if label == 1: return [clr_any, named_colors[color_of_square(state[1-1])], clr_any] #ulb,
363
if label == 2: return [clr_any,named_colors[color_of_square(state[2-1])],clr_any] # ub,
364
if label == 3: return [clr_any, named_colors[color_of_square(state[3-1])], named_colors[color_of_square(state[27-1])]] # ubr,
365
if label == 4: return [clr_any, named_colors[color_of_square(state[4-1])], clr_any] # ul,
366
if label == 5: return [clr_any, named_colors[color_of_square(state[5-1])], named_colors[color_of_square(state[26-1])]] # ur,
367
if label == 6: return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # ufl,
368
if label == 7: return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # uf,
369
if label == 8: return [named_colors[color_of_square(state[19-1])], named_colors[color_of_square(state[8-1])], named_colors[color_of_square(state[25-1])]] # urf,
370
if label == 17: return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # flu
371
if label == 18: return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # fu
372
if label == 19: return [named_colors[color_of_square(state[19-1])], named_colors[color_of_square(state[8-1])], named_colors[color_of_square(state[25-1])]] # fur
373
if label == 20: return [named_colors[color_of_square(state[20-1])], clr_any, clr_any] # fl
374
if label == 21: return [named_colors[color_of_square(state[21-1])], clr_any, named_colors[color_of_square(state[28-1])]] # fr
375
if label == 22: return [named_colors[color_of_square(state[22-1])], clr_any, clr_any] # fdl
376
if label == 23: return [named_colors[color_of_square(state[23-1])], clr_any, clr_any] # fd
377
if label == 24: return [named_colors[color_of_square(state[24-1])], clr_any, named_colors[color_of_square(state[30-1])]] # frd
378
if label == 25: return [named_colors[color_of_square(state[19-1])],named_colors[color_of_square(state[8-1])],named_colors[color_of_square(state[25-1])]] #rfu,
379
if label == 26: return [clr_any,named_colors[color_of_square(state[5-1])],named_colors[color_of_square(state[26-1])]] # ru,
380
if label == 27: return [clr_any,named_colors[color_of_square(state[3-1])],named_colors[color_of_square(state[27-1])]] # rub,
381
if label == 28: return [named_colors[color_of_square(state[21-1])],clr_any,named_colors[color_of_square(state[28-1])]] # rf,
382
if label == 29: return [clr_any,clr_any,named_colors[color_of_square(state[29-1])]] # rb,
383
if label == 30: return [named_colors[color_of_square(state[24-1])],clr_any,named_colors[color_of_square(state[30-1])]] # rdf,
384
if label == 31: return [clr_any,clr_any,named_colors[color_of_square(state[31-1])]] # rd,
385
if label == 32: return [clr_any,clr_any,named_colors[color_of_square(state[32-1])]] #rbd,
386
387
def plot3d_cubie(cnt, clrs):
388
"""
389
Plots the front, up and right face of a cubie centered at cnt and
390
rgbcolors given by clrs (in the order FUR).
391
392
Type P.show() to view.
393
394
EXAMPLES::
395
396
sage: from sage.groups.perm_gps.cubegroup import *
397
sage: clrF = blue; clrU = red; clrR = green
398
sage: P = plot3d_cubie([1/2,1/2,1/2],[clrF,clrU,clrR])
399
"""
400
x = cnt[0]-1/2; y = cnt[1]-1/2; z = cnt[2]-1/2
401
#ptsD = [[x+0,y+0,0+z],[x+1,y+0,0+z],[x+1,y+1,0+z],[x+0,y+1,0+z],[x+0,y+0,0+z]]
402
ptsF = [[x+1,y+0,0+z],[x+1,y+1,0+z],[x+1,y+1,1+z],[x+1,y+0,1+z],[x+1,y+0,0+z]]
403
#ptsB = [[x+0,y+0,0+z],[x+0,y+1,0+z],[x+0,y+1,1+z],[x+0,y+0,1+z],[x+0,y+0,0+z]]
404
ptsU = [[x+0,y+0,1+z],[x+1,y+0,1+z],[x+1,y+1,1+z],[x+0,y+1,1+z],[x+0,y+0,1+z]]
405
#ptsL = [[x+0,y+0,0+z],[x+1,y+0,0+z],[x+1,y+0,1+z],[x+0,y+0,1+z],[x+0,y+0,0+z]]
406
ptsR = [[x+0,y+1,0+z],[x+1,y+1,0+z],[x+1,y+1,1+z],[x+0,y+1,1+z],[x+0,y+1,0+z]]
407
PR = polygon_plot3d(ptsR,rgbcolor=clrs[2])
408
PU = polygon_plot3d(ptsU,rgbcolor=clrs[1])
409
PF = polygon_plot3d(ptsF,rgbcolor=clrs[0])
410
P = PR+PF+PU
411
P.axes(show=False)
412
return P
413
414
415
####################### end of "internal" utility plot functions #################
416
417
418
class CubeGroup(PermutationGroup_generic):
419
"""
420
A python class to help compute Rubik's cube group actions.
421
422
EXAMPLES: If G denotes the cube group then it may be regarded as a
423
subgroup of SymmetricGroup(48), where the 48 facets are labeled as
424
follows.
425
426
::
427
428
sage: rubik = CubeGroup()
429
sage: rubik.display2d("")
430
+--------------+
431
| 1 2 3 |
432
| 4 top 5 |
433
| 6 7 8 |
434
+------------+--------------+-------------+------------+
435
| 9 10 11 | 17 18 19 | 25 26 27 | 33 34 35 |
436
| 12 left 13 | 20 front 21 | 28 right 29 | 36 rear 37 |
437
| 14 15 16 | 22 23 24 | 30 31 32 | 38 39 40 |
438
+------------+--------------+-------------+------------+
439
| 41 42 43 |
440
| 44 bottom 45 |
441
| 46 47 48 |
442
+--------------+
443
444
::
445
446
sage: rubik
447
The PermutationGroup of all legal moves of the Rubik's cube.
448
sage: print rubik
449
The Rubik's cube group with generators R,L,F,B,U,D in SymmetricGroup(48).
450
"""
451
def __init__(self):
452
U = "( 1, 3, 8, 6)( 2, 5, 7, 4)( 9,33,25,17)(10,34,26,18)(11,35,27,19)" ## U = top
453
L = "( 9,11,16,14)(10,13,15,12)( 1,17,41,40)( 4,20,44,37)( 6,22,46,35)" ## L = left
454
F = "(17,19,24,22)(18,21,23,20)( 6,25,43,16)( 7,28,42,13)( 8,30,41,11)" ## F = front
455
R = "(25,27,32,30)(26,29,31,28)( 3,38,43,19)( 5,36,45,21)( 8,33,48,24)" ## R = right
456
B = "(33,35,40,38)(34,37,39,36)( 3, 9,46,32)( 2,12,47,29)( 1,14,48,27)" ## B = back or rear
457
D = "(41,43,48,46)(42,45,47,44)(14,22,30,38)(15,23,31,39)(16,24,32,40)" ## D = down or bottom
458
self.__gens = [B,D,F,L,R,U]
459
self._group = PermutationGroup([B,D,F,L,R,U], canonicalize=False)
460
461
def gen_names(self):
462
return ['B','D','F','L','R','U']
463
464
def __str__(self):
465
return "The Rubik's cube group with generators R,L,F,B,U,D in SymmetricGroup(48)."
466
467
def __repr__(self):
468
return "The PermutationGroup of all legal moves of the Rubik's cube."
469
470
def __call__(self, mv):
471
"""
472
EXAMPLES::
473
474
sage: rubik = CubeGroup()
475
sage: rubik(1)
476
()
477
"""
478
return self.parse(mv)
479
480
def group(self):
481
return self._group
482
483
def gens(self):
484
return self.__gens
485
486
def B(self):
487
G = self.group()
488
g = G(self.gens()[0])
489
return g
490
491
def D(self):
492
G = self.group()
493
g = G(self.gens()[1])
494
return g
495
496
def F(self):
497
G = self.group()
498
g = G(self.gens()[2])
499
return g
500
501
def L(self):
502
G = self.group()
503
g = G(self.gens()[3])
504
return g
505
506
def R(self):
507
G = self.group()
508
g = G(self.gens()[4])
509
return g
510
511
def U(self):
512
G = self.group()
513
g = G(self.gens()[5])
514
return g
515
516
517
def parse(self, mv):
518
"""
519
This function allows one to create the permutation group element
520
from a variety of formats.
521
522
INPUT: one of the following
523
524
525
- ``list`` - list of facets (as returned by
526
self.facets())
527
528
- ``dict`` - list of faces (as returned by
529
self.faces())
530
531
- ``str`` - either cycle notation (passed to GAP) or
532
a product of generators or Singmaster notation
533
534
- ``perm_group element`` - returned as an element of
535
self.group()
536
537
538
EXAMPLES::
539
540
sage: C = CubeGroup()
541
sage: C.parse(range(1,49))
542
()
543
sage: g = C.parse("L"); g
544
(1,17,41,40)(4,20,44,37)(6,22,46,35)(9,11,16,14)(10,13,15,12)
545
sage: C.parse(str(g)) == g
546
True
547
sage: facets = C.facets(g); facets
548
[17, 2, 3, 20, 5, 22, 7, 8, 11, 13, 16, 10, 15, 9, 12, 14, 41, 18, 19, 44, 21, 46, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 6, 36, 4, 38, 39, 1, 40, 42, 43, 37, 45, 35, 47, 48]
549
sage: C.parse(facets)
550
(1,17,41,40)(4,20,44,37)(6,22,46,35)(9,11,16,14)(10,13,15,12)
551
sage: C.parse(facets) == g
552
True
553
sage: faces = C.faces("L"); faces
554
{'right': [[25, 26, 27], [28, 0, 29], [30, 31, 32]], 'up': [[17, 2, 3], [20, 0, 5], [22, 7, 8]], 'back': [[33, 34, 6], [36, 0, 4], [38, 39, 1]], 'down': [[40, 42, 43], [37, 0, 45], [35, 47, 48]], 'front': [[41, 18, 19], [44, 0, 21], [46, 23, 24]], 'left': [[11, 13, 16], [10, 0, 15], [9, 12, 14]]}
555
sage: C.parse(faces) == C.parse("L")
556
True
557
sage: C.parse("L' R2") == C.parse("L^(-1)*R^2")
558
True
559
sage: C.parse("L' R2")
560
(1,40,41,17)(3,43)(4,37,44,20)(5,45)(6,35,46,22)(8,48)(9,14,16,11)(10,12,15,13)(19,38)(21,36)(24,33)(25,32)(26,31)(27,30)(28,29)
561
sage: C.parse("L^4")
562
()
563
sage: C.parse("L^(-1)*R")
564
(1,40,41,17)(3,38,43,19)(4,37,44,20)(5,36,45,21)(6,35,46,22)(8,33,48,24)(9,14,16,11)(10,12,15,13)(25,27,32,30)(26,29,31,28)
565
"""
566
G = self.group()
567
if isinstance(mv, PermutationGroupElement):
568
# mv is a perm_group element, return mv
569
return mv if mv.parent() is G else G(mv)
570
elif isinstance(mv, str):
571
# It is a string: may be in cycle notation or Rubik's notation
572
if '(' in mv and not '^' in mv:
573
return G(mv)
574
else:
575
gens = G.gens()
576
names = self.gen_names()
577
map = {}
578
for i in range(6):
579
map[names[i]] = gens[i]
580
g = G(1)
581
mv = mv.strip().replace(" ","*").replace("**", "*").replace("'", "-1").replace('^','').replace('(','').replace(')','')
582
M = mv.split("*")
583
for m in M:
584
if len(m) == 0:
585
pass
586
elif len(m) == 1:
587
g *= map[m[0]]
588
else:
589
g *= map[m[0]]**int(m[1:])
590
return g
591
elif isinstance(mv, dict):
592
state = mv
593
state_facets = []
594
keyss = state.keys()
595
keyss.sort()
596
for k in keyss:
597
r = state[k][0]+state[k][1]+state[k][2]
598
r.remove(0)
599
state_facets = state_facets + r
600
state0 = self.faces("")
601
state0_facets = []
602
keyss = state0.keys()
603
keyss.sort()
604
for k in keyss:
605
r = state0[k][0]+state0[k][1]+state0[k][2]
606
r.remove(0)
607
state0_facets = state0_facets + r
608
p1 = [state0_facets.index(x) for x in range(1,49)]
609
p2 = [state_facets[j] for j in p1]
610
return G(p2)
611
elif isinstance(mv, list):
612
return G(mv)
613
else:
614
return G(mv)
615
616
def facets(self, g=None):
617
r"""
618
Returns the set of facets on which the group acts. This function is
619
a "constant".
620
621
EXAMPLES::
622
623
sage: rubik = CubeGroup()
624
sage: rubik.facets()==range(1,49)
625
True
626
"""
627
fcts = range(1,49)
628
if g is not None:
629
return [g(i) for i in fcts]
630
else:
631
return fcts
632
633
def faces(self, mv):
634
r"""
635
Returns the dictionary of faces created by the effect of the move
636
mv, which is a string of the form `X^a*Y^b*...`, where
637
`X`, `Y`, ... are in `\{R,L,F,B,U,D\}` and
638
`a,b, ...` are integers. We call this ordering of the faces
639
the "BDFLRU, L2R, T2B ordering".
640
641
EXAMPLES::
642
643
sage: rubik = CubeGroup()
644
645
Now type ``rubik.faces("")`` for the dictionary of the
646
solved state and ``rubik.faces("R*L")`` for the
647
dictionary of the state obtained after making the move R followed
648
by L.
649
"""
650
fcts = self.facets(self.parse(mv))
651
faceR = [[fcts[24],fcts[25],fcts[26]],[fcts[27],0,fcts[28]],[fcts[29],fcts[30],fcts[31]]]
652
faceL = [[fcts[8],fcts[9],fcts[10]],[fcts[11],0,fcts[12]],[fcts[13],fcts[14],fcts[15]]]
653
faceU = [[fcts[0],fcts[1],fcts[2]],[fcts[3],0,fcts[4]],[fcts[5],fcts[6],fcts[7]]]
654
faceD = [[fcts[40],fcts[41],fcts[42]],[fcts[43],0,fcts[44]],[fcts[45],fcts[46],fcts[47]]]
655
faceF = [[fcts[16],fcts[17],fcts[18]],[fcts[19],0,fcts[20]],[fcts[21],fcts[22],fcts[23]]]
656
faceB = [[fcts[32],fcts[33],fcts[34]],[fcts[35],0,fcts[36]],[fcts[37],fcts[38],fcts[39]]]
657
return {'right':faceR,'left':faceL,'up':faceU,'down':faceD,'front':faceF,'back':faceB}
658
659
660
def move(self,mv):
661
r"""
662
Returns the group element and the reordered list of facets, as
663
moved by the list mv (read left-to-right)
664
665
INPUT: mv is a string of the form ``Xa*Yb*...``,
666
where X, Y, ... are in R,L,F,B,U,D and a,b, ... are integers.
667
668
EXAMPLES::
669
670
sage: rubik = CubeGroup()
671
sage: rubik.move("")[0]
672
()
673
sage: rubik.move("R")[0]
674
(3,38,43,19)(5,36,45,21)(8,33,48,24)(25,27,32,30)(26,29,31,28)
675
sage: rubik.R()
676
(3,38,43,19)(5,36,45,21)(8,33,48,24)(25,27,32,30)(26,29,31,28)
677
"""
678
g = self.parse(mv)
679
return g, self.facets(g)
680
681
def display2d(self,mv):
682
print self.repr2d(mv)
683
684
def repr2d(self, mv):
685
r"""
686
Displays a 2d map of the Rubik's cube after the move mv has been
687
made. Nothing is returned.
688
689
EXAMPLES::
690
691
sage: rubik = CubeGroup()
692
sage: rubik.display2d("")
693
+--------------+
694
| 1 2 3 |
695
| 4 top 5 |
696
| 6 7 8 |
697
+------------+--------------+-------------+------------+
698
| 9 10 11 | 17 18 19 | 25 26 27 | 33 34 35 |
699
| 12 left 13 | 20 front 21 | 28 right 29 | 36 rear 37 |
700
| 14 15 16 | 22 23 24 | 30 31 32 | 38 39 40 |
701
+------------+--------------+-------------+------------+
702
| 41 42 43 |
703
| 44 bottom 45 |
704
| 46 47 48 |
705
+--------------+
706
707
::
708
709
sage: rubik.display2d("R")
710
+--------------+
711
| 1 2 38 |
712
| 4 top 36 |
713
| 6 7 33 |
714
+------------+--------------+-------------+------------+
715
| 9 10 11 | 17 18 3 | 27 29 32 | 48 34 35 |
716
| 12 left 13 | 20 front 5 | 26 right 31 | 45 rear 37 |
717
| 14 15 16 | 22 23 8 | 25 28 30 | 43 39 40 |
718
+------------+--------------+-------------+------------+
719
| 41 42 19 |
720
| 44 bottom 21 |
721
| 46 47 24 |
722
+--------------+
723
724
You can see the right face has been rotated but not the left face.
725
"""
726
g = self.parse(mv)
727
lst = self.facets(g)
728
line1 = " +--------------+\n"
729
line2 = " |%3d %3d %3d |\n"%(lst[0],lst[1],lst[2])
730
line3 = " |%3d top %3d |\n"%(lst[3],lst[4])
731
line4 = " |%3d %3d %3d |\n"%(lst[5],lst[6],lst[7])
732
line5 = "+------------+--------------+-------------+------------+\n"
733
line6 = "|%3d %3d %3d |%3d %3d %3d |%3d %3d %3d |%3d %3d %3d |\n"%(lst[8],lst[9],lst[10],lst[16],lst[17],lst[18],lst[24],lst[25],lst[26],lst[32],lst[33],lst[34])
734
line7 = "|%3d left%3d |%3d front%3d |%3d right%3d |%3d rear%3d |\n"%(lst[11],lst[12],lst[19],lst[20],lst[27],lst[28],lst[35],lst[36])
735
line8 = "|%3d %3d %3d |%3d %3d %3d |%3d %3d %3d |%3d %3d %3d |\n"%(lst[13],lst[14],lst[15],lst[21],lst[22],lst[23],lst[29],lst[30],lst[31],lst[37],lst[38],lst[39])
736
line9 = "+------------+--------------+-------------+------------+\n"
737
line10 = " |%3d %3d %3d |\n"%(lst[40],lst[41],lst[42])
738
line11 = " |%3d bottom%3d |\n"%(lst[43],lst[44])
739
line12 = " |%3d %3d %3d |\n"%(lst[45],lst[46],lst[47])
740
line13 = " +--------------+\n"
741
return line1+line2+line3+line4+line5+line6+line7+line8+line9+line10+line11+line12+line13
742
743
def plot_cube(self, mv, title=True, colors = [lpurple, yellow, red, green, orange, blue]):
744
"""
745
Input the move mv, as a string in the Singmaster notation, and
746
output the 2-d plot of the cube in that state.
747
748
Type P.show() to display any of the plots below.
749
750
EXAMPLES::
751
752
sage: rubik = CubeGroup()
753
sage: P = rubik.plot_cube("R^2*U^2*R^2*U^2*R^2*U^2", title = False)
754
sage: # (R^2U^2)^3 permutes 2 pairs of edges (uf,ub)(fr,br)
755
sage: P = rubik.plot_cube("R*L*D^2*B^3*L^2*F^2*R^2*U^3*D*R^3*D^2*F^3*B^3*D^3*F^2*D^3*R^2*U^3*F^2*D^3")
756
sage: # the superflip (in 20f* moves)
757
sage: P = rubik.plot_cube("U^2*F*U^2*L*R^(-1)*F^2*U*F^3*B^3*R*L*U^2*R*D^3*U*L^3*R*D*R^3*L^3*D^2")
758
sage: # "superflip+4 spot" (in 26q* moves)
759
"""
760
g = self.parse(mv)
761
state = self.facets(g)
762
#print state
763
cubies = [create_poly(index2singmaster(state[x]), color_of_square(x+1, colors)) for x in range(48)]
764
centers = [create_poly('%s_center' % "ulfrbd"[i], colors[i]) for i in range(6)]
765
clrs = sum(cubies) + sum(centers)
766
clrs.axes(show=False)
767
if title == True:
768
t = text('sagemath.org', (7.8,-3.5),rgbcolor=lgrey)
769
P = clrs+t
770
P.axes(show=False)
771
return P
772
return clrs
773
774
def plot3d_cube(self,mv,title=True):
775
"""
776
Displays F,U,R faces of the cube after the given move mv, where mv
777
is a string in the Singmaster notation. Mostly included for the
778
purpose of drawing pictures and checking moves.
779
780
The first one below is "superflip+4 spot" (in 26q\* moves) and the
781
second one is the superflip (in 20f\* moves). Type show(P) to view
782
them.
783
784
EXAMPLES::
785
786
sage: rubik = CubeGroup()
787
sage: P = rubik.plot3d_cube("U^2*F*U^2*L*R^(-1)*F^2*U*F^3*B^3*R*L*U^2*R*D^3*U*L^3*R*D*R^3*L^3*D^2")
788
sage: P = rubik.plot3d_cube("R*L*D^2*B^3*L^2*F^2*R^2*U^3*D*R^3*D^2*F^3*B^3*D^3*F^2*D^3*R^2*U^3*F^2*D^3")
789
"""
790
g = self.parse(mv)
791
state = self.facets(g)
792
clr_any = white
793
shown_labels = range(1,9)+range(17,33)
794
clr = [color_of_square(state[c-1]) for c in shown_labels]
795
cubiesR = [plot3d_cubie(cubie_centers(c),cubie_colors(c,state)) for c in [32,31,30,29,28,27,26,25]]
796
cubeR = sum(cubiesR)
797
cubiesU = [plot3d_cubie(cubie_centers(c),cubie_colors(c,state)) for c in range(1,9)]
798
cubeU = sum(cubiesU)
799
cubiesF = [plot3d_cubie(cubie_centers(c),cubie_colors(c,state)) for c in [22,23,24,20,21]]
800
cubeF = sum(cubiesF)
801
centerR = polygon_plot3d([[1,3,1],[2,3,1],[2,3,2],[1,3,2],[1,3,1]],rgbcolor=green)
802
centerF = polygon_plot3d([[3,1,1],[3,2,1],[3,2,2],[3,1,2],[3,1,1]],rgbcolor=red)
803
centerU = polygon_plot3d([[1,1,3],[1,2,3],[2,2,3],[2,1,3],[1,1,3]],rgbcolor=lpurple)
804
centers = centerF+centerR+centerU
805
P = cubeR+cubeF+cubeU+centers
806
P.axes(show=False)
807
if title == True:
808
t1 = text('Up, Front, and Right faces. ' , (-0.2,-2.5))
809
t2 = text(' sagemath.org', (0.8,-3.1),rgbcolor=lgrey)
810
t3 = text(" ",(3.5,0),rgbcolor=white)
811
P = P+t1+t2+t3
812
P.axes(show=False)
813
return P
814
return P
815
816
def legal(self,state,mode="quiet"):
817
r"""
818
Returns 1 (true) if the dictionary ``state`` (in the
819
same format as returned by the faces method) represents a legal
820
position (or state) of the Rubik's cube. Returns 0 (false)
821
otherwise.
822
823
EXAMPLES::
824
825
sage: rubik = CubeGroup()
826
sage: G = rubik.group()
827
sage: r0 = rubik.faces("")
828
sage: r1 = {'back': [[33, 34, 35], [36, 0, 37], [38, 39, 40]], 'down': [[41, 42, 43], [44, 0, 45], [46, 47, 48]],'front': [[17, 18, 19], [20, 0, 21], [22, 23, 24]],'left': [[9, 10, 11], [12, 0, 13], [14, 15, 16]],'right': [[25, 26, 27], [28, 0, 29], [30, 31, 32]],'up': [[1, 2, 3], [4, 0, 5], [6, 8, 7]]}
829
sage: rubik.legal(r0)
830
1
831
sage: rubik.legal(r0,"verbose")
832
(1, ())
833
sage: rubik.legal(r1)
834
0
835
"""
836
try:
837
g = self.parse(state)
838
res = 1
839
except TypeError:
840
res = 0
841
g = self.group().identity()
842
843
if mode != 'quiet':
844
return res, g
845
else:
846
return res
847
848
def solve(self,state, algorithm='default'):
849
r"""
850
Solves the cube in the ``state``, given as a dictionary
851
as in ``legal``. See the ``solve`` method
852
of the RubiksCube class for more details.
853
854
This may use GAP's ``EpimorphismFromFreeGroup`` and
855
``PreImagesRepresentative`` as explained below, if
856
'gap' is passed in as the algorithm.
857
858
This algorithm
859
860
861
#. constructs the free group on 6 generators then computes a
862
reasonable set of relations which they satisfy
863
864
#. computes a homomorphism from the cube group to this free group
865
quotient
866
867
#. takes the cube position, regarded as a group element, and maps
868
it over to the free group quotient
869
870
#. using those relations and tricks from combinatorial group theory
871
(stabilizer chains), solves the "word problem" for that element.
872
873
#. uses python string parsing to rewrite that in cube notation.
874
875
876
The Rubik's cube group has about `4.3 \times 10^{19}`
877
elements, so this process is time-consuming. See
878
http://www.gap-system.org/Doc/Examples/rubik.html for an
879
interesting discussion of some GAP code analyzing the Rubik's
880
cube.
881
882
EXAMPLES::
883
884
sage: rubik = CubeGroup()
885
sage: state = rubik.faces("R")
886
sage: rubik.solve(state)
887
'R'
888
sage: state = rubik.faces("R*U")
889
sage: rubik.solve(state, algorithm='gap') # long time
890
'R*U'
891
892
You can also check this another (but similar) way using the
893
``word_problem`` method (eg, G = rubik.group(); g =
894
G("(3,38,43,19)(5,36,45,21)(8,33,48,24)(25,27,32,30)(26,29,31,28)");
895
g.word_problem([b,d,f,l,r,u]), though the output will be less
896
intuitive).
897
"""
898
from sage.groups.perm_gps.permgroup import PermutationGroup
899
from sage.interfaces.all import gap
900
G = self.group()
901
try:
902
g = self.parse(state)
903
except TypeError:
904
return "Illegal or syntactically incorrect state. No solution."
905
if algorithm != 'gap':
906
C = RubiksCube(g)
907
return C.solve(algorithm)
908
909
hom = G._gap_().EpimorphismFromFreeGroup()
910
soln = hom.PreImagesRepresentative(gap(str(g)))
911
# print soln
912
sol = str(soln)
913
names = self.gen_names()
914
for i in range(6):
915
sol = sol.replace("x%s" % (i+1), names[i])
916
return sol
917
918
919
##########################################################
920
# 3d object generation
921
##########################################################
922
923
def cubie_faces():
924
"""
925
This provides a map from the 6 faces of the 27 cubies to the 48
926
facets of the larger cube.
927
928
-1,-1,-1 is left, top, front
929
"""
930
faceR = [[25,26,27], [28,-3,29], [30,31,32]] # green
931
faceL = [[ 9,10,11], [12,-5,13], [14,15,16]] # orange
932
faceU = [[ 1, 2, 3], [ 4,-6, 5], [ 6, 7, 8]] # red
933
faceD = [[41,42,43], [44,-1,45], [46,47,48]] # purple
934
faceF = [[17,18,19], [20,-4,21], [22,23,24]] # yellow
935
faceB = [[33,34,35], [36,-2,37], [38,39,40]] # blue
936
cubies = {}
937
for x in [-1,0,1]:
938
for y in [-1,0,1]:
939
for z in [-1,0,1]:
940
cubies[x,y,z] = [0,0,0,0,0,0]
941
942
for i in [-1,0,1]:
943
for j in [-1,0,1]:
944
cubies[ i, j, -1][1] = faceF[1+j][1+i]
945
cubies[ i, j, 1][4] = faceB[1+j][1-i]
946
cubies[ i, -1, j][0] = faceU[1-j][1+i]
947
cubies[ i, 1, j][3] = faceD[1+j][1+i]
948
cubies[ -1, i, j][2] = faceL[1+i][1-j]
949
cubies[ 1, i, j][5] = faceR[1+i][1+j]
950
951
return cubies
952
953
cubie_face_list = cubie_faces()
954
955
956
rand_colors = [(RDF.random_element(), RDF.random_element(), RDF.random_element()) for _ in range(56)]
957
958
959
class RubiksCube(SageObject):
960
"""
961
::
962
963
sage: C = RubiksCube().move("R U R'")
964
sage: C.show3d()
965
966
::
967
968
sage: C = RubiksCube("R*L"); C
969
+--------------+
970
| 17 2 38 |
971
| 20 top 36 |
972
| 22 7 33 |
973
+------------+--------------+-------------+------------+
974
| 11 13 16 | 41 18 3 | 27 29 32 | 48 34 6 |
975
| 10 left 15 | 44 front 5 | 26 right 31 | 45 rear 4 |
976
| 9 12 14 | 46 23 8 | 25 28 30 | 43 39 1 |
977
+------------+--------------+-------------+------------+
978
| 40 42 19 |
979
| 37 bottom 21 |
980
| 35 47 24 |
981
+--------------+
982
sage: C.show()
983
sage: C.solve(algorithm='gap') # long time
984
'L R'
985
sage: C == RubiksCube("L*R")
986
True
987
"""
988
def __init__(self, state=None, history=[], colors=[lpurple,yellow,red,green,orange,blue]):
989
self.colors = colors
990
self._history = history
991
self._group = CubeGroup()
992
if state is None:
993
self._state = self._group(1)
994
else:
995
if isinstance(state, str):
996
state = self._group.faces(state)
997
if not isinstance(state, PermutationGroupElement):
998
legal, state = self._group.legal(state, mode="gimme_group_element")
999
if not legal:
1000
raise ValueError, "Not a legal cube."
1001
self._state = state
1002
1003
def move(self, g):
1004
if not g in self._group:
1005
g = self._group.move(g)[0]
1006
return RubiksCube(self._state * g, self._history + [g], self.colors)
1007
1008
def undo(self):
1009
g = self._history[-1]
1010
return RubiksCube(self._state * ~g, self._history[:-1], self.colors)
1011
1012
def __repr__(self):
1013
return self._group.repr2d(self._state)
1014
1015
def facets(self):
1016
return self._group.facets(self._state)
1017
1018
def plot(self):
1019
return self._group.plot_cube(self._state)
1020
1021
def show(self):
1022
self.plot().show()
1023
1024
1025
def cubie(self, size, gap, x,y,z, colors, stickers=True):
1026
sides = cubie_face_list[x,y,z]
1027
t = 2*size+gap
1028
my_colors = [colors[sides[i]+6] for i in range(6)]
1029
if stickers:
1030
B = Box(size, size, size, color=(.1, .1, .1))
1031
S = B + B.stickers(my_colors, size*.1, size*.01)
1032
return S.translate(-t*x, -t*z, -t*y)
1033
else:
1034
return ColorCube(size, [colors[sides[i]+6] for i in range(6)]).translate(-t*x, -t*z, -t*y)
1035
1036
def plot3d(self, stickers=True):
1037
"""
1038
::
1039
1040
sage: C = RubiksCube().move("R*U")
1041
sage: C.plot3d()
1042
sage: C.plot()
1043
"""
1044
while len(self.colors) < 7:
1045
self.colors.append((.1, .1, .1))
1046
side_colors = [Texture(color=c, ambient=.75) for c in self.colors]
1047
start_colors = sum([[c]*8 for c in side_colors], [])
1048
facets = self._group.facets(self._state)
1049
facet_colors = [0]*48
1050
for i in range(48):
1051
facet_colors[facets[i]-1] = start_colors[i]
1052
all_colors = side_colors + facet_colors
1053
pm = [-1,0,1]
1054
C = sum([self.cubie(.15, .025, x, y, z, all_colors, stickers) for x in pm for y in pm for z in pm], Box(.35, .35, .35, color=self.colors[-1]))
1055
return C.rotateZ(1.5) #.scale([1,-1,1]).rotateZ(1.5)
1056
1057
def show3d(self):
1058
return self.plot3d().show()
1059
1060
def __cmp__(self, other):
1061
c = cmp(type(self), type(other))
1062
if c == 0:
1063
return cmp(self._state, other._state)
1064
else:
1065
return c
1066
1067
def solve(self, algorithm="hybrid", timeout=15):
1068
"""
1069
Algorithm must be one of : hybrid - try kociemba for timeout
1070
seconds, then dietz kociemba - Use Dik T. Winter's program
1071
(reasonable speed, few moves) dietz - Use Eric Dietz's cubex
1072
program (fast but lots of moves) optimal - Use Michael Reid's
1073
optimal program (may take a long time) gap - Use GAP word solution
1074
(can be slow)
1075
1076
EXAMPLE::
1077
1078
sage: C = RubiksCube("R U F L B D")
1079
sage: C.solve()
1080
'R U F L B D'
1081
1082
Dietz's program is much faster, but may give highly non-optimal
1083
solutions.
1084
1085
::
1086
1087
sage: s = C.solve('dietz'); s
1088
"U' L' L' U L U' L U D L L D' L' D L' D' L D L' U' L D' L' U L' B' U' L' U B L D L D' U' L' U L B L B' L' U L U' L' F' L' F L' F L F' L' D' L' D D L D' B L B' L B' L B F' L F F B' L F' B D' D' L D B' B' L' D' B U' U' L' B' D' F' F' L D F'"
1089
sage: C2 = RubiksCube(s)
1090
sage: C == C2
1091
True
1092
"""
1093
import sage.interfaces.rubik # here to avoid circular referencing
1094
if algorithm == "default":
1095
algorithm = "hybrid"
1096
1097
if algorithm == "hybrid":
1098
try:
1099
solver = sage.interfaces.rubik.DikSolver()
1100
return solver.solve(self.facets(), timeout=timeout)
1101
except RuntimeError:
1102
solver = sage.interfaces.rubik.CubexSolver()
1103
return solver.solve(self.facets())
1104
1105
elif algorithm == "kociemba":
1106
solver = sage.interfaces.rubik.DikSolver()
1107
return solver.solve(self.facets(), timeout=timeout)
1108
1109
elif algorithm == "dietz":
1110
solver = sage.interfaces.rubik.CubexSolver()
1111
return solver.solve(self.facets())
1112
1113
elif algorithm == "optimal":
1114
# TODO: cache this, startup is expensive
1115
solver = sage.interfaces.rubik.OptimalSolver()
1116
return solver.solve(self.facets())
1117
1118
elif algorithm == "gap":
1119
solver = CubeGroup()
1120
return solver.solve(self._state)
1121
1122
else:
1123
raise ValueError, "Unrecognized algorithm: %s" % algorithm
1124
1125
def scramble(self, moves=30):
1126
last_move = move = " "
1127
all = []
1128
for i in range(moves):
1129
while move[0] == last_move[0]:
1130
move = "RLUDBF"[random.randint(0,5)] + " '2"[random.randint(0,2)]
1131
last_move = move
1132
all.append(move)
1133
return self.move(' '.join(all))
1134
1135