Browse Source

Initial commit

Daniel Walton 2 years ago
parent
commit
00ed991ac9
41 changed files with 226643 additions and 0 deletions
  1. 8
    0
      .gitignore
  2. 55
    0
      README.md
  3. 47
    0
      TODO.txt
  4. 17
    0
      misc/NOTES-2x2x2.txt
  5. 201
    0
      misc/NOTES-4x4x4.txt
  6. 185
    0
      misc/NOTES-5x5x5.txt
  7. 31
    0
      misc/NOTES-6x6x6.txt
  8. 10
    0
      misc/NOTES-7x7x7.txt
  9. 85
    0
      misc/TODO.txt
  10. 2507
    0
      rubikscubennnsolver/LookupTable.py
  11. 178
    0
      rubikscubennnsolver/RubiksCube222.py
  12. 29
    0
      rubikscubennnsolver/RubiksCube333.py
  13. 2700
    0
      rubikscubennnsolver/RubiksCube444.py
  14. 1728
    0
      rubikscubennnsolver/RubiksCube555.py
  15. 760
    0
      rubikscubennnsolver/RubiksCube666.py
  16. 1291
    0
      rubikscubennnsolver/RubiksCube777.py
  17. 450
    0
      rubikscubennnsolver/RubiksCubeNNN.py
  18. 619
    0
      rubikscubennnsolver/RubiksSide.py
  19. 4545
    0
      rubikscubennnsolver/__init__.py
  20. 1134
    0
      rubikscubennnsolver/rotate_xxx.py
  21. 15
    0
      setup.py
  22. 207
    0
      usr/bin/rubiks-cube-solver.py
  23. 258
    0
      utils/binary-search.c
  24. 98
    0
      utils/binary-search.py
  25. 151
    0
      utils/build-test-cases.py
  26. 42
    0
      utils/convert-to-binary.py
  27. 18
    0
      utils/find-next-prime-number.py
  28. 23
    0
      utils/fix_foo.py
  29. 27
    0
      utils/hash_stats.py
  30. 105
    0
      utils/make-5x5x5-step10-prune-table-LFRB.py
  31. 105
    0
      utils/make-5x5x5-step10-prune-table-UFDB.py
  32. 12
    0
      utils/print-solved.py
  33. 11
    0
      utils/prune-table-step-count-only.py
  34. 107
    0
      utils/rotate-printer.py
  35. 207540
    0
      utils/states-to-find-lookup-table-5x5x5-step10-UD-centers-stage.txt
  36. 159
    0
      utils/test.py
  37. 331
    0
      utils/test_cubes.json
  38. 831
    0
      utils/test_cubes.json.huge
  39. BIN
      www/Arrow-Next.png
  40. BIN
      www/Arrow-Prev.png
  41. 23
    0
      www/solution.js

+ 8
- 0
.gitignore View File

@@ -87,3 +87,11 @@ ENV/
87 87
 
88 88
 # Rope project settings
89 89
 .ropeproject
90
+
91
+lookup-table-*.txt
92
+*.swp
93
+cache
94
+lookup-table-*-deep*
95
+not_used
96
+binary-search
97
+*.hash

+ 55
- 0
README.md View File

@@ -0,0 +1,55 @@
1
+# rubiks-cube-NxNxN-solver
2
+
3
+## Overview
4
+This is a work in progress...here is what works so far:
5
+* 2x2x2 works
6
+* 3x3x3 works via the kociemba solver
7
+* 4x4x4 works, average solution is 65 moves
8
+* 5x5x5 works, average solution is 119 moves
9
+* 6x6x6 works, average solution is 214 moves
10
+* 7x7x7 works, average solution is 304 moves
11
+* NxNxN even cubes, am testing with a 14x14x14, not working yet
12
+* NxNxN odd cubes, am testing with a 15x15x15, not working yet
13
+
14
+All cubes 4x4x4 and larger follow the same basic approach:
15
+* Solve centers
16
+* Pair edges
17
+* Solve as 3x3x3
18
+
19
+## Install
20
+
21
+### Install 3x3x3 solver
22
+The kociemba solver is required to solve the larger cubes that have been
23
+reduced to 3x3x3.
24
+
25
+```
26
+$ git clone https://github.com/dwalton76/kociemba.git
27
+$ cd ~/kociemba/kociemba/ckociemba/
28
+$ make
29
+$ sudo make install
30
+```
31
+
32
+### Install the rubikscubennnsolver python module
33
+```
34
+$ git clone https://github.com/dwalton76/rubiks-cube-NxNxN-solver.git
35
+$ cd rubiks-cube-NxNxN-solver
36
+$ sudo python2 setup.py install
37
+$ gunzip lookup-table*.gz
38
+```
39
+
40
+## Usage
41
+Run rubiks-cube-solver.py where --state is your cube state in kociemba
42
+order (URFDLB). You must run rubiks-cube-solver.py from the directory that
43
+holds your lookup-table\*.txt files
44
+
45
+Example:
46
+```
47
+$ cd ~/rubiks-cube-NxNxN-solver
48
+$ ./usr/bin/rubiks-cube-solver.py --state LFBDUFLDBUBBFDFBLDLFRDFRRURFDFDLULUDLBLUUDRDUDUBBFFRBDFRRRRRRRLFBLLRDLDFBUBLFBLRLURUUBLBDUFUUFBD
49
+```
50
+
51
+## History
52
+One of my hobbies is building Lego Mindstorms robots that can solve rubiks cubes. I was able to find solvers for 2x2x2, 3x3x3, 4x4x4 and 5x5x5 but I couldn't find a solver for anything larger than that :(  The solvers that I did find for 4x4x4 and 5x5x5 took quite a bit of RAM (several gigs) but I wanted to be able to run the solver on a Lego Mindstorms EV3 which is 300Mhz and 64M of RAM. So I decided to write my own solver and here we are :)
53
+
54
+Here is the thread on speedsolving.com where I first posted looking for solvers. I ended up posting updates to this thread as my solver evolved:
55
+https://www.speedsolving.com/forum/threads/5x5x5-6x6x6-7x7x7-or-nxnxn-solvers.63592/

+ 47
- 0
TODO.txt View File

@@ -0,0 +1,47 @@
1
+
2
+DONE - remove max_depth everywhere
3
+DONE - remove prune_table boolean? I don't think this is used
4
+- build these deeper and convert to .bin file using pack()
5
+    - 4x4x4-ULFRBD-centers-stage - currently 6-deep
6
+    - 5x5x5-UD-centers-stage - currently 6-deep
7
+    - 5x5x5-LR-centers-stage would be 165 million so uses IDA...could probably build all 165million via .bin and avoid IDA here
8
+    - 6x6x6-UD-oblique-edge-pairing - currently 6-deep
9
+    - 6x6x6-LR-oblique-edge-pairing would be 165 million so uses IDA...could probably build all 165million via .bin and avoid IDA here
10
+
11
+
12
+DONE - build 5x5x5-ULFRBD-centers-solve deeper? I built a much better prune table
13
+- build 6x6x6-LFRB-solve-inner-x-center-and-oblique-edges deeper?  Need a clever way to compress it
14
+
15
+
16
+
17
+
18
+solutions.html
19
+DONE - need an arrow that loads the next step of instructions
20
+- highlight the face/rows that you should be turning
21
+- have a play/pause button so that it auto increments every 5s
22
+
23
+
24
+444 solver
25
+- look into the 52 moves tables xyzzy is using
26
+
27
+
28
+NNN solver
29
+- work on centers for 14x14x14
30
+
31
+
32
+DONE - rotate_xxx() can we compress the lists down to a few lists additions?
33
+same for state_functions
34
+
35
+
36
+4x4x4 Tsai...could we combine first two phases?
37
+        If you solved LR from the start that table would have 24!/(4!*4!*16!) or 51482970
38
+        If you stage UD and FB from the start that table would be 24!/(8!*8!*8!) or 9465511770
39
+        If you solved LR and stage UD and FB from the start that table would be 24!/(4!*4!*8!*8!) or 662585823900
40
+        The orient_table has 2704156
41
+
42
+        The main table would be 24!/(4!*4!*8!*8!) * 2704156 or 1791735431214128400
43
+        51482970/1791735431214128400     is 0.0000000000287336 so that would be very very slow
44
+        9465511770/1791735431214128400   is 0.0000000052828736 so that might be doable but this is borderline
45
+            but requires you to build a prune table that is 9.4 billion entries
46
+        662585823900/1791735431214128400 is 0.0000003698011505 so that would work
47
+            but requires you to build a prune table that is 662 billion entries

+ 17
- 0
misc/NOTES-2x2x2.txt View File

@@ -0,0 +1,17 @@
1
+
2
+Using solver from stackoverflow
3
+===============================
4
+2x2x2 min solution 6 steps
5
+2x2x2 max solution 10 steps
6
+2x2x2 avg solution 8 steps
7
+
8
+5.06user 0.08system 0:05.26elapsed 97%CPU (0avgtext+0avgdata 12336maxresident)k
9
+
10
+
11
+Using lookup table
12
+==================
13
+2x2x2 min solution 6 steps
14
+2x2x2 max solution 10 steps
15
+2x2x2 avg solution 8 steps
16
+
17
+0.08user 0.00system 0:00.08elapsed 100%CPU (0avgtext+0avgdata 12432maxresident)k

+ 201
- 0
misc/NOTES-4x4x4.txt View File

@@ -0,0 +1,201 @@
1
+These are the numbers from running --test against the 4x4x4 test cases
2
+
3
+4x4x4 original numbers
4
+======================
5
+4x4x4 min solution 172 steps
6
+4x4x4 max solution 415 steps
7
+4x4x4 avg solution 285 steps
8
+10.496s to test all 50
9
+
10
+
11
+Look at unpaired edges and do the one that solves
12
+the most edges in the least amount of moves
13
+=================================================
14
+4x4x4 min solution 132 steps
15
+4x4x4 max solution 335 steps
16
+4x4x4 avg solution 228 steps
17
+
18
+
19
+OLL/PLL parity avoided...exit on first parity free center setup
20
+===============================================================
21
+4x4x4 min solution 120 steps
22
+4x4x4 max solution 177 steps
23
+4x4x4 avg solution 148 steps
24
+real    1m0.480s
25
+user    0m53.284s
26
+sys 0m1.980s
27
+
28
+
29
+Simplified some code to make troubleshooting easier
30
+and to run faster. Without using a centers lookup table.
31
+===================================================
32
+4x4x4 min solution 138 steps
33
+4x4x4 max solution 190 steps
34
+4x4x4 avg solution 153 steps
35
+
36
+10.78user 0.23system 0:11.55elapsed 95%CPU (0avgtext+0avgdata 15568maxresident)k
37
+
38
+
39
+With center lookup table with a depth of 5
40
+===================================================
41
+4x4x4 min solution 124 steps
42
+4x4x4 max solution 190 steps
43
+4x4x4 avg solution 153 steps
44
+
45
+12.66user 2.25system 0:15.34elapsed 97%CPU (0avgtext+0avgdata 388084maxresident)k
46
+
47
+
48
+With center lookup table with a depth of 6
49
+===================================================
50
+4x4x4 min solution 126 steps
51
+4x4x4 max solution 172 steps
52
+4x4x4 avg solution 149 steps
53
+
54
+60.45user 32.45system 1:39.96elapsed 92%CPU (0avgtext+0avgdata 6016192maxresident)k
55
+
56
+
57
+With binary search through lookup table instead of loading into dictionary
58
+This went from 1m 39s to 15s!!!
59
+==========================================================================
60
+4x4x4 min solution 126 steps
61
+4x4x4 max solution 172 steps
62
+4x4x4 avg solution 149 steps
63
+
64
+14.53user 0.40system 0:15.53elapsed 96%CPU (0avgtext+0avgdata 15684maxresident)k
65
+
66
+
67
+With lookup tables:
68
+- lookup-table-4x4x4-centers-UD.txt
69
+- lookup-table-4x4x4-centers-LFRB.txt
70
+==========================================================================
71
+4x4x4 min solution 85 steps
72
+4x4x4 max solution 113 steps
73
+4x4x4 avg solution 97 steps
74
+
75
+9.51user 2.31system 0:12.42elapsed 95%CPU (0avgtext+0avgdata 16692maxresident)k
76
+
77
+
78
+With OLL and PLL detection working
79
+- group_edges now solves with each of the 12 edges as the first edge to solve
80
+  and keeps the one with the lowest score.  This makes it a bit slower though.
81
+==========================================================================
82
+4x4x4 min solution 83 steps
83
+4x4x4 max solution 110 steps
84
+4x4x4 avg solution 93 steps
85
+
86
+40s
87
+
88
+
89
+Added pair_six_444_edges()
90
+==========================================================================
91
+4x4x4 min solution 73 steps
92
+4x4x4 max solution 94 steps
93
+4x4x4 avg solution 83 steps
94
+
95
+4.43user 0.06system 0:04.59elapsed 97%CPU (0avgtext+0avgdata 20436maxresident)k
96
+
97
+
98
+Added
99
+- lookup-table-4x4x4-edges-slice-backward.txt
100
+- lookup-table-4x4x4-edges-slice-forward.txt
101
+=============================================
102
+4x4x4 min solution 66 steps
103
+4x4x4 max solution 90 steps
104
+4x4x4 avg solution 75 steps
105
+
106
+8.55user 0.34system 0:09.02elapsed 98%CPU (0avgtext+0avgdata 22132maxresident)k
107
+
108
+
109
+Added pair_four_444_edges()
110
+===========================
111
+4x4x4 min solution 65 steps
112
+4x4x4 max solution 88 steps
113
+4x4x4 avg solution 74 steps
114
+
115
+
116
+Added pair_last_six_edges_444()
117
+===============================
118
+4x4x4 min solution 63 steps
119
+4x4x4 max solution 88 steps
120
+4x4x4 avg solution 70 steps
121
+
122
+
123
+I forget what I changed :(
124
+==========================
125
+4x4x4 min solution 61 steps
126
+4x4x4 max solution 90 steps
127
+4x4x4 avg solution 68.0 steps
128
+4x4x4 avg centers solution 19.0 steps
129
+4x4x4 avg edges solution 27.0 steps
130
+
131
+7.55user 0.28system 0:08.19elapsed 95%CPU (0avgtext+0avgdata 12784maxresident)k
132
+23576inputs+0outputs (0major+65360minor)pagefaults 0swaps
133
+
134
+
135
+Changed state from dict to list
136
+===============================
137
+No change in move count but it does run a bit faster
138
+6.92user 0.22system 0:07.26elapsed 98%CPU (0avgtext+0avgdata 12712maxresident)k
139
+
140
+
141
+Sometimes accept a PLL edge pairing solution
142
+============================================
143
+The max solution drop from 90 to 79...solving PLL takes 12 moves so if all
144
+effecient edge pairing solutions lead to PLL just accept it and solve the
145
+PLL.
146
+
147
+4x4x4 min solution 59 steps
148
+4x4x4 max solution 79 steps
149
+4x4x4 avg solution 67.0 steps
150
+4x4x4 avg centers solution 19.0 steps
151
+4x4x4 avg edges solution 27.0 steps
152
+
153
+
154
+Consider number of wings paired by solution to solve centers
155
+============================================================
156
+4x4x4 min solution 59 steps
157
+4x4x4 max solution 75 steps
158
+4x4x4 avg solution 66.0 steps
159
+4x4x4 avg centers solution 18.0 steps
160
+4x4x4 avg edges solution 27.0 steps
161
+
162
+
163
+Use recursion for edge pairing and 4x4x4-edges
164
+==============================================
165
+The 4x4x4-edges table was only 9-moves deep here so this was just the beginning
166
+
167
+4x4x4 min solution 59 steps (FUULURFFRLRBDDDULUDFLFBBFUURRRUBLBLBDLUBDBULDDRDFLFBBRDBFDBLRBLDULUFFRLRDLDBBRLRUFFRUBFDUDFRLFRU)
168
+4x4x4 max solution 76 steps (FUBLRFFUBBBFFFRFURFFDDDRFRDDDRFLRDBRBRLFLBULRBUBUURRDUUDLLLRUURBDBLDURLDDRFFBULBULLLDFUFBBDLDUBL)
169
+4x4x4 avg solution 66.0 steps
170
+4x4x4 avg centers solution 19.0 steps
171
+4x4x4 avg edges solution 25.0 steps
172
+
173
+13.91user 0.70system 0:14.83elapsed 98%CPU (0avgtext+0avgdata 16640maxresident)
174
+
175
+
176
+Replaced all copy.copy with list slices...small speed improvement
177
+=================================================================
178
+13.59user 0.74system 0:14.57elapsed 98%CPU (0avgtext+0avgdata 16068maxresident)k
179
+
180
+
181
+Replaced all range() with xrange()
182
+==================================
183
+13.41user 0.62system 0:14.26elapsed 98%CPU (0avgtext+0avgdata 16076maxresident)k
184
+
185
+
186
+
187
+2017-08-02
188
+==========
189
+4x4x4 avg centers solution 19.0 steps
190
+4x4x4 avg edges solution 25.0 steps
191
+4x4x4 avg solution 65.0 steps
192
+4x4x4 min solution 59 steps (FUULURFFRLRBDDDULUDFLFBBFUURRRUBLBLBDLUBDBULDDRDFLFBBRDBFDBLRBLDULUFFRLRDLDBBRLRUFFRUBFDUDFRLFRU)
193
+4x4x4 max solution 75 steps (FLDRFFBLFDLRURUUFBUBLRUFUDUBULLFRUBLRRRUDFDFDUBBRRDLUFDDRFBDDFBDDDRFDLUDBLRBLFFBUFBLLLURRBBLRULB)
194
+
195
+13.22user 0.50system 0:13.98elapsed 98%CPU (0avgtext+0avgdata 15672maxresident)k
196
+
197
+
198
+unroll 4x4x4 state loops
199
+========================
200
+11.62user 0.47system 0:12.28elapsed 98%CPU (0avgtext+0avgdata 15740maxresident)k
201
+

+ 185
- 0
misc/NOTES-5x5x5.txt View File

@@ -0,0 +1,185 @@
1
+
2
+********************
3
+Main 5x5x5 test cube
4
+********************
5
+
6
+Baseline
7
+========
8
+190 steps to solve centers
9
+175 steps to group edges
10
+19 steps to solve 3x3x3
11
+384 steps total
12
+
13
+
14
+
15
+
16
+*************
17
+50 Test Cubes
18
+*************
19
+
20
+Initial solve for all 50 5x5x5 test cubes...this took some doing
21
+================================================================
22
+5x5x5 min solution 327 steps
23
+5x5x5 max solution 453 steps
24
+5x5x5 avg solution 387 steps
25
+1m 3s
26
+
27
+
28
+Lookup tables for centers
29
+edge pairing can do 3 on slice forward and 3 on slice back
30
+==========================================================
31
+5x5x5 min solution 107 steps
32
+5x5x5 max solution 169 steps
33
+5x5x5 avg solution 130 steps
34
+Takes a while...10m or so
35
+
36
+
37
+Use 4x4x4 to pair outer edges first
38
+This takes more moves than the old way but the code is a
39
+million times cleaner and it runs in 1min instead of 10min
40
+==========================================================
41
+5x5x5 min solution 124 steps
42
+5x5x5 max solution 190 steps
43
+5x5x5 avg solution 146 steps
44
+
45
+59.08user 3.60system 1:02.79elapsed 99%CPU (0avgtext+0avgdata 13604maxresident)k
46
+
47
+
48
+More 5x5x5 edge pairing cleanup
49
+===============================
50
+5x5x5 min solution 121 steps
51
+5x5x5 max solution 172 steps
52
+5x5x5 avg solution 141.0 steps
53
+5x5x5 avg centers solution 36.0 steps
54
+5x5x5 avg edges solution 84.0 steps
55
+
56
+44.12user 3.97system 0:59.96elapsed 80%CPU (0avgtext+0avgdata 13264maxresident)k
57
+
58
+
59
+Changed state from dict to list
60
+===============================
61
+No change in move count but it does run 6s faster
62
+38.03user 3.12system 0:41.25elapsed 99%CPU (0avgtext+0avgdata 12836maxresident)k
63
+
64
+
65
+Use init_wing_to_pair approach...saves 6 steps but doubles the time
66
+===================================================================
67
+5x5x5 min solution 119 steps
68
+5x5x5 max solution 153 steps
69
+5x5x5 avg solution 135.0 steps
70
+5x5x5 avg centers solution 36.0 steps
71
+5x5x5 avg edges solution 78.0 steps
72
+
73
+81.05user 4.18system 1:25.34elapsed 99%CPU (0avgtext+0avgdata 12972maxresident)k
74
+
75
+
76
+After lots of misc IDA fixes
77
+============================
78
+5x5x5 min solution 113 steps
79
+5x5x5 max solution 177 steps
80
+5x5x5 avg solution 141.0 steps
81
+5x5x5 avg centers solution 40.0 steps
82
+5x5x5 avg edges solution 79.0 steps
83
+
84
+
85
+After some 4x4x4 edge pairing fixes
86
+===================================
87
+The max solution case went from 177 to 157!!!
88
+
89
+5x5x5 min solution 114 steps
90
+5x5x5 max solution 157 steps
91
+5x5x5 avg solution 135.0 steps
92
+5x5x5 avg centers solution 40.0 steps
93
+5x5x5 avg edges solution 73.0 steps
94
+
95
+
96
+Use recursion for edge pairing and 4x4x4-edges
97
+==============================================
98
+The 4x4x4-edges table was only 9-moves deep here so this was just the beginning
99
+
100
+5x5x5 min solution 118 steps
101
+5x5x5 max solution 163 steps
102
+5x5x5 avg solution 141.0 steps
103
+5x5x5 avg centers solution 40.0 steps
104
+5x5x5 avg edges solution 80.0 steps
105
+
106
+
107
+Added NoIDASolution to dynamically 'solve' prune
108
+table when the IDA search it taking too long. This
109
+takes longer to run but chops 3 moves from centers
110
+solution.
111
+==================================================
112
+5x5x5 min solution 119 steps (LFUUFUBRBDRBUUFLLURLBRBFURFRLUDBBFRBDRURUBDDDDBDBULDURFFFFRFBBFUDBRRLLRLULRUBLUFRFDFUFLDFLRLLDDDFLRBFBDFDBDDUURBLFLLRLURBDDUBLRFUDFULUDURBFBLLRDBLBFDR)
113
+5x5x5 max solution 163 steps (LLBLDDFUBBUDUFDLDRRFUUDRRULBLBLLBUFDURURDBLDBDRFULRRFDBRRLLFUDFURULBRFFRFFLLBUDBFFDFFDRDRRRBLDBDLFLDBBBBFUULUDLFLDLURFURRBLRURDRUUUBBFBURBFBDDBLFFDLUF)
114
+5x5x5 avg solution 139.0 steps
115
+5x5x5 avg centers solution 37.0 steps
116
+5x5x5 avg edges solution 81.0 steps
117
+
118
+
119
+Edge pairing improvements
120
+- no longer use 4x4x4 first
121
+- recurse through all edge combos
122
+- edge prune estimate of 3
123
+==================================================
124
+5x5x5 min solution 101 steps (RDBFBUDDLRDRUULLFFDDFBBDDLLUDLRUUBRRLRBBBRUDBBDDFRUURRFDRLRULBFLUFLDFLFFLDDLRBBLLFDLBFBDBFFRFFRRFRBBBLRBRRLDUFURLLDUBFBUFLLUUUDULDUBFDBDUBRFRURUFUUFLD)
125
+5x5x5 max solution 133 steps (URUBFUUFRDFFUUFLRDBLLBDDDLUULRDLDUBDLRBBLFLBRBFUUBBRBFFUDLFLLBFUFUDRLBFUBBURRLLRUFRDUFFDFRFUBRBBDRFRFLLFURLLFBRBLUDRDDRRDRRFDUDLFLDLUUDUDBRBBBRBDDLDFL)
126
+5x5x5 avg solution 119.0 steps
127
+5x5x5 avg centers solution 36.0 steps
128
+5x5x5 avg edges solution 62.0 steps
129
+
130
+
131
+- edge prune estimate of 2.5
132
+==================================================
133
+# It is 4 moves shorter but takes much longer to run
134
+5x5x5 min solution 101 steps (RDBFBUDDLRDRUULLFFDDFBBDDLLUDLRUUBRRLRBBBRUDBBDDFRUURRFDRLRULBFLUFLDFLFFLDDLRBBLLFDLBFBDBFFRFFRRFRBBBLRBRRLDUFURLLDUBFBUFLLUUUDULDUBFDBDUBRFRURUFUUFLD)
135
+5x5x5 max solution 132 steps (URUBFUUFRDFFUUFLRDBLLBDDDLUULRDLDUBDLRBBLFLBRBFUUBBRBFFUDLFLLBFUFUDRLBFUBBURRLLRUFRDUFFDFRFUBRBBDRFRFLLFURLLFBRBLUDRDDRRDRRFDUDLFLDLUUDUDBRBBBRBDDLDFL)
136
+5x5x5 avg solution 115.0 steps
137
+5x5x5 avg centers solution 36.0 steps
138
+5x5x5 avg edges solution 58.0 steps
139
+
140
+
141
+2017-08-02
142
+==========
143
+5x5x5 avg centers solution 36.0 steps
144
+5x5x5 avg edges solution 62.0 steps
145
+5x5x5 avg solution 119.0 steps
146
+5x5x5 min solution 101 steps (RDBFBUDDLRDRUULLFFDDFBBDDLLUDLRUUBRRLRBBBRUDBBDDFRUURRFDRLRULBFLUFLDFLFFLDDLRBBLLFDLBFBDBFFRFFRRFRBBBLRBRRLDUFURLLDUBFBUFLLUUUDULDUBFDBDUBRFRURUFUUFLD)
147
+5x5x5 max solution 133 steps (URUBFUUFRDFFUUFLRDBLLBDDDLUULRDLDUBDLRBBLFLBRBFUUBBRBFFUDLFLLBFUFUDRLBFUBBURRLLRUFRDUFFDFRFUBRBBDRFRFLLFURLLFBRBLUDRDDRRDRRFDUDLFLDLUUDUDBRBBBRBDDLDFL)
148
+
149
+432.35user 10.22system 7:34.63elapsed 97%CPU (0avgtext+0avgdata 118340maxresident)k
150
+
151
+
152
+unroll 5x5x5 state loops
153
+========================
154
+401.68user 9.84system 7:08.33elapsed 96%CPU (0avgtext+0avgdata 118356maxresident)k
155
+
156
+
157
+
158
+WIP on switching to hash...why is this slower?
159
+==============================================
160
+299.76user 25.02system 8:35.15elapsed 63%CPU (0avgtext+0avgdata 96700maxresident)k
161
+
162
+
163
+with edge estimate 3.5 we get
164
+=============================
165
+5x5x5 avg centers solution 36.0 steps
166
+5x5x5 avg edges solution 68.0 steps
167
+5x5x5 avg solution 125.0 steps
168
+5x5x5 min solution 101 steps (FFRUUUDRFBFLUUURRFLLBBFFRFBBURFURUBFBRFBLUURBRFLFLULRLURBBDDLFFUURDRFDFUBDDDRDRBFRFLLUDDBDUFBLRRBDDBLFDFLRBLRBRLLRFDLDFDFLLULBLUDUDUUBULLBDBUDDBLDBRRD)
169
+5x5x5 max solution 141 steps (BLFUFDBFLFURULRBURFLDURLRDUBLRBLLLDLDRLFUFFURLULBBBRDBFBDDLUDRFUBBFBDFFRBRFDDUFULRRBLRBDBUDBUDRDRFLLRFRULDRDURBDLFLRBBDLBBFDLUFDFUFFUUBLUBLDURFRDUDUFR)
170
+
171
+175.40user 18.62system 5:25.17elapsed 59%CPU (0avgtext+0avgdata 96420maxresident)k
172
+
173
+
174
+with edge estimate 4.0 we get
175
+=============================
176
+5x5x5 avg centers solution 36.0 steps
177
+5x5x5 avg edges solution 71.0 steps
178
+5x5x5 avg solution 128.0 steps
179
+5x5x5 min solution 101 steps (FFRUUUDRFBFLUUURRFLLBBFFRFBBURFURUBFBRFBLUURBRFLFLULRLURBBDDLFFUURDRFDFUBDDDRDRBFRFLLUDDBDUFBLRRBDDBLFDFLRBLRBRLLRFDLDFDFLLULBLUDUDUUBULLBDBUDDBLDBRRD)
180
+5x5x5 max solution 149 steps (DFFURRULDLDLURLBDDRRBFRURFBFBFRBDLBBFRBLRFBRBBFLULDLBLULLFRUBUFLDFFLDULDDLUURRDRFBRLULUDRBDUUUBBRFFDBDFURDBBDDRULBUDRDLLLBDRFDLRDLLFDBBUFBRURFFUFFUUFU)
181
+
182
+143.13user 14.92system 4:06.53elapsed 64%CPU (0avgtext+0avgdata 98072maxresident)k
183
+
184
+
185
+

+ 31
- 0
misc/NOTES-6x6x6.txt View File

@@ -0,0 +1,31 @@
1
+
2
+Initial Numbers
3
+===============
4
+6x6x6 min solution 190 steps (DBURDLDLFBDUUFRBUULFFBUFFBUFUBFFBBLDLLRLBULBLRFUDBURBFLLUBFDLDLDRRFRRDRFLUDDFFDBFUFDUBDLBRDRRLRUDRLURDRURLFRURFBLUBBLRDUDDDUDDRRLLLLLUDRRFRBLUFDBFRFRUFLUDURUDDFUBUBDUUFBLFDLRDUBLRBBBBRLLLFRFUDLBRFBFFLFBLBBDRDFURUFBDB)
5
+6x6x6 max solution 296 steps (RURLLLDBBUULDUDURRFUBFULURLDLRLDFFFRUBDUFFBDRRFFLDRRUULDLLFUBDFULDLFLLBRDRLDRBRUDLRLDBRFLBUBFDBDDRDDFLDUBBDULFDRLBDLFBFRBFUUFUBRUBRULFLFBURRRURDFFRRBBFDFFLDFRBRLBLRFBBBRURLBBFUURBFUUFFFULDULBDFULDLDFBLDDBLRDBUUBBRDUD)
6
+6x6x6 avg solution 245.0 steps
7
+6x6x6 avg centers solution 96.0 steps
8
+6x6x6 avg edges solution 105.0 steps
9
+
10
+We currently do not avoid OLL or PLL (it takes too long) so it
11
+takes us 45 moves to solve 3x3x3 at the end
12
+
13
+
14
+
15
+Prevent OLL and avoid PLL (most of the time)
16
+============================================
17
+6x6x6 min solution 190 steps (DBURDLDLFBDUUFRBUULFFBUFFBUFUBFFBBLDLLRLBULBLRFUDBURBFLLUBFDLDLDRRFRRDRFLUDDFFDBFUFDUBDLBRDRRLRUDRLURDRURLFRURFBLUBBLRDUDDDUDDRRLLLLLUDRRFRBLUFDBFRFRUFLUDURUDDFUBUBDUUFBLFDLRDUBLRBBBBRLLLFRFUDLBRFBFFLFBLBBDRDFURUFBDB)
18
+6x6x6 max solution 273 steps (RFUUFDBDDLBBLBUBBRLRURFBRDLLDDBLRBDDBRRUDFLFURFDFRRULFUUDDULUBRLUFRBDDBFRUDRLLRRFDBBRLFFDRBDFFBFFBRUFRDDBLDDFLDFRBULFFRRLBDRBRLBBLLFLLUBUULBBBFUFLUDUURRRULFUDRLRFFFUBBDRUDUDUUFBDLRLULRLUBFLDLBDFLBFFBDLDULDRFRUFLDUUUB)
19
+6x6x6 avg solution 232.0 steps
20
+6x6x6 avg centers solution 108.0 steps
21
+6x6x6 avg edges solution 103.0 steps
22
+
23
+
24
+5x5x5 edge pairing improvements
25
+===============================
26
+6x6x6 min solution 189 steps (LDBRDBFURDLRFLLLRFFBDFRFFDBLULDURBRFRURRDUBDFFBUUDRRUDDRLRFDFFUBBBDFUUDDLRBLFDRUDLFDFRUBFFLLLBUBLDDDRLFDURRLUBBDBBURBFRLRLDFRRLLDUBFDLUFFRBLDRLBUUDLRFDBURDBLDFBDLUFUBLBBLULUULLUDFRRFUDFFRBURRLLFRFBUFDDUBBUFBULURBBLBU)
27
+6x6x6 max solution 237 steps (LBFDLRURFLUUUDRRFDLLLBURLRBBUBFRDURFLRURBBBBBRDFRDRDDRULLUFLLBFFUFFDLRFFDDFLFURLUULDBRBUFFDBFDBBFFLDRFBFLUDRDUDFLURLURFRDURBUFLDLLBBBBFUFDDDULBDBRRFBLURRBDUUFDFDDBRBFDFBUUBLULUDBFRUURURULBRLDLFLDURLBLFURRDDDLFLRDBBLB)
28
+6x6x6 avg solution 214.0 steps
29
+6x6x6 avg centers solution 106.0 steps
30
+6x6x6 avg edges solution 86.0 steps
31
+

+ 10
- 0
misc/NOTES-7x7x7.txt View File

@@ -0,0 +1,10 @@
1
+
2
+init numbers
3
+============
4
+DUULUFDLULFLLURLUDLBDDBFFLRDRRLDDLDDBDBFFFFBBURLLRBRFLDBUDFBDRFRBFRUUUBFRFRRDFLBDURRBLFFU
5
+7x7x7 min solution 290 steps (DBDBDDFBDDLUBDLFRFRBRLLDUFFDUFRBRDFDRUFDFDRDBDBULDBDBDBUFBUFFFULLFLDURRBBRRBRLFUUUDUURBRDUUURFFFLRFLRLDLBUFRLDLDFLLFBDFUFRFFUUUFURDRFULBRFURRBUDDRBDLLRLDLLDLUURFRFBUBURBRUDBDDLRBULBULUBDBBUDRBLFFBLRBURRUFULBRLFDUFDDBULBRLBUFULUDDLLDFRDRDBBFBUBBFLFFRRUFFRLRRDRULLLFRLFULBLLBBBLDFDBRBFDULLULRFDBR)
6
+7x7x7 max solution 322 steps (DBRFBBRLRBULFDLBBURDLBRRUUFLUFDFUFRRBUBUFRRLUFLDULBFUDLUDBDFDDFDFLDUFFFUFRRLDBBFUDUFDLRLUBUDRRRDBFUFFUBFBURLDDFLURDUFFRDRDFDLDDBURULULDDBDDRFFFBLFBDULRURRDUDUURUBLLLDFUDDDDRBUBULLRBRRLDUBLLLBLRBLDBUBLFFBDURFRURBBBBRRRUFFLLLLULFBBBLLFLBLRBFBDRRBLFUDLFULDLRRFURDRBFFUUBRBBLDBDLRBLULFBFDRDFRURFFDU)
7
+7x7x7 avg solution 304.0 steps
8
+7x7x7 avg centers solution 156.0 steps
9
+7x7x7 avg edges solution 127.0 steps
10
+

+ 85
- 0
misc/TODO.txt View File

@@ -0,0 +1,85 @@
1
+
2
+lrzip comparison
3
+
4
+'lrzip lookup-table-4x4x4-step10-solve-UD-centers.txt'
5
+dwalton@laptop ~/l/r/NxNxN> ls -lh lookup-table-4x4x4-step10-solve-UD-centers.txt*
6
+-rw-rw-r-- 1 dwalton dwalton 1.2G Mar 27 07:51 lookup-table-4x4x4-step10-solve-UD-centers.txt
7
+-rw-rw-r-- 1 dwalton dwalton 165M Mar 27 15:49 lookup-table-4x4x4-step10-solve-UD-centers.txt.lrz
8
+dwalton@laptop ~/l/r/NxNxN>
9
+
10
+
11
+'lrzip -z lookup-table-4x4x4-step10-solve-UD-centers.txt'
12
+dwalton@laptop ~/l/r/NxNxN> ls -lh lookup-table-4x4x4-step10-solve-UD-centers.txt*
13
+-rw-rw-r-- 1 dwalton dwalton 1.2G Mar 27 07:51 lookup-table-4x4x4-step10-solve-UD-centers.txt
14
+-rw-rw-r-- 1 dwalton dwalton 132M Mar 27 16:01 lookup-table-4x4x4-step10-solve-UD-centers.txt.lrz
15
+dwalton@laptop ~/l/r/NxNxN>
16
+dwalton@laptop ~/l/r/NxNxN>
17
+
18
+
19
+
20
+
21
+- code cleanup
22
+
23
+4x4x4 last two edges
24
+====================
25
+- Try with and without the tweak that pairs two edges at once. This should be
26
+  faster most of the time but there are cases where if we do this it always
27
+  leads to PLL parity. Basically resolve the edges with this enabled and if it
28
+  cannot find a PLL free solution then try without.
29
+
30
+- re-read the "pair 6 edges at once" page
31
+
32
+
33
+
34
+Misc
35
+====
36
+- redo how 5x5x5 centers are solved, it is a mess, use the following instead
37
+    http://rubik.rthost.org/5x5x5_centres.htm
38
+
39
+- redo how 555 edge parity IDs are assigned, this is also a mess at the moment
40
+
41
+
42
+5x5x5 UD center options
43
+=======================
44
+Do all three of these and see which approach solves UD centers with the fewest moves.
45
+Obviously #1 will be more moves than #2 but #2 requires a pretty huge lookup table so
46
+do this comparison to see if #2 is worth it.
47
+
48
+#1 - Solve U centers, then D center edges and corners while preserving U
49
+#2 - Solve U centers (lookup table), then D center edges and corners while preserving U
50
+#3 - Solve UD center corners (lookup table), then U center edges while preserving D, then D center edges while preserving U
51
+#4 - Solve UD center corners (lookup table), then U center edges without preserving D, then D center edges and corners while preserving U
52
+
53
+
54
+5x5x5 last two edges
55
+====================
56
+- find the patterns that require multiple parity ID sequences...create new parity IDs
57
+  from these and once we have them all, drop the "while True" loop in resolve_555_edge_parity()
58
+  and barf if we ever find a new one
59
+
60
+
61
+solver.py
62
+=========
63
+- clean up the use of pos vs square_index
64
+- clean up the use of wing vs edge
65
+
66
+- get_solution_score()
67
+    - Instead of counting the number of steps in a solution come up with a score for
68
+      how long we think the robot would take to solve it
69
+
70
+- compress_solution()
71
+    - combine "U U" into U2
72
+    - remove x,y,z steps
73
+
74
+- group_edges() and get_edge_we_can_solve_with_least_moves()
75
+    DONE - lot of cut-n-paste code
76
+    - Instead of calling move_wing_to_F_west just rotate xyz to get it there. This
77
+      should save us 5-10 moves in pairing edges for 4x4x4
78
+
79
+- bring in the python kociemba solver?
80
+
81
+
82
+2x2x2 solver
83
+============
84
+- ungolf the 2x2x2 solver
85
+    - wrap your head around how the hell this thing works

+ 2507
- 0
rubikscubennnsolver/LookupTable.py
File diff suppressed because it is too large
View File


+ 178
- 0
rubikscubennnsolver/RubiksCube222.py View File

@@ -0,0 +1,178 @@
1
+
2
+from rubikscubennnsolver import RubiksCube, SolveError
3
+from rubikscubennnsolver.LookupTable import LookupTable
4
+import logging
5
+
6
+log = logging.getLogger(__name__)
7
+
8
+
9
+moves_2x2x2 = ("U", "U'", "U2",
10
+               "L", "L'", "L2",
11
+               "F" , "F'", "F2",
12
+               "R" , "R'", "R2",
13
+               "B" , "B'", "B2",
14
+               "D" , "D'", "D2")
15
+solved_2x2x2 = 'UUUURRRRFFFFDDDDLLLLBBBB'
16
+
17
+
18
+class RubiksCube222(RubiksCube):
19
+
20
+    def __init__(self, state, order, colormap=None, debug=False):
21
+        RubiksCube.__init__(self, state, order, colormap, debug)
22
+
23
+        if debug:
24
+            log.setLevel(logging.DEBUG)
25
+
26
+    def phase(self):
27
+        return 'Solve 2x2x2'
28
+
29
+    def solve_non_table(self):
30
+        """
31
+        100% of the credit for this 2x2x2 solver goes to
32
+        http://codegolf.stackexchange.com/questions/35002/solve-the-rubiks-pocket-cube
33
+
34
+        In the codegolf challenge they defined the input as
35
+
36
+        - -   A B   - -   - -
37
+        - -   C D   - -   - -
38
+
39
+        E F   G H   I J   K L
40
+        M N   O P   Q R   S T
41
+
42
+        - -   U V   - -   - -
43
+        - -   W X   - -   - -
44
+
45
+        But normally we number cubes like this
46
+
47
+               01 02
48
+               03 04
49
+        05 06  09 10  13 14  17 18
50
+        07 08  11 12  15 16  19 20
51
+               21 22
52
+               23 24
53
+
54
+        So we will define the former layout as "scramble" and the latter as "normal".
55
+        Convert the normal layout (sys.argv[1] must be in the 'normal' layout) to
56
+        the scramble layout.
57
+        """
58
+
59
+        # 'normal' must be in U, R, F, D, L, B order
60
+        # This is the order used by the kociemba 3x3x3 solver so
61
+        # the rubiks-color-resolver uses this order
62
+        normal = self.get_kociemba_string(False)
63
+        log.info("NORMAL: %s" % normal)
64
+        upper = normal[0:4]
65
+        right = normal[4:8]
66
+        front = normal[8:12]
67
+        down = normal[12:16]
68
+        left = normal[16:20]
69
+        back = normal[20:24]
70
+
71
+        '''
72
+        from pprint import pformat
73
+        print "upper: %s" % pformat(upper)
74
+        print "right: %s" % pformat(right)
75
+        print "front: %s" % pformat(front)
76
+        print "down: %s" % pformat(down)
77
+        print "left: %s" % pformat(left)
78
+        print "back: %s" % pformat(back)
79
+        '''
80
+
81
+        scramble = []
82
+        scramble.extend(upper)
83
+        scramble.append(left[0])
84
+        scramble.append(left[1])
85
+        scramble.append(front[0])
86
+        scramble.append(front[1])
87
+        scramble.append(right[0])
88
+        scramble.append(right[1])
89
+        scramble.append(back[0])
90
+        scramble.append(back[1])
91
+
92
+        scramble.append(left[2])
93
+        scramble.append(left[3])
94
+        scramble.append(front[2])
95
+        scramble.append(front[3])
96
+        scramble.append(right[2])
97
+        scramble.append(right[3])
98
+        scramble.append(back[2])
99
+        scramble.append(back[3])
100
+        scramble.extend(down)
101
+
102
+        o = ''.join
103
+        data = [{''.join((' ', x)[x in scramble[12] + scramble[19] + scramble[22]]for x in scramble):[]},
104
+             {' ' * 4 + (scramble[12] * 2 + ' ' * 4 + scramble[19] * 2) * 2 + scramble[22] * 4:[]}]
105
+
106
+        from pprint import pprint
107
+        #pprint(data)
108
+
109
+        wtf_table = [
110
+            [0, 7, 2, 15, 4, 5, 6, 21, 16, 8, 3, 11, 12, 13, 14, 23, 17, 9, 1, 19, 20, 18, 22, 10],
111
+            [0, 7, 2, 15, 4, 5, 6, 21, 16, 8, 3, 11, 12, 13, 14, 23, 17, 9, 1, 19, 20, 18, 22, 10],
112
+            [0, 7, 2, 15, 4, 5, 6, 21, 16, 8, 3, 11, 12, 13, 14, 23, 17, 9, 1, 19, 20, 18, 22, 10],
113
+            [0, 7, 2, 15, 4, 5, 6, 21, 16, 8, 3, 11, 12, 13, 14, 23, 17, 9, 1, 19, 20, 18, 22, 10],
114
+            [2, 0, 3, 1, 6, 7, 8, 9, 10, 11, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
115
+            [2, 0, 3, 1, 6, 7, 8, 9, 10, 11, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
116
+            [2, 0, 3, 1, 6, 7, 8, 9, 10, 11, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
117
+            [2, 0, 3, 1, 6, 7, 8, 9, 10, 11, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
118
+            [0, 1, 13, 5, 4, 20, 14, 6, 2, 9, 10, 11, 12, 21, 15, 7, 3, 17, 18, 19, 16, 8, 22, 23],
119
+            [0, 1, 13, 5, 4, 20, 14, 6, 2, 9, 10, 11, 12, 21, 15, 7, 3, 17, 18, 19, 16, 8, 22, 23],
120
+            [0, 1, 13, 5, 4, 20, 14, 6, 2, 9, 10, 11, 12, 21, 15, 7, 3, 17, 18, 19, 16, 8, 22, 23],
121
+            [0, 1, 13, 5, 4, 20, 14, 6, 2, 9, 10, 11, 12, 21, 15, 7, 3, 17, 18, 19, 16, 8, 22, 23]
122
+        ]
123
+
124
+        for h in (0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1):
125
+            for s, x in data[h].items():
126
+                for y in xrange(12):
127
+
128
+                    data[h][s] = x + [y - [1, -1, 1, 3][h * y % 4]]
129
+
130
+                    if s in data[1 - h]:
131
+                        # pprint(data[0][s])
132
+                        # pprint(data[1][s])
133
+                        try:
134
+                            result = ''.join('RUF'[x / 4] + " 2'"[x % 4] for x in data[0][s] + data[1][s][::-1])
135
+                        except IndexError:
136
+                            print("Cube is already solved")
137
+                            sys.exit(0)
138
+
139
+                        result = result.replace('2', '2 ')
140
+                        result = result.replace("'", "' ")
141
+                        for step in result.strip().split():
142
+                            self.rotate(step)
143
+                        return
144
+
145
+                    s = ''.join(s[x] for x in wtf_table[y])
146
+
147
+        raise SolveError("Could not find a solution")
148
+
149
+    def solve_via_table(self):
150
+        '''
151
+        For grins I built a full lookup table for 2x2x2, it is too large to put in the
152
+        repo though and the solver from stackoverflow works just fine but I'll leave
153
+        this here for a rainy day.
154
+
155
+        lookup-table-2x2x2-solve.txt
156
+        ============================
157
+        1 steps has 18 entries (0 percent, 0.00x previous step)
158
+        2 steps has 244 entries (0 percent, 13.56x previous step)
159
+        3 steps has 2,874 entries (0 percent, 11.78x previous step)
160
+        4 steps has 28,000 entries (0 percent, 9.74x previous step)
161
+        5 steps has 205,416 entries (0 percent, 7.34x previous step)
162
+        6 steps has 1,168,516 entries (1 percent, 5.69x previous step)
163
+        7 steps has 5,402,628 entries (6 percent, 4.62x previous step)
164
+        8 steps has 20,776,176 entries (23 percent, 3.85x previous step)
165
+        9 steps has 45,391,616 entries (51 percent, 2.18x previous step)
166
+        10 steps has 15,139,616 entries (17 percent, 0.33x previous step)
167
+        11 steps has 64,736 entries (0 percent, 0.00x previous step)
168
+
169
+        Total: 88,179,840 entries
170
+        '''
171
+        self.lt = LookupTable(self, 'lookup-table-2x2x2-solve.txt', 'all', 'UUUULLLLFFFFRRRRBBBBDDDD')
172
+        self.lt.solve()
173
+        self.compress_solution()
174
+
175
+    def solve(self):
176
+        self.solve_non_table()
177
+        self.compress_solution()
178
+

+ 29
- 0
rubikscubennnsolver/RubiksCube333.py View File

@@ -0,0 +1,29 @@
1
+
2
+from rubikscubennnsolver import RubiksCube
3
+from rubikscubennnsolver.RubiksCube222 import moves_2x2x2
4
+import logging
5
+
6
+log = logging.getLogger(__name__)
7
+
8
+moves_3x3x3 = moves_2x2x2
9
+solved_3x3x3 = 'UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB'
10
+
11
+
12
+class RubiksCube333(RubiksCube):
13
+
14
+    def __init__(self, state, order, colormap=None, debug=False):
15
+        RubiksCube.__init__(self, state, order, colormap, debug)
16
+
17
+        if debug:
18
+            log.setLevel(logging.DEBUG)
19
+
20
+    def phase(self):
21
+        return 'Solve 3x3x3'
22
+
23
+    def solve(self):
24
+        self.rotate_U_to_U()
25
+        self.rotate_F_to_F()
26
+
27
+        if self.get_state_all() != 'UUUUUUUUULLLLLLLLLFFFFFFFFFRRRRRRRRRBBBBBBBBBDDDDDDDDD':
28
+            self.solve_333()
29
+            self.compress_solution()

+ 2700
- 0
rubikscubennnsolver/RubiksCube444.py
File diff suppressed because it is too large
View File


+ 1728
- 0
rubikscubennnsolver/RubiksCube555.py
File diff suppressed because it is too large
View File


+ 760
- 0
rubikscubennnsolver/RubiksCube666.py View File

@@ -0,0 +1,760 @@
1
+
2
+from collections import OrderedDict
3
+from pprint import pformat
4
+from rubikscubennnsolver import RubiksCube, ImplementThis
5
+from rubikscubennnsolver.RubiksCube444 import RubiksCube444, solved_4x4x4
6
+from rubikscubennnsolver.RubiksCube555 import RubiksCube555, solved_5x5x5
7
+from rubikscubennnsolver.RubiksSide import Side, SolveError
8
+from rubikscubennnsolver.LookupTable import LookupTable, LookupTableIDA, NoIDASolution
9
+import logging
10
+import math
11
+import os
12
+import random
13
+import re
14
+import subprocess
15
+import sys
16
+
17
+log = logging.getLogger(__name__)
18
+
19
+
20
+moves_6x6x6 = ("U", "U'", "U2", "Uw", "Uw'", "Uw2", "3Uw", "3Uw'", "3Uw2",
21
+               "L", "L'", "L2", "Lw", "Lw'", "Lw2", "3Lw", "3Lw'", "3Lw2",
22
+               "F" , "F'", "F2", "Fw", "Fw'", "Fw2", "3Fw", "3Fw'", "3Fw2",
23
+               "R" , "R'", "R2", "Rw", "Rw'", "Rw2", "3Rw", "3Rw'", "3Rw2",
24
+               "B" , "B'", "B2", "Bw", "Bw'", "Bw2", "3Bw", "3Bw'", "3Bw2",
25
+               "D" , "D'", "D2", "Dw", "Dw'", "Dw2", "3Dw", "3Dw'", "3Dw2")
26
+solved_6x6x6 = 'UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUURRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
27
+
28
+
29
+class RubiksCube666(RubiksCube):
30
+    """
31
+    6x6x6 strategy
32
+    - stage UD centers to sides U or D (use IDA)
33
+    - stage LR centers to sides L or R...this in turn stages FB centers to sides F or B
34
+    - solve all centers (use IDA)
35
+    - pair edges
36
+    - solve as 3x3x3
37
+    """
38
+
39
+    def __init__(self, state, order, colormap=None, debug=False):
40
+        RubiksCube.__init__(self, state, order, colormap)
41
+
42
+        if debug:
43
+            log.setLevel(logging.DEBUG)
44
+
45
+    def lt_init(self):
46
+        if self.lt_init_called:
47
+            return
48
+        self.lt_init_called = True
49
+
50
+        '''
51
+        Stage the inner X-centers
52
+        24!/(8!*16!) is 735,471
53
+
54
+        lookup-table-6x6x6-step10-UD-inner-x-centers-stage.txt
55
+        ======================================================
56
+        1 steps has 5 entries (0 percent, 0.00x previous step)
57
+        2 steps has 82 entries (0 percent, 16.40x previous step)
58
+        3 steps has 1,206 entries (0 percent, 14.71x previous step)
59
+        4 steps has 14,116 entries (1 percent, 11.70x previous step)
60
+        5 steps has 123,404 entries (16 percent, 8.74x previous step)
61
+        6 steps has 422,508 entries (57 percent, 3.42x previous step)
62
+        7 steps has 173,254 entries (23 percent, 0.41x previous step)
63
+        8 steps has 896 entries (0 percent, 0.01x previous step)
64
+
65
+        Total: 735,471 entries
66
+        '''
67
+        self.lt_UD_inner_x_centers_stage = LookupTable(self,
68
+                                                      'lookup-table-6x6x6-step10-UD-inner-x-centers-stage.txt',
69
+                                                      '666-UD-inner-X-centers-stage',
70
+                                                      '066000000000000000000660',
71
+                                                      True,  # state_hex
72
+                                                      modulo=735473)
73
+
74
+        '''
75
+        lookup-table-6x6x6-step21-UD-oblique-edge-pairing-left-only.txt
76
+        lookup-table-6x6x6-step22-UD-oblique-edge-pairing-right-only.txt
77
+        ================================================================
78
+        1 steps has 5 entries (0 percent, 0.00x previous step)
79
+        2 steps has 82 entries (0 percent, 16.40x previous step)
80
+        3 steps has 1,198 entries (0 percent, 14.61x previous step)
81
+        4 steps has 13,818 entries (1 percent, 11.53x previous step)
82
+        5 steps has 115,638 entries (15 percent, 8.37x previous step)
83
+        6 steps has 399,478 entries (54 percent, 3.45x previous step)
84
+        7 steps has 204,612 entries (27 percent, 0.51x previous step)
85
+        8 steps has 640 entries (0 percent, 0.00x previous step)
86
+
87
+        Total: 735,471 entries
88
+        '''
89
+        self.lt_UD_oblique_edge_pairing_left_only = LookupTable(self,
90
+                                                                'lookup-table-6x6x6-step21-UD-oblique-edge-pairing-left-only.txt',
91
+                                                                '666-UD-oblique-edge-pairing-left-only',
92
+                                                                '990000000099',
93
+                                                                True, # state_hex
94
+                                                                modulo=735473)
95
+
96
+        self.lt_UD_oblique_edge_pairing_right_only = LookupTable(self,
97
+                                                                'lookup-table-6x6x6-step22-UD-oblique-edge-pairing-right-only.txt',
98
+                                                                '666-UD-oblique-edge-pairing-right-only',
99
+                                                                '660000000066',
100
+                                                                True, # state_hex
101
+                                                                modulo=735473)
102
+
103
+        '''
104
+        Now pair the UD oblique edges so that we can reduce the 6x6x6 centers to a 5x5x5
105
+        (24!/(8!*16!))^2 is 540,917,591,841 so this is too large for us to build so use
106
+        IDA and build it 8 steps deep.
107
+
108
+        Our prune tables will be to solve on the left or right oblique edges. Each of these
109
+        tables are 24!/(8!*16!) or 735,471
110
+        735471/540917591841 is 0.0000013596729171
111
+
112
+        lookup-table-6x6x6-step20-UD-oblique-edge-pairing.txt
113
+        =====================================================
114
+        1 steps has 5 entries (0 percent, 0.00x previous step)
115
+        2 steps has 82 entries (0 percent, 16.40x previous step)
116
+        3 steps has 1,434 entries (0 percent, 17.49x previous step)
117
+        4 steps has 24,198 entries (0 percent, 16.87x previous step)
118
+        5 steps has 405,916 entries (0 percent, 16.77x previous step)
119
+        6 steps has 6,839,392 entries (5 percent, 16.85x previous step)
120
+
121
+        Total: 7,271,027 entries
122
+        '''
123
+        self.lt_UD_oblique_edge_pairing = LookupTableIDA(self,
124
+                                                         'lookup-table-6x6x6-step20-UD-oblique-edge-pairing.txt',
125
+                                                         '666-UD-oblique-edge-pairing',
126
+                                                         'ff00000000ff',
127
+                                                         True, # state_hex
128
+                                                         moves_6x6x6,
129
+
130
+                                                         # These would break up the staged UD inner x-centers
131
+                                                         ("3Rw", "3Rw'", "3Lw", "3Lw'", "3Fw", "3Fw'", "3Bw", "3Bw'"),
132
+
133
+                                                         # prune tables
134
+                                                         (self.lt_UD_oblique_edge_pairing_left_only,
135
+                                                          self.lt_UD_oblique_edge_pairing_right_only),
136
+                                                         modulo=7271027)
137
+        '''
138
+        16!/(8!*8!) is 12,870
139
+
140
+        lookup-table-6x6x6-step30-LR-inner-x-centers-stage.txt
141
+        ======================================================
142
+        1 steps has 3 entries (0 percent)
143
+        2 steps has 29 entries (0 percent)
144
+        3 steps has 234 entries (1 percent)
145
+        4 steps has 1,246 entries (9 percent)
146
+        5 steps has 4,466 entries (34 percent)
147
+        6 steps has 6,236 entries (48 percent)
148
+        7 steps has 656 entries (5 percent)
149
+
150
+        Total: 12,870 entries
151
+        '''
152
+        self.lt_LR_inner_x_centers_stage = LookupTable(self,
153
+                                                      'lookup-table-6x6x6-step30-LR-inner-x-centers-stage.txt',
154
+                                                      '666-LR-inner-X-centers-stage',
155
+                                                      '000006600000066000000000',
156
+                                                      True, # state_hex
157
+                                                      modulo=12889)
158
+
159
+        '''
160
+        lookup-table-6x6x6-step41-LR-oblique-pairing-left-only.txt
161
+        lookup-table-6x6x6-step42-LR-oblique-pairing-right-only.txt
162
+        ==========================================================
163
+        1 steps has 3 entries (0 percent, 0.00x previous step)
164
+        2 steps has 29 entries (0 percent, 9.67x previous step)
165
+        3 steps has 238 entries (1 percent, 8.21x previous step)
166
+        4 steps has 742 entries (5 percent, 3.12x previous step)
167
+        5 steps has 1836 entries (14 percent, 2.47x previous step)
168
+        6 steps has 4405 entries (34 percent, 2.40x previous step)
169
+        7 steps has 3774 entries (29 percent, 0.86x previous step)
170
+        8 steps has 1721 entries (13 percent, 0.46x previous step)
171
+        9 steps has 122 entries (0 percent, 0.07x previous step)
172
+
173
+        Total: 12870 entries
174
+        '''
175
+        self.lt_LR_oblique_edge_pairing_left_only = LookupTable(self,
176
+                                                                'lookup-table-6x6x6-step41-LR-oblique-pairing-left-only.txt',
177
+                                                                '666-LR-oblique-edge-pairing-left-only',
178
+                                                                '99009900',
179
+                                                                True, # state_hex
180
+                                                                modulo=12889)
181
+
182
+        self.lt_LR_oblique_edge_pairing_right_only = LookupTable(self,
183
+                                                                'lookup-table-6x6x6-step42-LR-oblique-pairing-right-only.txt',
184
+                                                                '666-LR-oblique-edge-pairing-right-only',
185
+                                                                '66006600',
186
+                                                                True, # state_hex
187
+                                                                modulo=12889)
188
+        '''
189
+        (16!/(8!*8!))^2 is 165,636,900
190
+        I only built this 9 deep to keep it small thus the IDA
191
+
192
+        lookup-table-6x6x6-step40-LR-oblique-pairing.txt
193
+        ================================================
194
+        1 steps has 3 entries (0 percent)
195
+        2 steps has 29 entries (0 percent)
196
+        3 steps has 286 entries (0 percent)
197
+        4 steps has 2,052 entries (0 percent)
198
+        5 steps has 16,348 entries (0 percent)
199
+        6 steps has 127,859 entries (0 percent)
200
+        7 steps has 844,248 entries (0 percent)
201
+        8 steps has 4,623,585 entries (2 percent)
202
+        9 steps has 19,019,322 entries (11 percent)
203
+        10 steps has 47,544,426 entries (28 percent)
204
+        11 steps has 61,805,656 entries (37 percent)
205
+        12 steps has 28,890,234 entries (17 percent)
206
+        13 steps has 2,722,462 entries (1 percent)
207
+        14 steps has 40,242 entries (0 percent)
208
+        15 steps has 148 entries (0 percent)
209
+
210
+        Total: 165,636,900 entries
211
+        '''
212
+        self.lt_LR_oblique_edge_pairing = LookupTableIDA(self,
213
+                                                         'lookup-table-6x6x6-step40-LR-oblique-pairing.txt',
214
+                                                         '666-LR-oblique-edge-pairing',
215
+                                                         'ff00ff00',
216
+                                                         True, # state_hex
217
+                                                         moves_6x6x6,
218
+
219
+                                                         # These would break up the staged UD inner x-centers
220
+                                                         ("3Rw", "3Rw'", "3Lw", "3Lw'", "3Fw", "3Fw'", "3Bw", "3Bw'", # do not mess up UD x-centers
221
+                                                          "Rw", "Rw'", "Lw", "Lw'", "Fw", "Fw'", "Bw", "Bw'",         # do not mess up UD oblique pairs
222
+                                                          "3Uw", "3Uw'", "3Dw", "3Dw'"),                              # do not mess up LR x-centers
223
+
224
+                                                         # prune tables
225
+                                                         (self.lt_LR_oblique_edge_pairing_left_only,
226
+                                                          self.lt_LR_oblique_edge_pairing_right_only),
227
+                                                         modulo=24633733)
228
+
229
+        '''
230
+        lookup-table-6x6x6-step50-UD-solve-inner-x-center-and-oblique-edges.txt
231
+        ========================================================================
232
+        1 steps has 9 entries (0 percent, 0.00x previous step)
233
+        2 steps has 47 entries (0 percent, 5.22x previous step)
234
+        3 steps has 232 entries (0 percent, 4.94x previous step)
235
+        4 steps has 1,001 entries (0 percent, 4.31x previous step)
236
+        5 steps has 4,266 entries (1 percent, 4.26x previous step)
237
+        6 steps has 16,697 entries (4 percent, 3.91x previous step)
238
+        7 steps has 52,894 entries (15 percent, 3.17x previous step)
239
+        8 steps has 114,134 entries (33 percent, 2.16x previous step)
240
+        9 steps has 113,888 entries (33 percent, 1.00x previous step)
241
+        10 steps has 37,136 entries (10 percent, 0.33x previous step)
242
+        11 steps has 2,696 entries (0 percent, 0.07x previous step)
243
+
244
+        Total: 343,000 entries
245
+        '''
246
+        self.lt_UD_solve_inner_x_centers_and_oblique_edges = LookupTable(self,
247
+                                                                         'lookup-table-6x6x6-step50-UD-solve-inner-x-center-and-oblique-edges.txt',
248
+                                                                         '666-UD-centers-oblique-edges-solve',
249
+                                                                         'xUUxUUUUUUUUxUUxxDDxDDDDDDDDxDDx',
250
+                                                                         False, # state_hex
251
+                                                                         modulo=343019)
252
+
253
+        '''
254
+        lookup-table-6x6x6-step61-LR-solve-inner-x-center-and-oblique-edges.txt
255
+        ========================================================================
256
+        1 steps has 5 entries (0 percent, 0.00x previous step)
257
+        2 steps has 26 entries (0 percent, 5.20x previous step)
258
+        3 steps has 86 entries (0 percent, 3.31x previous step)
259
+        4 steps has 356 entries (0 percent, 4.14x previous step)
260
+        5 steps has 1,186 entries (0 percent, 3.33x previous step)
261
+        6 steps has 4,264 entries (1 percent, 3.60x previous step)
262
+        7 steps has 12,946 entries (3 percent, 3.04x previous step)
263
+        8 steps has 35,741 entries (10 percent, 2.76x previous step)
264
+        9 steps has 77,322 entries (22 percent, 2.16x previous step)
265
+        10 steps has 111,116 entries (32 percent, 1.44x previous step)
266
+        11 steps has 80,988 entries (23 percent, 0.73x previous step)
267
+        12 steps has 18,436 entries (5 percent, 0.23x previous step)
268
+        13 steps has 528 entries (0 percent, 0.03x previous step)
269
+
270
+        Total: 343,000 entries
271
+        '''
272
+        self.lt_LR_solve_inner_x_centers_and_oblique_edges = LookupTable(self,
273
+                                                                         'lookup-table-6x6x6-step61-LR-solve-inner-x-center-and-oblique-edges.txt',
274
+                                                                         '666-LR-centers-oblique-edges-solve',
275
+                                                                         'xLLxLLLLLLLLxLLxxRRxRRRRRRRRxRRx',
276
+                                                                         False, # state_hex
277
+                                                                         modulo=343019)
278
+
279
+        '''
280
+        lookup-table-6x6x6-step62-FB-solve-inner-x-center-and-oblique-edges.txt
281
+        ========================================================================
282
+        1 steps has 5 entries (0 percent, 0.00x previous step)
283
+        2 steps has 26 entries (0 percent, 5.20x previous step)
284
+        3 steps has 86 entries (0 percent, 3.31x previous step)
285
+        4 steps has 356 entries (0 percent, 4.14x previous step)
286
+        5 steps has 1,186 entries (0 percent, 3.33x previous step)
287
+        6 steps has 4,264 entries (1 percent, 3.60x previous step)
288
+        7 steps has 12,946 entries (3 percent, 3.04x previous step)
289
+        8 steps has 35,741 entries (10 percent, 2.76x previous step)
290
+        9 steps has 77,322 entries (22 percent, 2.16x previous step)
291
+        10 steps has 111,116 entries (32 percent, 1.44x previous step)
292
+        11 steps has 80,988 entries (23 percent, 0.73x previous step)
293
+        12 steps has 18,436 entries (5 percent, 0.23x previous step)
294
+        13 steps has 528 entries (0 percent, 0.03x previous step)
295
+
296
+        Total: 343,000 entries
297
+        '''
298
+        self.lt_FB_solve_inner_x_centers_and_oblique_edges = LookupTable(self,
299
+                                                                         'lookup-table-6x6x6-step62-FB-solve-inner-x-center-and-oblique-edges.txt',
300
+                                                                         '666-FB-centers-oblique-edges-solve',
301
+                                                                         'xFFxFFFFFFFFxFFxxBBxBBBBBBBBxBBx',
302
+                                                                         False, # state_hex
303
+                                                                         modulo=343019)
304
+
305
+        '''
306
+        lookup-table-6x6x6-step60-LFRB-solve-inner-x-center-and-oblique-edges.txt
307
+        ==========================================================================
308
+        1 steps has 5 entries (0 percent, 0.00x previous step)
309
+        2 steps has 54 entries (0 percent, 10.80x previous step)
310
+        3 steps has 420 entries (0 percent, 7.78x previous step)
311
+        4 steps has 2,703 entries (0 percent, 6.44x previous step)
312
+        5 steps has 18,740 entries (0 percent, 6.93x previous step)
313
+        6 steps has 118,707 entries (0 percent, 6.33x previous step)
314
+        7 steps has 707,156 entries (2 percent, 5.96x previous step)
315
+        8 steps has 3,945,650 entries (15 percent, 5.58x previous step)
316
+        9 steps has 20,886,476 entries (81 percent, 5.29x previous step)
317
+
318
+        Total: 25,679,911 entries
319
+        '''
320
+        self.lt_LFRB_solve_inner_x_centers_and_oblique_edges = LookupTableIDA(self,
321
+                                                         'lookup-table-6x6x6-step60-LFRB-solve-inner-x-center-and-oblique-edges.txt',
322
+                                                         '666-LFRB-centers-oblique-edges-solve',
323
+                                                         'xLLxLLLLLLLLxLLxxFFxFFFFFFFFxFFxxRRxRRRRRRRRxRRxxBBxBBBBBBBBxBBx',
324
+                                                         False, # state_hex
325
+                                                         moves_6x6x6,
326
+
327
+                                                         ("3Rw", "3Rw'", "3Lw", "3Lw'", "3Fw", "3Fw'", "3Bw", "3Bw'", "3Uw", "3Uw'", "3Dw", "3Dw'", # do not mess up staged centers
328
+                                                          "Rw", "Rw'", "Lw", "Lw'", "Fw", "Fw'", "Bw", "Bw'", "Uw", "Uw'", "Dw", "Dw'",             # do not mess up staged centers
329
+                                                          "3Rw2", "3Lw2", "3Fw2", "3Bw2", "Rw2", "Lw2", "Fw2", "Bw2",                               # do not mess up solved UD
330
+                                                          "L", "L'", "L2", "R", "R'", "R2"), # do not mess up LR sides that we staged via self.lt_LR_solve_inner_x_centers_and_oblique_edges.solve()
331
+
332
+                                                         # prune tables
333
+                                                         (self.lt_LR_solve_inner_x_centers_and_oblique_edges,
334
+                                                          self.lt_FB_solve_inner_x_centers_and_oblique_edges),
335
+                                                         modulo=4793471)
336
+
337
+    def populate_fake_555_for_ULFRBD(self, fake_555):
338
+
339
+        for x in xrange(1, 151):
340
+            fake_555.state[x] = 'x'
341
+
342
+        # Upper
343
+        fake_555.state[7] = self.state[8]
344
+        fake_555.state[8] = self.state[9]
345
+        fake_555.state[9] = self.state[11]
346
+        fake_555.state[12] = self.state[14]
347
+        fake_555.state[13] = self.state[15]
348
+        fake_555.state[14] = self.state[17]
349
+        fake_555.state[17] = self.state[26]
350
+        fake_555.state[18] = self.state[27]
351
+        fake_555.state[19] = self.state[29]
352
+
353
+        # Left
354
+        fake_555.state[32] = self.state[44]
355
+        fake_555.state[33] = self.state[45]
356
+        fake_555.state[34] = self.state[47]
357
+        fake_555.state[37] = self.state[50]
358
+        fake_555.state[38] = self.state[51]
359
+        fake_555.state[39] = self.state[53]
360
+        fake_555.state[42] = self.state[62]
361
+        fake_555.state[43] = self.state[63]
362
+        fake_555.state[44] = self.state[65]
363
+
364
+        # Front
365
+        fake_555.state[57] = self.state[80]
366
+        fake_555.state[58] = self.state[81]
367
+        fake_555.state[59] = self.state[83]
368
+        fake_555.state[62] = self.state[86]
369
+        fake_555.state[63] = self.state[87]
370
+        fake_555.state[64] = self.state[89]
371
+        fake_555.state[67] = self.state[98]
372
+        fake_555.state[68] = self.state[99]
373
+        fake_555.state[69] = self.state[101]
374
+
375
+        # Right
376
+        fake_555.state[82] = self.state[116]
377
+        fake_555.state[83] = self.state[117]
378
+        fake_555.state[84] = self.state[119]
379
+        fake_555.state[87] = self.state[122]
380
+        fake_555.state[88] = self.state[123]
381
+        fake_555.state[89] = self.state[125]
382
+        fake_555.state[92] = self.state[134]
383
+        fake_555.state[93] = self.state[135]
384
+        fake_555.state[94] = self.state[137]
385
+
386
+        # Back
387
+        fake_555.state[107] = self.state[152]
388
+        fake_555.state[108] = self.state[153]
389
+        fake_555.state[109] = self.state[155]
390
+        fake_555.state[112] = self.state[158]
391
+        fake_555.state[113] = self.state[159]
392
+        fake_555.state[114] = self.state[161]
393
+        fake_555.state[117] = self.state[170]
394
+        fake_555.state[118] = self.state[171]
395
+        fake_555.state[119] = self.state[173]
396
+
397
+        # Down
398
+        fake_555.state[132] = self.state[188]
399
+        fake_555.state[133] = self.state[189]
400
+        fake_555.state[134] = self.state[191]
401
+        fake_555.state[137] = self.state[194]
402
+        fake_555.state[138] = self.state[195]
403
+        fake_555.state[139] = self.state[197]
404
+        fake_555.state[142] = self.state[206]
405
+        fake_555.state[143] = self.state[207]
406
+        fake_555.state[144] = self.state[209]
407
+
408
+    def group_centers_stage_UD(self):
409
+        self.lt_UD_inner_x_centers_stage.solve()
410
+
411
+        # This one can take a while so we do some tricks to speed it up
412
+        # - first try to find a solution without solving either prune table beforehand
413
+        # - if that doesn't work, solve the left_only prune table and try again
414
+        # - if that doesn't work, solve the right_only prune table and let it run
415
+        #
416
+        # We do this because for some cubes if you solve the left_only prune table it
417
+        # really speeds up IDA while for other cubes it doesn't but solving the right_only
418
+        # prune table will speed things up.
419
+        try:
420
+            self.lt_UD_oblique_edge_pairing.solve(8)
421
+        except NoIDASolution:
422
+            original_state = self.state[:]
423
+            original_solution = self.solution[:]
424
+            self.lt_UD_oblique_edge_pairing_left_only.solve() # speed up IDA
425
+
426
+            try:
427
+                self.lt_UD_oblique_edge_pairing.solve(9)
428
+            except NoIDASolution:
429
+                self.state = original_state
430
+                self.solution = original_solution
431
+                self.lt_UD_oblique_edge_pairing_right_only.solve() # speed up IDA
432
+                self.lt_UD_oblique_edge_pairing.solve(99)
433
+
434
+        #self.print_cube()
435
+        #sys.exit(0)
436
+
437
+    def group_centers_guts(self):
438
+        self.lt_init()
439
+        self.group_centers_stage_UD()
440
+
441
+        self.lt_LR_inner_x_centers_stage.solve()
442
+        self.lt_LR_oblique_edge_pairing.solve(99)
443
+        log.info("inner x-center and oblique edges staged, %d steps in" % self.get_solution_len_minus_rotates(self.solution))
444
+        self.print_cube()
445
+        log.info("")
446
+        log.info("")
447
+        log.info("")
448
+        log.info("")
449
+        log.info("")
450
+
451
+        # Reduce the centers to 5x5x5 centers
452
+        # - solve the UD inner x-centers and pair the UD oblique edges
453
+        # - solve the LR inner x-centers and pair the LR oblique edges
454
+        # - solve the FB inner x-centers and pair the FB oblique edges
455
+        self.lt_UD_solve_inner_x_centers_and_oblique_edges.solve()
456
+
457
+        self.lt_LR_solve_inner_x_centers_and_oblique_edges.solve() # speed up IDA
458
+        self.lt_LFRB_solve_inner_x_centers_and_oblique_edges.solve(99)
459
+        log.info("inner x-center and oblique edges paired, %d steps in" % self.get_solution_len_minus_rotates(self.solution))
460
+        self.print_cube()
461
+        log.info("")
462
+        log.info("")
463
+        log.info("")
464
+        log.info("")
465
+        log.info("")
466
+
467
+        # At this point the 6x6x6 centers have been reduced to 5x5x5 centers
468
+        fake_555 = RubiksCube555(solved_5x5x5)
469
+        fake_555.lt_init()
470
+        self.populate_fake_555_for_ULFRBD(fake_555)
471
+        fake_555.group_centers_guts()
472
+
473
+        for step in fake_555.solution:
474
+            self.rotate(step)
475
+
476
+        log.info("Took %d steps to solve centers" % self.get_solution_len_minus_rotates(self.solution))
477
+
478
+    def pair_inside_edges_via_444(self):
479
+        fake_444 = RubiksCube444(solved_4x4x4)
480
+        fake_444.lt_init()
481
+
482
+        # The corners don't matter but it does make troubleshooting easier if they match
483
+        fake_444.state[1] = self.state[1]
484
+        fake_444.state[4] = self.state[6]
485
+        fake_444.state[13] = self.state[31]
486
+        fake_444.state[16] = self.state[36]
487
+        fake_444.state[17] = self.state[37]
488
+        fake_444.state[20] = self.state[42]
489
+        fake_444.state[29] = self.state[67]
490
+        fake_444.state[32] = self.state[72]
491
+        fake_444.state[33] = self.state[73]
492
+        fake_444.state[36] = self.state[78]
493
+        fake_444.state[45] = self.state[103]
494
+        fake_444.state[48] = self.state[108]
495
+        fake_444.state[49] = self.state[109]
496
+        fake_444.state[52] = self.state[114]
497
+        fake_444.state[61] = self.state[139]
498
+        fake_444.state[64] = self.state[144]
499
+        fake_444.state[65] = self.state[145]
500
+        fake_444.state[68] = self.state[150]
501
+        fake_444.state[77] = self.state[175]
502
+        fake_444.state[80] = self.state[180]
503
+        fake_444.state[81] = self.state[181]
504
+        fake_444.state[84] = self.state[186]
505
+        fake_444.state[93] = self.state[211]
506
+        fake_444.state[96] = self.state[216]
507
+
508
+        # Upper
509
+        fake_444.state[2] = self.state[3]
510
+        fake_444.state[3] = self.state[4]
511
+        fake_444.state[5] = self.state[13]
512
+        fake_444.state[8] = self.state[18]
513
+        fake_444.state[9] = self.state[19]
514
+        fake_444.state[12] = self.state[24]
515
+        fake_444.state[14] = self.state[33]
516
+        fake_444.state[15] = self.state[34]
517
+
518
+        # Left
519
+        fake_444.state[18] = self.state[39]
520
+        fake_444.state[19] = self.state[40]
521
+        fake_444.state[21] = self.state[49]
522
+        fake_444.state[24] = self.state[54]
523
+        fake_444.state[25] = self.state[55]
524
+        fake_444.state[28] = self.state[60]
525
+        fake_444.state[30] = self.state[69]
526
+        fake_444.state[31] = self.state[70]
527
+
528
+        # Front
529
+        fake_444.state[34] = self.state[75]
530
+        fake_444.state[35] = self.state[76]
531
+        fake_444.state[37] = self.state[85]
532
+        fake_444.state[40] = self.state[90]
533
+        fake_444.state[41] = self.state[91]
534
+        fake_444.state[44] = self.state[96]
535
+        fake_444.state[46] = self.state[105]
536
+        fake_444.state[47] = self.state[106]
537
+
538
+        # Right
539
+        fake_444.state[50] = self.state[111]
540
+        fake_444.state[51] = self.state[112]
541
+        fake_444.state[53] = self.state[121]
542
+        fake_444.state[56] = self.state[126]
543
+        fake_444.state[57] = self.state[127]
544
+        fake_444.state[60] = self.state[132]
545
+        fake_444.state[62] = self.state[141]
546
+        fake_444.state[63] = self.state[142]
547
+
548
+        # Back
549
+        fake_444.state[66] = self.state[147]
550
+        fake_444.state[67] = self.state[148]
551
+        fake_444.state[69] = self.state[157]
552
+        fake_444.state[72] = self.state[162]
553
+        fake_444.state[73] = self.state[163]
554
+        fake_444.state[76] = self.state[168]
555
+        fake_444.state[78] = self.state[177]
556
+        fake_444.state[79] = self.state[178]
557
+
558
+        # Down
559
+        fake_444.state[82] = self.state[183]
560
+        fake_444.state[83] = self.state[184]
561
+        fake_444.state[85] = self.state[193]
562
+        fake_444.state[88] = self.state[198]
563
+        fake_444.state[89] = self.state[199]
564
+        fake_444.state[92] = self.state[204]
565
+        fake_444.state[94] = self.state[213]
566
+        fake_444.state[95] = self.state[214]
567
+
568
+        fake_444.group_edges()
569
+
570
+        for step in fake_444.solution:
571
+            if step == 'EDGES_GROUPED':
572
+                continue
573
+
574
+            if step.startswith('4'):
575
+                step = '6' + step[1:]
576
+            elif step.startswith('3'):
577
+                raise ImplementThis('4x4x4 steps starts with 3')
578
+            elif step in ("Uw", "Uw'", "Uw2",
579
+                          "Lw", "Lw'", "Lw2",
580
+                          "Fw", "Fw'", "Fw2",
581
+                          "Rw", "Rw'", "Rw2",
582
+                          "Bw", "Bw'", "Bw2",
583
+                          "Dw", "Dw'", "Dw2"):
584
+                step = '3' + step
585
+
586
+            # log.warning("fake_444 step %s" % step)
587
+            self.rotate(step)
588
+
589
+        log.info("Inside edges are paired, %d steps in" % self.get_solution_len_minus_rotates(self.solution))
590
+
591
+    def pair_outside_edges_via_555(self):
592
+        fake_555 = RubiksCube555(solved_5x5x5)
593
+        fake_555.lt_init()
594
+
595
+        # The corners matter for avoiding PLL
596
+        fake_555.state[1] = self.state[1]
597
+        fake_555.state[5] = self.state[6]
598
+        fake_555.state[21] = self.state[31]
599
+        fake_555.state[25] = self.state[36]
600
+        fake_555.state[26] = self.state[37]
601
+        fake_555.state[30] = self.state[42]
602
+        fake_555.state[46] = self.state[67]
603
+        fake_555.state[50] = self.state[72]
604
+        fake_555.state[51] = self.state[73]
605
+        fake_555.state[55] = self.state[78]
606
+        fake_555.state[71] = self.state[103]
607
+        fake_555.state[75] = self.state[108]
608
+        fake_555.state[76] = self.state[109]
609
+        fake_555.state[80] = self.state[114]
610
+        fake_555.state[96] = self.state[139]
611
+        fake_555.state[100] = self.state[144]
612
+        fake_555.state[101] = self.state[145]
613
+        fake_555.state[105] = self.state[150]
614
+        fake_555.state[121] = self.state[175]
615
+        fake_555.state[125] = self.state[180]
616
+        fake_555.state[126] = self.state[181]
617
+        fake_555.state[130] = self.state[186]
618
+        fake_555.state[146] = self.state[211]
619
+        fake_555.state[150] = self.state[216]
620
+
621
+        # Upper
622
+        fake_555.state[2] = self.state[2]
623
+        fake_555.state[3] = self.state[3]
624
+        fake_555.state[4] = self.state[5]
625
+        fake_555.state[6] = self.state[7]
626
+        fake_555.state[10] = self.state[12]
627
+        fake_555.state[11] = self.state[13]
628
+        fake_555.state[15] = self.state[18]
629
+        fake_555.state[16] = self.state[25]
630
+        fake_555.state[20] = self.state[30]
631
+        fake_555.state[22] = self.state[32]
632
+        fake_555.state[23] = self.state[33]
633
+        fake_555.state[24] = self.state[35]
634
+
635
+        # Left
636
+        fake_555.state[27] = self.state[38]
637
+        fake_555.state[28] = self.state[39]
638
+        fake_555.state[29] = self.state[41]
639
+        fake_555.state[31] = self.state[43]
640
+        fake_555.state[35] = self.state[48]
641
+        fake_555.state[36] = self.state[49]
642
+        fake_555.state[40] = self.state[54]
643
+        fake_555.state[41] = self.state[61]
644
+        fake_555.state[45] = self.state[66]
645
+        fake_555.state[47] = self.state[68]
646
+        fake_555.state[48] = self.state[69]
647
+        fake_555.state[49] = self.state[71]
648
+
649
+        # Front
650
+        fake_555.state[52] = self.state[74]
651
+        fake_555.state[53] = self.state[75]
652
+        fake_555.state[54] = self.state[77]
653
+        fake_555.state[56] = self.state[79]
654
+        fake_555.state[60] = self.state[84]
655
+        fake_555.state[61] = self.state[85]
656
+        fake_555.state[65] = self.state[90]
657
+        fake_555.state[66] = self.state[97]
658
+        fake_555.state[70] = self.state[102]
659
+        fake_555.state[72] = self.state[104]
660
+        fake_555.state[73] = self.state[105]
661
+        fake_555.state[74] = self.state[107]
662
+
663
+        # Right
664
+        fake_555.state[77] = self.state[110]
665
+        fake_555.state[78] = self.state[111]
666
+        fake_555.state[79] = self.state[113]
667
+        fake_555.state[81] = self.state[115]
668
+        fake_555.state[85] = self.state[120]
669
+        fake_555.state[86] = self.state[121]
670
+        fake_555.state[90] = self.state[126]
671
+        fake_555.state[91] = self.state[133]
672
+        fake_555.state[95] = self.state[138]
673
+        fake_555.state[97] = self.state[140]
674
+        fake_555.state[98] = self.state[141]
675
+        fake_555.state[99] = self.state[143]
676
+
677
+        # Back
678
+        fake_555.state[102] = self.state[146]
679
+        fake_555.state[103] = self.state[147]
680
+        fake_555.state[104] = self.state[149]
681
+        fake_555.state[106] = self.state[151]
682
+        fake_555.state[110] = self.state[156]
683
+        fake_555.state[111] = self.state[157]
684
+        fake_555.state[115] = self.state[162]
685
+        fake_555.state[116] = self.state[169]
686
+        fake_555.state[120] = self.state[174]
687
+        fake_555.state[122] = self.state[176]
688
+        fake_555.state[123] = self.state[177]
689
+        fake_555.state[124] = self.state[179]
690
+
691
+        # Down
692
+        fake_555.state[127] = self.state[182]
693
+        fake_555.state[128] = self.state[183]
694
+        fake_555.state[129] = self.state[185]
695
+        fake_555.state[131] = self.state[187]
696
+        fake_555.state[135] = self.state[192]
697
+        fake_555.state[136] = self.state[193]
698
+        fake_555.state[140] = self.state[198]
699
+        fake_555.state[141] = self.state[205]
700
+        fake_555.state[145] = self.state[210]
701
+        fake_555.state[147] = self.state[212]
702
+        fake_555.state[148] = self.state[213]
703
+        fake_555.state[149] = self.state[215]
704
+
705
+        #self.print_cube()
706
+        #fake_555.print_cube()
707
+        fake_555.avoid_pll = True
708
+        fake_555.group_edges()
709
+
710
+        for step in fake_555.solution:
711
+            if step == 'EDGES_GROUPED':
712
+                continue
713
+
714
+            if step.startswith('5'):
715
+                step = '6' + step[1:]
716
+            elif step.startswith('3'):
717
+                step = '4' + step[1:]
718
+
719
+            self.rotate(step)
720
+
721
+        log.info("Outside edges are paired, %d steps in" % self.get_solution_len_minus_rotates(self.solution))
722
+
723
+    def group_edges(self):
724
+        """
725
+        Create a fake 444 to pair the inside edges
726
+        Create a fake 555 to pair the outside edges
727
+        """
728
+
729
+        if not self.get_non_paired_edges():
730
+            self.solution.append('EDGES_GROUPED')
731
+            return
732
+
733
+        self.lt_init()
734
+        self.pair_inside_edges_via_444()
735
+        self.pair_outside_edges_via_555()
736
+        self.solution.append('EDGES_GROUPED')
737
+
738
+    def phase(self):
739
+        if self._phase is None:
740
+            self._phase = 'Stage UD centers'
741
+            return self._phase
742
+
743
+        if self._phase == 'Stage UD centers':
744
+            if self.UD_centers_staged():
745
+                self._phase = 'Stage LR centers'
746
+            return self._phase
747
+
748
+        if self._phase == 'Stage LR centers':
749
+            if self.LR_centers_staged():
750
+                self._phase = 'Solve Centers'
751
+
752
+        if self._phase == 'Solve Centers':
753
+            if self.centers_solved():
754
+                self._phase = 'Pair Edges'
755
+
756
+        if self._phase == 'Pair Edges':
757
+            if not self.get_non_paired_edges():
758
+                self._phase = 'Solve 3x3x3'
759
+
760
+        return self._phase

+ 1291
- 0
rubikscubennnsolver/RubiksCube777.py
File diff suppressed because it is too large
View File


+ 450
- 0
rubikscubennnsolver/RubiksCubeNNN.py View File

@@ -0,0 +1,450 @@
1
+
2
+from pprint import pformat
3
+from rubikscubennnsolver import RubiksCube, ImplementThis
4
+from rubikscubennnsolver.RubiksCube444 import RubiksCube444, solved_4x4x4
5
+from rubikscubennnsolver.RubiksCube555 import RubiksCube555, solved_5x5x5
6
+from rubikscubennnsolver.RubiksCube666 import RubiksCube666, solved_6x6x6
7
+import logging
8
+import sys
9
+
10
+log = logging.getLogger(__name__)
11
+
12
+
13
+class RubiksCubeNNNOdd(RubiksCube):
14
+
15
+    def __init__(self, state, order,  colormap, debug=False):
16
+        RubiksCube.__init__(self, state, order, colormap)
17
+
18
+        if debug:
19
+            log.setLevel(logging.DEBUG)
20
+
21
+
22
+class RubiksCubeNNNEven(RubiksCube):
23
+
24
+    def __init__(self, state, order, colormap, debug=False):
25
+        RubiksCube.__init__(self, state, order, colormap)
26
+
27
+        if debug:
28
+            log.setLevel(logging.DEBUG)
29
+
30
+    def group_centers_guts(self):
31
+
32
+        # Group UD centers
33
+        # - create a fake 6x6x6 to solve the inside 4x4 block
34
+        fake_666 = RubiksCube666(solved_6x6x6)
35
+        start_666 = 0
36
+        start_NNN = 0
37
+
38
+        for x in range(6):
39
+            start_NNN_row1 = start_NNN + (((self.size/2) - 2) * self.size) + ((self.size/2) - 1)
40
+            start_NNN_row2 = start_NNN_row1 + self.size
41
+            start_NNN_row3 = start_NNN_row2 + self.size
42
+            start_NNN_row4 = start_NNN_row3 + self.size
43
+
44
+            log.info("%d: start_NNN_row1 %d, row2 %d, row3 %d row4 %d" %
45
+                (x, start_NNN_row1, start_NNN_row2, start_NNN_row3, start_NNN_row4))
46
+
47
+            fake_666.state[start_666+8] = self.state[start_NNN_row1]
48
+            fake_666.state[start_666+9] = self.state[start_NNN_row1+1]
49
+            fake_666.state[start_666+10] = self.state[start_NNN_row1+2]
50
+            fake_666.state[start_666+11] = self.state[start_NNN_row1+3]
51
+
52
+            fake_666.state[start_666+14] = self.state[start_NNN_row2]
53
+            fake_666.state[start_666+15] = self.state[start_NNN_row2+1]
54
+            fake_666.state[start_666+16] = self.state[start_NNN_row2+2]
55
+            fake_666.state[start_666+17] = self.state[start_NNN_row2+3]
56
+
57
+            fake_666.state[start_666+20] = self.state[start_NNN_row3]
58
+            fake_666.state[start_666+21] = self.state[start_NNN_row3+1]
59
+            fake_666.state[start_666+22] = self.state[start_NNN_row3+2]
60
+            fake_666.state[start_666+23] = self.state[start_NNN_row3+3]
61
+
62
+            fake_666.state[start_666+26] = self.state[start_NNN_row4]
63
+            fake_666.state[start_666+27] = self.state[start_NNN_row4+1]
64
+            fake_666.state[start_666+28] = self.state[start_NNN_row4+2]
65
+            fake_666.state[start_666+29] = self.state[start_NNN_row4+3]
66
+            start_666 += 36
67
+            start_NNN += (self.size * self.size)
68
+
69
+        # Group LR centers (in turn groups FB)
70
+        fake_666.print_cube()
71
+        fake_666.lt_init()
72
+        #fake_666.group_centers_stage_UD()
73
+        fake_666.group_centers_guts()
74
+        fake_666.print_solution()
75
+        fake_666.print_cube()
76
+
77
+        half_size = int(self.size/2)
78
+
79
+        for step in fake_666.solution:
80
+            if step.startswith("3"):
81
+                self.rotate(str(half_size) + step[1:])
82
+            elif "w" in step:
83
+                self.rotate(str(half_size - 1) + step)
84
+            else:
85
+                self.rotate(step)
86
+
87
+        # Solve UD centers
88
+        # Solve LRFB centers
89
+        self.print_cube()
90
+
91
+        # still more work to do here so go ahead and exit
92
+        sys.exit(0)
93
+
94
+    def pair_inside_edges_via_444(self):
95
+        fake_444 = RubiksCube444(solved_4x4x4)
96
+        fake_444.lt_init()
97
+
98
+        # The corners don't matter but it does make troubleshooting easier if they match
99
+        start_index = 0
100
+        fake_444.state[1] = self.state[start_index+1]
101
+        fake_444.state[4] = self.state[start_index + self.size]
102
+        fake_444.state[13] = self.state[start_index + (self.size * self.size) - self.size + 1]
103
+        fake_444.state[16] = self.state[start_index + (self.size * self.size)]
104
+        start_index += self.size * self.size
105
+
106
+        fake_444.state[17] = self.state[start_index+1]
107
+        fake_444.state[20] = self.state[start_index + self.size]
108
+        fake_444.state[29] = self.state[start_index + (self.size * self.size) - self.size + 1]
109
+        fake_444.state[32] = self.state[start_index + (self.size * self.size)]
110
+        start_index += self.size * self.size
111
+
112
+        fake_444.state[33] = self.state[start_index+1]
113
+        fake_444.state[36] = self.state[start_index + self.size]
114
+        fake_444.state[45] = self.state[start_index + (self.size * self.size) - self.size + 1]
115
+        fake_444.state[48] = self.state[start_index + (self.size * self.size)]
116
+        start_index += self.size * self.size
117
+
118
+        fake_444.state[49] = self.state[start_index+1]
119
+        fake_444.state[52] = self.state[start_index + self.size]
120
+        fake_444.state[61] = self.state[start_index + (self.size * self.size) - self.size + 1]
121
+        fake_444.state[64] = self.state[start_index + (self.size * self.size)]
122
+        start_index += self.size * self.size
123
+
124
+        fake_444.state[65] = self.state[start_index+1]
125
+        fake_444.state[68] = self.state[start_index + self.size]
126
+        fake_444.state[77] = self.state[start_index + (self.size * self.size) - self.size + 1]
127
+        fake_444.state[80] = self.state[start_index + (self.size * self.size)]
128
+        start_index += self.size * self.size
129
+
130
+        fake_444.state[81] = self.state[start_index+1]
131
+        fake_444.state[84] = self.state[start_index + self.size]
132
+        fake_444.state[93] = self.state[start_index + (self.size * self.size) - self.size + 1]
133
+        fake_444.state[96] = self.state[start_index + (self.size * self.size)]
134
+        start_index += self.size * self.size
135
+
136
+        # Upper
137
+        half_size = int(self.size/2)
138
+        start_index = 0
139
+        fake_444.state[2] = self.state[start_index + half_size]
140
+        fake_444.state[3] = self.state[start_index + half_size + 1]
141
+        fake_444.state[5] = self.state[start_index + (self.size * (half_size-1)) + 1]
142
+        fake_444.state[8] = self.state[start_index + (self.size * half_size)]
143
+        fake_444.state[9] = self.state[start_index + (self.size * half_size) + 1]
144
+        fake_444.state[12] = self.state[start_index + (self.size * (half_size+1))]
145
+        fake_444.state[14] = self.state[start_index + (self.size * self.size) - half_size]
146
+        fake_444.state[15] = self.state[start_index + (self.size * self.size) - half_size + 1]
147
+        start_index += self.size * self.size
148
+
149
+        # Left
150
+        fake_444.state[18] = self.state[start_index + half_size]
151
+        fake_444.state[19] = self.state[start_index + half_size + 1]
152
+        fake_444.state[21] = self.state[start_index + (self.size * (half_size-1)) + 1]
153
+        fake_444.state[24] = self.state[start_index + (self.size * half_size)]
154
+        fake_444.state[25] = self.state[start_index + (self.size * half_size) + 1]
155
+        fake_444.state[28] = self.state[start_index + (self.size * (half_size+1))]
156
+        fake_444.state[30] = self.state[start_index + (self.size * self.size) - half_size]
157
+        fake_444.state[31] = self.state[start_index + (self.size * self.size) - half_size + 1]
158
+        start_index += self.size * self.size
159
+
160
+        # Front
161
+        fake_444.state[34] = self.state[start_index + half_size]
162
+        fake_444.state[35] = self.state[start_index + half_size + 1]
163
+        fake_444.state[37] = self.state[start_index + (self.size * (half_size-1)) + 1]
164
+        fake_444.state[40] = self.state[start_index + (self.size * half_size)]
165
+        fake_444.state[41] = self.state[start_index + (self.size * half_size) + 1]
166
+        fake_444.state[44] = self.state[start_index + (self.size * (half_size+1))]
167
+        fake_444.state[46] = self.state[start_index + (self.size * self.size) - half_size]
168
+        fake_444.state[47] = self.state[start_index + (self.size * self.size) - half_size + 1]
169
+        start_index += self.size * self.size
170
+
171
+        # Right
172
+        fake_444.state[50] = self.state[start_index + half_size]
173
+        fake_444.state[51] = self.state[start_index + half_size + 1]
174
+        fake_444.state[53] = self.state[start_index + (self.size * (half_size-1)) + 1]
175
+        fake_444.state[56] = self.state[start_index + (self.size * half_size)]
176
+        fake_444.state[57] = self.state[start_index + (self.size * half_size) + 1]
177
+        fake_444.state[60] = self.state[start_index + (self.size * (half_size+1))]
178
+        fake_444.state[62] = self.state[start_index + (self.size * self.size) - half_size]
179
+        fake_444.state[63] = self.state[start_index + (self.size * self.size) - half_size + 1]
180
+        start_index += self.size * self.size
181
+
182
+        # Back
183
+        fake_444.state[66] = self.state[start_index + half_size]
184
+        fake_444.state[67] = self.state[start_index + half_size + 1]
185
+        fake_444.state[69] = self.state[start_index + (self.size * (half_size-1)) + 1]
186
+        fake_444.state[72] = self.state[start_index + (self.size * half_size)]
187
+        fake_444.state[73] = self.state[start_index + (self.size * half_size) + 1]
188
+        fake_444.state[76] = self.state[start_index + (self.size * (half_size+1))]
189
+        fake_444.state[78] = self.state[start_index + (self.size * self.size) - half_size]
190
+        fake_444.state[79] = self.state[start_index + (self.size * self.size) - half_size + 1]
191
+        start_index += self.size * self.size
192
+
193
+        # Down
194
+        fake_444.state[82] = self.state[start_index + half_size]
195
+        fake_444.state[83] = self.state[start_index + half_size + 1]
196
+        fake_444.state[85] = self.state[start_index + (self.size * (half_size-1)) + 1]
197
+        fake_444.state[88] = self.state[start_index + (self.size * half_size)]
198
+        fake_444.state[89] = self.state[start_index + (self.size * half_size) + 1]
199
+        fake_444.state[92] = self.state[start_index + (self.size * (half_size+1))]
200
+        fake_444.state[94] = self.state[start_index + (self.size * self.size) - half_size]
201
+        fake_444.state[95] = self.state[start_index + (self.size * self.size) - half_size + 1]
202
+        start_index += self.size * self.size
203
+
204
+        fake_444.group_edges()
205
+        #fake_444.print_cube()
206
+        #fake_444.print_solution()
207
+        half_size_str = str(half_size)
208
+
209
+        for step in fake_444.solution:
210
+
211
+            if step == 'EDGES_GROUPED':
212
+                continue
213
+
214
+            # Rotate the entire cube
215
+            if step.startswith('4'):
216
+                step = str(self.size) + step[1:]
217
+
218
+            elif step in ("Uw", "Uw'", "Uw2",
219
+                          "Lw", "Lw'", "Lw2",
220
+                          "Fw", "Fw'", "Fw2",
221
+                          "Rw", "Rw'", "Rw2",
222
+                          "Bw", "Bw'", "Bw2",
223
+                          "Dw", "Dw'", "Dw2"):
224
+                step = half_size_str + step
225
+
226
+            #log.warning("fake_444 step %s" % step)
227
+            self.rotate(step)
228
+
229
+        #log.info("Inside edges are paired, %d steps in" % self.get_solution_len_minus_rotates(self.solution))
230
+
231
+    def pair_edge_orbit_via_555(self, orbit):
232
+        log.warning("pair_edge_orbit_via_555 for %d" % orbit)
233
+        fake_555 = RubiksCube555(solved_5x5x5)
234
+        fake_555.lt_init()
235
+
236
+        # The corners don't matter but it does make troubleshooting easier if they match
237
+        start_index = 0
238
+        fake_555.state[1] = self.state[start_index+1]
239
+        fake_555.state[5] = self.state[start_index + self.size]
240
+        fake_555.state[21] = self.state[start_index + (self.size * self.size) - self.size + 1]
241
+        fake_555.state[25] = self.state[start_index + (self.size * self.size)]
242
+        start_index += self.size * self.size
243
+
244
+        fake_555.state[26] = self.state[start_index+1]
245
+        fake_555.state[30] = self.state[start_index + self.size]
246
+        fake_555.state[46] = self.state[start_index + (self.size * self.size) - self.size + 1]
247
+        fake_555.state[50] = self.state[start_index + (self.size * self.size)]
248
+        start_index += self.size * self.size
249
+
250
+        fake_555.state[51] = self.state[start_index+1]
251
+        fake_555.state[55] = self.state[start_index + self.size]
252
+        fake_555.state[71] = self.state[start_index + (self.size * self.size) - self.size + 1]
253
+        fake_555.state[75] = self.state[start_index + (self.size * self.size)]
254
+        start_index += self.size * self.size
255
+
256
+        fake_555.state[76] = self.state[start_index+1]
257
+        fake_555.state[80] = self.state[start_index + self.size]
258
+        fake_555.state[96] = self.state[start_index + (self.size * self.size) - self.size + 1]
259
+        fake_555.state[100] = self.state[start_index + (self.size * self.size)]
260
+        start_index += self.size * self.size
261
+
262
+        fake_555.state[101] = self.state[start_index+1]
263
+        fake_555.state[105] = self.state[start_index + self.size]
264
+        fake_555.state[121] = self.state[start_index + (self.size * self.size) - self.size + 1]
265
+        fake_555.state[125] = self.state[start_index + (self.size * self.size)]
266
+        start_index += self.size * self.size
267
+
268
+        fake_555.state[126] = self.state[start_index+1]
269
+        fake_555.state[130] = self.state[start_index + self.size]
270
+        fake_555.state[146] = self.state[start_index + (self.size * self.size) - self.size + 1]
271
+        fake_555.state[150] = self.state[start_index + (self.size * self.size)]
272
+        start_index += self.size * self.size
273
+
274
+        half_size = int(self.size/2)
275
+
276
+        # Upper
277
+        start_index = 0
278
+        fake_555.state[2] = self.state[start_index + orbit + 1]
279
+        fake_555.state[3] = self.state[start_index + half_size]
280
+        fake_555.state[4] = self.state[start_index + self.size - orbit]
281
+
282
+        fake_555.state[6] = self.state[start_index + (orbit * self.size) + 1]
283
+        fake_555.state[10] = self.state[start_index + (orbit * self.size) + self.size]
284
+
285
+        # The middle of the edge so orbit doesn't matter
286
+        fake_555.state[11] = self.state[start_index + (self.size * (half_size - 1)) + 1]
287
+        fake_555.state[15] = self.state[start_index + (self.size * half_size)]
288
+
289
+        fake_555.state[16] = self.state[start_index + (self.size * self.size) - (orbit * self.size) - self.size + 1]
290
+        fake_555.state[20] = self.state[start_index + (self.size * self.size) - (orbit * self.size)]
291
+
292
+        fake_555.state[22] = self.state[start_index + (self.size * self.size) - self.size + 1 + orbit]
293
+        fake_555.state[23] = self.state[start_index + (self.size * self.size) - half_size]
294
+        fake_555.state[24] = self.state[start_index + (self.size * self.size) - orbit]
295
+        start_index += self.size * self.size
296
+
297
+
298
+        # Left
299
+        fake_555.state[27] = self.state[start_index + orbit + 1]
300
+        fake_555.state[28] = self.state[start_index + half_size]
301
+        fake_555.state[29] = self.state[start_index + self.size - orbit]
302
+
303
+        fake_555.state[31] = self.state[start_index + (orbit * self.size) + 1]
304
+        fake_555.state[35] = self.state[start_index + (orbit * self.size) + self.size]
305
+
306
+        # The middle of the edge so orbit doesn't matter
307
+        fake_555.state[36] = self.state[start_index + (self.size * (half_size - 1)) + 1]
308
+        fake_555.state[40] = self.state[start_index + (self.size * half_size)]
309
+
310
+        fake_555.state[41] = self.state[start_index + (self.size * self.size) - (orbit * self.size) - self.size + 1]
311
+        fake_555.state[45] = self.state[start_index + (self.size * self.size) - (orbit * self.size)]
312
+
313
+        fake_555.state[47] = self.state[start_index + (self.size * self.size) - self.size + 1 + orbit]
314
+        fake_555.state[48] = self.state[start_index + (self.size * self.size) - half_size]
315
+        fake_555.state[49] = self.state[start_index + (self.size * self.size) - orbit]
316
+        start_index += self.size * self.size
317
+
318
+        # Front
319
+        fake_555.state[52] = self.state[start_index + orbit + 1]
320
+        fake_555.state[53] = self.state[start_index + half_size]
321
+        fake_555.state[54] = self.state[start_index + self.size - orbit]
322
+
323
+        fake_555.state[56] = self.state[start_index + (orbit * self.size) + 1]
324
+        fake_555.state[60] = self.state[start_index + (orbit * self.size) + self.size]
325
+
326
+        # The middle of the edge so orbit doesn't matter
327
+        fake_555.state[61] = self.state[start_index + (self.size * (half_size - 1)) + 1]
328
+        fake_555.state[65] = self.state[start_index + (self.size * half_size)]
329
+
330
+        fake_555.state[66] = self.state[start_index + (self.size * self.size) - (orbit * self.size) - self.size + 1]
331
+        fake_555.state[70] = self.state[start_index + (self.size * self.size) - (orbit * self.size)]
332
+
333
+        fake_555.state[72] = self.state[start_index + (self.size * self.size) - self.size + 1 + orbit]
334
+        fake_555.state[73] = self.state[start_index + (self.size * self.size) - half_size]
335
+        fake_555.state[74] = self.state[start_index + (self.size * self.size) - orbit]
336
+        start_index += self.size * self.size
337
+
338
+        # Right
339
+        fake_555.state[77] = self.state[start_index + orbit + 1]
340
+        fake_555.state[78] = self.state[start_index + half_size]
341
+        fake_555.state[79] = self.state[start_index + self.size - orbit]
342
+
343
+        fake_555.state[81] = self.state[start_index + (orbit * self.size) + 1]
344
+        fake_555.state[85] = self.state[start_index + (orbit * self.size) + self.size]
345
+
346
+        # The middle of the edge so orbit doesn't matter
347
+        fake_555.state[86] = self.state[start_index + (self.size * (half_size - 1)) + 1]
348
+        fake_555.state[90] = self.state[start_index + (self.size * half_size)]
349
+
350
+        fake_555.state[91] = self.state[start_index + (self.size * self.size) - (orbit * self.size) - self.size + 1]
351
+        fake_555.state[95] = self.state[start_index + (self.size * self.size) - (orbit * self.size)]
352
+
353
+        fake_555.state[97] = self.state[start_index + (self.size * self.size) - self.size + 1 + orbit]
354
+        fake_555.state[98] = self.state[start_index + (self.size * self.size) - half_size]
355
+        fake_555.state[99] = self.state[start_index + (self.size * self.size) - orbit]
356
+        start_index += self.size * self.size
357
+
358
+        # Back
359
+        fake_555.state[102] = self.state[start_index + orbit + 1]
360
+        fake_555.state[103] = self.state[start_index + half_size]
361
+        fake_555.state[104] = self.state[start_index + self.size - orbit]
362
+
363
+        fake_555.state[106] = self.state[start_index + (orbit * self.size) + 1]
364
+        fake_555.state[110] = self.state[start_index + (orbit * self.size) + self.size]
365
+
366
+        # The middle of the edge so orbit doesn't matter
367
+        fake_555.state[111] = self.state[start_index + (self.size * (half_size - 1)) + 1]
368
+        fake_555.state[115] = self.state[start_index + (self.size * half_size)]
369
+
370
+        fake_555.state[116] = self.state[start_index + (self.size * self.size) - (orbit * self.size) - self.size + 1]
371
+        fake_555.state[120] = self.state[start_index + (self.size * self.size) - (orbit * self.size)]
372
+
373
+        fake_555.state[122] = self.state[start_index + (self.size * self.size) - self.size + 1 + orbit]
374
+        fake_555.state[123] = self.state[start_index + (self.size * self.size) - half_size]
375
+        fake_555.state[124] = self.state[start_index + (self.size * self.size) - orbit]
376
+        start_index += self.size * self.size
377
+
378
+
379
+        # Down
380
+        fake_555.state[127] = self.state[start_index + orbit + 1]
381
+        fake_555.state[128] = self.state[start_index + half_size]
382
+        fake_555.state[129] = self.state[start_index + self.size - orbit]
383
+
384
+        fake_555.state[131] = self.state[start_index + (orbit * self.size) + 1]
385
+        fake_555.state[135] = self.state[start_index + (orbit * self.size) + self.size]
386
+
387
+        # The middle of the edge so orbit doesn't matter
388
+        fake_555.state[136] = self.state[start_index + (self.size * (half_size - 1)) + 1]
389
+        fake_555.state[140] = self.state[start_index + (self.size * half_size)]
390
+
391
+        fake_555.state[141] = self.state[start_index + (self.size * self.size) - (orbit * self.size) - self.size + 1]
392
+        fake_555.state[145] = self.state[start_index + (self.size * self.size) - (orbit * self.size)]
393
+
394
+        fake_555.state[147] = self.state[start_index + (self.size * self.size) - self.size + 1 + orbit]
395
+        fake_555.state[148] = self.state[start_index + (self.size * self.size) - half_size]
396
+        fake_555.state[149] = self.state[start_index + (self.size * self.size) - orbit]
397
+        start_index += self.size * self.size
398
+
399
+        self.print_cube()
400
+        fake_555.print_cube()
401
+        #sys.exit(0)
402
+        fake_555.avoid_pll = False
403
+        fake_555.group_edges()
404
+
405
+        # dwalton
406
+        wide_str = str(orbit + 1)
407
+        for step in fake_555.solution:
408
+
409
+            if step == 'EDGES_GROUPED':
410
+                continue
411
+
412
+            # Rotate the entire cube
413
+            if step.startswith('5'):
414
+                step = str(self.size) + step[1:]
415
+
416
+            elif step in ("Uw", "Uw'", "Uw2",
417
+                          "Lw", "Lw'", "Lw2",
418
+                          "Fw", "Fw'", "Fw2",
419
+                          "Rw", "Rw'", "Rw2",
420
+                          "Bw", "Bw'", "Bw2",
421
+                          "Dw", "Dw'", "Dw2"):
422
+                step = wide_str + step
423
+
424
+            #log.warning("fake_555 step %s" % step)
425
+            self.rotate(step)
426
+
427
+    def group_edges(self):
428
+
429
+        if not self.get_non_paired_edges():
430
+            self.solution.append('EDGES_GROUPED')
431
+            return
432
+
433
+        # dwalton - we need to tell both pair_inside_edges_via_444() and pair_edge_orbit_via_555() to
434
+        # avoid PLL
435
+
436
+        # Pair the inside edges via fake 4x4x4
437
+        self.pair_inside_edges_via_444()
438
+
439
+        # How many orbits of edges does this cube have?
440
+        orbits = (self.size/2) - 1
441
+
442
+        # The inside orbit was paired above via pair_inside_edges_via_444()
443
+        # For all of the rest work your way from inside to outside and pair
444
+        # them via the 5x5x5 solver.
445
+        for orbit in reversed(range(1, orbits)):
446
+            self.pair_edge_orbit_via_555(orbit)
447
+
448
+        log.info("Edges are paired, %d steps in" % self.get_solution_len_minus_rotates(self.solution))
449
+        self.print_cube()
450
+        sys.exit(0)

+ 619
- 0
rubikscubennnsolver/RubiksSide.py View File

@@ -0,0 +1,619 @@
1
+
2
+from pprint import pformat
3
+import logging
4
+import math
5
+
6
+log = logging.getLogger(__name__)
7
+
8
+
9
+def build_2d_list(squares_list):
10
+    """
11
+    Convert 1d list to a 2d list
12
+    squares_list is for a single side
13
+    """
14
+    result = []
15
+    row = []
16
+
17
+    squares_per_side = len(squares_list)
18
+    size = int(math.sqrt(squares_per_side))
19
+
20
+    for (square_index, x) in enumerate(squares_list):
21
+        row.append(x)
22
+
23
+        if (square_index + 1) % size == 0:
24
+            result.append(row)
25
+            row = []
26
+
27
+    return result
28
+
29
+
30
+class SolveError(Exception):
31
+    pass
32
+
33
+
34
+class StuckInALoop(Exception):
35
+    pass 
36
+
37
+
38
+class ImplementThis(Exception):
39
+    pass 
40
+
41
+
42
+class Side(object):
43
+
44
+    def __init__(self, parent, name):
45
+        self.parent = parent
46
+        self.name = name
47
+        self.squares_per_side = self.parent.squares_per_side
48
+        self.size = int(math.sqrt(self.squares_per_side))
49
+        self.wing_partner = {}
50
+
51
+        if self.name == 'U':
52
+            index = 0
53
+        elif self.name == 'L':
54
+            index = 1
55
+        elif self.name == 'F':
56
+            index = 2
57
+        elif self.name == 'R':
58
+            index = 3
59
+        elif self.name == 'B':
60
+            index = 4
61
+        elif self.name == 'D':
62
+            index = 5
63
+
64
+        self.min_pos = (index * self.squares_per_side) + 1
65
+        self.max_pos = (index * self.squares_per_side) + self.squares_per_side
66
+
67
+        # If this is a cube of odd size (3x3x3) then define a mid_pos
68
+        if self.size % 2 == 0:
69
+            self.mid_pos = None
70
+        else:
71
+            self.mid_pos = int((self.min_pos + self.max_pos) / 2)
72
+
73
+
74
+        # Corners
75
+        self.corner_pos = (self.min_pos,
76
+                           self.min_pos + self.size - 1,
77
+                           self.max_pos - self.size + 1,
78
+                           self.max_pos)
79
+
80
+        # Edges
81
+        self.edge_pos = []
82
+        self.edge_north_pos = []
83
+        self.edge_west_pos = []
84
+        self.edge_south_pos = []
85
+        self.edge_east_pos = []
86
+        self.center_pos = []
87
+
88
+        for position in xrange(self.min_pos, self.max_pos):
89
+            if position in self.corner_pos:
90
+                pass
91
+
92
+            # Edges at the north
93
+            elif position > self.corner_pos[0] and position < self.corner_pos[1]:
94
+                self.edge_pos.append(position)
95
+                self.edge_north_pos.append(position)
96
+
97
+            # Edges at the south
98
+            elif position > self.corner_pos[2] and position < self.corner_pos[3]:
99
+                self.edge_pos.append(position)
100
+                self.edge_south_pos.append(position)
101
+
102
+            elif (position - 1) % self.size == 0:
103
+                west_edge = position
104
+                east_edge = west_edge + self.size - 1
105
+
106
+                # Edges on the west
107
+                self.edge_pos.append(west_edge)
108
+                self.edge_west_pos.append(west_edge)
109
+
110
+                # Edges on the east
111
+                self.edge_pos.append(east_edge)
112
+                self.edge_east_pos.append(east_edge)
113
+
114
+                # Center squares
115
+                for x in xrange(west_edge + 1, east_edge):
116
+                    self.center_pos.append(x)
117
+
118
+        self.center_corner_pos = [self.min_pos + self.size + 1,
119
+                               self.min_pos + (self.size * 2) - 2,
120
+                               self.max_pos - (self.size * 2) + 2,
121
+                               self.max_pos - self.size - 1]
122
+
123
+        if self.size >= 5:
124
+            self.center_edge_pos = [self.center_corner_pos[0] + 1,
125
+                                    self.center_corner_pos[0] + self.size,
126
+                                    self.center_corner_pos[1] + self.size,
127
+                                    self.center_corner_pos[2] + 1]
128
+        else:
129
+            self.center_edge_pos = []
130
+
131
+        log.debug("\nSide %s\n\tmin/max %d/%d\n\tedges %s\n\tcorners %s\n\tcenters %s\n\tcenter corners %s\n\tcenter edges %s\n" %
132
+            (self.name, self.min_pos, self.max_pos,
133
+             pformat(self.edge_pos),
134
+             pformat(self.corner_pos),
135
+             pformat(self.center_pos),
136
+             pformat(self.center_corner_pos),
137
+             pformat(self.center_edge_pos)))
138
+
139
+    def __str__(self):
140
+        return 'side %s' % self.name
141
+
142
+    def is_even(self):
143
+        if self.size % 2 == 0:
144
+            return True
145
+        return False
146
+
147
+    def is_odd(self):
148
+        if self.size % 2 == 0:
149
+            return False
150
+        return True
151
+
152
+    # TODO rename these center_corner_is...
153
+    def corner_is_top_left(self, square_index):
154
+        return bool(self.center_corner_pos[0] == square_index)
155
+
156
+    def corner_is_top_right(self, square_index):
157
+        return bool(self.center_corner_pos[1] == square_index)
158
+
159
+    def corner_is_bottom_left(self, square_index):
160
+        return bool(self.center_corner_pos[2] == square_index)
161
+
162
+    def corner_is_bottom_right(self, square_index):
163
+        return bool(self.center_corner_pos[3] == square_index)
164
+
165
+    # TODO rename these center_edge_is...
166
+    def edge_is_top(self, square_index):
167
+        return bool(self.center_edge_pos[0] == square_index)
168
+
169
+    def edge_is_left(self, square_index):
170
+        return bool(self.center_edge_pos[1] == square_index)
171
+
172
+    def edge_is_right(self, square_index):
173
+        return bool(self.center_edge_pos[2] == square_index)
174
+
175
+    def edge_is_bottom(self, square_index):
176
+        return bool(self.center_edge_pos[3] == square_index)
177
+
178
+    def get_face_as_2d_list(self):
179
+        """
180
+        Used by RubiksCube rotate()
181
+        """
182
+        return build_2d_list([self.parent.state[square_index] for square_index in xrange(self.min_pos, self.max_pos + 1)])
183
+
184
+    def center_corners_solved(self):
185
+        if self.mid_pos:
186
+            mid_pos_value = self.parent.state[self.mid_pos]
187
+        else:
188
+            mid_pos_value = None
189
+
190
+        prev_value = None
191
+
192
+        for square_index in self.center_corner_pos:
193
+            square_value = self.parent.state[square_index]
194
+
195
+            if mid_pos_value:
196
+                if square_value != mid_pos_value:
197
+                    return False
198
+
199
+            if prev_value is not None:
200
+                if prev_value != square_value:
201
+                    return False
202
+            prev_value = square_value
203
+
204
+        return True
205
+
206
+    def center_edges_solved(self):
207
+        if not self.mid_pos:
208
+            return True
209
+
210
+        target = self.parent.state[self.mid_pos]
211
+
212
+        for square_index in self.center_edge_pos:
213
+            square_value = self.parent.state[square_index]
214
+            if square_value != target:
215
+                return False
216
+        return True
217
+
218
+    def center_solved(self):
219
+        if self.center_corners_solved() and self.center_edges_solved():
220
+            return True
221
+        return False
222
+
223
+    def verify_center_corners_solved(self):
224
+        if not self.center_corners_solved():
225
+            raise SolveError("%s center corners are not solved" % self)
226
+
227
+    def verify_center_edges_solved(self):
228
+        if not self.center_edges_solved():
229
+            raise SolveError("%s center edges are not solved" % self)
230
+
231
+    def verify_center_solved(self):
232
+        if not self.center_solved():
233
+            raise SolveError("%s center not solved" % self)
234
+
235
+    def get_wing_partner(self, wing_index):
236
+        try:
237
+            return self.wing_partner[wing_index]
238
+        except KeyError:
239
+            log.info("wing_partner\n%s\n" % pformat(self.wing_partner))
240
+            raise
241
+
242
+    def get_wing_neighbors(self, target_wing_index):
243
+
244
+        if target_wing_index in self.edge_north_pos:
245
+            edges_to_check = self.edge_north_pos
246
+
247
+        elif target_wing_index in self.edge_west_pos:
248
+            edges_to_check = self.edge_west_pos
249
+
250
+        elif target_wing_index in self.edge_south_pos:
251
+            edges_to_check = self.edge_south_pos
252
+
253
+        elif target_wing_index in self.edge_east_pos:
254
+            edges_to_check = self.edge_east_pos
255
+
256
+        else:
257
+            raise SolveError("%s does not have wing %d" % (self, target_wing_index))
258
+
259
+        neighbors = []
260
+        prev_wing = None
261
+
262
+        for wing in edges_to_check:
263
+            if wing == target_wing_index:
264
+                if prev_wing is not None:
265
+                    neighbors.append(prev_wing)
266
+            elif prev_wing == target_wing_index:
267
+                neighbors.append(wing)
268
+            prev_wing = wing
269
+
270
+        return neighbors
271
+
272
+    def paired_wings(self, check_north, check_west, check_south, check_east):
273
+        paired_wings = []
274
+
275
+        # north edge
276
+        if check_north:
277
+            prev_pos1 = None
278
+            prev_pos2 = None
279
+
280
+            for pos1 in self.edge_north_pos:
281
+                pos2 = self.get_wing_partner(pos1)
282
+                if prev_pos1 is not None:
283
+                    if self.parent.state[prev_pos1] == self.parent.state[pos1] and self.parent.state[prev_pos2] == self.parent.state[pos2]:
284
+                        paired_wings.append(((pos1, pos2), (prev_pos1, prev_pos2)))
285
+                prev_pos1 = pos1
286
+                prev_pos2 = pos2
287
+
288
+        # west edge
289
+        if check_west:
290
+            prev_pos1 = None
291
+            prev_pos2 = None
292
+
293
+            for pos1 in self.edge_west_pos:
294
+                pos2 = self.get_wing_partner(pos1)
295
+                if prev_pos1 is not None:
296
+                    if self.parent.state[prev_pos1] == self.parent.state[pos1] and self.parent.state[prev_pos2] == self.parent.state[pos2]:
297
+                        paired_wings.append(((pos1, pos2), (prev_pos1, prev_pos2)))
298
+                prev_pos1 = pos1
299
+                prev_pos2 = pos2
300
+
301
+        # south edge
302
+        if check_south:
303
+            prev_pos1 = None
304
+            prev_pos2 = None
305
+
306
+            for pos1 in self.edge_south_pos:
307
+                pos2 = self.get_wing_partner(pos1)
308
+                if prev_pos1 is not None:
309
+                    if self.parent.state[prev_pos1] == self.parent.state[pos1] and self.parent.state[prev_pos2] == self.parent.state[pos2]:
310
+                        paired_wings.append(((pos1, pos2), (prev_pos1, prev_pos2)))
311
+                prev_pos1 = pos1
312
+                prev_pos2 = pos2
313
+
314
+        # east edge
315
+        if check_east:
316
+            prev_pos1 = None
317
+            prev_pos2 = None
318
+
319
+            for pos1 in self.edge_east_pos:
320
+                pos2 = self.get_wing_partner(pos1)
321
+                if prev_pos1 is not None:
322
+                    if self.parent.state[prev_pos1] == self.parent.state[pos1] and self.parent.state[prev_pos2] == self.parent.state[pos2]:
323
+                        paired_wings.append(((pos1, pos2), (prev_pos1, prev_pos2)))
324
+                prev_pos1 = pos1
325
+                prev_pos2 = pos2
326
+
327
+        return paired_wings
328
+
329
+    def non_paired_wings(self, check_north, check_west, check_south, check_east):
330
+        """
331
+        TODO there is a lot of cut-n-paste code in here to clean up
332
+        """
333
+        non_paired_wings = []
334
+
335
+        # north edge
336
+        if check_north:
337
+            prev_pos1 = None
338
+            prev_pos2 = None
339
+
340
+            for pos1 in self.edge_north_pos:
341
+                pos2 = self.get_wing_partner(pos1)
342
+                if prev_pos1 is not None:
343
+                    if self.parent.state[prev_pos1] != self.parent.state[pos1] or self.parent.state[prev_pos2] != self.parent.state[pos2]:
344
+                        non_paired_wings.append(((pos1, pos2), (prev_pos1, prev_pos2)))
345
+                prev_pos1 = pos1
346
+                prev_pos2 = pos2
347
+
348
+        # west edge
349
+        if check_west:
350
+            prev_pos1 = None
351
+            prev_pos2 = None
352
+
353
+            for pos1 in self.edge_west_pos:
354
+                pos2 = self.get_wing_partner(pos1)
355
+                if prev_pos1 is not None:
356
+                    if self.parent.state[prev_pos1] != self.parent.state[pos1] or self.parent.state[prev_pos2] != self.parent.state[pos2]:
357
+                        non_paired_wings.append(((pos1, pos2), (prev_pos1, prev_pos2)))
358
+                prev_pos1 = pos1
359
+                prev_pos2 = pos2
360
+
361
+        # south edge
362
+        if check_south:
363
+            prev_pos1 = None
364
+            prev_pos2 = None
365
+
366
+            for pos1 in self.edge_south_pos:
367
+                pos2 = self.get_wing_partner(pos1)
368
+                if prev_pos1 is not None:
369
+                    if self.parent.state[prev_pos1] != self.parent.state[pos1] or self.parent.state[prev_pos2] != self.parent.state[pos2]:
370
+                        non_paired_wings.append(((pos1, pos2), (prev_pos1, prev_pos2)))
371
+                prev_pos1 = pos1
372
+                prev_pos2 = pos2
373
+
374
+        # east edge
375
+        if check_east:
376
+            prev_pos1 = None
377
+            prev_pos2 = None
378
+
379
+            for pos1 in self.edge_east_pos:
380
+                pos2 = self.get_wing_partner(pos1)
381
+                if prev_pos1 is not None:
382
+                    if self.parent.state[prev_pos1] != self.parent.state[pos1] or self.parent.state[prev_pos2] != self.parent.state[pos2]:
383
+                        non_paired_wings.append(((pos1, pos2), (prev_pos1, prev_pos2)))
384
+                prev_pos1 = pos1
385
+                prev_pos2 = pos2
386
+
387
+        return non_paired_wings
388
+
389
+    def non_paired_edges(self, check_north, check_west, check_south, check_east):
390
+        """
391
+        TODO there is a lot of cut-n-paste code in here to clean up
392
+        """
393
+        non_paired_edges = []
394
+
395
+        # north edge
396
+        if check_north:
397
+            prev_pos1 = None
398
+            prev_pos2 = None
399
+
400
+            for pos1 in self.edge_north_pos:
401
+                pos2 = self.get_wing_partner(pos1)
402
+                if prev_pos1 is not None:
403
+                    if self.parent.state[prev_pos1] != self.parent.state[pos1] or self.parent.state[prev_pos2] != self.parent.state[pos2]:
404
+                        non_paired_edges.append(((pos1, pos2), (prev_pos1, prev_pos2)))
405
+                        break
406
+                prev_pos1 = pos1
407
+                prev_pos2 = pos2
408
+
409
+        # west edge
410
+        if check_west:
411
+            prev_pos1 = None
412
+            prev_pos2 = None
413
+
414
+            for pos1 in self.edge_west_pos:
415
+                pos2 = self.get_wing_partner(pos1)
416
+                if prev_pos1 is not None:
417
+                    if self.parent.state[prev_pos1] != self.parent.state[pos1] or self.parent.state[prev_pos2] != self.parent.state[pos2]:
418
+                        non_paired_edges.append(((pos1, pos2), (prev_pos1, prev_pos2)))
419
+                        break
420
+                prev_pos1 = pos1
421
+                prev_pos2 = pos2
422
+
423
+        # south edge
424
+        if check_south:
425
+            prev_pos1 = None
426
+            prev_pos2 = None
427
+
428
+            for pos1 in self.edge_south_pos:
429
+                pos2 = self.get_wing_partner(pos1)
430
+                if prev_pos1 is not None:
431
+                    if self.parent.state[prev_pos1] != self.parent.state[pos1] or self.parent.state[prev_pos2] != self.parent.state[pos2]:
432
+                        non_paired_edges.append(((pos1, pos2), (prev_pos1, prev_pos2)))
433
+                        break
434
+                prev_pos1 = pos1
435
+                prev_pos2 = pos2
436
+
437
+        # east edge
438
+        if check_east:
439
+            prev_pos1 = None
440
+            prev_pos2 = None
441
+
442
+            for pos1 in self.edge_east_pos:
443
+                pos2 = self.get_wing_partner(pos1)
444
+                if prev_pos1 is not None:
445
+                    if self.parent.state[prev_pos1] != self.parent.state[pos1] or self.parent.state[prev_pos2] != self.parent.state[pos2]:
446
+                        non_paired_edges.append(((pos1, pos2), (prev_pos1, prev_pos2)))
447
+                        break
448
+                prev_pos1 = pos1
449
+                prev_pos2 = pos2
450
+
451
+        #log.info("%s; non_paired_edges %s for check_north %s, check_west %s, check_south %s, check_east %s" %
452
+        #    (self, check_north, check_west, check_south, check_east, non_paired_edges))
453
+        return non_paired_edges
454
+
455
+    def all_edges_paired(self):
456
+        if self.non_paired_edges(True, True, True, True):
457
+            return False
458
+        return True
459
+
460
+    def north_edge_non_paired(self):
461
+        """
462
+        TODO lots of cut-n-paste code here
463
+        """
464
+        non_paired_edges = self.non_paired_wings(True, False, False, False)
465
+
466
+        for ((pos1, pos2), (pos3, pos4)) in non_paired_edges:
467
+            if pos1 in self.edge_north_pos:
468
+                return True
469
+            if pos3 in self.edge_north_pos:
470
+                return True
471
+        return False
472
+
473
+    def north_edge_non_paired_wings_count(self):
474
+        return len(self.non_paired_wings(True, False, False, False))
475
+
476
+    def west_edge_non_paired_wings_count(self):
477
+        return len(self.non_paired_wings(False, True, False, False))
478
+
479
+    def south_edge_non_paired_wings_count(self):
480
+        return len(self.non_paired_wings(False, False, True, False))
481
+
482
+    def east_edge_non_paired_wings_count(self):
483
+        return len(self.non_paired_wings(False, False, False, True))
484
+
485
+    def north_edge_paired(self):
486
+        return not self.north_edge_non_paired()
487
+
488
+    def north_wing_paired(self):
489
+        if self.paired_wings(True, False, False, False):
490
+            return True
491
+        return False
492
+
493
+    def south_edge_non_paired(self):
494
+        non_paired_edges = self.non_paired_wings(False, False, True, False)
495
+
496
+        for ((pos1, pos2), (pos3, pos4)) in non_paired_edges:
497
+            if pos1 in self.edge_south_pos:
498
+                return True
499
+            if pos3 in self.edge_south_pos:
500
+                return True
501
+        return False