Path: blob/master/src/sage/groups/perm_gps/cubegroup.py
8815 views
r"""1Rubik's cube group functions23.. _sec-rubik:45.. NOTE::67"Rubiks cube" is trademarked. We shall omit the trademark8symbol below for simplicity.910NOTATION:1112`B` denotes a clockwise quarter turn of the back face, `D`13denotes a clockwise quarter turn of the down face, and similarly for14`F` (front), `L` (left), `R` (right), and `U` (up). Products of moves are read15right to left, so for example, `R \cdot U` means move `U` first and then `R`.1617See ``CubeGroup.parse()`` for all possible input notations.1819The "Singmaster notation":2021- moves: `U, D, R, L, F, B` as in the22diagram below,2324- corners: `xyz` means the facet is on face `x` (in `R,F,L,U,D,B`) and the25clockwise rotation of the corner sends `x-y-z`2627- edges: `xy` means the facet is on face `x` and a flip of the edge sends28`x-y`.2930::3132sage: rubik = CubeGroup()33sage: 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+--------------+4748AUTHORS:4950- David Joyner (2006-10-21): first version5152- David Joyner (2007-05): changed faces, added legal and solve5354- David Joyner(2007-06): added plotting functions5556- David Joyner (2007, 2008): colors corrected, "solve" rewritten57(again),typos fixed.5859- Robert Miller (2007, 2008): editing, cleaned up display2d6061- Robert Bradshaw (2007, 2008): RubiksCube object, 3d62plotting.6364- David Joyner (2007-09): rewrote docstring for CubeGroup's65"solve".6667- Robert Bradshaw (2007-09): Versatile parse function for68all input types.6970- Robert Bradshaw (2007-11): Cleanup.7172REFERENCES:7374- Cameron, P., Permutation Groups. New York: Cambridge75University Press, 1999.7677- Wielandt, H., Finite Permutation Groups.78New York: Academic Press, 1964.7980- Dixon, J. and Mortimer, B.,81Permutation Groups, Springer-Verlag, Berlin/New York, 1996.8283- Joyner,D., Adventures in Group Theory, Johns Hopkins Univ Press,842002.85"""8687#**************************************************************************************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#**************************************************************************************9394from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic95import random9697from sage.structure.sage_object import SageObject9899from sage.rings.all import RationalField, Integer, RDF100#from sage.matrix.all import MatrixSpace101from sage.interfaces.all import gap102from sage.groups.perm_gps.permgroup_element import PermutationGroupElement103from sage.plot.polygon import polygon104from sage.plot.text import text105pi = RDF.pi()106107108from sage.plot.plot3d.shapes import Box109from sage.plot.plot3d.texture import Texture110111####################### predefined colors ##################112113named_colors = {114'red': (1,0,0), ## F face115'green': (0,1,0), ## R face116'blue': (0,0,1), ## D face117'yellow': (1,1,0), ## L face118'white': (1,1,1), ## none119'orange': (1,0.6,0.3), ## B face120'purple': (1,0,1), ## none121'lpurple': (1,0.63,1), ## U face122'lightblue': (0,1,1), ## none123'lgrey': (0.75,0.75,0.75), ## sagemath.org color124}125globals().update(named_colors)126127#########################################################128#written by Tom Boothby, placed in the public domain129130def xproj(x,y,z,r):131r"""132Return the `x`-projection of `(x,y,z)` rotated by `r`.133134EXAMPLES::135136sage: from sage.groups.perm_gps.cubegroup import rotation_list, xproj137sage: rot = rotation_list(30, 45)138sage: xproj(1,2,3,rot)1390.612372435696140"""141return (y*r[1] - x*r[3])*r[2]142143def yproj(x,y,z,r):144r"""145Return the `y`-projection of `(x,y,z)` rotated by `r`.146147EXAMPLES::148149sage: from sage.groups.perm_gps.cubegroup import rotation_list, yproj150sage: rot = rotation_list(30, 45)151sage: yproj(1,2,3,rot)1521.37849741698153"""154return z*r[2] - (x*r[1] + y*r[2])*r[0]155156def rotation_list(tilt,turn):157r"""158Return a list `[\sin(\theta), \sin(\phi), \cos(\theta), \cos(\phi)]` of159rotations where `\theta` is ``tilt`` and `\phi` is ``turn``.160161EXAMPLES::162163sage: from sage.groups.perm_gps.cubegroup import rotation_list164sage: rotation_list(30, 45)165[0.5, 0.707106781187, 0.866025403784, 0.707106781187]166"""167from sage.functions.all import sin, cos168return [ sin(tilt*pi/180.0), sin(turn*pi/180.0), cos(tilt*pi/180.0), cos(turn*pi/180.0) ]169170def polygon_plot3d(points, tilt=30, turn=30, **kwargs):171r"""172Plot a polygon viewed from an angle determined by ``tilt``, ``turn``, and173vertices ``points``.174175.. WARNING::176177The ordering of the points is important to get "correct"178and if you add several of these plots together, the one added first179is also drawn first (ie, addition of Graphics objects is not180commutative).181182The following example produced a green-colored square with vertices183at the points indicated.184185EXAMPLES::186187sage: from sage.groups.perm_gps.cubegroup import polygon_plot3d,green188sage: P = polygon_plot3d([[1,3,1],[2,3,1],[2,3,2],[1,3,2],[1,3,1]],rgbcolor=green)189"""190rot = rotation_list(tilt,turn)191points2 = [(xproj(x,y,z,rot), yproj(x,y,z,rot)) for (x,y,z) in points ]192return polygon(points2, **kwargs)193194###########################################################195196############# lots of "internal" utility plot functions #########197198199def inv_list(lst):200r"""201Input a list of ints `1, \ldots, m` (in any order), outputs inverse202perm.203204EXAMPLES::205206sage: from sage.groups.perm_gps.cubegroup import inv_list207sage: L = [2,3,1]208sage: inv_list(L)209[3, 1, 2]210"""211return [lst.index(i)+1 for i in range(1,1+len(lst))]212213face_polys = {214### bottom layer L, F, R, B215'ldb': [[-3,0],[-2,0], [-2,1], [-3,1]], #square labeled 14216'ld': [[-2,0],[-1,0], [-1,1], [-2,1]], #square labeled 15217'lfd': [[-1,0],[0,0], [0,1], [-1,1]], #square labeled 16218'fdl': [[0,0],[1,0], [1,1], [0,1]], #square labeled 22219'fd': [[1,0],[2,0], [2,1], [1,1]], #square labeled 23220'frd': [[2,0],[3,0], [3,1], [2,1]], #square labeled 24221'rdf': [[3,0],[4,0], [4,1], [3,1]], #square labeled 30222'rd': [[4,0],[5,0], [5,1], [4,1]], #square labeled 31223'rbd': [[5,0],[6,0], [6,1], [5,1]], #square labeled 32224'bdr': [[6,0],[7,0], [7,1], [6,1]], #square labeled 38225'bd': [[7,0],[8,0], [8,1], [7,1]], #square labeled 39226'bld': [[8,0],[9,0], [9,1], [8,1]], #square labeled 40227### middle layer L,F,R, B228'lb': [[-3,1],[-2,1], [-2,2], [-3,2]], #square labeled 12229'l_center': [[-2,1],[-1,1], [-1,2], [-2,2]], #center square230'lf': [[-1,1],[0,1], [0,2], [-1,2]], #square labeled 13231'fl': [[0,1],[1,1], [1,2], [0,2]], #square labeled 20232'f_center': [[1,1],[2,1], [2,2], [1,2]], #center square233'fr': [[2,1],[3,1], [3,2], [2,2]], #square labeled 21234'rf': [[3,1],[4,1], [4,2], [3,2]], #square labeled 28235'r_center': [[4,1],[5,1], [5,2], [4,2]], #center square236'rb': [[5,1],[6,1], [6,2], [5,2]], #square labeled 29237'br': [[6,1],[7,1], [7,2], [6,2]], #square labeled 36238'b_center': [[7,1],[8,1], [8,2], [7,2]], #center square239'bl': [[8,1],[9,1], [9,2], [8,2]], #square labeled 37240## top layer L, F, R, B241'lbu': [[-3,2],[-2,2], [-2,3], [-3,3]], #square labeled 9242'lu': [[-2,2],[-1,2], [-1,3], [-2,3]], #square labeled 10243'luf': [[-1,2],[0,2], [0,3], [-1,3]], #square labeled 11244'flu': [[0,2],[1,2], [1,3], [0,3]], #square labeled 17245'fu': [[1,2],[2,2], [2,3], [1,3]], #square labeled 18246'fur': [[2,2],[3,2], [3,3], [2,3]], #square labeled 19247'ruf': [[3,2],[4,2], [4,3], [3,3]], #square labeled 25248'ru': [[4,2],[5,2], [5,3], [4,3]], #square labeled 26249'rub': [[5,2],[6,2], [6,3], [5,3]], #square labeled 27250'bur': [[6,2],[7,2], [7,3], [6,3]], #square labeled 33251'bu': [[7,2],[8,2], [8,3], [7,3]], #square labeled 34252'bul': [[8,2],[9,2], [9,3], [8,3]], #square labeled 35253# down face254'dlf': [[0,-1],[1,-1], [1,0], [0,0]], #square labeled 41255'df': [[1,-1],[2,-1], [2,0], [1,0]], #square labeled 42256'dfr': [[2,-1],[3,-1], [3,0], [2,0]], #square labeled 43257'dl': [[0,-2],[1,-2], [1,-1], [0,-1]], #square labeled 44258'd_center': [[1,-2],[2,-2], [2,-1], [1,-1]], #center square259'dr': [[2,-2],[3,-2], [3,-1], [2,-1]], #square labeled 45260'dlb': [[0,-3],[1,-3], [1,-2], [0,-2]], #square labeled 46261'db': [[1,-3],[2,-3], [2,-2], [1,-2]], #square labeled 47262'drb': [[2,-3],[3,-3], [3,-2], [2,-2]], #square labeled 48263# up face264'ufl': [[0,3],[1,3], [1,4], [0,4]], #square labeled 6265'uf': [[1,3],[2,3], [2,4], [1,4]], #square labeled 7266'urf': [[2,3],[3,3], [3,4], [2,4]], #square labeled 8267'ul': [[0,4],[1,4], [1,5], [0,5]], #square labeled 4268'u_center': [[1,4],[2,4], [2,5], [1,5]], #center square269'ur': [[2,4],[3,4], [3,5], [2,5]], #square labeled 5270'ulb': [[0,6],[1,6], [1,5], [0,5]], #square labeled 1271'ub': [[1,6],[2,6], [2,5], [1,5]], #square labeled 2272'ubr': [[2,6],[3,6], [3,5], [2,5]], #square labeled 3273}274275def create_poly(face, color):276"""277Create the polygon given by ``face`` with color ``color``.278279EXAMPLES::280281sage: from sage.groups.perm_gps.cubegroup import create_poly, red282sage: create_poly('ur', red)283"""284return polygon(face_polys[face], rgbcolor=color)285286####################################################287288singmaster_indices = {2891: "ulb",2902: "ub",2913: "ubr",2924: "ul",2935: "ur",2946: "ufl",2957: "uf",2968: "urf",29714: "ldb",29815: "ld",29916: "lfd",30012: "lb",30113: "lf",3029: "lbu",30310: "lu",30411: "luf",30517: "flu",30618: "fu",30719: "fur",30820: "fl",30921: "fr",31022: "fdl",31123: "fd",31224: "frd",31341: "dlf",31442: "df",31543: "dfr",31644: "dl",31745: "dr",31846: "dlb",31947: "db",32048: "drb",32133: "bur",32234: "bu",32335: "bul",32436: "br",32537: "bl",32638: "bdr",32739: "bd",32840: "bld",32925: "ruf",33026: "ru",33127: "rub",33228: "rf",33329: "rb",33430: "rdf",33531: "rd",33632: "rbd",337}338339def index2singmaster(facet):340"""341Translate index used (eg, 43) to Singmaster facet notation (eg,342fdr).343344EXAMPLES::345346sage: from sage.groups.perm_gps.cubegroup import *347sage: index2singmaster(41)348'dlf'349"""350return singmaster_indices[facet]351352def color_of_square(facet, colors=['lpurple', 'yellow', 'red', 'green', 'orange', 'blue']):353"""354Return the color the facet has in the solved state.355356EXAMPLES::357358sage: from sage.groups.perm_gps.cubegroup import *359sage: color_of_square(41)360'blue'361"""362return colors[(facet-1) // 8]363364cubie_center_list = {365# centers of the cubies on the F,U, R faces3661: [1/2,1/2,5/2], # ulb3672: [1/2,3/2,5/2], # ub3683: [1/2,5/2,5/2], # ubr3694: [3/2,1/2,5/2], # ul3705: [3/2,5/2,5/2], # ur3716: [5/2,1/2,5/2], # ufl3727: [5/2,3/2,5/2], # uf3738: [5/2,5/2,5/2], # urf37417: [5/2,1/2,5/2], # flu37518: [5/2,3/2,5/2], # fu37619: [5/2,5/2,5/2], # fur37720: [5/2,1/2,3/2], # fl37821: [5/2,5/2,3/2], # fr37922: [5/2,1/2,1/2], # fdl38023: [5/2,3/2,1/2], # fd38124: [5/2,5/2,1/2], # frd38225: [5/2,5/2,5/2], # rfu38326: [3/2,5/2,5/2], # ru38427: [1/2,5/2,5/2], # rub38528: [5/2,5/2,3/2], # rf38629: [1/2,5/2,3/2], # rb38730: [5/2,5/2,1/2], # rdf38831: [3/2,5/2,1/2], # rd38932: [1/2,5/2,1/2], # rbd390}391392def cubie_centers(label):393r"""394Return the cubie center list element given by ``label``.395396EXAMPLES::397398sage: from sage.groups.perm_gps.cubegroup import cubie_centers399sage: cubie_centers(3)400[0, 2, 2]401"""402return cubie_center_list[label]403404def cubie_colors(label,state0):405r"""406Return the color of the cubie given by ``label`` at ``state0``.407408EXAMPLES::409410sage: from sage.groups.perm_gps.cubegroup import cubie_colors411sage: G = CubeGroup()412sage: g = G.parse("R*U")413sage: cubie_colors(3, G.facets(g))414[(1, 1, 1), (1, 0.63, 1), (1, 0.6, 0.3)]415"""416# colors of the cubies on the F,U, R faces417clr_any = named_colors['white']418state = inv_list(state0)419if label == 1: return [clr_any, named_colors[color_of_square(state[1-1])], clr_any] #ulb,420if label == 2: return [clr_any,named_colors[color_of_square(state[2-1])],clr_any] # ub,421if label == 3: return [clr_any, named_colors[color_of_square(state[3-1])], named_colors[color_of_square(state[27-1])]] # ubr,422if label == 4: return [clr_any, named_colors[color_of_square(state[4-1])], clr_any] # ul,423if label == 5: return [clr_any, named_colors[color_of_square(state[5-1])], named_colors[color_of_square(state[26-1])]] # ur,424if label == 6: return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # ufl,425if label == 7: return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # uf,426if 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,427if label == 17: return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # flu428if label == 18: return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # fu429if 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])]] # fur430if label == 20: return [named_colors[color_of_square(state[20-1])], clr_any, clr_any] # fl431if label == 21: return [named_colors[color_of_square(state[21-1])], clr_any, named_colors[color_of_square(state[28-1])]] # fr432if label == 22: return [named_colors[color_of_square(state[22-1])], clr_any, clr_any] # fdl433if label == 23: return [named_colors[color_of_square(state[23-1])], clr_any, clr_any] # fd434if label == 24: return [named_colors[color_of_square(state[24-1])], clr_any, named_colors[color_of_square(state[30-1])]] # frd435if 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,436if label == 26: return [clr_any,named_colors[color_of_square(state[5-1])],named_colors[color_of_square(state[26-1])]] # ru,437if label == 27: return [clr_any,named_colors[color_of_square(state[3-1])],named_colors[color_of_square(state[27-1])]] # rub,438if label == 28: return [named_colors[color_of_square(state[21-1])],clr_any,named_colors[color_of_square(state[28-1])]] # rf,439if label == 29: return [clr_any,clr_any,named_colors[color_of_square(state[29-1])]] # rb,440if label == 30: return [named_colors[color_of_square(state[24-1])],clr_any,named_colors[color_of_square(state[30-1])]] # rdf,441if label == 31: return [clr_any,clr_any,named_colors[color_of_square(state[31-1])]] # rd,442if label == 32: return [clr_any,clr_any,named_colors[color_of_square(state[32-1])]] #rbd,443444def plot3d_cubie(cnt, clrs):445r"""446Plot the front, up and right face of a cubie centered at cnt and447rgbcolors given by clrs (in the order FUR).448449Type ``P.show()`` to view.450451EXAMPLES::452453sage: from sage.groups.perm_gps.cubegroup import *454sage: clrF = blue; clrU = red; clrR = green455sage: P = plot3d_cubie([1/2,1/2,1/2],[clrF,clrU,clrR])456"""457x = cnt[0]-1/2; y = cnt[1]-1/2; z = cnt[2]-1/2458#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]]459ptsF = [[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]]460#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]]461ptsU = [[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]]462#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]]463ptsR = [[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]]464PR = polygon_plot3d(ptsR,rgbcolor=clrs[2])465PU = polygon_plot3d(ptsU,rgbcolor=clrs[1])466PF = polygon_plot3d(ptsF,rgbcolor=clrs[0])467P = PR+PF+PU468P.axes(show=False)469return P470471472####################### end of "internal" utility plot functions #################473474475class CubeGroup(PermutationGroup_generic):476r"""477A python class to help compute Rubik's cube group actions.478479.. NOTE::480481This group is also available via ``groups.permutation.RubiksCube()``.482483EXAMPLES:484485If G denotes the cube group then it may be regarded as a486subgroup of ``SymmetricGroup(48)``, where the 48 facets are labeled as487follows.488489::490491sage: rubik = CubeGroup()492sage: rubik.display2d("")493+--------------+494| 1 2 3 |495| 4 top 5 |496| 6 7 8 |497+------------+--------------+-------------+------------+498| 9 10 11 | 17 18 19 | 25 26 27 | 33 34 35 |499| 12 left 13 | 20 front 21 | 28 right 29 | 36 rear 37 |500| 14 15 16 | 22 23 24 | 30 31 32 | 38 39 40 |501+------------+--------------+-------------+------------+502| 41 42 43 |503| 44 bottom 45 |504| 46 47 48 |505+--------------+506507::508509sage: rubik510The Rubik's cube group with generators R,L,F,B,U,D in SymmetricGroup(48).511512TESTS::513514sage: groups.permutation.RubiksCube()515The Rubik's cube group with generators R,L,F,B,U,D in SymmetricGroup(48).516"""517def __init__(self):518"""519Initialize ``self``.520521EXAMPLES::522523sage: rubik = CubeGroup()524sage: TestSuite(rubik).run(skip="_test_enumerated_set_contains") # because the group is very large525526TESTS:527528Check that :trac:`11360` is fixed::529530sage: rubik = CubeGroup()531sage: rubik.order()53243252003274489856000533"""534U = "( 1, 3, 8, 6)( 2, 5, 7, 4)( 9,33,25,17)(10,34,26,18)(11,35,27,19)" ## U = top535L = "( 9,11,16,14)(10,13,15,12)( 1,17,41,40)( 4,20,44,37)( 6,22,46,35)" ## L = left536F = "(17,19,24,22)(18,21,23,20)( 6,25,43,16)( 7,28,42,13)( 8,30,41,11)" ## F = front537R = "(25,27,32,30)(26,29,31,28)( 3,38,43,19)( 5,36,45,21)( 8,33,48,24)" ## R = right538B = "(33,35,40,38)(34,37,39,36)( 3, 9,46,32)( 2,12,47,29)( 1,14,48,27)" ## B = back or rear539D = "(41,43,48,46)(42,45,47,44)(14,22,30,38)(15,23,31,39)(16,24,32,40)" ## D = down or bottom540PermutationGroup_generic.__init__(self, gens=[B,D,F,L,R,U], canonicalize=False)541542def gen_names(self):543"""544Return the names of the generators.545546EXAMPLES::547548sage: rubik = CubeGroup()549sage: rubik.gen_names()550['B', 'D', 'F', 'L', 'R', 'U']551"""552return ['B','D','F','L','R','U']553554def __repr__(self):555"""556Return a string representation of ``self``.557558EXAMPLES::559560sage: rubik = CubeGroup()561sage: rubik562The Rubik's cube group with generators R,L,F,B,U,D in SymmetricGroup(48).563"""564return "The Rubik's cube group with generators R,L,F,B,U,D in SymmetricGroup(48)."565566def group(self):567r"""568This is deprecated in trac:`11360`. Use the :class:`CubeGroup` instead.569570EXAMPLES::571572sage: CubeGroup().group()573doctest:...: DeprecationWarning: group() is deprecated. Use the CubeGroup instead.574See http://trac.sagemath.org/11360 for details.575The Rubik's cube group with generators R,L,F,B,U,D in SymmetricGroup(48).576"""577from sage.misc.superseded import deprecation578deprecation(11360, 'group() is deprecated. Use the CubeGroup instead.')579return self580581def B(self):582"""583Return the generator `B` in Singmaster notation.584585EXAMPLES::586587sage: rubik = CubeGroup()588sage: rubik.B()589(1,14,48,27)(2,12,47,29)(3,9,46,32)(33,35,40,38)(34,37,39,36)590"""591return self.gens()[0]592593def D(self):594"""595Return the generator `D` in Singmaster notation.596597EXAMPLES::598599sage: rubik = CubeGroup()600sage: rubik.D()601(14,22,30,38)(15,23,31,39)(16,24,32,40)(41,43,48,46)(42,45,47,44)602"""603return self.gens()[1]604605def F(self):606"""607Return the generator `F` in Singmaster notation.608609EXAMPLES::610611sage: rubik = CubeGroup()612sage: rubik.F()613(6,25,43,16)(7,28,42,13)(8,30,41,11)(17,19,24,22)(18,21,23,20)614"""615return self.gens()[2]616617def L(self):618"""619Return the generator `L` in Singmaster notation.620621EXAMPLES::622623sage: rubik = CubeGroup()624sage: rubik.L()625(1,17,41,40)(4,20,44,37)(6,22,46,35)(9,11,16,14)(10,13,15,12)626"""627return self.gens()[3]628629def R(self):630"""631Return the generator `R` in Singmaster notation.632633EXAMPLES::634635sage: rubik = CubeGroup()636sage: rubik.R()637(3,38,43,19)(5,36,45,21)(8,33,48,24)(25,27,32,30)(26,29,31,28)638"""639return self.gens()[4]640641def U(self):642"""643Return the generator `U` in Singmaster notation.644645EXAMPLES::646647sage: rubik = CubeGroup()648sage: rubik.U()649(1,3,8,6)(2,5,7,4)(9,33,25,17)(10,34,26,18)(11,35,27,19)650"""651return self.gens()[5]652653def parse(self, mv, check=True):654r"""655This function allows one to create the permutation group element656from a variety of formats.657658INPUT:659660- ``mv`` -- Can one of the following:661662- ``list`` - list of facets (as returned by663self.facets())664665- ``dict`` - list of faces (as returned by666``self.faces()``)667668- ``str`` - either cycle notation (passed to GAP) or669a product of generators or Singmaster notation670671- ``perm_group element`` - returned as an element of ``self``672673- ``check`` -- check if the input is valid674675EXAMPLES::676677sage: C = CubeGroup()678sage: C.parse(range(1,49))679()680sage: g = C.parse("L"); g681(1,17,41,40)(4,20,44,37)(6,22,46,35)(9,11,16,14)(10,13,15,12)682sage: C.parse(str(g)) == g683True684sage: facets = C.facets(g); facets685[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]686sage: C.parse(facets)687(1,17,41,40)(4,20,44,37)(6,22,46,35)(9,11,16,14)(10,13,15,12)688sage: C.parse(facets) == g689True690sage: faces = C.faces("L"); faces691{'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]]}692sage: C.parse(faces) == C.parse("L")693True694sage: C.parse("L' R2") == C.parse("L^(-1)*R^2")695True696sage: C.parse("L' R2")697(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)698sage: C.parse("L^4")699()700sage: C.parse("L^(-1)*R")701(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)702"""703if isinstance(mv, PermutationGroupElement):704# mv is a perm_group element, return mv705return mv if mv.parent() is self else PermutationGroup_generic.__call__(self, mv, check)706elif isinstance(mv, str):707# It is a string: may be in cycle notation or Rubik's notation708if '(' in mv and not '^' in mv:709return PermutationGroup_generic.__call__(self, mv, check)710else:711gens = self.gens()712names = self.gen_names()713map = {}714for i in range(6):715map[names[i]] = gens[i]716g = self.identity()717mv = mv.strip().replace(" ","*").replace("**", "*").replace("'", "-1").replace('^','').replace('(','').replace(')','')718M = mv.split("*")719for m in M:720if len(m) == 0:721pass722elif len(m) == 1:723g *= map[m[0]]724else:725g *= map[m[0]]**int(m[1:])726return g727elif isinstance(mv, dict):728state = mv729state_facets = []730keyss = state.keys()731keyss.sort()732for k in keyss:733r = state[k][0]+state[k][1]+state[k][2]734r.remove(0)735state_facets = state_facets + r736state0 = self.faces("")737state0_facets = []738keyss = state0.keys()739keyss.sort()740for k in keyss:741r = state0[k][0]+state0[k][1]+state0[k][2]742r.remove(0)743state0_facets = state0_facets + r744p1 = [state0_facets.index(x) for x in range(1,49)]745p2 = [state_facets[j] for j in p1]746return PermutationGroup_generic.__call__(self, p2, check)747else:748return PermutationGroup_generic.__call__(self, mv, check)749750__call__ = parse751752def facets(self, g=None):753r"""754Return the set of facets on which the group acts. This function is755a "constant".756757EXAMPLES::758759sage: rubik = CubeGroup()760sage: rubik.facets() == range(1,49)761True762"""763fcts = range(1,49)764if g is not None:765return [g(i) for i in fcts]766else:767return fcts768769def faces(self, mv):770r"""771Return the dictionary of faces created by the effect of the move772mv, which is a string of the form `X^a*Y^b*...`, where773`X, Y, \ldots` are in `\{R,L,F,B,U,D\}` and774`a, b, \ldots` are integers. We call this ordering of the faces775the "BDFLRU, L2R, T2B ordering".776777EXAMPLES::778779sage: rubik = CubeGroup()780781Here is the dictionary of the solved state::782783sage: sorted(rubik.faces("").items())784[('back', [[33, 34, 35], [36, 0, 37], [38, 39, 40]]),785('down', [[41, 42, 43], [44, 0, 45], [46, 47, 48]]),786('front', [[17, 18, 19], [20, 0, 21], [22, 23, 24]]),787('left', [[9, 10, 11], [12, 0, 13], [14, 15, 16]]),788('right', [[25, 26, 27], [28, 0, 29], [30, 31, 32]]),789('up', [[1, 2, 3], [4, 0, 5], [6, 7, 8]])]790791Now the dictionary of the state obtained after making the move `R`792followed by `L`::793794sage: sorted(rubik.faces("R*U").items())795[('back', [[48, 26, 27], [45, 0, 37], [43, 39, 40]]),796('down', [[41, 42, 11], [44, 0, 21], [46, 47, 24]]),797('front', [[9, 10, 8], [20, 0, 7], [22, 23, 6]]),798('left', [[33, 34, 35], [12, 0, 13], [14, 15, 16]]),799('right', [[19, 29, 32], [18, 0, 31], [17, 28, 30]]),800('up', [[3, 5, 38], [2, 0, 36], [1, 4, 25]])]801"""802fcts = self.facets(self.parse(mv))803faceR = [[fcts[24],fcts[25],fcts[26]],[fcts[27],0,fcts[28]],[fcts[29],fcts[30],fcts[31]]]804faceL = [[fcts[8],fcts[9],fcts[10]],[fcts[11],0,fcts[12]],[fcts[13],fcts[14],fcts[15]]]805faceU = [[fcts[0],fcts[1],fcts[2]],[fcts[3],0,fcts[4]],[fcts[5],fcts[6],fcts[7]]]806faceD = [[fcts[40],fcts[41],fcts[42]],[fcts[43],0,fcts[44]],[fcts[45],fcts[46],fcts[47]]]807faceF = [[fcts[16],fcts[17],fcts[18]],[fcts[19],0,fcts[20]],[fcts[21],fcts[22],fcts[23]]]808faceB = [[fcts[32],fcts[33],fcts[34]],[fcts[35],0,fcts[36]],[fcts[37],fcts[38],fcts[39]]]809return {'right':faceR,'left':faceL,'up':faceU,'down':faceD,'front':faceF,'back':faceB}810811def move(self, mv):812r"""813Return the group element and the reordered list of facets, as814moved by the list ``mv`` (read left-to-right)815816INPUT:817818- ``mv`` -- A string of the form ``Xa*Yb*...``,819where ``X``, ``Y``, ... are in ``R``, ``L``, ``F``, ``B``, ``U``,820``D`` and ``a``, ``b``, ... are integers.821822EXAMPLES::823824sage: rubik = CubeGroup()825sage: rubik.move("")[0]826()827sage: rubik.move("R")[0]828(3,38,43,19)(5,36,45,21)(8,33,48,24)(25,27,32,30)(26,29,31,28)829sage: rubik.R()830(3,38,43,19)(5,36,45,21)(8,33,48,24)(25,27,32,30)(26,29,31,28)831"""832g = self.parse(mv)833return g, self.facets(g)834835def display2d(self,mv):836r"""837Print the 2d representation of ``self``.838839EXAMPLES::840841sage: rubik = CubeGroup()842sage: rubik.display2d("R")843+--------------+844| 1 2 38 |845| 4 top 36 |846| 6 7 33 |847+------------+--------------+-------------+------------+848| 9 10 11 | 17 18 3 | 27 29 32 | 48 34 35 |849| 12 left 13 | 20 front 5 | 26 right 31 | 45 rear 37 |850| 14 15 16 | 22 23 8 | 25 28 30 | 43 39 40 |851+------------+--------------+-------------+------------+852| 41 42 19 |853| 44 bottom 21 |854| 46 47 24 |855+--------------+856"""857print self.repr2d(mv)858859def repr2d(self, mv):860r"""861Displays a 2D map of the Rubik's cube after the move mv has been862made. Nothing is returned.863864EXAMPLES::865866sage: rubik = CubeGroup()867sage: print rubik.repr2d("")868+--------------+869| 1 2 3 |870| 4 top 5 |871| 6 7 8 |872+------------+--------------+-------------+------------+873| 9 10 11 | 17 18 19 | 25 26 27 | 33 34 35 |874| 12 left 13 | 20 front 21 | 28 right 29 | 36 rear 37 |875| 14 15 16 | 22 23 24 | 30 31 32 | 38 39 40 |876+------------+--------------+-------------+------------+877| 41 42 43 |878| 44 bottom 45 |879| 46 47 48 |880+--------------+881882::883884sage: print rubik.repr2d("R")885+--------------+886| 1 2 38 |887| 4 top 36 |888| 6 7 33 |889+------------+--------------+-------------+------------+890| 9 10 11 | 17 18 3 | 27 29 32 | 48 34 35 |891| 12 left 13 | 20 front 5 | 26 right 31 | 45 rear 37 |892| 14 15 16 | 22 23 8 | 25 28 30 | 43 39 40 |893+------------+--------------+-------------+------------+894| 41 42 19 |895| 44 bottom 21 |896| 46 47 24 |897+--------------+898899You can see the right face has been rotated but not the left face.900"""901g = self.parse(mv)902lst = self.facets(g)903line1 = " +--------------+\n"904line2 = " |%3d %3d %3d |\n"%(lst[0],lst[1],lst[2])905line3 = " |%3d top %3d |\n"%(lst[3],lst[4])906line4 = " |%3d %3d %3d |\n"%(lst[5],lst[6],lst[7])907line5 = "+------------+--------------+-------------+------------+\n"908line6 = "|%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])909line7 = "|%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])910line8 = "|%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])911line9 = "+------------+--------------+-------------+------------+\n"912line10 = " |%3d %3d %3d |\n"%(lst[40],lst[41],lst[42])913line11 = " |%3d bottom%3d |\n"%(lst[43],lst[44])914line12 = " |%3d %3d %3d |\n"%(lst[45],lst[46],lst[47])915line13 = " +--------------+\n"916return line1+line2+line3+line4+line5+line6+line7+line8+line9+line10+line11+line12+line13917918def plot_cube(self, mv, title=True, colors = [lpurple, yellow, red, green, orange, blue]):919r"""920Input the move mv, as a string in the Singmaster notation, and921output the 2D plot of the cube in that state.922923Type ``P.show()`` to display any of the plots below.924925EXAMPLES::926927sage: rubik = CubeGroup()928sage: P = rubik.plot_cube("R^2*U^2*R^2*U^2*R^2*U^2", title = False)929sage: # (R^2U^2)^3 permutes 2 pairs of edges (uf,ub)(fr,br)930sage: 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")931sage: # the superflip (in 20f* moves)932sage: 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")933sage: # "superflip+4 spot" (in 26q* moves)934"""935g = self.parse(mv)936state = self.facets(g)937#print state938cubies = [create_poly(index2singmaster(state[x]), color_of_square(x+1, colors)) for x in range(48)]939centers = [create_poly('%s_center' % "ulfrbd"[i], colors[i]) for i in range(6)]940clrs = sum(cubies) + sum(centers)941clrs.axes(show=False)942if title == True:943t = text('sagemath.org', (7.8,-3.5),rgbcolor=lgrey)944P = clrs+t945P.axes(show=False)946return P947return clrs948949def plot3d_cube(self,mv,title=True):950r"""951Displays `F,U,R` faces of the cube after the given move ``mv``. Mostly952included for the purpose of drawing pictures and checking moves.953954INPUT:955956- ``mv`` -- A string in the Singmaster notation957- ``title`` -- (Default: ``True``) Display the title information958959The first one below is "superflip+4 spot" (in 26q\* moves) and the960second one is the superflip (in 20f\* moves). Type show(P) to view961them.962963EXAMPLES::964965sage: rubik = CubeGroup()966sage: 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")967sage: 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")968"""969g = self.parse(mv)970state = self.facets(g)971clr_any = white972shown_labels = range(1,9)+range(17,33)973clr = [color_of_square(state[c-1]) for c in shown_labels]974cubiesR = [plot3d_cubie(cubie_centers(c),cubie_colors(c,state)) for c in [32,31,30,29,28,27,26,25]]975cubeR = sum(cubiesR)976cubiesU = [plot3d_cubie(cubie_centers(c),cubie_colors(c,state)) for c in range(1,9)]977cubeU = sum(cubiesU)978cubiesF = [plot3d_cubie(cubie_centers(c),cubie_colors(c,state)) for c in [22,23,24,20,21]]979cubeF = sum(cubiesF)980centerR = polygon_plot3d([[1,3,1],[2,3,1],[2,3,2],[1,3,2],[1,3,1]],rgbcolor=green)981centerF = polygon_plot3d([[3,1,1],[3,2,1],[3,2,2],[3,1,2],[3,1,1]],rgbcolor=red)982centerU = polygon_plot3d([[1,1,3],[1,2,3],[2,2,3],[2,1,3],[1,1,3]],rgbcolor=lpurple)983centers = centerF+centerR+centerU984P = cubeR+cubeF+cubeU+centers985P.axes(show=False)986if title == True:987t1 = text('Up, Front, and Right faces. ' , (-0.2,-2.5))988t2 = text(' sagemath.org', (0.8,-3.1),rgbcolor=lgrey)989t3 = text(" ",(3.5,0),rgbcolor=white)990P = P+t1+t2+t3991P.axes(show=False)992return P993return P994995def legal(self,state,mode="quiet"):996r"""997Return 1 (true) if the dictionary ``state`` (in the998same format as returned by the faces method) represents a legal999position (or state) of the Rubik's cube or 0 (false)1000otherwise.10011002EXAMPLES::10031004sage: rubik = CubeGroup()1005sage: r0 = rubik.faces("")1006sage: 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]]}1007sage: rubik.legal(r0)100811009sage: rubik.legal(r0,"verbose")1010(1, ())1011sage: rubik.legal(r1)101201013"""1014try:1015g = self.parse(state)1016res = 11017except TypeError:1018res = 01019g = self.identity()1020if mode != 'quiet':1021return res, g1022else:1023return res10241025def solve(self,state, algorithm='default'):1026r"""1027Solves the cube in the ``state``, given as a dictionary1028as in ``legal``. See the ``solve`` method1029of the RubiksCube class for more details.10301031This may use GAP's ``EpimorphismFromFreeGroup`` and1032``PreImagesRepresentative`` as explained below, if1033'gap' is passed in as the algorithm.10341035This algorithm103610371038#. constructs the free group on 6 generators then computes a1039reasonable set of relations which they satisfy10401041#. computes a homomorphism from the cube group to this free group1042quotient10431044#. takes the cube position, regarded as a group element, and maps1045it over to the free group quotient10461047#. using those relations and tricks from combinatorial group theory1048(stabilizer chains), solves the "word problem" for that element.10491050#. uses python string parsing to rewrite that in cube notation.105110521053The Rubik's cube group has about `4.3 \times 10^{19}`1054elements, so this process is time-consuming. See1055http://www.gap-system.org/Doc/Examples/rubik.html for an1056interesting discussion of some GAP code analyzing the Rubik's1057cube.10581059EXAMPLES::10601061sage: rubik = CubeGroup()1062sage: state = rubik.faces("R")1063sage: rubik.solve(state)1064'R'1065sage: state = rubik.faces("R*U")1066sage: rubik.solve(state, algorithm='gap') # long time1067'R*U'10681069You can also check this another (but similar) way using the1070``word_problem`` method (eg, G = rubik.group(); g =1071G("(3,38,43,19)(5,36,45,21)(8,33,48,24)(25,27,32,30)(26,29,31,28)");1072g.word_problem([b,d,f,l,r,u]), though the output will be less1073intuitive).1074"""1075from sage.groups.perm_gps.permgroup import PermutationGroup1076from sage.interfaces.all import gap1077try:1078g = self.parse(state)1079except TypeError:1080return "Illegal or syntactically incorrect state. No solution."1081if algorithm != 'gap':1082C = RubiksCube(g)1083return C.solve(algorithm)10841085hom = self._gap_().EpimorphismFromFreeGroup()1086soln = hom.PreImagesRepresentative(gap(str(g)))1087# print soln1088sol = str(soln)1089names = self.gen_names()1090for i in range(6):1091sol = sol.replace("x%s" % (i+1), names[i])1092return sol109310941095##########################################################1096# 3d object generation1097##########################################################10981099def cubie_faces():1100"""1101This provides a map from the 6 faces of the 27 cubies to the 481102facets of the larger cube.11031104-1,-1,-1 is left, top, front11051106EXAMPLES::11071108sage: from sage.groups.perm_gps.cubegroup import cubie_faces1109sage: sorted(cubie_faces().items())1110[((-1, -1, -1), [6, 17, 11, 0, 0, 0]),1111((-1, -1, 0), [4, 0, 10, 0, 0, 0]),1112((-1, -1, 1), [1, 0, 9, 0, 35, 0]),1113((-1, 0, -1), [0, 20, 13, 0, 0, 0]),1114((-1, 0, 0), [0, 0, -5, 0, 0, 0]),1115((-1, 0, 1), [0, 0, 12, 0, 37, 0]),1116((-1, 1, -1), [0, 22, 16, 41, 0, 0]),1117((-1, 1, 0), [0, 0, 15, 44, 0, 0]),1118((-1, 1, 1), [0, 0, 14, 46, 40, 0]),1119((0, -1, -1), [7, 18, 0, 0, 0, 0]),1120((0, -1, 0), [-6, 0, 0, 0, 0, 0]),1121((0, -1, 1), [2, 0, 0, 0, 34, 0]),1122((0, 0, -1), [0, -4, 0, 0, 0, 0]),1123((0, 0, 0), [0, 0, 0, 0, 0, 0]),1124((0, 0, 1), [0, 0, 0, 0, -2, 0]),1125((0, 1, -1), [0, 23, 0, 42, 0, 0]),1126((0, 1, 0), [0, 0, 0, -1, 0, 0]),1127((0, 1, 1), [0, 0, 0, 47, 39, 0]),1128((1, -1, -1), [8, 19, 0, 0, 0, 25]),1129((1, -1, 0), [5, 0, 0, 0, 0, 26]),1130((1, -1, 1), [3, 0, 0, 0, 33, 27]),1131((1, 0, -1), [0, 21, 0, 0, 0, 28]),1132((1, 0, 0), [0, 0, 0, 0, 0, -3]),1133((1, 0, 1), [0, 0, 0, 0, 36, 29]),1134((1, 1, -1), [0, 24, 0, 43, 0, 30]),1135((1, 1, 0), [0, 0, 0, 45, 0, 31]),1136((1, 1, 1), [0, 0, 0, 48, 38, 32])]1137"""1138faceR = [[25,26,27], [28,-3,29], [30,31,32]] # green1139faceL = [[ 9,10,11], [12,-5,13], [14,15,16]] # orange1140faceU = [[ 1, 2, 3], [ 4,-6, 5], [ 6, 7, 8]] # red1141faceD = [[41,42,43], [44,-1,45], [46,47,48]] # purple1142faceF = [[17,18,19], [20,-4,21], [22,23,24]] # yellow1143faceB = [[33,34,35], [36,-2,37], [38,39,40]] # blue1144cubies = {}1145for x in [-1,0,1]:1146for y in [-1,0,1]:1147for z in [-1,0,1]:1148cubies[x,y,z] = [0,0,0,0,0,0]11491150for i in [-1,0,1]:1151for j in [-1,0,1]:1152cubies[ i, j, -1][1] = faceF[1+j][1+i]1153cubies[ i, j, 1][4] = faceB[1+j][1-i]1154cubies[ i, -1, j][0] = faceU[1-j][1+i]1155cubies[ i, 1, j][3] = faceD[1+j][1+i]1156cubies[ -1, i, j][2] = faceL[1+i][1-j]1157cubies[ 1, i, j][5] = faceR[1+i][1+j]11581159return cubies11601161cubie_face_list = cubie_faces()116211631164rand_colors = [(RDF.random_element(), RDF.random_element(), RDF.random_element()) for _ in range(56)]116511661167class RubiksCube(SageObject):1168r"""1169The Rubik's cube (in a given state).11701171EXAMPLES::11721173sage: C = RubiksCube().move("R U R'")1174sage: C.show3d()11751176::11771178sage: C = RubiksCube("R*L"); C1179+--------------+1180| 17 2 38 |1181| 20 top 36 |1182| 22 7 33 |1183+------------+--------------+-------------+------------+1184| 11 13 16 | 41 18 3 | 27 29 32 | 48 34 6 |1185| 10 left 15 | 44 front 5 | 26 right 31 | 45 rear 4 |1186| 9 12 14 | 46 23 8 | 25 28 30 | 43 39 1 |1187+------------+--------------+-------------+------------+1188| 40 42 19 |1189| 37 bottom 21 |1190| 35 47 24 |1191+--------------+1192sage: C.show()1193sage: C.solve(algorithm='gap') # long time1194'L R'1195sage: C == RubiksCube("L*R")1196True1197"""1198def __init__(self, state=None, history=[], colors=[lpurple,yellow,red,green,orange,blue]):1199"""1200Initialize ``self``.12011202EXAMPLES::12031204sage: C = RubiksCube().move("R*U")1205sage: TestSuite(C).run()1206"""1207self.colors = colors1208self._history = history1209self._group = CubeGroup()1210if state is None:1211self._state = self._group.identity()1212else:1213if isinstance(state, str):1214state = self._group.faces(state)1215if not isinstance(state, PermutationGroupElement):1216legal, state = self._group.legal(state, mode="gimme_group_element")1217if not legal:1218raise ValueError("Not a legal cube.")1219self._state = state12201221def move(self, g):1222r"""1223Move the Rubik's cube by ``g``.12241225EXAMPLES::12261227sage: RubiksCube().move("R*U") == RubiksCube("R*U")1228True1229"""1230if not isinstance(g, self._group._element_class()):1231g = self._group.move(g)[0]1232return RubiksCube(self._state * g, self._history + [g], self.colors)12331234def undo(self):1235r"""1236Undo the last move of the Rubik's cube.12371238EXAMPLES::12391240sage: C = RubiksCube()1241sage: D = C.move("R*U")1242sage: D.undo() == C1243True1244"""1245if len(self._history) == 0:1246raise ValueError("no moves to undo")1247g = self._history[-1]1248return RubiksCube(self._state * ~g, self._history[:-1], self.colors)12491250def _repr_(self):1251r"""1252Return a string representation of ``self``.12531254EXAMPLES::12551256sage: RubiksCube().move("R*U")1257+--------------+1258| 3 5 38 |1259| 2 top 36 |1260| 1 4 25 |1261+------------+--------------+-------------+------------+1262| 33 34 35 | 9 10 8 | 19 29 32 | 48 26 27 |1263| 12 left 13 | 20 front 7 | 18 right 31 | 45 rear 37 |1264| 14 15 16 | 22 23 6 | 17 28 30 | 43 39 40 |1265+------------+--------------+-------------+------------+1266| 41 42 11 |1267| 44 bottom 21 |1268| 46 47 24 |1269+--------------+1270<BLANKLINE>1271"""1272return self._group.repr2d(self._state)12731274def facets(self):1275r"""1276Return the facets of ``self``.12771278EXAMPLES::12791280sage: C = RubiksCube("R*U")1281sage: C.facets()1282[3, 5, 38, 2, 36, 1, 4, 25, 33, 34, 35, 12, 13, 14, 15, 16, 9, 10,12838, 20, 7, 22, 23, 6, 19, 29, 32, 18, 31, 17, 28, 30, 48, 26, 27,128445, 37, 43, 39, 40, 41, 42, 11, 44, 21, 46, 47, 24]1285"""1286return self._group.facets(self._state)12871288def plot(self):1289r"""1290Return a plot of ``self``.12911292EXAMPLES::12931294sage: C = RubiksCube("R*U")1295sage: C.plot()1296"""1297return self._group.plot_cube(self._state)12981299def show(self):1300r"""1301Show a plot of ``self``.13021303EXAMPLES::13041305sage: C = RubiksCube("R*U")1306sage: C.show()1307"""1308self.plot().show()13091310def cubie(self, size, gap, x,y,z, colors, stickers=True):1311"""1312Return the cubie at `(x,y,z)`.13131314INPUT:13151316- ``size`` -- The size of the cubie1317- ``gap`` -- The gap between cubies1318- ``x,y,z`` -- The position of the cubie1319- ``colors`` -- The list of colors1320- ``stickers`` -- (Default ``True``) Boolean to display stickers13211322EXAMPLES::13231324sage: C = RubiksCube("R*U")1325sage: C.cubie(0.15, 0.025, 0,0,0, C.colors*3)1326"""1327sides = cubie_face_list[x,y,z]1328t = 2*size+gap1329my_colors = [colors[sides[i]+6] for i in range(6)]1330if stickers:1331B = Box(size, size, size, color=(.1, .1, .1))1332S = B + B.stickers(my_colors, size*.1, size*.01)1333return S.translate(-t*x, -t*z, -t*y)1334else:1335return ColorCube(size, [colors[sides[i]+6] for i in range(6)]).translate(-t*x, -t*z, -t*y)13361337def plot3d(self, stickers=True):1338r"""1339Return a 3D plot of ``self``.13401341EXAMPLES::13421343sage: C = RubiksCube("R*U")1344sage: C.plot3d()1345"""1346while len(self.colors) < 7:1347self.colors.append((.1, .1, .1))1348side_colors = [Texture(color=c, ambient=.75) for c in self.colors]1349start_colors = sum([[c]*8 for c in side_colors], [])1350facets = self._group.facets(self._state)1351facet_colors = [0]*481352for i in range(48):1353facet_colors[facets[i]-1] = start_colors[i]1354all_colors = side_colors + facet_colors1355pm = [-1,0,1]1356C = 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]))1357return C.rotateZ(1.5) #.scale([1,-1,1]).rotateZ(1.5)13581359def show3d(self):1360r"""1361Show a 3D plot of ``self``.13621363EXAMPLES::13641365sage: C = RubiksCube("R*U")1366sage: C.show3d()1367"""1368return self.plot3d().show()13691370def __cmp__(self, other):1371"""1372Comparison.13731374EXAMPLES::13751376sage: C = RubiksCube()1377sage: D = RubiksCube("R*U")1378sage: C < D1379True1380sage: C > D1381False1382sage: C == C1383True1384sage: C != D1385True1386"""1387c = cmp(type(self), type(other))1388if c == 0:1389return cmp(self._state, other._state)1390else:1391return c13921393def solve(self, algorithm="hybrid", timeout=15):1394r"""1395Solve the Rubik's cube.13961397INPUT:13981399- ``algorithm`` -- must be one of the following:14001401- ``hybrid`` - try ``kociemba`` for timeout seconds, then ``dietz``1402- ``kociemba`` - Use Dik T. Winter's program1403(reasonable speed, few moves)1404- ``dietz`` - Use Eric Dietz's cubex program1405(fast but lots of moves)1406- ``optimal`` - Use Michael Reid's optimal program1407(may take a long time)1408- ``gap`` - Use GAP word solution (can be slow)14091410EXAMPLES::14111412sage: C = RubiksCube("R U F L B D")1413sage: C.solve()1414'R U F L B D'14151416Dietz's program is much faster, but may give highly non-optimal1417solutions::14181419sage: s = C.solve('dietz'); s1420"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'"1421sage: C2 = RubiksCube(s)1422sage: C == C21423True1424"""1425import sage.interfaces.rubik # here to avoid circular referencing1426if algorithm == "default":1427algorithm = "hybrid"14281429if algorithm == "hybrid":1430try:1431solver = sage.interfaces.rubik.DikSolver()1432return solver.solve(self.facets(), timeout=timeout)1433except RuntimeError:1434solver = sage.interfaces.rubik.CubexSolver()1435return solver.solve(self.facets())14361437elif algorithm == "kociemba":1438solver = sage.interfaces.rubik.DikSolver()1439return solver.solve(self.facets(), timeout=timeout)14401441elif algorithm == "dietz":1442solver = sage.interfaces.rubik.CubexSolver()1443return solver.solve(self.facets())14441445elif algorithm == "optimal":1446# TODO: cache this, startup is expensive1447solver = sage.interfaces.rubik.OptimalSolver()1448return solver.solve(self.facets())14491450elif algorithm == "gap":1451solver = CubeGroup()1452return solver.solve(self._state)14531454else:1455raise ValueError, "Unrecognized algorithm: %s" % algorithm14561457def scramble(self, moves=30):1458"""1459Scramble the Rubik's cube.14601461EXAMPLES::14621463sage: C = RubiksCube()1464sage: C.scramble() # random1465+--------------+1466| 38 29 35 |1467| 20 top 42 |1468| 11 44 30 |1469+------------+--------------+-------------+------------+1470| 48 13 17 | 6 15 24 | 43 23 9 | 1 36 32 |1471| 4 left 18 | 7 front 37 | 12 right 26 | 5 rear 10 |1472| 33 31 40 | 14 28 8 | 25 47 16 | 22 2 3 |1473+------------+--------------+-------------+------------+1474| 46 21 19 |1475| 45 bottom 39 |1476| 27 34 41 |1477+--------------+1478<BLANKLINE>1479"""1480last_move = move = " "1481all = []1482for i in range(moves):1483while move[0] == last_move[0]:1484move = "RLUDBF"[random.randint(0,5)] + " '2"[random.randint(0,2)]1485last_move = move1486all.append(move)1487return self.move(' '.join(all))1488148914901491