(FORKED) Rubiks Cube solver for NxNxN cubes https://github.com/dwalton76/rubiks-cube-NxNxN-solver

__init__.py 190KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206
  1. from copy import copy
  2. from collections import OrderedDict
  3. from pprint import pformat
  4. from rubikscubennnsolver.RubiksSide import Side, SolveError, StuckInALoop, ImplementThis
  5. import itertools
  6. import json
  7. import logging
  8. import math
  9. import os
  10. import random
  11. import re
  12. import shutil
  13. import subprocess
  14. import sys
  15. log = logging.getLogger(__name__)
  16. class InvalidCubeReduction(Exception):
  17. pass
  18. def reverse_steps(steps):
  19. """
  20. Reverse the order of all steps and the direction of each individual step
  21. """
  22. results = []
  23. for step in reversed(steps):
  24. if step.endswith("'"):
  25. reverse_step = step[0:-1]
  26. else:
  27. reverse_step = step + "'"
  28. results.append(reverse_step)
  29. return results
  30. def get_cube_layout(size):
  31. """
  32. Example: size is 3, return the following string:
  33. 01 02 03
  34. 04 05 06
  35. 07 08 09
  36. 10 11 12 19 20 21 28 29 30 37 38 39
  37. 13 14 15 22 23 24 31 32 33 40 41 42
  38. 16 17 18 25 26 27 34 35 36 43 44 45
  39. 46 47 48
  40. 49 50 51
  41. 52 53 54
  42. """
  43. result = []
  44. squares = (size * size) * 6
  45. square_index = 1
  46. if squares >= 1000:
  47. digits_size = 4
  48. digits_format = "%04d "
  49. elif squares >= 100:
  50. digits_size = 3
  51. digits_format = "%03d "
  52. else:
  53. digits_size = 2
  54. digits_format = "%02d "
  55. indent = ((digits_size * size) + size + 1) * ' '
  56. rows = size * 3
  57. for row in range(1, rows + 1):
  58. line = []
  59. if row <= size:
  60. line.append(indent)
  61. for col in range(1, size + 1):
  62. line.append(digits_format % square_index)
  63. square_index += 1
  64. elif row > rows - size:
  65. line.append(indent)
  66. for col in range(1, size + 1):
  67. line.append(digits_format % square_index)
  68. square_index += 1
  69. else:
  70. init_square_index = square_index
  71. last_col = size * 4
  72. for col in range(1, last_col + 1):
  73. line.append(digits_format % square_index)
  74. if col == last_col:
  75. square_index += 1
  76. elif col % size == 0:
  77. square_index += (size * size) - size + 1
  78. line.append(' ')
  79. else:
  80. square_index += 1
  81. if row % size:
  82. square_index = init_square_index + size
  83. result.append(''.join(line))
  84. if row == size or row == rows - size:
  85. result.append('')
  86. return '\n'.join(result)
  87. def rotate_2d_list(squares_list):
  88. """
  89. http://stackoverflow.com/questions/8421337/rotating-a-two-dimensional-array-in-python
  90. """
  91. return [x for x in zip(*squares_list[::-1])]
  92. def rotate_clockwise(squares_list):
  93. return rotate_2d_list(squares_list)
  94. def rotate_counter_clockwise(squares_list):
  95. squares_list = rotate_2d_list(squares_list)
  96. squares_list = rotate_2d_list(squares_list)
  97. squares_list = rotate_2d_list(squares_list)
  98. return squares_list
  99. def compress_2d_list(squares_list):
  100. """
  101. Convert 2d list to a 1d list
  102. """
  103. return [col for row in squares_list for col in row]
  104. def find_index_for_value(list_foo, target, min_index):
  105. for (index, value) in enumerate(list_foo):
  106. if value == target and index >= min_index:
  107. return index
  108. raise SolveError("Did not find %s in list %s" % (target, pformat(list_foo)))
  109. def get_swap_count(listA, listB, debug):
  110. """
  111. How many swaps do we have to make in listB for it to match listA
  112. Example:
  113. A = [1, 2, 3, 0, 4]
  114. B = [3, 4, 1, 0, 2]
  115. would require 2 swaps
  116. """
  117. A_length = len(listA)
  118. B_length = len(listB)
  119. swaps = 0
  120. index = 0
  121. if A_length != B_length:
  122. log.info("listA %s" % ' '.join(listA))
  123. log.info("listB %s" % ' '.join(listB))
  124. assert False, "listA (len %d) and listB (len %d) must be the same length" % (A_length, B_length)
  125. if debug:
  126. log.info("INIT")
  127. log.info("listA: %s" % ' '.join(listA))
  128. log.info("listB: %s" % ' '.join(listB))
  129. log.info("")
  130. while listA != listB:
  131. if listA[index] != listB[index]:
  132. listA_value = listA[index]
  133. listB_index_with_A_value = find_index_for_value(listB, listA_value, index+1)
  134. tmp = listB[index]
  135. listB[index] = listB[listB_index_with_A_value]
  136. listB[listB_index_with_A_value] = tmp
  137. swaps += 1
  138. if debug:
  139. log.info("index %d, swaps %d" % (index, swaps))
  140. log.info("listA: %s" % ' '.join(listA))
  141. log.info("listB: %s" % ' '.join(listB))
  142. log.info("")
  143. index += 1
  144. if debug:
  145. log.info("swaps: %d" % swaps)
  146. log.info("")
  147. return swaps
  148. def apply_rotations(size, step, rotations):
  149. """
  150. Apply the "rotations" to step and return the step. This is used by
  151. compress_solution() to remove all of the whole cube rotations from
  152. the solution.
  153. """
  154. if step in ('CENTERS_SOLVED', 'EDGES_GROUPED'):
  155. return step
  156. for rotation in rotations:
  157. # remove the number at the start of the rotation...for a 4x4x4 cube
  158. # there might be a 4U rotation (to rotate about the y-axis) but we
  159. # don't need to keep the '4' part.
  160. if size <= 9:
  161. rotation = rotation[1:]
  162. elif size <= 99:
  163. rotation = rotation[2:]
  164. else:
  165. rotation = rotation[3:] # For a 100x or larger cube!!
  166. if rotation == "U" or rotation == "D'":
  167. if "U" in step:
  168. pass
  169. elif "L" in step:
  170. step = step.replace("L", "F")
  171. elif "F" in step:
  172. step = step.replace("F", "R")
  173. elif "R" in step:
  174. step = step.replace("R", "B")
  175. elif "B" in step:
  176. step = step.replace("B", "L")
  177. elif "D" in step:
  178. pass
  179. elif rotation == "U'" or rotation == "D":
  180. if "U" in step:
  181. pass
  182. elif "L" in step:
  183. step = step.replace("L", "B")
  184. elif "F" in step:
  185. step = step.replace("F", "L")
  186. elif "R" in step:
  187. step = step.replace("R", "F")
  188. elif "B" in step:
  189. step = step.replace("B", "R")
  190. elif "D" in step:
  191. pass
  192. elif rotation == "F" or rotation == "B'":
  193. if "U" in step:
  194. step = step.replace("U", "L")
  195. elif "L" in step:
  196. step = step.replace("L", "D")
  197. elif "F" in step:
  198. pass
  199. elif "R" in step:
  200. step = step.replace("R", "U")
  201. elif "B" in step:
  202. pass
  203. elif "D" in step:
  204. step = step.replace("D", "R")
  205. elif rotation == "F'" or rotation == "B":
  206. if "U" in step:
  207. step = step.replace("U", "R")
  208. elif "L" in step:
  209. step = step.replace("L", "U")
  210. elif "F" in step:
  211. pass
  212. elif "R" in step:
  213. step = step.replace("R", "D")
  214. elif "B" in step:
  215. pass
  216. elif "D" in step:
  217. step = step.replace("D", "L")
  218. elif rotation == "R" or rotation == "L'":
  219. if "U" in step:
  220. step = step.replace("U", "F")
  221. elif "L" in step:
  222. pass
  223. elif "F" in step:
  224. step = step.replace("F", "D")
  225. elif "R" in step:
  226. pass
  227. elif "B" in step:
  228. step = step.replace("B", "U")
  229. elif "D" in step:
  230. step = step.replace("D", "B")
  231. elif rotation == "R'" or rotation == "L":
  232. if "U" in step:
  233. step = step.replace("U", "B")
  234. elif "L" in step:
  235. pass
  236. elif "F" in step:
  237. step = step.replace("F", "U")
  238. elif "R" in step:
  239. pass
  240. elif "B" in step:
  241. step = step.replace("B", "D")
  242. elif "D" in step:
  243. step = step.replace("D", "F")
  244. else:
  245. raise Exception("%s is an invalid rotation" % rotation)
  246. return step
  247. def orbit_matches(edges_per_side, orbit, edge_index):
  248. if orbit is None:
  249. return True
  250. if orbit == 0:
  251. if edge_index == 0 or edge_index == edges_per_side-1:
  252. return True
  253. return False
  254. if orbit == 1:
  255. if edge_index == 1 or edge_index == edges_per_side-2:
  256. return True
  257. return False
  258. if edge_index == orbit or edge_index == (edges_per_side - 1 - orbit):
  259. return True
  260. return False
  261. def get_important_square_indexes(size):
  262. """
  263. Used for writing www pages
  264. """
  265. squares_per_side = size * size
  266. max_square = squares_per_side * 6
  267. first_squares = []
  268. last_squares = []
  269. for index in range(1, max_square + 1):
  270. if (index - 1) % squares_per_side == 0:
  271. first_squares.append(index)
  272. elif index % squares_per_side == 0:
  273. last_squares.append(index)
  274. last_UBD_squares = (last_squares[0], last_squares[4], last_squares[5])
  275. return (first_squares, last_squares, last_UBD_squares)
  276. def number_ranges(i):
  277. """
  278. https://stackoverflow.com/questions/4628333/converting-a-list-of-integers-into-range-in-python
  279. """
  280. for a, b in itertools.groupby(enumerate(i), lambda x_y: x_y[1] - x_y[0]):
  281. b = list(b)
  282. yield b[0][1], b[-1][1]
  283. class RubiksCube(object):
  284. def __init__(self, state_string, order, colormap=None, debug=False):
  285. init_state = ['dummy', ]
  286. init_state.extend(list(state_string))
  287. self.squares_per_side = int((len(init_state) - 1)/6)
  288. self.size = math.sqrt(self.squares_per_side)
  289. assert str(self.size).endswith('.0'), "Cube has %d squares per side which is not possible" % self.squares_per_side
  290. self.size = int(self.size)
  291. self.solution = []
  292. self.steps_to_rotate_cube = 0
  293. self.steps_to_solve_centers = 0
  294. self.steps_to_group_edges = 0
  295. self.steps_to_solve_3x3x3 = 0
  296. self.ida_count = 0
  297. self._phase = None
  298. self.lt_init_called = False
  299. self.orient_edges = {}
  300. self.cpu_mode = None
  301. if colormap:
  302. colormap = json.loads(colormap)
  303. self.color_map = {}
  304. self.color_map_html = {}
  305. for (side_name, color) in list(colormap.items()):
  306. side_name = str(side_name)
  307. if color == 'Wh':
  308. self.color_map[side_name] = 97
  309. self.color_map_html[side_name] = (235, 254, 250)
  310. elif color == 'Gr':
  311. self.color_map[side_name] = 92
  312. self.color_map_html[side_name] = (20, 105, 74)
  313. elif color == 'Rd':
  314. self.color_map[side_name] = 91
  315. self.color_map_html[side_name] = (104, 4, 2)
  316. elif color == 'Bu':
  317. self.color_map[side_name] = 94
  318. self.color_map_html[side_name] = (22, 57, 103)
  319. elif color == 'OR':
  320. self.color_map[side_name] = 90
  321. self.color_map_html[side_name] = (148, 53, 9)
  322. elif color == 'Ye':
  323. self.color_map[side_name] = 93
  324. self.color_map_html[side_name] = (210, 208, 2)
  325. #log.warning("color_map:\n%s\n" % pformat(self.color_map))
  326. else:
  327. # Match the colors on alg.cubing.net to make life easier
  328. self.color_map = {
  329. 'U': 97, # Wh
  330. 'L': 90, # Or
  331. 'F': 92, # Gr
  332. 'R': 91, # Rd
  333. 'B': 94, # Bu
  334. 'D': 93, # Ye
  335. }
  336. self.color_map_html = {
  337. 'U': (235, 254, 250), # Wh
  338. 'L': (148, 53, 9), # Or
  339. 'F': (20, 105, 74), # Gr
  340. 'R': (104, 4, 2), # Rd
  341. 'B': (22, 57, 103), # Bu
  342. 'D': (210, 208, 2), # Ye
  343. 'x': (0, 0, 0), # black
  344. }
  345. if debug:
  346. log.setLevel(logging.DEBUG)
  347. self.load_state(state_string, order)
  348. self.sides = OrderedDict()
  349. self.sides['U'] = Side(self, 'U')
  350. self.sides['L'] = Side(self, 'L')
  351. self.sides['F'] = Side(self, 'F')
  352. self.sides['R'] = Side(self, 'R')
  353. self.sides['B'] = Side(self, 'B')
  354. self.sides['D'] = Side(self, 'D')
  355. self.sideU = self.sides['U']
  356. self.sideL = self.sides['L']
  357. self.sideF = self.sides['F']
  358. self.sideR = self.sides['R']
  359. self.sideB = self.sides['B']
  360. self.sideD = self.sides['D']
  361. self.all_edge_positions = []
  362. # U and B
  363. for (pos1, pos2) in zip(self.sideU.edge_north_pos, reversed(self.sideB.edge_north_pos)):
  364. self.all_edge_positions.append((pos1, pos2))
  365. # U and L
  366. for (pos1, pos2) in zip(self.sideU.edge_west_pos, self.sideL.edge_north_pos):
  367. self.all_edge_positions.append((pos1, pos2))
  368. # U and F
  369. for (pos1, pos2) in zip(self.sideU.edge_south_pos, self.sideF.edge_north_pos):
  370. self.all_edge_positions.append((pos1, pos2))
  371. # U and R
  372. for (pos1, pos2) in zip(self.sideU.edge_east_pos, reversed(self.sideR.edge_north_pos)):
  373. self.all_edge_positions.append((pos1, pos2))
  374. # F and L
  375. for (pos1, pos2) in zip(self.sideF.edge_west_pos, self.sideL.edge_east_pos):
  376. self.all_edge_positions.append((pos1, pos2))
  377. # F and R
  378. for (pos1, pos2) in zip(self.sideF.edge_east_pos, self.sideR.edge_west_pos):
  379. self.all_edge_positions.append((pos1, pos2))
  380. # F and D
  381. for (pos1, pos2) in zip(self.sideF.edge_south_pos, self.sideD.edge_north_pos):
  382. self.all_edge_positions.append((pos1, pos2))
  383. # L and B
  384. for (pos1, pos2) in zip(self.sideL.edge_west_pos, self.sideB.edge_east_pos):
  385. self.all_edge_positions.append((pos1, pos2))
  386. # L and D
  387. for (pos1, pos2) in zip(self.sideL.edge_south_pos, reversed(self.sideD.edge_west_pos)):
  388. self.all_edge_positions.append((pos1, pos2))
  389. # R and D
  390. for (pos1, pos2) in zip(self.sideR.edge_south_pos, self.sideD.edge_east_pos):
  391. self.all_edge_positions.append((pos1, pos2))
  392. # R and B
  393. for (pos1, pos2) in zip(self.sideR.edge_east_pos, self.sideB.edge_west_pos):
  394. self.all_edge_positions.append((pos1, pos2))
  395. # B and D
  396. for (pos1, pos2) in zip(reversed(self.sideB.edge_south_pos), self.sideD.edge_south_pos):
  397. self.all_edge_positions.append((pos1, pos2))
  398. for side in list(self.sides.values()):
  399. side.calculate_wing_partners()
  400. def __str__(self):
  401. return "%dx%dx%d" % (self.size, self.size, self.size)
  402. def _sanity_check(self, desc, indexes, expected_count):
  403. count = {
  404. 'U' : 0,
  405. 'L' : 0,
  406. 'F' : 0,
  407. 'R' : 0,
  408. 'B' : 0,
  409. 'D' : 0,
  410. 'x' : 0,
  411. }
  412. for x in indexes:
  413. count[self.state[x]] += 1
  414. for (side, value) in count.items():
  415. if side == 'x' or value == 0:
  416. continue
  417. if value != expected_count:
  418. self.print_cube()
  419. raise InvalidCubeReduction("side %s %s count is %d (should be %d)" % (desc, side, value, expected_count))
  420. def sanity_check(self):
  421. """
  422. Implemented by the various child classes to verify that
  423. the 'state' content makes sense
  424. """
  425. pass
  426. def load_state(self, state_string, order):
  427. # kociemba_string is in URFDLB order so split this apart and re-arrange it to
  428. # be ULFRBD so that is is sequential with the normal square numbering scheme
  429. foo = []
  430. init_state = ['dummy', ]
  431. init_state.extend(list(state_string))
  432. if order == 'URFDLB':
  433. foo.extend(init_state[1:self.squares_per_side + 1]) # U
  434. foo.extend(init_state[(self.squares_per_side * 4) + 1 : (self.squares_per_side * 5) + 1]) # L
  435. foo.extend(init_state[(self.squares_per_side * 2) + 1 : (self.squares_per_side * 3) + 1]) # F
  436. foo.extend(init_state[(self.squares_per_side * 1) + 1 : (self.squares_per_side * 2) + 1]) # R
  437. foo.extend(init_state[(self.squares_per_side * 5) + 1 : (self.squares_per_side * 6) + 1]) # B
  438. foo.extend(init_state[(self.squares_per_side * 3) + 1 : (self.squares_per_side * 4) + 1]) # D
  439. elif order == 'ULFRBD':
  440. foo.extend(init_state[1:self.squares_per_side + 1]) # U
  441. foo.extend(init_state[(self.squares_per_side * 1) + 1 : (self.squares_per_side * 2) + 1]) # L
  442. foo.extend(init_state[(self.squares_per_side * 2) + 1 : (self.squares_per_side * 3) + 1]) # F
  443. foo.extend(init_state[(self.squares_per_side * 3) + 1 : (self.squares_per_side * 4) + 1]) # R
  444. foo.extend(init_state[(self.squares_per_side * 4) + 1 : (self.squares_per_side * 5) + 1]) # B
  445. foo.extend(init_state[(self.squares_per_side * 5) + 1 : (self.squares_per_side * 6) + 1]) # D
  446. else:
  447. raise Exception("Add support for order %s" % order)
  448. self.state = ['x', ]
  449. for (square_index, side_name) in enumerate(foo):
  450. self.state.append(side_name)
  451. def is_even(self):
  452. if self.size % 2 == 0:
  453. return True
  454. return False
  455. def is_odd(self):
  456. if self.size % 2 == 0:
  457. return False
  458. return True
  459. def solved(self):
  460. """
  461. Return True if the cube is solved
  462. """
  463. for side in list(self.sides.values()):
  464. if not side.solved():
  465. return False
  466. return True
  467. def rotation_map(self, action):
  468. """
  469. This returns a set of tuples
  470. that correspond to the movements
  471. of each piece on the cube
  472. for the move "action".
  473. """
  474. results = []
  475. if action[-1] in ("'", "`"):
  476. reverse = True
  477. action = action[0:-1]
  478. else:
  479. reverse = False
  480. # 2Uw, Uw and 2U all mean rotate the top 2 U rows
  481. # 3Uw and 3U mean rotate the top 3 U rows
  482. if len(action) >= 2 and action[0].isdigit() and action[1].isdigit():
  483. rows_to_rotate = int(action[0:2])
  484. action = action[2:]
  485. elif action[0].isdigit():
  486. rows_to_rotate = int(action[0])
  487. action = action[1:]
  488. else:
  489. # Uw also means rotate the top 2 U rows
  490. if 'w' in action:
  491. rows_to_rotate = 2
  492. else:
  493. rows_to_rotate = 1
  494. # We've accounted for this so remove it
  495. if 'w' in action:
  496. action = action.replace('w', '')
  497. # The digit at the end indicates how many quarter turns to do
  498. if action[-1].isdigit():
  499. quarter_turns = int(action[-1])
  500. action = action[0:-1]
  501. else:
  502. quarter_turns = 1
  503. side_name = action
  504. # Skip moves x, y, z
  505. side = self.sides[side_name]
  506. min_pos = side.min_pos
  507. max_pos = side.max_pos
  508. # Rotation of the squares of the face itself
  509. for turn in range(quarter_turns):
  510. # Number the squares of the faces
  511. # in a 2D array, then re-use all the
  512. # existing transforms
  513. oldface = []
  514. counter = min_pos
  515. for ii in range(self.size):
  516. facerow = []
  517. for jj in range(self.size):
  518. facerow.append(counter)
  519. counter += 1
  520. oldface.append(facerow)
  521. # This is not what we want, because it returns
  522. # the face colors, not the face index numbers:
  523. #oldface = side.get_face_as_2d_list()
  524. if reverse:
  525. newface = rotate_counter_clockwise(oldface)
  526. else:
  527. newface = rotate_clockwise(oldface)
  528. oldface = compress_2d_list(oldface)
  529. newface = compress_2d_list(newface)
  530. for (j,k) in zip(oldface,newface):
  531. results.append((j,k))
  532. # Again skip rotation of entire cube with x, y, z
  533. if side_name == "U":
  534. for turn in range(quarter_turns):
  535. # rotate the connecting row(s) of the surrounding sides
  536. for row in range(rows_to_rotate):
  537. left_first_square = self.squares_per_side + 1 + (row * self.size)
  538. left_last_square = left_first_square + self.size - 1
  539. front_first_square = (self.squares_per_side * 2) + 1 + (row * self.size)
  540. front_last_square = front_first_square + self.size - 1
  541. right_first_square = (self.squares_per_side * 3) + 1 + (row * self.size)
  542. right_last_square = right_first_square + self.size - 1
  543. back_first_square = (self.squares_per_side * 4) + 1 + (row * self.size)
  544. back_last_square = back_first_square + self.size - 1
  545. if reverse:
  546. for square_index in range(left_first_square, left_last_square + 1):
  547. old_index = square_index
  548. new_index = square_index + (3 * self.squares_per_side)
  549. results.append((old_index,new_index))
  550. for square_index in range(front_first_square, front_last_square + 1):
  551. old_index = square_index
  552. new_index = square_index - self.squares_per_side
  553. results.append((old_index,new_index))
  554. for square_index in range(right_first_square, right_last_square + 1):
  555. old_index = square_index
  556. new_index = square_index - self.squares_per_side
  557. results.append((old_index,new_index))
  558. for square_index in range(back_first_square, back_last_square + 1):
  559. old_index = square_index
  560. new_index = square_index - self.squares_per_side
  561. results.append((old_index,new_index))
  562. else:
  563. for square_index in range(left_first_square, left_last_square + 1):
  564. old_index = square_index
  565. new_index = square_index + self.squares_per_side
  566. results.append((old_index,new_index))
  567. for square_index in range(front_first_square, front_last_square + 1):
  568. old_index = square_index
  569. new_index = square_index + self.squares_per_side
  570. results.append((old_index,new_index))
  571. for square_index in range(right_first_square, right_last_square + 1):
  572. old_index = square_index
  573. new_index = square_index + self.squares_per_side
  574. results.append((old_index,new_index))
  575. for square_index in range(back_first_square, back_last_square + 1):
  576. old_index = square_index
  577. new_index = square_index - (3 * self.squares_per_side)
  578. results.append((old_index,new_index))
  579. elif side_name == "L":
  580. for turn in range(quarter_turns):
  581. # rotate the connecting row(s) of the surrounding sides
  582. for row in range(rows_to_rotate):
  583. top_first_square = 1 + row
  584. top_last_square = top_first_square + ((self.size - 1) * self.size)
  585. front_first_square = (self.squares_per_side * 2) + 1 + row
  586. front_last_square = front_first_square + ((self.size - 1) * self.size)
  587. down_first_square = (self.squares_per_side * 5) + 1 + row
  588. down_last_square = down_first_square + ((self.size - 1) * self.size)
  589. back_first_square = (self.squares_per_side * 4) + self.size - row
  590. back_last_square = back_first_square + ((self.size - 1) * self.size)
  591. top_squares = []
  592. for square_index in range(top_first_square, top_last_square + 1, self.size):
  593. top_squares.append(square_index)
  594. front_squares = []
  595. for square_index in range(front_first_square, front_last_square + 1, self.size):
  596. front_squares.append(square_index)
  597. down_squares = []
  598. for square_index in range(down_first_square, down_last_square + 1, self.size):
  599. down_squares.append(square_index)
  600. back_squares = []
  601. for square_index in range(back_first_square, back_last_square + 1, self.size):
  602. back_squares.append(square_index)
  603. if reverse:
  604. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)):
  605. old_index = square_index
  606. new_index = front_squares[index]
  607. results.append((old_index,new_index))
  608. for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)):
  609. old_index = square_index
  610. new_index = down_squares[index]
  611. results.append((old_index,new_index))
  612. back_squares = list(reversed(back_squares))
  613. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)):
  614. old_index = square_index
  615. new_index = back_squares[index]
  616. results.append((old_index,new_index))
  617. top_squares = list(reversed(top_squares))
  618. for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)):
  619. old_index = square_index
  620. new_index = top_squares[index]
  621. results.append((old_index,new_index))
  622. else:
  623. back_squares = list(reversed(back_squares))
  624. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)):
  625. old_index = square_index
  626. new_index = back_squares[index]
  627. results.append((old_index,new_index))
  628. for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)):
  629. old_index = square_index
  630. new_index = top_squares[index]
  631. results.append((old_index,new_index))
  632. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)):
  633. old_index = square_index
  634. new_index = front_squares[index]
  635. results.append((old_index,new_index))
  636. down_squares = list(reversed(down_squares))
  637. for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)):
  638. old_index = square_index
  639. new_index = down_squares[index]
  640. results.append((old_index,new_index))
  641. elif side_name == "F":
  642. for turn in range(quarter_turns):
  643. # rotate the connecting row(s) of the surrounding sides
  644. for row in range(rows_to_rotate):
  645. top_first_square = (self.squares_per_side - self.size) + 1 - (row * self.size)
  646. top_last_square = top_first_square + self.size - 1
  647. left_first_square = self.squares_per_side + self.size - row
  648. left_last_square = left_first_square + ((self.size - 1) * self.size)
  649. down_first_square = (self.squares_per_side * 5) + 1 + (row * self.size)
  650. down_last_square = down_first_square + self.size - 1
  651. right_first_square = (self.squares_per_side * 3) + 1 + row
  652. right_last_square = right_first_square + ((self.size - 1) * self.size)
  653. #log.info("top first %d, last %d" % (top_first_square, top_last_square))
  654. #log.info("left first %d, last %d" % (left_first_square, left_last_square))
  655. #log.info("down first %d, last %d" % (down_first_square, down_last_square))
  656. #log.info("right first %d, last %d" % (right_first_square, right_last_square))
  657. top_squares = []
  658. for square_index in range(top_first_square, top_last_square + 1):
  659. top_squares.append(square_index)
  660. left_squares = []
  661. for square_index in range(left_first_square, left_last_square + 1, self.size):
  662. left_squares.append(square_index)
  663. down_squares = []
  664. for square_index in range(down_first_square, down_last_square + 1):
  665. down_squares.append(square_index)
  666. right_squares = []
  667. for square_index in range(right_first_square, right_last_square + 1, self.size):
  668. right_squares.append(square_index)
  669. if reverse:
  670. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)):
  671. old_index = square_index
  672. new_index = right_squares[index]
  673. results.append((old_index,new_index))
  674. top_squares = list(reversed(top_squares))
  675. for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)):
  676. old_index = square_index
  677. new_index = top_squares[index]
  678. results.append((old_index,new_index))
  679. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)):
  680. old_index = square_index
  681. new_index = left_squares[index]
  682. results.append((old_index,new_index))
  683. down_squares = list(reversed(down_squares))
  684. for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)):
  685. old_index = square_index
  686. new_index = down_squares[index]
  687. results.append((old_index,new_index))
  688. else:
  689. left_squares = list(reversed(left_squares))
  690. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)):
  691. old_index = square_index
  692. new_index = left_squares[index]
  693. results.append((old_index,new_index))
  694. for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)):
  695. old_index = square_index
  696. new_index = down_squares[index]
  697. results.append((old_index,new_index))
  698. right_squares = list(reversed(right_squares))
  699. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)):
  700. old_index = square_index
  701. new_index = right_squares[index]
  702. results.append((old_index,new_index))
  703. for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)):
  704. old_index = square_index
  705. new_index = top_squares[index]
  706. results.append((old_index,new_index))
  707. elif side_name == "R":
  708. for turn in range(quarter_turns):
  709. # rotate the connecting row(s) of the surrounding sides
  710. for row in range(rows_to_rotate):
  711. top_first_square = self.size - row
  712. top_last_square = self.squares_per_side
  713. front_first_square = (self.squares_per_side * 2) + self.size - row
  714. front_last_square = front_first_square + ((self.size - 1) * self.size)
  715. down_first_square = (self.squares_per_side * 5) + self.size - row
  716. down_last_square = down_first_square + ((self.size - 1) * self.size)
  717. back_first_square = (self.squares_per_side * 4) + 1 + row
  718. back_last_square = back_first_square + ((self.size - 1) * self.size)
  719. #log.info("top first %d, last %d" % (top_first_square, top_last_square))
  720. #log.info("front first %d, last %d" % (front_first_square, front_last_square))
  721. #log.info("down first %d, last %d" % (down_first_square, down_last_square))
  722. #log.info("back first %d, last %d" % (back_first_square, back_last_square))
  723. top_squares = []
  724. for square_index in range(top_first_square, top_last_square + 1, self.size):
  725. top_squares.append(square_index)
  726. front_squares = []
  727. for square_index in range(front_first_square, front_last_square + 1, self.size):
  728. front_squares.append(square_index)
  729. down_squares = []
  730. for square_index in range(down_first_square, down_last_square + 1, self.size):
  731. down_squares.append(square_index)
  732. back_squares = []
  733. for square_index in range(back_first_square, back_last_square + 1, self.size):
  734. back_squares.append(square_index)
  735. if reverse:
  736. back_squares = list(reversed(back_squares))
  737. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)):
  738. old_index = square_index
  739. new_index = back_squares[index]
  740. results.append((old_index,new_index))
  741. for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)):
  742. old_index = square_index
  743. new_index = top_squares[index]
  744. results.append((old_index,new_index))
  745. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)):
  746. old_index = square_index
  747. new_index = front_squares[index]
  748. results.append((old_index,new_index))
  749. down_squares = list(reversed(down_squares))
  750. for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)):
  751. old_index = square_index
  752. new_index = down_squares[index]
  753. results.append((old_index,new_index))
  754. else:
  755. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)):
  756. old_index = square_index
  757. new_index = front_squares[index]
  758. results.append((old_index,new_index))
  759. for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)):
  760. old_index = square_index
  761. new_index = down_squares[index]
  762. results.append((old_index,new_index))
  763. back_squares = list(reversed(back_squares))
  764. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)):
  765. old_index = square_index
  766. new_index = back_squares[index]
  767. results.append((old_index,new_index))
  768. top_squares = list(reversed(top_squares))
  769. for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)):
  770. old_index = square_index
  771. new_index = top_squares[index]
  772. results.append((old_index,new_index))
  773. elif side_name == "B":
  774. for turn in range(quarter_turns):
  775. # rotate the connecting row(s) of the surrounding sides
  776. for row in range(rows_to_rotate):
  777. top_first_square = 1 + (row * self.size)
  778. top_last_square = top_first_square + self.size - 1
  779. left_first_square = self.squares_per_side + 1 + row
  780. left_last_square = left_first_square + ((self.size - 1) * self.size)
  781. down_first_square = (self.squares_per_side * 6) - self.size + 1 - (row * self.size)
  782. down_last_square = down_first_square + self.size - 1
  783. right_first_square = (self.squares_per_side * 3) + self.size - row
  784. right_last_square = right_first_square + ((self.size - 1) * self.size)
  785. #log.info("top first %d, last %d" % (top_first_square, top_last_square))
  786. #log.info("left first %d, last %d" % (left_first_square, left_last_square))
  787. #log.info("down first %d, last %d" % (down_first_square, down_last_square))
  788. #log.info("right first %d, last %d" % (right_first_square, right_last_square))
  789. top_squares = []
  790. for square_index in range(top_first_square, top_last_square + 1):
  791. top_squares.append(square_index)
  792. left_squares = []
  793. for square_index in range(left_first_square, left_last_square + 1, self.size):
  794. left_squares.append(square_index)
  795. down_squares = []
  796. for square_index in range(down_first_square, down_last_square + 1):
  797. down_squares.append(square_index)
  798. right_squares = []
  799. for square_index in range(right_first_square, right_last_square + 1, self.size):
  800. right_squares.append(square_index)
  801. if reverse:
  802. left_squares = list(reversed(left_squares))
  803. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)):
  804. old_index = square_index
  805. new_index = left_squares[index]
  806. results.append((old_index,new_index))
  807. for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)):
  808. old_index = square_index
  809. new_index = down_squares[index]
  810. results.append((old_index,new_index))
  811. right_squares = list(reversed(right_squares))
  812. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)):
  813. old_index = square_index
  814. new_index = right_squares[index]
  815. results.append((old_index,new_index))
  816. for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)):
  817. old_index = square_index
  818. new_index = top_squares[index]
  819. results.append((old_index,new_index))
  820. else:
  821. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)):
  822. old_index = square_index
  823. new_index = right_squares[index]
  824. results.append((old_index,new_index))
  825. top_squares = list(reversed(top_squares))
  826. for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)):
  827. old_index = square_index
  828. new_index = top_squares[index]
  829. results.append((old_index,new_index))
  830. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)):
  831. old_index = square_index
  832. new_index = left_squares[index]
  833. results.append((old_index,new_index))
  834. down_squares = list(reversed(down_squares))
  835. for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)):
  836. old_index = square_index
  837. new_index = down_squares[index]
  838. results.append((old_index,new_index))
  839. elif side_name == "D":
  840. for turn in range(quarter_turns):
  841. # rotate the connecting row(s) of the surrounding sides
  842. for row in range(rows_to_rotate):
  843. left_first_square = (self.squares_per_side * 2) - self.size + 1 - (row * self.size)
  844. left_last_square = left_first_square + self.size - 1
  845. front_first_square = (self.squares_per_side * 3) - self.size + 1 - (row * self.size)
  846. front_last_square = front_first_square + self.size - 1
  847. right_first_square = (self.squares_per_side * 4) - self.size + 1 - (row * self.size)
  848. right_last_square = right_first_square + self.size - 1
  849. back_first_square = (self.squares_per_side * 5) - self.size + 1 - (row * self.size)
  850. back_last_square = back_first_square + self.size - 1
  851. #log.info("left first %d, last %d" % (left_first_square, left_last_square))
  852. #log.info("front first %d, last %d" % (front_first_square, front_last_square))
  853. #log.info("right first %d, last %d" % (right_first_square, right_last_square))
  854. #log.info("back first %d, last %d" % (back_first_square, back_last_square))
  855. if reverse:
  856. for square_index in range(left_first_square, left_last_square + 1):
  857. old_index = square_index
  858. new_index = square_index + self.squares_per_side
  859. results.append((old_index,new_index))
  860. for square_index in range(front_first_square, front_last_square + 1):
  861. old_index = square_index
  862. new_index = square_index + self.squares_per_side
  863. results.append((old_index,new_index))
  864. for square_index in range(right_first_square, right_last_square + 1):
  865. old_index = square_index
  866. new_index = square_index + self.squares_per_side
  867. results.append((old_index,new_index))
  868. for square_index in range(back_first_square, back_last_square + 1):
  869. old_index = square_index
  870. new_index = square_index - (3 * self.squares_per_side)
  871. results.append((old_index,new_index))
  872. else:
  873. for square_index in range(left_first_square, left_last_square + 1):
  874. old_index = square_index
  875. new_index = square_index + (3 * self.squares_per_side)
  876. results.append((old_index,new_index))
  877. for square_index in range(front_first_square, front_last_square + 1):
  878. old_index = square_index
  879. new_index = square_index - self.squares_per_side
  880. results.append((old_index,new_index))
  881. for square_index in range(right_first_square, right_last_square + 1):
  882. old_index = square_index
  883. new_index = square_index - self.squares_per_side
  884. results.append((old_index,new_index))
  885. for square_index in range(back_first_square, back_last_square + 1):
  886. old_index = square_index
  887. new_index = square_index - self.squares_per_side
  888. results.append((old_index,new_index))
  889. return results
  890. def rotate(self, action):
  891. """
  892. self.state is a dictionary where the key is the square_index and the
  893. value is that square side name (U, F, etc)
  894. """
  895. self.solution.append(action)
  896. result = self.state[:]
  897. # log.info("move %s" % action)
  898. if action[-1] in ("'", "`"):
  899. reverse = True
  900. action = action[0:-1]
  901. else:
  902. reverse = False
  903. # 2Uw, Uw and 2U all mean rotate the top 2 U rows
  904. # 3Uw and 3U mean rotate the top 3 U rows
  905. if len(action) >= 2 and action[0].isdigit() and action[1].isdigit():
  906. rows_to_rotate = int(action[0:2])
  907. action = action[2:]
  908. elif action[0].isdigit():
  909. rows_to_rotate = int(action[0])
  910. action = action[1:]
  911. else:
  912. # Uw also means rotate the top 2 U rows
  913. if 'w' in action:
  914. rows_to_rotate = 2
  915. else:
  916. rows_to_rotate = 1
  917. # We've accounted for this so remove it
  918. if 'w' in action:
  919. action = action.replace('w', '')
  920. # The digit at the end indicates how many quarter turns to do
  921. if action[-1].isdigit():
  922. quarter_turns = int(action[-1])
  923. action = action[0:-1]
  924. else:
  925. quarter_turns = 1
  926. side_name = action
  927. if side_name == 'x':
  928. side_name = 'R'
  929. rows_to_rotate = self.size
  930. elif side_name == 'y':
  931. side_name = 'U'
  932. rows_to_rotate = self.size
  933. elif side_name == 'z':
  934. side_name = 'F'
  935. rows_to_rotate = self.size
  936. if side_name in ('CENTERS_SOLVED', 'EDGES_GROUPED'):
  937. return
  938. side = self.sides[side_name]
  939. min_pos = side.min_pos
  940. max_pos = side.max_pos
  941. # rotate the face...this is the same for all sides
  942. for turn in range(quarter_turns):
  943. face = side.get_face_as_2d_list()
  944. if reverse:
  945. face = rotate_counter_clockwise(face)
  946. else:
  947. face = rotate_clockwise(face)
  948. face = compress_2d_list(face)
  949. for (index, value) in enumerate(face):
  950. square_index = min_pos + index
  951. result[square_index] = value
  952. self.state = result[:]
  953. # If we are rotating the entire self.state we must rotate the opposite face as well
  954. if rows_to_rotate == self.size:
  955. if side_name == 'U':
  956. opp_side_name = 'D'
  957. elif side_name == 'D':
  958. opp_side_name = 'U'
  959. elif side_name == 'L':
  960. opp_side_name = 'R'
  961. elif side_name == 'R':
  962. opp_side_name = 'L'
  963. elif side_name == 'B':
  964. opp_side_name = 'F'
  965. elif side_name == 'F':
  966. opp_side_name = 'B'
  967. else:
  968. raise SolveError("")
  969. opp_side = self.sides[opp_side_name]
  970. opp_min_pos = opp_side.min_pos
  971. face = opp_side.get_face_as_2d_list()
  972. # This is reversed from what we did with the original layer
  973. if reverse:
  974. face = rotate_clockwise(face)
  975. else:
  976. face = rotate_counter_clockwise(face)
  977. face = compress_2d_list(face)
  978. for (index, value) in enumerate(face):
  979. square_index = opp_min_pos + index
  980. result[square_index] = value
  981. self.state = result[:]
  982. if side_name == "U":
  983. for turn in range(quarter_turns):
  984. # rotate the connecting row(s) of the surrounding sides
  985. for row in range(rows_to_rotate):
  986. left_first_square = self.squares_per_side + 1 + (row * self.size)
  987. left_last_square = left_first_square + self.size - 1
  988. front_first_square = (self.squares_per_side * 2) + 1 + (row * self.size)
  989. front_last_square = front_first_square + self.size - 1
  990. right_first_square = (self.squares_per_side * 3) + 1 + (row * self.size)
  991. right_last_square = right_first_square + self.size - 1
  992. back_first_square = (self.squares_per_side * 4) + 1 + (row * self.size)
  993. back_last_square = back_first_square + self.size - 1
  994. #log.info("left first %d, last %d" % (left_first_square, left_last_square))
  995. #log.info("front first %d, last %d" % (front_first_square, front_last_square))
  996. #log.info("right first %d, last %d" % (right_first_square, right_last_square))
  997. #log.info("back first %d, last %d" % (back_first_square, back_last_square))
  998. if reverse:
  999. for square_index in range(left_first_square, left_last_square + 1):
  1000. result[square_index] = self.state[square_index + (3 * self.squares_per_side)]
  1001. for square_index in range(front_first_square, front_last_square + 1):
  1002. result[square_index] = self.state[square_index - self.squares_per_side]
  1003. for square_index in range(right_first_square, right_last_square + 1):
  1004. result[square_index] = self.state[square_index - self.squares_per_side]
  1005. for square_index in range(back_first_square, back_last_square + 1):
  1006. result[square_index] = self.state[square_index - self.squares_per_side]
  1007. else:
  1008. for square_index in range(left_first_square, left_last_square + 1):
  1009. result[square_index] = self.state[square_index + self.squares_per_side]
  1010. for square_index in range(front_first_square, front_last_square + 1):
  1011. result[square_index] = self.state[square_index + self.squares_per_side]
  1012. for square_index in range(right_first_square, right_last_square + 1):
  1013. result[square_index] = self.state[square_index + self.squares_per_side]
  1014. for square_index in range(back_first_square, back_last_square + 1):
  1015. result[square_index] = self.state[square_index - (3 * self.squares_per_side)]
  1016. self.state = result[:]
  1017. elif side_name == "L":
  1018. for turn in range(quarter_turns):
  1019. # rotate the connecting row(s) of the surrounding sides
  1020. for row in range(rows_to_rotate):
  1021. top_first_square = 1 + row
  1022. top_last_square = top_first_square + ((self.size - 1) * self.size)
  1023. front_first_square = (self.squares_per_side * 2) + 1 + row
  1024. front_last_square = front_first_square + ((self.size - 1) * self.size)
  1025. down_first_square = (self.squares_per_side * 5) + 1 + row
  1026. down_last_square = down_first_square + ((self.size - 1) * self.size)
  1027. back_first_square = (self.squares_per_side * 4) + self.size - row
  1028. back_last_square = back_first_square + ((self.size - 1) * self.size)
  1029. #log.info("top first %d, last %d" % (top_first_square, top_last_square))
  1030. #log.info("front first %d, last %d" % (front_first_square, front_last_square))
  1031. #log.info("down first %d, last %d" % (down_first_square, down_last_square))
  1032. #log.info("back first %d, last %d" % (back_first_square, back_last_square))
  1033. top_squares = []
  1034. for square_index in range(top_first_square, top_last_square + 1, self.size):
  1035. top_squares.append(self.state[square_index])
  1036. front_squares = []
  1037. for square_index in range(front_first_square, front_last_square + 1, self.size):
  1038. front_squares.append(self.state[square_index])
  1039. down_squares = []
  1040. for square_index in range(down_first_square, down_last_square + 1, self.size):
  1041. down_squares.append(self.state[square_index])
  1042. back_squares = []
  1043. for square_index in range(back_first_square, back_last_square + 1, self.size):
  1044. back_squares.append(self.state[square_index])
  1045. if reverse:
  1046. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)):
  1047. result[square_index] = front_squares[index]
  1048. for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)):
  1049. result[square_index] = down_squares[index]
  1050. back_squares = list(reversed(back_squares))
  1051. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)):
  1052. result[square_index] = back_squares[index]
  1053. top_squares = list(reversed(top_squares))
  1054. for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)):
  1055. result[square_index] = top_squares[index]
  1056. else:
  1057. back_squares = list(reversed(back_squares))
  1058. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)):
  1059. result[square_index] = back_squares[index]
  1060. for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)):
  1061. result[square_index] = top_squares[index]
  1062. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)):
  1063. result[square_index] = front_squares[index]
  1064. down_squares = list(reversed(down_squares))
  1065. for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)):
  1066. result[square_index] = down_squares[index]
  1067. self.state = result[:]
  1068. elif side_name == "F":
  1069. for turn in range(quarter_turns):
  1070. # rotate the connecting row(s) of the surrounding sides
  1071. for row in range(rows_to_rotate):
  1072. top_first_square = (self.squares_per_side - self.size) + 1 - (row * self.size)
  1073. top_last_square = top_first_square + self.size - 1
  1074. left_first_square = self.squares_per_side + self.size - row
  1075. left_last_square = left_first_square + ((self.size - 1) * self.size)
  1076. down_first_square = (self.squares_per_side * 5) + 1 + (row * self.size)
  1077. down_last_square = down_first_square + self.size - 1
  1078. right_first_square = (self.squares_per_side * 3) + 1 + row
  1079. right_last_square = right_first_square + ((self.size - 1) * self.size)
  1080. #log.info("top first %d, last %d" % (top_first_square, top_last_square))
  1081. #log.info("left first %d, last %d" % (left_first_square, left_last_square))
  1082. #log.info("down first %d, last %d" % (down_first_square, down_last_square))
  1083. #log.info("right first %d, last %d" % (right_first_square, right_last_square))
  1084. top_squares = []
  1085. for square_index in range(top_first_square, top_last_square + 1):
  1086. top_squares.append(self.state[square_index])
  1087. left_squares = []
  1088. for square_index in range(left_first_square, left_last_square + 1, self.size):
  1089. left_squares.append(self.state[square_index])
  1090. down_squares = []
  1091. for square_index in range(down_first_square, down_last_square + 1):
  1092. down_squares.append(self.state[square_index])
  1093. right_squares = []
  1094. for square_index in range(right_first_square, right_last_square + 1, self.size):
  1095. right_squares.append(self.state[square_index])
  1096. if reverse:
  1097. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)):
  1098. result[square_index] = right_squares[index]
  1099. top_squares = list(reversed(top_squares))
  1100. for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)):
  1101. result[square_index] = top_squares[index]
  1102. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)):
  1103. result[square_index] = left_squares[index]
  1104. down_squares = list(reversed(down_squares))
  1105. for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)):
  1106. result[square_index] = down_squares[index]
  1107. else:
  1108. left_squares = list(reversed(left_squares))
  1109. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)):
  1110. result[square_index] = left_squares[index]
  1111. for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)):
  1112. result[square_index] = down_squares[index]
  1113. right_squares = list(reversed(right_squares))
  1114. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)):
  1115. result[square_index] = right_squares[index]
  1116. for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)):
  1117. result[square_index] = top_squares[index]
  1118. self.state = result[:]
  1119. elif side_name == "R":
  1120. for turn in range(quarter_turns):
  1121. # rotate the connecting row(s) of the surrounding sides
  1122. for row in range(rows_to_rotate):
  1123. top_first_square = self.size - row
  1124. top_last_square = self.squares_per_side
  1125. front_first_square = (self.squares_per_side * 2) + self.size - row
  1126. front_last_square = front_first_square + ((self.size - 1) * self.size)
  1127. down_first_square = (self.squares_per_side * 5) + self.size - row
  1128. down_last_square = down_first_square + ((self.size - 1) * self.size)
  1129. back_first_square = (self.squares_per_side * 4) + 1 + row
  1130. back_last_square = back_first_square + ((self.size - 1) * self.size)
  1131. #log.info("top first %d, last %d" % (top_first_square, top_last_square))
  1132. #log.info("front first %d, last %d" % (front_first_square, front_last_square))
  1133. #log.info("down first %d, last %d" % (down_first_square, down_last_square))
  1134. #log.info("back first %d, last %d" % (back_first_square, back_last_square))
  1135. top_squares = []
  1136. for square_index in range(top_first_square, top_last_square + 1, self.size):
  1137. top_squares.append(self.state[square_index])
  1138. front_squares = []
  1139. for square_index in range(front_first_square, front_last_square + 1, self.size):
  1140. front_squares.append(self.state[square_index])
  1141. down_squares = []
  1142. for square_index in range(down_first_square, down_last_square + 1, self.size):
  1143. down_squares.append(self.state[square_index])
  1144. back_squares = []
  1145. for square_index in range(back_first_square, back_last_square + 1, self.size):
  1146. back_squares.append(self.state[square_index])
  1147. if reverse:
  1148. back_squares = list(reversed(back_squares))
  1149. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)):
  1150. result[square_index] = back_squares[index]
  1151. for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)):
  1152. result[square_index] = top_squares[index]
  1153. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)):
  1154. result[square_index] = front_squares[index]
  1155. down_squares = list(reversed(down_squares))
  1156. for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)):
  1157. result[square_index] = down_squares[index]
  1158. else:
  1159. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)):
  1160. result[square_index] = front_squares[index]
  1161. for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)):
  1162. result[square_index] = down_squares[index]
  1163. back_squares = list(reversed(back_squares))
  1164. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)):
  1165. result[square_index] = back_squares[index]
  1166. top_squares = list(reversed(top_squares))
  1167. for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)):
  1168. result[square_index] = top_squares[index]
  1169. self.state = result[:]
  1170. elif side_name == "B":
  1171. for turn in range(quarter_turns):
  1172. # rotate the connecting row(s) of the surrounding sides
  1173. for row in range(rows_to_rotate):
  1174. top_first_square = 1 + (row * self.size)
  1175. top_last_square = top_first_square + self.size - 1
  1176. left_first_square = self.squares_per_side + 1 + row
  1177. left_last_square = left_first_square + ((self.size - 1) * self.size)
  1178. down_first_square = (self.squares_per_side * 6) - self.size + 1 - (row * self.size)
  1179. down_last_square = down_first_square + self.size - 1
  1180. right_first_square = (self.squares_per_side * 3) + self.size - row
  1181. right_last_square = right_first_square + ((self.size - 1) * self.size)
  1182. #log.info("top first %d, last %d" % (top_first_square, top_last_square))
  1183. #log.info("left first %d, last %d" % (left_first_square, left_last_square))
  1184. #log.info("down first %d, last %d" % (down_first_square, down_last_square))
  1185. #log.info("right first %d, last %d" % (right_first_square, right_last_square))
  1186. top_squares = []
  1187. for square_index in range(top_first_square, top_last_square + 1):
  1188. top_squares.append(self.state[square_index])
  1189. left_squares = []
  1190. for square_index in range(left_first_square, left_last_square + 1, self.size):
  1191. left_squares.append(self.state[square_index])
  1192. down_squares = []
  1193. for square_index in range(down_first_square, down_last_square + 1):
  1194. down_squares.append(self.state[square_index])
  1195. right_squares = []
  1196. for square_index in range(right_first_square, right_last_square + 1, self.size):
  1197. right_squares.append(self.state[square_index])
  1198. if reverse:
  1199. left_squares = list(reversed(left_squares))
  1200. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)):
  1201. result[square_index] = left_squares[index]
  1202. for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)):
  1203. result[square_index] = down_squares[index]
  1204. right_squares = list(reversed(right_squares))
  1205. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)):
  1206. result[square_index] = right_squares[index]
  1207. for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)):
  1208. result[square_index] = top_squares[index]
  1209. else:
  1210. for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)):
  1211. result[square_index] = right_squares[index]
  1212. top_squares = list(reversed(top_squares))
  1213. for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)):
  1214. result[square_index] = top_squares[index]
  1215. for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)):
  1216. result[square_index] = left_squares[index]
  1217. down_squares = list(reversed(down_squares))
  1218. for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)):
  1219. result[square_index] = down_squares[index]
  1220. self.state = result[:]
  1221. elif side_name == "D":
  1222. for turn in range(quarter_turns):
  1223. # rotate the connecting row(s) of the surrounding sides
  1224. for row in range(rows_to_rotate):
  1225. left_first_square = (self.squares_per_side * 2) - self.size + 1 - (row * self.size)
  1226. left_last_square = left_first_square + self.size - 1
  1227. front_first_square = (self.squares_per_side * 3) - self.size + 1 - (row * self.size)
  1228. front_last_square = front_first_square + self.size - 1
  1229. right_first_square = (self.squares_per_side * 4) - self.size + 1 - (row * self.size)
  1230. right_last_square = right_first_square + self.size - 1
  1231. back_first_square = (self.squares_per_side * 5) - self.size + 1 - (row * self.size)
  1232. back_last_square = back_first_square + self.size - 1
  1233. #log.info("left first %d, last %d" % (left_first_square, left_last_square))
  1234. #log.info("front first %d, last %d" % (front_first_square, front_last_square))
  1235. #log.info("right first %d, last %d" % (right_first_square, right_last_square))
  1236. #log.info("back first %d, last %d" % (back_first_square, back_last_square))
  1237. if reverse:
  1238. for square_index in range(left_first_square, left_last_square + 1):
  1239. result[square_index] = self.state[square_index + self.squares_per_side]
  1240. for square_index in range(front_first_square, front_last_square + 1):
  1241. result[square_index] = self.state[square_index + self.squares_per_side]
  1242. for square_index in range(right_first_square, right_last_square + 1):
  1243. result[square_index] = self.state[square_index + self.squares_per_side]
  1244. for square_index in range(back_first_square, back_last_square + 1):
  1245. result[square_index] = self.state[square_index - (3 * self.squares_per_side)]
  1246. else:
  1247. for square_index in range(left_first_square, left_last_square + 1):
  1248. result[square_index] = self.state[square_index + (3 * self.squares_per_side)]
  1249. for square_index in range(front_first_square, front_last_square + 1):
  1250. result[square_index] = self.state[square_index - self.squares_per_side]
  1251. for square_index in range(right_first_square, right_last_square + 1):
  1252. result[square_index] = self.state[square_index - self.squares_per_side]
  1253. for square_index in range(back_first_square, back_last_square + 1):
  1254. result[square_index] = self.state[square_index - self.squares_per_side]
  1255. self.state = result[:]
  1256. else:
  1257. raise Exception("Unsupported action %s" % action)
  1258. def print_cube_layout(self):
  1259. print((get_cube_layout(self.size) + '\n'))
  1260. def print_cube(self, print_positions=False):
  1261. side_names = ('U', 'L', 'F', 'R', 'B', 'D')
  1262. side_name_index = 0
  1263. rows = []
  1264. row_index = 0
  1265. for x in range(self.size * 3):
  1266. rows.append([])
  1267. all_digits = True
  1268. for (square_index, square_state) in enumerate(self.state):
  1269. if not square_state.isdigit():
  1270. all_digits = False
  1271. break
  1272. for (square_index, square_state) in enumerate(self.state):
  1273. # ignore the placeholder (x)
  1274. if square_index == 0:
  1275. continue
  1276. side_name = side_names[side_name_index]
  1277. color = self.color_map.get(square_state, None)
  1278. if color:
  1279. # end of the row
  1280. if square_index % self.size == 0:
  1281. rows[row_index].append("\033[%dm%s\033[0m%s " % (color, square_state, " (%4d) " % square_index if print_positions else ""))
  1282. row_index += 1
  1283. else:
  1284. rows[row_index].append("\033[%dm%s\033[0m%s" % (color, square_state, " (%4d) " % square_index if print_positions else ""))
  1285. else:
  1286. # end of the row
  1287. if square_index % self.size == 0:
  1288. if square_state.endswith('x'):
  1289. rows[row_index].append("%s " % square_state)
  1290. else:
  1291. if all_digits:
  1292. rows[row_index].append("%02d" % int(square_state))
  1293. else:
  1294. rows[row_index].append("%s" % square_state)
  1295. row_index += 1
  1296. else:
  1297. if square_state.endswith('x'):
  1298. rows[row_index].append("%s" % square_state)
  1299. else:
  1300. if all_digits:
  1301. rows[row_index].append("%02d" % int(square_state))
  1302. else:
  1303. rows[row_index].append("%s" % square_state)
  1304. # end of the side
  1305. if square_index % self.squares_per_side == 0:
  1306. if side_name in ('L', 'F', 'R'):
  1307. row_index = self.size
  1308. side_name_index += 1
  1309. for (row_index, row) in enumerate(rows):
  1310. if row_index < self.size or row_index >= (self.size * 2):
  1311. if all_digits:
  1312. sys.stdout.write(' ' * (self.size * 3))
  1313. else:
  1314. sys.stdout.write(' ' * (self.size + self.size + 1))
  1315. print((' '.join(row)))
  1316. if ((row_index+1) % self.size) == 0:
  1317. print('')
  1318. print('')
  1319. def print_case_statement_C(self, case, first_step):
  1320. """
  1321. This is called via --rotate-printer, it is used to print the
  1322. case statements used by lookup-table-builder.c
  1323. """
  1324. if first_step:
  1325. print((" if (strcmp(step, \"%s\") == 0) {" % case))
  1326. else:
  1327. print((" } else if (strcmp(step, \"%s\") == 0) {" % case))
  1328. for (key, value) in enumerate(self.state[1:]):
  1329. key += 1
  1330. if str(key) != str(value):
  1331. print((" cube[%s] = cube_tmp[%s];" % (key, value)))
  1332. print("")
  1333. def print_case_statement_python(self, case):
  1334. """
  1335. This is called via utils/rotate-printer.py, it is used to print the
  1336. contents of rotate_xxx.py
  1337. """
  1338. numbers = []
  1339. numbers.append(0)
  1340. for (key, value) in enumerate(self.state[1:]):
  1341. numbers.append(int(value))
  1342. '''
  1343. If you feed number_ranges()
  1344. [0, 1, 2, 3, 4, 7, 8, 9, 11]
  1345. It will return:
  1346. [(0, 4), (7, 9), (11, 11)]
  1347. '''
  1348. lists = []
  1349. indexes_outside_streak = []
  1350. for (start_index, last_index) in number_ranges(numbers):
  1351. if start_index == last_index:
  1352. indexes_outside_streak.append("cube[%d]" % start_index)
  1353. else:
  1354. if indexes_outside_streak:
  1355. # cube[11], cube[13]
  1356. lists.append("[%s]" % ','.join(indexes_outside_streak))
  1357. indexes_outside_streak = []
  1358. lists.append("cube[%d:%d]" % (start_index, last_index+1))
  1359. if indexes_outside_streak:
  1360. lists.append("[%s]" % ','.join(indexes_outside_streak))
  1361. indexes_outside_streak = []
  1362. print((" return %s" % ' + '.join(lists)))
  1363. def randomize(self):
  1364. """
  1365. Perform a bunch of random moves to scramble a cube. This was used to generate test cases.
  1366. """
  1367. if self.is_even():
  1368. max_rows = int(self.size/2)
  1369. else:
  1370. max_rows = int((self.size - 1)/2)
  1371. sides = ['U', 'L', 'F', 'R', 'B', 'D']
  1372. count = ((self.size * self.size) * 6) * 3
  1373. # uncomment to limit randomness of the scramble
  1374. # count = 12
  1375. for x in range(count):
  1376. rows = random.randint(1, max_rows)
  1377. side_index = random.randint(0, 5)
  1378. side = sides[side_index]
  1379. quarter_turns = random.randint(1, 2)
  1380. clockwise = random.randint(0, 1)
  1381. if rows > 1:
  1382. move = "%d%s" % (rows, side)
  1383. else:
  1384. move = side
  1385. if quarter_turns > 1:
  1386. move += str(quarter_turns)
  1387. if not clockwise:
  1388. move += "'"
  1389. self.rotate(move)
  1390. def get_side_for_index(self, square_index):
  1391. """
  1392. Return the Side object that owns square_index
  1393. """
  1394. for side in list(self.sides.values()):
  1395. if square_index >= side.min_pos and square_index <= side.max_pos:
  1396. return side
  1397. raise SolveError("We should not be here, square_index %s" % pformat(square_index))
  1398. def get_edge_colors(self, square_index):
  1399. side = self.get_side_for_index(square_index)
  1400. edge_indexes = None
  1401. if square_index in side.edge_north_pos:
  1402. edge_indexes = side.edge_north_pos
  1403. elif square_index in side.edge_west_pos:
  1404. edge_indexes = side.edge_west_pos
  1405. elif square_index in side.edge_south_pos:
  1406. edge_indexes = side.edge_south_pos
  1407. elif square_index in side.edge_east_pos:
  1408. edge_indexes = side.edge_east_pos
  1409. colors = []
  1410. for edge_index in edge_indexes:
  1411. partner_index = side.get_wing_partner(edge_index)
  1412. colors.append(tuple(sorted((self.state[edge_index], self.state[partner_index]))))
  1413. colors = sorted(list(set(colors)))
  1414. #log.info("%s colors %s" % (square_index, pformat(colors)))
  1415. return colors
  1416. def get_non_paired_wings(self):
  1417. return (self.sideU.non_paired_wings(True, True, True, True) +
  1418. self.sideF.non_paired_wings(False, True, False, True) +
  1419. self.sideB.non_paired_wings(False, True, False, True) +
  1420. self.sideD.non_paired_wings(True, True, True, True))
  1421. def get_non_paired_wings_count(self):
  1422. return len(self.get_non_paired_wings())
  1423. def get_non_paired_edges(self):
  1424. # north, west, south, east
  1425. return (self.sideU.non_paired_edges(True, True, True, True) +
  1426. self.sideF.non_paired_edges(False, True, False, True) +
  1427. self.sideB.non_paired_edges(False, True, False, True) +
  1428. self.sideD.non_paired_edges(True, True, True, True))
  1429. def get_non_paired_edges_count(self):
  1430. non_paired_edges = self.get_non_paired_edges()
  1431. result = len(non_paired_edges)
  1432. if result > 12:
  1433. raise SolveError("Found %d unpaired edges but a cube only has 12 edges" % result)
  1434. return result
  1435. def edges_paired(self):
  1436. if self.get_non_paired_edges_count() == 0:
  1437. return True
  1438. return False
  1439. def find_edge(self, color1, color2):
  1440. positions = []
  1441. for (pos1, pos2) in self.all_edge_positions:
  1442. if ((self.state[pos1] == color1 and self.state[pos2] == color2) or
  1443. (self.state[pos1] == color2 and self.state[pos2] == color1)):
  1444. positions.append((pos1, pos2))
  1445. return positions
  1446. def get_wings(self, pos1, remove_if_in_same_edge=False):
  1447. pos1_side = self.get_side_for_index(pos1)
  1448. pos2 = pos1_side.get_wing_partner(pos1)
  1449. pos2_side = self.get_side_for_index(pos2)
  1450. color1 = self.state[pos1]
  1451. color2 = self.state[pos2]
  1452. wings = self.find_edge(color1, color2)
  1453. wings_to_remove = []
  1454. #log.info("get_wings (%d, %d), pos1_side %s, remove_if_in_same_edge %s, %s" %
  1455. # (pos1, pos2, pos1_side, remove_if_in_same_edge, pformat(wings)))
  1456. for (wing_pos1, wing_pos2) in wings:
  1457. # Remove the one we started with
  1458. if (wing_pos1, wing_pos2) == (pos1, pos2):
  1459. wings_to_remove.append((wing_pos1, wing_pos2))
  1460. elif (wing_pos1, wing_pos2) == (pos2, pos1):
  1461. wings_to_remove.append((wing_pos1, wing_pos2))
  1462. # Some callers do not want wings that are part of the same edge as pos1
  1463. elif remove_if_in_same_edge:
  1464. wing_pos1_side = self.get_side_for_index(wing_pos1)
  1465. wing_pos2_side = self.get_side_for_index(wing_pos2)
  1466. #log.info("wing_pos1 %s, wing_pos1_side %s, wing_pos2 %s, wing_pos2_side %s" %
  1467. # (wing_pos1, wing_pos1_side, wing_pos2, wing_pos2_side))
  1468. if ((wing_pos1_side == pos1_side and wing_pos2_side == pos2_side) or
  1469. (wing_pos2_side == pos1_side and wing_pos1_side == pos2_side)):
  1470. wings_to_remove.append((wing_pos1, wing_pos2))
  1471. #log.info("get_wings wings_to_remove %s" % pformat(wings_to_remove))
  1472. for x in wings_to_remove:
  1473. wings.remove(x)
  1474. #log.info("get_wings returning %s\n" % pformat(wings))
  1475. return wings
  1476. def get_wing_in_middle_of_edge(self, pos1, remove_if_in_same_edge=False):
  1477. wings = self.get_wings(pos1, remove_if_in_same_edge)
  1478. for wing in wings:
  1479. wing_side = self.get_side_for_index(wing[0])
  1480. if wing_side.wing_is_middle_of_edge(wing[0]):
  1481. return wing
  1482. return None
  1483. def get_wings_on_edge(self, pos1, side1_name, side2_name):
  1484. wings = self.get_wings(pos1)
  1485. wings_to_keep = []
  1486. #log.info("get_wings_on_edge for pos1 %d, side1 %s, side2 %s, init_wings %s" % (pos1, side1_name, side2_name, pformat(wings)))
  1487. for (wing_pos1, wing_pos2) in wings:
  1488. wing_pos1_side = self.get_side_for_index(wing_pos1)
  1489. wing_pos2_side = self.get_side_for_index(wing_pos2)
  1490. #log.info("get_wings_on_edge wing_pos1 %d side %s, wing_pos2 %d side %s\n" %
  1491. # (wing_pos1, wing_pos1_side, wing_pos2, wing_pos2_side))
  1492. if ((wing_pos1_side.name == side1_name and wing_pos2_side.name == side2_name) or
  1493. (wing_pos2_side.name == side1_name and wing_pos1_side.name == side2_name)):
  1494. wings_to_keep.append((wing_pos1, wing_pos2))
  1495. #log.info("get_wings_on_edge keeping %s\n" % pformat(wings_to_keep))
  1496. return wings_to_keep
  1497. def rotate_edge_to_F_west(self, edge):
  1498. side = self.get_side_for_index(edge[0])
  1499. direction = side.has_wing(edge)
  1500. if side == self.sideU:
  1501. if direction == 'north':
  1502. self.rotate_y_reverse()
  1503. self.rotate_x_reverse()
  1504. elif direction == 'west':
  1505. self.rotate_x_reverse()
  1506. elif direction == 'south':
  1507. self.rotate_x_reverse()
  1508. self.rotate_z()
  1509. elif direction == 'east':
  1510. self.rotate_y()
  1511. self.rotate_y()
  1512. self.rotate_x_reverse()
  1513. elif side == self.sideL:
  1514. if direction == 'north':
  1515. self.rotate_x_reverse()
  1516. elif direction == 'west':
  1517. self.rotate_x_reverse()
  1518. self.rotate_x_reverse()
  1519. elif direction == 'south':
  1520. self.rotate_x()
  1521. elif direction == 'east':
  1522. pass
  1523. elif side == self.sideF:
  1524. if direction == 'north':
  1525. self.rotate_z_reverse()
  1526. elif direction == 'west':
  1527. pass
  1528. elif direction == 'south':
  1529. self.rotate_z()
  1530. elif direction == 'east':
  1531. self.rotate_z_reverse()
  1532. self.rotate_z_reverse()
  1533. elif side == self.sideR:
  1534. if direction == 'north':
  1535. self.rotate_y()
  1536. self.rotate_y()
  1537. self.rotate_x_reverse()
  1538. elif direction == 'west':
  1539. self.rotate_y()
  1540. elif direction == 'south':
  1541. self.rotate_x()
  1542. self.rotate_y()
  1543. elif direction == 'east':
  1544. self.rotate_y()
  1545. self.rotate_y()
  1546. elif side == self.sideB:
  1547. if direction == 'north':
  1548. self.rotate_y_reverse()
  1549. self.rotate_x_reverse()
  1550. elif direction == 'west':
  1551. self.rotate_y()
  1552. self.rotate_y()
  1553. elif direction == 'south':
  1554. self.rotate_z()
  1555. self.rotate_x()
  1556. self.rotate_x()
  1557. elif direction == 'east':
  1558. self.rotate_y_reverse()
  1559. elif side == self.sideD:
  1560. if direction == 'north':
  1561. self.rotate_z()
  1562. elif direction == 'west':
  1563. self.rotate_x()
  1564. elif direction == 'south':
  1565. self.rotate_y_reverse()
  1566. self.rotate_x()
  1567. elif direction == 'east':
  1568. self.rotate_y()
  1569. self.rotate_y()
  1570. self.rotate_x()
  1571. def move_wing_to_U_north(self, wing):
  1572. if isinstance(wing, tuple) or isinstance(wing, list):
  1573. wing_pos1 = wing[0]
  1574. else:
  1575. wing_pos1 = wing
  1576. # Upper
  1577. if wing_pos1 in self.sideU.edge_north_pos:
  1578. pass
  1579. elif wing_pos1 in self.sideU.edge_south_pos:
  1580. for step in ("U2", ):
  1581. self.rotate(step)
  1582. elif wing_pos1 in self.sideU.edge_east_pos:
  1583. for step in ("U'", ):
  1584. self.rotate(step)
  1585. elif wing_pos1 in self.sideU.edge_west_pos:
  1586. for step in ("U", ):
  1587. self.rotate(step)
  1588. # Left
  1589. elif wing_pos1 in self.sideL.edge_north_pos:
  1590. for step in ("U", ):
  1591. self.rotate(step)
  1592. elif wing_pos1 in self.sideL.edge_south_pos:
  1593. for step in ("L", "B'"):
  1594. self.rotate(step)
  1595. elif wing_pos1 in self.sideL.edge_east_pos:
  1596. for step in ("L2", "B'"):
  1597. self.rotate(step)
  1598. elif wing_pos1 in self.sideL.edge_west_pos:
  1599. for step in ("B'", ):
  1600. self.rotate(step)
  1601. # Front
  1602. elif wing_pos1 in self.sideF.edge_north_pos:
  1603. for step in ("U2", ):
  1604. self.rotate(step)
  1605. elif wing_pos1 in self.sideF.edge_south_pos:
  1606. for step in ("F2", "U2"):
  1607. self.rotate(step)
  1608. elif wing_pos1 in self.sideF.edge_east_pos:
  1609. for step in ("R", "U'"):
  1610. self.rotate(step)
  1611. elif wing_pos1 in self.sideF.edge_west_pos:
  1612. for step in ("F", "U2"):
  1613. self.rotate(step)
  1614. # Right
  1615. elif wing_pos1 in self.sideR.edge_north_pos:
  1616. for step in ("U'", ):
  1617. self.rotate(step)
  1618. elif wing_pos1 in self.sideR.edge_south_pos:
  1619. for step in ("R2", "U'"):
  1620. self.rotate(step)
  1621. elif wing_pos1 in self.sideR.edge_east_pos:
  1622. for step in ("B", ):
  1623. self.rotate(step)
  1624. elif wing_pos1 in self.sideR.edge_west_pos:
  1625. for step in ("R", "U'"):
  1626. self.rotate(step)
  1627. # Back
  1628. elif wing_pos1 in self.sideB.edge_north_pos:
  1629. pass
  1630. elif wing_pos1 in self.sideB.edge_south_pos:
  1631. for step in ("B2", ):
  1632. self.rotate(step)
  1633. elif wing_pos1 in self.sideB.edge_east_pos:
  1634. for step in ("B'", ):
  1635. self.rotate(step)
  1636. elif wing_pos1 in self.sideB.edge_west_pos:
  1637. for step in ("B", ):
  1638. self.rotate(step)
  1639. # Down
  1640. elif wing_pos1 in self.sideD.edge_north_pos:
  1641. for step in ("F2", "U2"):
  1642. self.rotate(step)
  1643. elif wing_pos1 in self.sideD.edge_south_pos:
  1644. for step in ("B2", ):
  1645. self.rotate(step)
  1646. elif wing_pos1 in self.sideD.edge_east_pos:
  1647. for step in ("R2", "U'"):
  1648. self.rotate(step)
  1649. elif wing_pos1 in self.sideD.edge_west_pos:
  1650. for step in ("L2", "U"):
  1651. self.rotate(step)
  1652. else:
  1653. raise ImplementThis("implement wing %s to U north" % str(wing))
  1654. def move_wing_to_U_west(self, wing):
  1655. if isinstance(wing, tuple) or isinstance(wing, list):
  1656. wing_pos1 = wing[0]
  1657. else:
  1658. wing_pos1 = wing
  1659. # Upper
  1660. if wing_pos1 in self.sideU.edge_north_pos:
  1661. for step in ("U'", ):
  1662. self.rotate(step)
  1663. elif wing_pos1 in self.sideU.edge_south_pos:
  1664. for step in ("U", ):
  1665. self.rotate(step)
  1666. elif wing_pos1 in self.sideU.edge_east_pos:
  1667. for step in ("U2", ):
  1668. self.rotate(step)
  1669. elif wing_pos1 in self.sideU.edge_west_pos:
  1670. pass
  1671. # Left
  1672. elif wing_pos1 in self.sideL.edge_north_pos:
  1673. pass
  1674. elif wing_pos1 in self.sideL.edge_south_pos:
  1675. for step in ("L2", ):
  1676. self.rotate(step)
  1677. elif wing_pos1 in self.sideL.edge_east_pos:
  1678. for step in ("L'", ):
  1679. self.rotate(step)
  1680. elif wing_pos1 in self.sideL.edge_west_pos:
  1681. for step in ("L", ):
  1682. self.rotate(step)
  1683. # Front
  1684. elif wing_pos1 in self.sideF.edge_north_pos:
  1685. for step in ("U", ):
  1686. self.rotate(step)
  1687. elif wing_pos1 in self.sideF.edge_south_pos:
  1688. for step in ("F2", "U"):
  1689. self.rotate(step)
  1690. elif wing_pos1 in self.sideF.edge_east_pos:
  1691. for step in ("F'", "U"):
  1692. self.rotate(step)
  1693. elif wing_pos1 in self.sideF.edge_west_pos:
  1694. for step in ("L'", ):
  1695. self.rotate(step)
  1696. # Right
  1697. elif wing_pos1 in self.sideR.edge_north_pos:
  1698. for step in ("U2", ):
  1699. self.rotate(step)
  1700. elif wing_pos1 in self.sideR.edge_south_pos:
  1701. for step in ("R2", "U2"):
  1702. self.rotate(step)
  1703. elif wing_pos1 in self.sideR.edge_east_pos:
  1704. for step in ("B", "U'"):
  1705. self.rotate(step)
  1706. elif wing_pos1 in self.sideR.edge_west_pos:
  1707. for step in ("R", "U2"):
  1708. self.rotate(step)
  1709. # Back
  1710. elif wing_pos1 in self.sideB.edge_north_pos:
  1711. for step in ("U'", ):
  1712. self.rotate(step)
  1713. elif wing_pos1 in self.sideB.edge_south_pos:
  1714. for step in ("B2", "U'"):
  1715. self.rotate(step)
  1716. elif wing_pos1 in self.sideB.edge_east_pos:
  1717. for step in ("L", ):
  1718. self.rotate(step)
  1719. elif wing_pos1 in self.sideB.edge_west_pos:
  1720. for step in ("B", "U'"):
  1721. self.rotate(step)
  1722. # Down
  1723. elif wing_pos1 in self.sideD.edge_north_pos:
  1724. for step in ("D'", "L2"):
  1725. self.rotate(step)
  1726. elif wing_pos1 in self.sideD.edge_south_pos:
  1727. for step in ("D", "L2"):
  1728. self.rotate(step)
  1729. elif wing_pos1 in self.sideD.edge_east_pos:
  1730. for step in ("D2", "L2"):
  1731. self.rotate(step)
  1732. elif wing_pos1 in self.sideD.edge_west_pos:
  1733. for step in ("L2", ):
  1734. self.rotate(step)
  1735. else:
  1736. raise ImplementThis("implement wing %s to U west" % str(wing))
  1737. def move_wing_to_U_south(self, wing):
  1738. if isinstance(wing, tuple) or isinstance(wing, list):
  1739. wing_pos1 = wing[0]
  1740. else:
  1741. wing_pos1 = wing
  1742. # Upper
  1743. if wing_pos1 in self.sideU.edge_north_pos:
  1744. for step in ("U2", ):
  1745. self.rotate(step)
  1746. elif wing_pos1 in self.sideU.edge_south_pos:
  1747. pass
  1748. elif wing_pos1 in self.sideU.edge_east_pos:
  1749. for step in ("U", ):
  1750. self.rotate(step)
  1751. elif wing_pos1 in self.sideU.edge_west_pos:
  1752. for step in ("U'", ):
  1753. self.rotate(step)
  1754. # Left
  1755. elif wing_pos1 in self.sideL.edge_north_pos:
  1756. for step in ("U'", ):
  1757. self.rotate(step)
  1758. elif wing_pos1 in self.sideL.edge_south_pos:
  1759. for step in ("L2", "U'"):
  1760. self.rotate(step)
  1761. elif wing_pos1 in self.sideL.edge_east_pos:
  1762. for step in ("F", ):
  1763. self.rotate(step)
  1764. elif wing_pos1 in self.sideL.edge_west_pos:
  1765. for step in ("L", "U'"):
  1766. self.rotate(step)
  1767. # Front
  1768. elif wing_pos1 in self.sideF.edge_north_pos:
  1769. pass
  1770. elif wing_pos1 in self.sideF.edge_south_pos:
  1771. for step in ("F2", ):
  1772. self.rotate(step)
  1773. elif wing_pos1 in self.sideF.edge_east_pos:
  1774. for step in ("F'", ):
  1775. self.rotate(step)
  1776. elif wing_pos1 in self.sideF.edge_west_pos:
  1777. for step in ("F", ):
  1778. self.rotate(step)
  1779. # Right
  1780. elif wing_pos1 in self.sideR.edge_north_pos:
  1781. for step in ("U", ):
  1782. self.rotate(step)
  1783. elif wing_pos1 in self.sideR.edge_south_pos:
  1784. for step in ("R2", "U"):
  1785. self.rotate(step)
  1786. elif wing_pos1 in self.sideR.edge_east_pos:
  1787. for step in ("R'", "U"):
  1788. self.rotate(step)
  1789. elif wing_pos1 in self.sideR.edge_west_pos:
  1790. for step in ("R", "U"):
  1791. self.rotate(step)
  1792. # Back
  1793. elif wing_pos1 in self.sideB.edge_north_pos:
  1794. for step in ("U2", ):
  1795. self.rotate(step)
  1796. elif wing_pos1 in self.sideB.edge_south_pos:
  1797. for step in ("B2", "U2"):
  1798. self.rotate(step)
  1799. elif wing_pos1 in self.sideB.edge_east_pos:
  1800. for step in ("B'", "U2"):
  1801. self.rotate(step)
  1802. elif wing_pos1 in self.sideB.edge_west_pos:
  1803. for step in ("B", "U2"):
  1804. self.rotate(step)
  1805. # Down
  1806. elif wing_pos1 in self.sideD.edge_north_pos:
  1807. for step in ("F2", ):
  1808. self.rotate(step)
  1809. elif wing_pos1 in self.sideD.edge_south_pos:
  1810. for step in ("D2", "F2"):
  1811. self.rotate(step)
  1812. elif wing_pos1 in self.sideD.edge_east_pos:
  1813. for step in ("D'", "F2"):
  1814. self.rotate(step)
  1815. elif wing_pos1 in self.sideD.edge_west_pos:
  1816. for step in ("D", "F2"):
  1817. self.rotate(step)
  1818. else:
  1819. raise ImplementThis("implement wing %s to U south" % str(wing))
  1820. def move_wing_to_U_east(self, wing):
  1821. if isinstance(wing, tuple) or isinstance(wing, list):
  1822. wing_pos1 = wing[0]
  1823. else:
  1824. wing_pos1 = wing
  1825. # Upper
  1826. if wing_pos1 in self.sideU.edge_north_pos:
  1827. for step in ("U", ):
  1828. self.rotate(step)
  1829. elif wing_pos1 in self.sideU.edge_south_pos:
  1830. for step in ("U'", ):
  1831. self.rotate(step)
  1832. elif wing_pos1 in self.sideU.edge_east_pos:
  1833. pass
  1834. elif wing_pos1 in self.sideU.edge_west_pos:
  1835. for step in ("U2", ):
  1836. self.rotate(step)
  1837. # Left
  1838. elif wing_pos1 in self.sideL.edge_north_pos:
  1839. for step in ("U2", ):
  1840. self.rotate(step)
  1841. elif wing_pos1 in self.sideL.edge_south_pos:
  1842. for step in ("L2", "U2"):
  1843. self.rotate(step)
  1844. elif wing_pos1 in self.sideL.edge_east_pos:
  1845. for step in ("F", "U'"):
  1846. self.rotate(step)
  1847. elif wing_pos1 in self.sideL.edge_west_pos:
  1848. for step in ("L", "U2"):
  1849. self.rotate(step)
  1850. # Front
  1851. elif wing_pos1 in self.sideF.edge_north_pos:
  1852. for step in ("U'", ):
  1853. self.rotate(step)
  1854. elif wing_pos1 in self.sideF.edge_south_pos:
  1855. for step in ("F'", "R"):
  1856. self.rotate(step)
  1857. elif wing_pos1 in self.sideF.edge_east_pos:
  1858. for step in ("R", ):
  1859. self.rotate(step)
  1860. elif wing_pos1 in self.sideF.edge_west_pos:
  1861. for step in ("F", "U'"):
  1862. self.rotate(step)
  1863. # Right
  1864. elif wing_pos1 in self.sideR.edge_north_pos:
  1865. pass
  1866. elif wing_pos1 in self.sideR.edge_south_pos:
  1867. for step in ("R2", ):
  1868. self.rotate(step)
  1869. elif wing_pos1 in self.sideR.edge_east_pos:
  1870. for step in ("R'", ):
  1871. self.rotate(step)
  1872. elif wing_pos1 in self.sideR.edge_west_pos:
  1873. for step in ("R", ):
  1874. self.rotate(step)
  1875. # Back
  1876. elif wing_pos1 in self.sideB.edge_north_pos:
  1877. for step in ("U", ):
  1878. self.rotate(step)
  1879. elif wing_pos1 in self.sideB.edge_south_pos:
  1880. for step in ("B", "R'"):
  1881. self.rotate(step)
  1882. elif wing_pos1 in self.sideB.edge_east_pos:
  1883. for step in ("B'", "U"):
  1884. self.rotate(step)
  1885. elif wing_pos1 in self.sideB.edge_west_pos:
  1886. for step in ("R'", ):
  1887. self.rotate(step)
  1888. # Down
  1889. elif wing_pos1 in self.sideD.edge_north_pos:
  1890. for step in ("D", "R2"):
  1891. self.rotate(step)
  1892. elif wing_pos1 in self.sideD.edge_south_pos:
  1893. for step in ("D'", "R2"):
  1894. self.rotate(step)
  1895. elif wing_pos1 in self.sideD.edge_east_pos:
  1896. for step in ("R2", ):
  1897. self.rotate(step)
  1898. elif wing_pos1 in self.sideD.edge_west_pos:
  1899. for step in ("D2", "R2"):
  1900. self.rotate(step)
  1901. else:
  1902. raise ImplementThis("implement wing %s to U east" % str(wing))
  1903. def move_wing_to_L_west(self, wing):
  1904. if isinstance(wing, tuple) or isinstance(wing, list):
  1905. wing_pos1 = wing[0]
  1906. else:
  1907. wing_pos1 = wing
  1908. # Upper
  1909. if wing_pos1 in self.sideU.edge_north_pos:
  1910. for step in ("B", ):
  1911. self.rotate(step)
  1912. elif wing_pos1 in self.sideU.edge_south_pos:
  1913. for step in ("U2", "B"):
  1914. self.rotate(step)
  1915. elif wing_pos1 in self.sideU.edge_east_pos:
  1916. for step in ("U'", "B"):
  1917. self.rotate(step)
  1918. elif wing_pos1 in self.sideU.edge_west_pos:
  1919. for step in ("U", "B"):
  1920. self.rotate(step)
  1921. # Left
  1922. elif wing_pos1 in self.sideL.edge_north_pos:
  1923. for step in ("L'", ):
  1924. self.rotate(step)
  1925. elif wing_pos1 in self.sideL.edge_south_pos:
  1926. for step in ("L", ):
  1927. self.rotate(step)
  1928. elif wing_pos1 in self.sideL.edge_east_pos:
  1929. for step in ("L2", ):
  1930. self.rotate(step)
  1931. elif wing_pos1 in self.sideL.edge_west_pos:
  1932. pass
  1933. # Front
  1934. elif wing_pos1 in self.sideF.edge_north_pos:
  1935. for step in ("F'", "L2"):
  1936. self.rotate(step)
  1937. elif wing_pos1 in self.sideF.edge_south_pos:
  1938. for step in ("F", "L2"):
  1939. self.rotate(step)
  1940. elif wing_pos1 in self.sideF.edge_east_pos:
  1941. for step in ("F2", "L2"):
  1942. self.rotate(step)
  1943. elif wing_pos1 in self.sideF.edge_west_pos:
  1944. for step in ("L2", ):
  1945. self.rotate(step)
  1946. # Right
  1947. elif wing_pos1 in self.sideR.edge_north_pos:
  1948. for step in ("R", "B2"):
  1949. self.rotate(step)
  1950. elif wing_pos1 in self.sideR.edge_south_pos:
  1951. for step in ("R'", "B2"):
  1952. self.rotate(step)
  1953. elif wing_pos1 in self.sideR.edge_east_pos:
  1954. for step in ("B2", ):
  1955. self.rotate(step)
  1956. elif wing_pos1 in self.sideR.edge_west_pos:
  1957. for step in ("R2", "B2"):
  1958. self.rotate(step)
  1959. # Back
  1960. elif wing_pos1 in self.sideB.edge_north_pos:
  1961. for step in ("B", ):
  1962. self.rotate(step)
  1963. elif wing_pos1 in self.sideB.edge_south_pos:
  1964. for step in ("B'", ):
  1965. self.rotate(step)
  1966. elif wing_pos1 in self.sideB.edge_east_pos:
  1967. pass
  1968. elif wing_pos1 in self.sideB.edge_west_pos:
  1969. for step in ("B2", ):
  1970. self.rotate(step)
  1971. # Down
  1972. elif wing_pos1 in self.sideD.edge_north_pos:
  1973. for step in ("D'", "L"):
  1974. self.rotate(step)
  1975. elif wing_pos1 in self.sideD.edge_south_pos:
  1976. for step in ("D", "L"):
  1977. self.rotate(step)
  1978. elif wing_pos1 in self.sideD.edge_east_pos:
  1979. for step in ("D2", "L"):
  1980. self.rotate(step)
  1981. elif wing_pos1 in self.sideD.edge_west_pos:
  1982. for step in ("L", ):
  1983. self.rotate(step)
  1984. else:
  1985. raise ImplementThis("implement wing %s to L west" % str(wing))
  1986. def move_wing_to_L_east(self, wing):
  1987. if isinstance(wing, tuple) or isinstance(wing, list):
  1988. wing_pos1 = wing[0]
  1989. else:
  1990. wing_pos1 = wing
  1991. # Upper
  1992. if wing_pos1 in self.sideU.edge_north_pos:
  1993. for step in ("U'", "L"):
  1994. self.rotate(step)
  1995. elif wing_pos1 in self.sideU.edge_south_pos:
  1996. self.rotate("F'")
  1997. elif wing_pos1 in self.sideU.edge_east_pos:
  1998. for step in ("U2", "L"):
  1999. self.rotate(step)
  2000. elif wing_pos1 in self.sideU.edge_west_pos:
  2001. self.rotate("L")
  2002. # Left
  2003. elif wing_pos1 in self.sideL.edge_north_pos:
  2004. self.rotate("L")
  2005. elif wing_pos1 in self.sideL.edge_south_pos:
  2006. self.rotate("L'")
  2007. elif wing_pos1 in self.sideL.edge_east_pos:
  2008. pass
  2009. elif wing_pos1 in self.sideL.edge_west_pos:
  2010. self.rotate("L2")
  2011. # Front
  2012. elif wing_pos1 in self.sideF.edge_north_pos:
  2013. self.rotate("F'")
  2014. elif wing_pos1 in self.sideF.edge_south_pos:
  2015. self.rotate("F")
  2016. elif wing_pos1 in self.sideF.edge_east_pos:
  2017. self.rotate("F2")
  2018. elif wing_pos1 in self.sideF.edge_west_pos:
  2019. pass
  2020. # Right
  2021. elif wing_pos1 in self.sideR.edge_north_pos:
  2022. for step in ("U2", "L"):
  2023. self.rotate(step)
  2024. elif wing_pos1 in self.sideR.edge_south_pos:
  2025. for step in ("D2", "L'"):
  2026. self.rotate(step)
  2027. elif wing_pos1 in self.sideR.edge_east_pos:
  2028. for step in ("B2", "L2"):
  2029. self.rotate(step)
  2030. elif wing_pos1 in self.sideR.edge_west_pos:
  2031. self.rotate("F2")
  2032. # Back
  2033. elif wing_pos1 in self.sideB.edge_north_pos:
  2034. for step in ("U'", "L"):
  2035. self.rotate(step)
  2036. elif wing_pos1 in self.sideB.edge_south_pos:
  2037. for step in ("B'", "L2"):
  2038. self.rotate(step)
  2039. elif wing_pos1 in self.sideB.edge_east_pos:
  2040. self.rotate("L2")
  2041. elif wing_pos1 in self.sideB.edge_west_pos:
  2042. for step in ("B2", "L2"):
  2043. self.rotate(step)
  2044. # Down
  2045. elif wing_pos1 in self.sideD.edge_north_pos:
  2046. self.rotate("F")
  2047. elif wing_pos1 in self.sideD.edge_south_pos:
  2048. for step in ("D", "L'"):
  2049. self.rotate(step)
  2050. elif wing_pos1 in self.sideD.edge_east_pos:
  2051. for step in ("D2", "L'"):
  2052. self.rotate(step)
  2053. elif wing_pos1 in self.sideD.edge_west_pos:
  2054. self.rotate("L'")
  2055. else:
  2056. raise ImplementThis("implement wing %s to L east" % str(wing))
  2057. def move_wing_to_F_west(self, wing):
  2058. self.move_wing_to_L_east(wing)
  2059. def move_wing_to_R_west(self, wing):
  2060. if isinstance(wing, tuple) or isinstance(wing, list):
  2061. wing_pos1 = wing[0]
  2062. else:
  2063. wing_pos1 = wing
  2064. # Upper
  2065. if wing_pos1 in self.sideU.edge_north_pos:
  2066. for step in ("U", "R'"):
  2067. self.rotate(step)
  2068. elif wing_pos1 in self.sideU.edge_south_pos:
  2069. for step in ("U'", "R'"):
  2070. self.rotate(step)
  2071. elif wing_pos1 in self.sideU.edge_east_pos:
  2072. for step in ("R'",):
  2073. self.rotate(step)
  2074. elif wing_pos1 in self.sideU.edge_west_pos:
  2075. for step in ("U2", "R'"):
  2076. self.rotate(step)
  2077. # Left
  2078. elif wing_pos1 in self.sideL.edge_north_pos:
  2079. for step in ("U2", "R'"):
  2080. self.rotate(step)
  2081. elif wing_pos1 in self.sideL.edge_south_pos:
  2082. for step in ("D2", "R"):
  2083. self.rotate(step)
  2084. elif wing_pos1 in self.sideL.edge_east_pos:
  2085. for step in ("F2", ):
  2086. self.rotate(step)
  2087. elif wing_pos1 in self.sideL.edge_west_pos:
  2088. for step in ("B2", "R2"):
  2089. self.rotate(step)
  2090. # Front
  2091. elif wing_pos1 in self.sideF.edge_north_pos:
  2092. for step in ("F", ):
  2093. self.rotate(step)
  2094. elif wing_pos1 in self.sideF.edge_south_pos:
  2095. for step in ("D", "R"):
  2096. self.rotate(step)
  2097. elif wing_pos1 in self.sideF.edge_east_pos:
  2098. pass
  2099. elif wing_pos1 in self.sideF.edge_west_pos:
  2100. for step in ("F2", ):
  2101. self.rotate(step)
  2102. # Right
  2103. elif wing_pos1 in self.sideR.edge_north_pos:
  2104. for step in ("R'", ):
  2105. self.rotate(step)
  2106. elif wing_pos1 in self.sideR.edge_south_pos:
  2107. for step in ("R", ):
  2108. self.rotate(step)
  2109. elif wing_pos1 in self.sideR.edge_east_pos:
  2110. for step in ("R2",):
  2111. self.rotate(step)
  2112. elif wing_pos1 in self.sideR.edge_west_pos:
  2113. pass
  2114. # Back
  2115. elif wing_pos1 in self.sideB.edge_north_pos:
  2116. for step in ("U", "R'"):
  2117. self.rotate(step)
  2118. elif wing_pos1 in self.sideB.edge_south_pos:
  2119. for step in ("D'", "R"):
  2120. self.rotate(step)
  2121. elif wing_pos1 in self.sideB.edge_east_pos:
  2122. for step in ("B2", "R2"):
  2123. self.rotate(step)
  2124. elif wing_pos1 in self.sideB.edge_west_pos:
  2125. for step in ("R2",):
  2126. self.rotate(step)
  2127. # Down
  2128. elif wing_pos1 in self.sideD.edge_north_pos:
  2129. for step in ("D", "R"):
  2130. self.rotate(step)
  2131. elif wing_pos1 in self.sideD.edge_south_pos:
  2132. for step in ("D'", "R"):
  2133. self.rotate(step)
  2134. elif wing_pos1 in self.sideD.edge_east_pos:
  2135. for step in ("R", ):
  2136. self.rotate(step)
  2137. elif wing_pos1 in self.sideD.edge_west_pos:
  2138. for step in ("D2", "R"):
  2139. self.rotate(step)
  2140. else:
  2141. raise ImplementThis("implement wing %s to R west" % str(wing))
  2142. def move_wing_to_R_east(self, wing):
  2143. if isinstance(wing, tuple) or isinstance(wing, list):
  2144. wing_pos1 = wing[0]
  2145. else:
  2146. wing_pos1 = wing
  2147. # Upper
  2148. if wing_pos1 in self.sideU.edge_north_pos:
  2149. for step in ("B'", ):
  2150. self.rotate(step)
  2151. elif wing_pos1 in self.sideU.edge_south_pos:
  2152. for step in ("U'", "R"):
  2153. self.rotate(step)
  2154. elif wing_pos1 in self.sideU.edge_east_pos:
  2155. for step in ("R", ):
  2156. self.rotate(step)
  2157. elif wing_pos1 in self.sideU.edge_west_pos:
  2158. for step in ("U2", "R"):
  2159. self.rotate(step)
  2160. # Left
  2161. elif wing_pos1 in self.sideL.edge_north_pos:
  2162. for step in ("L'", "B2"):
  2163. self.rotate(step)
  2164. elif wing_pos1 in self.sideL.edge_south_pos:
  2165. for step in ("L", "B2"):
  2166. self.rotate(step)
  2167. elif wing_pos1 in self.sideL.edge_east_pos:
  2168. for step in ("F2", "R2"):
  2169. self.rotate(step)
  2170. elif wing_pos1 in self.sideL.edge_west_pos:
  2171. for step in ("B2", ):
  2172. self.rotate(step)
  2173. # Front
  2174. elif wing_pos1 in self.sideF.edge_north_pos:
  2175. for step in ("U'", "R"):
  2176. self.rotate(step)
  2177. elif wing_pos1 in self.sideF.edge_south_pos:
  2178. for step in ("F'", "R2"):
  2179. self.rotate(step)
  2180. elif wing_pos1 in self.sideF.edge_east_pos:
  2181. for step in ("R2", ):
  2182. self.rotate(step)
  2183. elif wing_pos1 in self.sideF.edge_west_pos:
  2184. for step in ("F2", "R2"):
  2185. self.rotate(step)
  2186. # Right
  2187. elif wing_pos1 in self.sideR.edge_north_pos:
  2188. for step in ("R", ):
  2189. self.rotate(step)
  2190. elif wing_pos1 in self.sideR.edge_south_pos:
  2191. for step in ("R'", ):
  2192. self.rotate(step)
  2193. elif wing_pos1 in self.sideR.edge_east_pos:
  2194. pass
  2195. elif wing_pos1 in self.sideR.edge_west_pos:
  2196. for step in ("R2", ):
  2197. self.rotate(step)
  2198. # Back
  2199. elif wing_pos1 in self.sideB.edge_north_pos:
  2200. for step in ("B'", ):
  2201. self.rotate(step)
  2202. elif wing_pos1 in self.sideB.edge_south_pos:
  2203. for step in ("B", ):
  2204. self.rotate(step)
  2205. elif wing_pos1 in self.sideB.edge_east_pos:
  2206. for step in ("B2", ):
  2207. self.rotate(step)
  2208. elif wing_pos1 in self.sideB.edge_west_pos:
  2209. pass
  2210. # Down
  2211. elif wing_pos1 in self.sideD.edge_north_pos:
  2212. for step in ("D", "R'"):
  2213. self.rotate(step)
  2214. elif wing_pos1 in self.sideD.edge_south_pos:
  2215. for step in ("D'", "R'"):
  2216. self.rotate(step)
  2217. elif wing_pos1 in self.sideD.edge_east_pos:
  2218. for step in ("R'", ):
  2219. self.rotate(step)
  2220. elif wing_pos1 in self.sideD.edge_west_pos:
  2221. for step in ("D2", "R'"):
  2222. self.rotate(step)
  2223. else:
  2224. raise ImplementThis("implement wing %s to R east" % str(wing))
  2225. def move_wing_to_F_east(self, wing):
  2226. self.move_wing_to_R_west(wing)
  2227. def move_wing_to_D_north(self, wing):
  2228. if isinstance(wing, tuple) or isinstance(wing, list):
  2229. wing_pos1 = wing[0]
  2230. else:
  2231. wing_pos1 = wing
  2232. # Upper
  2233. if wing_pos1 in self.sideU.edge_north_pos:
  2234. for step in ("B2", "D2"):
  2235. self.rotate(step)
  2236. elif wing_pos1 in self.sideU.edge_south_pos:
  2237. for step in ("F2", ):
  2238. self.rotate(step)
  2239. elif wing_pos1 in self.sideU.edge_east_pos:
  2240. for step in ("R2", "D'"):
  2241. self.rotate(step)
  2242. elif wing_pos1 in self.sideU.edge_west_pos:
  2243. for step in ("L2", "D"):
  2244. self.rotate(step)
  2245. # Left
  2246. elif wing_pos1 in self.sideL.edge_north_pos:
  2247. for step in ("L2", "D"):
  2248. self.rotate(step)
  2249. elif wing_pos1 in self.sideL.edge_south_pos:
  2250. for step in ("D", ):
  2251. self.rotate(step)
  2252. elif wing_pos1 in self.sideL.edge_east_pos:
  2253. for step in ("L", "D"):
  2254. self.rotate(step)
  2255. elif wing_pos1 in self.sideL.edge_west_pos:
  2256. for step in ("L'", "D"):
  2257. self.rotate(step)
  2258. # Front
  2259. elif wing_pos1 in self.sideF.edge_north_pos:
  2260. for step in ("F2", ):
  2261. self.rotate(step)
  2262. elif wing_pos1 in self.sideF.edge_south_pos:
  2263. pass
  2264. elif wing_pos1 in self.sideF.edge_east_pos:
  2265. for step in ("F", ):
  2266. self.rotate(step)
  2267. elif wing_pos1 in self.sideF.edge_west_pos:
  2268. for step in ("F'", ):
  2269. self.rotate(step)
  2270. # Right
  2271. elif wing_pos1 in self.sideR.edge_north_pos:
  2272. for step in ("R2", "D'"):
  2273. self.rotate(step)
  2274. elif wing_pos1 in self.sideR.edge_south_pos:
  2275. for step in ("D'", ):
  2276. self.rotate(step)
  2277. elif wing_pos1 in self.sideR.edge_east_pos:
  2278. for step in ("R", "D'"):
  2279. self.rotate(step)
  2280. elif wing_pos1 in self.sideR.edge_west_pos:
  2281. for step in ("R'", "D'"):
  2282. self.rotate(step)
  2283. # Back
  2284. elif wing_pos1 in self.sideB.edge_north_pos:
  2285. for step in ("B2", "D2"):
  2286. self.rotate(step)
  2287. elif wing_pos1 in self.sideB.edge_south_pos:
  2288. for step in ("D2", ):
  2289. self.rotate(step)
  2290. elif wing_pos1 in self.sideB.edge_east_pos:
  2291. for step in ("B", "D2"):
  2292. self.rotate(step)
  2293. elif wing_pos1 in self.sideB.edge_west_pos:
  2294. for step in ("R", "D'"):
  2295. self.rotate(step)
  2296. # Down
  2297. elif wing_pos1 in self.sideD.edge_north_pos:
  2298. pass
  2299. elif wing_pos1 in self.sideD.edge_south_pos:
  2300. for step in ("D2", ):
  2301. self.rotate(step)
  2302. elif wing_pos1 in self.sideD.edge_east_pos:
  2303. for step in ("D'", ):
  2304. self.rotate(step)
  2305. elif wing_pos1 in self.sideD.edge_west_pos:
  2306. for step in ("D", ):
  2307. self.rotate(step)
  2308. else:
  2309. raise ImplementThis("implement wing %s to D north" % str(wing))
  2310. def move_wing_to_D_west(self, wing):
  2311. if isinstance(wing, tuple) or isinstance(wing, list):
  2312. wing_pos1 = wing[0]
  2313. else:
  2314. wing_pos1 = wing
  2315. # Upper
  2316. if wing_pos1 in self.sideU.edge_north_pos:
  2317. for step in ("U'", "L2"):
  2318. self.rotate(step)
  2319. elif wing_pos1 in self.sideU.edge_south_pos:
  2320. for step in ("U", "L2"):
  2321. self.rotate(step)
  2322. elif wing_pos1 in self.sideU.edge_east_pos:
  2323. for step in ("U2", "L2"):
  2324. self.rotate(step)
  2325. elif wing_pos1 in self.sideU.edge_west_pos:
  2326. for step in ("L2", ):
  2327. self.rotate(step)
  2328. # Left
  2329. elif wing_pos1 in self.sideL.edge_north_pos:
  2330. for step in ("L2", ):
  2331. self.rotate(step)
  2332. elif wing_pos1 in self.sideL.edge_south_pos:
  2333. pass
  2334. elif wing_pos1 in self.sideL.edge_east_pos:
  2335. for step in ("L", ):
  2336. self.rotate(step)
  2337. elif wing_pos1 in self.sideL.edge_west_pos:
  2338. for step in ("L'", ):
  2339. self.rotate(step)
  2340. # Front
  2341. elif wing_pos1 in self.sideF.edge_north_pos:
  2342. for step in ("F'", "L"):
  2343. self.rotate(step)
  2344. elif wing_pos1 in self.sideF.edge_south_pos:
  2345. for step in ("D'", ):
  2346. self.rotate(step)
  2347. elif wing_pos1 in self.sideF.edge_east_pos:
  2348. for step in ("F", "D'"):
  2349. self.rotate(step)
  2350. elif wing_pos1 in self.sideF.edge_west_pos:
  2351. for step in ("L", ):
  2352. self.rotate(step)
  2353. # Right
  2354. elif wing_pos1 in self.sideR.edge_north_pos:
  2355. for step in ("R2", "D2"):
  2356. self.rotate(step)
  2357. elif wing_pos1 in self.sideR.edge_south_pos:
  2358. for step in ("D2", ):
  2359. self.rotate(step)
  2360. elif wing_pos1 in self.sideR.edge_east_pos:
  2361. for step in ("R", "D2"):
  2362. self.rotate(step)
  2363. elif wing_pos1 in self.sideR.edge_west_pos:
  2364. for step in ("R'", "D2"):
  2365. self.rotate(step)
  2366. # Back
  2367. elif wing_pos1 in self.sideB.edge_north_pos:
  2368. for step in ("B", "L'"):
  2369. self.rotate(step)
  2370. elif wing_pos1 in self.sideB.edge_south_pos:
  2371. for step in ("D", ):
  2372. self.rotate(step)
  2373. elif wing_pos1 in self.sideB.edge_east_pos:
  2374. for step in ("L'", ):
  2375. self.rotate(step)
  2376. elif wing_pos1 in self.sideB.edge_west_pos:
  2377. for step in ("B'", "D"):
  2378. self.rotate(step)
  2379. # Down
  2380. elif wing_pos1 in self.sideD.edge_north_pos:
  2381. for step in ("D'", ):
  2382. self.rotate(step)
  2383. elif wing_pos1 in self.sideD.edge_south_pos:
  2384. for step in ("D", ):
  2385. self.rotate(step)
  2386. elif wing_pos1 in self.sideD.edge_east_pos:
  2387. for step in ("D2", ):
  2388. self.rotate(step)
  2389. elif wing_pos1 in self.sideD.edge_west_pos:
  2390. pass
  2391. else:
  2392. raise ImplementThis("implement wing %s to D west" % str(wing))
  2393. def move_wing_to_D_south(self, wing):
  2394. if isinstance(wing, tuple) or isinstance(wing, list):
  2395. wing_pos1 = wing[0]
  2396. else:
  2397. wing_pos1 = wing
  2398. # Upper
  2399. if wing_pos1 in self.sideU.edge_north_pos:
  2400. for step in ("B2", ):
  2401. self.rotate(step)
  2402. elif wing_pos1 in self.sideU.edge_south_pos:
  2403. for step in ("F2", "D2"):
  2404. self.rotate(step)
  2405. elif wing_pos1 in self.sideU.edge_east_pos:
  2406. for step in ("R2", "D"):
  2407. self.rotate(step)
  2408. elif wing_pos1 in self.sideU.edge_west_pos:
  2409. for step in ("L2", "D'"):
  2410. self.rotate(step)
  2411. # Left
  2412. elif wing_pos1 in self.sideL.edge_north_pos:
  2413. for step in ("L2", "D'"):
  2414. self.rotate(step)
  2415. elif wing_pos1 in self.sideL.edge_south_pos:
  2416. for step in ("D'", ):
  2417. self.rotate(step)
  2418. elif wing_pos1 in self.sideL.edge_east_pos:
  2419. for step in ("L", "D'"):
  2420. self.rotate(step)
  2421. elif wing_pos1 in self.sideL.edge_west_pos:
  2422. for step in ("L'", "D'"):
  2423. self.rotate(step)
  2424. # Front
  2425. elif wing_pos1 in self.sideF.edge_north_pos:
  2426. for step in ("F2", "D2"):
  2427. self.rotate(step)
  2428. elif wing_pos1 in self.sideF.edge_south_pos:
  2429. for step in ("D2", ):
  2430. self.rotate(step)
  2431. elif wing_pos1 in self.sideF.edge_east_pos:
  2432. for step in ("F", "D2"):
  2433. self.rotate(step)
  2434. elif wing_pos1 in self.sideF.edge_west_pos:
  2435. for step in ("F'", "D2"):
  2436. self.rotate(step)
  2437. # Right
  2438. elif wing_pos1 in self.sideR.edge_north_pos:
  2439. for step in ("R2", "D"):
  2440. self.rotate(step)
  2441. elif wing_pos1 in self.sideR.edge_south_pos:
  2442. for step in ("D", ):
  2443. self.rotate(step)
  2444. elif wing_pos1 in self.sideR.edge_east_pos:
  2445. for step in ("R", "D"):
  2446. self.rotate(step)
  2447. elif wing_pos1 in self.sideR.edge_west_pos:
  2448. for step in ("R'", "D"):
  2449. self.rotate(step)
  2450. # Back
  2451. elif wing_pos1 in self.sideB.edge_north_pos:
  2452. for step in ("B2", ):
  2453. self.rotate(step)
  2454. elif wing_pos1 in self.sideB.edge_south_pos:
  2455. pass
  2456. elif wing_pos1 in self.sideB.edge_east_pos:
  2457. for step in ("B", ):
  2458. self.rotate(step)
  2459. elif wing_pos1 in self.sideB.edge_west_pos:
  2460. for step in ("B'", ):
  2461. self.rotate(step)
  2462. # Down
  2463. elif wing_pos1 in self.sideD.edge_north_pos:
  2464. for step in ("D2", ):
  2465. self.rotate(step)
  2466. elif wing_pos1 in self.sideD.edge_south_pos:
  2467. pass
  2468. elif wing_pos1 in self.sideD.edge_east_pos:
  2469. for step in ("D", ):
  2470. self.rotate(step)
  2471. elif wing_pos1 in self.sideD.edge_west_pos:
  2472. for step in ("D'", ):
  2473. self.rotate(step)
  2474. else:
  2475. raise ImplementThis("implement wing %s to D south" % str(wing))
  2476. def move_wing_to_D_east(self, wing):
  2477. if isinstance(wing, tuple) or isinstance(wing, list):
  2478. wing_pos1 = wing[0]
  2479. else:
  2480. wing_pos1 = wing
  2481. # Upper
  2482. if wing_pos1 in self.sideU.edge_north_pos:
  2483. for step in ("U", "R2"):
  2484. self.rotate(step)
  2485. elif wing_pos1 in self.sideU.edge_south_pos:
  2486. for step in ("U'", "R2"):
  2487. self.rotate(step)
  2488. elif wing_pos1 in self.sideU.edge_east_pos:
  2489. for step in ("R2", ):
  2490. self.rotate(step)
  2491. elif wing_pos1 in self.sideU.edge_west_pos:
  2492. for step in ("U2", "R2"):
  2493. self.rotate(step)
  2494. # Left
  2495. elif wing_pos1 in self.sideL.edge_north_pos:
  2496. for step in ("L2", "D2"):
  2497. self.rotate(step)
  2498. elif wing_pos1 in self.sideL.edge_south_pos:
  2499. for step in ("D2", ):
  2500. self.rotate(step)
  2501. elif wing_pos1 in self.sideL.edge_east_pos:
  2502. for step in ("L", "D2"):
  2503. self.rotate(step)
  2504. elif wing_pos1 in self.sideL.edge_west_pos:
  2505. for step in ("L'", "D2"):
  2506. self.rotate(step)
  2507. # Front
  2508. elif wing_pos1 in self.sideF.edge_north_pos:
  2509. for step in ("F", "R'"):
  2510. self.rotate(step)
  2511. elif wing_pos1 in self.sideF.edge_south_pos:
  2512. for step in ("D", ):
  2513. self.rotate(step)
  2514. elif wing_pos1 in self.sideF.edge_east_pos:
  2515. for step in ("R'", ):
  2516. self.rotate(step)
  2517. elif wing_pos1 in self.sideF.edge_west_pos:
  2518. for step in ("F'", "D"):
  2519. self.rotate(step)
  2520. # Right
  2521. elif wing_pos1 in self.sideR.edge_north_pos:
  2522. for step in ("R2", ):
  2523. self.rotate(step)
  2524. elif wing_pos1 in self.sideR.edge_south_pos:
  2525. pass
  2526. elif wing_pos1 in self.sideR.edge_east_pos:
  2527. for step in ("R", ):
  2528. self.rotate(step)
  2529. elif wing_pos1 in self.sideR.edge_west_pos:
  2530. for step in ("R'", ):
  2531. self.rotate(step)
  2532. # Back
  2533. elif wing_pos1 in self.sideB.edge_north_pos:
  2534. for step in ("B2", "D'"):
  2535. self.rotate(step)
  2536. elif wing_pos1 in self.sideB.edge_south_pos:
  2537. for step in ("D'", ):
  2538. self.rotate(step)
  2539. elif wing_pos1 in self.sideB.edge_east_pos:
  2540. for step in ("B", "D'"):
  2541. self.rotate(step)
  2542. elif wing_pos1 in self.sideB.edge_west_pos:
  2543. for step in ("R", ):
  2544. self.rotate(step)
  2545. # Down
  2546. elif wing_pos1 in self.sideD.edge_north_pos:
  2547. for step in ("D", ):
  2548. self.rotate(step)
  2549. elif wing_pos1 in self.sideD.edge_south_pos:
  2550. for step in ("D'", ):
  2551. self.rotate(step)
  2552. elif wing_pos1 in self.sideD.edge_east_pos:
  2553. pass
  2554. elif wing_pos1 in self.sideD.edge_west_pos:
  2555. for step in ("D2", ):
  2556. self.rotate(step)
  2557. else:
  2558. raise ImplementThis("implement wing %s to D east" % str(wing))
  2559. def rotate_x(self):
  2560. self.rotate("x")
  2561. def rotate_x_reverse(self):
  2562. self.rotate("x'")
  2563. def rotate_y(self):
  2564. self.rotate("y")
  2565. def rotate_y_reverse(self):
  2566. self.rotate("y'")
  2567. def rotate_z(self):
  2568. self.rotate("z")
  2569. def rotate_z_reverse(self):
  2570. self.rotate("z'")
  2571. def get_center_corner_state(self):
  2572. return ''.join([self.state[square_index] for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD) for square_index in side.center_corner_pos])
  2573. def centers_solved(self):
  2574. for side in list(self.sides.values()):
  2575. prev_pos = None
  2576. for pos in side.center_pos:
  2577. if prev_pos is not None:
  2578. if self.state[prev_pos] != self.state[pos]:
  2579. return False
  2580. prev_pos = pos
  2581. return True
  2582. def crosses_solved(self):
  2583. # side is a Side object
  2584. # added a cross_pos to the Side object
  2585. for side in list(self.sides.values()):
  2586. prev_pos = None
  2587. for pos in side.cross_pos:
  2588. if prev_pos is not None:
  2589. if self.state[prev_pos] != self.state[pos]:
  2590. return False
  2591. prev_pos = pos
  2592. return True
  2593. def UD_centers_staged(self):
  2594. for side in (self.sideU, self.sideD):
  2595. for pos in side.center_pos:
  2596. if self.state[pos] not in ('U', 'D'):
  2597. return False
  2598. return True
  2599. def LR_centers_staged(self):
  2600. for side in (self.sideL, self.sideR):
  2601. for pos in side.center_pos:
  2602. if self.state[pos] not in ('L', 'R'):
  2603. return False
  2604. return True
  2605. def rotate_side_X_to_Y(self, x, y):
  2606. #assert x in ('U', 'L', 'F', 'R', 'B', 'D'), "Invalid side %s" % x
  2607. #assert y in ('U', 'L', 'F', 'R', 'B', 'D'), "Invalid side %s" % y
  2608. if y == 'U':
  2609. side = self.sideU
  2610. elif y == 'L':
  2611. side = self.sideL
  2612. elif y == 'F':
  2613. side = self.sideF
  2614. elif y == 'R':
  2615. side = self.sideR
  2616. elif y == 'B':
  2617. side = self.sideB
  2618. elif y == 'D':
  2619. side = self.sideD
  2620. # odd cube
  2621. if side.mid_pos:
  2622. pos_to_check = side.mid_pos
  2623. F_pos_to_check = self.sideF.mid_pos
  2624. D_pos_to_check = self.sideD.mid_pos
  2625. # even cube
  2626. else:
  2627. # Use the top-right inner x-center
  2628. offset = int(((self.size/2) * self.size) - (self.size/2))
  2629. pos_to_check = side.min_pos + offset
  2630. F_pos_to_check = self.sideF.min_pos + offset
  2631. D_pos_to_check = self.sideD.min_pos + offset
  2632. count = 0
  2633. while self.state[pos_to_check] != x:
  2634. #log.info("%s (%s): rotate %s to %s, pos_to_check %s, state at pos_to_check %s" %
  2635. # (side, side.mid_pos, x, y, pos_to_check, self.state[pos_to_check]))
  2636. if self.state[F_pos_to_check] == x and y == 'U':
  2637. self.rotate_x()
  2638. elif self.state[F_pos_to_check] == x and y == 'D':
  2639. self.rotate_x_reverse()
  2640. elif self.state[D_pos_to_check] == x and y == 'F':
  2641. self.rotate_x()
  2642. elif self.state[D_pos_to_check] == x and y == 'U':
  2643. self.rotate_x()
  2644. self.rotate_x()
  2645. else:
  2646. self.rotate_y()
  2647. count += 1
  2648. if count > 30:
  2649. raise StuckInALoop("rotate %s to %s, %s, pos_to_check %s, state at pos_to_check %s" % (x, y, side, pos_to_check, self.state[pos_to_check]))
  2650. def rotate_U_to_U(self):
  2651. self.rotate_side_X_to_Y('U', 'U')
  2652. def rotate_F_to_F(self):
  2653. self.rotate_side_X_to_Y('F', 'F')
  2654. def get_kociemba_string(self, all_squares):
  2655. # kociemba uses order U R F D L B
  2656. foo = []
  2657. if all_squares:
  2658. # This is only used to print cubes for test cases (see --test-build)
  2659. for side_name in ('U', 'R', 'F', 'D', 'L', 'B'):
  2660. side = self.sides[side_name]
  2661. for square_index in range(side.min_pos, side.max_pos + 1):
  2662. foo.append(self.state[square_index])
  2663. else:
  2664. if self.size == 2:
  2665. for side_name in ('U', 'R', 'F', 'D', 'L', 'B'):
  2666. side = self.sides[side_name]
  2667. # first row
  2668. foo.append(self.state[side.corner_pos[0]])
  2669. foo.append(self.state[side.corner_pos[1]])
  2670. # second row
  2671. foo.append(self.state[side.corner_pos[2]])
  2672. foo.append(self.state[side.corner_pos[3]])
  2673. else:
  2674. for side_name in ('U', 'R', 'F', 'D', 'L', 'B'):
  2675. side = self.sides[side_name]
  2676. # first row
  2677. foo.append(self.state[side.corner_pos[0]])
  2678. foo.append(self.state[side.edge_north_pos[0]])
  2679. foo.append(self.state[side.corner_pos[1]])
  2680. # second row
  2681. foo.append(self.state[side.edge_west_pos[0]])
  2682. if side.mid_pos:
  2683. foo.append(self.state[side.mid_pos])
  2684. else:
  2685. offset = int(((self.size/2) * self.size) - (self.size/2))
  2686. pos_to_check = side.min_pos + offset
  2687. foo.append(self.state[pos_to_check])
  2688. foo.append(self.state[side.edge_east_pos[0]])
  2689. # third row
  2690. foo.append(self.state[side.corner_pos[2]])
  2691. foo.append(self.state[side.edge_south_pos[0]])
  2692. foo.append(self.state[side.corner_pos[3]])
  2693. kociemba_string = ''.join(foo)
  2694. log.debug('kociemba string: %s' % kociemba_string)
  2695. return kociemba_string
  2696. def prevent_OLL(self):
  2697. """
  2698. Solving OLL at the end takes 26 moves, preventing it takes 10
  2699. """
  2700. # OLL only applies for even cubes
  2701. if self.is_odd():
  2702. return False
  2703. orbits_with_oll_parity = self.center_solution_leads_to_oll_parity()
  2704. steps = None
  2705. if not orbits_with_oll_parity:
  2706. return False
  2707. if self.size == 4:
  2708. if orbits_with_oll_parity == [0]:
  2709. steps = "Rw U2 Rw U2 Rw U2 Rw U2 Rw U2"
  2710. else:
  2711. raise SolveError("prevent_OLL for %sx%sx%s, orbits %s have parity issues" %
  2712. (self.size, self.size, self.size, pformat(orbits_with_oll_parity)))
  2713. elif self.size == 6:
  2714. # 10 steps
  2715. if orbits_with_oll_parity == [0,1]:
  2716. steps = "3Rw U2 3Rw U2 3Rw U2 3Rw U2 3Rw U2"
  2717. # 10 steps
  2718. elif orbits_with_oll_parity == [0]:
  2719. steps = "Rw U2 Rw U2 Rw U2 Rw U2 Rw U2"
  2720. # 15 steps for an inside orbit
  2721. elif orbits_with_oll_parity == [1]:
  2722. steps = "3Rw Rw' U2 3Rw Rw' U2 3Rw Rw' U2 3Rw Rw' U2 3Rw Rw' U2"
  2723. else:
  2724. raise SolveError("prevent_OLL for %sx%sx%s, orbits %s have parity issues" %
  2725. (self.size, self.size, self.size, pformat(orbits_with_oll_parity)))
  2726. #else:
  2727. # raise ImplementThis("prevent_OLL for %sx%sx%s, orbits %s have parity issues" %
  2728. # (self.size, self.size, self.size, pformat(orbits_with_oll_parity)))
  2729. if steps:
  2730. for step in steps.split():
  2731. self.rotate(step)
  2732. return True
  2733. return False
  2734. def solve_OLL(self):
  2735. # Check all 12 edges, rotate the one with OLL to U-south
  2736. while True:
  2737. has_oll = False
  2738. if self.state[self.sideU.corner_pos[2]] == self.state[self.sideF.edge_north_pos[0]]:
  2739. has_oll = True
  2740. elif self.state[self.sideU.corner_pos[0]] == self.state[self.sideL.edge_north_pos[0]]:
  2741. has_oll = True
  2742. self.rotate_y_reverse()
  2743. elif self.state[self.sideU.corner_pos[3]] == self.state[self.sideR.edge_north_pos[0]]:
  2744. has_oll = True
  2745. self.rotate_y()
  2746. elif self.state[self.sideU.corner_pos[1]] == self.state[self.sideB.edge_north_pos[0]]:
  2747. has_oll = True
  2748. self.rotate_y()
  2749. self.rotate_y()
  2750. elif self.state[self.sideD.corner_pos[0]] == self.state[self.sideF.edge_south_pos[0]]:
  2751. has_oll = True
  2752. self.rotate_x()
  2753. elif self.state[self.sideD.corner_pos[0]] == self.state[self.sideL.edge_south_pos[0]]:
  2754. has_oll = True
  2755. self.rotate_y_reverse()
  2756. self.rotate_x()
  2757. elif self.state[self.sideD.corner_pos[1]] == self.state[self.sideR.edge_south_pos[0]]:
  2758. has_oll = True
  2759. self.rotate_y()
  2760. self.rotate_x()
  2761. elif self.state[self.sideD.corner_pos[2]] == self.state[self.sideB.edge_south_pos[0]]:
  2762. has_oll = True
  2763. self.rotate_y()
  2764. self.rotate_y()
  2765. self.rotate_x()
  2766. elif self.state[self.sideF.corner_pos[0]] == self.state[self.sideL.edge_east_pos[0]]:
  2767. has_oll = True
  2768. self.rotate_z()
  2769. elif self.state[self.sideF.corner_pos[1]] == self.state[self.sideR.edge_west_pos[0]]:
  2770. has_oll = True
  2771. self.rotate_z_reverse()
  2772. elif self.state[self.sideB.corner_pos[0]] == self.state[self.sideR.edge_east_pos[0]]:
  2773. has_oll = True
  2774. self.rotate_y()
  2775. self.rotate_z_reverse()
  2776. elif self.state[self.sideB.corner_pos[1]] == self.state[self.sideL.edge_west_pos[0]]:
  2777. has_oll = True
  2778. self.rotate_y_reverse()
  2779. self.rotate_z()
  2780. if has_oll:
  2781. # 26 moves :(
  2782. oll_solution = "%dRw2 R2 U2 %dRw2 R2 U2 %dRw R' U2 %dRw R' U2 %dRw' R' U2 B2 U %dRw' R U' B2 U %dRw R' U R2" % (self.size/2, self.size/2, self.size/2, self.size/2, self.size/2, self.size/2, self.size/2)
  2783. log.warning("Solving OLL %s" % oll_solution)
  2784. self.print_cube()
  2785. for step in oll_solution.split():
  2786. self.rotate(step)
  2787. else:
  2788. break
  2789. def solve_PLL(self):
  2790. pll_id = None
  2791. self.rotate_U_to_U()
  2792. self.rotate_F_to_F()
  2793. # rotate one of the hosed edges to U-south
  2794. if self.state[self.sideU.edge_south_pos[0]] != 'U':
  2795. pass
  2796. elif self.state[self.sideU.edge_north_pos[0]] != 'U':
  2797. self.rotate_y()
  2798. self.rotate_y()
  2799. elif self.state[self.sideU.edge_west_pos[0]] != 'U':
  2800. self.rotate_y_reverse()
  2801. elif self.state[self.sideU.edge_east_pos[0]] != 'U':
  2802. self.rotate_y()
  2803. elif self.state[self.sideL.edge_north_pos[0]] != 'L':
  2804. raise ImplementThis("pll")
  2805. elif self.state[self.sideL.edge_south_pos[0]] != 'L':
  2806. self.rotate_x()
  2807. self.rotate_x()
  2808. self.rotate_y_reverse()
  2809. elif self.state[self.sideL.edge_east_pos[0]] != 'L':
  2810. self.rotate_z()
  2811. elif self.state[self.sideL.edge_west_pos[0]] != 'L':
  2812. self.rotate_y_reverse()
  2813. self.rotate_z()
  2814. elif self.state[self.sideF.edge_north_pos[0]] != 'F':
  2815. raise ImplementThis("pll")
  2816. elif self.state[self.sideF.edge_south_pos[0]] != 'F':
  2817. self.rotate_x()
  2818. elif self.state[self.sideF.edge_east_pos[0]] != 'F':
  2819. self.rotate_z_reverse()
  2820. elif self.state[self.sideF.edge_west_pos[0]] != 'F':
  2821. raise ImplementThis("pll")
  2822. elif self.state[self.sideR.edge_north_pos[0]] != 'R':
  2823. raise ImplementThis("pll")
  2824. elif self.state[self.sideR.edge_south_pos[0]] != 'R':
  2825. self.rotate_y()
  2826. self.rotate_x()
  2827. elif self.state[self.sideR.edge_east_pos[0]] != 'R':
  2828. self.rotate_y()
  2829. self.rotate_z_reverse()
  2830. elif self.state[self.sideR.edge_west_pos[0]] != 'R':
  2831. raise ImplementThis("pll")
  2832. elif self.state[self.sideB.edge_north_pos[0]] != 'B':
  2833. raise ImplementThis("pll")
  2834. elif self.state[self.sideB.edge_south_pos[0]] != 'B':
  2835. self.rotate_x()
  2836. self.rotate_x()
  2837. elif self.state[self.sideB.edge_east_pos[0]] != 'B':
  2838. raise ImplementThis("pll")
  2839. elif self.state[self.sideB.edge_west_pos[0]] != 'B':
  2840. raise ImplementThis("pll")
  2841. elif self.state[self.sideD.edge_north_pos[0]] != 'D':
  2842. raise ImplementThis("pll")
  2843. elif self.state[self.sideD.edge_south_pos[0]] != 'D':
  2844. raise ImplementThis("pll")
  2845. elif self.state[self.sideD.edge_east_pos[0]] != 'D':
  2846. raise ImplementThis("pll")
  2847. elif self.state[self.sideD.edge_west_pos[0]] != 'D':
  2848. raise ImplementThis("pll")
  2849. else:
  2850. self.print_cube()
  2851. raise SolveError("we should not be here")
  2852. if self.state[self.sideF.edge_north_pos[0]] == 'F':
  2853. raise SolveError("F-north should have PLL edge")
  2854. # rotate the other hosed edges to U-west
  2855. if self.state[self.sideU.edge_south_pos[0]] != self.state[self.sideU.corner_pos[0]]:
  2856. raise ImplementThis("pll")
  2857. elif self.state[self.sideU.edge_north_pos[0]] != self.state[self.sideU.corner_pos[0]]:
  2858. raise ImplementThis("pll")
  2859. elif self.state[self.sideU.edge_west_pos[0]] != self.state[self.sideU.corner_pos[0]]:
  2860. raise ImplementThis("pll")
  2861. elif self.state[self.sideU.edge_east_pos[0]] != self.state[self.sideU.corner_pos[0]]:
  2862. raise ImplementThis("pll")
  2863. elif self.state[self.sideL.edge_north_pos[0]] != self.state[self.sideL.corner_pos[0]]:
  2864. raise ImplementThis("pll")
  2865. elif self.state[self.sideL.edge_south_pos[0]] != self.state[self.sideL.corner_pos[0]]:
  2866. raise ImplementThis("pll")
  2867. elif self.state[self.sideL.edge_east_pos[0]] != self.state[self.sideL.corner_pos[0]]:
  2868. raise ImplementThis("pll")
  2869. elif self.state[self.sideL.edge_west_pos[0]] != self.state[self.sideL.corner_pos[0]]:
  2870. self.rotate_y()
  2871. pll_id = 2
  2872. elif self.state[self.sideF.edge_south_pos[0]] != self.state[self.sideF.corner_pos[0]]:
  2873. raise ImplementThis("pll")
  2874. elif self.state[self.sideF.edge_east_pos[0]] != self.state[self.sideF.corner_pos[0]]:
  2875. raise ImplementThis("pll")
  2876. elif self.state[self.sideF.edge_west_pos[0]] != self.state[self.sideF.corner_pos[0]]:
  2877. raise ImplementThis("pll")
  2878. elif self.state[self.sideR.edge_north_pos[0]] != self.state[self.sideR.corner_pos[0]]:
  2879. self.rotate_y()
  2880. pll_id = 2
  2881. elif self.state[self.sideR.edge_south_pos[0]] != self.state[self.sideR.corner_pos[0]]:
  2882. raise ImplementThis("pll")
  2883. elif self.state[self.sideR.edge_east_pos[0]] != self.state[self.sideR.corner_pos[0]]:
  2884. raise ImplementThis("pll")
  2885. elif self.state[self.sideR.edge_west_pos[0]] != self.state[self.sideR.corner_pos[0]]:
  2886. raise ImplementThis("pll")
  2887. elif self.state[self.sideB.edge_north_pos[0]] != self.state[self.sideB.corner_pos[0]]:
  2888. raise ImplementThis("pll")
  2889. elif self.state[self.sideB.edge_south_pos[0]] != self.state[self.sideB.corner_pos[0]]:
  2890. raise ImplementThis("pll")
  2891. elif self.state[self.sideB.edge_east_pos[0]] != self.state[self.sideB.corner_pos[0]]:
  2892. raise ImplementThis("pll")
  2893. elif self.state[self.sideB.edge_west_pos[0]] != self.state[self.sideB.corner_pos[0]]:
  2894. raise ImplementThis("pll")
  2895. elif self.state[self.sideD.edge_north_pos[0]] != self.state[self.sideD.corner_pos[0]]:
  2896. raise ImplementThis("pll")
  2897. elif self.state[self.sideD.edge_south_pos[0]] != self.state[self.sideD.corner_pos[0]]:
  2898. raise ImplementThis("pll")
  2899. elif self.state[self.sideD.edge_east_pos[0]] != self.state[self.sideD.corner_pos[0]]:
  2900. raise ImplementThis("pll")
  2901. elif self.state[self.sideD.edge_west_pos[0]] != self.state[self.sideD.corner_pos[0]]:
  2902. raise ImplementThis("pll")
  2903. else:
  2904. raise Exception("we should not be here")
  2905. # http://www.speedcubing.com/chris/4speedsolve3.html
  2906. if pll_id == 2:
  2907. # Takes 12 steps
  2908. pll_solution = "L2 D %dFw2 %dLw2 F2 %dLw2 L2 F2 %dLw2 %dFw2 D' L2" % (self.size/2, self.size/2, self.size/2, self.size/2, self.size/2)
  2909. log.warning("Solving PLL ID %d: %s" % (pll_id, pll_solution))
  2910. self.print_cube()
  2911. for step in pll_solution.split():
  2912. self.rotate(step)
  2913. else:
  2914. raise ImplementThis("pll_id %s" % pll_id)
  2915. def solve_333(self):
  2916. if self.solved():
  2917. return
  2918. kociemba_string = self.get_kociemba_string(False)
  2919. try:
  2920. steps = subprocess.check_output(['kociemba', kociemba_string]).decode('ascii').splitlines()[-1].strip().split()
  2921. kociemba_ok = True
  2922. except Exception:
  2923. kociemba_ok = False
  2924. if not kociemba_ok:
  2925. #edge_swap_count = self.get_edge_swap_count(edges_paired=True, debug=True)
  2926. #corner_swap_count = self.get_corner_swap_count(debug=True)
  2927. #raise SolveError("parity error made kociemba barf, edge parity %d, corner parity %d, kociemba %s" %
  2928. # (edge_swap_count, corner_swap_count, kociemba_string))
  2929. raise SolveError("parity error made kociemba barf, kociemba %s" % kociemba_string)
  2930. log.debug("kociemba : %s" % kociemba_string)
  2931. log.debug("kociemba steps : %s" % ', '.join(steps))
  2932. for step in steps:
  2933. step = str(step)
  2934. self.rotate(step)
  2935. if not self.solved():
  2936. self.solve_OLL()
  2937. if not self.solved():
  2938. self.solve_PLL()
  2939. if not self.solved():
  2940. raise SolveError("We hit either OLL or PLL parity and could not solve it")
  2941. def get_corner_swap_count(self, debug=False):
  2942. needed_corners = [
  2943. 'BLU',
  2944. 'BRU',
  2945. 'FLU',
  2946. 'FRU',
  2947. 'DFL',
  2948. 'DFR',
  2949. 'BDL',
  2950. 'BDR']
  2951. to_check = [
  2952. (self.sideU.corner_pos[0], self.sideL.corner_pos[0], self.sideB.corner_pos[1]), # ULB
  2953. (self.sideU.corner_pos[1], self.sideR.corner_pos[1], self.sideB.corner_pos[0]), # URB
  2954. (self.sideU.corner_pos[2], self.sideL.corner_pos[1], self.sideF.corner_pos[0]), # ULF
  2955. (self.sideU.corner_pos[3], self.sideF.corner_pos[1], self.sideR.corner_pos[0]), # UFR
  2956. (self.sideD.corner_pos[0], self.sideL.corner_pos[3], self.sideF.corner_pos[2]), # DLF
  2957. (self.sideD.corner_pos[1], self.sideF.corner_pos[3], self.sideR.corner_pos[2]), # DFR
  2958. (self.sideD.corner_pos[2], self.sideL.corner_pos[2], self.sideB.corner_pos[3]), # DLB
  2959. (self.sideD.corner_pos[3], self.sideR.corner_pos[3], self.sideB.corner_pos[2]) # DRB
  2960. ]
  2961. current_corners = []
  2962. for (square_index1, square_index2, square_index3) in to_check:
  2963. square1 = self.state[square_index1]
  2964. square2 = self.state[square_index2]
  2965. square3 = self.state[square_index3]
  2966. corner_str = ''.join(sorted([square1, square2, square3]))
  2967. current_corners.append(corner_str)
  2968. if debug:
  2969. log.info("to_check:\n%s" % pformat(to_check))
  2970. to_check_str = ''
  2971. for (a, b, c) in to_check:
  2972. to_check_str += "%4s" % a
  2973. log.info("to_check :%s" % to_check_str)
  2974. log.info("needed corners : %s" % ' '.join(needed_corners))
  2975. log.info("currnet corners: %s" % ' '.join(current_corners))
  2976. log.info("")
  2977. return get_swap_count(needed_corners, current_corners, debug)
  2978. def corner_swaps_even(self, debug=False):
  2979. if self.get_corner_swap_count(debug) % 2 == 0:
  2980. return True
  2981. return False
  2982. def corner_swaps_odd(self, debug=False):
  2983. if self.get_corner_swap_count(debug) % 2 == 1:
  2984. return True
  2985. return False
  2986. def get_edge_swap_count(self, edges_paired, orbit, debug=False):
  2987. needed_edges = []
  2988. to_check = []
  2989. # should not happen
  2990. if edges_paired and orbit is not None:
  2991. raise Exception("edges_paired is True and orbit is %s" % orbit)
  2992. edges_per_side = len(self.sideU.edge_north_pos)
  2993. # Upper
  2994. for (edge_index, square_index) in enumerate(self.sideU.edge_north_pos):
  2995. if edges_paired:
  2996. to_check.append(square_index)
  2997. needed_edges.append('UB')
  2998. break
  2999. else:
  3000. if orbit_matches(edges_per_side, orbit, edge_index):
  3001. to_check.append(square_index)
  3002. needed_edges.append('UB%d' % edge_index)
  3003. for (edge_index, square_index) in enumerate(reversed(self.sideU.edge_west_pos)):
  3004. if edges_paired:
  3005. to_check.append(square_index)
  3006. needed_edges.append('UL')
  3007. break
  3008. else:
  3009. if orbit_matches(edges_per_side, orbit, edge_index):
  3010. to_check.append(square_index)
  3011. needed_edges.append('UL%d' % edge_index)
  3012. for (edge_index, square_index) in enumerate(reversed(self.sideU.edge_south_pos)):
  3013. if edges_paired:
  3014. to_check.append(square_index)
  3015. needed_edges.append('UF')
  3016. break
  3017. else:
  3018. if orbit_matches(edges_per_side, orbit, edge_index):
  3019. to_check.append(square_index)
  3020. needed_edges.append('UF%d' % edge_index)
  3021. for (edge_index, square_index) in enumerate(self.sideU.edge_east_pos):
  3022. if edges_paired:
  3023. to_check.append(square_index)
  3024. needed_edges.append('UR')
  3025. break
  3026. else:
  3027. if orbit_matches(edges_per_side, orbit, edge_index):
  3028. to_check.append(square_index)
  3029. needed_edges.append('UR%d' % edge_index)
  3030. # Left
  3031. for (edge_index, square_index) in enumerate(reversed(self.sideL.edge_west_pos)):
  3032. if edges_paired:
  3033. to_check.append(square_index)
  3034. needed_edges.append('LB')
  3035. break
  3036. else:
  3037. if orbit_matches(edges_per_side, orbit, edge_index):
  3038. to_check.append(square_index)
  3039. needed_edges.append('LB%d' % edge_index)
  3040. for (edge_index, square_index) in enumerate(self.sideL.edge_east_pos):
  3041. if edges_paired:
  3042. to_check.append(square_index)
  3043. needed_edges.append('LF')
  3044. break
  3045. else:
  3046. if orbit_matches(edges_per_side, orbit, edge_index):
  3047. to_check.append(square_index)
  3048. needed_edges.append('LF%d' % edge_index)
  3049. # Right
  3050. for (edge_index, square_index) in enumerate(reversed(self.sideR.edge_west_pos)):
  3051. if edges_paired:
  3052. to_check.append(square_index)
  3053. needed_edges.append('RF')
  3054. break
  3055. else:
  3056. if orbit_matches(edges_per_side, orbit, edge_index):
  3057. to_check.append(square_index)
  3058. needed_edges.append('RF%d' % edge_index)
  3059. for (edge_index, square_index) in enumerate(self.sideR.edge_east_pos):
  3060. if edges_paired:
  3061. to_check.append(square_index)
  3062. needed_edges.append('RB')
  3063. break
  3064. else:
  3065. if orbit_matches(edges_per_side, orbit, edge_index):
  3066. to_check.append(square_index)
  3067. needed_edges.append('RB%d' % edge_index)
  3068. # Down
  3069. for (edge_index, square_index) in enumerate(self.sideD.edge_north_pos):
  3070. if edges_paired:
  3071. to_check.append(square_index)
  3072. needed_edges.append('DF')
  3073. break
  3074. else:
  3075. if orbit_matches(edges_per_side, orbit, edge_index):
  3076. to_check.append(square_index)
  3077. needed_edges.append('DF%d' % edge_index)
  3078. for (edge_index, square_index) in enumerate(reversed(self.sideD.edge_west_pos)):
  3079. if edges_paired:
  3080. to_check.append(square_index)
  3081. needed_edges.append('DL')
  3082. break
  3083. else:
  3084. if orbit_matches(edges_per_side, orbit, edge_index):
  3085. to_check.append(square_index)
  3086. needed_edges.append('DL%d' % edge_index)
  3087. for (edge_index, square_index) in enumerate(reversed(self.sideD.edge_south_pos)):
  3088. if edges_paired:
  3089. to_check.append(square_index)
  3090. needed_edges.append('DB')
  3091. break
  3092. else:
  3093. if orbit_matches(edges_per_side, orbit, edge_index):
  3094. to_check.append(square_index)
  3095. needed_edges.append('DB%d' % edge_index)
  3096. for (edge_index, square_index) in enumerate(self.sideD.edge_east_pos):
  3097. if edges_paired:
  3098. to_check.append(square_index)
  3099. needed_edges.append('DR')
  3100. break
  3101. else:
  3102. if orbit_matches(edges_per_side, orbit, edge_index):
  3103. to_check.append(square_index)
  3104. needed_edges.append('DR%d' % edge_index)
  3105. if debug:
  3106. to_check_str = ''
  3107. for x in to_check:
  3108. if edges_paired:
  3109. to_check_str += "%3s" % x
  3110. else:
  3111. to_check_str += "%4s" % x
  3112. log.info("to_check :%s" % to_check_str)
  3113. log.info("needed edges : %s" % ' '.join(needed_edges))
  3114. current_edges = []
  3115. for square_index in to_check:
  3116. side = self.get_side_for_index(square_index)
  3117. partner_index = side.get_wing_partner(square_index)
  3118. square1 = self.state[square_index]
  3119. square2 = self.state[partner_index]
  3120. if square1 in ('U', 'D'):
  3121. wing_str = square1 + square2
  3122. elif square2 in ('U', 'D'):
  3123. wing_str = square2 + square1
  3124. elif square1 in ('L', 'R'):
  3125. wing_str = square1 + square2
  3126. elif square2 in ('L', 'R'):
  3127. wing_str = square2 + square1
  3128. elif (square1, square2) == ('x', 'x'):
  3129. continue
  3130. else:
  3131. raise Exception("Could not determine wing_str for (%s, %s)" % (square1, square2))
  3132. if not edges_paired:
  3133. # - backup the current state
  3134. # - add an 'x' to the end of the square_index/partner_index
  3135. # - move square_index/partner_index to its final edge location
  3136. # - look for the 'x' to determine if this is the '0' vs '1' wing
  3137. # - restore the original state
  3138. square1_with_x = square1 + 'x'
  3139. square2_with_x = square2 + 'x'
  3140. original_state = self.state[:]
  3141. original_solution = self.solution[:]
  3142. self.state[square_index] = square1_with_x
  3143. self.state[partner_index] = square2_with_x
  3144. # 'UB0', 'UB1', 'UL0', 'UL1', 'UF0', 'UF1', 'UR0', 'UR1',
  3145. # 'LB0', 'LB1', 'LF0', 'LF1', 'RF0', 'RF1', 'RB0', 'RB1',
  3146. # 'DF0', 'DF1', 'DL0', 'DL1', 'DB0', 'DB1', 'DR0', 'DR1
  3147. if wing_str == 'UB':
  3148. self.move_wing_to_U_north(square_index)
  3149. edge_to_check = self.sideU.edge_north_pos
  3150. target_side = self.sideU
  3151. elif wing_str == 'UL':
  3152. self.move_wing_to_U_west(square_index)
  3153. edge_to_check = reversed(self.sideU.edge_west_pos)
  3154. target_side = self.sideU
  3155. elif wing_str == 'UF':
  3156. self.move_wing_to_U_south(square_index)
  3157. edge_to_check = reversed(self.sideU.edge_south_pos)
  3158. target_side = self.sideU
  3159. elif wing_str == 'UR':
  3160. self.move_wing_to_U_east(square_index)
  3161. edge_to_check = self.sideU.edge_east_pos
  3162. target_side = self.sideU
  3163. elif wing_str == 'LB':
  3164. self.move_wing_to_L_west(square_index)
  3165. edge_to_check = reversed(self.sideL.edge_west_pos)
  3166. target_side = self.sideL
  3167. elif wing_str == 'LF':
  3168. self.move_wing_to_L_east(square_index)
  3169. edge_to_check = self.sideL.edge_east_pos
  3170. target_side = self.sideL
  3171. elif wing_str == 'RF':
  3172. self.move_wing_to_R_west(square_index)
  3173. edge_to_check = reversed(self.sideR.edge_west_pos)
  3174. target_side = self.sideR
  3175. elif wing_str == 'RB':
  3176. self.move_wing_to_R_east(square_index)
  3177. edge_to_check = self.sideR.edge_east_pos
  3178. target_side = self.sideR
  3179. elif wing_str == 'DF':
  3180. self.move_wing_to_D_north(square_index)
  3181. edge_to_check = self.sideD.edge_north_pos
  3182. target_side = self.sideD
  3183. elif wing_str == 'DL':
  3184. self.move_wing_to_D_west(square_index)
  3185. edge_to_check = reversed(self.sideD.edge_west_pos)
  3186. target_side = self.sideD
  3187. elif wing_str == 'DB':
  3188. self.move_wing_to_D_south(square_index)
  3189. edge_to_check = reversed(self.sideD.edge_south_pos)
  3190. target_side = self.sideD
  3191. elif wing_str == 'DR':
  3192. self.move_wing_to_D_east(square_index)
  3193. edge_to_check = self.sideD.edge_east_pos
  3194. target_side = self.sideD
  3195. else:
  3196. raise SolveError("invalid wing %s" % wing_str)
  3197. for (edge_index, wing_index) in enumerate(edge_to_check):
  3198. wing_value = self.state[wing_index]
  3199. if wing_value.endswith('x'):
  3200. if wing_value.startswith(target_side.name):
  3201. wing_str += str(edge_index)
  3202. else:
  3203. max_edge_index = len(target_side.edge_east_pos) - 1
  3204. wing_str += str(max_edge_index - edge_index)
  3205. # This is commented out because we used this once upon a time to generate the
  3206. # orbit_index_444, etc dictionaries in https://github.com/dwalton76/rubiks-color-resolver
  3207. #
  3208. # The workflow was:
  3209. # - tweak code to call center_solution_leads_to_oll_parity() for odd cubes too
  3210. # - solve 500 cubes via "./utils/test.py --test-cubes utils/test_cubes.json --size 7x7x7"
  3211. # - sort /tmp/orbit_index.txt > /tmp/orbit_index_sorted.txt
  3212. # - cat /tmp/orbit_index_sorted.txt | uniq > /tmp/orbit_index_sorted_uniq.txt
  3213. #
  3214. # The lines in /tmp/orbit_index_sorted_uniq.txt are used to create orbit_index_777
  3215. '''
  3216. with open('/tmp/orbit_index.txt', 'a') as fh:
  3217. fh.write(" (%d, %d, '%s', '%s') : '%s',\n" % (square_index, partner_index, square1, square2, wing_str))
  3218. fh.write(" (%d, %d, '%s', '%s') : '%s',\n" % (partner_index, square_index, square2, square1, wing_str))
  3219. '''
  3220. break
  3221. else:
  3222. raise SolveError("Could not find wing %s (%d, %d) among %s" % (wing_str, square_index, partner_index, str(edge_to_check)))
  3223. self.state = original_state[:]
  3224. self.solution = original_solution[:]
  3225. current_edges.append(wing_str)
  3226. if debug:
  3227. log.info("current edges: %s" % ' '.join(current_edges))
  3228. return get_swap_count(needed_edges, current_edges, debug)
  3229. def edge_swaps_even(self, edges_paired, orbit, debug):
  3230. if self.get_edge_swap_count(edges_paired, orbit, debug) % 2 == 0:
  3231. return True
  3232. return False
  3233. def edge_swaps_odd(self, edges_paired, orbit, debug):
  3234. if self.get_edge_swap_count(edges_paired, orbit, debug) % 2 == 1:
  3235. return True
  3236. return False
  3237. def edge_solution_leads_to_pll_parity(self, debug=False):
  3238. self.rotate_U_to_U()
  3239. self.rotate_F_to_F()
  3240. if self.edge_swaps_even(edges_paired=True, orbit=None, debug=debug) == self.corner_swaps_even(debug):
  3241. if debug:
  3242. log.info("Predict we are free of PLL parity")
  3243. return False
  3244. if debug:
  3245. log.info("Predict we have PLL parity")
  3246. return True
  3247. def center_solution_leads_to_oll_parity(self, debug=False):
  3248. """
  3249. http://www.speedcubing.com/chris/4speedsolve3.html
  3250. http://www.rubik.rthost.org/4x4x4_edges.htm
  3251. """
  3252. if self.centers_solved():
  3253. self.rotate_U_to_U()
  3254. self.rotate_F_to_F()
  3255. orbits_with_oll_parity = []
  3256. orbits = int((self.size - 2) / 2)
  3257. for orbit in range(orbits):
  3258. # OLL Parity - "...is caused by solving the centers such that the edge permutation is odd"
  3259. # http://www.speedcubing.com/chris/4speedsolve3.html
  3260. if self.edge_swaps_odd(False, orbit, debug):
  3261. orbits_with_oll_parity.append(orbit)
  3262. #log.info("orbit %d has OLL parity" % orbit)
  3263. if not orbits_with_oll_parity:
  3264. log.debug("Predict we are free of OLL parity")
  3265. return orbits_with_oll_parity
  3266. def get_state_all(self):
  3267. result = []
  3268. for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD):
  3269. for square_index in range(side.min_pos, side.max_pos + 1):
  3270. result.append(self.state[square_index])
  3271. return ''.join(result)
  3272. def group_centers_guts(self):
  3273. raise ImplementThis("Child class must implement group_centers_guts")
  3274. def group_centers(self):
  3275. if self.is_odd():
  3276. self.rotate_U_to_U()
  3277. self.rotate_F_to_F()
  3278. if self.centers_solved():
  3279. self.rotate_U_to_U()
  3280. self.rotate_F_to_F()
  3281. log.info("group center solution: centers are already solved")
  3282. else:
  3283. log.info("")
  3284. log.info("")
  3285. log.info("")
  3286. self.group_centers_guts()
  3287. log.info("group center solution (%d steps in)" % (self.get_solution_len_minus_rotates(self.solution)))
  3288. if self.prevent_OLL():
  3289. log.info("prevented OLL (%d steps in)" % (self.get_solution_len_minus_rotates(self.solution)))
  3290. self.solution.append('CENTERS_SOLVED')
  3291. def get_wing_value(self, wing):
  3292. if isinstance(wing, tuple) or isinstance(wing, list):
  3293. square_index = wing[0]
  3294. else:
  3295. square_index = wing
  3296. side = self.get_side_for_index(square_index)
  3297. partner_index = side.get_wing_partner(square_index)
  3298. if square_index < partner_index:
  3299. return (self.state[square_index], self.state[partner_index])
  3300. else:
  3301. return (self.state[partner_index], self.state[square_index])
  3302. def get_solution_len_minus_rotates(self, solution):
  3303. count = 0
  3304. size_str = str(self.size)
  3305. for step in solution:
  3306. if step in ('CENTERS_SOLVED', 'EDGES_GROUPED'):
  3307. continue
  3308. if step in ("x", "x'", "x2",
  3309. "y", "y'", "y2",
  3310. "z", "z'", "z2"):
  3311. continue
  3312. if not step.startswith(size_str):
  3313. count += 1
  3314. return count
  3315. def compress_solution(self):
  3316. solution_string = []
  3317. for step in self.solution:
  3318. if step == "x":
  3319. step = "%dR" % self.size
  3320. elif step == "x'":
  3321. step = "%dR'" % self.size
  3322. elif step == "y":
  3323. step = "%dU" % self.size
  3324. elif step == "y'":
  3325. step = "%dU'" % self.size
  3326. elif step == "z":
  3327. step = "%dF" % self.size
  3328. elif step == "z'":
  3329. step = "%dF'" % self.size
  3330. solution_string.append(step)
  3331. moves = set(solution_string)
  3332. solution_string = ' '.join(solution_string)
  3333. while True:
  3334. original_solution_string = solution_string[:]
  3335. for move in moves:
  3336. if move in ('CENTERS_SOLVED', 'EDGES_GROUPED'):
  3337. continue
  3338. if move in ('x', 'y', 'z'):
  3339. raise Exception('compress_solution does not support move "%s"' % move)
  3340. if move.endswith("2'"):
  3341. raise Exception('compress_solution does not support move "%s"' % move)
  3342. if move.endswith("'"):
  3343. reverse_move = move[0:-1]
  3344. else:
  3345. reverse_move = move + "'"
  3346. # If the same half turn is done 2x in a row, remove it
  3347. if move.endswith("2"):
  3348. solution_string = solution_string.replace(" %s %s " % (move, move), " ")
  3349. else:
  3350. # If the same quarter turn is done 4x in a row, remove it
  3351. solution_string = solution_string.replace(" %s %s %s %s " % (move, move, move, move), " ")
  3352. # If the same quarter turn is done 3x in a row, replace it with one backwards move
  3353. solution_string = solution_string.replace(" %s %s %s " % (move, move, move), " %s " % reverse_move)
  3354. # If the same quarter turn is done 2x in a row, replace it with one half turn
  3355. # Do not bother doing this with whole cube rotations we will pull those out later
  3356. if not move.startswith(str(self.size)):
  3357. if move.endswith("'"):
  3358. solution_string = solution_string.replace(" %s %s " % (move, move), " %s2 " % move[0:-1])
  3359. else:
  3360. solution_string = solution_string.replace(" %s %s " % (move, move), " %s2 " % move)
  3361. # "F F'" and "F' F" will cancel each other out, remove them
  3362. solution_string = solution_string.replace(" %s %s " % (move, reverse_move), " ")
  3363. solution_string = solution_string.replace(" %s %s " % (reverse_move, move), " ")
  3364. if original_solution_string == solution_string:
  3365. break
  3366. # Remove full cube rotations by changing all of the steps that follow the cube rotation
  3367. steps = solution_string.strip().split()
  3368. final_steps = []
  3369. rotations = []
  3370. for (index, step) in enumerate(steps):
  3371. if step.startswith(str(self.size)):
  3372. rotations.append(apply_rotations(self.size, step, rotations))
  3373. else:
  3374. final_steps.append(apply_rotations(self.size, step, rotations))
  3375. solution_string = ' '.join(final_steps)
  3376. # We put some markers in the solution to track how many steps
  3377. # each stage took...remove those markers
  3378. solution_minus_markers = []
  3379. self.steps_to_rotate_cube = 0
  3380. self.steps_to_solve_centers = 0
  3381. self.steps_to_group_edges = 0
  3382. self.steps_to_solve_3x3x3 = 0
  3383. index = 0
  3384. # log.info("pre compress; %s" % ' '.join(self.solution))
  3385. for step in solution_string.split():
  3386. if step.startswith(str(self.size)):
  3387. self.steps_to_rotate_cube += 1
  3388. if step == 'CENTERS_SOLVED':
  3389. self.steps_to_solve_centers = index
  3390. index = 0
  3391. elif step == 'EDGES_GROUPED':
  3392. self.steps_to_group_edges = index
  3393. index = 0
  3394. else:
  3395. solution_minus_markers.append(step)
  3396. index += 1
  3397. self.steps_to_solve_3x3x3 = index
  3398. self.solution = solution_minus_markers
  3399. def solve(self):
  3400. """
  3401. The RubiksCube222 and RubiksCube333 child classes will override
  3402. this since they don't need to group centers or edges
  3403. """
  3404. solved_string = 'U' * self.squares_per_side +\
  3405. 'L' * self.squares_per_side +\
  3406. 'F' * self.squares_per_side +\
  3407. 'R' * self.squares_per_side +\
  3408. 'B' * self.squares_per_side +\
  3409. 'D' * self.squares_per_side
  3410. if self.get_state_all() != solved_string:
  3411. self.group_centers()
  3412. self.group_edges()
  3413. self.rotate_U_to_U()
  3414. self.rotate_F_to_F()
  3415. self.solve_333()
  3416. self.compress_solution()
  3417. # Cube is solved, rotate it around so white is on top, etc
  3418. #self.rotate_U_to_U()
  3419. #self.rotate_F_to_F()
  3420. def print_solution(self):
  3421. # Print an alg.cubing.net URL for this setup/solution
  3422. url = "https://alg.cubing.net/?puzzle=%dx%dx%d&setup=" % (self.size, self.size, self.size)
  3423. url += '_'.join(reverse_steps(self.solution))
  3424. url += '&alg='
  3425. url += '_'.join(self.solution)
  3426. url = url.replace("'", "-")
  3427. print("\nURL : %s" % url)
  3428. print("\nSolution: %s" % ' '.join(self.solution))
  3429. if self.steps_to_rotate_cube:
  3430. print(("%d steps to rotate entire cube" % self.steps_to_rotate_cube))
  3431. if self.steps_to_solve_centers:
  3432. print(("%d steps to solve centers" % self.steps_to_solve_centers))
  3433. if self.steps_to_group_edges:
  3434. print(("%d steps to group edges" % self.steps_to_group_edges))
  3435. if self.steps_to_solve_3x3x3:
  3436. print(("%d steps to solve 3x3x3" % self.steps_to_solve_3x3x3))
  3437. print(("%d steps total" % len(self.solution)))
  3438. def edge_string_to_find(self, target_wing, sister_wing1, sister_wing2, sister_wing3):
  3439. edge_pos_square_index = (2, 3, 4, 6, 10, 11, 15, 16, 20, 22, 23, 24, 27,
  3440. 28, 29, 31, 35, 36, 40, 41, 45, 47, 48, 49, 52, 53, 54, 56, 60, 61,
  3441. 65, 66, 70, 72, 73, 74, 77, 78, 79, 81, 85, 86, 90, 91, 95, 97, 98,
  3442. 99, 102, 103, 104, 106, 110, 111, 115, 116, 120, 122, 123, 124, 127,
  3443. 128, 129, 131, 135, 136, 140, 141, 145, 147, 148, 149)
  3444. foo = {
  3445. target_wing[0] : 'A',
  3446. target_wing[1] : 'B',
  3447. sister_wing1[0] : 'C',
  3448. sister_wing1[1] : 'D',
  3449. sister_wing2[0] : 'E',
  3450. sister_wing2[1] : 'F',
  3451. sister_wing3[0] : 'G',
  3452. sister_wing3[1] : 'H',
  3453. }
  3454. return ''.join([foo.get(square_index, 'x') for square_index in edge_pos_square_index])
  3455. def nuke_corners(self):
  3456. for side in list(self.sides.values()):
  3457. for square_index in side.corner_pos:
  3458. self.state[square_index] = 'x'
  3459. def nuke_centers(self):
  3460. for side in list(self.sides.values()):
  3461. for square_index in side.center_pos:
  3462. self.state[square_index] = 'x'
  3463. def nuke_edges(self):
  3464. for side in list(self.sides.values()):
  3465. for square_index in side.edge_pos:
  3466. self.state[square_index] = 'x'
  3467. def www_header(self):
  3468. """
  3469. Write the <head> including css
  3470. """
  3471. side_margin = 10
  3472. square_size = 40
  3473. size = self.size # 3 for 3x3x3, etc
  3474. shutil.copy('www/solution.js', '/tmp/')
  3475. shutil.copy('www/Arrow-Next.png', '/tmp/')
  3476. shutil.copy('www/Arrow-Prev.png', '/tmp/')
  3477. with open('/tmp/solution.html', 'w') as fh:
  3478. fh.write("""<!DOCTYPE html>
  3479. <html>
  3480. <head>
  3481. <meta charset="UTF-8">
  3482. <style>
  3483. div.clear {
  3484. clear: both;
  3485. }
  3486. div.clear_left {
  3487. clear: left;
  3488. }
  3489. .clickable {
  3490. cursor: pointer;
  3491. }
  3492. div.side {
  3493. margin: %dpx;
  3494. float: left;
  3495. }
  3496. a.prev_page {
  3497. float: left;
  3498. }
  3499. a.next_page {
  3500. float: right;
  3501. }
  3502. """ % side_margin)
  3503. for x in range(1, size-1):
  3504. fh.write("div.col%d,\n" % x)
  3505. fh.write("""div.col%d {
  3506. float: left;
  3507. }
  3508. div.col%d {
  3509. margin-left: %dpx;
  3510. }
  3511. div#upper,
  3512. div#down {
  3513. margin-left: %dpx;
  3514. }
  3515. """ % (size-1,
  3516. size,
  3517. (size - 1) * square_size,
  3518. (size * square_size) + (3 * side_margin)))
  3519. fh.write("""
  3520. span.square {
  3521. width: %dpx;
  3522. height: %dpx;
  3523. white-space-collapsing: discard;
  3524. display: inline-block;
  3525. color: black;
  3526. font-weight: bold;
  3527. line-height: %dpx;
  3528. text-align: center;
  3529. }
  3530. div.square {
  3531. width: %dpx;
  3532. height: %dpx;
  3533. color: black;
  3534. font-weight: bold;
  3535. line-height: %dpx;
  3536. text-align: center;
  3537. }
  3538. div.square span {
  3539. display: inline-block;
  3540. vertical-align: middle;
  3541. line-height: normal;
  3542. }
  3543. div.page {
  3544. display: none;
  3545. }
  3546. div#page_holder {
  3547. width: %dpx;
  3548. }
  3549. </style>
  3550. <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
  3551. <script type='text/javascript' src='http://code.jquery.com/ui/1.10.3/jquery-ui.min.js'></script>
  3552. <script type="text/javascript" src="solution.js"></script>
  3553. <title>CraneCuber</title>
  3554. </head>
  3555. <body>
  3556. <div id="page_holder">
  3557. """ % (square_size, square_size, square_size, square_size, square_size, square_size,
  3558. (square_size * size * 4) + square_size + (4 * side_margin)))
  3559. def www_write_cube(self, desc):
  3560. """
  3561. 'cube' is a list of (R,G,B) tuples
  3562. """
  3563. cube = ['dummy',]
  3564. for square in self.state[1:]:
  3565. cube.append(self.color_map_html[square])
  3566. col = 1
  3567. squares_per_side = self.size * self.size
  3568. max_square = squares_per_side * 6
  3569. sides = ('upper', 'left', 'front', 'right', 'back', 'down')
  3570. side_index = -1
  3571. (first_squares, last_squares, last_UBD_squares) = get_important_square_indexes(self.size)
  3572. with open('/tmp/solution.html', 'a') as fh:
  3573. fh.write("<div class='page' style='display: none;'>\n")
  3574. fh.write("<h1>%s</h1>\n" % desc)
  3575. for index in range(1, max_square + 1):
  3576. if index in first_squares:
  3577. side_index += 1
  3578. fh.write("<div class='side' id='%s'>\n" % sides[side_index])
  3579. (red, green, blue) = cube[index]
  3580. fh.write(" <div class='square col%d' title='RGB (%d, %d, %d)' style='background-color: #%02x%02x%02x;'><span>%02d</span></div>\n" %
  3581. (col,
  3582. red, green, blue,
  3583. red, green, blue,
  3584. index))
  3585. if index in last_squares:
  3586. fh.write("</div>\n")
  3587. if index in last_UBD_squares:
  3588. fh.write("<div class='clear'></div>\n")
  3589. col += 1
  3590. if col == self.size + 1:
  3591. col = 1
  3592. fh.write("</div>\n")
  3593. def www_footer(self):
  3594. with open('/tmp/solution.html', 'a') as fh:
  3595. fh.write("""
  3596. <div id="sets-browse-controls">
  3597. <a class="prev_page" style="display: block;"><img src="Arrow-Prev.png" class="clickable" width="128"></a>
  3598. <a class="next_page" style="display: block;"><img src="Arrow-Next.png" class="clickable" width="128"></a>
  3599. </div>
  3600. </div>
  3601. </body>
  3602. </html>
  3603. """)
  3604. def test(self):
  3605. """
  3606. Run some tests to sanity check some things
  3607. """
  3608. for side in list(self.sides.values()):
  3609. for square_index in range(side.min_pos, side.max_pos+1):
  3610. self.state[square_index] = 'x'
  3611. original_state = self.state[:]
  3612. original_solution = self.solution[:]
  3613. for side in list(self.sides.values()):
  3614. for direction in ('north', 'south', 'east', 'west'):
  3615. if direction == 'north':
  3616. edges = side.edge_north_pos
  3617. elif direction == 'south':
  3618. edges = side.edge_south_pos
  3619. elif direction == 'east':
  3620. edges = side.edge_east_pos
  3621. elif direction == 'west':
  3622. edges = side.edge_west_pos
  3623. for edge_index in edges:
  3624. partner_index = side.get_wing_partner(edge_index)
  3625. self.state[edge_index] = 'U'
  3626. self.state[partner_index] = 'U'
  3627. tmp_state = self.state[:]
  3628. log.info("test move_wing for %s-%s (%d, %d)" % (side, direction, edge_index, partner_index))
  3629. # U-north
  3630. for index_to_test in (edge_index, partner_index):
  3631. self.move_wing_to_U_north(index_to_test)
  3632. for tmp_edge_index in self.sideU.edge_north_pos:
  3633. if self.state[tmp_edge_index] == 'U':
  3634. break
  3635. else:
  3636. raise SolveError("move_wing_to_U_north failed")
  3637. self.state[:] = tmp_state
  3638. # U-south
  3639. for index_to_test in (edge_index, partner_index):
  3640. self.move_wing_to_U_south(index_to_test)
  3641. for tmp_edge_index in self.sideU.edge_south_pos:
  3642. if self.state[tmp_edge_index] == 'U':
  3643. break
  3644. else:
  3645. raise SolveError("move_wing_to_U_south failed")
  3646. self.state[:] = tmp_state
  3647. # U-east
  3648. for index_to_test in (edge_index, partner_index):
  3649. self.move_wing_to_U_east(index_to_test)
  3650. for tmp_edge_index in self.sideU.edge_east_pos:
  3651. if self.state[tmp_edge_index] == 'U':
  3652. break
  3653. else:
  3654. raise SolveError("move_wing_to_U_east failed")
  3655. self.state[:] = tmp_state
  3656. # U-west
  3657. for index_to_test in (edge_index, partner_index):
  3658. self.move_wing_to_U_west(index_to_test)
  3659. for tmp_edge_index in self.sideU.edge_west_pos:
  3660. if self.state[tmp_edge_index] == 'U':
  3661. break
  3662. else:
  3663. raise SolveError("move_wing_to_U_west failed")
  3664. self.state[:] = tmp_state
  3665. # L-north
  3666. '''
  3667. for index_to_test in (edge_index, partner_index):
  3668. self.move_wing_to_L_north(index_to_test)
  3669. for tmp_edge_index in self.sideL.edge_north_pos:
  3670. if self.state[tmp_edge_index] == 'U':
  3671. break
  3672. else:
  3673. raise SolveError("move_wing_to_L_north failed")
  3674. self.state[:] = tmp_state
  3675. '''
  3676. # L-south
  3677. '''
  3678. for index_to_test in (edge_index, partner_index):
  3679. self.move_wing_to_L_south(index_to_test)
  3680. for tmp_edge_index in self.sideL.edge_south_pos:
  3681. if self.state[tmp_edge_index] == 'U':
  3682. break
  3683. else:
  3684. raise SolveError("move_wing_to_L_south failed")
  3685. self.state[:] = tmp_state
  3686. '''
  3687. # L-east
  3688. for index_to_test in (edge_index, partner_index):
  3689. self.move_wing_to_L_east(index_to_test)
  3690. for tmp_edge_index in self.sideL.edge_east_pos:
  3691. if self.state[tmp_edge_index] == 'U':
  3692. break
  3693. else:
  3694. raise SolveError("move_wing_to_L_east failed for %d" % index_to_test)
  3695. self.state[:] = tmp_state
  3696. # L-west
  3697. for index_to_test in (edge_index, partner_index):
  3698. self.move_wing_to_L_west(index_to_test)
  3699. for tmp_edge_index in self.sideL.edge_west_pos:
  3700. if self.state[tmp_edge_index] == 'U':
  3701. break
  3702. else:
  3703. raise SolveError("move_wing_to_L_west failed")
  3704. self.state[:] = tmp_state
  3705. # F-north
  3706. '''
  3707. for index_to_test in (edge_index, partner_index):
  3708. self.move_wing_to_F_north(index_to_test)
  3709. for tmp_edge_index in self.sideF.edge_north_pos:
  3710. if self.state[tmp_edge_index] == 'U':
  3711. break
  3712. else:
  3713. raise SolveError("move_wing_to_F_north failed")
  3714. self.state[:] = tmp_state
  3715. '''
  3716. # F-south
  3717. '''
  3718. for index_to_test in (edge_index, partner_index):
  3719. self.move_wing_to_F_south(index_to_test)
  3720. for tmp_edge_index in self.sideF.edge_south_pos:
  3721. if self.state[tmp_edge_index] == 'U':
  3722. break
  3723. else:
  3724. raise SolveError("move_wing_to_F_south failed")
  3725. self.state[:] = tmp_state
  3726. '''
  3727. # F-east
  3728. for index_to_test in (edge_index, partner_index):
  3729. self.move_wing_to_F_east(index_to_test)
  3730. for tmp_edge_index in self.sideF.edge_east_pos:
  3731. if self.state[tmp_edge_index] == 'U':
  3732. break
  3733. else:
  3734. raise SolveError("move_wing_to_F_east failed")
  3735. self.state[:] = tmp_state
  3736. # F-west
  3737. for index_to_test in (edge_index, partner_index):
  3738. self.move_wing_to_F_west(index_to_test)
  3739. for tmp_edge_index in self.sideF.edge_west_pos:
  3740. if self.state[tmp_edge_index] == 'U':
  3741. break
  3742. else:
  3743. raise SolveError("move_wing_to_F_west failed")
  3744. self.state[:] = tmp_state
  3745. # R-north
  3746. '''
  3747. for index_to_test in (edge_index, partner_index):
  3748. self.move_wing_to_R_north(index_to_test)
  3749. for tmp_edge_index in self.sideR.edge_north_pos:
  3750. if self.state[tmp_edge_index] == 'U':
  3751. break
  3752. else:
  3753. raise SolveError("move_wing_to_R_north failed")
  3754. self.state[:] = tmp_state
  3755. '''
  3756. # R-south
  3757. '''
  3758. for index_to_test in (edge_index, partner_index):
  3759. self.move_wing_to_R_south(index_to_test)
  3760. for tmp_edge_index in self.sideR.edge_south_pos:
  3761. if self.state[tmp_edge_index] == 'U':
  3762. break
  3763. else:
  3764. raise SolveError("move_wing_to_R_south failed")
  3765. self.state[:] = tmp_state
  3766. '''
  3767. # R-east
  3768. for index_to_test in (edge_index, partner_index):
  3769. self.move_wing_to_R_east(index_to_test)
  3770. for tmp_edge_index in self.sideR.edge_east_pos:
  3771. if self.state[tmp_edge_index] == 'U':
  3772. break
  3773. else:
  3774. raise SolveError("move_wing_to_R_east failed")
  3775. self.state[:] = tmp_state
  3776. # R-west
  3777. for index_to_test in (edge_index, partner_index):
  3778. self.move_wing_to_R_west(index_to_test)
  3779. for tmp_edge_index in self.sideR.edge_west_pos:
  3780. if self.state[tmp_edge_index] == 'U':
  3781. break
  3782. else:
  3783. raise SolveError("move_wing_to_R_west failed")
  3784. self.state[:] = tmp_state
  3785. # B-north
  3786. '''
  3787. for index_to_test in (edge_index, partner_index):
  3788. self.move_wing_to_B_north(index_to_test)
  3789. for tmp_edge_index in self.sideB.edge_north_pos:
  3790. if self.state[tmp_edge_index] == 'U':
  3791. break
  3792. else:
  3793. raise SolveError("move_wing_to_B_north failed")
  3794. self.state[:] = tmp_state
  3795. '''
  3796. # B-south
  3797. '''
  3798. for index_to_test in (edge_index, partner_index):
  3799. self.move_wing_to_B_south(index_to_test)
  3800. for tmp_edge_index in self.sideB.edge_south_pos:
  3801. if self.state[tmp_edge_index] == 'U':
  3802. break
  3803. else:
  3804. raise SolveError("move_wing_to_B_south failed")
  3805. self.state[:] = tmp_state
  3806. '''
  3807. # B-east
  3808. '''
  3809. for index_to_test in (edge_index, partner_index):
  3810. self.move_wing_to_B_east(index_to_test)
  3811. for tmp_edge_index in self.sideB.edge_east_pos:
  3812. if self.state[tmp_edge_index] == 'U':
  3813. break
  3814. else:
  3815. raise SolveError("move_wing_to_B_east failed")
  3816. self.state[:] = tmp_state
  3817. '''
  3818. # B-west
  3819. '''
  3820. for index_to_test in (edge_index, partner_index):
  3821. self.move_wing_to_B_west(index_to_test)
  3822. for tmp_edge_index in self.sideB.edge_west_pos:
  3823. if self.state[tmp_edge_index] == 'U':
  3824. break
  3825. else:
  3826. raise SolveError("move_wing_to_B_west failed")
  3827. self.state[:] = tmp_state
  3828. '''
  3829. # D-north
  3830. for index_to_test in (edge_index, partner_index):
  3831. self.move_wing_to_D_north(index_to_test)
  3832. for tmp_edge_index in self.sideD.edge_north_pos:
  3833. if self.state[tmp_edge_index] == 'U':
  3834. break
  3835. else:
  3836. raise SolveError("move_wing_to_D_north failed")
  3837. self.state[:] = tmp_state
  3838. # D-south
  3839. for index_to_test in (edge_index, partner_index):
  3840. self.move_wing_to_D_south(index_to_test)
  3841. for tmp_edge_index in self.sideD.edge_south_pos:
  3842. if self.state[tmp_edge_index] == 'U':
  3843. break
  3844. else:
  3845. raise SolveError("move_wing_to_D_south failed")
  3846. self.state[:] = tmp_state
  3847. # D-east
  3848. for index_to_test in (edge_index, partner_index):
  3849. self.move_wing_to_D_east(index_to_test)
  3850. for tmp_edge_index in self.sideD.edge_east_pos:
  3851. if self.state[tmp_edge_index] == 'U':
  3852. break
  3853. else:
  3854. raise SolveError("move_wing_to_D_east failed")
  3855. self.state[:] = tmp_state
  3856. # D-west
  3857. for index_to_test in (edge_index, partner_index):
  3858. self.move_wing_to_D_west(index_to_test)
  3859. for tmp_edge_index in self.sideD.edge_west_pos:
  3860. if self.state[tmp_edge_index] == 'U':
  3861. break
  3862. else:
  3863. raise SolveError("move_wing_to_D_west failed")
  3864. self.state[:] = tmp_state
  3865. self.state[:] = original_state
  3866. self.state[:] = original_state
  3867. def rotate_to_side(self, upper_side_name, front_side_name):
  3868. if upper_side_name == front_side_name:
  3869. return False
  3870. if upper_side_name == 'U':
  3871. if front_side_name == 'D':
  3872. return False
  3873. if front_side_name == 'L':
  3874. self.rotate_y_reverse()
  3875. elif front_side_name == 'F':
  3876. pass
  3877. elif front_side_name == 'R':
  3878. self.rotate_y()
  3879. elif front_side_name == 'B':
  3880. self.rotate_y()
  3881. self.rotate_y()
  3882. elif upper_side_name == 'D':
  3883. if front_side_name == 'U':
  3884. return False
  3885. self.rotate_x()
  3886. self.rotate_x()
  3887. if front_side_name == 'L':
  3888. self.rotate_y_reverse()
  3889. elif front_side_name == 'F':
  3890. self.rotate_y()
  3891. self.rotate_y()
  3892. elif front_side_name == 'R':
  3893. self.rotate_y()
  3894. elif front_side_name == 'B':
  3895. pass
  3896. elif upper_side_name == 'L':
  3897. if front_side_name == 'R':
  3898. return False
  3899. self.rotate_y_reverse()
  3900. self.rotate_x()
  3901. # dwalton
  3902. if front_side_name == 'U':
  3903. self.rotate_y()
  3904. self.rotate_y()
  3905. elif front_side_name == 'F':
  3906. self.rotate_y()
  3907. elif front_side_name == 'D':
  3908. pass
  3909. elif front_side_name == 'B':
  3910. self.rotate_y_reverse()
  3911. elif upper_side_name == 'F':
  3912. if front_side_name == 'B':
  3913. return False
  3914. self.rotate_x()
  3915. if front_side_name == 'L':
  3916. self.rotate_y_reverse()
  3917. elif front_side_name == 'U':
  3918. self.rotate_y()
  3919. self.rotate_y()
  3920. elif front_side_name == 'R':
  3921. self.rotate_y()
  3922. elif front_side_name == 'D':
  3923. pass
  3924. elif upper_side_name == 'R':
  3925. if front_side_name == 'L':
  3926. return False
  3927. self.rotate_y()
  3928. self.rotate_x()
  3929. if front_side_name == 'U':
  3930. self.rotate_y()
  3931. self.rotate_y()
  3932. elif front_side_name == 'F':
  3933. self.rotate_y_reverse()
  3934. elif front_side_name == 'D':
  3935. pass
  3936. elif front_side_name == 'B':
  3937. self.rotate_y()
  3938. elif upper_side_name == 'B':
  3939. if front_side_name == 'F':
  3940. return False
  3941. self.rotate_x_reverse()
  3942. if front_side_name == 'L':
  3943. self.rotate_y_reverse()
  3944. elif front_side_name == 'U':
  3945. pass
  3946. elif front_side_name == 'R':
  3947. self.rotate_y()
  3948. elif front_side_name == 'D':
  3949. self.rotate_y()
  3950. self.rotate_y()
  3951. return True
  3952. def transform_x(self):
  3953. for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD):
  3954. for square_index in range(side.min_pos, side.max_pos+1):
  3955. if self.state[square_index] == 'U':
  3956. self.state[square_index] = 'B'
  3957. elif self.state[square_index] == 'L':
  3958. pass
  3959. elif self.state[square_index] == 'F':
  3960. self.state[square_index] = 'U'
  3961. elif self.state[square_index] == 'R':
  3962. pass
  3963. elif self.state[square_index] == 'B':
  3964. self.state[square_index] = 'D'
  3965. elif self.state[square_index] == 'D':
  3966. self.state[square_index] = 'F'
  3967. def transform_x_prime(self):
  3968. for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD):
  3969. for square_index in range(side.min_pos, side.max_pos+1):
  3970. if self.state[square_index] == 'U':
  3971. self.state[square_index] = 'F'
  3972. elif self.state[square_index] == 'L':
  3973. pass
  3974. elif self.state[square_index] == 'F':
  3975. self.state[square_index] = 'D'
  3976. elif self.state[square_index] == 'R':
  3977. pass
  3978. elif self.state[square_index] == 'B':
  3979. self.state[square_index] = 'U'
  3980. elif self.state[square_index] == 'D':
  3981. self.state[square_index] = 'B'
  3982. def transform_y(self):
  3983. for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD):
  3984. for square_index in range(side.min_pos, side.max_pos+1):
  3985. if self.state[square_index] == 'U':
  3986. pass
  3987. elif self.state[square_index] == 'L':
  3988. self.state[square_index] = 'B'
  3989. elif self.state[square_index] == 'F':
  3990. self.state[square_index] = 'L'
  3991. elif self.state[square_index] == 'R':
  3992. self.state[square_index] = 'F'
  3993. elif self.state[square_index] == 'B':
  3994. self.state[square_index] = 'R'
  3995. elif self.state[square_index] == 'D':
  3996. pass
  3997. def transform_y_prime(self):
  3998. for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD):
  3999. for square_index in range(side.min_pos, side.max_pos+1):
  4000. if self.state[square_index] == 'U':
  4001. pass
  4002. elif self.state[square_index] == 'L':
  4003. self.state[square_index] = 'F'
  4004. elif self.state[square_index] == 'F':
  4005. self.state[square_index] = 'R'
  4006. elif self.state[square_index] == 'R':
  4007. self.state[square_index] = 'B'
  4008. elif self.state[square_index] == 'B':
  4009. self.state[square_index] = 'L'
  4010. elif self.state[square_index] == 'D':
  4011. pass
  4012. def transform_z(self):
  4013. for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD):
  4014. for square_index in range(side.min_pos, side.max_pos+1):
  4015. if self.state[square_index] == 'U':
  4016. self.state[square_index] = 'R'
  4017. elif self.state[square_index] == 'L':
  4018. self.state[square_index] = 'U'
  4019. elif self.state[square_index] == 'F':
  4020. pass
  4021. elif self.state[square_index] == 'R':
  4022. self.state[square_index] = 'D'
  4023. elif self.state[square_index] == 'B':
  4024. pass
  4025. elif self.state[square_index] == 'D':
  4026. self.state[square_index] = 'L'
  4027. def transform_z_prime(self):
  4028. for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD):
  4029. for square_index in range(side.min_pos, side.max_pos+1):
  4030. if self.state[square_index] == 'U':
  4031. self.state[square_index] = 'L'
  4032. elif self.state[square_index] == 'L':
  4033. self.state[square_index] = 'D'
  4034. elif self.state[square_index] == 'F':
  4035. pass
  4036. elif self.state[square_index] == 'R':
  4037. self.state[square_index] = 'U'
  4038. elif self.state[square_index] == 'B':
  4039. pass
  4040. elif self.state[square_index] == 'D':
  4041. self.state[square_index] = 'R'
  4042. def transform(self, target):
  4043. """
  4044. This should cover every scenario:
  4045. rotations = (
  4046. (),
  4047. ("y",),
  4048. ("y'",),
  4049. ("y", "y"),
  4050. ("x", "x", "y"),
  4051. ("x", "x", "y'"),
  4052. ("x", "x", "y", "y"),
  4053. ("y'", "x", "y"),
  4054. ("y'", "x", "y'"),
  4055. ("y'", "x", "y", "y"),
  4056. ("x", "y"),
  4057. ("x", "y'"),
  4058. ("x", "y", "y"),
  4059. ("y", "x", "y"),
  4060. ("y", "x", "y'"),
  4061. ("y", "x", "y", "y"),
  4062. ("x'", "y"),
  4063. ("x'", "y'"),
  4064. ("x'", "y", "y")
  4065. )
  4066. """
  4067. if not target:
  4068. pass
  4069. elif target == "x":
  4070. self.transform_x()
  4071. elif target == "x'":
  4072. self.transform_x_prime()
  4073. elif target == "y":
  4074. self.transform_y()
  4075. elif target == "y'":
  4076. self.transform_y_prime()
  4077. elif target == "z":
  4078. self.transform_z()
  4079. elif target == "z'":
  4080. self.transform_z_prime()
  4081. else:
  4082. raise Exception("Implement target %s" % target)