def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ left_checked = {} right_checked = {} left_current = {start: []} right_current = {end: []} left_new = {} right_new = {} left_depth = 0 right_depth = 0 while left_depth < 8: for x in left_current.keys(): y = right_current.get(x, None) if y is not None: ans = left_current[x] + [ rubik.perm_inverse(move) for move in reversed(y) ] return ans for x in left_current.keys(): for move in rubik.quarter_twists: r = rubik.perm_apply(move, x) if not left_checked.get(r, None) and not left_current.get( r, None): left_new[r] = left_current[x] + [move] left_checked[x] = left_current[x] left_current = left_new left_new = {} left_depth += 1 while right_depth < 8: for x in right_current.keys(): y = left_current.get(x, None) if y is not None: ans = y + [ rubik.perm_inverse(move) for move in reversed(right_current[x]) ] return ans for x in right_current.keys(): for move in rubik.quarter_twists: r = rubik.perm_apply(move, x) if not right_checked.get(r, None) and not right_current.get( r, None): right_new[r] = right_current[x] + [move] right_checked[x] = right_current[x] right_current = right_new right_new = {} right_depth += 1 return None
def calculate_path(start_parent, end_parent, position): """ Assumes start_parent and end_parent are as specified in shortest_path. Assumes position is in both start_parent and end_parent. Returns a list of moves to get from start to end. """ start_half = [] current_position = position while (start_parent[current_position] is not None): pair = start_parent[current_position] parent_position = pair[0] move = pair[1] current_position = parent_position # move inserted at beginning, because we are walking back to start. start_half.insert(0, move) end_half = [] current_position = position while (end_parent[current_position] is not None): pair = end_parent[current_position] parent_position = pair[0] move = pair[1] # Inverse is taken, because move wass originally away from end. inverse_move = rubik.perm_inverse(move) current_position = parent_position end_half.append(inverse_move) return start_half + end_half
def calculate_path(start_parent, end_parent, position): """ Assumes start_parent and end_parent are as specified in shortest_path. Assumes position is in both start_parent and end_parent. Returns a list of moves to get from start to end. """ start_half = [] current_position = position while(start_parent[current_position] is not None): pair = start_parent[current_position] parent_position = pair[0] move = pair[1] current_position = parent_position # move inserted at beginning, because we are walking back to start. start_half.insert(0, move) end_half = [] current_position = position while(end_parent[current_position] is not None): pair = end_parent[current_position] parent_position = pair[0] move = pair[1] # Inverse is taken, because move wass originally away from end. inverse_move = rubik.perm_inverse(move) current_position = parent_position end_half.append(inverse_move) return start_half + end_half
def shortest_path(start, end): if start == end: return [] fparents = {start: None} bparents = {end: None} fmoves = {} bmoves = {} for move in rubik.quarter_twists: fmoves[move] = move bmoves[rubik.perm_inverse(move)] = move forward = (fmoves, fparents, bparents) backward = (bmoves, bparents, fparents) queue = deque([(start, forward), (end, backward), None]) for i in xrange(7): while True: vertex = queue.popleft() if vertex is None: queue.append(None) break position = vertex[0] moves, parents, other_parents = vertex[1] for move in moves: nextp = rubik.perm_apply(move, position) if nextp not in parents: parents[nextp] = (moves[move], position) queue.append((nextp, vertex[1])) if nextp in other_parents: forwardp = path(nextp, fparents) backwardp = path(nextp, bparents) forwardp.reverse() return forwardp + backwardp return None
def shortest_path_optmized(start, end): """ For Question 2, using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ movesStart = dict() movesStart[start] = [] movesEnd = dict() movesEnd[end] = [] qStart = collections.deque() qEnd = collections.deque() qStart.append(start) qEnd.append(end) found = False finalMoves = [] while (len(qStart) != 0 and len(qEnd) != 0 and not found): s = qStart.popleft() e = qEnd.popleft() m = movesStart[s] n = movesEnd[e] if len(m) > 8 and len(n) > 8: break if s == end: finalMoves = m found = True break if s in movesEnd: finalMoves = m + movesEnd[s][::-1] found = True break if e in movesStart: finalMoves = movesStart[e] + n[::-1] found = True break for i in range(6): x = rubik.perm_apply(rubik.quarter_twists[i], s) y = rubik.perm_apply(rubik.quarter_twists[i], e) if x not in movesStart: qStart.append(x) movesStart[x] = m + [ rubik.quarter_twists_names[rubik.quarter_twists[i]] ] if y not in movesEnd: qEnd.append(y) movesEnd[y] = n + [ rubik.quarter_twists_names[rubik.perm_inverse( rubik.quarter_twists[i])] ] if found: sys.stdout.write(str(finalMoves) + "\n") return finalMoves else: sys.stdout.write(str("No solution\n")) return [None]
def adj(u, dir): adj_u = [] for move in rubik.quarter_twists: if(dir): adj_u.append(rubik.perm_apply(move, u)) else: adj_u.append(rubik.perm_apply(rubik.perm_inverse(move), u)) return adj_u
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ if not (legal(start) or legal(end)): return None if start==end: return[] f_visited={} b_visited={} f_visited[start] = {'parent':None, 'move':None} b_visited[end] = {'parent':None, 'move':None} forward_frontier=[start] backward_frontier=[end] count=0 ans=None while count<8: f_next=[] b_next=[] BFS(forward_frontier, f_visited, f_next) BFS(backward_frontier, b_visited, b_next) for seq in f_next: if not b_visited.get(seq, False): continue else: ans=seq break forward_frontier=f_next backward_frontier=b_next count+=1 if ans!=None: break if ans==None: return ans answer=[] value=f_visited[ans] while value['move']: answer.append(value['move']) value=f_visited[value['parent']] answer=answer[::-1] value = b_visited[ans] while value['move']: answer.append(rubik.perm_inverse(value['move'])) value=b_visited[value['parent']] return answer
def find_move(u, v, dir): for move in rubik.quarter_twists: if(dir): if v == rubik.perm_apply(move, u): return move else: if v == rubik.perm_apply(rubik.perm_inverse(move), u): return move return -1
def build_path(parents, start_config, current_config, path, backward): """ Shamelessly stolen from CLRS (where it's called PRINT_PATH). """ if current_config == start_config: # Bottom of the recursion. We've reached our goal config, so we don't need # to record it - we'll get the permutation required to GET to our goal config # when we step back up the call stack. pass else: next_config = rubik.perm_apply( rubik.perm_inverse(parents[current_config]), current_config) build_path(parents, start_config, next_config, path, backward) if backward: # We don't add the configuration to the path, we add the PERMUTATION # required to GET to the next configuration. path.append(rubik.perm_inverse(parents[current_config])) else: path.append(parents[current_config])
def get_moves(parentS, parentE, position): start_moves = [] current_position = position while parentS[current_position] is not None: (current_position, move) = parentS[current_position] start_moves.insert(0, move) end_moves = [] current_position = position while parentE[current_position] is not None: (current_position, move) = parentE[current_position] end_moves.append(rubik.perm_inverse(move)) return start_moves + end_moves
def two_way_bfs(start, end): if start == end: return [] # initialize moves for backward bfs' forward_moves = dict() backward_moves = dict() for move in rubik.quarter_twists: forward_moves[move] = move backward_moves[rubik.perm_inverse(move)] = move # item format # next_position: (twist, position) forward_parents = {start: None} backward_parents = {end: None} # set indicators for forward and backward # (moves, parents, other parents) forward = (forward_moves, forward_parents, backward_parents) backward = (backward_moves, backward_parents, forward_parents) # every depth level ends with None as indicator queue = deque([(start, forward), (end, backward), None]) for i in range(7): while True: vertex = queue.popleft() if vertex is None: # next depth level queue.append(None) break position = vertex[0] moves, parents, other_parents = vertex[1] for move in moves: next_position = rubik.perm_apply(move, position) if next_position in parents: # do not bother with cycles continue parents[next_position] = (moves[move], position) queue.append((next_position, vertex[1])) if next_position in other_parents: forward_path = path(next_position, forward_parents) backward_path = path(next_position, backward_parents) return forward_path + backward_path[::-1]
def shortest_path_optmized(start, end): q1 = deque() q2 = deque() visited1 = {} visited2 = {} path1 = {} path2 = {} visited1[start] = 1 visited2[end] = 1 path1[start] = [] path2[end] = [] q1.append(start) q2.append(end) while (q1 and q2): curr1 = q1[0] curr2 = q2[0] q1.popleft() q2.popleft() for i in rubik.quarter_twists: p1 = rubik.perm_apply(curr1, i) if (visited1.has_key(p1) == False): visited1[p1] = 1 path1[p1] = path1[curr1] + [rubik.quarter_twists_names[i]] #print(rubik.quarter_twists_names[i]) q1.append(p1) if (visited2.has_key(p1) == True): return path1[p1] + path2[p1] for i in rubik.quarter_twists: p2 = rubik.perm_apply(curr2, i) if (visited2.has_key(p2) == False): visited2[p2] = 1 path2[p2] = [ rubik.quarter_twists_names[rubik.perm_inverse(i)] ] + path2[curr2] #print(rubik.quarter_twists_names[i]) q2.append(p2) if (visited1.has_key(p2) == True): return path1[p2] + path2[p2] return [] """ For Question 2, using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ raise NotImplementedError
def construct_path_two_ends(start_end, parent_from_start, end_end, parent_from_end): position = start_end result = [] while parent_from_start[position] is not None: result.append(position[1]) position = parent_from_start[position] result.reverse() position = end_end while parent_from_end[position] is not None: result.append(rubik.perm_inverse(position[1])) position = parent_from_end[position] return result
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ level = {start:0} backLevel = {end:0} parent = {start:None} backParent = {end:None} i = 1 frontier = [start] solution = [] backFrontier = [end] connected = False connections = [] while (not connected) and (i <= 7): # explore graph next = [] for u in frontier: # move forwards for move in rubik.quarter_twists: v = rubik.perm_apply(move,u) if v not in level: level[v] = i parent[v] = (u,move) next.append(v) frontier = next backNext = [] for u in backFrontier: # move backwards for move in rubik.quarter_twists: v = rubik.perm_apply(move,u) if v not in backLevel: backLevel[v] = i backParent[v] = (u,rubik.perm_inverse(move)) backNext.append(v) backFrontier = backNext for config in level.keys(): # check for a solution if config in backLevel: connected = True connections.append(config) i += 1 if not connected: # we have explored all options and found no solution return None minConn = findShortestPath(connections,level,backLevel) buildSolution(level,parent,minConn,solution,'forwards') buildSolution(backLevel,backParent,minConn,solution,'backwards') return solution
def bidirectional_bfs_search( start, end=rubik.I, next_fun=lambda x: [(rubik.perm_apply(t, x), t) for t in rubik.quarter_twists], rev_edge=lambda e: rubik.perm_inverse(e), ): """ next_fun should return a list of tuples [(neighbour, edge)]. rev_edge should return the reverse of an edge (so we can reverse when joining final path) """ # Setup print(f"Start: {start}") if start == end: return [] move_before = {start: None} # Gets the last move before given state. move_before_reverse = { end: None } # Same but for the opposite end of the search. # When we do the search, this lets us know which dictionary to use. (reverse or not.) forward, backward = ( (start, move_before, move_before_reverse), (end, move_before_reverse, move_before), ) # Search q = deque([forward, backward, None]) # None helps keep track of level for i in range(GODS_NUMBER // 2): while True: node = q.popleft() if node is None: q.append(None) break cur_state, cur_moves, other_moves = node[0], node[1], node[2] for next_state, edge in next_fun(cur_state): if next_state in cur_moves: continue # We already recorded this state. cur_moves[next_state] = edge q.append((next_state, cur_moves, other_moves)) # next_state is the common 'middle' node, and we have to join 2 paths to find it. if next_state in other_moves: first_path = _path(start, next_state, move_before, rev_edge) second_path = _path(end, next_state, move_before_reverse, rev_edge) second_path.reverse() return first_path + [rev_edge(e) for e in second_path] raise ValueError("Invalid starting point")
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ processed_f = set() processed_b = set() parent_f = {start: None} parent_b = {end: None} frontier_f = [start] frontier_b = [end] isforward = True for i in range(16): frontier = frontier_f if isforward else frontier_b parent = parent_f if isforward else parent_b processed = processed_f if isforward else processed_b next_ = [] for u in frontier: process(u, parent, next_) processed.add(u) break_ = (u in processed_b) if isforward else (u in processed_f) if break_: x = u break if break_: break if isforward: frontier_f = next_ else: frontier_b = next_ isforward = not isforward else: return None path = [] u = x while u != start: path.insert(0, parent_f[u][0]) u = parent_f[u][1] u = x while u != end: path.append(rubik.perm_inverse(parent_b[u][0])) u = parent_b[u][1] return path
def bfs(s, e): front_parents = {s: None} back_parents = {e: None} # front_moves = rubik.quarter_twists #back_moves = (rubik.perm_inverse(move) for move in rubik.quarter_twists) front_moves={} back_moves = {} for move in rubik.quarter_twists: front_moves[move] = move back_moves[rubik.perm_inverse(move)] = move front = (front_moves, front_parents, back_parents) back = (back_moves, back_parents, front_parents) queue = deque([(s, front), (e, back), None]) for i in range(7): while True: node = queue.popleft() if node is None: # One Level of BFS complete queue.append(None) break current_node = node[0] moves, parents, other_parents = node[1] for move in moves: next_node = rubik.perm_apply(move, current_node) if next_node in parents: continue parents[next_node] = (moves[move], current_node) queue.append((next_node, node[1])) if next_node in other_parents: return (front_parents, back_parents, next_node) return None """
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ if start == end: return [] forward_parents = {start: None} backward_parents = {end: None} forward_moves = {} backward_moves = {} for move in rubik.quarter_twists: forward_moves[move] = move backward_moves[rubik.perm_inverse(move)] = move forward = (forward_moves, forward_parents, backward_parents) backward = (backward_moves, backward_parents, forward_parents) queue = deque([(start, forward), (end, backward), None]) for i in range(7): while True: vertex = queue.popleft() if vertex is None: queue.append(None) break position = vertex[0] moves, parents, other_parents = vertex[1] for move in moves: next_position = rubik.perm_apply(move, position) if next_position in parents: continue parents[next_position] = (moves[move], position) queue.append((next_position, vertex[1])) if next_position in other_parents: forward_path = path(next_position, forward_parents) backward_path = path(next_position, backward_parents) backward_path.reverse() return forward_path + backward_path return None
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ if start == end: return [] forward = ([start], {start : []}) backward = ([end], {end : []}) for iter in range(7): (forward_states, forward_moves) = forward (backward_states, backward_moves) = backward next_forward_states = [] for state in forward_states: moves = forward_moves[state] for twist in rubik.quarter_twists: next_state = rubik.perm_apply(twist, state) if next_state in backward_moves: return moves + [twist] + backward_moves[next_state] if not next_state in forward_moves: next_forward_states.append(next_state) forward_moves[next_state] = moves + [twist] next_backward_states = [] for state in backward_states: moves = backward_moves[state] for twist in rubik.quarter_twists: next_state = rubik.perm_apply(rubik.perm_inverse(twist), state) if next_state in forward_moves: return forward_moves[next_state] + [twist] + moves if not next_state in backward_moves: next_backward_states.append(next_state) backward_moves[next_state] = [twist] + moves forward = (next_forward_states, forward_moves) backward = (next_backward_states, backward_moves) return None
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ if start == end: return [] forward = ([start], {start: []}) backward = ([end], {end: []}) for iter in range(7): (forward_states, forward_moves) = forward (backward_states, backward_moves) = backward next_forward_states = [] for state in forward_states: moves = forward_moves[state] for twist in rubik.quarter_twists: next_state = rubik.perm_apply(twist, state) if next_state in backward_moves: return moves + [twist] + backward_moves[next_state] if not next_state in forward_moves: next_forward_states.append(next_state) forward_moves[next_state] = moves + [twist] next_backward_states = [] for state in backward_states: moves = backward_moves[state] for twist in rubik.quarter_twists: next_state = rubik.perm_apply(rubik.perm_inverse(twist), state) if next_state in forward_moves: return forward_moves[next_state] + [twist] + moves if not next_state in backward_moves: next_backward_states.append(next_state) backward_moves[next_state] = [twist] + moves forward = (next_forward_states, forward_moves) backward = (next_backward_states, backward_moves) return None
def construct_path(overlap, Ldict, Rdict): Lpath = [] Rpath = [] Lcurr = overlap Rcurr = overlap # starting with overlap, prepend moves to the left list until the start is found while True: if Ldict[Lcurr][0] == 's': break Lpath.insert(0, Ldict[Lcurr][0]) Lcurr = Ldict[Lcurr][1] # starting with overlap, append inverse moves to the right list until the end is found while True: if Rdict[Rcurr][0] == 'e': break Rpath.append(rubik.perm_inverse(Rdict[Rcurr][0])) Rcurr = Rdict[Rcurr][1] # return right list appended to left list for the complete moveset return Lpath + Rpath
def solution(startParents, endParents, state): moves = [] # Array to store sequence of moves currentState = state # Intermediate state of rubiks cube # Working way back up to start state while startParents[currentState] is not None: parent = startParents[currentState] # Parent state + move to current state currentState = parent[0] # Move one level towards initial state move = parent[1] moves.insert(0, move) # Store moves in FILO (Start -> Intermediate) currentState = state # Return to intermediate state of rubiks cube # Working way back down to end state while endParents[currentState] is not None: parent = endParents[currentState] # Parent state + move to current state currentState = parent[0] # Move on level towards end state move = parent[1] moves.append(rubik.perm_inverse(move)) # Store inverse of moves in FIFO (Intermediate -> End) return moves
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start to end position. Returns a list of moves and assumes the rubik.quarter_twists move set. """ if start == end: return [] #if no shortest path exists (start==end), return empty list fparents = {start: None} #first half parents sparents = {end: None} #second half parents fmoves = {} #first half moves smoves = {} #second half moves for turn in rubik.quarter_twists: fmoves[turn] = turn #insert forward moves from quarter_twists smoves[rubik.perm_inverse(turn)] = turn #insert reverse moves (starting from second half) forward = (fmoves, fparents, sparents) #set of moves and first/second half parents backward = (smoves, sparents, fparents) #set of inverse moves with first/second half parents Q = deque([(start, forward), (end, backward), None]) #create new deque(quicklist) Q = (first half, second half, None) for x in range(7): #for each move in quarter_twist while True: #loop v = Q.popleft() #return leftmost element from Q if v is None: #if at the end of Q (None) Q.append(None) #add None to Q break #break from loop position = v[0] #set vertex to start or end moves, parents, otherparents = v[1] #set moves, parents, other parents to forward or backward for i in moves: #start two way BFS nextpos = rubik.perm_apply(i, position) #set node to search if nextpos in parents: continue #if node in parents, continue parents[nextpos] = (moves[i], position) #set parent pointer to node, vertex Q.append((nextpos, v[1])) #add expanded search to Q if nextpos in otherparents: #if node discovered in both searches fpath = path(nextpos, fparents) #forward path set with node + parent pointers spath = path(nextpos, sparents) #backward path set with node + parent pointers spath.reverse() #must reverse the inverse path before joining it with forward path return fpath + spath #return list of moves for the shortest path (forward + backward moves) return None #else return None
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ # raise NotImplementedError forward_dict = {start: []} backward_dict = {end: []} forward_path, backward_path = two_way_trans(forward_dict, backward_dict, 0) if forward_path is None and backward_path is None: return None margin_path = [] for iterator in forward_path: if iterator != []: margin_path.append(iterator) for i in range(len(backward_path) - 1, -1, -1): if backward_path[i] != []: margin_path.append(rubik.perm_inverse(backward_path[i])) return margin_path
def shortest_path_optmized(start, end): """ For Question 2, using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ # simultaneous bfs from both the ends and maintain visted_nodes_1 and 2. # when node is discovered while bfs of start check if it is present in visite_nodes_2 and vice versa to check meeting point # SPECIAL CASE, better not to do all the next if unneeded if start == end: return [] # INITIALISATION # returned list of moves result_path = [] # bfs distance and recovery dictionaries dist_front , recover_front = {} , {} dist_back , recover_back = {} , {} # bfs queues, using deque instead of list to have O(1) removal of 1st element q_front = __import__('collections').deque() q_back = __import__('collections').deque() # SETTING UP # setting up bfs from front q_front.append(start) dist_front[start] = 0 recover_front[start] = (None,None) # setting up bfs from back q_back.append(end) dist_back[end] = 0 recover_back[end] = (None,None) # setting up the intersection point intersection_pt = None # BFS LOOP - runs till both bfs intersect done = False # while not done: # state to act on in front bfs curr = q_front.popleft() # If 7 levels have been visited and no intersection, # no solution. if 7 < dist_front[curr]: break # visiting all neighbours for move in rubik.quarter_twists: # find neighbour by applying move neighbour = rubik.perm_apply(move, curr) # if this has not been explored yet if neighbour not in dist_front: dist_front[neighbour] = dist_front[curr] + 1 recover_front[neighbour] = (curr, move) q_front.append(neighbour) # if this has been explored from back, then intersection if neighbour in dist_back: intersection_pt = neighbour done = True break # no need to continue back bfs if intersection found if done: break # state to act on in back bfs curr = q_back.popleft() # If 7 levels have been visited and no intersection, # no solution. if 7 < dist_back[curr]: break # visiting all neighbours for move in rubik.quarter_twists: # find neighbour by applying move neighbour = rubik.perm_apply(move, curr) # if this has not been explored yet if neighbour not in dist_back: dist_back[neighbour] = dist_back[curr] + 1 recover_back[neighbour] = (curr, move) q_back.append(neighbour) # if this has been explored from front, then intersection if neighbour in dist_front: intersection_pt = neighbour done = True break # SPECIAL CASE: No solution if intersection_pt == None: return None # BACKTRACKING # recover moves from start to current state # using recovery list of front bfs ptr = intersection_pt while ptr != start: result_path.append(recover_front[ptr][1]) ptr = recover_front[ptr][0] result_path = result_path[::-1] # recover moves from current state to end # using recovery list of back bfs ptr = intersection_pt while ptr != end: result_path.append(rubik.perm_inverse(recover_back[ptr][1])) ptr = recover_back[ptr][0] # SANITY CHECK cube = start[:] for move in result_path: cube = rubik.perm_apply(move, cube) assert cube == end return list(map(lambda x: rubik.quarter_twists_names[x], result_path))
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ if start==end: return [] startroot=node(start) endroot=node(end) startq=queue([startroot,None]) endq=queue([endroot,None]) starth=[] endh=[] for i in range(0,335923): starth.append([]) endh.append([]) starth[hash(start)].append((start,None,None)) endh[hash(end)].append((end,None,None)) #startd[startroot.list]=(startroot.list,startroot.parent,startroot.orientation) #endd[endroot.list]=(endroot.list,endroot.parent,endroot.orientation) move=[rubik.F,rubik.Fi,rubik.L,rubik.Li,rubik.U,rubik.Ui] i=0 while i<7: while True: g=startq.dequeue() if g is None: startq.enqueue(None) break j=0 for k in move: m=rubik.perm_apply(k,g.list) if search(m,starth[hash(m)]) is None: g.child[j]=node(m,g,k) starth[hash(m)].append((m,g,k)) startq.enqueue(g.child[j]) if search(m,endh[hash(m)]) is not None: fr=path(m,starth[hash(m)]) br=path(m,endh[hash(m)]) fr.reverse() return fr + br j+=1 while True: g=endq.dequeue() if g is None: endq.enqueue(None) break j=0 for k in move: m=rubik.perm_apply(rubik.perm_inverse(k),g.list) if search(m,endh[hash(m)]) is None: g.child[j]=node(m,g,k) endh[hash(m)].append((m,g,k)) endq.enqueue(g.child[j]) if search(m,starth[hash(m)]) is not None: fr=path(m,starth[hash(m)]) br=path(m,endh[hash(m)]) fr.reverse() return fr + br j+=1 i+=1 #print i return None
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ if start == end: return [] # keep track of states that have been expanded from the start parent = {start: None} # child_node: (parent_node, perm) # where perm_apply(parent_node) = child_node # keep track of states that have been expanded from the end child = {end: None} # parent_node: (child_node, perm) # where perm_appry(parent_node) = child_node Q = [start] # first in, first out queue for forward expansion R = [end] # first in, first out queue for backward expansion count = 0 found = None while count < 6000: # forward expansion current_state = Q.pop() # dequeue for twist in rubik.quarter_twists: # explore current_state state = rubik.perm_apply(twist, current_state) if state not in parent: parent[state] = (current_state, twist) Q = [state] + Q # forward expansion done once # if end node is found if end in parent: return [parent[end][1]] # backward expansion current_state = R.pop() # dequeue for twist in rubik.quarter_twists: state = rubik.perm_apply(rubik.perm_inverse(twist), current_state) if state not in child: child[state] = (current_state, twist) R = [state] + R if state in parent: found = state break # backward expansion done once if found is not None: break count += 1 if found is None: return None result = [] current_state = found while current_state != start: pred_pair = parent[current_state] result = [pred_pair[1]] + result current_state = pred_pair[0] current_state = found while current_state != end: succ_pair = child[current_state] result = result + [succ_pair[1]] current_state = succ_pair[0] return result
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply The forward and backward search use exactly the same code, so it seems prudent to refactor them into a function. However, the number of variables that must be passed in order for this to work makes it seem like a clumsy solution. I wonder if there's something better... """ if start == end: # Length 0 path. return [] # Initialize level counters and parent pointers. level = {start: 0, end: 0} forward_parent = {start: None} backward_parent = {end: None} # Initialize forward and backward search frontiers. i = 1 found = False middle = None forward_frontier = [start] backward_frontier = [end] # Keep track of whether a given node has been discovered by both searches. found_by_forward_search = set() found_by_forward_search.add(start) found_by_backward_search = set() found_by_backward_search.add(end) # Once the same node has been touched by both searches, we're done. # Checking only out to level 9 is an ugly hack! There has to be a better way to # determine if there's no solution. I've read some things about permutation parity # and checking corner twists, but I'm not sure how to do that. while (forward_frontier or backward_frontier) and not found and i < 9: # Reset next level of both search frontiers. next_forward = [] next_backward = [] # Search forward frontier (one level). for config in forward_frontier: # Find the config's neighbors by applying all possible moves. for permutation in rubik.quarter_twists: neighbor = rubik.perm_apply(permutation, config) # Avoid duplicates. if neighbor not in forward_parent: # Set the level and parent of the neighbor. level[neighbor] = i forward_parent[neighbor] = permutation # Add the neighbor to the new search frontier. next_forward.append(neighbor) # Search backward frontier (one level). for config in backward_frontier: # Find the config's neighbors by applying all possible moves. for permutation in rubik.quarter_twists: neighbor = rubik.perm_apply(permutation, config) # Avoid duplicates. if neighbor not in backward_parent: # Set the level and parent of the neighbor. level[neighbor] = i backward_parent[neighbor] = permutation # Add the neighbor to the new search frontier. next_backward.append(neighbor) # Check to see if any neighbors have been discovered by both searches. If one # has, we're done. found_by_forward_search = found_by_forward_search.union( set(next_forward)) found_by_backward_search = found_by_backward_search.union( set(next_backward)) found_by_both = found_by_forward_search.intersection( found_by_backward_search) if found_by_both: found = True middle = found_by_both.pop() # Move to the new search frontiers, one level deeper. forward_frontier = next_forward backward_frontier = next_backward i += 1 # Return the shortest path between the start and end configurations. if middle is None: # No path exists between the two configurations. return None elif middle == start or middle == end: # Path of length 1. if middle == start: return [rubik.perm_inverse(backward_parent[middle])] else: return [forward_parent[middle]] else: # Path of length >1. moves_to_end = build(backward_parent, end, middle, backward=True) moves_to_end.reverse() moves_to_start = build(forward_parent, start, middle, backward=False) path = moves_to_start path.extend(moves_to_end) return path
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ # Start side of BFS start_init = node_info(start, (None, None), 0) s_frontier = deque([start_init]) s_parents = [] # End side of BFS end_init = node_info(end, (None, None), 0) e_frontier = deque([end_init]) e_parents = [] # Flag determines which side we need to advance the frontier # When flag is 1 it means that we might advance the left frontier # When flag is -1 it means we might advance the right frontier flag = 1 # Test to see if the two given states (start, end) are the same # We know to return an empty list """ Invariant: At each iteration, the position of l_elm in s_frontier is less than the len(s_frontier) Initialization: There is a list called s_frontier that has elements in it Maintenance: Given that there are still elements in s_frontier to go through, the loop will continue Termination: We have reached the end of s_frontier or the two given states are the same, thus returning an empty list because no path needs to be found """ for l_elm in s_frontier: """ Invariant: At each iteration, the position of r_elm in e_frontier is less than the len(e_frontier) Initialization: There is a list called e_frontier that has elements in it Maintenance: Given that there are still elements in e_frontier to go through, the loop will continue and check to see if the the states are the same Termination: We have reached the end of e_frontier or the two given states are the same, thus returning an empty list because no path needs to be found """ for r_elm in e_frontier: if l_elm.st == r_elm.st: return [] found = False """ Invariant: At each iteration, the solution has not been found Initialization: found = False Maintenance: Given that we have not found a solution, the function will alternate between the start and end sides (bidirectional) to create a new frontier and check its values for a solution Termination: The cube is unsolvable (order exceeds 7) or we have found a solution """ # Run while False while not found: # Checks the depth of the nodes in both frontiers # If their order exceeds 7, then there is no shortest path if s_frontier[0].order > 6 and e_frontier[0].order > 6: return None # Flag alternates the sides if flag == 1: # Gets the next frontier next_frontier(s_frontier, s_parents) flag = flag * (-1) else: # Gets the next frontier next_frontier(e_frontier, e_parents) flag = flag * (-1) # Checking to see if there is the same block state in each list """ Invariant: At each iteration, the position of l_elm in s_frontier is less than the len(s_frontier) Initialization: There is a list called s_frontier that has elements in it Maintenance: Given that there are still elements in s_frontier to go through, the loop will continue Termination: We have reached the end of s_frontier or the intersection of the two ends has been found """ # Grabs a left element from the starting frontier for l_elm in s_frontier: """ Invariant: At each iteration, the position of r_elm in e_frontier is less than the len(e_frontier) Initialization: There is a list called e_frontier that has elements in it Maintenance: Given that there are still elements in e_frontier to go through, the loop will continue and check to see if s_frontier and e_frontier has an intersection Termination: We have reached the end of e_frontier or the intersection of the two ends has been found """ # Compares it to each of the right elements from the end frontier for r_elm in e_frontier: # If we find an element that matches if l_elm.st == r_elm.st: # Find the intersect of the two lists intersect = (l_elm, r_elm) # We set found to true found = True # Then break out of the loop break # If the element was found we can break out of this loop as well if found == True: break # Creates a lsit for return final_list = [] # Temp variable to hold l_elm l_temp = intersect[0] """ Invariant: At each iteration, there are still nodes to be added to the final solution list Initialization: The current node does not equal the first parent node of start Maintenance: Given that we have not reached the first parent node of start, the parent of the current node is added to the beginning of the final solution list. The new node to look at is then updated. Termination: We have reached the first parent node of start and now have a list of its path """ # Gets the parent of l_temp (l_elm) until the start state while (l_temp.st != start): final_list.insert(0, l_temp.parent[0]) l_temp = l_temp.parent[1] r_temp = intersect[1] """ Invariant: At each iteration, there are still nodes to be added to the final solution list Initialization: The current node does not equal the first parent node of end Maintenance: Given that we have not reached the first parent node of end, the parent of the current node is added to the end of the final solution list. The new node to look at is then updated. Termination: We have reached the first parent node of end and now have a list of its path connecting start to end """ # Gets the parent of r_temp (r_elm) until the end state # Perm inverse is because we have to do the inverse of each move to get the parent. while (r_temp.st != end): final_list.append(rubik.perm_inverse(r_temp.parent[0])) r_temp = r_temp.parent[1] return final_list
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ if start == end: return [] queueF = deque([start]) queueB = deque([end]) visited = {} forwardParents = {} backwardParents = {} intersection = [] counter = 0 queuedItemsF = {} queuedItemsB = {} queuedItemsF[start] = 1 queuedItemsB[end] = 1 forward_moves = {} for move in rubik.quarter_twists: forward_moves[move] = move while len(intersection) == 0 and counter < 3047: currF = queueF.popleft() currB = queueB.popleft() queuedItemsF.pop(currF) queuedItemsB.pop(currB) #Forwards for twist in forward_moves: config = rubik.perm_apply(twist, currF) if config not in visited and config not in queuedItemsF: forwardParents[config] = (twist, currF) if config == end: intersection.append(config) break queueF.append(config) queuedItemsF[config] = 1 #Backwards for twist in forward_moves: config = rubik.perm_apply(twist, currB) if config not in visited and config not in queuedItemsB: backwardParents[config] = (twist, currB) queueB.append(config) queuedItemsB[config] = 1 visited[currF] = 1 visited[currB] = 1 intersect = list(set(queueF).intersection(queueB)) if len(intersect) != 0: intersection.append(intersect[0]) counter += 1 if len(intersection) == 0: return None ans = [] curr = intersection[0] while start != curr: twist, curr = forwardParents[curr] ans.insert(0, twist) curr = intersection[0] while end != curr: twist, curr = backwardParents[curr] ans.append(rubik.perm_inverse(twist)) return ans
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ if start==end: return [] startroot=node(start) endroot=node(end) startq=queue([startroot,None]) endq=queue([endroot,None]) startd={} startd[start]=(start,None,None) endd={} endd[end]=(end,None,None) i=0 while i<7: while True: g=startq.dequeue() if g==None: startq.enqueue(None) break if rubik.perm_apply(rubik.F,g.list) not in startd: g.child[0]=node(rubik.perm_apply(rubik.F,g.list),g.list,rubik.F) startd[rubik.perm_apply(rubik.F,g.list)]=(rubik.perm_apply(rubik.F,g.list),g.list,rubik.F) startq.enqueue(g.child[0]) if rubik.perm_apply(rubik.F,g.list) in endd: fr=moves(rubik.perm_apply(rubik.F,g.list),startd) br=moves(rubik.perm_apply(rubik.F,g.list),endd) fr.reverse() z=fr+br return z if rubik.perm_apply(rubik.Fi,g.list) not in startd: g.child[1]=node(rubik.perm_apply(rubik.Fi,g.list),g.list,rubik.Fi) startd[rubik.perm_apply(rubik.Fi,g.list)]=(rubik.perm_apply(rubik.Fi,g.list),g.list,rubik.Fi) startq.enqueue(g.child[1]) if rubik.perm_apply(rubik.Fi,g.list) in endd: fr=moves(rubik.perm_apply(rubik.Fi,g.list),startd) br=moves(rubik.perm_apply(rubik.Fi,g.list),endd) fr.reverse() z=fr+br return z if rubik.perm_apply(rubik.L,g.list) not in startd: g.child[2]=node(rubik.perm_apply(rubik.L,g.list),g.list,rubik.L) startd[rubik.perm_apply(rubik.L,g.list)]=(rubik.perm_apply(rubik.L,g.list),g.list,rubik.L) startq.enqueue(g.child[2]) if rubik.perm_apply(rubik.L,g.list) in endd: fr=moves(rubik.perm_apply(rubik.L,g.list),startd) br=moves(rubik.perm_apply(rubik.L,g.list),endd) fr.reverse() z=fr+br return z if rubik.perm_apply(rubik.Li,g.list) not in startd: g.child[3]=node(rubik.perm_apply(rubik.Li,g.list),g.list,rubik.Li) startd[rubik.perm_apply(rubik.Li,g.list)]=(rubik.perm_apply(rubik.Li,g.list),g.list,rubik.Li) startq.enqueue(g.child[3]) if rubik.perm_apply(rubik.Li,g.list) in endd: fr=moves(rubik.perm_apply(rubik.Li,g.list),startd) br=moves(rubik.perm_apply(rubik.Li,g.list),endd) fr.reverse() z=fr+br return z if rubik.perm_apply(rubik.U,g.list) not in startd: g.child[4]=node(rubik.perm_apply(rubik.U,g.list),g.list,rubik.U) startd[rubik.perm_apply(rubik.U,g.list)]=(rubik.perm_apply(rubik.U,g.list),g.list,rubik.U) startq.enqueue(g.child[4]) if rubik.perm_apply(rubik.U,g.list) in endd: fr=moves(rubik.perm_apply(rubik.U,g.list),startd) br=moves(rubik.perm_apply(rubik.U,g.list),endd) fr.reverse() z=fr+br return z if rubik.perm_apply(rubik.Ui,g.list) not in startd: g.child[5]=node(rubik.perm_apply(rubik.Ui,g.list),g.list,rubik.Ui) startd[rubik.perm_apply(rubik.Ui,g.list)]=(rubik.perm_apply(rubik.Ui,g.list),g.list,rubik.Ui) startq.enqueue(g.child[5]) if rubik.perm_apply(rubik.Li,g.list) in endd: fr=moves(rubik.perm_apply(rubik.Ui,g.list),startd) br=moves(rubik.perm_apply(rubik.Ui,g.list),endd) fr.reverse() z=fr+br return z while True: k=endq.dequeue() if k==None: endq.enqueue(None) break if rubik.perm_apply(rubik.perm_inverse(rubik.F),k.list) not in endd: k.child[0]=node(rubik.perm_apply(rubik.perm_inverse(rubik.F),k.list),k.list,rubik.F) endd[rubik.perm_apply(rubik.perm_inverse(rubik.F),k.list)]=(rubik.perm_apply(rubik.perm_inverse(rubik.F),k.list),k.list,rubik.F) endq.enqueue(k.child[0]) if rubik.perm_apply(rubik.perm_inverse(rubik.F),k.list) in startd: fr=moves(rubik.perm_apply(rubik.perm_inverse(rubik.F),k.list),startd) br=moves(rubik.perm_apply(rubik.perm_inverse(rubik.F),k.list),endd) fr.reverse() z=fr+br return z if rubik.perm_apply(rubik.perm_inverse(rubik.Fi),k.list) not in endd: k.child[1]=node(rubik.perm_apply(rubik.perm_inverse(rubik.Fi),k.list),k.list,rubik.Fi) endd[rubik.perm_apply(rubik.perm_inverse(rubik.Fi),k.list)]=(rubik.perm_apply(rubik.perm_inverse(rubik.Fi),k.list),k.list,rubik.Fi) endq.enqueue(k.child[1]) if rubik.perm_apply(rubik.perm_inverse(rubik.Fi),k.list) in startd: fr=moves(rubik.perm_apply(rubik.perm_inverse(rubik.Fi),k.list),startd) br=moves(rubik.perm_apply(rubik.perm_inverse(rubik.Fi),k.list),endd) fr.reverse() z=fr+br return z if rubik.perm_apply(rubik.perm_inverse(rubik.L),k.list) not in endd: k.child[2]=node(rubik.perm_apply(rubik.perm_inverse(rubik.L),k.list),k.list,rubik.L) endd[rubik.perm_apply(rubik.perm_inverse(rubik.L),k.list)]=(rubik.perm_apply(rubik.perm_inverse(rubik.L),k.list),k.list,rubik.L) endq.enqueue(k.child[2]) if rubik.perm_apply(rubik.perm_inverse(rubik.L),k.list) in startd: fr=moves(rubik.perm_apply(rubik.perm_inverse(rubik.L),k.list),startd) br=moves(rubik.perm_apply(rubik.perm_inverse(rubik.L),k.list),endd) fr.reverse() z=fr+br return z if rubik.perm_apply(rubik.perm_inverse(rubik.Li),k.list) not in endd: k.child[3]=node(rubik.perm_apply(rubik.perm_inverse(rubik.Li),k.list),k.list,rubik.Li) endd[rubik.perm_apply(rubik.perm_inverse(rubik.Li),k.list)]=(rubik.perm_apply(rubik.perm_inverse(rubik.Li),k.list),k.list,rubik.Li) endq.enqueue(k.child[3]) if rubik.perm_apply(rubik.perm_inverse(rubik.Li),k.list) in startd: fr=moves(rubik.perm_apply(rubik.perm_inverse(rubik.Li),k.list),startd) br=moves(rubik.perm_apply(rubik.perm_inverse(rubik.Li),k.list),endd) fr.reverse() z=fr+br return z if rubik.perm_apply(rubik.perm_inverse(rubik.U),k.list) not in endd: k.child[4]=node(rubik.perm_apply(rubik.perm_inverse(rubik.U),k.list),k.list,rubik.U) endd[rubik.perm_apply(rubik.perm_inverse(rubik.U),k.list)]=(rubik.perm_apply(rubik.perm_inverse(rubik.U),k.list),k.list,rubik.U) endq.enqueue(k.child[4]) if rubik.perm_apply(rubik.perm_inverse(rubik.U),k.list) in startd: fr=moves(rubik.perm_apply(rubik.perm_inverse(rubik.U),k.list),startd) br=moves(rubik.perm_apply(rubik.perm_inverse(rubik.U),k.list),endd) fr.reverse() z=fr+br return z if rubik.perm_apply(rubik.perm_inverse(rubik.Ui),k.list) not in endd: k.child[5]=node(rubik.perm_apply(rubik.perm_inverse(rubik.Ui),k.list),k.list,rubik.Ui) endd[rubik.perm_apply(rubik.perm_inverse(rubik.Ui),k.list)]=(rubik.perm_apply(rubik.perm_inverse(rubik.Ui),k.list),k.list,rubik.Ui) endq.enqueue(k.child[5]) if rubik.perm_apply(rubik.perm_inverse(rubik.Ui),k.list) in startd: fr=moves(rubik.perm_apply(rubik.perm_inverse(rubik.Ui),k.list),startd) br=moves(rubik.perm_apply(rubik.perm_inverse(rubik.Ui),k.list),endd) fr.reverse() z=fr+br return z i+=1 return None
def shortest_path(start, end): # Start side of BFS start_init = node_info(start, (None, None), 0) s_frontier = deque([start_init]) s_parents = [] # End side of BFS end_init = node_info(end, (None, None), 0) e_frontier = deque([end_init]) e_parents = [] # Flag determines which side we need to advance the frontier # When flag is 1 it means that we might advance the left frontier # When flag is -1 it means we might advance the right frontier flag = 1 # Test to see if the two given states (start, end) are the same # We know to return an empty list for l_elm in s_frontier: for r_elm in e_frontier: if l_elm.st == r_elm.st: return [] found = False # Given that we have not found a solution, the function will alternate between the start and end # sides (bidirectional) to create a new frontier and check its values for a solution while not found: # Checks the depth of the nodes in both frontiers # If their order exceeds 7, then there is no shortest path if s_frontier[0].order > 6 and e_frontier[0].order > 6: return None # Flag alternates the sides if flag == 1: # Gets the next frontier next_frontier(s_frontier, s_parents) flag = flag * (-1) else: # Gets the next frontier next_frontier(e_frontier, e_parents) flag = flag * (-1) # Checking to see if there is the same block state in each list # Grabs a left element from the starting frontier for l_elm in s_frontier: # Compares it to each of the right elements from the end frontier for r_elm in e_frontier: # If we find an element that matches if l_elm.st == r_elm.st: # Find the intersect of the two lists intersect = (l_elm, r_elm) # We set found to true found = True # Then break out of the loop break # If the element was found we can break out of this loop as well if found == True: break # Creates a lsit for return final_list = [] # Temp variable to hold l_elm l_temp = intersect[0] # Gets the parent of l_temp (l_elm) until the start state while (l_temp.st != start): final_list.insert(0, l_temp.parent[0]) l_temp = l_temp.parent[1] r_temp = intersect[1] # Gets the parent of r_temp (r_elm) until the end state # Perm inverse is because we have to do the inverse of each move to get the parent. while (r_temp.st != end): final_list.append(rubik.perm_inverse(r_temp.parent[0])) r_temp = r_temp.parent[1] return final_list
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ if start == end: return [] front = {0: [start]} back = {0: [end]} front_discovered = {start: (None, None)} # position: parent, twist back_discovered = {end: (None, None)} common = None try: for i in range(0, 8): front[i + 1] = [] for position in front.get(i): for twist in rubik.quarter_twists: new = rubik.perm_apply(twist, position) if position in back_discovered: common = position raise LoopException #TODO this is ugly as hell if new not in front_discovered: front[i + 1].append(new) front_discovered[new] = (position, twist) back[i + 1] = [] for position in back.get(i): for twist in rubik.quarter_twists: new = rubik.perm_apply(rubik.perm_inverse(twist), position) if position in front_discovered: common = position raise LoopException # TODO this is ugly as hell if new not in back_discovered: back[i + 1].append(new) back_discovered[new] = (position, twist) finally: result = [] current = common front_parent, twist = front_discovered[current] while front_parent is not None: result.append(twist) current = front_parent front_parent, twist = front_discovered[current] result.reverse() current = common back_parent, twist = back_discovered[current] while back_parent is not None: result.append(twist) current = back_parent back_parent, twist = back_discovered[current] return result
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. Assumes the rubik.quarter_twists move set. """ root1=node(start) root2=node(end) arr1=[[] for wqw in range(8)] arr2=[[] for qwq in range(8)] ser1=[[] for lp in range(10000)] ser2=[[] for lp2 in range(10000)] arr1[0].append(root1) arr2[0].append(root2) ser1[(hash(start))%10000].append(start) ser2[(hash(end))%10000].append(end) flag=0 chk=None res=[] res1=[] res2=[] for h in range(0,8): chk=check_match(arr1[h],arr2[h],ser2) if not chk is None: flag=1 break else: for w in range(len(arr1[h])): fin=insert_e(arr1[h][w],h+1,arr1,ser1) arr1=fin[0] ser1=fin[1] if (h+1)<8: chk=check_match(arr2[h],arr1[h+1],ser1) if not chk is None: flag=2 break else: for q in range(len(arr2[h])): fim=insert_e(arr2[h][q],h+1,arr2,ser2) arr2=fim[0] ser2=fim[1] if flag==1: while not chk[0].parent is None: res1.append(chk[0].per) chk[0]=chk[0].parent if not res1 is None: res+=res1[::-1] while not chk[1].parent is None: res2.append(chk[1].per) chk[1]=chk[1].parent if not res2 is None: res+=[rubik.perm_inverse(i) for i in res2] return res elif flag==2: while not chk[1].parent is None: res1.append(chk[1].per) chk[1]=chk[1].parent if not res1 is None: res+=res1[::-1] while not chk[0].parent is None: res2.append(chk[0].per) chk[0]=chk[0].parent if not res2 is None: res+=[rubik.perm_inverse(i) for i in res2] return res else: print "The given configuration is not solvable/wrong."
def getInverse(moves): for m in range(len(moves)): moves[m] = rubik.perm_inverse(moves[m])
def getPath(beg, end): end.reverse() for x in end: beg.append(rubik.perm_inverse(x)) return beg
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ if start == end: return [] startroot = node(start) endroot = node(end) startq = queue([startroot, None]) endq = queue([endroot, None]) starth = [] endh = [] for i in range(0, 335923): starth.append([]) endh.append([]) startd = {} starth[hash(start)] = (start, None, None) endd = {} endh[hash(end)] = (end, None, None) startd[startroot.list] = (startroot.list, startroot.parent, startroot.orientation) endd[endroot.list] = (endroot.list, endroot.parent, endroot.orientation) i = 0 while i < 7: while True: g = startq.dequeue() if g == None: startq.enqueue(None) break if rubik.perm_apply(rubik.F, g.list) is not starth[hash(rubik.perm_apply(rubik.F, g.list))]: g.child[0] = node(rubik.perm_apply(rubik.F, g.list), g.list, rubik.F) starth[hash(rubik.perm_apply(rubik.F, g.list))].append( (rubik.perm_apply(rubik.F, g.list), g.list, rubik.F) ) startq.enqueue(g.child[0]) if rubik.perm_apply(rubik.F, g.list) is endh[hash(rubik.perm_apply(rubik.F, g.list))]: fr = moves(rubik.perm_apply(rubik.F, g.list), starth) br = moves(rubik.perm_apply(rubik.F, g.list), endh) fr.reverse() z = fr + br return z if rubik.perm_apply(rubik.Fi, g.list) is not starth[hash(rubik.perm_apply(rubik.Fi, g.list))]: g.child[1] = node(rubik.perm_apply(rubik.Fi, g.list), g.list, rubik.Fi) starth[hash(rubik.perm_apply(rubik.Fi, g.list))].append( (rubik.perm_apply(rubik.Fi, g.list), g.list, rubik.Fi) ) startq.enqueue(g.child[1]) if rubik.perm_apply(rubik.Fi, g.list) is endh[hash(rubik.perm_apply(rubik.Fi, g.list))]: fr = moves(rubik.perm_apply(rubik.Fi, g.list), starth) br = moves(rubik.perm_apply(rubik.Fi, g.list), endh) fr.reverse() z = fr + br return z if rubik.perm_apply(rubik.L, g.list) is not starth[hash(rubik.perm_apply(rubik.L, g.list))]: g.child[2] = node(rubik.perm_apply(rubik.L, g.list), g.list, rubik.L) starth[hash(rubik.perm_apply(rubik.L, g.list))].append( (rubik.perm_apply(rubik.L, g.list), g.list, rubik.L) ) startq.enqueue(g.child[2]) if rubik.perm_apply(rubik.L, g.list) is endh[hash(rubik.perm_apply(rubik.L, g.list))]: fr = moves(rubik.perm_apply(rubik.L, g.list), starth) br = moves(rubik.perm_apply(rubik.L, g.list), endh) fr.reverse() z = fr + br return z if rubik.perm_apply(rubik.Li, g.list) is not starth[hash(rubik.perm_apply(rubik.Li, g.list))]: g.child[3] = node(rubik.perm_apply(rubik.Li, g.list), g.list, rubik.Li) starth[hash(rubik.perm_apply(rubik.Li, g.list))].append( (rubik.perm_apply(rubik.Li, g.list), g.list, rubik.Li) ) startq.enqueue(g.child[3]) if rubik.perm_apply(rubik.Li, g.list) is endh[hash(rubik.perm_apply(rubik.Li, g.list))]: fr = moves(rubik.perm_apply(rubik.Li, g.list), starth) br = moves(rubik.perm_apply(rubik.Li, g.list), endh) fr.reverse() z = fr + br return z if rubik.perm_apply(rubik.U, g.list) is not starth[hash(rubik.perm_apply(rubik.U, g.list))]: g.child[4] = node(rubik.perm_apply(rubik.U, g.list), g.list, rubik.U) starth[hash(rubik.perm_apply(rubik.U, g.list))].append( (rubik.perm_apply(rubik.U, g.list), g.list, rubik.U) ) startq.enqueue(g.child[4]) if rubik.perm_apply(rubik.U, g.list) is endh[hash(rubik.perm_apply(rubik.U, g.list))]: fr = moves(rubik.perm_apply(rubik.U, g.list), starth) br = moves(rubik.perm_apply(rubik.U, g.list), endh) fr.reverse() z = fr + br return z if rubik.perm_apply(rubik.Ui, g.list) is not starth[hash(rubik.perm_apply(rubik.Ui, g.list))]: g.child[5] = node(rubik.perm_apply(rubik.Ui, g.list), g.list, rubik.Ui) starth[hash(rubik.perm_apply(rubik.Ui, g.list))].append( (rubik.perm_apply(rubik.Ui, g.list), g.list, rubik.Ui) ) startq.enqueue(g.child[5]) if rubik.perm_apply(rubik.Li, g.list) is endh[hash(rubik.perm_apply(rubik.Ui, g.list))]: fr = moves(rubik.perm_apply(rubik.Ui, g.list), starth) br = moves(rubik.perm_apply(rubik.Ui, g.list), endh) fr.reverse() z = fr + br return z while True: k = endq.dequeue() if k == None: endq.enqueue(None) break if ( rubik.perm_apply(rubik.perm_inverse(rubik.F), k.list) is not endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.F), k.list))] ): k.child[0] = node(rubik.perm_apply(rubik.perm_inverse(rubik.F), k.list), k.list, rubik.F) endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.F), k.list))].append( (rubik.perm_apply(rubik.perm_inverse(rubik.F), k.list), k.list, rubik.F) ) endq.enqueue(k.child[0]) if ( rubik.perm_apply(rubik.perm_inverse(rubik.F), k.list) is starth[hash(rubik.perm_apply(rubik.perm_inverse(rubik.F), k.list))] ): fr = moves(rubik.perm_apply(rubik.perm_inverse(rubik.F), k.list), starth) br = moves(rubik.perm_apply(rubik.perm_inverse(rubik.F), k.list), endh) fr.reverse() z = fr + br return z if ( rubik.perm_apply(rubik.perm_inverse(rubik.Fi), k.list) is not endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.Fi), k.list))] ): k.child[1] = node(rubik.perm_apply(rubik.perm_inverse(rubik.Fi), k.list), k.list, rubik.Fi) endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.Fi), k.list))] = ( rubik.perm_apply(rubik.perm_inverse(rubik.Fi), k.list), k.list, rubik.Fi, ) endq.enqueue(k.child[1]) if rubik.perm_apply(rubik.perm_inverse(rubik.Fi), k.list) is startd: fr = moves(rubik.perm_apply(rubik.perm_inverse(rubik.Fi), k.list), starth) br = moves(rubik.perm_apply(rubik.perm_inverse(rubik.Fi), k.list), endh) fr.reverse() z = fr + br return z if ( rubik.perm_apply(rubik.perm_inverse(rubik.L), k.list) is not endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.L), k.list))] ): k.child[2] = node(rubik.perm_apply(rubik.perm_inverse(rubik.L), k.list), k.list, rubik.L) endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.L), k.list))] = ( rubik.perm_apply(rubik.perm_inverse(rubik.L), k.list), k.list, rubik.L, ) endq.enqueue(k.child[2]) if ( rubik.perm_apply(rubik.perm_inverse(rubik.L), k.list) is starth[hash(rubik.perm_apply(rubik.perm_inverse(rubik.L), k.list))] ): fr = moves(rubik.perm_apply(rubik.perm_inverse(rubik.L), k.list), starth) br = moves(rubik.perm_apply(rubik.perm_inverse(rubik.L), k.list), endh) fr.reverse() z = fr + br return z if ( rubik.perm_apply(rubik.perm_inverse(rubik.Li), k.list) is not endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.Li), k.list))] ): k.child[3] = node(rubik.perm_apply(rubik.perm_inverse(rubik.Li), k.list), k.list, rubik.Li) endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.Li), k.list))] = ( rubik.perm_apply(rubik.perm_inverse(rubik.Li), k.list), k.list, rubik.Li, ) endq.enqueue(k.child[3]) if ( rubik.perm_apply(rubik.perm_inverse(rubik.Li), k.list) is starth[hash(rubik.perm_apply(rubik.perm_inverse(rubik.Li), k.list))] ): fr = moves(rubik.perm_apply(rubik.perm_inverse(rubik.Li), k.list), starth) br = moves(rubik.perm_apply(rubik.perm_inverse(rubik.Li), k.list), endh) fr.reverse() z = fr + br return z if ( rubik.perm_apply(rubik.perm_inverse(rubik.U), k.list) is not endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.U), k.list))] ): k.child[4] = node(rubik.perm_apply(rubik.perm_inverse(rubik.U), k.list), k.list, rubik.U) endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.U), k.list))] = ( rubik.perm_apply(rubik.perm_inverse(rubik.U), k.list), k.list, rubik.U, ) endq.enqueue(k.child[4]) if ( rubik.perm_apply(rubik.perm_inverse(rubik.U), k.list) is starth[hash(rubik.perm_apply(rubik.perm_inverse(rubik.U), k.list))] ): fr = moves(rubik.perm_apply(rubik.perm_inverse(rubik.U), k.list), starth) br = moves(rubik.perm_apply(rubik.perm_inverse(rubik.U), k.list), endh) fr.reverse() z = fr + br return z if ( rubik.perm_apply(rubik.perm_inverse(rubik.Ui), k.list) is not endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.Ui), k.list))] ): k.child[5] = node(rubik.perm_apply(rubik.perm_inverse(rubik.Ui), k.list), k.list, rubik.Ui) endh[hash(rubik.perm_apply(rubik.perm_inverse(rubik.Ui), k.list))] = ( rubik.perm_apply(rubik.perm_inverse(rubik.Ui), k.list), k.list, rubik.Ui, ) endq.enqueue(k.child[5]) if ( rubik.perm_apply(rubik.perm_inverse(rubik.Ui), k.list) is starth[hash(rubik.perm_apply(rubik.perm_inverse(rubik.Ui), k.list))] ): fr = moves(rubik.perm_apply(rubik.perm_inverse(rubik.Ui), k.list), starth) br = moves(rubik.perm_apply(rubik.perm_inverse(rubik.Ui), k.list), endh) fr.reverse() z = fr + br return z i += 1 return None
def shortest_path(start, end): """ Using 2-way BFS, finds the shortest path from start_position to end_position. Returns a list of moves. You can use the rubik.quarter_twists move set. Each move can be applied using rubik.perm_apply """ #Set of moves the rubik's cube can make moves = [rubik.F, rubik.Fi, rubik.L, rubik.Li, rubik.U, rubik.Ui] #Depth of search depth = 1 startQ = [start] #Holds the starting values we make the frontier from endQ = [end] #Holds the ending values we compare the frontier to visit = [] #Holds all positions we have already visited heapq.heappush(visit, start) #adds start and end to the visited heap heapq.heappush(visit, end) path = {start: None} #Hold the paths from the node to the start position pathE = {end: None} #Holds the paths from the node to the end position fromStart = True #True if we are oriendted from the start position #true if cube is already solved if start == end: return [] #While there exists a list of values to test we will continue to generate the frontier to # compare to the end points #Termination: When startQ no longer exists we know there is no end points to compare to so # we have reached the point where no solution is possible while startQ: nextQ = [] depth += 1 if depth >= 14: # if we go beyond what is the shortest path, terminate break # While startQ exists and has values that a frontier can be made from the loop will continue to generate the # frontier until a match is found or the end of the list is reached # Termination: The loop will terminate when either a match is found signalling that the shortest path has # been found or it reaches the end of the startQ meaning that the next frontier has been found for i in range(len(startQ)): adjList = genAdj(startQ[i])#generates the frontier by calculating all adjacent moves #j - 0=F, 1=Fi, 2=L, 3=Li, 4=U, 5=Ui #j is the specific move done to the cube #While there exists a node j in the adjList we will compare it to the frontier on the other side #If a match is found we return the shortest path other wise we add the node to the visisted heap #and add its path to the dictionary for j in range(len(adjList)): #While an element k exists in the endQ frontier we will compare every element k with every element #j from the adjList to see if an equality exists, if one exists then a match and by extension a shortest #path has been found. #The loop will terminate when the end frontier has been completly checked meaning that element j is not # on both the start and ending frontiers for k in range(len(endQ)): #This is where we merge the list depending on if we are currently adding from the start side #or the end side if adjList[j] == endQ[k]: if fromStart:#If we are oriented from the start tPath = [] if path.get(startQ[i]) != None: tPath = path.get(startQ[i]) tPath.append(moves[j]) sPath = [] if pathE.get(endQ[k]) != None: sPath = pathE.get(endQ[k]) q = len(sPath) - 1 #Loops through the path oriented on the end in reverse appending it to the working path while(q >= 0): tPath.append(perm_inverse(sPath[q])) q -= 1 return tPath else: #If we are oriented from the end side tPath = [] if path.get(endQ[k]) != None: tPath = path.get(endQ[k]) tPath.append(perm_inverse(moves[j])) sPath = [] if pathE.get(startQ[i]) != None: sPath = pathE.get(startQ[i]) q = len(sPath) - 1 # Loops through the path oriented on the end in reverse appending it to the working path while q >= 0: tPath.append(perm_inverse(sPath[q])) q -= 1 return tPath #This will check if we have already visited the current node and if we did not, we add its path to the #corresonding dictionary if adjList[j] not in visit: heapq.heappush(visit, adjList[j]) #add to visit heapq.heappush(nextQ, adjList[j]) #add to the next frontier if fromStart: #If we are oriendted from the start tPath = [] if path.get(startQ[i]) != None: tPath = path.get(startQ[i]) tPath.append(moves[j]) nuPath = {adjList[j]: tPath} path.update(nuPath) else: #If we are oriendinted from the end/target tPath = [] if path.get(startQ[i]) != None: tPath = pathE.get(startQ[i]) tPath.append(moves[j]) nuPath = {adjList[j]: tPath} pathE.update(nuPath) #This will flip the algorithm to either fuction from the start of the graph or the end if(len(nextQ) < len(endQ)): startQ = nextQ else: startQ = endQ endQ = nextQ if fromStart: fromStart = False else: fromStart = True return