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

RubiksCube444.py 90KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443
  1. from copy import copy
  2. from rubikscubennnsolver import RubiksCube
  3. from rubikscubennnsolver.RubiksSide import SolveError
  4. from rubikscubennnsolver.RubiksCube444Misc import (
  5. lookup_table_444_last_two_edges_place_F_east,
  6. lookup_table_444_sister_wing_to_F_east,
  7. lookup_table_444_sister_wing_to_U_west,
  8. tsai_phase2_orient_edges,
  9. tsai_edge_mapping_combinations,
  10. )
  11. from rubikscubennnsolver.LookupTable import (
  12. LookupTable,
  13. LookupTableIDA,
  14. LookupTableAStar,
  15. NoSteps,
  16. NoIDASolution,
  17. )
  18. from rubikscubennnsolver.rotate_xxx import rotate_444
  19. from subprocess import check_output
  20. from pprint import pformat
  21. import itertools
  22. import logging
  23. import sys
  24. log = logging.getLogger(__name__)
  25. moves_4x4x4 = ("U", "U'", "U2", "Uw", "Uw'", "Uw2",
  26. "L", "L'", "L2", "Lw", "Lw'", "Lw2",
  27. "F" , "F'", "F2", "Fw", "Fw'", "Fw2",
  28. "R" , "R'", "R2", "Rw", "Rw'", "Rw2",
  29. "B" , "B'", "B2", "Bw", "Bw'", "Bw2",
  30. "D" , "D'", "D2", "Dw", "Dw'", "Dw2")
  31. solved_4x4x4 = 'UUUUUUUUUUUUUUUURRRRRRRRRRRRRRRRFFFFFFFFFFFFFFFFDDDDDDDDDDDDDDDDLLLLLLLLLLLLLLLLBBBBBBBBBBBBBBBB'
  32. '''
  33. lookup-table-4x4x4-step11-UD-centers-stage.txt
  34. lookup-table-4x4x4-step12-LR-centers-stage.txt
  35. lookup-table-4x4x4-step13-FB-centers-stage.txt
  36. ==============================================
  37. 1 steps has 5 entries (0 percent, 0.00x previous step)
  38. 2 steps has 82 entries (0 percent, 16.40x previous step)
  39. 3 steps has 1,206 entries (0 percent, 14.71x previous step)
  40. 4 steps has 14,116 entries (1 percent, 11.70x previous step)
  41. 5 steps has 123,404 entries (16 percent, 8.74x previous step)
  42. 6 steps has 422,508 entries (57 percent, 3.42x previous step)
  43. 7 steps has 173,254 entries (23 percent, 0.41x previous step)
  44. 8 steps has 896 entries (0 percent, 0.01x previous step)
  45. Total: 735,471 entries
  46. '''
  47. class LookupTable444UDCentersStage(LookupTable):
  48. def __init__(self, parent):
  49. LookupTable.__init__(
  50. self,
  51. parent,
  52. 'lookup-table-4x4x4-step11-UD-centers-stage.txt',
  53. 'f0000f',
  54. linecount=735471)
  55. def state(self):
  56. parent_state = self.parent.state
  57. result = [
  58. # Upper
  59. parent_state[6],
  60. parent_state[7],
  61. parent_state[10],
  62. parent_state[11],
  63. # Left
  64. parent_state[22],
  65. parent_state[23],
  66. parent_state[26],
  67. parent_state[27],
  68. # Front
  69. parent_state[38],
  70. parent_state[39],
  71. parent_state[42],
  72. parent_state[43],
  73. # Right
  74. parent_state[54],
  75. parent_state[55],
  76. parent_state[58],
  77. parent_state[59],
  78. # Back
  79. parent_state[70],
  80. parent_state[71],
  81. parent_state[74],
  82. parent_state[75],
  83. # Down
  84. parent_state[86],
  85. parent_state[87],
  86. parent_state[90],
  87. parent_state[91]
  88. ]
  89. result = ['1' if x in ('U', 'D') else '0' for x in result]
  90. result = ''.join(result)
  91. # Convert to hex
  92. return self.hex_format % int(result, 2)
  93. class LookupTable444LRCentersStage(LookupTable):
  94. def __init__(self, parent):
  95. LookupTable.__init__(
  96. self,
  97. parent,
  98. 'lookup-table-4x4x4-step12-LR-centers-stage.txt',
  99. '0f0f00',
  100. linecount=735471)
  101. def state(self):
  102. parent_state = self.parent.state
  103. result = [
  104. # Upper
  105. parent_state[6],
  106. parent_state[7],
  107. parent_state[10],
  108. parent_state[11],
  109. # Left
  110. parent_state[22],
  111. parent_state[23],
  112. parent_state[26],
  113. parent_state[27],
  114. # Front
  115. parent_state[38],
  116. parent_state[39],
  117. parent_state[42],
  118. parent_state[43],
  119. # Right
  120. parent_state[54],
  121. parent_state[55],
  122. parent_state[58],
  123. parent_state[59],
  124. # Back
  125. parent_state[70],
  126. parent_state[71],
  127. parent_state[74],
  128. parent_state[75],
  129. # Down
  130. parent_state[86],
  131. parent_state[87],
  132. parent_state[90],
  133. parent_state[91]
  134. ]
  135. result = ['1' if x in ('L', 'R') else '0' for x in result]
  136. result = ''.join(result)
  137. # Convert to hex
  138. return self.hex_format % int(result, 2)
  139. class LookupTable444FBCentersStage(LookupTable):
  140. def __init__(self, parent):
  141. LookupTable.__init__(
  142. self,
  143. parent,
  144. 'lookup-table-4x4x4-step13-FB-centers-stage.txt',
  145. '00f0f0',
  146. linecount=735471)
  147. def state(self):
  148. parent_state = self.parent.state
  149. result = [
  150. # Upper
  151. parent_state[6],
  152. parent_state[7],
  153. parent_state[10],
  154. parent_state[11],
  155. # Left
  156. parent_state[22],
  157. parent_state[23],
  158. parent_state[26],
  159. parent_state[27],
  160. # Front
  161. parent_state[38],
  162. parent_state[39],
  163. parent_state[42],
  164. parent_state[43],
  165. # Right
  166. parent_state[54],
  167. parent_state[55],
  168. parent_state[58],
  169. parent_state[59],
  170. # Back
  171. parent_state[70],
  172. parent_state[71],
  173. parent_state[74],
  174. parent_state[75],
  175. # Down
  176. parent_state[86],
  177. parent_state[87],
  178. parent_state[90],
  179. parent_state[91]
  180. ]
  181. result = ['1' if x in ('F', 'B') else '0' for x in result]
  182. result = ''.join(result)
  183. # Convert to hex
  184. return self.hex_format % int(result, 2)
  185. class LookupTableIDA444ULFRBDCentersStage(LookupTableIDA):
  186. """
  187. lookup-table-4x4x4-step10-ULFRBD-centers-stage.txt
  188. ==================================================
  189. 1 steps has 7 entries (0 percent, 0.00x previous step)
  190. 2 steps has 135 entries (0 percent, 19.29x previous step)
  191. 3 steps has 2,286 entries (0 percent, 16.93x previous step)
  192. 4 steps has 36,728 entries (0 percent, 16.07x previous step)
  193. 5 steps has 562,932 entries (6 percent, 15.33x previous step)
  194. 6 steps has 8,047,054 entries (93 percent, 14.29x previous step)
  195. Total: 8,649,142 entries
  196. """
  197. def __init__(self, parent):
  198. LookupTableIDA.__init__(
  199. self,
  200. parent,
  201. 'lookup-table-4x4x4-step10-ULFRBD-centers-stage.txt',
  202. 'UUUULLLLFFFFLLLLFFFFUUUU',
  203. moves_4x4x4,
  204. (), # illegal_moves
  205. # prune tables
  206. (parent.lt_UD_centers_stage,
  207. parent.lt_LR_centers_stage,
  208. parent.lt_FB_centers_stage),
  209. linecount=8649142)
  210. def state(self):
  211. parent_state = self.parent.state
  212. result = [
  213. # Upper
  214. parent_state[6],
  215. parent_state[7],
  216. parent_state[10],
  217. parent_state[11],
  218. # Left
  219. parent_state[22],
  220. parent_state[23],
  221. parent_state[26],
  222. parent_state[27],
  223. # Front
  224. parent_state[38],
  225. parent_state[39],
  226. parent_state[42],
  227. parent_state[43],
  228. # Right
  229. parent_state[54],
  230. parent_state[55],
  231. parent_state[58],
  232. parent_state[59],
  233. # Back
  234. parent_state[70],
  235. parent_state[71],
  236. parent_state[74],
  237. parent_state[75],
  238. # Down
  239. parent_state[86],
  240. parent_state[87],
  241. parent_state[90],
  242. parent_state[91]
  243. ]
  244. tmp_result = []
  245. for x in result:
  246. if x in ('L', 'F', 'U'):
  247. tmp_result.append(x)
  248. elif x == 'R':
  249. tmp_result.append('L')
  250. elif x == 'B':
  251. tmp_result.append('F')
  252. elif x == 'D':
  253. tmp_result.append('U')
  254. result = ''.join(tmp_result)
  255. return result
  256. class LookupTable444LFRBCentersStage(LookupTable):
  257. """
  258. lookup-table-4x4x4-step20-LFRB-centers-stage.txt
  259. ================================================
  260. 1 steps has 3 entries (0 percent, 0.00x previous step)
  261. 2 steps has 29 entries (0 percent, 9.67x previous step)
  262. 3 steps has 234 entries (1 percent, 8.07x previous step)
  263. 4 steps has 1246 entries (9 percent, 5.32x previous step)
  264. 5 steps has 4466 entries (34 percent, 3.58x previous step)
  265. 6 steps has 6236 entries (48 percent, 1.40x previous step)
  266. 7 steps has 656 entries (5 percent, 0.11x previous step)
  267. Total: 12870 entries
  268. """
  269. def __init__(self, parent):
  270. LookupTable.__init__(
  271. self,
  272. parent,
  273. 'lookup-table-4x4x4-step20-LFRB-centers-stage.txt',
  274. 'xxxxLLLLFFFFLLLLFFFFxxxx',
  275. linecount=12870)
  276. def state(self):
  277. parent_state = self.parent.state
  278. result = [
  279. # Upper
  280. parent_state[6],
  281. parent_state[7],
  282. parent_state[10],
  283. parent_state[11],
  284. # Left
  285. parent_state[22],
  286. parent_state[23],
  287. parent_state[26],
  288. parent_state[27],
  289. # Front
  290. parent_state[38],
  291. parent_state[39],
  292. parent_state[42],
  293. parent_state[43],
  294. # Right
  295. parent_state[54],
  296. parent_state[55],
  297. parent_state[58],
  298. parent_state[59],
  299. # Back
  300. parent_state[70],
  301. parent_state[71],
  302. parent_state[74],
  303. parent_state[75],
  304. # Down
  305. parent_state[86],
  306. parent_state[87],
  307. parent_state[90],
  308. parent_state[91]
  309. ]
  310. tmp_result = []
  311. for x in result:
  312. if x in ('U', 'D'):
  313. tmp_result.append('x')
  314. elif x == 'R':
  315. tmp_result.append('L')
  316. elif x == 'B':
  317. tmp_result.append('F')
  318. else:
  319. tmp_result.append(x)
  320. result = ''.join(tmp_result)
  321. return result
  322. class LookupTable444ULFRBDCentersSolve(LookupTable):
  323. """
  324. lookup-table-4x4x4-step30-ULFRBD-centers-solve.txt
  325. ==================================================
  326. 1 steps has 7 entries (0 percent)
  327. 2 steps has 99 entries (0 percent)
  328. 3 steps has 996 entries (0 percent)
  329. 4 steps has 6,477 entries (1 percent)
  330. 5 steps has 23,540 entries (6 percent)
  331. 6 steps has 53,537 entries (15 percent)
  332. 7 steps has 86,464 entries (25 percent)
  333. 8 steps has 83,240 entries (24 percent)
  334. 9 steps has 54,592 entries (15 percent)
  335. 10 steps has 29,568 entries (8 percent)
  336. 11 steps has 4,480 entries (1 percent)
  337. Total: 343,000 entries
  338. """
  339. def __init__(self, parent):
  340. LookupTable.__init__(
  341. self,
  342. parent,
  343. 'lookup-table-4x4x4-step30-ULFRBD-centers-solve.txt',
  344. 'UUUULLLLFFFFRRRRBBBBDDDD',
  345. linecount=343000)
  346. def state(self):
  347. parent_state = self.parent.state
  348. result = [
  349. # Upper
  350. parent_state[6],
  351. parent_state[7],
  352. parent_state[10],
  353. parent_state[11],
  354. # Left
  355. parent_state[22],
  356. parent_state[23],
  357. parent_state[26],
  358. parent_state[27],
  359. # Front
  360. parent_state[38],
  361. parent_state[39],
  362. parent_state[42],
  363. parent_state[43],
  364. # Right
  365. parent_state[54],
  366. parent_state[55],
  367. parent_state[58],
  368. parent_state[59],
  369. # Back
  370. parent_state[70],
  371. parent_state[71],
  372. parent_state[74],
  373. parent_state[75],
  374. # Down
  375. parent_state[86],
  376. parent_state[87],
  377. parent_state[90],
  378. parent_state[91]
  379. ]
  380. result = ''.join(result)
  381. return result
  382. class LookupTable444TsaiPhase2Centers(LookupTable):
  383. """
  384. lookup-table-4x4x4-step61-centers.txt
  385. =====================================
  386. 1 steps has 46 entries (0 percent, 0.00x previous step)
  387. 2 steps has 384 entries (0 percent, 8.35x previous step)
  388. 3 steps has 3,354 entries (0 percent, 8.73x previous step)
  389. 4 steps has 22,324 entries (2 percent, 6.66x previous step)
  390. 5 steps has 113,276 entries (12 percent, 5.07x previous step)
  391. 6 steps has 338,860 entries (37 percent, 2.99x previous step)
  392. 7 steps has 388,352 entries (43 percent, 1.15x previous step)
  393. 8 steps has 34,048 entries (3 percent, 0.09x previous step)
  394. 9 steps has 256 entries (0 percent, 0.01x previous step)
  395. Total: 900,900 entries
  396. """
  397. def __init__(self, parent):
  398. LookupTable.__init__(
  399. self,
  400. parent,
  401. 'lookup-table-4x4x4-step61-centers.txt',
  402. ('UUUULLLLFFFFRRRRFFFFUUUU',
  403. 'UUUURRRRFFFFLLLLFFFFUUUU',
  404. 'UUUULLRRFFFFRRLLFFFFUUUU',
  405. 'UUUULLRRFFFFLLRRFFFFUUUU',
  406. 'UUUURRLLFFFFRRLLFFFFUUUU',
  407. 'UUUURRLLFFFFLLRRFFFFUUUU',
  408. 'UUUURLRLFFFFRLRLFFFFUUUU',
  409. 'UUUURLRLFFFFLRLRFFFFUUUU',
  410. 'UUUULRLRFFFFRLRLFFFFUUUU',
  411. 'UUUULRLRFFFFLRLRFFFFUUUU',
  412. 'UUUURLLRFFFFLRRLFFFFUUUU',
  413. 'UUUULRRLFFFFRLLRFFFFUUUU'),
  414. linecount=900900)
  415. def state(self):
  416. babel = {
  417. 'L' : 'L',
  418. 'F' : 'F',
  419. 'R' : 'R',
  420. 'B' : 'F',
  421. 'D' : 'U',
  422. 'U' : 'U',
  423. }
  424. parent_state = self.parent.state
  425. result = [
  426. # Upper
  427. babel[parent_state[6]],
  428. babel[parent_state[7]],
  429. babel[parent_state[10]],
  430. babel[parent_state[11]],
  431. # Left
  432. babel[parent_state[22]],
  433. babel[parent_state[23]],
  434. babel[parent_state[26]],
  435. babel[parent_state[27]],
  436. # Front
  437. babel[parent_state[38]],
  438. babel[parent_state[39]],
  439. babel[parent_state[42]],
  440. babel[parent_state[43]],
  441. # Right
  442. babel[parent_state[54]],
  443. babel[parent_state[55]],
  444. babel[parent_state[58]],
  445. babel[parent_state[59]],
  446. # Back
  447. babel[parent_state[70]],
  448. babel[parent_state[71]],
  449. babel[parent_state[74]],
  450. babel[parent_state[75]],
  451. # Down
  452. babel[parent_state[86]],
  453. babel[parent_state[87]],
  454. babel[parent_state[90]],
  455. babel[parent_state[91]]
  456. ]
  457. result = ''.join(result)
  458. return result
  459. class LookupTable444TsaiPhase2Edges(LookupTable):
  460. """
  461. This is an experiment that needs more work....it is not used
  462. lookup-table-4x4x4-step62-edges.txt
  463. ===================================
  464. 1 steps has 5 entries (0 percent, 0.00x previous step)
  465. 2 steps has 62 entries (0 percent, 12.40x previous step)
  466. 3 steps has 906 entries (0 percent, 14.61x previous step)
  467. 4 steps has 11,163 entries (0 percent, 12.32x previous step)
  468. 5 steps has 127,148 entries (4 percent, 11.39x previous step)
  469. 6 steps has 889,398 entries (32 percent, 6.99x previous step)
  470. 7 steps has 1,553,434 entries (57 percent, 1.75x previous step)
  471. 8 steps has 122,040 entries (4 percent, 0.08x previous step)
  472. Total: 2,704,156 entries
  473. """
  474. def __init__(self, parent):
  475. LookupTable.__init__(
  476. self,
  477. parent,
  478. 'lookup-table-4x4x4-step62-edges.txt',
  479. 'UDDUUDDUDUDUUDUDDUUDDUUDDUDUUDUDDUUDDUUDUDDUUDDU',
  480. linecount=2704156)
  481. def state(self):
  482. '''
  483. centers_cost = self.parent.lt_tsai_phase2_centers.steps_cost()
  484. min_edges_cost = None
  485. min_edges_state = None
  486. for uu_dd_count in tsai_edge_mapping_combinations.keys():
  487. if uu_dd_count <= 4:
  488. for edges_to_flip in tsai_edge_mapping_combinations[uu_dd_count]:
  489. edges_state = self.parent.tsai_phase2_orient_edges_state(edges_to_flip)
  490. edges_cost = self.steps_cost(edges_state)
  491. if edges_cost <= centers_cost:
  492. return edges_state
  493. if min_edges_cost is None or edges_cost < min_edges_cost:
  494. min_edges_cost = edges_cost
  495. min_edges_state = edges_state
  496. return min_edges_state
  497. '''
  498. edges_state = self.parent.tsai_phase2_orient_edges_state(set())
  499. #edges_cost = self.steps_cost(edges_state)
  500. return edges_state
  501. class LookupTableIDA444TsaiPhase2(LookupTableIDA):
  502. """
  503. lookup-table-4x4x4-step60.txt
  504. =============================
  505. 1 steps has 60 entries (0 percent, 0.00x previous step)
  506. 2 steps has 744 entries (0 percent, 12.40x previous step)
  507. 3 steps has 11,224 entries (0 percent, 15.09x previous step)
  508. 4 steps has 158,608 entries (6 percent, 14.13x previous step)
  509. 5 steps has 2,349,908 entries (93 percent, 14.82x previous step)
  510. Total: 2,520,544 entries
  511. """
  512. def __init__(self, parent):
  513. LookupTableIDA.__init__(
  514. self,
  515. parent,
  516. #'lookup-table-4x4x4-step60-dummy.txt',
  517. 'lookup-table-4x4x4-step60.txt',
  518. 'UDDUUUUUUDDUDUDLLUULLDUDDUUFFDDFFUUDDUDRRUURRDUDDUUFFDDFFUUDUDDUUUUUUDDU',
  519. moves_4x4x4,
  520. ("Fw", "Fw'", "Bw", "Bw'",
  521. "Uw", "Uw'", "Dw", "Dw'"), # illegal_moves
  522. # prune tables
  523. #(parent.lt_tsai_phase2_centers,),
  524. (parent.lt_tsai_phase2_centers,
  525. parent.lt_tsai_phase2_edges),
  526. linecount=2520544)
  527. def state(self):
  528. babel = {
  529. 'L' : 'L',
  530. 'F' : 'F',
  531. 'R' : 'R',
  532. 'B' : 'F',
  533. 'D' : 'U',
  534. 'U' : 'U',
  535. }
  536. orient_edges = tsai_phase2_orient_edges
  537. parent_state = self.parent.state
  538. result = [
  539. # Upper
  540. orient_edges[(2, 67, parent_state[2], parent_state[67])],
  541. orient_edges[(3, 66, parent_state[3], parent_state[66])],
  542. orient_edges[(5, 18, parent_state[5], parent_state[18])],
  543. babel[parent_state[6]],
  544. babel[parent_state[7]],
  545. orient_edges[(8, 51, parent_state[8], parent_state[51])],
  546. orient_edges[(9, 19, parent_state[9], parent_state[19])],
  547. babel[parent_state[10]],
  548. babel[parent_state[11]],
  549. orient_edges[(12, 50, parent_state[12], parent_state[50])],
  550. orient_edges[(14, 34, parent_state[14], parent_state[34])],
  551. orient_edges[(15, 35, parent_state[15], parent_state[35])],
  552. # Left
  553. orient_edges[(18, 5, parent_state[18], parent_state[5])],
  554. orient_edges[(19, 9, parent_state[19], parent_state[9])],
  555. orient_edges[(21, 72, parent_state[21], parent_state[72])],
  556. babel[parent_state[22]],
  557. babel[parent_state[23]],
  558. orient_edges[(24, 37, parent_state[24], parent_state[37])],
  559. orient_edges[(25, 76, parent_state[25], parent_state[76])],
  560. babel[parent_state[26]],
  561. babel[parent_state[27]],
  562. orient_edges[(28, 41, parent_state[28], parent_state[41])],
  563. orient_edges[(30, 89, parent_state[30], parent_state[89])],
  564. orient_edges[(31, 85, parent_state[31], parent_state[85])],
  565. # Front
  566. orient_edges[(34, 14, parent_state[34], parent_state[14])],
  567. orient_edges[(35, 15, parent_state[35], parent_state[15])],
  568. orient_edges[(37, 24, parent_state[37], parent_state[24])],
  569. babel[parent_state[38]],
  570. babel[parent_state[39]],
  571. orient_edges[(40, 53, parent_state[40], parent_state[53])],
  572. orient_edges[(41, 28, parent_state[41], parent_state[28])],
  573. babel[parent_state[42]],
  574. babel[parent_state[43]],
  575. orient_edges[(44, 57, parent_state[44], parent_state[57])],
  576. orient_edges[(46, 82, parent_state[46], parent_state[82])],
  577. orient_edges[(47, 83, parent_state[47], parent_state[83])],
  578. # Right
  579. orient_edges[(50, 12, parent_state[50], parent_state[12])],
  580. orient_edges[(51, 8, parent_state[51], parent_state[8])],
  581. orient_edges[(53, 40, parent_state[53], parent_state[40])],
  582. babel[parent_state[54]],
  583. babel[parent_state[55]],
  584. orient_edges[(56, 69, parent_state[56], parent_state[69])],
  585. orient_edges[(57, 44, parent_state[57], parent_state[44])],
  586. babel[parent_state[58]],
  587. babel[parent_state[59]],
  588. orient_edges[(60, 73, parent_state[60], parent_state[73])],
  589. orient_edges[(62, 88, parent_state[62], parent_state[88])],
  590. orient_edges[(63, 92, parent_state[63], parent_state[92])],
  591. # Back
  592. orient_edges[(66, 3, parent_state[66], parent_state[3])],
  593. orient_edges[(67, 2, parent_state[67], parent_state[2])],
  594. orient_edges[(69, 56, parent_state[69], parent_state[56])],
  595. babel[parent_state[70]],
  596. babel[parent_state[71]],
  597. orient_edges[(72, 21, parent_state[72], parent_state[21])],
  598. orient_edges[(73, 60, parent_state[73], parent_state[60])],
  599. babel[parent_state[74]],
  600. babel[parent_state[75]],
  601. orient_edges[(76, 25, parent_state[76], parent_state[25])],
  602. orient_edges[(78, 95, parent_state[78], parent_state[95])],
  603. orient_edges[(79, 94, parent_state[79], parent_state[94])],
  604. # Down
  605. orient_edges[(82, 46, parent_state[82], parent_state[46])],
  606. orient_edges[(83, 47, parent_state[83], parent_state[47])],
  607. orient_edges[(85, 31, parent_state[85], parent_state[31])],
  608. babel[parent_state[86]],
  609. babel[parent_state[87]],
  610. orient_edges[(88, 62, parent_state[88], parent_state[62])],
  611. orient_edges[(89, 30, parent_state[89], parent_state[30])],
  612. babel[parent_state[90]],
  613. babel[parent_state[91]],
  614. orient_edges[(92, 63, parent_state[92], parent_state[63])],
  615. orient_edges[(94, 79, parent_state[94], parent_state[79])],
  616. orient_edges[(95, 78, parent_state[95], parent_state[78])]
  617. ]
  618. result = ''.join(result)
  619. return result
  620. def experimental_ida_search_complete(self, state, steps_to_here):
  621. # Are UD and FB staged?
  622. for side in (self.parent.sideU, self.parent.sideD):
  623. for square_index in side.center_pos:
  624. if self.parent.state[square_index] not in ('U', 'D'):
  625. return False
  626. # Are the LR sides in 1 of the 12 states we want?
  627. LR_center_targets = set(('LLLLRRRR',
  628. 'RRRRLLLL',
  629. 'LLRRRRLL',
  630. 'LLRRLLRR',
  631. 'RRLLRRLL',
  632. 'RRLLLLRR',
  633. 'RLRLRLRL',
  634. 'RLRLLRLR',
  635. 'LRLRRLRL',
  636. 'LRLRLRLR',
  637. 'RLLRLRRL',
  638. 'LRRLRLLR'))
  639. LR_centers = []
  640. for side in (self.parent.sideL, self.parent.sideR):
  641. for square_index in side.center_pos:
  642. LR_centers.append(self.parent.state[square_index])
  643. LR_centers = ''.join(LR_centers)
  644. if LR_centers not in LR_center_targets:
  645. return False
  646. # If we are here then our centers are all good...check the edges.
  647. # If the edges are not in lt_tsai_phase3_edges_solve it may throw a KeyError
  648. try:
  649. if self.parent.lt_tsai_phase3_edges_solve.steps() is not None and self.parent.edge_swaps_even(False, None, False):
  650. # rotate_xxx() is very fast but it does not append the
  651. # steps to the solution so put the cube back in original state
  652. # and execute the steps via a normal rotate() call
  653. self.parent.state = self.original_state[:]
  654. self.parent.solution = self.original_solution[:]
  655. for step in steps_to_here:
  656. self.parent.rotate(step)
  657. return True
  658. except KeyError:
  659. pass
  660. return False
  661. symmetry_rotations_tsai_phase3_444 =\
  662. ("",
  663. "y y",
  664. "x",
  665. "x y y",
  666. "x'",
  667. "x' y y",
  668. "x x",
  669. "x x y y",
  670. "reflect-x",
  671. "reflect-x y y",
  672. "reflect-x x",
  673. "reflect-x x y y",
  674. "reflect-x x'",
  675. "reflect-x x' y y",
  676. "reflect-x x x",
  677. "reflect-x x x y y")
  678. # 12-23 are high edges, make these U (1)
  679. # 0-11 are low edges, make these D (6)
  680. # https://github.com/cs0x7f/TPR-4x4x4-Solver/blob/master/src/FullCube.java
  681. high_edges_444 = ((14, 2, 67), # upper
  682. (13, 9, 19),
  683. (15, 8, 51),
  684. (12, 15, 35),
  685. (21, 25, 76), # left
  686. (20, 24, 37),
  687. (23, 57, 44), # right
  688. (22, 56, 69),
  689. (18, 82, 46), # down
  690. (17, 89, 30),
  691. (19, 88, 62),
  692. (16, 95, 78))
  693. low_edges_444 = ((2, 3, 66), # upper
  694. (1, 5, 18),
  695. (3, 12, 50),
  696. (0, 14, 34),
  697. (9, 21, 72), # left
  698. (8, 28, 41),
  699. (11, 53, 40), # right
  700. (10, 60, 73),
  701. (6, 83, 47), # down
  702. (5, 85, 31),
  703. (7, 92, 63),
  704. (4, 94, 79))
  705. def edges_high_low_recolor_444(state):
  706. """
  707. Look at all of the high edges and find the low edge for each.
  708. Return a string that represents where all the low edge siblings live in relation to their high edge counterpart.
  709. """
  710. #assert len(state) == 97, "Invalid state %s, len is %d" % (state, len(state))
  711. low_edge_map = {}
  712. for (low_edge_index, square_index, partner_index) in low_edges_444:
  713. square_value = state[square_index]
  714. partner_value = state[partner_index]
  715. #assert square_value != partner_value, "both squares are %s" % square_value
  716. wing_str = ''.join(sorted([square_value, partner_value]))
  717. low_edge_index = str(hex(low_edge_index))[2:]
  718. state[square_index] = low_edge_index
  719. state[partner_index] = low_edge_index
  720. #assert wing_str not in low_edge_map, "We have two %s wings, one at high_index %s %s and one at high_index %s (%d, %d), state %s" %\
  721. # (wing_str,
  722. # low_edge_map[wing_str],
  723. # pformat(low_edges_444[int(low_edge_map[wing_str])]),
  724. # low_edge_index,
  725. # square_index, partner_index,
  726. # ''.join(state[1:]))
  727. # save low_edge_index in hex and chop the leading 0x via [2:]
  728. low_edge_map[wing_str] = low_edge_index
  729. #assert len(low_edge_map.keys()) == 12, "Invalid low_edge_map\n%s\n" % pformat(low_edge_map)
  730. for (high_edge_index, square_index, partner_index) in high_edges_444:
  731. square_value = state[square_index]
  732. partner_value = state[partner_index]
  733. wing_str = ''.join(sorted([square_value, partner_value]))
  734. state[square_index] = low_edge_map[wing_str]
  735. state[partner_index] = low_edge_map[wing_str]
  736. return state
  737. def reflect_x_444(cube):
  738. return [cube[0],
  739. cube[93], cube[94], cube[95], cube[96],
  740. cube[89], cube[90], cube[91], cube[92],
  741. cube[85], cube[86], cube[87], cube[88],
  742. cube[81], cube[82], cube[83], cube[84],
  743. cube[29], cube[30], cube[31], cube[32],
  744. cube[25], cube[26], cube[27], cube[28],
  745. cube[21], cube[22], cube[23], cube[24],
  746. cube[17], cube[18], cube[19], cube[20],
  747. cube[45], cube[46], cube[47], cube[48],
  748. cube[41], cube[42], cube[43], cube[44],
  749. cube[37], cube[38], cube[39], cube[40],
  750. cube[33], cube[34], cube[35], cube[36],
  751. cube[61], cube[62], cube[63], cube[64],
  752. cube[57], cube[58], cube[59], cube[60],
  753. cube[53], cube[54], cube[55], cube[56],
  754. cube[49], cube[50], cube[51], cube[52],
  755. cube[77], cube[78], cube[79], cube[80],
  756. cube[73], cube[74], cube[75], cube[76],
  757. cube[69], cube[70], cube[71], cube[72],
  758. cube[65], cube[66], cube[67], cube[68],
  759. cube[13], cube[14], cube[15], cube[16],
  760. cube[9], cube[10], cube[11], cube[12],
  761. cube[5], cube[6], cube[7], cube[8],
  762. cube[1], cube[2], cube[3], cube[4]]
  763. class LookupTable444TsaiPhase3Edges(LookupTable):
  764. """
  765. lookup-table-4x4x4-step71-tsai-phase3-edges.txt
  766. - without symmetry
  767. - we use the copy with symmetry I just left this here for the history
  768. ===============================================
  769. 1 steps has 4 entries (0 percent, 0.00x previous step)
  770. 2 steps has 20 entries (0 percent, 5.00x previous step)
  771. 3 steps has 140 entries (0 percent, 7.00x previous step)
  772. 4 steps has 1,141 entries (0 percent, 8.15x previous step)
  773. 5 steps has 8,059 entries (0 percent, 7.06x previous step)
  774. 6 steps has 62,188 entries (0 percent, 7.72x previous step)
  775. 7 steps has 442,293 entries (0 percent, 7.11x previous step)
  776. 8 steps has 2,958,583 entries (1 percent, 6.69x previous step)
  777. 9 steps has 17,286,512 entries (7 percent, 5.84x previous step)
  778. 10 steps has 69,004,356 entries (28 percent, 3.99x previous step)
  779. 11 steps has 122,416,936 entries (51 percent, 1.77x previous step)
  780. 12 steps has 27,298,296 entries (11 percent, 0.22x previous step)
  781. 13 steps has 22,272 entries (0 percent, 0.00x previous step)
  782. Total: 239,500,800 entries
  783. lookup-table-4x4x4-step71-tsai-phase3-edges.txt
  784. - with symmetry
  785. ===============================================
  786. 1 steps has 3 entries (0 percent, 0.00x previous step)
  787. 2 steps has 7 entries (0 percent, 2.33x previous step)
  788. 3 steps has 24 entries (0 percent, 3.43x previous step)
  789. 4 steps has 103 entries (0 percent, 4.29x previous step)
  790. 5 steps has 619 entries (0 percent, 6.01x previous step)
  791. 6 steps has 4,287 entries (0 percent, 6.93x previous step)
  792. 7 steps has 28,697 entries (0 percent, 6.69x previous step)
  793. 8 steps has 187,493 entries (1 percent, 6.53x previous step)
  794. 9 steps has 1,087,267 entries (7 percent, 5.80x previous step)
  795. 10 steps has 4,323,558 entries (28 percent, 3.98x previous step)
  796. 11 steps has 7,657,009 entries (51 percent, 1.77x previous step)
  797. 12 steps has 1,708,625 entries (11 percent, 0.22x previous step)
  798. 13 steps has 1,448 entries (0 percent, 0.00x previous step)
  799. Total: 14,999,140 entries
  800. """
  801. def __init__(self, parent):
  802. LookupTable.__init__(
  803. self,
  804. parent,
  805. 'lookup-table-4x4x4-step71-tsai-phase3-edges.txt',
  806. '213098ba6574',
  807. linecount=14999140)
  808. def state(self):
  809. parent_state = self.parent.state
  810. original_state = list('x' + ''.join(parent_state[1:]))
  811. results = []
  812. for seq in symmetry_rotations_tsai_phase3_444:
  813. state = original_state[:]
  814. for step in seq.split():
  815. if step == 'reflect-x':
  816. state = reflect_x_444(state[:])
  817. else:
  818. state = rotate_444(state[:], step)
  819. state = edges_high_low_recolor_444(state[:])
  820. # record the state of all edges
  821. state = ''.join(state)
  822. state = ''.join((state[2], state[9], state[8], state[15],
  823. state[25], state[24],
  824. state[57], state[56],
  825. state[82], state[89], state[88], state[95]))
  826. results.append(state[:])
  827. results = sorted(results)
  828. return results[0]
  829. class LookupTable444TsaiPhase3CentersSolve(LookupTable):
  830. """
  831. lookup-table-4x4x4-step72-tsai-phase3-centers.txt
  832. =================================================
  833. 1 steps has 7 entries (0 percent, 0.00x previous step)
  834. 2 steps has 83 entries (0 percent, 11.86x previous step)
  835. 3 steps has 724 entries (1 percent, 8.72x previous step)
  836. 4 steps has 3851 entries (6 percent, 5.32x previous step)
  837. 5 steps has 10,426 entries (17 percent, 2.71x previous step)
  838. 6 steps has 16,693 entries (28 percent, 1.60x previous step)
  839. 7 steps has 16,616 entries (28 percent, 1.00x previous step)
  840. 8 steps has 8,928 entries (15 percent, 0.54x previous step)
  841. 9 steps has 1,472 entries (2 percent, 0.16x previous step)
  842. Total: 58,800 entries
  843. """
  844. def __init__(self, parent):
  845. LookupTable.__init__(
  846. self,
  847. parent,
  848. 'lookup-table-4x4x4-step72-tsai-phase3-centers.txt',
  849. 'UUUULLLLFFFFRRRRBBBBDDDD',
  850. linecount=58800)
  851. def state(self):
  852. parent_state = self.parent.state
  853. result = [
  854. # Upper
  855. parent_state[6],
  856. parent_state[7],
  857. parent_state[10],
  858. parent_state[11],
  859. # Left
  860. parent_state[22],
  861. parent_state[23],
  862. parent_state[26],
  863. parent_state[27],
  864. # Front
  865. parent_state[38],
  866. parent_state[39],
  867. parent_state[42],
  868. parent_state[43],
  869. # Right
  870. parent_state[54],
  871. parent_state[55],
  872. parent_state[58],
  873. parent_state[59],
  874. # Back
  875. parent_state[70],
  876. parent_state[71],
  877. parent_state[74],
  878. parent_state[75],
  879. # Down
  880. parent_state[86],
  881. parent_state[87],
  882. parent_state[90],
  883. parent_state[91]
  884. ]
  885. result = ''.join(result)
  886. return result
  887. class LookupTableIDA444TsaiPhase3(LookupTableIDA):
  888. """
  889. If you build this to 8-deep it adds 119,166,578 which makes it too big to
  890. check into the repo
  891. lookup-table-4x4x4-step70-tsai-phase3.txt
  892. ==========================================
  893. 1 steps has 7 entries (0 percent, 0.00x previous step)
  894. 2 steps has 83 entries (0 percent, 11.86x previous step)
  895. 3 steps has 960 entries (0 percent, 11.57x previous step)
  896. 4 steps has 10,303 entries (0 percent, 10.73x previous step)
  897. 5 steps has 107,474 entries (0 percent, 10.43x previous step)
  898. 6 steps has 1,124,149 entries (8 percent, 10.46x previous step)
  899. 7 steps has 11,660,824 entries (90 percent, 10.37x previous step)
  900. Total: 12,903,800 entries
  901. """
  902. def __init__(self, parent):
  903. LookupTableIDA.__init__(
  904. self,
  905. parent,
  906. 'lookup-table-4x4x4-step70-tsai-phase3.txt',
  907. '001UU31UU322118LL98LL955229BBa9BBa4433aRRbaRRb7700bFF8bFF866445DD75DD766',
  908. moves_4x4x4,
  909. ("Fw", "Fw'", "Bw", "Bw'", "Uw", "Uw'", "Dw", "Dw'",
  910. "Rw", "Rw'", "Lw", "Lw'", "R", "R'", "L", "L'"), # illegal_moves
  911. # prune tables
  912. (parent.lt_tsai_phase3_edges_solve,
  913. parent.lt_tsai_phase3_centers_solve),
  914. linecount=12903800)
  915. def state(self):
  916. parent_state = self.parent.state
  917. original_state = edges_high_low_recolor_444(parent_state[:])
  918. results = []
  919. for seq in symmetry_rotations_tsai_phase3_444:
  920. tmp_state = original_state[:]
  921. for step in seq.split():
  922. if step == 'reflect-x':
  923. tmp_state = reflect_x_444(tmp_state[:])
  924. else:
  925. tmp_state = rotate_444(tmp_state[:], step)
  926. # record the state of all edges and centers
  927. result = \
  928. tmp_state[2:4] + tmp_state[5:13] + tmp_state[14:16] +\
  929. tmp_state[18:20] + tmp_state[21:29] + tmp_state[30:32] +\
  930. tmp_state[34:36] + tmp_state[37:45] + tmp_state[46:48] +\
  931. tmp_state[50:52] + tmp_state[53:61] + tmp_state[62:64] +\
  932. tmp_state[66:68] + tmp_state[69:77] + tmp_state[78:80] +\
  933. tmp_state[82:84] + tmp_state[85:93] + tmp_state[94:96]
  934. result = ''.join(result)
  935. results.append(result[:])
  936. results = sorted(results)
  937. return results[0]
  938. class LookupTable444EdgeSliceForward(LookupTable):
  939. """
  940. 22*20*18 is 7920
  941. lookup-table-4x4x4-step40-edges-slice-forward.txt
  942. =================================================
  943. 1 steps has 7 entries (0 percent)
  944. 2 steps has 42 entries (0 percent)
  945. 3 steps has 299 entries (3 percent)
  946. 4 steps has 1,306 entries (16 percent)
  947. 5 steps has 3,449 entries (43 percent)
  948. 6 steps has 2,617 entries (33 percent)
  949. 7 steps has 200 entries (2 percent)
  950. Total: 7,920 entries
  951. """
  952. def __init__(self, parent):
  953. LookupTable.__init__(
  954. self,
  955. parent,
  956. 'lookup-table-4x4x4-step40-edges-slice-forward.txt',
  957. 'EDGES',
  958. linecount=7920)
  959. def state(self):
  960. raise Exception("This should never be called")
  961. class LookupTable444EdgesSliceBackward(LookupTable):
  962. """
  963. 22*20*18 is 7920
  964. No idea why I am one entry short (should be 7920 total)...oh well
  965. lookup-table-4x4x4-step50-edges-slice-backward.txt
  966. ==================================================
  967. 1 steps has 1 entries (0 percent)
  968. 3 steps has 36 entries (0 percent)
  969. 4 steps has 66 entries (0 percent)
  970. 5 steps has 334 entries (4 percent)
  971. 6 steps has 1,369 entries (17 percent)
  972. 7 steps has 3,505 entries (44 percent)
  973. 8 steps has 2,539 entries (32 percent)
  974. 9 steps has 69 entries (0 percent)
  975. Total: 7,919 entries
  976. """
  977. def __init__(self, parent):
  978. LookupTable.__init__(
  979. self,
  980. parent,
  981. 'lookup-table-4x4x4-step50-edges-slice-backward.txt',
  982. 'EDGES',
  983. linecount=7919)
  984. def state(self):
  985. raise Exception("This should never be called")
  986. class BidirIDA444ULFRBDCentersStage(LookupTableIDA):
  987. """
  988. lookup-table-4x4x4-step10-ULFRBD-centers-stage.txt
  989. ==================================================
  990. 1 steps has 7 entries (0 percent, 0.00x previous step)
  991. 2 steps has 135 entries (0 percent, 19.29x previous step)
  992. 3 steps has 2,286 entries (0 percent, 16.93x previous step)
  993. 4 steps has 36,728 entries (0 percent, 16.07x previous step)
  994. 5 steps has 562,932 entries (6 percent, 15.33x previous step)
  995. 6 steps has 8,047,054 entries (93 percent, 14.29x previous step)
  996. Total: 8,649,142 entries
  997. """
  998. def __init__(self, parent):
  999. LookupTableIDA.__init__(
  1000. self,
  1001. parent,
  1002. 'lookup-table-4x4x4-step10-ULFRBD-centers-stage.txt',
  1003. 'UUUULLLLFFFFLLLLFFFFUUUU',
  1004. moves_4x4x4,
  1005. (), # illegal_moves
  1006. # prune tables
  1007. (parent.lt_UD_centers_stage,
  1008. parent.lt_LR_centers_stage,
  1009. parent.lt_FB_centers_stage),
  1010. linecount=8649142,
  1011. max_depth=6)
  1012. def state(self):
  1013. parent_state = self.parent.state
  1014. result = [
  1015. # Upper
  1016. parent_state[6],
  1017. parent_state[7],
  1018. parent_state[10],
  1019. parent_state[11],
  1020. # Left
  1021. parent_state[22],
  1022. parent_state[23],
  1023. parent_state[26],
  1024. parent_state[27],
  1025. # Front
  1026. parent_state[38],
  1027. parent_state[39],
  1028. parent_state[42],
  1029. parent_state[43],
  1030. # Right
  1031. parent_state[54],
  1032. parent_state[55],
  1033. parent_state[58],
  1034. parent_state[59],
  1035. # Back
  1036. parent_state[70],
  1037. parent_state[71],
  1038. parent_state[74],
  1039. parent_state[75],
  1040. # Down
  1041. parent_state[86],
  1042. parent_state[87],
  1043. parent_state[90],
  1044. parent_state[91]
  1045. ]
  1046. tmp_result = []
  1047. for x in result:
  1048. if x in ('L', 'F', 'U'):
  1049. tmp_result.append(x)
  1050. elif x == 'R':
  1051. tmp_result.append('L')
  1052. elif x == 'B':
  1053. tmp_result.append('F')
  1054. elif x == 'D':
  1055. tmp_result.append('U')
  1056. result = ''.join(tmp_result)
  1057. return result
  1058. class RubiksCube444(RubiksCube):
  1059. def __init__(self, state, order, colormap=None, avoid_pll=True, debug=False):
  1060. RubiksCube.__init__(self, state, order, colormap, debug)
  1061. self.avoid_pll = avoid_pll
  1062. self.edge_mapping = {}
  1063. if debug:
  1064. log.setLevel(logging.DEBUG)
  1065. def phase(self):
  1066. if self._phase is None:
  1067. self._phase = 'Stage UD centers'
  1068. return self._phase
  1069. if self._phase == 'Stage UD centers':
  1070. if self.UD_centers_staged():
  1071. self._phase = 'Stage LR centers'
  1072. return self._phase
  1073. if self._phase == 'Stage LR centers':
  1074. if self.LR_centers_staged():
  1075. self._phase = 'Solve Centers'
  1076. if self._phase == 'Solve Centers':
  1077. if self.centers_solved():
  1078. self._phase = 'Pair Edges'
  1079. if self._phase == 'Pair Edges':
  1080. if not self.get_non_paired_edges():
  1081. self._phase = 'Solve 3x3x3'
  1082. return self._phase
  1083. def sanity_check(self):
  1084. corners = (1, 4, 13, 16,
  1085. 17, 20, 29, 32,
  1086. 33, 36, 45, 48,
  1087. 49, 52, 61, 64,
  1088. 65, 68, 77, 80,
  1089. 81, 84, 93, 96)
  1090. centers = (6, 7, 10, 11,
  1091. 22, 23, 26, 27,
  1092. 38, 39, 42, 43,
  1093. 54, 55, 58, 59,
  1094. 70, 71, 74, 75,
  1095. 86, 87, 90, 91)
  1096. edge_orbit_0 = (2, 3, 8, 12, 15, 14, 9, 5,
  1097. 18, 19, 24, 28, 31, 30, 25, 21,
  1098. 34, 35, 40, 44, 47, 46, 41, 37,
  1099. 50, 51, 56, 60, 62, 63, 57, 53,
  1100. 66, 67, 72, 76, 79, 78, 73, 69,
  1101. 82, 83, 88, 92, 95, 94, 89, 85)
  1102. self._sanity_check('corners', corners, 4)
  1103. self._sanity_check('centers', centers, 4)
  1104. self._sanity_check('edge-orbit-0', edge_orbit_0, 8)
  1105. def lt_init(self):
  1106. if self.lt_init_called:
  1107. return
  1108. self.lt_init_called = True
  1109. # brainstorm
  1110. # Today we typically stage all centers and then solve them
  1111. # - Staging is 24!/(8! * 8! * 8!) or 9,465,511,770
  1112. # - We have three prune tables (UD, LR, and FB) of 24!/(8! * 16!) or 735,471
  1113. #
  1114. # option #1 - solve all centers at once
  1115. # - would be 24!/(4! * 4! * 4! * 4! * 4! * 4!) or 3,246,670,537,110,000
  1116. # - three prune tables (UD, LR, and FB) of 24!/(4! * 4! * 16!) or 51,482,970
  1117. # - 51,482,970 / 3,246,670,537,110,000 is 0.000 000 015 8571587, IDA might take a few hours
  1118. # - I've done this before and it removes ~6 steps when solving centers. We
  1119. # currently average 64 steps to solve a 4x4x4 but the tsai solver averages 55....so
  1120. # this would take a few hours to run but solutions still wouldn't be as short as
  1121. # the tsai solver.
  1122. # - feasible but not worth it
  1123. #
  1124. #
  1125. # option #2 - combine tsai phases 1 and 2
  1126. # - this would be staging UD, FB centers, solving LR centers and orienting all edges
  1127. # - orienting edges is 2,704,156
  1128. # - centers is 24!/(4! * 4! * 8! * 8!) or 662,585,823,900
  1129. # - so would be 662,585,823,900 * 2,704,156 or 1,791,735,431,214,128,400
  1130. # - 2,704,156 / 1,791,735,431,214,128,400 or 0.000 000 000 001 5092, IDA might take weeks
  1131. # - 662,585,823,900 / 1,791,735,431,214,128,400 or 0.000 000 369 8011505, IDA would be
  1132. # fast but that is with a 662 billion entry prune table
  1133. # - a LR prune table would be 24!/(4! * 4! * 16!) or 51,482,970
  1134. # - 51,482,970 / 1,791,735,431,214,128,400 or 0.000 000 000 028 7336, IDA might take weeks
  1135. # - a UDFB prune table would be 24!/(8! * 8! * 8!) or 9,465,511,770
  1136. # - 9,465,511,770 / 1,791,735,431,214,128,400 or 0.000 000 005 2828736, IDA would take a few hours
  1137. # 9 billion would be a huge prune table
  1138. # - probably not feasible
  1139. # There are four CPU "modes" we can run in:
  1140. #
  1141. # min : Uses the least CPU but produces a longer solution.
  1142. # This will stage UD centers first, then LFRB centers.
  1143. #
  1144. # normal : Uses a middle ground of CPU and produces not the shortest or longest solution.
  1145. # This will stage all centers at once.
  1146. #
  1147. # max : Uses more CPU and produce a shorter solution
  1148. # This will stage all centers at once.
  1149. #
  1150. # tsai : Uses the most CPU but produces the shortest solution
  1151. # ==============
  1152. # Phase 1 tables
  1153. # ==============
  1154. if self.cpu_mode == 'min':
  1155. self.lt_UD_centers_stage = LookupTable444UDCentersStage(self)
  1156. elif self.cpu_mode in ('normal', 'max'):
  1157. # prune tables
  1158. self.lt_UD_centers_stage = LookupTable444UDCentersStage(self)
  1159. self.lt_LR_centers_stage = LookupTable444LRCentersStage(self)
  1160. self.lt_FB_centers_stage = LookupTable444FBCentersStage(self)
  1161. # Stage all centers via IDA
  1162. self.lt_ULFRBD_centers_stage = LookupTableIDA444ULFRBDCentersStage(self)
  1163. elif self.cpu_mode == 'tsai':
  1164. # Stage LR centers
  1165. self.lt_LR_centers_stage = LookupTable444LRCentersStage(self)
  1166. elif self.cpu_mode == 'exp':
  1167. self.lt_UD_centers_stage = LookupTable444UDCentersStage(self)
  1168. self.lt_LR_centers_stage = LookupTable444LRCentersStage(self)
  1169. self.lt_FB_centers_stage = LookupTable444FBCentersStage(self)
  1170. self.lt_ULFRBD_centers_stage = BidirIDA444ULFRBDCentersStage(self)
  1171. else:
  1172. raise Exception("We should not be here, cpu_mode %s" % self.cpu_mode)
  1173. # =============
  1174. # Phase2 tables
  1175. # =============
  1176. if self.cpu_mode == 'min':
  1177. self.lt_LFRB_centers_stage = LookupTable444LFRBCentersStage(self)
  1178. elif self.cpu_mode in ('normal', 'max'):
  1179. self.lt_ULFRBD_centers_solve = LookupTable444ULFRBDCentersSolve(self)
  1180. elif self.cpu_mode == 'tsai':
  1181. # - orient the edges into high/low groups
  1182. # - solve LR centers to one of 12 states
  1183. # - stage UD and FB centers
  1184. self.lt_tsai_phase2_centers = LookupTable444TsaiPhase2Centers(self)
  1185. self.lt_tsai_phase2_edges = LookupTable444TsaiPhase2Edges(self)
  1186. self.lt_tsai_phase2 = LookupTableIDA444TsaiPhase2(self)
  1187. elif self.cpu_mode == 'exp':
  1188. pass
  1189. else:
  1190. raise Exception("We should not be here, cpu_mode %s" % self.cpu_mode)
  1191. # =============
  1192. # Phase3 tables
  1193. # =============
  1194. if self.cpu_mode == 'min':
  1195. self.lt_ULFRBD_centers_solve = LookupTable444ULFRBDCentersSolve(self)
  1196. elif self.cpu_mode in ('normal', 'max'):
  1197. pass
  1198. elif self.cpu_mode == 'tsai':
  1199. # prune tables
  1200. self.lt_tsai_phase3_edges_solve = LookupTable444TsaiPhase3Edges(self)
  1201. self.lt_tsai_phase3_centers_solve = LookupTable444TsaiPhase3CentersSolve(self)
  1202. self.lt_tsai_phase3 = LookupTableIDA444TsaiPhase3(self)
  1203. elif self.cpu_mode == 'exp':
  1204. pass
  1205. else:
  1206. raise Exception("We should not be here, cpu_mode %s" % self.cpu_mode)
  1207. # For tsai these two tables are only used if the centers have already been solved
  1208. # For non-tsai they are always used
  1209. self.lt_edge_slice_forward = LookupTable444EdgeSliceForward(self)
  1210. self.lt_edge_slice_backward = LookupTable444EdgesSliceBackward(self)
  1211. def tsai_phase2_orient_edges_state(self, edges_to_flip):
  1212. state = self.state
  1213. result = []
  1214. wing_str_map = {
  1215. 'UB' : 'UB',
  1216. 'BU' : 'UB',
  1217. 'UL' : 'UL',
  1218. 'LU' : 'UL',
  1219. 'UR' : 'UR',
  1220. 'RU' : 'UR',
  1221. 'UF' : 'UF',
  1222. 'FU' : 'UF',
  1223. 'LB' : 'LB',
  1224. 'BL' : 'LB',
  1225. 'LF' : 'LF',
  1226. 'FL' : 'LF',
  1227. 'RB' : 'RB',
  1228. 'BR' : 'RB',
  1229. 'RF' : 'RF',
  1230. 'FR' : 'RF',
  1231. 'DB' : 'DB',
  1232. 'BD' : 'DB',
  1233. 'DL' : 'DL',
  1234. 'LD' : 'DL',
  1235. 'DR' : 'DR',
  1236. 'RD' : 'DR',
  1237. 'DF' : 'DF',
  1238. 'FD' : 'DF',
  1239. }
  1240. for (x, y) in (
  1241. (2, 67), (3, 66), (5, 18), (8, 51), (9, 19), (12, 50), (14, 34),
  1242. (15, 35), (18, 5), (19, 9), (21, 72), (24, 37), (25, 76), (28, 41),
  1243. (30, 89), (31, 85), (34, 14), (35, 15), (37, 24), (40, 53), (41, 28),
  1244. (44, 57), (46, 82), (47, 83), (50, 12), (51, 8), (53, 40), (56, 69),
  1245. (57, 44), (60, 73), (62, 88), (63, 92), (66, 3), (67, 2), (69, 56),
  1246. (72, 21), (73, 60), (76, 25), (78, 95), (79, 94), (82, 46), (83, 47),
  1247. (85, 31), (88, 62), (89, 30), (92, 63), (94, 79), (95, 78)):
  1248. state_x = state[x]
  1249. state_y = state[y]
  1250. wing_str = wing_str_map[''.join((state_x, state_y))]
  1251. high_low = tsai_phase2_orient_edges[(x, y, state_x, state_y)]
  1252. if wing_str in edges_to_flip:
  1253. if high_low == 'U':
  1254. high_low = 'D'
  1255. else:
  1256. high_low = 'U'
  1257. result.append(high_low)
  1258. return ''.join(result)
  1259. def tsai_phase2_orient_edges_print(self):
  1260. # save cube state
  1261. original_state = self.state[:]
  1262. original_solution = self.solution[:]
  1263. self.nuke_corners()
  1264. self.nuke_centers()
  1265. orient_edge_state = list(self.tsai_phase2_orient_edges_state(self.edge_mapping))
  1266. orient_edge_state_index = 0
  1267. for side in list(self.sides.values()):
  1268. for square_index in side.edge_pos:
  1269. self.state[square_index] = orient_edge_state[orient_edge_state_index]
  1270. orient_edge_state_index += 1
  1271. self.print_cube()
  1272. self.state = original_state[:]
  1273. self.solution = original_solution[:]
  1274. def group_centers_guts(self):
  1275. self.lt_init()
  1276. # The non-tsai solver will only solve the centers here
  1277. if self.cpu_mode == 'min':
  1278. # If the centers are already solve then return and let group_edges() pair the edges
  1279. if self.centers_solved():
  1280. self.solution.append('CENTERS_SOLVED')
  1281. return
  1282. log.info("%s: Start of Phase1" % self)
  1283. self.lt_UD_centers_stage.solve()
  1284. self.print_cube()
  1285. log.info("%s: End of Phase1, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1286. log.info("")
  1287. log.info("%s: Start of Phase2, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1288. self.lt_LFRB_centers_stage.solve()
  1289. self.print_cube()
  1290. log.info("%s: End of Phase2, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1291. log.info("")
  1292. log.info("%s: Start of Phase3, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1293. self.lt_ULFRBD_centers_solve.solve()
  1294. self.print_cube()
  1295. log.info("%s: End of Phase3, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1296. log.info("")
  1297. elif self.cpu_mode in ('normal', 'max'):
  1298. # If the centers are already solve then return and let group_edges() pair the edges
  1299. if self.centers_solved():
  1300. self.solution.append('CENTERS_SOLVED')
  1301. return
  1302. log.info("%s: Start of Phase1" % self)
  1303. self.lt_ULFRBD_centers_stage.avoid_oll = True
  1304. self.lt_ULFRBD_centers_stage.solve()
  1305. log.info("%s: End of Phase1, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1306. self.print_cube()
  1307. log.info("")
  1308. log.info("%s: Start of Phase2, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1309. self.lt_ULFRBD_centers_solve.solve()
  1310. self.print_cube()
  1311. log.info("%s: End of Phase2, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1312. log.info("")
  1313. elif self.cpu_mode == 'exp':
  1314. log.info("%s: Start of Phase1" % self)
  1315. self.lt_ULFRBD_centers_stage.avoid_oll = True
  1316. self.lt_ULFRBD_centers_stage.solve()
  1317. log.info("%s: End of Phase1, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1318. self.print_cube()
  1319. log.info("")
  1320. sys.exit(0) # dwalton
  1321. # The tsai will solve the centers and pair the edges
  1322. elif self.cpu_mode == 'tsai':
  1323. # save cube state
  1324. original_state = self.state[:]
  1325. original_solution = self.solution[:]
  1326. '''
  1327. # Collect phase1 options
  1328. phase1_options = []
  1329. phase1_options_states = []
  1330. for upper_side_name in ('U', 'D', 'L', 'F', 'R', 'B'):
  1331. for front_side_name in ('F', 'R', 'B', 'L', 'U', 'D'):
  1332. for transform in ("", "x", "x'", "y", "y'", "z", "z'"):
  1333. if transform == "":
  1334. pass
  1335. elif transform == "x":
  1336. self.transform_x()
  1337. elif transform == "x'":
  1338. self.transform_x_prime()
  1339. elif transform == "y":
  1340. self.transform_y()
  1341. elif transform == "y'":
  1342. self.transform_y_prime()
  1343. elif transform == "z":
  1344. self.transform_z()
  1345. elif transform == "z'":
  1346. self.transform_z_prime()
  1347. else:
  1348. raise Exception("Implement transform %s" % transform)
  1349. if self.rotate_to_side(upper_side_name, front_side_name):
  1350. self.lt_LR_centers_stage.solve()
  1351. # 168, 104
  1352. if self.state in phase1_options_states:
  1353. log.warning("%s %s %-2s would add a duplicate cube state" % (upper_side_name, front_side_name, transform))
  1354. else:
  1355. phase1_options.append((upper_side_name, front_side_name, transform, self.state[:], self.solution[:]))
  1356. phase1_options_states.append(self.state[:])
  1357. self.state = original_state[:]
  1358. self.solution = original_solution[:]
  1359. # Print all the permutations
  1360. for (upper_side_name, front_side_name, transform, tmp_state, tmp_solution) in phase1_options:
  1361. log.info("%s %s %-2s - %d phase1 steps" % (upper_side_name, front_side_name, transform, self.get_solution_len_minus_rotates(tmp_solution)))
  1362. log.info("%s: %d phase1 options" % (self, len(phase1_options)))
  1363. min_solution_len = None
  1364. min_solution = None
  1365. min_state = None
  1366. for init_max_ida_threshold in range(7, 99):
  1367. for (upper_side_name, front_side_name, transform, tmp_state, tmp_solution) in phase1_options:
  1368. self.state = tmp_state[:]
  1369. self.solution = tmp_solution[:]
  1370. # Test the prune table
  1371. #self.lt_tsai_phase2_edges.solve()
  1372. #self.lt_tsai_phase2_centers.solve()
  1373. #self.tsai_phase2_orient_edges_print()
  1374. #self.print_cube()
  1375. #sys.exit(0)
  1376. if min_solution_len is None:
  1377. max_ida_threshold = init_max_ida_threshold
  1378. log.info("%s: max_ida_threshold (%d)" % (self, max_ida_threshold))
  1379. else:
  1380. phase1_solution_length = self.get_solution_len_minus_rotates(tmp_solution)
  1381. max_ida_threshold = min_solution_len - phase1_solution_length - 1
  1382. log.info("%s: max_ida_threshold (%d) = min_solution_len (%d) - phase1_solution_length (%d) - 1" %
  1383. (self, max_ida_threshold, min_solution_len, phase1_solution_length))
  1384. try:
  1385. self.lt_tsai_phase2.solve(max_ida_threshold=max_ida_threshold)
  1386. except NoIDASolution:
  1387. log.info("%s: NOT NEW MIN, no solution, min %s\n\n\n\n\n" % (self, min_solution_len))
  1388. continue
  1389. solution_len = self.get_solution_len_minus_rotates(self.solution)
  1390. if min_solution_len is None or solution_len < min_solution_len:
  1391. min_solution_len = solution_len
  1392. min_state = self.state[:]
  1393. min_solution = self.solution[:]
  1394. log.warning("%s: NEW MIN %d (%s, %s %-2s)\n\n\n\n\n" % (self, min_solution_len, upper_side_name, front_side_name, transform))
  1395. else:
  1396. log.info("%s: NOT NEW MIN, this one %d, min %d\n\n\n\n\n" % (self, solution_len, min_solution_len))
  1397. # This is pretty good, we might be able to find a shorter one but it would take us a while.
  1398. # If we are ever able to speed up phase2 IDA via an edges prune table then we can probably
  1399. # remove this.
  1400. if min_solution_len <= 15:
  1401. break
  1402. if min_solution_len is not None:
  1403. break
  1404. self.state = min_state[:]
  1405. self.solution = min_solution[:]
  1406. self.print_cube()
  1407. log.info("%s: End of Phase1/2, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1408. log.info("")
  1409. '''
  1410. log.info("%s: Start of Phase1, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1411. self.lt_LR_centers_stage.solve()
  1412. self.print_cube()
  1413. log.info("%s: End of Phase1, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1414. # Test the prune table
  1415. #self.lt_tsai_phase2_edges.solve()
  1416. #self.lt_tsai_phase2_centers.solve()
  1417. #self.tsai_phase2_orient_edges_print()
  1418. #self.print_cube()
  1419. #sys.exit(0)
  1420. log.info("%s: Start of Phase2, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1421. self.lt_tsai_phase2.solve()
  1422. self.print_cube()
  1423. log.info("%s: End of Phase2, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1424. # Testing the phase3 prune tables
  1425. #self.lt_tsai_phase3_edges_solve.solve()
  1426. #self.lt_tsai_phase3_centers_solve.solve()
  1427. #self.print_cube()
  1428. #sys.exit(0)
  1429. log.info("%s: Start of Phase3, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1430. #self.lt_tsai_phase3.avoid_oll = True
  1431. self.lt_tsai_phase3.avoid_pll = True
  1432. self.lt_tsai_phase3.solve()
  1433. self.print_cube()
  1434. log.info("%s: End of Phase3, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)))
  1435. log.info("")
  1436. self.solution.append('CENTERS_SOLVED')
  1437. def edge_string_to_find(self, target_wing, sister_wing1, sister_wing2, sister_wing3):
  1438. state = []
  1439. for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD):
  1440. for square_index in sorted(side.edge_pos):
  1441. if square_index in (target_wing[0], target_wing[1]):
  1442. state.append('A')
  1443. elif square_index in (sister_wing1[0], sister_wing1[1]):
  1444. state.append('B')
  1445. elif square_index in (sister_wing2[0], sister_wing2[1]):
  1446. state.append('C')
  1447. elif square_index in (sister_wing3[0], sister_wing3[1]):
  1448. state.append('D')
  1449. else:
  1450. state.append('x')
  1451. return ''.join(state)
  1452. def find_moves_to_stage_slice_forward_444(self, target_wing, sister_wing1, sister_wing2, sister_wing3):
  1453. """
  1454. target_wing must go in spot 41
  1455. sister_wing1 must go in spot (40, 53)
  1456. sister_wing2 must go in spot (56, 69)
  1457. sister_wing3 must go in spot (72, 21)
  1458. """
  1459. state = self.edge_string_to_find(target_wing, sister_wing1, sister_wing2, sister_wing3)
  1460. return self.lt_edge_slice_forward.steps(state)
  1461. def find_moves_to_stage_slice_backward_444(self, target_wing, sister_wing1, sister_wing2, sister_wing3):
  1462. """
  1463. target_wing must go in spot (44, 57)
  1464. sister_wing1 must go in spot (24, 37)
  1465. sister_wing2 must go in spot (72, 21))
  1466. sister_wing3 must go in spot (56, 69)
  1467. """
  1468. state = self.edge_string_to_find(target_wing, sister_wing1, sister_wing2, sister_wing3)
  1469. return self.lt_edge_slice_backward.steps(state)
  1470. def prep_for_slice_back_444(self):
  1471. # Now set things up so that when we slice back we pair another 3 edges.
  1472. # Work with the wing on the bottom of F-east
  1473. target_wing = self.sideF.edge_east_pos[-1]
  1474. sister_wing = self.get_wings(target_wing)[0]
  1475. target_wing_partner_index = 57
  1476. sister_wing1 = self.get_wings(target_wing)[0]
  1477. sister_wing1_side = self.get_side_for_index(sister_wing1[0])
  1478. sister_wing1_neighbor = sister_wing1_side.get_wing_neighbors(sister_wing1[0])[0]
  1479. sister_wing2 = self.get_wings(sister_wing1_neighbor)[0]
  1480. sister_wing2_side = self.get_side_for_index(sister_wing2[0])
  1481. sister_wing2_neighbor = sister_wing2_side.get_wing_neighbors(sister_wing2[0])[0]
  1482. sister_wing3 = self.get_wings(sister_wing2_neighbor)[0]
  1483. steps = self.find_moves_to_stage_slice_backward_444((target_wing, target_wing_partner_index), sister_wing1, sister_wing2, sister_wing3)
  1484. if steps:
  1485. for step in steps:
  1486. self.rotate(step)
  1487. return True
  1488. # If we are here it means the unpaired edge on F-east needs to be swapped with
  1489. # its sister_wing1. In other words F-east and sister-wing1 have the same two
  1490. # sets of colors and the two of them together would create two paired edges if
  1491. # we swapped their wings.
  1492. #
  1493. # As a work-around, move some other unpaired edge into F-east. There are no
  1494. # guarantees we won't hit the exact same problem with that edge but that doesn't
  1495. # happen too often.
  1496. if not self.sideU.north_edge_paired() and self.sideU.has_wing(sister_wing1) != 'north':
  1497. self.rotate("F'")
  1498. self.rotate("U2")
  1499. self.rotate("F")
  1500. elif not self.sideU.east_edge_paired() and self.sideU.has_wing(sister_wing1) != 'east':
  1501. self.rotate("F'")
  1502. self.rotate("U")
  1503. self.rotate("F")
  1504. elif not self.sideU.west_edge_paired() and self.sideU.has_wing(sister_wing1) != 'west':
  1505. self.rotate("F'")
  1506. self.rotate("U'")
  1507. self.rotate("F")
  1508. elif not self.sideD.south_edge_paired() and self.sideD.has_wing(sister_wing1) != 'south':
  1509. self.rotate("F")
  1510. self.rotate("D2")
  1511. self.rotate("F'")
  1512. elif not self.sideD.east_edge_paired() and self.sideD.has_wing(sister_wing1) != 'east':
  1513. self.rotate("F")
  1514. self.rotate("D'")
  1515. self.rotate("F'")
  1516. elif not self.sideD.west_edge_paired() and self.sideD.has_wing(sister_wing1) != 'west':
  1517. self.rotate("F")
  1518. self.rotate("D")
  1519. self.rotate("F'")
  1520. # Look for these last since they take 4 steps instead of 3
  1521. elif not self.sideU.south_edge_paired() and self.sideU.has_wing(sister_wing1) != 'south':
  1522. self.rotate("U'")
  1523. self.rotate("F'")
  1524. self.rotate("U")
  1525. self.rotate("F")
  1526. elif not self.sideD.north_edge_paired() and self.sideD.has_wing(sister_wing1) != 'north':
  1527. self.rotate("D")
  1528. self.rotate("F")
  1529. self.rotate("D'")
  1530. self.rotate("F'")
  1531. else:
  1532. # If we are here we are down to two unpaired wings
  1533. return False
  1534. if self.sideF.east_edge_paired():
  1535. raise SolveError("F-east should not be paired")
  1536. target_wing = self.sideF.edge_east_pos[-1]
  1537. sister_wing = self.get_wings(target_wing)[0]
  1538. target_wing_partner_index = 57
  1539. sister_wing1 = self.get_wings(target_wing)[0]
  1540. sister_wing1_side = self.get_side_for_index(sister_wing1[0])
  1541. sister_wing1_neighbor = sister_wing1_side.get_wing_neighbors(sister_wing1[0])[0]
  1542. sister_wing2 = self.get_wings(sister_wing1_neighbor)[0]
  1543. sister_wing2_side = self.get_side_for_index(sister_wing2[0])
  1544. sister_wing2_neighbor = sister_wing2_side.get_wing_neighbors(sister_wing2[0])[0]
  1545. sister_wing3 = self.get_wings(sister_wing2_neighbor)[0]
  1546. steps = self.find_moves_to_stage_slice_backward_444((target_wing, target_wing_partner_index), sister_wing1, sister_wing2, sister_wing3)
  1547. if steps:
  1548. for step in steps:
  1549. self.rotate(step)
  1550. return True
  1551. else:
  1552. return False
  1553. def pair_six_edges_444(self, wing_to_pair):
  1554. """
  1555. Sections are:
  1556. - PREP-FOR-Uw-SLICE
  1557. - Uw
  1558. - PREP-FOR-REVERSE-Uw-SLICE
  1559. - Uw'
  1560. """
  1561. # save cube state
  1562. original_state = self.state[:]
  1563. original_solution = self.solution[:]
  1564. original_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1565. original_non_paired_wings_count = self.get_non_paired_wings_count()
  1566. self.rotate_edge_to_F_west(wing_to_pair)
  1567. #log.info("PREP-FOR-Uw-SLICE (begin)")
  1568. #self.print_cube()
  1569. # Work with the wing at the bottom of the F-west edge
  1570. # Move the sister wing to the top of F-east
  1571. target_wing = self.sideF.edge_west_pos[-1]
  1572. target_wing_partner_index = 28
  1573. sister_wing1 = self.get_wings(target_wing)[0]
  1574. sister_wing1_side = self.get_side_for_index(sister_wing1[0])
  1575. sister_wing1_neighbor = sister_wing1_side.get_wing_neighbors(sister_wing1[0])[0]
  1576. sister_wing2 = self.get_wings(sister_wing1_neighbor)[0]
  1577. sister_wing2_side = self.get_side_for_index(sister_wing2[0])
  1578. sister_wing2_neighbor = sister_wing2_side.get_wing_neighbors(sister_wing2[0])[0]
  1579. sister_wing3 = self.get_wings(sister_wing2_neighbor)[0]
  1580. steps = self.find_moves_to_stage_slice_forward_444((target_wing, target_wing_partner_index), sister_wing1, sister_wing2, sister_wing3)
  1581. if not steps:
  1582. log.info("pair_six_edges_444() could not find steps to slice forward")
  1583. self.state = original_state[:]
  1584. self.solution = original_solution[:]
  1585. return False
  1586. for step in steps:
  1587. self.rotate(step)
  1588. # At this point we are setup to slice forward and pair 3 edges
  1589. #log.info("PREP-FOR-Uw-SLICE (end)....SLICE (begin)")
  1590. #self.print_cube()
  1591. self.rotate("Uw")
  1592. #log.info("SLICE (end)")
  1593. #self.print_cube()
  1594. post_slice_forward_non_paired_wings_count = self.get_non_paired_wings_count()
  1595. post_slice_forward_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1596. log.info("pair_six_edges_444() paired %d wings in %d moves on slice forward (%d left to pair)" %
  1597. (original_non_paired_wings_count - post_slice_forward_non_paired_wings_count,
  1598. post_slice_forward_solution_len - original_solution_len,
  1599. post_slice_forward_non_paired_wings_count))
  1600. if self.sideL.west_edge_paired():
  1601. # The stars aligned and we paired 4 at once so we cannot rotate L-west around
  1602. # to F-east, move an unpaired edge to F-east. This preserves the LFRB centers
  1603. # for the slice back.
  1604. if not self.sideU.north_edge_paired():
  1605. self.rotate("F'")
  1606. self.rotate("U2")
  1607. self.rotate("F")
  1608. elif not self.sideU.east_edge_paired():
  1609. self.rotate("F'")
  1610. self.rotate("U")
  1611. self.rotate("F")
  1612. elif not self.sideU.west_edge_paired():
  1613. self.rotate("F'")
  1614. self.rotate("U'")
  1615. self.rotate("F")
  1616. elif not self.sideD.south_edge_paired():
  1617. self.rotate("F")
  1618. self.rotate("D2")
  1619. self.rotate("F'")
  1620. elif not self.sideD.east_edge_paired():
  1621. self.rotate("F")
  1622. self.rotate("D'")
  1623. self.rotate("F'")
  1624. elif not self.sideD.west_edge_paired():
  1625. self.rotate("F")
  1626. self.rotate("D")
  1627. self.rotate("F'")
  1628. # Look for these last since they take 4 steps instead of 3
  1629. elif not self.sideU.south_edge_paired():
  1630. self.rotate("U'")
  1631. self.rotate("F'")
  1632. self.rotate("U")
  1633. self.rotate("F")
  1634. elif not self.sideD.north_edge_paired():
  1635. self.rotate("D")
  1636. self.rotate("F")
  1637. self.rotate("D'")
  1638. self.rotate("F'")
  1639. else:
  1640. raise SolveError("Did not find an unpaired edge")
  1641. else:
  1642. self.rotate_y()
  1643. self.rotate_y()
  1644. if self.sideF.east_edge_paired():
  1645. log.warning("F-east should not be paired")
  1646. self.state = original_state[:]
  1647. self.solution = original_solution[:]
  1648. return False
  1649. if not self.prep_for_slice_back_444():
  1650. log.warning("cannot slice back")
  1651. self.state = original_state[:]
  1652. self.solution = original_solution[:]
  1653. return False
  1654. #log.info("PREP-FOR-Uw'-SLICE-BACK (end)...SLICE BACK (begin)")
  1655. #self.print_cube()
  1656. self.rotate("Uw'")
  1657. #log.info("SLICE BACK (end)")
  1658. #self.print_cube()
  1659. post_slice_back_non_paired_wings_count = self.get_non_paired_wings_count()
  1660. post_slice_back_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1661. log.info("pair_six_edges_444() paired %d wings in %d moves on slice back (%d left to pair)" %
  1662. (post_slice_forward_non_paired_wings_count - post_slice_back_non_paired_wings_count,
  1663. post_slice_back_solution_len - post_slice_forward_solution_len,
  1664. post_slice_back_non_paired_wings_count))
  1665. return True
  1666. def pair_last_six_edges_444(self):
  1667. """
  1668. Sections are:
  1669. - PREP-FOR-Uw-SLICE
  1670. - Uw
  1671. - PREP-FOR-REVERSE-Uw-SLICE
  1672. - Uw'
  1673. """
  1674. # save cube state
  1675. original_state = self.state[:]
  1676. original_solution = self.solution[:]
  1677. original_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1678. original_non_paired_wings_count = self.get_non_paired_wings_count()
  1679. original_non_paired_edges = self.get_non_paired_edges()
  1680. min_solution_len = None
  1681. min_solution_state = None
  1682. min_solution = None
  1683. for wing_to_pair in original_non_paired_edges:
  1684. self.state = original_state[:]
  1685. self.solution = original_solution[:]
  1686. self.rotate_edge_to_F_west(wing_to_pair[0])
  1687. # Work with the wing at the bottom of the F-west edge
  1688. # Move the sister wing to the top of F-east
  1689. target_wing = self.sideF.edge_west_pos[-1]
  1690. target_wing_partner_index = 28
  1691. sister_wing1 = self.get_wings(target_wing)[0]
  1692. sister_wing1_side = self.get_side_for_index(sister_wing1[0])
  1693. sister_wing1_neighbor = sister_wing1_side.get_wing_neighbors(sister_wing1[0])[0]
  1694. sister_wing2 = self.get_wings(sister_wing1_neighbor)[0]
  1695. sister_wing2_side = self.get_side_for_index(sister_wing2[0])
  1696. sister_wing2_neighbor = sister_wing2_side.get_wing_neighbors(sister_wing2[0])[0]
  1697. sister_wing3 = self.get_wings(sister_wing2_neighbor)[0]
  1698. #log.info("target_wing: %s" % target_wing)
  1699. #log.info("sister_wing1 %s on %s, neighbor %s" % (sister_wing1, sister_wing1_side, sister_wing1_neighbor))
  1700. #log.info("sister_wing2 %s on %s, neighbor %s" % (sister_wing2, sister_wing2_side, sister_wing2_neighbor))
  1701. #log.info("sister_wing3 %s" % pformat(sister_wing3))
  1702. sister_wing3_candidates = []
  1703. # We need sister_wing3 to be any unpaired edge that allows us
  1704. # to only pair 2 on the slice forward
  1705. for wing in self.get_non_paired_wings():
  1706. if (wing[0] not in (target_wing, sister_wing1, sister_wing2, sister_wing3) and
  1707. wing[1] not in (target_wing, sister_wing1, sister_wing2, sister_wing3)):
  1708. sister_wing3_candidates.append(wing[1])
  1709. min_sister_wing3_steps_len = None
  1710. min_sister_wing3_steps = None
  1711. min_sister_wing3 = None
  1712. for x in sister_wing3_candidates:
  1713. steps = self.find_moves_to_stage_slice_forward_444((target_wing, target_wing_partner_index), sister_wing1, sister_wing2, x)
  1714. if steps:
  1715. steps_len = len(steps)
  1716. if min_sister_wing3_steps_len is None or steps_len < min_sister_wing3_steps_len:
  1717. min_sister_wing3_steps_len = steps_len
  1718. min_sister_wing3_steps = steps
  1719. min_sister_wing3 = x
  1720. sister_wing3 = min_sister_wing3
  1721. steps = min_sister_wing3_steps
  1722. #log.info("sister_wing3 %s" % pformat(sister_wing3))
  1723. if not steps:
  1724. log.info("pair_last_six_edges_444() cannot slice back (no steps found)")
  1725. continue
  1726. for step in steps:
  1727. self.rotate(step)
  1728. # At this point we are setup to slice forward and pair 2 edges
  1729. #log.info("PREP-FOR-Uw-SLICE (end)....SLICE (begin)")
  1730. #self.print_cube()
  1731. self.rotate("Uw")
  1732. #log.info("SLICE (end)")
  1733. #self.print_cube()
  1734. post_slice_forward_non_paired_wings_count = self.get_non_paired_wings_count()
  1735. post_slice_forward_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1736. log.info("pair_last_six_edges_444() paired %d wings in %d moves on slice forward (%d left to pair)" %
  1737. (original_non_paired_wings_count - post_slice_forward_non_paired_wings_count,
  1738. post_slice_forward_solution_len - original_solution_len,
  1739. post_slice_forward_non_paired_wings_count))
  1740. if self.sideF.east_edge_paired():
  1741. for x in range(3):
  1742. self.rotate_y()
  1743. if not self.sideF.east_edge_paired():
  1744. break
  1745. if self.sideF.east_edge_paired():
  1746. log.info("pair_last_six_edges_444() cannot slice back (F-east paired)")
  1747. continue
  1748. if not self.prep_for_slice_back_444():
  1749. log.info("pair_last_six_edges_444() cannot slice back (prep failed)")
  1750. continue
  1751. self.rotate("Uw'")
  1752. post_slice_back_non_paired_wings_count = self.get_non_paired_wings_count()
  1753. post_slice_back_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1754. if min_solution_len is None or post_slice_back_solution_len < min_solution_len:
  1755. min_solution_len = post_slice_back_solution_len
  1756. min_solution_state = self.state[:]
  1757. min_solution = self.solution[:]
  1758. log.info("pair_last_six_edges_444() paired %d wings in %d moves on slice back (%d left to pair) (NEW MIN %d)" %
  1759. (post_slice_forward_non_paired_wings_count - post_slice_back_non_paired_wings_count,
  1760. post_slice_back_solution_len - post_slice_forward_solution_len,
  1761. post_slice_back_non_paired_wings_count,
  1762. post_slice_back_solution_len - original_solution_len))
  1763. else:
  1764. log.info("pair_last_six_edges_444() paired %d wings in %d moves on slice back (%d left to pair)" %
  1765. (post_slice_forward_non_paired_wings_count - post_slice_back_non_paired_wings_count,
  1766. post_slice_back_solution_len - post_slice_forward_solution_len,
  1767. post_slice_back_non_paired_wings_count))
  1768. if min_solution_len:
  1769. self.state = min_solution_state
  1770. self.solution = min_solution
  1771. return True
  1772. else:
  1773. self.state = original_state[:]
  1774. self.solution = original_solution[:]
  1775. return False
  1776. def pair_four_edges_444(self, edge):
  1777. # save cube state
  1778. original_state = self.state[:]
  1779. original_solution = self.solution[:]
  1780. original_non_paired_wings_count = self.get_non_paired_wings_count()
  1781. original_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1782. if original_non_paired_wings_count < 4:
  1783. raise SolveError("pair_four_edges_444() requires at least 4 unpaired edges")
  1784. self.rotate_edge_to_F_west(edge)
  1785. # Work with the wing at the bottom of the F-west edge
  1786. target_wing = self.sideF.edge_west_pos[-1]
  1787. # Move the sister wing to F east
  1788. sister_wing = self.get_wings(target_wing)[0]
  1789. steps = lookup_table_444_sister_wing_to_F_east[sister_wing]
  1790. for step in steps.split():
  1791. self.rotate(step)
  1792. self.rotate("Uw")
  1793. if not self.sideR.west_edge_paired():
  1794. pass
  1795. elif not self.sideB.west_edge_paired():
  1796. self.rotate_y()
  1797. elif not self.sideL.west_edge_paired():
  1798. self.rotate_y()
  1799. self.rotate_y()
  1800. if not self.prep_for_slice_back_444():
  1801. self.state = original_state[:]
  1802. self.solution = original_solution[:]
  1803. return False
  1804. self.rotate("Uw'")
  1805. current_non_paired_wings_count = self.get_non_paired_wings_count()
  1806. current_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1807. log.info("pair_four_edges_444() paired %d wings in %d moves (%d left to pair)" %
  1808. (original_non_paired_wings_count - current_non_paired_wings_count,
  1809. current_solution_len - original_solution_len,
  1810. current_non_paired_wings_count))
  1811. if current_non_paired_wings_count >= original_non_paired_wings_count:
  1812. raise SolveError("Went from %d to %d non_paired_edges" %
  1813. (original_non_paired_wings_count, current_non_paired_wings_count))
  1814. return True
  1815. def pair_two_edges_444(self, edge):
  1816. original_state = self.state[:]
  1817. original_solution = self.solution[:]
  1818. original_non_paired_wings_count = self.get_non_paired_wings_count()
  1819. original_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1820. if original_non_paired_wings_count == 2:
  1821. raise SolveError("pair_last_two_edges_444() should be used when there are only 2 edges left")
  1822. self.rotate_edge_to_F_west(edge)
  1823. # Work with the wing at the bottom of the F-west edge
  1824. target_wing = self.sideF.edge_west_pos[-1]
  1825. # Move the sister wing to F east...this uses a small lookup table
  1826. # that I built manually. This puts the sister wing at F-east in the correct
  1827. # orientation (it will not need to be flipped). We used to just move the
  1828. # sister wing to F-east but then sometimes we would need to "R U' B' R2"
  1829. # to flip it around.
  1830. sister_wing = self.get_wings(target_wing)[0]
  1831. '''
  1832. if sister_wing not in lookup_table_444_sister_wing_to_F_east:
  1833. log.warning("lookup_table_444_sister_wing_to_F_east needs %s" % pformat(sister_wing))
  1834. self.find_moves_to_reach_state(sister_wing, 'F-east')
  1835. raise ImplementThis("lookup_table_444_sister_wing_to_F_east needs %s" % pformat(sister_wing))
  1836. '''
  1837. steps = lookup_table_444_sister_wing_to_F_east[sister_wing]
  1838. for step in steps.split():
  1839. self.rotate(step)
  1840. # Now that that two edges on F are in place, put an unpaired edge at U-west.
  1841. # The unpaired edge that we place at U-west should contain the
  1842. # sister wing of the wing that is at the bottom of F-east. This
  1843. # will allow us to pair two wings at once.
  1844. wing_bottom_F_east = self.sideF.edge_east_pos[-1]
  1845. sister_wing_bottom_F_east = self.get_wings(wing_bottom_F_east)[0]
  1846. if sister_wing_bottom_F_east not in lookup_table_444_sister_wing_to_U_west:
  1847. raise ImplementThis("sister_wing_bottom_F_east %s" % pformat(sister_wing_bottom_F_east))
  1848. steps = lookup_table_444_sister_wing_to_U_west[sister_wing_bottom_F_east]
  1849. # If steps is None it means sister_wing_bottom_F_east is at (37, 24)
  1850. # which is the top wing on F-west. If that is the case call
  1851. # pair_last_two_edges_444()
  1852. if steps == None:
  1853. self.state = original_state[:]
  1854. self.solution = original_solution[:]
  1855. #self.print_cube()
  1856. self.pair_last_two_edges_444(edge)
  1857. else:
  1858. for step in steps.split():
  1859. self.rotate(step)
  1860. for step in ("Uw", "L'", "U'", "L", "Uw'"):
  1861. self.rotate(step)
  1862. current_non_paired_wings_count = self.get_non_paired_wings_count()
  1863. current_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1864. log.info("pair_two_edges_444() paired %d wings in %d moves (%d left to pair)" %
  1865. (original_non_paired_wings_count - current_non_paired_wings_count,
  1866. current_solution_len - original_solution_len,
  1867. current_non_paired_wings_count))
  1868. if current_non_paired_wings_count < original_non_paired_wings_count:
  1869. return True
  1870. raise SolveError("Went from %d to %d non_paired_edges" %
  1871. (original_non_paired_wings_count, current_non_paired_wings_count))
  1872. def pair_last_two_edges_444(self, edge):
  1873. """
  1874. At one point I looked into using two lookup tables to do this:
  1875. - the first to stage edges to F-west and F-east
  1876. - the second to solve the two staged edges
  1877. The first stage took 1 or steps and the 2nd stage took either 7 or 10, it
  1878. was 10 if the wing at F-east was turned the wrong way and needed to be
  1879. rotated around. It wasn't worth it...what I have below works just fine and
  1880. takes between 7 to 11 steps total.
  1881. """
  1882. original_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1883. original_non_paired_wings_count = self.get_non_paired_wings_count()
  1884. # rotate unpaired edge to F-west
  1885. self.rotate_edge_to_F_west(edge)
  1886. pos1 = self.sideF.edge_west_pos[-1]
  1887. # Put the other unpaired edge on F east...this uses a small lookup table
  1888. # that I built manually. This puts the sister wing at F-east in the correct
  1889. # orientation (it will not need to be flipped). We used to just move the
  1890. # sister wing to F-east but then sometimes we would need to "R F' U R' F"
  1891. # to flip it around.
  1892. sister_wing = self.get_wings(pos1)[0]
  1893. steps = lookup_table_444_last_two_edges_place_F_east[sister_wing]
  1894. for step in steps.split():
  1895. self.rotate(step)
  1896. # "Solving the last 4 edge blocks" in
  1897. # http://www.rubiksplace.com/cubes/4x4/
  1898. for step in ("Dw", "R", "F'", "U", "R'", "F", "Dw'"):
  1899. self.rotate(step)
  1900. current_non_paired_wings_count = self.get_non_paired_wings_count()
  1901. current_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1902. log.info("pair_last_two_edges_444() paired %d wings in %d moves (%d left to pair)" %
  1903. (original_non_paired_wings_count - current_non_paired_wings_count,
  1904. current_solution_len - original_solution_len,
  1905. current_non_paired_wings_count))
  1906. if original_non_paired_wings_count == 2:
  1907. if current_non_paired_wings_count:
  1908. raise SolveError("Failed to pair last two edges")
  1909. def pair_edge(self, edge_to_pair):
  1910. """
  1911. Pair a specific edge
  1912. """
  1913. pre_solution_len = self.get_solution_len_minus_rotates(self.solution)
  1914. pre_non_paired_edges_count = self.get_non_paired_edges_count()
  1915. log.info("pair_edge() for %s (%d wings left to pair)" % (pformat(edge_to_pair), pre_non_paired_edges_count))
  1916. if pre_non_paired_edges_count > 6:
  1917. if not self.pair_six_edges_444(edge_to_pair[0]):
  1918. log.info("pair_six_edges_444() returned False")
  1919. if not self.pair_four_edges_444(edge_to_pair[0]):
  1920. log.info("pair_four_edges_444() returned False")
  1921. self.pair_two_edges_444(edge_to_pair[0])
  1922. elif pre_non_paired_edges_count == 6:
  1923. if not self.pair_last_six_edges_444():
  1924. log.info("pair_last_six_edges_444() returned False")
  1925. if not self.pair_four_edges_444(edge_to_pair[0]):
  1926. log.info("pair_four_edges_444() returned False")
  1927. self.pair_two_edges_444(edge_to_pair[0])
  1928. elif pre_non_paired_edges_count >= 4:
  1929. if not self.pair_four_edges_444(edge_to_pair[0]):
  1930. log.info("pair_four_edges_444() returned False")
  1931. self.pair_two_edges_444(edge_to_pair[0])
  1932. elif pre_non_paired_edges_count == 2:
  1933. self.pair_last_two_edges_444(edge_to_pair[0])
  1934. # The scenario where you have 3 unpaired edges
  1935. elif pre_non_paired_edges_count > 2:
  1936. self.pair_two_edges_444(edge_to_pair[0])
  1937. post_non_paired_edges_count = self.get_non_paired_edges_count()
  1938. edges_paired = pre_non_paired_edges_count - post_non_paired_edges_count
  1939. if edges_paired < 1:
  1940. raise SolveError("Paired %d edges" % edges_paired)
  1941. return True
  1942. def group_edges_recursive(self, depth, edge_to_pair):
  1943. """
  1944. """
  1945. pre_non_paired_wings_count = len(self.get_non_paired_wings())
  1946. pre_non_paired_edges_count = len(self.get_non_paired_edges())
  1947. edge_solution_len = self.get_solution_len_minus_rotates(self.solution) - self.center_solution_len
  1948. log.info("")
  1949. log.info("group_edges_recursive(%d) called with edge_to_pair %s (%d edges and %d wings left to pair, min solution len %s, current solution len %d)" %
  1950. (depth,
  1951. pformat(edge_to_pair),
  1952. pre_non_paired_edges_count,
  1953. pre_non_paired_wings_count,
  1954. self.min_edge_solution_len,
  1955. edge_solution_len))
  1956. # Should we continue down this branch or should we prune it? An estimate
  1957. # of 2 moves to pair an edge is a low estimate so if the current number of
  1958. # steps plus 2 * pre_non_paired_wings_count is greater than our current minimum
  1959. # there isn't any point in continuing down this branch so prune it and save
  1960. # some CPU cycles.
  1961. if self.cpu_mode == 'min':
  1962. estimate_per_wing = 3.0
  1963. else:
  1964. estimate_per_wing = 2.0
  1965. estimated_solution_len = edge_solution_len + (estimate_per_wing * pre_non_paired_wings_count)
  1966. if estimated_solution_len >= self.min_edge_solution_len:
  1967. #log.warning("PRUNE: %s + (2 * %d) > %s" % (edge_solution_len, non_paired_wings_count, self.min_edge_solution_len))
  1968. return False
  1969. # The only time this will be None is on the initial call to group_edges_recursive()
  1970. if edge_to_pair:
  1971. self.pair_edge(edge_to_pair)
  1972. non_paired_edges = self.get_non_paired_edges()
  1973. if non_paired_edges:
  1974. original_state = self.state[:]
  1975. original_solution = self.solution[:]
  1976. # call group_edges_recursive() for each non-paired edge
  1977. for edge in non_paired_edges:
  1978. self.group_edges_recursive(depth+1, edge)
  1979. self.state = original_state[:]
  1980. self.solution = original_solution[:]
  1981. else:
  1982. # If you solve 3x3x3 and then resolve PLL it takes 12 steps but if we avoid it here
  1983. # it only takes 7 steps. If we are pairing the outside edges of a 5x5x5 self.avoid_pll
  1984. # will be False.
  1985. if self.avoid_pll and self.edge_solution_leads_to_pll_parity():
  1986. for step in "Rw2 U2 F2 Rw2 F2 U2 Rw2".split():
  1987. self.rotate(step)
  1988. # There are no edges left to pair, note how many steps it took pair them all
  1989. edge_solution_len = self.get_solution_len_minus_rotates(self.solution) - self.center_solution_len
  1990. # Remember the solution that pairs all edges in the least number of moves
  1991. if edge_solution_len < self.min_edge_solution_len:
  1992. self.min_edge_solution_len = edge_solution_len
  1993. self.min_edge_solution = self.solution[:]
  1994. self.min_edge_solution_state = self.state[:]
  1995. log.warning("NEW MIN: edges paired in %d steps" % self.min_edge_solution_len)
  1996. return True
  1997. def group_edges(self):
  1998. if not self.get_non_paired_edges():
  1999. self.solution.append('EDGES_GROUPED')
  2000. return
  2001. depth = 0
  2002. self.lt_init()
  2003. self.center_solution_len = self.get_solution_len_minus_rotates(self.solution)
  2004. self.min_edge_solution_len = 9999
  2005. self.min_edge_solution = None
  2006. self.min_edge_solution_state = None
  2007. # group_edges_recursive() is where the magic happens
  2008. self.group_edges_recursive(depth, None)
  2009. self.state = self.min_edge_solution_state[:]
  2010. self.solution = self.min_edge_solution[:]
  2011. self.solution.append('EDGES_GROUPED')