def solve(self): if not self.filename_exists: raise SolveError("%s does not exist" % self.filename) if "TBD" in self.state_target: tbd = True else: tbd = False while True: self.ida_graph_node = None (state, cost_to_goal) = self.ida_heuristic() if tbd: log.info( "%s: solve() state %s vs state_target %s, cost_to_goal %d" % (self, state, pformat(self.state_target), cost_to_goal) ) if state in self.state_target: break steps = self.steps(state) if steps: for step in steps: self.parent.rotate(step) else: # self.parent.print_cube() raise NoSteps("%s: state %s does not have steps" % (self, state))
def solve(self) -> None: if not self.filename_exists: raise SolveError(f"{self.filename} does not exist") if "TBD" in self.state_target: tbd = True else: tbd = False while True: self.ida_graph_node = None (state, cost_to_goal) = self.ida_heuristic() if tbd: logger.info( f"{self}: solve() state {state} vs state_target {self.state_target}, cost_to_goal {cost_to_goal}" ) if state in self.state_target: break steps = self.steps(state) if steps: for step in steps: self.parent.rotate(step) else: raise NoSteps(f"{self}: state {state} does not have steps")
def solve(self): if not self.filename_exists: raise SolveError("%s does not exist" % self.filename) if 'TBD' in self.state_target: tbd = True else: tbd = False while True: (state, _) = self.ida_heuristic(0) if tbd: log.info("%s: solve() state %s vs state_target %s" % (self, state, pformat(self.state_target))) if state in self.state_target: break steps = self.steps(state) if steps: for step in steps: self.parent.rotate(step) else: self.parent.print_cube() raise NoSteps("%s: state %s does not have steps" % (self, state))
def stage_final_four_edges_in_x_plane(self): original_state = self.state[:] original_solution = self.solution[:] done = False # Traverse a table of moves that place L4E in one of three planes # and then rotate that plane to the x-plane for min_unpaired_count in (0, -1): for pre_steps in pre_steps_to_try: self.state = original_state[:] self.solution = original_solution[:] steps = None for step in pre_steps: self.rotate(step) if self.x_plane_edges_are_l4e() and self.x_plane_edges_unpaired_count() > min_unpaired_count: # if pre_steps: # log.info("%s: %s puts L4E group in x-plane" % (self, "".join(pre_steps))) # else: # log.info("%s: L4E group in x-plane" % self) done = True break elif (self.y_plane_edges_are_l4e() and self.y_plane_edges_unpaired_count() > min_unpaired_count): # if pre_steps: # log.info("%s: %s puts L4E group in y-plane, moving to x-plane" % (self, "".join(pre_steps))) # else: # log.info("%s: L4E group in y-plane, moving to x-plane" % self) self.rotate("z") done = True break elif (self.z_plane_edges_are_l4e() and self.z_plane_edges_unpaired_count() > min_unpaired_count): # if pre_steps: # log.info("%s: %s puts L4E group in z-plane" % (self, "".join(pre_steps))) # else: # log.info("%s: L4E group in z-plane, moving to x-plane" % self) self.rotate("x") done = True break if done: break if not done: msg = "Could not stage L4E in x-plane" log.warning(msg) self.enable_print_cube = True self.print_cube() raise SolveError(msg)
def steps_cost(self, state_to_find): # compute the hash_index for state_to_find, look that many bytes into the # file/self.conten and retrieve a single hex character. This hex character # is the number of steps required to solve the corresponding state. hash_raw = hashxx(state_to_find.encode('utf-8')) hash_index = int(hash_raw % self.bucketcount) result = int(chr(self.content[hash_index]), 16) # This should never be zero if not result: #log.warning("%s: state_to_find %s, hash_raw %s. hash_index %s, result is %s" % (self, state_to_find, hash_raw, hash_index, result)) raise SolveError( "%s: state_to_find %s, hash_raw %s. hash_index %s, result is %s" % (self, state_to_find, hash_raw, hash_index, result)) return result
def stage_final_four_edges_in_x_plane(self): original_state = self.state[:] original_solution = self.solution[:] # Traverse a table of moves that place L4E in one of three planes # and then rotate that plane to the x-plane for pre_steps in pre_steps_to_try: self.state = original_state[:] self.solution = original_solution[:] steps = None for step in pre_steps: self.rotate(step) if self.x_plane_edges_are_l4e() and self.x_plane_edges_unpaired_count() > 0: # if pre_steps: # log.info("%s: %s puts L4E group in x-plane" % (self, "".join(pre_steps))) # else: # log.info("%s: L4E group in x-plane" % self) break elif ( self.y_plane_edges_are_l4e() and self.y_plane_edges_unpaired_count() > 0 ): # if pre_steps: # log.info("%s: %s puts L4E group in y-plane, moving to x-plane" % (self, "".join(pre_steps))) # else: # log.info("%s: L4E group in y-plane, moving to x-plane" % self) self.rotate("z") break elif ( self.z_plane_edges_are_l4e() and self.z_plane_edges_unpaired_count() > 0 ): # if pre_steps: # log.info("%s: %s puts L4E group in z-plane" % (self, "".join(pre_steps))) # else: # log.info("%s: L4E group in z-plane, moving to x-plane" % self) self.rotate("x") break else: raise SolveError("Could not stage L4E in x-plane")
def heuristic(self, pt_state): if pt_state in self.state_target: return 0 else: result = self.steps_cost(pt_state) if result == 0: #log.warning("%s: pt_state %s cost is 0 but this is not a state_target" % (self, pt_state)) self.parent.enable_print_cube = True #self.parent.print_cube() raise NoPruneTableState( "%s: pt_state %s cost is 0 but this is not a state_target" % (self, pt_state)) return result self.parent.print_cube() raise SolveError( "%s does not have max_depth and does not have steps for %s, state_width %d" % (self, pt_state, self.state_width))
def heuristic(self): pt_state = self.state() if pt_state in self.state_target: return 0 else: pt_steps_cost = self.steps_cost(pt_state) if pt_steps_cost: return pt_steps_cost elif self.max_depth: # This is the exception to the rule but some prune tables such # as lookup-table-6x6x6-step23-UD-oblique-edge-pairing-LFRB-only.txt # are partial tables so use the max_depth of the table +1 return self.max_depth + 1 self.parent.print_cube() raise SolveError( "%s does not have max_depth and does not have steps for %s, state_width %d" % (self, pt_state, self.state_width))
def solve(self): if not self.filename_exists: raise SolveError("%s does not exist" % self.filename) if 'TBD' in self.state_target: tbd = True else: tbd = False while True: state = self.state() if tbd: log.info("%s: solve() state %s vs state_target %s" % (self, state, pformat(self.state_target))) if state in self.state_target: break steps = self.steps(state) if steps: #log.info("%s: PRE solve() state %s found %s" % (self, state, ' '.join(steps))) #self.parent.print_cube() #log.info("%s: %d steps" % (self, len(steps))) for step in steps: self.parent.rotate(step) #log.info("%s: POST solve()" % self) #self.parent.print_cube() else: self.parent.print_cube() raise NoSteps("%s: state %s does not have steps" % (self, state))
def pair_x_plane_edges_in_l4e(self): if not self.x_plane_edges_are_l4e(): raise SolveError("There must be a L4E group of edges in the x-plane") if self.x_plane_edges_paired(): log.info( "%s: x-plane edges already paired, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)) ) return original_state = self.state[:] original_solution = self.solution[:] original_solution_len = len(self.solution) original_paired_edges_count = self.get_paired_edges_count() # The 4 paired edges are in the x-plane only_colors = [] for square_index in (36, 40, 86, 90): partner_index = edges_partner_555[square_index] square_value = self.state[square_index] partner_value = self.state[partner_index] wing_str = wing_str_map[square_value + partner_value] only_colors.append(wing_str) # Old way where we would IDA """ self.lt_edges_x_plane_edges_only.only_colors = only_colors self.lt_edges_x_plane.only_colors = only_colors # Recolor the centers in the x-plane to LFRB since LFRB was used to build our tables centers_recolor = { self.state[38] : "L", self.state[63] : "F", self.state[88] : "R", self.state[113] : "B", } for x in LFRB_centers_555: self.state[x] = centers_recolor[self.state[x]] # Recolor the edges to they are all oriented using their original orientation. # We do this because our tables were built will all edges at their original orientation. self.edges_flip_to_original_orientation() # Now we can solve self.lt_edges_x_plane.solve() """ # Recolor the edges to they are all oriented using their original orientation. # We do this because our tables were built will all edges at their original orientation. self.edges_flip_to_original_orientation() # Now we can solve self.lt_edges_x_plane_centers_solved.only_colors = only_colors self.lt_edges_x_plane_centers_solved.solve() # Put the cube back the way it was (to undo all of the recoloring we did) and apply the solution l4e_solution = self.solution[original_solution_len:] self.state = original_state[:] self.solution = original_solution[:] for step in l4e_solution: self.rotate(step) # self.print_cube() assert self.x_plane_edges_paired(), "4-edges in x-plane should have paired" self.solution.append( "COMMENT_%d_steps_555_L4E_paired" % self.get_solution_len_minus_rotates(self.solution[original_solution_len:]) ) log.info( "%s: x-plane edges paired in %d steps, %d steps in" % ( self, len(l4e_solution), self.get_solution_len_minus_rotates(self.solution), ) )
def stage_second_four_edges_555(self): """ The 1st 4-edges have been staged to LB, LF, RF, RB. Stage the next four edges to UB, UF, DF, DB (this in turn stages the final four edges). Since there are 8-edges there are 70 different combinations of edges we can choose to stage to UB, UF, DF, DB. Walk through all 70 combinations and see which one leads to the shortest solution. """ # return if they are already staged if self.y_plane_edges_are_l4e() and self.z_plane_edges_are_l4e(): return first_four_wing_strs = list(self.get_x_plane_wing_strs()) wing_strs_for_second_four = [] log.info("first_four_wing_strs %s" % pformat(first_four_wing_strs)) for wing_str in wing_strs_all: if wing_str not in first_four_wing_strs: wing_strs_for_second_four.append(wing_str) log.info("wing_strs_for_second_four %s" % pformat(wing_strs_for_second_four)) assert len(wing_strs_for_second_four) == 8 # Remember what things looked like original_state = self.state[:] original_solution = self.solution[:] original_solution_len = len(self.solution) min_solution_len = None min_solution_steps = None for pre_steps in pre_steps_to_try: self.state = original_state[:] self.solution = original_solution[:] for step in pre_steps: self.rotate(step) post_pre_steps_state = self.state[:] post_pre_steps_solution = self.solution[:] states_to_find = [] for wing_strs in itertools.combinations(wing_strs_for_second_four, 4): states_to_find.append(self.lt_edges_stage_second_four.state(wing_strs)) log.info("%s: %d states_to_find" % (self, len(states_to_find))) results = self.lt_edges_stage_second_four.binary_search_multiple( states_to_find ) len_results = len(results) # log.info(results) log.info("%s: %d states found" % (self, len(results))) # We sort the keys of the dict so that the order is the same everytime, this isn't # required but makes troubleshooting easier. for (line_number, key) in enumerate(sorted(results.keys())): steps = results[key] self.state = post_pre_steps_state[:] self.solution = post_pre_steps_solution[:] for step in steps.split(): self.rotate(step) solution_steps = self.solution[original_solution_len:] solution_len = len(solution_steps) if min_solution_len is None or solution_len < min_solution_len: log.info( "%s: %d/%d 2nd 4-edges can be staged in %d steps %s (NEW MIN)" % ( self, line_number + 1, len_results, solution_len, " ".join(solution_steps), ) ) min_solution_len = solution_len min_solution_steps = solution_steps else: log.info( "%s: %d/%d 2nd 4-edges can be staged in %d steps" % (self, line_number + 1, len_results, solution_len) ) if min_solution_len is not None: self.state = original_state[:] self.solution = original_solution[:] for step in min_solution_steps: self.rotate(step) break self.state = original_state[:] self.solution = original_solution[:] if min_solution_len is None: raise SolveError("Could not find 4-edges to stage") else: for step in min_solution_steps: self.rotate(step) self.solution.append( "COMMENT_%d_steps_555_second_L4E_edges_staged" % self.get_solution_len_minus_rotates(self.solution[original_solution_len:]) ) log.info( "%s: 2nd 4-edges staged to x-plane, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)) )
def stage_first_four_edges_555(self): """ There are 495 different permutations of 4-edges out of 12-edges, use the one that gives us the shortest solution for getting 4-edges staged to LB, LF, RF, RB """ # return if they are already staged if self.x_plane_edges_are_l4e(): log.info("%s: first L4E group in x-plane" % self) return if self.y_plane_edges_are_l4e(): log.info("%s: first L4E group in y-plane, moving to x-plane" % self) self.rotate("z") return if self.z_plane_edges_are_l4e(): log.info("%s: first L4E group in z-plane, moving to x-plane" % self) self.rotate("x") return min_solution_len = None min_solution_steps = None # The table for staging the 1st 4-edges would have 364,058,145 if built to completion. # Building that table the entire way is difficult though because this is a table where # the centers must be kept solved...so this involves building out a HUGE table and only # keeping the entries where the centers are solved. To build one deep enough to find # all 364,058,145 entries needed that also have solved centers would probably take a # few months and more drive space than I have access to. # # To avoid building such a massive table we only build the table out 10-deep which gives # us a few million entries. We then try all 495 permutations of 4-edges out of 12-edges # looking for one that does have a hit. Most of the time this is all that is needed and # we can find a hit. On the off chance that we cannot though we need a way to find a solution # so what we do is try all outer layer moves up to 3 moves deep and see if any of those # sequences put the cube in a state such that one of the 495 edge permutations does find # a hit. I have yet to find a cube that cannot be solved with this approach but if I did # the pre_steps_to_try could be expanded to 4-deep. # Remember what things looked like original_state = self.state[:] original_solution = self.solution[:] original_solution_len = len(self.solution) min_solution_len = None min_solution_steps = None for pre_steps in pre_steps_to_try: self.state = original_state[:] self.solution = original_solution[:] for step in pre_steps: self.rotate(step) post_pre_steps_state = self.state[:] post_pre_steps_solution = self.solution[:] states_to_find = [] for wing_strs in itertools.combinations(wing_strs_all, 4): states_to_find.append(self.lt_edges_stage_first_four.state(wing_strs)) log.info("%s: %d states_to_find" % (self, len(states_to_find))) results = self.lt_edges_stage_first_four.binary_search_multiple(states_to_find) len_results = len(results) log.info("%s: %d states found" % (self, len(results))) # We sort the keys of the dict so that the order is the same everytime, this isn't # required but makes troubleshooting easier. for (line_number, key) in enumerate(sorted(results.keys())): steps = results[key] self.state = post_pre_steps_state[:] self.solution = post_pre_steps_solution[:] for step in steps.split(): self.rotate(step) self.stage_final_four_edges_in_x_plane() solution_steps = self.solution[original_solution_len:] solution_len = self.get_solution_len_minus_rotates(solution_steps) # Technically we only need 4 edges to be pairable for the next phase but 5 is nice because it gives # the next phase some wiggle room...it can choose the best 4-edge tuple. if min_solution_len is None or solution_len < min_solution_len: log.info( "%s: %d/%d 1st 4-edges can be staged in %d steps %s (NEW MIN)" % ( self, line_number + 1, len_results, solution_len, " ".join(solution_steps), ) ) min_solution_len = solution_len min_solution_steps = solution_steps else: log.info( "%s: %d/%d 1st 4-edges can be staged in %d steps" % (self, line_number + 1, len_results, solution_len) ) if min_solution_len is not None: self.state = original_state[:] self.solution = original_solution[:] for step in min_solution_steps: self.rotate(step) break if not self.x_plane_edges_are_l4e(): raise SolveError("There should be an L4E group in x-plane but there is not") # self.print_cube() self.solution.append( "COMMENT_%d_steps_555_first_L4E_edges_staged" % self.get_solution_len_minus_rotates(self.solution[original_solution_len:]) ) log.info( "%s: 1st 4-edges staged to x-plane, %d steps in" % (self, self.get_solution_len_minus_rotates(self.solution)) )