from copy import copy from collections import OrderedDict from pprint import pformat from rubikscubennnsolver.RubiksSide import Side, SolveError, StuckInALoop, ImplementThis import itertools import json import logging import math import os import random import re import shutil import subprocess import sys log = logging.getLogger(__name__) class InvalidCubeReduction(Exception): pass def reverse_steps(steps): """ Reverse the order of all steps and the direction of each individual step """ results = [] for step in reversed(steps): if step.endswith("'"): reverse_step = step[0:-1] else: reverse_step = step + "'" results.append(reverse_step) return results def get_cube_layout(size): """ Example: size is 3, return the following string: 01 02 03 04 05 06 07 08 09 10 11 12 19 20 21 28 29 30 37 38 39 13 14 15 22 23 24 31 32 33 40 41 42 16 17 18 25 26 27 34 35 36 43 44 45 46 47 48 49 50 51 52 53 54 """ result = [] squares = (size * size) * 6 square_index = 1 if squares >= 1000: digits_size = 4 digits_format = "%04d " elif squares >= 100: digits_size = 3 digits_format = "%03d " else: digits_size = 2 digits_format = "%02d " indent = ((digits_size * size) + size + 1) * ' ' rows = size * 3 for row in range(1, rows + 1): line = [] if row <= size: line.append(indent) for col in range(1, size + 1): line.append(digits_format % square_index) square_index += 1 elif row > rows - size: line.append(indent) for col in range(1, size + 1): line.append(digits_format % square_index) square_index += 1 else: init_square_index = square_index last_col = size * 4 for col in range(1, last_col + 1): line.append(digits_format % square_index) if col == last_col: square_index += 1 elif col % size == 0: square_index += (size * size) - size + 1 line.append(' ') else: square_index += 1 if row % size: square_index = init_square_index + size result.append(''.join(line)) if row == size or row == rows - size: result.append('') return '\n'.join(result) def rotate_2d_list(squares_list): """ http://stackoverflow.com/questions/8421337/rotating-a-two-dimensional-array-in-python """ return [x for x in zip(*squares_list[::-1])] def rotate_clockwise(squares_list): return rotate_2d_list(squares_list) def rotate_counter_clockwise(squares_list): squares_list = rotate_2d_list(squares_list) squares_list = rotate_2d_list(squares_list) squares_list = rotate_2d_list(squares_list) return squares_list def compress_2d_list(squares_list): """ Convert 2d list to a 1d list """ return [col for row in squares_list for col in row] def find_index_for_value(list_foo, target, min_index): for (index, value) in enumerate(list_foo): if value == target and index >= min_index: return index raise SolveError("Did not find %s in list %s" % (target, pformat(list_foo))) def get_swap_count(listA, listB, debug): """ How many swaps do we have to make in listB for it to match listA Example: A = [1, 2, 3, 0, 4] B = [3, 4, 1, 0, 2] would require 2 swaps """ A_length = len(listA) B_length = len(listB) swaps = 0 index = 0 if A_length != B_length: log.info("listA %s" % ' '.join(listA)) log.info("listB %s" % ' '.join(listB)) assert False, "listA (len %d) and listB (len %d) must be the same length" % (A_length, B_length) if debug: log.info("INIT") log.info("listA: %s" % ' '.join(listA)) log.info("listB: %s" % ' '.join(listB)) log.info("") while listA != listB: if listA[index] != listB[index]: listA_value = listA[index] listB_index_with_A_value = find_index_for_value(listB, listA_value, index+1) tmp = listB[index] listB[index] = listB[listB_index_with_A_value] listB[listB_index_with_A_value] = tmp swaps += 1 if debug: log.info("index %d, swaps %d" % (index, swaps)) log.info("listA: %s" % ' '.join(listA)) log.info("listB: %s" % ' '.join(listB)) log.info("") index += 1 if debug: log.info("swaps: %d" % swaps) log.info("") return swaps def apply_rotations(size, step, rotations): """ Apply the "rotations" to step and return the step. This is used by compress_solution() to remove all of the whole cube rotations from the solution. """ if step in ('CENTERS_SOLVED', 'EDGES_GROUPED'): return step for rotation in rotations: # remove the number at the start of the rotation...for a 4x4x4 cube # there might be a 4U rotation (to rotate about the y-axis) but we # don't need to keep the '4' part. if size <= 9: rotation = rotation[1:] elif size <= 99: rotation = rotation[2:] else: rotation = rotation[3:] # For a 100x or larger cube!! if rotation == "U" or rotation == "D'": if "U" in step: pass elif "L" in step: step = step.replace("L", "F") elif "F" in step: step = step.replace("F", "R") elif "R" in step: step = step.replace("R", "B") elif "B" in step: step = step.replace("B", "L") elif "D" in step: pass elif rotation == "U'" or rotation == "D": if "U" in step: pass elif "L" in step: step = step.replace("L", "B") elif "F" in step: step = step.replace("F", "L") elif "R" in step: step = step.replace("R", "F") elif "B" in step: step = step.replace("B", "R") elif "D" in step: pass elif rotation == "F" or rotation == "B'": if "U" in step: step = step.replace("U", "L") elif "L" in step: step = step.replace("L", "D") elif "F" in step: pass elif "R" in step: step = step.replace("R", "U") elif "B" in step: pass elif "D" in step: step = step.replace("D", "R") elif rotation == "F'" or rotation == "B": if "U" in step: step = step.replace("U", "R") elif "L" in step: step = step.replace("L", "U") elif "F" in step: pass elif "R" in step: step = step.replace("R", "D") elif "B" in step: pass elif "D" in step: step = step.replace("D", "L") elif rotation == "R" or rotation == "L'": if "U" in step: step = step.replace("U", "F") elif "L" in step: pass elif "F" in step: step = step.replace("F", "D") elif "R" in step: pass elif "B" in step: step = step.replace("B", "U") elif "D" in step: step = step.replace("D", "B") elif rotation == "R'" or rotation == "L": if "U" in step: step = step.replace("U", "B") elif "L" in step: pass elif "F" in step: step = step.replace("F", "U") elif "R" in step: pass elif "B" in step: step = step.replace("B", "D") elif "D" in step: step = step.replace("D", "F") else: raise Exception("%s is an invalid rotation" % rotation) return step def orbit_matches(edges_per_side, orbit, edge_index): if orbit is None: return True if orbit == 0: if edge_index == 0 or edge_index == edges_per_side-1: return True return False if orbit == 1: if edge_index == 1 or edge_index == edges_per_side-2: return True return False if edge_index == orbit or edge_index == (edges_per_side - 1 - orbit): return True return False def get_important_square_indexes(size): """ Used for writing www pages """ squares_per_side = size * size max_square = squares_per_side * 6 first_squares = [] last_squares = [] for index in range(1, max_square + 1): if (index - 1) % squares_per_side == 0: first_squares.append(index) elif index % squares_per_side == 0: last_squares.append(index) last_UBD_squares = (last_squares[0], last_squares[4], last_squares[5]) return (first_squares, last_squares, last_UBD_squares) def number_ranges(i): """ https://stackoverflow.com/questions/4628333/converting-a-list-of-integers-into-range-in-python """ for a, b in itertools.groupby(enumerate(i), lambda x_y: x_y[1] - x_y[0]): b = list(b) yield b[0][1], b[-1][1] class RubiksCube(object): def __init__(self, state_string, order, colormap=None, debug=False): init_state = ['dummy', ] init_state.extend(list(state_string)) self.squares_per_side = int((len(init_state) - 1)/6) self.size = math.sqrt(self.squares_per_side) assert str(self.size).endswith('.0'), "Cube has %d squares per side which is not possible" % self.squares_per_side self.size = int(self.size) self.solution = [] self.steps_to_rotate_cube = 0 self.steps_to_solve_centers = 0 self.steps_to_group_edges = 0 self.steps_to_solve_3x3x3 = 0 self.ida_count = 0 self._phase = None self.lt_init_called = False self.orient_edges = {} self.cpu_mode = None if colormap: colormap = json.loads(colormap) self.color_map = {} self.color_map_html = {} for (side_name, color) in list(colormap.items()): side_name = str(side_name) if color == 'Wh': self.color_map[side_name] = 97 self.color_map_html[side_name] = (235, 254, 250) elif color == 'Gr': self.color_map[side_name] = 92 self.color_map_html[side_name] = (20, 105, 74) elif color == 'Rd': self.color_map[side_name] = 91 self.color_map_html[side_name] = (104, 4, 2) elif color == 'Bu': self.color_map[side_name] = 94 self.color_map_html[side_name] = (22, 57, 103) elif color == 'OR': self.color_map[side_name] = 90 self.color_map_html[side_name] = (148, 53, 9) elif color == 'Ye': self.color_map[side_name] = 93 self.color_map_html[side_name] = (210, 208, 2) #log.warning("color_map:\n%s\n" % pformat(self.color_map)) else: # Match the colors on alg.cubing.net to make life easier self.color_map = { 'U': 97, # Wh 'L': 90, # Or 'F': 92, # Gr 'R': 91, # Rd 'B': 94, # Bu 'D': 93, # Ye } self.color_map_html = { 'U': (235, 254, 250), # Wh 'L': (148, 53, 9), # Or 'F': (20, 105, 74), # Gr 'R': (104, 4, 2), # Rd 'B': (22, 57, 103), # Bu 'D': (210, 208, 2), # Ye 'x': (0, 0, 0), # black } if debug: log.setLevel(logging.DEBUG) self.load_state(state_string, order) self.sides = OrderedDict() self.sides['U'] = Side(self, 'U') self.sides['L'] = Side(self, 'L') self.sides['F'] = Side(self, 'F') self.sides['R'] = Side(self, 'R') self.sides['B'] = Side(self, 'B') self.sides['D'] = Side(self, 'D') self.sideU = self.sides['U'] self.sideL = self.sides['L'] self.sideF = self.sides['F'] self.sideR = self.sides['R'] self.sideB = self.sides['B'] self.sideD = self.sides['D'] self.all_edge_positions = [] # U and B for (pos1, pos2) in zip(self.sideU.edge_north_pos, reversed(self.sideB.edge_north_pos)): self.all_edge_positions.append((pos1, pos2)) # U and L for (pos1, pos2) in zip(self.sideU.edge_west_pos, self.sideL.edge_north_pos): self.all_edge_positions.append((pos1, pos2)) # U and F for (pos1, pos2) in zip(self.sideU.edge_south_pos, self.sideF.edge_north_pos): self.all_edge_positions.append((pos1, pos2)) # U and R for (pos1, pos2) in zip(self.sideU.edge_east_pos, reversed(self.sideR.edge_north_pos)): self.all_edge_positions.append((pos1, pos2)) # F and L for (pos1, pos2) in zip(self.sideF.edge_west_pos, self.sideL.edge_east_pos): self.all_edge_positions.append((pos1, pos2)) # F and R for (pos1, pos2) in zip(self.sideF.edge_east_pos, self.sideR.edge_west_pos): self.all_edge_positions.append((pos1, pos2)) # F and D for (pos1, pos2) in zip(self.sideF.edge_south_pos, self.sideD.edge_north_pos): self.all_edge_positions.append((pos1, pos2)) # L and B for (pos1, pos2) in zip(self.sideL.edge_west_pos, self.sideB.edge_east_pos): self.all_edge_positions.append((pos1, pos2)) # L and D for (pos1, pos2) in zip(self.sideL.edge_south_pos, reversed(self.sideD.edge_west_pos)): self.all_edge_positions.append((pos1, pos2)) # R and D for (pos1, pos2) in zip(self.sideR.edge_south_pos, self.sideD.edge_east_pos): self.all_edge_positions.append((pos1, pos2)) # R and B for (pos1, pos2) in zip(self.sideR.edge_east_pos, self.sideB.edge_west_pos): self.all_edge_positions.append((pos1, pos2)) # B and D for (pos1, pos2) in zip(reversed(self.sideB.edge_south_pos), self.sideD.edge_south_pos): self.all_edge_positions.append((pos1, pos2)) for side in list(self.sides.values()): side.calculate_wing_partners() def __str__(self): return "%dx%dx%d" % (self.size, self.size, self.size) def _sanity_check(self, desc, indexes, expected_count): count = { 'U' : 0, 'L' : 0, 'F' : 0, 'R' : 0, 'B' : 0, 'D' : 0, 'x' : 0, } for x in indexes: count[self.state[x]] += 1 for (side, value) in count.items(): if side == 'x' or value == 0: continue if value != expected_count: self.print_cube() raise InvalidCubeReduction("side %s %s count is %d (should be %d)" % (desc, side, value, expected_count)) def sanity_check(self): """ Implemented by the various child classes to verify that the 'state' content makes sense """ pass def load_state(self, state_string, order): # kociemba_string is in URFDLB order so split this apart and re-arrange it to # be ULFRBD so that is is sequential with the normal square numbering scheme foo = [] init_state = ['dummy', ] init_state.extend(list(state_string)) if order == 'URFDLB': foo.extend(init_state[1:self.squares_per_side + 1]) # U foo.extend(init_state[(self.squares_per_side * 4) + 1 : (self.squares_per_side * 5) + 1]) # L foo.extend(init_state[(self.squares_per_side * 2) + 1 : (self.squares_per_side * 3) + 1]) # F foo.extend(init_state[(self.squares_per_side * 1) + 1 : (self.squares_per_side * 2) + 1]) # R foo.extend(init_state[(self.squares_per_side * 5) + 1 : (self.squares_per_side * 6) + 1]) # B foo.extend(init_state[(self.squares_per_side * 3) + 1 : (self.squares_per_side * 4) + 1]) # D elif order == 'ULFRBD': foo.extend(init_state[1:self.squares_per_side + 1]) # U foo.extend(init_state[(self.squares_per_side * 1) + 1 : (self.squares_per_side * 2) + 1]) # L foo.extend(init_state[(self.squares_per_side * 2) + 1 : (self.squares_per_side * 3) + 1]) # F foo.extend(init_state[(self.squares_per_side * 3) + 1 : (self.squares_per_side * 4) + 1]) # R foo.extend(init_state[(self.squares_per_side * 4) + 1 : (self.squares_per_side * 5) + 1]) # B foo.extend(init_state[(self.squares_per_side * 5) + 1 : (self.squares_per_side * 6) + 1]) # D else: raise Exception("Add support for order %s" % order) self.state = ['x', ] for (square_index, side_name) in enumerate(foo): self.state.append(side_name) def is_even(self): if self.size % 2 == 0: return True return False def is_odd(self): if self.size % 2 == 0: return False return True def solved(self): """ Return True if the cube is solved """ for side in list(self.sides.values()): if not side.solved(): return False return True def rotation_map(self, action): """ This returns a set of tuples that correspond to the movements of each piece on the cube for the move "action". """ results = [] if action[-1] in ("'", "`"): reverse = True action = action[0:-1] else: reverse = False # 2Uw, Uw and 2U all mean rotate the top 2 U rows # 3Uw and 3U mean rotate the top 3 U rows if len(action) >= 2 and action[0].isdigit() and action[1].isdigit(): rows_to_rotate = int(action[0:2]) action = action[2:] elif action[0].isdigit(): rows_to_rotate = int(action[0]) action = action[1:] else: # Uw also means rotate the top 2 U rows if 'w' in action: rows_to_rotate = 2 else: rows_to_rotate = 1 # We've accounted for this so remove it if 'w' in action: action = action.replace('w', '') # The digit at the end indicates how many quarter turns to do if action[-1].isdigit(): quarter_turns = int(action[-1]) action = action[0:-1] else: quarter_turns = 1 side_name = action # Skip moves x, y, z side = self.sides[side_name] min_pos = side.min_pos max_pos = side.max_pos # Rotation of the squares of the face itself for turn in range(quarter_turns): # Number the squares of the faces # in a 2D array, then re-use all the # existing transforms oldface = [] counter = min_pos for ii in range(self.size): facerow = [] for jj in range(self.size): facerow.append(counter) counter += 1 oldface.append(facerow) # This is not what we want, because it returns # the face colors, not the face index numbers: #oldface = side.get_face_as_2d_list() if reverse: newface = rotate_counter_clockwise(oldface) else: newface = rotate_clockwise(oldface) oldface = compress_2d_list(oldface) newface = compress_2d_list(newface) for (j,k) in zip(oldface,newface): results.append((j,k)) # Again skip rotation of entire cube with x, y, z if side_name == "U": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): left_first_square = self.squares_per_side + 1 + (row * self.size) left_last_square = left_first_square + self.size - 1 front_first_square = (self.squares_per_side * 2) + 1 + (row * self.size) front_last_square = front_first_square + self.size - 1 right_first_square = (self.squares_per_side * 3) + 1 + (row * self.size) right_last_square = right_first_square + self.size - 1 back_first_square = (self.squares_per_side * 4) + 1 + (row * self.size) back_last_square = back_first_square + self.size - 1 if reverse: for square_index in range(left_first_square, left_last_square + 1): old_index = square_index new_index = square_index + (3 * self.squares_per_side) results.append((old_index,new_index)) for square_index in range(front_first_square, front_last_square + 1): old_index = square_index new_index = square_index - self.squares_per_side results.append((old_index,new_index)) for square_index in range(right_first_square, right_last_square + 1): old_index = square_index new_index = square_index - self.squares_per_side results.append((old_index,new_index)) for square_index in range(back_first_square, back_last_square + 1): old_index = square_index new_index = square_index - self.squares_per_side results.append((old_index,new_index)) else: for square_index in range(left_first_square, left_last_square + 1): old_index = square_index new_index = square_index + self.squares_per_side results.append((old_index,new_index)) for square_index in range(front_first_square, front_last_square + 1): old_index = square_index new_index = square_index + self.squares_per_side results.append((old_index,new_index)) for square_index in range(right_first_square, right_last_square + 1): old_index = square_index new_index = square_index + self.squares_per_side results.append((old_index,new_index)) for square_index in range(back_first_square, back_last_square + 1): old_index = square_index new_index = square_index - (3 * self.squares_per_side) results.append((old_index,new_index)) elif side_name == "L": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): top_first_square = 1 + row top_last_square = top_first_square + ((self.size - 1) * self.size) front_first_square = (self.squares_per_side * 2) + 1 + row front_last_square = front_first_square + ((self.size - 1) * self.size) down_first_square = (self.squares_per_side * 5) + 1 + row down_last_square = down_first_square + ((self.size - 1) * self.size) back_first_square = (self.squares_per_side * 4) + self.size - row back_last_square = back_first_square + ((self.size - 1) * self.size) top_squares = [] for square_index in range(top_first_square, top_last_square + 1, self.size): top_squares.append(square_index) front_squares = [] for square_index in range(front_first_square, front_last_square + 1, self.size): front_squares.append(square_index) down_squares = [] for square_index in range(down_first_square, down_last_square + 1, self.size): down_squares.append(square_index) back_squares = [] for square_index in range(back_first_square, back_last_square + 1, self.size): back_squares.append(square_index) if reverse: for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)): old_index = square_index new_index = front_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)): old_index = square_index new_index = down_squares[index] results.append((old_index,new_index)) back_squares = list(reversed(back_squares)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)): old_index = square_index new_index = back_squares[index] results.append((old_index,new_index)) top_squares = list(reversed(top_squares)) for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)): old_index = square_index new_index = top_squares[index] results.append((old_index,new_index)) else: back_squares = list(reversed(back_squares)) for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)): old_index = square_index new_index = back_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)): old_index = square_index new_index = top_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)): old_index = square_index new_index = front_squares[index] results.append((old_index,new_index)) down_squares = list(reversed(down_squares)) for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)): old_index = square_index new_index = down_squares[index] results.append((old_index,new_index)) elif side_name == "F": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): top_first_square = (self.squares_per_side - self.size) + 1 - (row * self.size) top_last_square = top_first_square + self.size - 1 left_first_square = self.squares_per_side + self.size - row left_last_square = left_first_square + ((self.size - 1) * self.size) down_first_square = (self.squares_per_side * 5) + 1 + (row * self.size) down_last_square = down_first_square + self.size - 1 right_first_square = (self.squares_per_side * 3) + 1 + row right_last_square = right_first_square + ((self.size - 1) * self.size) #log.info("top first %d, last %d" % (top_first_square, top_last_square)) #log.info("left first %d, last %d" % (left_first_square, left_last_square)) #log.info("down first %d, last %d" % (down_first_square, down_last_square)) #log.info("right first %d, last %d" % (right_first_square, right_last_square)) top_squares = [] for square_index in range(top_first_square, top_last_square + 1): top_squares.append(square_index) left_squares = [] for square_index in range(left_first_square, left_last_square + 1, self.size): left_squares.append(square_index) down_squares = [] for square_index in range(down_first_square, down_last_square + 1): down_squares.append(square_index) right_squares = [] for square_index in range(right_first_square, right_last_square + 1, self.size): right_squares.append(square_index) if reverse: for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)): old_index = square_index new_index = right_squares[index] results.append((old_index,new_index)) top_squares = list(reversed(top_squares)) for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)): old_index = square_index new_index = top_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)): old_index = square_index new_index = left_squares[index] results.append((old_index,new_index)) down_squares = list(reversed(down_squares)) for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)): old_index = square_index new_index = down_squares[index] results.append((old_index,new_index)) else: left_squares = list(reversed(left_squares)) for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)): old_index = square_index new_index = left_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)): old_index = square_index new_index = down_squares[index] results.append((old_index,new_index)) right_squares = list(reversed(right_squares)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)): old_index = square_index new_index = right_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)): old_index = square_index new_index = top_squares[index] results.append((old_index,new_index)) elif side_name == "R": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): top_first_square = self.size - row top_last_square = self.squares_per_side front_first_square = (self.squares_per_side * 2) + self.size - row front_last_square = front_first_square + ((self.size - 1) * self.size) down_first_square = (self.squares_per_side * 5) + self.size - row down_last_square = down_first_square + ((self.size - 1) * self.size) back_first_square = (self.squares_per_side * 4) + 1 + row back_last_square = back_first_square + ((self.size - 1) * self.size) #log.info("top first %d, last %d" % (top_first_square, top_last_square)) #log.info("front first %d, last %d" % (front_first_square, front_last_square)) #log.info("down first %d, last %d" % (down_first_square, down_last_square)) #log.info("back first %d, last %d" % (back_first_square, back_last_square)) top_squares = [] for square_index in range(top_first_square, top_last_square + 1, self.size): top_squares.append(square_index) front_squares = [] for square_index in range(front_first_square, front_last_square + 1, self.size): front_squares.append(square_index) down_squares = [] for square_index in range(down_first_square, down_last_square + 1, self.size): down_squares.append(square_index) back_squares = [] for square_index in range(back_first_square, back_last_square + 1, self.size): back_squares.append(square_index) if reverse: back_squares = list(reversed(back_squares)) for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)): old_index = square_index new_index = back_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)): old_index = square_index new_index = top_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)): old_index = square_index new_index = front_squares[index] results.append((old_index,new_index)) down_squares = list(reversed(down_squares)) for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)): old_index = square_index new_index = down_squares[index] results.append((old_index,new_index)) else: for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)): old_index = square_index new_index = front_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)): old_index = square_index new_index = down_squares[index] results.append((old_index,new_index)) back_squares = list(reversed(back_squares)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)): old_index = square_index new_index = back_squares[index] results.append((old_index,new_index)) top_squares = list(reversed(top_squares)) for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)): old_index = square_index new_index = top_squares[index] results.append((old_index,new_index)) elif side_name == "B": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): top_first_square = 1 + (row * self.size) top_last_square = top_first_square + self.size - 1 left_first_square = self.squares_per_side + 1 + row left_last_square = left_first_square + ((self.size - 1) * self.size) down_first_square = (self.squares_per_side * 6) - self.size + 1 - (row * self.size) down_last_square = down_first_square + self.size - 1 right_first_square = (self.squares_per_side * 3) + self.size - row right_last_square = right_first_square + ((self.size - 1) * self.size) #log.info("top first %d, last %d" % (top_first_square, top_last_square)) #log.info("left first %d, last %d" % (left_first_square, left_last_square)) #log.info("down first %d, last %d" % (down_first_square, down_last_square)) #log.info("right first %d, last %d" % (right_first_square, right_last_square)) top_squares = [] for square_index in range(top_first_square, top_last_square + 1): top_squares.append(square_index) left_squares = [] for square_index in range(left_first_square, left_last_square + 1, self.size): left_squares.append(square_index) down_squares = [] for square_index in range(down_first_square, down_last_square + 1): down_squares.append(square_index) right_squares = [] for square_index in range(right_first_square, right_last_square + 1, self.size): right_squares.append(square_index) if reverse: left_squares = list(reversed(left_squares)) for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)): old_index = square_index new_index = left_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)): old_index = square_index new_index = down_squares[index] results.append((old_index,new_index)) right_squares = list(reversed(right_squares)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)): old_index = square_index new_index = right_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)): old_index = square_index new_index = top_squares[index] results.append((old_index,new_index)) else: for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)): old_index = square_index new_index = right_squares[index] results.append((old_index,new_index)) top_squares = list(reversed(top_squares)) for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)): old_index = square_index new_index = top_squares[index] results.append((old_index,new_index)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)): old_index = square_index new_index = left_squares[index] results.append((old_index,new_index)) down_squares = list(reversed(down_squares)) for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)): old_index = square_index new_index = down_squares[index] results.append((old_index,new_index)) elif side_name == "D": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): left_first_square = (self.squares_per_side * 2) - self.size + 1 - (row * self.size) left_last_square = left_first_square + self.size - 1 front_first_square = (self.squares_per_side * 3) - self.size + 1 - (row * self.size) front_last_square = front_first_square + self.size - 1 right_first_square = (self.squares_per_side * 4) - self.size + 1 - (row * self.size) right_last_square = right_first_square + self.size - 1 back_first_square = (self.squares_per_side * 5) - self.size + 1 - (row * self.size) back_last_square = back_first_square + self.size - 1 #log.info("left first %d, last %d" % (left_first_square, left_last_square)) #log.info("front first %d, last %d" % (front_first_square, front_last_square)) #log.info("right first %d, last %d" % (right_first_square, right_last_square)) #log.info("back first %d, last %d" % (back_first_square, back_last_square)) if reverse: for square_index in range(left_first_square, left_last_square + 1): old_index = square_index new_index = square_index + self.squares_per_side results.append((old_index,new_index)) for square_index in range(front_first_square, front_last_square + 1): old_index = square_index new_index = square_index + self.squares_per_side results.append((old_index,new_index)) for square_index in range(right_first_square, right_last_square + 1): old_index = square_index new_index = square_index + self.squares_per_side results.append((old_index,new_index)) for square_index in range(back_first_square, back_last_square + 1): old_index = square_index new_index = square_index - (3 * self.squares_per_side) results.append((old_index,new_index)) else: for square_index in range(left_first_square, left_last_square + 1): old_index = square_index new_index = square_index + (3 * self.squares_per_side) results.append((old_index,new_index)) for square_index in range(front_first_square, front_last_square + 1): old_index = square_index new_index = square_index - self.squares_per_side results.append((old_index,new_index)) for square_index in range(right_first_square, right_last_square + 1): old_index = square_index new_index = square_index - self.squares_per_side results.append((old_index,new_index)) for square_index in range(back_first_square, back_last_square + 1): old_index = square_index new_index = square_index - self.squares_per_side results.append((old_index,new_index)) return results def rotate(self, action): """ self.state is a dictionary where the key is the square_index and the value is that square side name (U, F, etc) """ self.solution.append(action) result = self.state[:] # log.info("move %s" % action) if action[-1] in ("'", "`"): reverse = True action = action[0:-1] else: reverse = False # 2Uw, Uw and 2U all mean rotate the top 2 U rows # 3Uw and 3U mean rotate the top 3 U rows if len(action) >= 2 and action[0].isdigit() and action[1].isdigit(): rows_to_rotate = int(action[0:2]) action = action[2:] elif action[0].isdigit(): rows_to_rotate = int(action[0]) action = action[1:] else: # Uw also means rotate the top 2 U rows if 'w' in action: rows_to_rotate = 2 else: rows_to_rotate = 1 # We've accounted for this so remove it if 'w' in action: action = action.replace('w', '') # The digit at the end indicates how many quarter turns to do if action[-1].isdigit(): quarter_turns = int(action[-1]) action = action[0:-1] else: quarter_turns = 1 side_name = action if side_name == 'x': side_name = 'R' rows_to_rotate = self.size elif side_name == 'y': side_name = 'U' rows_to_rotate = self.size elif side_name == 'z': side_name = 'F' rows_to_rotate = self.size if side_name in ('CENTERS_SOLVED', 'EDGES_GROUPED'): return side = self.sides[side_name] min_pos = side.min_pos max_pos = side.max_pos # rotate the face...this is the same for all sides for turn in range(quarter_turns): face = side.get_face_as_2d_list() if reverse: face = rotate_counter_clockwise(face) else: face = rotate_clockwise(face) face = compress_2d_list(face) for (index, value) in enumerate(face): square_index = min_pos + index result[square_index] = value self.state = result[:] # If we are rotating the entire self.state we must rotate the opposite face as well if rows_to_rotate == self.size: if side_name == 'U': opp_side_name = 'D' elif side_name == 'D': opp_side_name = 'U' elif side_name == 'L': opp_side_name = 'R' elif side_name == 'R': opp_side_name = 'L' elif side_name == 'B': opp_side_name = 'F' elif side_name == 'F': opp_side_name = 'B' else: raise SolveError("") opp_side = self.sides[opp_side_name] opp_min_pos = opp_side.min_pos face = opp_side.get_face_as_2d_list() # This is reversed from what we did with the original layer if reverse: face = rotate_clockwise(face) else: face = rotate_counter_clockwise(face) face = compress_2d_list(face) for (index, value) in enumerate(face): square_index = opp_min_pos + index result[square_index] = value self.state = result[:] if side_name == "U": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): left_first_square = self.squares_per_side + 1 + (row * self.size) left_last_square = left_first_square + self.size - 1 front_first_square = (self.squares_per_side * 2) + 1 + (row * self.size) front_last_square = front_first_square + self.size - 1 right_first_square = (self.squares_per_side * 3) + 1 + (row * self.size) right_last_square = right_first_square + self.size - 1 back_first_square = (self.squares_per_side * 4) + 1 + (row * self.size) back_last_square = back_first_square + self.size - 1 #log.info("left first %d, last %d" % (left_first_square, left_last_square)) #log.info("front first %d, last %d" % (front_first_square, front_last_square)) #log.info("right first %d, last %d" % (right_first_square, right_last_square)) #log.info("back first %d, last %d" % (back_first_square, back_last_square)) if reverse: for square_index in range(left_first_square, left_last_square + 1): result[square_index] = self.state[square_index + (3 * self.squares_per_side)] for square_index in range(front_first_square, front_last_square + 1): result[square_index] = self.state[square_index - self.squares_per_side] for square_index in range(right_first_square, right_last_square + 1): result[square_index] = self.state[square_index - self.squares_per_side] for square_index in range(back_first_square, back_last_square + 1): result[square_index] = self.state[square_index - self.squares_per_side] else: for square_index in range(left_first_square, left_last_square + 1): result[square_index] = self.state[square_index + self.squares_per_side] for square_index in range(front_first_square, front_last_square + 1): result[square_index] = self.state[square_index + self.squares_per_side] for square_index in range(right_first_square, right_last_square + 1): result[square_index] = self.state[square_index + self.squares_per_side] for square_index in range(back_first_square, back_last_square + 1): result[square_index] = self.state[square_index - (3 * self.squares_per_side)] self.state = result[:] elif side_name == "L": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): top_first_square = 1 + row top_last_square = top_first_square + ((self.size - 1) * self.size) front_first_square = (self.squares_per_side * 2) + 1 + row front_last_square = front_first_square + ((self.size - 1) * self.size) down_first_square = (self.squares_per_side * 5) + 1 + row down_last_square = down_first_square + ((self.size - 1) * self.size) back_first_square = (self.squares_per_side * 4) + self.size - row back_last_square = back_first_square + ((self.size - 1) * self.size) #log.info("top first %d, last %d" % (top_first_square, top_last_square)) #log.info("front first %d, last %d" % (front_first_square, front_last_square)) #log.info("down first %d, last %d" % (down_first_square, down_last_square)) #log.info("back first %d, last %d" % (back_first_square, back_last_square)) top_squares = [] for square_index in range(top_first_square, top_last_square + 1, self.size): top_squares.append(self.state[square_index]) front_squares = [] for square_index in range(front_first_square, front_last_square + 1, self.size): front_squares.append(self.state[square_index]) down_squares = [] for square_index in range(down_first_square, down_last_square + 1, self.size): down_squares.append(self.state[square_index]) back_squares = [] for square_index in range(back_first_square, back_last_square + 1, self.size): back_squares.append(self.state[square_index]) if reverse: for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)): result[square_index] = front_squares[index] for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)): result[square_index] = down_squares[index] back_squares = list(reversed(back_squares)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)): result[square_index] = back_squares[index] top_squares = list(reversed(top_squares)) for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)): result[square_index] = top_squares[index] else: back_squares = list(reversed(back_squares)) for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)): result[square_index] = back_squares[index] for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)): result[square_index] = top_squares[index] for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)): result[square_index] = front_squares[index] down_squares = list(reversed(down_squares)) for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)): result[square_index] = down_squares[index] self.state = result[:] elif side_name == "F": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): top_first_square = (self.squares_per_side - self.size) + 1 - (row * self.size) top_last_square = top_first_square + self.size - 1 left_first_square = self.squares_per_side + self.size - row left_last_square = left_first_square + ((self.size - 1) * self.size) down_first_square = (self.squares_per_side * 5) + 1 + (row * self.size) down_last_square = down_first_square + self.size - 1 right_first_square = (self.squares_per_side * 3) + 1 + row right_last_square = right_first_square + ((self.size - 1) * self.size) #log.info("top first %d, last %d" % (top_first_square, top_last_square)) #log.info("left first %d, last %d" % (left_first_square, left_last_square)) #log.info("down first %d, last %d" % (down_first_square, down_last_square)) #log.info("right first %d, last %d" % (right_first_square, right_last_square)) top_squares = [] for square_index in range(top_first_square, top_last_square + 1): top_squares.append(self.state[square_index]) left_squares = [] for square_index in range(left_first_square, left_last_square + 1, self.size): left_squares.append(self.state[square_index]) down_squares = [] for square_index in range(down_first_square, down_last_square + 1): down_squares.append(self.state[square_index]) right_squares = [] for square_index in range(right_first_square, right_last_square + 1, self.size): right_squares.append(self.state[square_index]) if reverse: for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)): result[square_index] = right_squares[index] top_squares = list(reversed(top_squares)) for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)): result[square_index] = top_squares[index] for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)): result[square_index] = left_squares[index] down_squares = list(reversed(down_squares)) for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)): result[square_index] = down_squares[index] else: left_squares = list(reversed(left_squares)) for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)): result[square_index] = left_squares[index] for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)): result[square_index] = down_squares[index] right_squares = list(reversed(right_squares)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)): result[square_index] = right_squares[index] for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)): result[square_index] = top_squares[index] self.state = result[:] elif side_name == "R": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): top_first_square = self.size - row top_last_square = self.squares_per_side front_first_square = (self.squares_per_side * 2) + self.size - row front_last_square = front_first_square + ((self.size - 1) * self.size) down_first_square = (self.squares_per_side * 5) + self.size - row down_last_square = down_first_square + ((self.size - 1) * self.size) back_first_square = (self.squares_per_side * 4) + 1 + row back_last_square = back_first_square + ((self.size - 1) * self.size) #log.info("top first %d, last %d" % (top_first_square, top_last_square)) #log.info("front first %d, last %d" % (front_first_square, front_last_square)) #log.info("down first %d, last %d" % (down_first_square, down_last_square)) #log.info("back first %d, last %d" % (back_first_square, back_last_square)) top_squares = [] for square_index in range(top_first_square, top_last_square + 1, self.size): top_squares.append(self.state[square_index]) front_squares = [] for square_index in range(front_first_square, front_last_square + 1, self.size): front_squares.append(self.state[square_index]) down_squares = [] for square_index in range(down_first_square, down_last_square + 1, self.size): down_squares.append(self.state[square_index]) back_squares = [] for square_index in range(back_first_square, back_last_square + 1, self.size): back_squares.append(self.state[square_index]) if reverse: back_squares = list(reversed(back_squares)) for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)): result[square_index] = back_squares[index] for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)): result[square_index] = top_squares[index] for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)): result[square_index] = front_squares[index] down_squares = list(reversed(down_squares)) for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)): result[square_index] = down_squares[index] else: for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1, self.size)): result[square_index] = front_squares[index] for (index, square_index) in enumerate(range(front_first_square, front_last_square + 1, self.size)): result[square_index] = down_squares[index] back_squares = list(reversed(back_squares)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1, self.size)): result[square_index] = back_squares[index] top_squares = list(reversed(top_squares)) for (index, square_index) in enumerate(range(back_first_square, back_last_square + 1, self.size)): result[square_index] = top_squares[index] self.state = result[:] elif side_name == "B": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): top_first_square = 1 + (row * self.size) top_last_square = top_first_square + self.size - 1 left_first_square = self.squares_per_side + 1 + row left_last_square = left_first_square + ((self.size - 1) * self.size) down_first_square = (self.squares_per_side * 6) - self.size + 1 - (row * self.size) down_last_square = down_first_square + self.size - 1 right_first_square = (self.squares_per_side * 3) + self.size - row right_last_square = right_first_square + ((self.size - 1) * self.size) #log.info("top first %d, last %d" % (top_first_square, top_last_square)) #log.info("left first %d, last %d" % (left_first_square, left_last_square)) #log.info("down first %d, last %d" % (down_first_square, down_last_square)) #log.info("right first %d, last %d" % (right_first_square, right_last_square)) top_squares = [] for square_index in range(top_first_square, top_last_square + 1): top_squares.append(self.state[square_index]) left_squares = [] for square_index in range(left_first_square, left_last_square + 1, self.size): left_squares.append(self.state[square_index]) down_squares = [] for square_index in range(down_first_square, down_last_square + 1): down_squares.append(self.state[square_index]) right_squares = [] for square_index in range(right_first_square, right_last_square + 1, self.size): right_squares.append(self.state[square_index]) if reverse: left_squares = list(reversed(left_squares)) for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)): result[square_index] = left_squares[index] for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)): result[square_index] = down_squares[index] right_squares = list(reversed(right_squares)) for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)): result[square_index] = right_squares[index] for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)): result[square_index] = top_squares[index] else: for (index, square_index) in enumerate(range(top_first_square, top_last_square + 1)): result[square_index] = right_squares[index] top_squares = list(reversed(top_squares)) for (index, square_index) in enumerate(range(left_first_square, left_last_square + 1, self.size)): result[square_index] = top_squares[index] for (index, square_index) in enumerate(range(down_first_square, down_last_square + 1)): result[square_index] = left_squares[index] down_squares = list(reversed(down_squares)) for (index, square_index) in enumerate(range(right_first_square, right_last_square + 1, self.size)): result[square_index] = down_squares[index] self.state = result[:] elif side_name == "D": for turn in range(quarter_turns): # rotate the connecting row(s) of the surrounding sides for row in range(rows_to_rotate): left_first_square = (self.squares_per_side * 2) - self.size + 1 - (row * self.size) left_last_square = left_first_square + self.size - 1 front_first_square = (self.squares_per_side * 3) - self.size + 1 - (row * self.size) front_last_square = front_first_square + self.size - 1 right_first_square = (self.squares_per_side * 4) - self.size + 1 - (row * self.size) right_last_square = right_first_square + self.size - 1 back_first_square = (self.squares_per_side * 5) - self.size + 1 - (row * self.size) back_last_square = back_first_square + self.size - 1 #log.info("left first %d, last %d" % (left_first_square, left_last_square)) #log.info("front first %d, last %d" % (front_first_square, front_last_square)) #log.info("right first %d, last %d" % (right_first_square, right_last_square)) #log.info("back first %d, last %d" % (back_first_square, back_last_square)) if reverse: for square_index in range(left_first_square, left_last_square + 1): result[square_index] = self.state[square_index + self.squares_per_side] for square_index in range(front_first_square, front_last_square + 1): result[square_index] = self.state[square_index + self.squares_per_side] for square_index in range(right_first_square, right_last_square + 1): result[square_index] = self.state[square_index + self.squares_per_side] for square_index in range(back_first_square, back_last_square + 1): result[square_index] = self.state[square_index - (3 * self.squares_per_side)] else: for square_index in range(left_first_square, left_last_square + 1): result[square_index] = self.state[square_index + (3 * self.squares_per_side)] for square_index in range(front_first_square, front_last_square + 1): result[square_index] = self.state[square_index - self.squares_per_side] for square_index in range(right_first_square, right_last_square + 1): result[square_index] = self.state[square_index - self.squares_per_side] for square_index in range(back_first_square, back_last_square + 1): result[square_index] = self.state[square_index - self.squares_per_side] self.state = result[:] else: raise Exception("Unsupported action %s" % action) def print_cube_layout(self): print((get_cube_layout(self.size) + '\n')) def print_cube(self, print_positions=False): side_names = ('U', 'L', 'F', 'R', 'B', 'D') side_name_index = 0 rows = [] row_index = 0 for x in range(self.size * 3): rows.append([]) all_digits = True for (square_index, square_state) in enumerate(self.state): if not square_state.isdigit(): all_digits = False break for (square_index, square_state) in enumerate(self.state): # ignore the placeholder (x) if square_index == 0: continue side_name = side_names[side_name_index] color = self.color_map.get(square_state, None) if color: # end of the row if square_index % self.size == 0: rows[row_index].append("\033[%dm%s\033[0m%s " % (color, square_state, " (%4d) " % square_index if print_positions else "")) row_index += 1 else: rows[row_index].append("\033[%dm%s\033[0m%s" % (color, square_state, " (%4d) " % square_index if print_positions else "")) else: # end of the row if square_index % self.size == 0: if square_state.endswith('x'): rows[row_index].append("%s " % square_state) else: if all_digits: rows[row_index].append("%02d" % int(square_state)) else: rows[row_index].append("%s" % square_state) row_index += 1 else: if square_state.endswith('x'): rows[row_index].append("%s" % square_state) else: if all_digits: rows[row_index].append("%02d" % int(square_state)) else: rows[row_index].append("%s" % square_state) # end of the side if square_index % self.squares_per_side == 0: if side_name in ('L', 'F', 'R'): row_index = self.size side_name_index += 1 for (row_index, row) in enumerate(rows): if row_index < self.size or row_index >= (self.size * 2): if all_digits: sys.stdout.write(' ' * (self.size * 3)) else: sys.stdout.write(' ' * (self.size + self.size + 1)) print((' '.join(row))) if ((row_index+1) % self.size) == 0: print('') print('') def print_case_statement_C(self, case, first_step): """ This is called via --rotate-printer, it is used to print the case statements used by lookup-table-builder.c """ if first_step: print((" if (strcmp(step, \"%s\") == 0) {" % case)) else: print((" } else if (strcmp(step, \"%s\") == 0) {" % case)) for (key, value) in enumerate(self.state[1:]): key += 1 if str(key) != str(value): print((" cube[%s] = cube_tmp[%s];" % (key, value))) print("") def print_case_statement_python(self, case): """ This is called via utils/rotate-printer.py, it is used to print the contents of rotate_xxx.py """ numbers = [] numbers.append(0) for (key, value) in enumerate(self.state[1:]): numbers.append(int(value)) ''' If you feed number_ranges() [0, 1, 2, 3, 4, 7, 8, 9, 11] It will return: [(0, 4), (7, 9), (11, 11)] ''' lists = [] indexes_outside_streak = [] for (start_index, last_index) in number_ranges(numbers): if start_index == last_index: indexes_outside_streak.append("cube[%d]" % start_index) else: if indexes_outside_streak: # cube[11], cube[13] lists.append("[%s]" % ','.join(indexes_outside_streak)) indexes_outside_streak = [] lists.append("cube[%d:%d]" % (start_index, last_index+1)) if indexes_outside_streak: lists.append("[%s]" % ','.join(indexes_outside_streak)) indexes_outside_streak = [] print((" return %s" % ' + '.join(lists))) def randomize(self): """ Perform a bunch of random moves to scramble a cube. This was used to generate test cases. """ if self.is_even(): max_rows = int(self.size/2) else: max_rows = int((self.size - 1)/2) sides = ['U', 'L', 'F', 'R', 'B', 'D'] count = ((self.size * self.size) * 6) * 3 # uncomment to limit randomness of the scramble # count = 12 for x in range(count): rows = random.randint(1, max_rows) side_index = random.randint(0, 5) side = sides[side_index] quarter_turns = random.randint(1, 2) clockwise = random.randint(0, 1) if rows > 1: move = "%d%s" % (rows, side) else: move = side if quarter_turns > 1: move += str(quarter_turns) if not clockwise: move += "'" self.rotate(move) def get_side_for_index(self, square_index): """ Return the Side object that owns square_index """ for side in list(self.sides.values()): if square_index >= side.min_pos and square_index <= side.max_pos: return side raise SolveError("We should not be here, square_index %s" % pformat(square_index)) def get_edge_colors(self, square_index): side = self.get_side_for_index(square_index) edge_indexes = None if square_index in side.edge_north_pos: edge_indexes = side.edge_north_pos elif square_index in side.edge_west_pos: edge_indexes = side.edge_west_pos elif square_index in side.edge_south_pos: edge_indexes = side.edge_south_pos elif square_index in side.edge_east_pos: edge_indexes = side.edge_east_pos colors = [] for edge_index in edge_indexes: partner_index = side.get_wing_partner(edge_index) colors.append(tuple(sorted((self.state[edge_index], self.state[partner_index])))) colors = sorted(list(set(colors))) #log.info("%s colors %s" % (square_index, pformat(colors))) return colors def get_non_paired_wings(self): return (self.sideU.non_paired_wings(True, True, True, True) + self.sideF.non_paired_wings(False, True, False, True) + self.sideB.non_paired_wings(False, True, False, True) + self.sideD.non_paired_wings(True, True, True, True)) def get_non_paired_wings_count(self): return len(self.get_non_paired_wings()) def get_non_paired_edges(self): # north, west, south, east return (self.sideU.non_paired_edges(True, True, True, True) + self.sideF.non_paired_edges(False, True, False, True) + self.sideB.non_paired_edges(False, True, False, True) + self.sideD.non_paired_edges(True, True, True, True)) def get_non_paired_edges_count(self): non_paired_edges = self.get_non_paired_edges() result = len(non_paired_edges) if result > 12: raise SolveError("Found %d unpaired edges but a cube only has 12 edges" % result) return result def edges_paired(self): if self.get_non_paired_edges_count() == 0: return True return False def find_edge(self, color1, color2): positions = [] for (pos1, pos2) in self.all_edge_positions: if ((self.state[pos1] == color1 and self.state[pos2] == color2) or (self.state[pos1] == color2 and self.state[pos2] == color1)): positions.append((pos1, pos2)) return positions def get_wings(self, pos1, remove_if_in_same_edge=False): pos1_side = self.get_side_for_index(pos1) pos2 = pos1_side.get_wing_partner(pos1) pos2_side = self.get_side_for_index(pos2) color1 = self.state[pos1] color2 = self.state[pos2] wings = self.find_edge(color1, color2) wings_to_remove = [] #log.info("get_wings (%d, %d), pos1_side %s, remove_if_in_same_edge %s, %s" % # (pos1, pos2, pos1_side, remove_if_in_same_edge, pformat(wings))) for (wing_pos1, wing_pos2) in wings: # Remove the one we started with if (wing_pos1, wing_pos2) == (pos1, pos2): wings_to_remove.append((wing_pos1, wing_pos2)) elif (wing_pos1, wing_pos2) == (pos2, pos1): wings_to_remove.append((wing_pos1, wing_pos2)) # Some callers do not want wings that are part of the same edge as pos1 elif remove_if_in_same_edge: wing_pos1_side = self.get_side_for_index(wing_pos1) wing_pos2_side = self.get_side_for_index(wing_pos2) #log.info("wing_pos1 %s, wing_pos1_side %s, wing_pos2 %s, wing_pos2_side %s" % # (wing_pos1, wing_pos1_side, wing_pos2, wing_pos2_side)) if ((wing_pos1_side == pos1_side and wing_pos2_side == pos2_side) or (wing_pos2_side == pos1_side and wing_pos1_side == pos2_side)): wings_to_remove.append((wing_pos1, wing_pos2)) #log.info("get_wings wings_to_remove %s" % pformat(wings_to_remove)) for x in wings_to_remove: wings.remove(x) #log.info("get_wings returning %s\n" % pformat(wings)) return wings def get_wing_in_middle_of_edge(self, pos1, remove_if_in_same_edge=False): wings = self.get_wings(pos1, remove_if_in_same_edge) for wing in wings: wing_side = self.get_side_for_index(wing[0]) if wing_side.wing_is_middle_of_edge(wing[0]): return wing return None def get_wings_on_edge(self, pos1, side1_name, side2_name): wings = self.get_wings(pos1) wings_to_keep = [] #log.info("get_wings_on_edge for pos1 %d, side1 %s, side2 %s, init_wings %s" % (pos1, side1_name, side2_name, pformat(wings))) for (wing_pos1, wing_pos2) in wings: wing_pos1_side = self.get_side_for_index(wing_pos1) wing_pos2_side = self.get_side_for_index(wing_pos2) #log.info("get_wings_on_edge wing_pos1 %d side %s, wing_pos2 %d side %s\n" % # (wing_pos1, wing_pos1_side, wing_pos2, wing_pos2_side)) if ((wing_pos1_side.name == side1_name and wing_pos2_side.name == side2_name) or (wing_pos2_side.name == side1_name and wing_pos1_side.name == side2_name)): wings_to_keep.append((wing_pos1, wing_pos2)) #log.info("get_wings_on_edge keeping %s\n" % pformat(wings_to_keep)) return wings_to_keep def rotate_edge_to_F_west(self, edge): side = self.get_side_for_index(edge[0]) direction = side.has_wing(edge) if side == self.sideU: if direction == 'north': self.rotate_y_reverse() self.rotate_x_reverse() elif direction == 'west': self.rotate_x_reverse() elif direction == 'south': self.rotate_x_reverse() self.rotate_z() elif direction == 'east': self.rotate_y() self.rotate_y() self.rotate_x_reverse() elif side == self.sideL: if direction == 'north': self.rotate_x_reverse() elif direction == 'west': self.rotate_x_reverse() self.rotate_x_reverse() elif direction == 'south': self.rotate_x() elif direction == 'east': pass elif side == self.sideF: if direction == 'north': self.rotate_z_reverse() elif direction == 'west': pass elif direction == 'south': self.rotate_z() elif direction == 'east': self.rotate_z_reverse() self.rotate_z_reverse() elif side == self.sideR: if direction == 'north': self.rotate_y() self.rotate_y() self.rotate_x_reverse() elif direction == 'west': self.rotate_y() elif direction == 'south': self.rotate_x() self.rotate_y() elif direction == 'east': self.rotate_y() self.rotate_y() elif side == self.sideB: if direction == 'north': self.rotate_y_reverse() self.rotate_x_reverse() elif direction == 'west': self.rotate_y() self.rotate_y() elif direction == 'south': self.rotate_z() self.rotate_x() self.rotate_x() elif direction == 'east': self.rotate_y_reverse() elif side == self.sideD: if direction == 'north': self.rotate_z() elif direction == 'west': self.rotate_x() elif direction == 'south': self.rotate_y_reverse() self.rotate_x() elif direction == 'east': self.rotate_y() self.rotate_y() self.rotate_x() def move_wing_to_U_north(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: pass elif wing_pos1 in self.sideU.edge_south_pos: for step in ("U2", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_east_pos: for step in ("U'", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_west_pos: for step in ("U", ): self.rotate(step) # Left elif wing_pos1 in self.sideL.edge_north_pos: for step in ("U", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_south_pos: for step in ("L", "B'"): self.rotate(step) elif wing_pos1 in self.sideL.edge_east_pos: for step in ("L2", "B'"): self.rotate(step) elif wing_pos1 in self.sideL.edge_west_pos: for step in ("B'", ): self.rotate(step) # Front elif wing_pos1 in self.sideF.edge_north_pos: for step in ("U2", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_south_pos: for step in ("F2", "U2"): self.rotate(step) elif wing_pos1 in self.sideF.edge_east_pos: for step in ("R", "U'"): self.rotate(step) elif wing_pos1 in self.sideF.edge_west_pos: for step in ("F", "U2"): self.rotate(step) # Right elif wing_pos1 in self.sideR.edge_north_pos: for step in ("U'", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_south_pos: for step in ("R2", "U'"): self.rotate(step) elif wing_pos1 in self.sideR.edge_east_pos: for step in ("B", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_west_pos: for step in ("R", "U'"): self.rotate(step) # Back elif wing_pos1 in self.sideB.edge_north_pos: pass elif wing_pos1 in self.sideB.edge_south_pos: for step in ("B2", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_east_pos: for step in ("B'", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_west_pos: for step in ("B", ): self.rotate(step) # Down elif wing_pos1 in self.sideD.edge_north_pos: for step in ("F2", "U2"): self.rotate(step) elif wing_pos1 in self.sideD.edge_south_pos: for step in ("B2", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_east_pos: for step in ("R2", "U'"): self.rotate(step) elif wing_pos1 in self.sideD.edge_west_pos: for step in ("L2", "U"): self.rotate(step) else: raise ImplementThis("implement wing %s to U north" % str(wing)) def move_wing_to_U_west(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: for step in ("U'", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_south_pos: for step in ("U", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_east_pos: for step in ("U2", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_west_pos: pass # Left elif wing_pos1 in self.sideL.edge_north_pos: pass elif wing_pos1 in self.sideL.edge_south_pos: for step in ("L2", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_east_pos: for step in ("L'", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_west_pos: for step in ("L", ): self.rotate(step) # Front elif wing_pos1 in self.sideF.edge_north_pos: for step in ("U", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_south_pos: for step in ("F2", "U"): self.rotate(step) elif wing_pos1 in self.sideF.edge_east_pos: for step in ("F'", "U"): self.rotate(step) elif wing_pos1 in self.sideF.edge_west_pos: for step in ("L'", ): self.rotate(step) # Right elif wing_pos1 in self.sideR.edge_north_pos: for step in ("U2", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_south_pos: for step in ("R2", "U2"): self.rotate(step) elif wing_pos1 in self.sideR.edge_east_pos: for step in ("B", "U'"): self.rotate(step) elif wing_pos1 in self.sideR.edge_west_pos: for step in ("R", "U2"): self.rotate(step) # Back elif wing_pos1 in self.sideB.edge_north_pos: for step in ("U'", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_south_pos: for step in ("B2", "U'"): self.rotate(step) elif wing_pos1 in self.sideB.edge_east_pos: for step in ("L", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_west_pos: for step in ("B", "U'"): self.rotate(step) # Down elif wing_pos1 in self.sideD.edge_north_pos: for step in ("D'", "L2"): self.rotate(step) elif wing_pos1 in self.sideD.edge_south_pos: for step in ("D", "L2"): self.rotate(step) elif wing_pos1 in self.sideD.edge_east_pos: for step in ("D2", "L2"): self.rotate(step) elif wing_pos1 in self.sideD.edge_west_pos: for step in ("L2", ): self.rotate(step) else: raise ImplementThis("implement wing %s to U west" % str(wing)) def move_wing_to_U_south(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: for step in ("U2", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_south_pos: pass elif wing_pos1 in self.sideU.edge_east_pos: for step in ("U", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_west_pos: for step in ("U'", ): self.rotate(step) # Left elif wing_pos1 in self.sideL.edge_north_pos: for step in ("U'", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_south_pos: for step in ("L2", "U'"): self.rotate(step) elif wing_pos1 in self.sideL.edge_east_pos: for step in ("F", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_west_pos: for step in ("L", "U'"): self.rotate(step) # Front elif wing_pos1 in self.sideF.edge_north_pos: pass elif wing_pos1 in self.sideF.edge_south_pos: for step in ("F2", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_east_pos: for step in ("F'", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_west_pos: for step in ("F", ): self.rotate(step) # Right elif wing_pos1 in self.sideR.edge_north_pos: for step in ("U", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_south_pos: for step in ("R2", "U"): self.rotate(step) elif wing_pos1 in self.sideR.edge_east_pos: for step in ("R'", "U"): self.rotate(step) elif wing_pos1 in self.sideR.edge_west_pos: for step in ("R", "U"): self.rotate(step) # Back elif wing_pos1 in self.sideB.edge_north_pos: for step in ("U2", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_south_pos: for step in ("B2", "U2"): self.rotate(step) elif wing_pos1 in self.sideB.edge_east_pos: for step in ("B'", "U2"): self.rotate(step) elif wing_pos1 in self.sideB.edge_west_pos: for step in ("B", "U2"): self.rotate(step) # Down elif wing_pos1 in self.sideD.edge_north_pos: for step in ("F2", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_south_pos: for step in ("D2", "F2"): self.rotate(step) elif wing_pos1 in self.sideD.edge_east_pos: for step in ("D'", "F2"): self.rotate(step) elif wing_pos1 in self.sideD.edge_west_pos: for step in ("D", "F2"): self.rotate(step) else: raise ImplementThis("implement wing %s to U south" % str(wing)) def move_wing_to_U_east(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: for step in ("U", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_south_pos: for step in ("U'", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_east_pos: pass elif wing_pos1 in self.sideU.edge_west_pos: for step in ("U2", ): self.rotate(step) # Left elif wing_pos1 in self.sideL.edge_north_pos: for step in ("U2", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_south_pos: for step in ("L2", "U2"): self.rotate(step) elif wing_pos1 in self.sideL.edge_east_pos: for step in ("F", "U'"): self.rotate(step) elif wing_pos1 in self.sideL.edge_west_pos: for step in ("L", "U2"): self.rotate(step) # Front elif wing_pos1 in self.sideF.edge_north_pos: for step in ("U'", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_south_pos: for step in ("F'", "R"): self.rotate(step) elif wing_pos1 in self.sideF.edge_east_pos: for step in ("R", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_west_pos: for step in ("F", "U'"): self.rotate(step) # Right elif wing_pos1 in self.sideR.edge_north_pos: pass elif wing_pos1 in self.sideR.edge_south_pos: for step in ("R2", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_east_pos: for step in ("R'", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_west_pos: for step in ("R", ): self.rotate(step) # Back elif wing_pos1 in self.sideB.edge_north_pos: for step in ("U", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_south_pos: for step in ("B", "R'"): self.rotate(step) elif wing_pos1 in self.sideB.edge_east_pos: for step in ("B'", "U"): self.rotate(step) elif wing_pos1 in self.sideB.edge_west_pos: for step in ("R'", ): self.rotate(step) # Down elif wing_pos1 in self.sideD.edge_north_pos: for step in ("D", "R2"): self.rotate(step) elif wing_pos1 in self.sideD.edge_south_pos: for step in ("D'", "R2"): self.rotate(step) elif wing_pos1 in self.sideD.edge_east_pos: for step in ("R2", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_west_pos: for step in ("D2", "R2"): self.rotate(step) else: raise ImplementThis("implement wing %s to U east" % str(wing)) def move_wing_to_L_west(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: for step in ("B", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_south_pos: for step in ("U2", "B"): self.rotate(step) elif wing_pos1 in self.sideU.edge_east_pos: for step in ("U'", "B"): self.rotate(step) elif wing_pos1 in self.sideU.edge_west_pos: for step in ("U", "B"): self.rotate(step) # Left elif wing_pos1 in self.sideL.edge_north_pos: for step in ("L'", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_south_pos: for step in ("L", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_east_pos: for step in ("L2", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_west_pos: pass # Front elif wing_pos1 in self.sideF.edge_north_pos: for step in ("F'", "L2"): self.rotate(step) elif wing_pos1 in self.sideF.edge_south_pos: for step in ("F", "L2"): self.rotate(step) elif wing_pos1 in self.sideF.edge_east_pos: for step in ("F2", "L2"): self.rotate(step) elif wing_pos1 in self.sideF.edge_west_pos: for step in ("L2", ): self.rotate(step) # Right elif wing_pos1 in self.sideR.edge_north_pos: for step in ("R", "B2"): self.rotate(step) elif wing_pos1 in self.sideR.edge_south_pos: for step in ("R'", "B2"): self.rotate(step) elif wing_pos1 in self.sideR.edge_east_pos: for step in ("B2", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_west_pos: for step in ("R2", "B2"): self.rotate(step) # Back elif wing_pos1 in self.sideB.edge_north_pos: for step in ("B", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_south_pos: for step in ("B'", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_east_pos: pass elif wing_pos1 in self.sideB.edge_west_pos: for step in ("B2", ): self.rotate(step) # Down elif wing_pos1 in self.sideD.edge_north_pos: for step in ("D'", "L"): self.rotate(step) elif wing_pos1 in self.sideD.edge_south_pos: for step in ("D", "L"): self.rotate(step) elif wing_pos1 in self.sideD.edge_east_pos: for step in ("D2", "L"): self.rotate(step) elif wing_pos1 in self.sideD.edge_west_pos: for step in ("L", ): self.rotate(step) else: raise ImplementThis("implement wing %s to L west" % str(wing)) def move_wing_to_L_east(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: for step in ("U'", "L"): self.rotate(step) elif wing_pos1 in self.sideU.edge_south_pos: self.rotate("F'") elif wing_pos1 in self.sideU.edge_east_pos: for step in ("U2", "L"): self.rotate(step) elif wing_pos1 in self.sideU.edge_west_pos: self.rotate("L") # Left elif wing_pos1 in self.sideL.edge_north_pos: self.rotate("L") elif wing_pos1 in self.sideL.edge_south_pos: self.rotate("L'") elif wing_pos1 in self.sideL.edge_east_pos: pass elif wing_pos1 in self.sideL.edge_west_pos: self.rotate("L2") # Front elif wing_pos1 in self.sideF.edge_north_pos: self.rotate("F'") elif wing_pos1 in self.sideF.edge_south_pos: self.rotate("F") elif wing_pos1 in self.sideF.edge_east_pos: self.rotate("F2") elif wing_pos1 in self.sideF.edge_west_pos: pass # Right elif wing_pos1 in self.sideR.edge_north_pos: for step in ("U2", "L"): self.rotate(step) elif wing_pos1 in self.sideR.edge_south_pos: for step in ("D2", "L'"): self.rotate(step) elif wing_pos1 in self.sideR.edge_east_pos: for step in ("B2", "L2"): self.rotate(step) elif wing_pos1 in self.sideR.edge_west_pos: self.rotate("F2") # Back elif wing_pos1 in self.sideB.edge_north_pos: for step in ("U'", "L"): self.rotate(step) elif wing_pos1 in self.sideB.edge_south_pos: for step in ("B'", "L2"): self.rotate(step) elif wing_pos1 in self.sideB.edge_east_pos: self.rotate("L2") elif wing_pos1 in self.sideB.edge_west_pos: for step in ("B2", "L2"): self.rotate(step) # Down elif wing_pos1 in self.sideD.edge_north_pos: self.rotate("F") elif wing_pos1 in self.sideD.edge_south_pos: for step in ("D", "L'"): self.rotate(step) elif wing_pos1 in self.sideD.edge_east_pos: for step in ("D2", "L'"): self.rotate(step) elif wing_pos1 in self.sideD.edge_west_pos: self.rotate("L'") else: raise ImplementThis("implement wing %s to L east" % str(wing)) def move_wing_to_F_west(self, wing): self.move_wing_to_L_east(wing) def move_wing_to_R_west(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: for step in ("U", "R'"): self.rotate(step) elif wing_pos1 in self.sideU.edge_south_pos: for step in ("U'", "R'"): self.rotate(step) elif wing_pos1 in self.sideU.edge_east_pos: for step in ("R'",): self.rotate(step) elif wing_pos1 in self.sideU.edge_west_pos: for step in ("U2", "R'"): self.rotate(step) # Left elif wing_pos1 in self.sideL.edge_north_pos: for step in ("U2", "R'"): self.rotate(step) elif wing_pos1 in self.sideL.edge_south_pos: for step in ("D2", "R"): self.rotate(step) elif wing_pos1 in self.sideL.edge_east_pos: for step in ("F2", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_west_pos: for step in ("B2", "R2"): self.rotate(step) # Front elif wing_pos1 in self.sideF.edge_north_pos: for step in ("F", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_south_pos: for step in ("D", "R"): self.rotate(step) elif wing_pos1 in self.sideF.edge_east_pos: pass elif wing_pos1 in self.sideF.edge_west_pos: for step in ("F2", ): self.rotate(step) # Right elif wing_pos1 in self.sideR.edge_north_pos: for step in ("R'", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_south_pos: for step in ("R", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_east_pos: for step in ("R2",): self.rotate(step) elif wing_pos1 in self.sideR.edge_west_pos: pass # Back elif wing_pos1 in self.sideB.edge_north_pos: for step in ("U", "R'"): self.rotate(step) elif wing_pos1 in self.sideB.edge_south_pos: for step in ("D'", "R"): self.rotate(step) elif wing_pos1 in self.sideB.edge_east_pos: for step in ("B2", "R2"): self.rotate(step) elif wing_pos1 in self.sideB.edge_west_pos: for step in ("R2",): self.rotate(step) # Down elif wing_pos1 in self.sideD.edge_north_pos: for step in ("D", "R"): self.rotate(step) elif wing_pos1 in self.sideD.edge_south_pos: for step in ("D'", "R"): self.rotate(step) elif wing_pos1 in self.sideD.edge_east_pos: for step in ("R", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_west_pos: for step in ("D2", "R"): self.rotate(step) else: raise ImplementThis("implement wing %s to R west" % str(wing)) def move_wing_to_R_east(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: for step in ("B'", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_south_pos: for step in ("U'", "R"): self.rotate(step) elif wing_pos1 in self.sideU.edge_east_pos: for step in ("R", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_west_pos: for step in ("U2", "R"): self.rotate(step) # Left elif wing_pos1 in self.sideL.edge_north_pos: for step in ("L'", "B2"): self.rotate(step) elif wing_pos1 in self.sideL.edge_south_pos: for step in ("L", "B2"): self.rotate(step) elif wing_pos1 in self.sideL.edge_east_pos: for step in ("F2", "R2"): self.rotate(step) elif wing_pos1 in self.sideL.edge_west_pos: for step in ("B2", ): self.rotate(step) # Front elif wing_pos1 in self.sideF.edge_north_pos: for step in ("U'", "R"): self.rotate(step) elif wing_pos1 in self.sideF.edge_south_pos: for step in ("F'", "R2"): self.rotate(step) elif wing_pos1 in self.sideF.edge_east_pos: for step in ("R2", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_west_pos: for step in ("F2", "R2"): self.rotate(step) # Right elif wing_pos1 in self.sideR.edge_north_pos: for step in ("R", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_south_pos: for step in ("R'", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_east_pos: pass elif wing_pos1 in self.sideR.edge_west_pos: for step in ("R2", ): self.rotate(step) # Back elif wing_pos1 in self.sideB.edge_north_pos: for step in ("B'", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_south_pos: for step in ("B", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_east_pos: for step in ("B2", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_west_pos: pass # Down elif wing_pos1 in self.sideD.edge_north_pos: for step in ("D", "R'"): self.rotate(step) elif wing_pos1 in self.sideD.edge_south_pos: for step in ("D'", "R'"): self.rotate(step) elif wing_pos1 in self.sideD.edge_east_pos: for step in ("R'", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_west_pos: for step in ("D2", "R'"): self.rotate(step) else: raise ImplementThis("implement wing %s to R east" % str(wing)) def move_wing_to_F_east(self, wing): self.move_wing_to_R_west(wing) def move_wing_to_D_north(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: for step in ("B2", "D2"): self.rotate(step) elif wing_pos1 in self.sideU.edge_south_pos: for step in ("F2", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_east_pos: for step in ("R2", "D'"): self.rotate(step) elif wing_pos1 in self.sideU.edge_west_pos: for step in ("L2", "D"): self.rotate(step) # Left elif wing_pos1 in self.sideL.edge_north_pos: for step in ("L2", "D"): self.rotate(step) elif wing_pos1 in self.sideL.edge_south_pos: for step in ("D", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_east_pos: for step in ("L", "D"): self.rotate(step) elif wing_pos1 in self.sideL.edge_west_pos: for step in ("L'", "D"): self.rotate(step) # Front elif wing_pos1 in self.sideF.edge_north_pos: for step in ("F2", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_south_pos: pass elif wing_pos1 in self.sideF.edge_east_pos: for step in ("F", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_west_pos: for step in ("F'", ): self.rotate(step) # Right elif wing_pos1 in self.sideR.edge_north_pos: for step in ("R2", "D'"): self.rotate(step) elif wing_pos1 in self.sideR.edge_south_pos: for step in ("D'", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_east_pos: for step in ("R", "D'"): self.rotate(step) elif wing_pos1 in self.sideR.edge_west_pos: for step in ("R'", "D'"): self.rotate(step) # Back elif wing_pos1 in self.sideB.edge_north_pos: for step in ("B2", "D2"): self.rotate(step) elif wing_pos1 in self.sideB.edge_south_pos: for step in ("D2", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_east_pos: for step in ("B", "D2"): self.rotate(step) elif wing_pos1 in self.sideB.edge_west_pos: for step in ("R", "D'"): self.rotate(step) # Down elif wing_pos1 in self.sideD.edge_north_pos: pass elif wing_pos1 in self.sideD.edge_south_pos: for step in ("D2", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_east_pos: for step in ("D'", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_west_pos: for step in ("D", ): self.rotate(step) else: raise ImplementThis("implement wing %s to D north" % str(wing)) def move_wing_to_D_west(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: for step in ("U'", "L2"): self.rotate(step) elif wing_pos1 in self.sideU.edge_south_pos: for step in ("U", "L2"): self.rotate(step) elif wing_pos1 in self.sideU.edge_east_pos: for step in ("U2", "L2"): self.rotate(step) elif wing_pos1 in self.sideU.edge_west_pos: for step in ("L2", ): self.rotate(step) # Left elif wing_pos1 in self.sideL.edge_north_pos: for step in ("L2", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_south_pos: pass elif wing_pos1 in self.sideL.edge_east_pos: for step in ("L", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_west_pos: for step in ("L'", ): self.rotate(step) # Front elif wing_pos1 in self.sideF.edge_north_pos: for step in ("F'", "L"): self.rotate(step) elif wing_pos1 in self.sideF.edge_south_pos: for step in ("D'", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_east_pos: for step in ("F", "D'"): self.rotate(step) elif wing_pos1 in self.sideF.edge_west_pos: for step in ("L", ): self.rotate(step) # Right elif wing_pos1 in self.sideR.edge_north_pos: for step in ("R2", "D2"): self.rotate(step) elif wing_pos1 in self.sideR.edge_south_pos: for step in ("D2", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_east_pos: for step in ("R", "D2"): self.rotate(step) elif wing_pos1 in self.sideR.edge_west_pos: for step in ("R'", "D2"): self.rotate(step) # Back elif wing_pos1 in self.sideB.edge_north_pos: for step in ("B", "L'"): self.rotate(step) elif wing_pos1 in self.sideB.edge_south_pos: for step in ("D", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_east_pos: for step in ("L'", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_west_pos: for step in ("B'", "D"): self.rotate(step) # Down elif wing_pos1 in self.sideD.edge_north_pos: for step in ("D'", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_south_pos: for step in ("D", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_east_pos: for step in ("D2", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_west_pos: pass else: raise ImplementThis("implement wing %s to D west" % str(wing)) def move_wing_to_D_south(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: for step in ("B2", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_south_pos: for step in ("F2", "D2"): self.rotate(step) elif wing_pos1 in self.sideU.edge_east_pos: for step in ("R2", "D"): self.rotate(step) elif wing_pos1 in self.sideU.edge_west_pos: for step in ("L2", "D'"): self.rotate(step) # Left elif wing_pos1 in self.sideL.edge_north_pos: for step in ("L2", "D'"): self.rotate(step) elif wing_pos1 in self.sideL.edge_south_pos: for step in ("D'", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_east_pos: for step in ("L", "D'"): self.rotate(step) elif wing_pos1 in self.sideL.edge_west_pos: for step in ("L'", "D'"): self.rotate(step) # Front elif wing_pos1 in self.sideF.edge_north_pos: for step in ("F2", "D2"): self.rotate(step) elif wing_pos1 in self.sideF.edge_south_pos: for step in ("D2", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_east_pos: for step in ("F", "D2"): self.rotate(step) elif wing_pos1 in self.sideF.edge_west_pos: for step in ("F'", "D2"): self.rotate(step) # Right elif wing_pos1 in self.sideR.edge_north_pos: for step in ("R2", "D"): self.rotate(step) elif wing_pos1 in self.sideR.edge_south_pos: for step in ("D", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_east_pos: for step in ("R", "D"): self.rotate(step) elif wing_pos1 in self.sideR.edge_west_pos: for step in ("R'", "D"): self.rotate(step) # Back elif wing_pos1 in self.sideB.edge_north_pos: for step in ("B2", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_south_pos: pass elif wing_pos1 in self.sideB.edge_east_pos: for step in ("B", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_west_pos: for step in ("B'", ): self.rotate(step) # Down elif wing_pos1 in self.sideD.edge_north_pos: for step in ("D2", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_south_pos: pass elif wing_pos1 in self.sideD.edge_east_pos: for step in ("D", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_west_pos: for step in ("D'", ): self.rotate(step) else: raise ImplementThis("implement wing %s to D south" % str(wing)) def move_wing_to_D_east(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): wing_pos1 = wing[0] else: wing_pos1 = wing # Upper if wing_pos1 in self.sideU.edge_north_pos: for step in ("U", "R2"): self.rotate(step) elif wing_pos1 in self.sideU.edge_south_pos: for step in ("U'", "R2"): self.rotate(step) elif wing_pos1 in self.sideU.edge_east_pos: for step in ("R2", ): self.rotate(step) elif wing_pos1 in self.sideU.edge_west_pos: for step in ("U2", "R2"): self.rotate(step) # Left elif wing_pos1 in self.sideL.edge_north_pos: for step in ("L2", "D2"): self.rotate(step) elif wing_pos1 in self.sideL.edge_south_pos: for step in ("D2", ): self.rotate(step) elif wing_pos1 in self.sideL.edge_east_pos: for step in ("L", "D2"): self.rotate(step) elif wing_pos1 in self.sideL.edge_west_pos: for step in ("L'", "D2"): self.rotate(step) # Front elif wing_pos1 in self.sideF.edge_north_pos: for step in ("F", "R'"): self.rotate(step) elif wing_pos1 in self.sideF.edge_south_pos: for step in ("D", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_east_pos: for step in ("R'", ): self.rotate(step) elif wing_pos1 in self.sideF.edge_west_pos: for step in ("F'", "D"): self.rotate(step) # Right elif wing_pos1 in self.sideR.edge_north_pos: for step in ("R2", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_south_pos: pass elif wing_pos1 in self.sideR.edge_east_pos: for step in ("R", ): self.rotate(step) elif wing_pos1 in self.sideR.edge_west_pos: for step in ("R'", ): self.rotate(step) # Back elif wing_pos1 in self.sideB.edge_north_pos: for step in ("B2", "D'"): self.rotate(step) elif wing_pos1 in self.sideB.edge_south_pos: for step in ("D'", ): self.rotate(step) elif wing_pos1 in self.sideB.edge_east_pos: for step in ("B", "D'"): self.rotate(step) elif wing_pos1 in self.sideB.edge_west_pos: for step in ("R", ): self.rotate(step) # Down elif wing_pos1 in self.sideD.edge_north_pos: for step in ("D", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_south_pos: for step in ("D'", ): self.rotate(step) elif wing_pos1 in self.sideD.edge_east_pos: pass elif wing_pos1 in self.sideD.edge_west_pos: for step in ("D2", ): self.rotate(step) else: raise ImplementThis("implement wing %s to D east" % str(wing)) def rotate_x(self): self.rotate("x") def rotate_x_reverse(self): self.rotate("x'") def rotate_y(self): self.rotate("y") def rotate_y_reverse(self): self.rotate("y'") def rotate_z(self): self.rotate("z") def rotate_z_reverse(self): self.rotate("z'") def get_center_corner_state(self): return ''.join([self.state[square_index] for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD) for square_index in side.center_corner_pos]) def centers_solved(self): for side in list(self.sides.values()): prev_pos = None for pos in side.center_pos: if prev_pos is not None: if self.state[prev_pos] != self.state[pos]: return False prev_pos = pos return True def crosses_solved(self): # side is a Side object # added a cross_pos to the Side object for side in list(self.sides.values()): prev_pos = None for pos in side.cross_pos: if prev_pos is not None: if self.state[prev_pos] != self.state[pos]: return False prev_pos = pos return True def UD_centers_staged(self): for side in (self.sideU, self.sideD): for pos in side.center_pos: if self.state[pos] not in ('U', 'D'): return False return True def LR_centers_staged(self): for side in (self.sideL, self.sideR): for pos in side.center_pos: if self.state[pos] not in ('L', 'R'): return False return True def rotate_side_X_to_Y(self, x, y): #assert x in ('U', 'L', 'F', 'R', 'B', 'D'), "Invalid side %s" % x #assert y in ('U', 'L', 'F', 'R', 'B', 'D'), "Invalid side %s" % y if y == 'U': side = self.sideU elif y == 'L': side = self.sideL elif y == 'F': side = self.sideF elif y == 'R': side = self.sideR elif y == 'B': side = self.sideB elif y == 'D': side = self.sideD # odd cube if side.mid_pos: pos_to_check = side.mid_pos F_pos_to_check = self.sideF.mid_pos D_pos_to_check = self.sideD.mid_pos # even cube else: # Use the top-right inner x-center offset = int(((self.size/2) * self.size) - (self.size/2)) pos_to_check = side.min_pos + offset F_pos_to_check = self.sideF.min_pos + offset D_pos_to_check = self.sideD.min_pos + offset count = 0 while self.state[pos_to_check] != x: #log.info("%s (%s): rotate %s to %s, pos_to_check %s, state at pos_to_check %s" % # (side, side.mid_pos, x, y, pos_to_check, self.state[pos_to_check])) if self.state[F_pos_to_check] == x and y == 'U': self.rotate_x() elif self.state[F_pos_to_check] == x and y == 'D': self.rotate_x_reverse() elif self.state[D_pos_to_check] == x and y == 'F': self.rotate_x() elif self.state[D_pos_to_check] == x and y == 'U': self.rotate_x() self.rotate_x() else: self.rotate_y() count += 1 if count > 30: raise StuckInALoop("rotate %s to %s, %s, pos_to_check %s, state at pos_to_check %s" % (x, y, side, pos_to_check, self.state[pos_to_check])) def rotate_U_to_U(self): self.rotate_side_X_to_Y('U', 'U') def rotate_F_to_F(self): self.rotate_side_X_to_Y('F', 'F') def get_kociemba_string(self, all_squares): # kociemba uses order U R F D L B foo = [] if all_squares: # This is only used to print cubes for test cases (see --test-build) for side_name in ('U', 'R', 'F', 'D', 'L', 'B'): side = self.sides[side_name] for square_index in range(side.min_pos, side.max_pos + 1): foo.append(self.state[square_index]) else: if self.size == 2: for side_name in ('U', 'R', 'F', 'D', 'L', 'B'): side = self.sides[side_name] # first row foo.append(self.state[side.corner_pos[0]]) foo.append(self.state[side.corner_pos[1]]) # second row foo.append(self.state[side.corner_pos[2]]) foo.append(self.state[side.corner_pos[3]]) else: for side_name in ('U', 'R', 'F', 'D', 'L', 'B'): side = self.sides[side_name] # first row foo.append(self.state[side.corner_pos[0]]) foo.append(self.state[side.edge_north_pos[0]]) foo.append(self.state[side.corner_pos[1]]) # second row foo.append(self.state[side.edge_west_pos[0]]) if side.mid_pos: foo.append(self.state[side.mid_pos]) else: offset = int(((self.size/2) * self.size) - (self.size/2)) pos_to_check = side.min_pos + offset foo.append(self.state[pos_to_check]) foo.append(self.state[side.edge_east_pos[0]]) # third row foo.append(self.state[side.corner_pos[2]]) foo.append(self.state[side.edge_south_pos[0]]) foo.append(self.state[side.corner_pos[3]]) kociemba_string = ''.join(foo) log.debug('kociemba string: %s' % kociemba_string) return kociemba_string def prevent_OLL(self): """ Solving OLL at the end takes 26 moves, preventing it takes 10 """ # OLL only applies for even cubes if self.is_odd(): return False orbits_with_oll_parity = self.center_solution_leads_to_oll_parity() steps = None if not orbits_with_oll_parity: return False if self.size == 4: if orbits_with_oll_parity == [0]: steps = "Rw U2 Rw U2 Rw U2 Rw U2 Rw U2" else: raise SolveError("prevent_OLL for %sx%sx%s, orbits %s have parity issues" % (self.size, self.size, self.size, pformat(orbits_with_oll_parity))) elif self.size == 6: # 10 steps if orbits_with_oll_parity == [0,1]: steps = "3Rw U2 3Rw U2 3Rw U2 3Rw U2 3Rw U2" # 10 steps elif orbits_with_oll_parity == [0]: steps = "Rw U2 Rw U2 Rw U2 Rw U2 Rw U2" # 15 steps for an inside orbit elif orbits_with_oll_parity == [1]: steps = "3Rw Rw' U2 3Rw Rw' U2 3Rw Rw' U2 3Rw Rw' U2 3Rw Rw' U2" else: raise SolveError("prevent_OLL for %sx%sx%s, orbits %s have parity issues" % (self.size, self.size, self.size, pformat(orbits_with_oll_parity))) #else: # raise ImplementThis("prevent_OLL for %sx%sx%s, orbits %s have parity issues" % # (self.size, self.size, self.size, pformat(orbits_with_oll_parity))) if steps: for step in steps.split(): self.rotate(step) return True return False def solve_OLL(self): # Check all 12 edges, rotate the one with OLL to U-south while True: has_oll = False if self.state[self.sideU.corner_pos[2]] == self.state[self.sideF.edge_north_pos[0]]: has_oll = True elif self.state[self.sideU.corner_pos[0]] == self.state[self.sideL.edge_north_pos[0]]: has_oll = True self.rotate_y_reverse() elif self.state[self.sideU.corner_pos[3]] == self.state[self.sideR.edge_north_pos[0]]: has_oll = True self.rotate_y() elif self.state[self.sideU.corner_pos[1]] == self.state[self.sideB.edge_north_pos[0]]: has_oll = True self.rotate_y() self.rotate_y() elif self.state[self.sideD.corner_pos[0]] == self.state[self.sideF.edge_south_pos[0]]: has_oll = True self.rotate_x() elif self.state[self.sideD.corner_pos[0]] == self.state[self.sideL.edge_south_pos[0]]: has_oll = True self.rotate_y_reverse() self.rotate_x() elif self.state[self.sideD.corner_pos[1]] == self.state[self.sideR.edge_south_pos[0]]: has_oll = True self.rotate_y() self.rotate_x() elif self.state[self.sideD.corner_pos[2]] == self.state[self.sideB.edge_south_pos[0]]: has_oll = True self.rotate_y() self.rotate_y() self.rotate_x() elif self.state[self.sideF.corner_pos[0]] == self.state[self.sideL.edge_east_pos[0]]: has_oll = True self.rotate_z() elif self.state[self.sideF.corner_pos[1]] == self.state[self.sideR.edge_west_pos[0]]: has_oll = True self.rotate_z_reverse() elif self.state[self.sideB.corner_pos[0]] == self.state[self.sideR.edge_east_pos[0]]: has_oll = True self.rotate_y() self.rotate_z_reverse() elif self.state[self.sideB.corner_pos[1]] == self.state[self.sideL.edge_west_pos[0]]: has_oll = True self.rotate_y_reverse() self.rotate_z() if has_oll: # 26 moves :( oll_solution = "%dRw2 R2 U2 %dRw2 R2 U2 %dRw R' U2 %dRw R' U2 %dRw' R' U2 B2 U %dRw' R U' B2 U %dRw R' U R2" % (self.size/2, self.size/2, self.size/2, self.size/2, self.size/2, self.size/2, self.size/2) log.warning("Solving OLL %s" % oll_solution) self.print_cube() for step in oll_solution.split(): self.rotate(step) else: break def solve_PLL(self): pll_id = None self.rotate_U_to_U() self.rotate_F_to_F() # rotate one of the hosed edges to U-south if self.state[self.sideU.edge_south_pos[0]] != 'U': pass elif self.state[self.sideU.edge_north_pos[0]] != 'U': self.rotate_y() self.rotate_y() elif self.state[self.sideU.edge_west_pos[0]] != 'U': self.rotate_y_reverse() elif self.state[self.sideU.edge_east_pos[0]] != 'U': self.rotate_y() elif self.state[self.sideL.edge_north_pos[0]] != 'L': raise ImplementThis("pll") elif self.state[self.sideL.edge_south_pos[0]] != 'L': self.rotate_x() self.rotate_x() self.rotate_y_reverse() elif self.state[self.sideL.edge_east_pos[0]] != 'L': self.rotate_z() elif self.state[self.sideL.edge_west_pos[0]] != 'L': self.rotate_y_reverse() self.rotate_z() elif self.state[self.sideF.edge_north_pos[0]] != 'F': raise ImplementThis("pll") elif self.state[self.sideF.edge_south_pos[0]] != 'F': self.rotate_x() elif self.state[self.sideF.edge_east_pos[0]] != 'F': self.rotate_z_reverse() elif self.state[self.sideF.edge_west_pos[0]] != 'F': raise ImplementThis("pll") elif self.state[self.sideR.edge_north_pos[0]] != 'R': raise ImplementThis("pll") elif self.state[self.sideR.edge_south_pos[0]] != 'R': self.rotate_y() self.rotate_x() elif self.state[self.sideR.edge_east_pos[0]] != 'R': self.rotate_y() self.rotate_z_reverse() elif self.state[self.sideR.edge_west_pos[0]] != 'R': raise ImplementThis("pll") elif self.state[self.sideB.edge_north_pos[0]] != 'B': raise ImplementThis("pll") elif self.state[self.sideB.edge_south_pos[0]] != 'B': self.rotate_x() self.rotate_x() elif self.state[self.sideB.edge_east_pos[0]] != 'B': raise ImplementThis("pll") elif self.state[self.sideB.edge_west_pos[0]] != 'B': raise ImplementThis("pll") elif self.state[self.sideD.edge_north_pos[0]] != 'D': raise ImplementThis("pll") elif self.state[self.sideD.edge_south_pos[0]] != 'D': raise ImplementThis("pll") elif self.state[self.sideD.edge_east_pos[0]] != 'D': raise ImplementThis("pll") elif self.state[self.sideD.edge_west_pos[0]] != 'D': raise ImplementThis("pll") else: self.print_cube() raise SolveError("we should not be here") if self.state[self.sideF.edge_north_pos[0]] == 'F': raise SolveError("F-north should have PLL edge") # rotate the other hosed edges to U-west if self.state[self.sideU.edge_south_pos[0]] != self.state[self.sideU.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideU.edge_north_pos[0]] != self.state[self.sideU.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideU.edge_west_pos[0]] != self.state[self.sideU.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideU.edge_east_pos[0]] != self.state[self.sideU.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideL.edge_north_pos[0]] != self.state[self.sideL.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideL.edge_south_pos[0]] != self.state[self.sideL.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideL.edge_east_pos[0]] != self.state[self.sideL.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideL.edge_west_pos[0]] != self.state[self.sideL.corner_pos[0]]: self.rotate_y() pll_id = 2 elif self.state[self.sideF.edge_south_pos[0]] != self.state[self.sideF.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideF.edge_east_pos[0]] != self.state[self.sideF.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideF.edge_west_pos[0]] != self.state[self.sideF.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideR.edge_north_pos[0]] != self.state[self.sideR.corner_pos[0]]: self.rotate_y() pll_id = 2 elif self.state[self.sideR.edge_south_pos[0]] != self.state[self.sideR.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideR.edge_east_pos[0]] != self.state[self.sideR.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideR.edge_west_pos[0]] != self.state[self.sideR.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideB.edge_north_pos[0]] != self.state[self.sideB.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideB.edge_south_pos[0]] != self.state[self.sideB.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideB.edge_east_pos[0]] != self.state[self.sideB.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideB.edge_west_pos[0]] != self.state[self.sideB.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideD.edge_north_pos[0]] != self.state[self.sideD.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideD.edge_south_pos[0]] != self.state[self.sideD.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideD.edge_east_pos[0]] != self.state[self.sideD.corner_pos[0]]: raise ImplementThis("pll") elif self.state[self.sideD.edge_west_pos[0]] != self.state[self.sideD.corner_pos[0]]: raise ImplementThis("pll") else: raise Exception("we should not be here") # http://www.speedcubing.com/chris/4speedsolve3.html if pll_id == 2: # Takes 12 steps pll_solution = "L2 D %dFw2 %dLw2 F2 %dLw2 L2 F2 %dLw2 %dFw2 D' L2" % (self.size/2, self.size/2, self.size/2, self.size/2, self.size/2) log.warning("Solving PLL ID %d: %s" % (pll_id, pll_solution)) self.print_cube() for step in pll_solution.split(): self.rotate(step) else: raise ImplementThis("pll_id %s" % pll_id) def solve_333(self): if self.solved(): return kociemba_string = self.get_kociemba_string(False) try: steps = subprocess.check_output(['kociemba', kociemba_string]).decode('ascii').splitlines()[-1].strip().split() kociemba_ok = True except Exception: kociemba_ok = False if not kociemba_ok: #edge_swap_count = self.get_edge_swap_count(edges_paired=True, debug=True) #corner_swap_count = self.get_corner_swap_count(debug=True) #raise SolveError("parity error made kociemba barf, edge parity %d, corner parity %d, kociemba %s" % # (edge_swap_count, corner_swap_count, kociemba_string)) raise SolveError("parity error made kociemba barf, kociemba %s" % kociemba_string) log.debug("kociemba : %s" % kociemba_string) log.debug("kociemba steps : %s" % ', '.join(steps)) for step in steps: step = str(step) self.rotate(step) if not self.solved(): self.solve_OLL() if not self.solved(): self.solve_PLL() if not self.solved(): raise SolveError("We hit either OLL or PLL parity and could not solve it") def get_corner_swap_count(self, debug=False): needed_corners = [ 'BLU', 'BRU', 'FLU', 'FRU', 'DFL', 'DFR', 'BDL', 'BDR'] to_check = [ (self.sideU.corner_pos[0], self.sideL.corner_pos[0], self.sideB.corner_pos[1]), # ULB (self.sideU.corner_pos[1], self.sideR.corner_pos[1], self.sideB.corner_pos[0]), # URB (self.sideU.corner_pos[2], self.sideL.corner_pos[1], self.sideF.corner_pos[0]), # ULF (self.sideU.corner_pos[3], self.sideF.corner_pos[1], self.sideR.corner_pos[0]), # UFR (self.sideD.corner_pos[0], self.sideL.corner_pos[3], self.sideF.corner_pos[2]), # DLF (self.sideD.corner_pos[1], self.sideF.corner_pos[3], self.sideR.corner_pos[2]), # DFR (self.sideD.corner_pos[2], self.sideL.corner_pos[2], self.sideB.corner_pos[3]), # DLB (self.sideD.corner_pos[3], self.sideR.corner_pos[3], self.sideB.corner_pos[2]) # DRB ] current_corners = [] for (square_index1, square_index2, square_index3) in to_check: square1 = self.state[square_index1] square2 = self.state[square_index2] square3 = self.state[square_index3] corner_str = ''.join(sorted([square1, square2, square3])) current_corners.append(corner_str) if debug: log.info("to_check:\n%s" % pformat(to_check)) to_check_str = '' for (a, b, c) in to_check: to_check_str += "%4s" % a log.info("to_check :%s" % to_check_str) log.info("needed corners : %s" % ' '.join(needed_corners)) log.info("currnet corners: %s" % ' '.join(current_corners)) log.info("") return get_swap_count(needed_corners, current_corners, debug) def corner_swaps_even(self, debug=False): if self.get_corner_swap_count(debug) % 2 == 0: return True return False def corner_swaps_odd(self, debug=False): if self.get_corner_swap_count(debug) % 2 == 1: return True return False def get_edge_swap_count(self, edges_paired, orbit, debug=False): needed_edges = [] to_check = [] # should not happen if edges_paired and orbit is not None: raise Exception("edges_paired is True and orbit is %s" % orbit) edges_per_side = len(self.sideU.edge_north_pos) # Upper for (edge_index, square_index) in enumerate(self.sideU.edge_north_pos): if edges_paired: to_check.append(square_index) needed_edges.append('UB') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('UB%d' % edge_index) for (edge_index, square_index) in enumerate(reversed(self.sideU.edge_west_pos)): if edges_paired: to_check.append(square_index) needed_edges.append('UL') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('UL%d' % edge_index) for (edge_index, square_index) in enumerate(reversed(self.sideU.edge_south_pos)): if edges_paired: to_check.append(square_index) needed_edges.append('UF') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('UF%d' % edge_index) for (edge_index, square_index) in enumerate(self.sideU.edge_east_pos): if edges_paired: to_check.append(square_index) needed_edges.append('UR') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('UR%d' % edge_index) # Left for (edge_index, square_index) in enumerate(reversed(self.sideL.edge_west_pos)): if edges_paired: to_check.append(square_index) needed_edges.append('LB') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('LB%d' % edge_index) for (edge_index, square_index) in enumerate(self.sideL.edge_east_pos): if edges_paired: to_check.append(square_index) needed_edges.append('LF') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('LF%d' % edge_index) # Right for (edge_index, square_index) in enumerate(reversed(self.sideR.edge_west_pos)): if edges_paired: to_check.append(square_index) needed_edges.append('RF') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('RF%d' % edge_index) for (edge_index, square_index) in enumerate(self.sideR.edge_east_pos): if edges_paired: to_check.append(square_index) needed_edges.append('RB') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('RB%d' % edge_index) # Down for (edge_index, square_index) in enumerate(self.sideD.edge_north_pos): if edges_paired: to_check.append(square_index) needed_edges.append('DF') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('DF%d' % edge_index) for (edge_index, square_index) in enumerate(reversed(self.sideD.edge_west_pos)): if edges_paired: to_check.append(square_index) needed_edges.append('DL') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('DL%d' % edge_index) for (edge_index, square_index) in enumerate(reversed(self.sideD.edge_south_pos)): if edges_paired: to_check.append(square_index) needed_edges.append('DB') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('DB%d' % edge_index) for (edge_index, square_index) in enumerate(self.sideD.edge_east_pos): if edges_paired: to_check.append(square_index) needed_edges.append('DR') break else: if orbit_matches(edges_per_side, orbit, edge_index): to_check.append(square_index) needed_edges.append('DR%d' % edge_index) if debug: to_check_str = '' for x in to_check: if edges_paired: to_check_str += "%3s" % x else: to_check_str += "%4s" % x log.info("to_check :%s" % to_check_str) log.info("needed edges : %s" % ' '.join(needed_edges)) current_edges = [] for square_index in to_check: side = self.get_side_for_index(square_index) partner_index = side.get_wing_partner(square_index) square1 = self.state[square_index] square2 = self.state[partner_index] if square1 in ('U', 'D'): wing_str = square1 + square2 elif square2 in ('U', 'D'): wing_str = square2 + square1 elif square1 in ('L', 'R'): wing_str = square1 + square2 elif square2 in ('L', 'R'): wing_str = square2 + square1 elif (square1, square2) == ('x', 'x'): continue else: raise Exception("Could not determine wing_str for (%s, %s)" % (square1, square2)) if not edges_paired: # - backup the current state # - add an 'x' to the end of the square_index/partner_index # - move square_index/partner_index to its final edge location # - look for the 'x' to determine if this is the '0' vs '1' wing # - restore the original state square1_with_x = square1 + 'x' square2_with_x = square2 + 'x' original_state = self.state[:] original_solution = self.solution[:] self.state[square_index] = square1_with_x self.state[partner_index] = square2_with_x # 'UB0', 'UB1', 'UL0', 'UL1', 'UF0', 'UF1', 'UR0', 'UR1', # 'LB0', 'LB1', 'LF0', 'LF1', 'RF0', 'RF1', 'RB0', 'RB1', # 'DF0', 'DF1', 'DL0', 'DL1', 'DB0', 'DB1', 'DR0', 'DR1 if wing_str == 'UB': self.move_wing_to_U_north(square_index) edge_to_check = self.sideU.edge_north_pos target_side = self.sideU elif wing_str == 'UL': self.move_wing_to_U_west(square_index) edge_to_check = reversed(self.sideU.edge_west_pos) target_side = self.sideU elif wing_str == 'UF': self.move_wing_to_U_south(square_index) edge_to_check = reversed(self.sideU.edge_south_pos) target_side = self.sideU elif wing_str == 'UR': self.move_wing_to_U_east(square_index) edge_to_check = self.sideU.edge_east_pos target_side = self.sideU elif wing_str == 'LB': self.move_wing_to_L_west(square_index) edge_to_check = reversed(self.sideL.edge_west_pos) target_side = self.sideL elif wing_str == 'LF': self.move_wing_to_L_east(square_index) edge_to_check = self.sideL.edge_east_pos target_side = self.sideL elif wing_str == 'RF': self.move_wing_to_R_west(square_index) edge_to_check = reversed(self.sideR.edge_west_pos) target_side = self.sideR elif wing_str == 'RB': self.move_wing_to_R_east(square_index) edge_to_check = self.sideR.edge_east_pos target_side = self.sideR elif wing_str == 'DF': self.move_wing_to_D_north(square_index) edge_to_check = self.sideD.edge_north_pos target_side = self.sideD elif wing_str == 'DL': self.move_wing_to_D_west(square_index) edge_to_check = reversed(self.sideD.edge_west_pos) target_side = self.sideD elif wing_str == 'DB': self.move_wing_to_D_south(square_index) edge_to_check = reversed(self.sideD.edge_south_pos) target_side = self.sideD elif wing_str == 'DR': self.move_wing_to_D_east(square_index) edge_to_check = self.sideD.edge_east_pos target_side = self.sideD else: raise SolveError("invalid wing %s" % wing_str) for (edge_index, wing_index) in enumerate(edge_to_check): wing_value = self.state[wing_index] if wing_value.endswith('x'): if wing_value.startswith(target_side.name): wing_str += str(edge_index) else: max_edge_index = len(target_side.edge_east_pos) - 1 wing_str += str(max_edge_index - edge_index) # This is commented out because we used this once upon a time to generate the # orbit_index_444, etc dictionaries in https://github.com/dwalton76/rubiks-color-resolver # # The workflow was: # - tweak code to call center_solution_leads_to_oll_parity() for odd cubes too # - solve 500 cubes via "./utils/test.py --test-cubes utils/test_cubes.json --size 7x7x7" # - sort /tmp/orbit_index.txt > /tmp/orbit_index_sorted.txt # - cat /tmp/orbit_index_sorted.txt | uniq > /tmp/orbit_index_sorted_uniq.txt # # The lines in /tmp/orbit_index_sorted_uniq.txt are used to create orbit_index_777 ''' with open('/tmp/orbit_index.txt', 'a') as fh: fh.write(" (%d, %d, '%s', '%s') : '%s',\n" % (square_index, partner_index, square1, square2, wing_str)) fh.write(" (%d, %d, '%s', '%s') : '%s',\n" % (partner_index, square_index, square2, square1, wing_str)) ''' break else: raise SolveError("Could not find wing %s (%d, %d) among %s" % (wing_str, square_index, partner_index, str(edge_to_check))) self.state = original_state[:] self.solution = original_solution[:] current_edges.append(wing_str) if debug: log.info("current edges: %s" % ' '.join(current_edges)) return get_swap_count(needed_edges, current_edges, debug) def edge_swaps_even(self, edges_paired, orbit, debug): if self.get_edge_swap_count(edges_paired, orbit, debug) % 2 == 0: return True return False def edge_swaps_odd(self, edges_paired, orbit, debug): if self.get_edge_swap_count(edges_paired, orbit, debug) % 2 == 1: return True return False def edge_solution_leads_to_pll_parity(self, debug=False): self.rotate_U_to_U() self.rotate_F_to_F() if self.edge_swaps_even(edges_paired=True, orbit=None, debug=debug) == self.corner_swaps_even(debug): if debug: log.info("Predict we are free of PLL parity") return False if debug: log.info("Predict we have PLL parity") return True def center_solution_leads_to_oll_parity(self, debug=False): """ http://www.speedcubing.com/chris/4speedsolve3.html http://www.rubik.rthost.org/4x4x4_edges.htm """ if self.centers_solved(): self.rotate_U_to_U() self.rotate_F_to_F() orbits_with_oll_parity = [] orbits = int((self.size - 2) / 2) for orbit in range(orbits): # OLL Parity - "...is caused by solving the centers such that the edge permutation is odd" # http://www.speedcubing.com/chris/4speedsolve3.html if self.edge_swaps_odd(False, orbit, debug): orbits_with_oll_parity.append(orbit) #log.info("orbit %d has OLL parity" % orbit) if not orbits_with_oll_parity: log.debug("Predict we are free of OLL parity") return orbits_with_oll_parity def get_state_all(self): result = [] for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD): for square_index in range(side.min_pos, side.max_pos + 1): result.append(self.state[square_index]) return ''.join(result) def group_centers_guts(self): raise ImplementThis("Child class must implement group_centers_guts") def group_centers(self): if self.is_odd(): self.rotate_U_to_U() self.rotate_F_to_F() if self.centers_solved(): self.rotate_U_to_U() self.rotate_F_to_F() log.info("group center solution: centers are already solved") else: log.info("") log.info("") log.info("") self.group_centers_guts() log.info("group center solution (%d steps in)" % (self.get_solution_len_minus_rotates(self.solution))) if self.prevent_OLL(): log.info("prevented OLL (%d steps in)" % (self.get_solution_len_minus_rotates(self.solution))) self.solution.append('CENTERS_SOLVED') def get_wing_value(self, wing): if isinstance(wing, tuple) or isinstance(wing, list): square_index = wing[0] else: square_index = wing side = self.get_side_for_index(square_index) partner_index = side.get_wing_partner(square_index) if square_index < partner_index: return (self.state[square_index], self.state[partner_index]) else: return (self.state[partner_index], self.state[square_index]) def get_solution_len_minus_rotates(self, solution): count = 0 size_str = str(self.size) for step in solution: if step in ('CENTERS_SOLVED', 'EDGES_GROUPED'): continue if step in ("x", "x'", "x2", "y", "y'", "y2", "z", "z'", "z2"): continue if not step.startswith(size_str): count += 1 return count def compress_solution(self): solution_string = [] for step in self.solution: if step == "x": step = "%dR" % self.size elif step == "x'": step = "%dR'" % self.size elif step == "y": step = "%dU" % self.size elif step == "y'": step = "%dU'" % self.size elif step == "z": step = "%dF" % self.size elif step == "z'": step = "%dF'" % self.size solution_string.append(step) moves = set(solution_string) solution_string = ' '.join(solution_string) while True: original_solution_string = solution_string[:] for move in moves: if move in ('CENTERS_SOLVED', 'EDGES_GROUPED'): continue if move in ('x', 'y', 'z'): raise Exception('compress_solution does not support move "%s"' % move) if move.endswith("2'"): raise Exception('compress_solution does not support move "%s"' % move) if move.endswith("'"): reverse_move = move[0:-1] else: reverse_move = move + "'" # If the same half turn is done 2x in a row, remove it if move.endswith("2"): solution_string = solution_string.replace(" %s %s " % (move, move), " ") else: # If the same quarter turn is done 4x in a row, remove it solution_string = solution_string.replace(" %s %s %s %s " % (move, move, move, move), " ") # If the same quarter turn is done 3x in a row, replace it with one backwards move solution_string = solution_string.replace(" %s %s %s " % (move, move, move), " %s " % reverse_move) # If the same quarter turn is done 2x in a row, replace it with one half turn # Do not bother doing this with whole cube rotations we will pull those out later if not move.startswith(str(self.size)): if move.endswith("'"): solution_string = solution_string.replace(" %s %s " % (move, move), " %s2 " % move[0:-1]) else: solution_string = solution_string.replace(" %s %s " % (move, move), " %s2 " % move) # "F F'" and "F' F" will cancel each other out, remove them solution_string = solution_string.replace(" %s %s " % (move, reverse_move), " ") solution_string = solution_string.replace(" %s %s " % (reverse_move, move), " ") if original_solution_string == solution_string: break # Remove full cube rotations by changing all of the steps that follow the cube rotation steps = solution_string.strip().split() final_steps = [] rotations = [] for (index, step) in enumerate(steps): if step.startswith(str(self.size)): rotations.append(apply_rotations(self.size, step, rotations)) else: final_steps.append(apply_rotations(self.size, step, rotations)) solution_string = ' '.join(final_steps) # We put some markers in the solution to track how many steps # each stage took...remove those markers solution_minus_markers = [] self.steps_to_rotate_cube = 0 self.steps_to_solve_centers = 0 self.steps_to_group_edges = 0 self.steps_to_solve_3x3x3 = 0 index = 0 # log.info("pre compress; %s" % ' '.join(self.solution)) for step in solution_string.split(): if step.startswith(str(self.size)): self.steps_to_rotate_cube += 1 if step == 'CENTERS_SOLVED': self.steps_to_solve_centers = index index = 0 elif step == 'EDGES_GROUPED': self.steps_to_group_edges = index index = 0 else: solution_minus_markers.append(step) index += 1 self.steps_to_solve_3x3x3 = index self.solution = solution_minus_markers def solve(self): """ The RubiksCube222 and RubiksCube333 child classes will override this since they don't need to group centers or edges """ solved_string = 'U' * self.squares_per_side +\ 'L' * self.squares_per_side +\ 'F' * self.squares_per_side +\ 'R' * self.squares_per_side +\ 'B' * self.squares_per_side +\ 'D' * self.squares_per_side if self.get_state_all() != solved_string: self.group_centers() self.group_edges() self.rotate_U_to_U() self.rotate_F_to_F() self.solve_333() self.compress_solution() # Cube is solved, rotate it around so white is on top, etc #self.rotate_U_to_U() #self.rotate_F_to_F() def print_solution(self): # Print an alg.cubing.net URL for this setup/solution url = "https://alg.cubing.net/?puzzle=%dx%dx%d&setup=" % (self.size, self.size, self.size) url += '_'.join(reverse_steps(self.solution)) url += '&alg=' url += '_'.join(self.solution) url = url.replace("'", "-") print("\nURL : %s" % url) print("\nSolution: %s" % ' '.join(self.solution)) if self.steps_to_rotate_cube: print(("%d steps to rotate entire cube" % self.steps_to_rotate_cube)) if self.steps_to_solve_centers: print(("%d steps to solve centers" % self.steps_to_solve_centers)) if self.steps_to_group_edges: print(("%d steps to group edges" % self.steps_to_group_edges)) if self.steps_to_solve_3x3x3: print(("%d steps to solve 3x3x3" % self.steps_to_solve_3x3x3)) print(("%d steps total" % len(self.solution))) def edge_string_to_find(self, target_wing, sister_wing1, sister_wing2, sister_wing3): edge_pos_square_index = (2, 3, 4, 6, 10, 11, 15, 16, 20, 22, 23, 24, 27, 28, 29, 31, 35, 36, 40, 41, 45, 47, 48, 49, 52, 53, 54, 56, 60, 61, 65, 66, 70, 72, 73, 74, 77, 78, 79, 81, 85, 86, 90, 91, 95, 97, 98, 99, 102, 103, 104, 106, 110, 111, 115, 116, 120, 122, 123, 124, 127, 128, 129, 131, 135, 136, 140, 141, 145, 147, 148, 149) foo = { target_wing[0] : 'A', target_wing[1] : 'B', sister_wing1[0] : 'C', sister_wing1[1] : 'D', sister_wing2[0] : 'E', sister_wing2[1] : 'F', sister_wing3[0] : 'G', sister_wing3[1] : 'H', } return ''.join([foo.get(square_index, 'x') for square_index in edge_pos_square_index]) def nuke_corners(self): for side in list(self.sides.values()): for square_index in side.corner_pos: self.state[square_index] = 'x' def nuke_centers(self): for side in list(self.sides.values()): for square_index in side.center_pos: self.state[square_index] = 'x' def nuke_edges(self): for side in list(self.sides.values()): for square_index in side.edge_pos: self.state[square_index] = 'x' def www_header(self): """ Write the including css """ side_margin = 10 square_size = 40 size = self.size # 3 for 3x3x3, etc shutil.copy('www/solution.js', '/tmp/') shutil.copy('www/Arrow-Next.png', '/tmp/') shutil.copy('www/Arrow-Prev.png', '/tmp/') with open('/tmp/solution.html', 'w') as fh: fh.write(""" CraneCuber
""" % (square_size, square_size, square_size, square_size, square_size, square_size, (square_size * size * 4) + square_size + (4 * side_margin))) def www_write_cube(self, desc): """ 'cube' is a list of (R,G,B) tuples """ cube = ['dummy',] for square in self.state[1:]: cube.append(self.color_map_html[square]) col = 1 squares_per_side = self.size * self.size max_square = squares_per_side * 6 sides = ('upper', 'left', 'front', 'right', 'back', 'down') side_index = -1 (first_squares, last_squares, last_UBD_squares) = get_important_square_indexes(self.size) with open('/tmp/solution.html', 'a') as fh: fh.write("\n") def www_footer(self): with open('/tmp/solution.html', 'a') as fh: fh.write("""
""") def test(self): """ Run some tests to sanity check some things """ for side in list(self.sides.values()): for square_index in range(side.min_pos, side.max_pos+1): self.state[square_index] = 'x' original_state = self.state[:] original_solution = self.solution[:] for side in list(self.sides.values()): for direction in ('north', 'south', 'east', 'west'): if direction == 'north': edges = side.edge_north_pos elif direction == 'south': edges = side.edge_south_pos elif direction == 'east': edges = side.edge_east_pos elif direction == 'west': edges = side.edge_west_pos for edge_index in edges: partner_index = side.get_wing_partner(edge_index) self.state[edge_index] = 'U' self.state[partner_index] = 'U' tmp_state = self.state[:] log.info("test move_wing for %s-%s (%d, %d)" % (side, direction, edge_index, partner_index)) # U-north for index_to_test in (edge_index, partner_index): self.move_wing_to_U_north(index_to_test) for tmp_edge_index in self.sideU.edge_north_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_U_north failed") self.state[:] = tmp_state # U-south for index_to_test in (edge_index, partner_index): self.move_wing_to_U_south(index_to_test) for tmp_edge_index in self.sideU.edge_south_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_U_south failed") self.state[:] = tmp_state # U-east for index_to_test in (edge_index, partner_index): self.move_wing_to_U_east(index_to_test) for tmp_edge_index in self.sideU.edge_east_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_U_east failed") self.state[:] = tmp_state # U-west for index_to_test in (edge_index, partner_index): self.move_wing_to_U_west(index_to_test) for tmp_edge_index in self.sideU.edge_west_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_U_west failed") self.state[:] = tmp_state # L-north ''' for index_to_test in (edge_index, partner_index): self.move_wing_to_L_north(index_to_test) for tmp_edge_index in self.sideL.edge_north_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_L_north failed") self.state[:] = tmp_state ''' # L-south ''' for index_to_test in (edge_index, partner_index): self.move_wing_to_L_south(index_to_test) for tmp_edge_index in self.sideL.edge_south_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_L_south failed") self.state[:] = tmp_state ''' # L-east for index_to_test in (edge_index, partner_index): self.move_wing_to_L_east(index_to_test) for tmp_edge_index in self.sideL.edge_east_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_L_east failed for %d" % index_to_test) self.state[:] = tmp_state # L-west for index_to_test in (edge_index, partner_index): self.move_wing_to_L_west(index_to_test) for tmp_edge_index in self.sideL.edge_west_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_L_west failed") self.state[:] = tmp_state # F-north ''' for index_to_test in (edge_index, partner_index): self.move_wing_to_F_north(index_to_test) for tmp_edge_index in self.sideF.edge_north_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_F_north failed") self.state[:] = tmp_state ''' # F-south ''' for index_to_test in (edge_index, partner_index): self.move_wing_to_F_south(index_to_test) for tmp_edge_index in self.sideF.edge_south_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_F_south failed") self.state[:] = tmp_state ''' # F-east for index_to_test in (edge_index, partner_index): self.move_wing_to_F_east(index_to_test) for tmp_edge_index in self.sideF.edge_east_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_F_east failed") self.state[:] = tmp_state # F-west for index_to_test in (edge_index, partner_index): self.move_wing_to_F_west(index_to_test) for tmp_edge_index in self.sideF.edge_west_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_F_west failed") self.state[:] = tmp_state # R-north ''' for index_to_test in (edge_index, partner_index): self.move_wing_to_R_north(index_to_test) for tmp_edge_index in self.sideR.edge_north_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_R_north failed") self.state[:] = tmp_state ''' # R-south ''' for index_to_test in (edge_index, partner_index): self.move_wing_to_R_south(index_to_test) for tmp_edge_index in self.sideR.edge_south_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_R_south failed") self.state[:] = tmp_state ''' # R-east for index_to_test in (edge_index, partner_index): self.move_wing_to_R_east(index_to_test) for tmp_edge_index in self.sideR.edge_east_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_R_east failed") self.state[:] = tmp_state # R-west for index_to_test in (edge_index, partner_index): self.move_wing_to_R_west(index_to_test) for tmp_edge_index in self.sideR.edge_west_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_R_west failed") self.state[:] = tmp_state # B-north ''' for index_to_test in (edge_index, partner_index): self.move_wing_to_B_north(index_to_test) for tmp_edge_index in self.sideB.edge_north_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_B_north failed") self.state[:] = tmp_state ''' # B-south ''' for index_to_test in (edge_index, partner_index): self.move_wing_to_B_south(index_to_test) for tmp_edge_index in self.sideB.edge_south_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_B_south failed") self.state[:] = tmp_state ''' # B-east ''' for index_to_test in (edge_index, partner_index): self.move_wing_to_B_east(index_to_test) for tmp_edge_index in self.sideB.edge_east_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_B_east failed") self.state[:] = tmp_state ''' # B-west ''' for index_to_test in (edge_index, partner_index): self.move_wing_to_B_west(index_to_test) for tmp_edge_index in self.sideB.edge_west_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_B_west failed") self.state[:] = tmp_state ''' # D-north for index_to_test in (edge_index, partner_index): self.move_wing_to_D_north(index_to_test) for tmp_edge_index in self.sideD.edge_north_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_D_north failed") self.state[:] = tmp_state # D-south for index_to_test in (edge_index, partner_index): self.move_wing_to_D_south(index_to_test) for tmp_edge_index in self.sideD.edge_south_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_D_south failed") self.state[:] = tmp_state # D-east for index_to_test in (edge_index, partner_index): self.move_wing_to_D_east(index_to_test) for tmp_edge_index in self.sideD.edge_east_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_D_east failed") self.state[:] = tmp_state # D-west for index_to_test in (edge_index, partner_index): self.move_wing_to_D_west(index_to_test) for tmp_edge_index in self.sideD.edge_west_pos: if self.state[tmp_edge_index] == 'U': break else: raise SolveError("move_wing_to_D_west failed") self.state[:] = tmp_state self.state[:] = original_state self.state[:] = original_state def rotate_to_side(self, upper_side_name, front_side_name): if upper_side_name == front_side_name: return False if upper_side_name == 'U': if front_side_name == 'D': return False if front_side_name == 'L': self.rotate_y_reverse() elif front_side_name == 'F': pass elif front_side_name == 'R': self.rotate_y() elif front_side_name == 'B': self.rotate_y() self.rotate_y() elif upper_side_name == 'D': if front_side_name == 'U': return False self.rotate_x() self.rotate_x() if front_side_name == 'L': self.rotate_y_reverse() elif front_side_name == 'F': self.rotate_y() self.rotate_y() elif front_side_name == 'R': self.rotate_y() elif front_side_name == 'B': pass elif upper_side_name == 'L': if front_side_name == 'R': return False self.rotate_y_reverse() self.rotate_x() # dwalton if front_side_name == 'U': self.rotate_y() self.rotate_y() elif front_side_name == 'F': self.rotate_y() elif front_side_name == 'D': pass elif front_side_name == 'B': self.rotate_y_reverse() elif upper_side_name == 'F': if front_side_name == 'B': return False self.rotate_x() if front_side_name == 'L': self.rotate_y_reverse() elif front_side_name == 'U': self.rotate_y() self.rotate_y() elif front_side_name == 'R': self.rotate_y() elif front_side_name == 'D': pass elif upper_side_name == 'R': if front_side_name == 'L': return False self.rotate_y() self.rotate_x() if front_side_name == 'U': self.rotate_y() self.rotate_y() elif front_side_name == 'F': self.rotate_y_reverse() elif front_side_name == 'D': pass elif front_side_name == 'B': self.rotate_y() elif upper_side_name == 'B': if front_side_name == 'F': return False self.rotate_x_reverse() if front_side_name == 'L': self.rotate_y_reverse() elif front_side_name == 'U': pass elif front_side_name == 'R': self.rotate_y() elif front_side_name == 'D': self.rotate_y() self.rotate_y() return True def transform_x(self): for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD): for square_index in range(side.min_pos, side.max_pos+1): if self.state[square_index] == 'U': self.state[square_index] = 'B' elif self.state[square_index] == 'L': pass elif self.state[square_index] == 'F': self.state[square_index] = 'U' elif self.state[square_index] == 'R': pass elif self.state[square_index] == 'B': self.state[square_index] = 'D' elif self.state[square_index] == 'D': self.state[square_index] = 'F' def transform_x_prime(self): for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD): for square_index in range(side.min_pos, side.max_pos+1): if self.state[square_index] == 'U': self.state[square_index] = 'F' elif self.state[square_index] == 'L': pass elif self.state[square_index] == 'F': self.state[square_index] = 'D' elif self.state[square_index] == 'R': pass elif self.state[square_index] == 'B': self.state[square_index] = 'U' elif self.state[square_index] == 'D': self.state[square_index] = 'B' def transform_y(self): for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD): for square_index in range(side.min_pos, side.max_pos+1): if self.state[square_index] == 'U': pass elif self.state[square_index] == 'L': self.state[square_index] = 'B' elif self.state[square_index] == 'F': self.state[square_index] = 'L' elif self.state[square_index] == 'R': self.state[square_index] = 'F' elif self.state[square_index] == 'B': self.state[square_index] = 'R' elif self.state[square_index] == 'D': pass def transform_y_prime(self): for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD): for square_index in range(side.min_pos, side.max_pos+1): if self.state[square_index] == 'U': pass elif self.state[square_index] == 'L': self.state[square_index] = 'F' elif self.state[square_index] == 'F': self.state[square_index] = 'R' elif self.state[square_index] == 'R': self.state[square_index] = 'B' elif self.state[square_index] == 'B': self.state[square_index] = 'L' elif self.state[square_index] == 'D': pass def transform_z(self): for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD): for square_index in range(side.min_pos, side.max_pos+1): if self.state[square_index] == 'U': self.state[square_index] = 'R' elif self.state[square_index] == 'L': self.state[square_index] = 'U' elif self.state[square_index] == 'F': pass elif self.state[square_index] == 'R': self.state[square_index] = 'D' elif self.state[square_index] == 'B': pass elif self.state[square_index] == 'D': self.state[square_index] = 'L' def transform_z_prime(self): for side in (self.sideU, self.sideL, self.sideF, self.sideR, self.sideB, self.sideD): for square_index in range(side.min_pos, side.max_pos+1): if self.state[square_index] == 'U': self.state[square_index] = 'L' elif self.state[square_index] == 'L': self.state[square_index] = 'D' elif self.state[square_index] == 'F': pass elif self.state[square_index] == 'R': self.state[square_index] = 'U' elif self.state[square_index] == 'B': pass elif self.state[square_index] == 'D': self.state[square_index] = 'R' def transform(self, target): """ This should cover every scenario: rotations = ( (), ("y",), ("y'",), ("y", "y"), ("x", "x", "y"), ("x", "x", "y'"), ("x", "x", "y", "y"), ("y'", "x", "y"), ("y'", "x", "y'"), ("y'", "x", "y", "y"), ("x", "y"), ("x", "y'"), ("x", "y", "y"), ("y", "x", "y"), ("y", "x", "y'"), ("y", "x", "y", "y"), ("x'", "y"), ("x'", "y'"), ("x'", "y", "y") ) """ if not target: pass elif target == "x": self.transform_x() elif target == "x'": self.transform_x_prime() elif target == "y": self.transform_y() elif target == "y'": self.transform_y_prime() elif target == "z": self.transform_z() elif target == "z'": self.transform_z_prime() else: raise Exception("Implement target %s" % target)