import copy import dataclasses import twophase.solver as sv # import kociemba as ksv # import serial import math import time import requests import threading import pygame import sys import kociemba import pygame import sys import sched # serialPort = "/dev/ttyACM0" # arduino = serial.Serial(serialPort, 115200, timeout=1) # translator dictionaries npTr = { "n": 1, "p": -1 } pnTr = { "n": "p", "p": "n" } # dictionary which returns a face which is guaranteed to not be connected to a servo for a specific rotation upDownDict = {0: 'U', 1: 'R', 2: 'B'} class Cube: faceLocation = { "F": 0, "L": 1, "B": 2, "R": 3, "U": 4, "D": 5, } servoAngles = [[1, 1, 1, 1]] # modify these to fit the final servo angles currentRotation = 0 # and cube rotations after scanning the cube. def rotate_fb(self, invert): values = list(self.faceLocation.values()) keys = list(self.faceLocation.keys()) temp = dict(zip(values, keys)) # D -> L -> U -> R if not invert: keys = temp[0], temp[5], temp[2], temp[4], temp[1], temp[3] else: keys = temp[0], temp[4], temp[2], temp[5], temp[3], temp[1] return dict(zip(keys, values)) def rotate_rl(self, invert): values = list(self.faceLocation.values()) keys = list(self.faceLocation.keys()) temp = dict(zip(values, keys)) # D -> F -> U -> B if invert: keys = temp[4], temp[1], temp[5], temp[3], temp[2], temp[0] else: keys = temp[5], temp[1], temp[4], temp[3], temp[0], temp[2] return dict(zip(keys, values)) # def rotate42(self, invert): class Move: face = 'U' # the face on which the move happens servoNum = 0 # the servo on which the move happens angle = 0 # the degree of rotation of the move (1, 2 or 3). # In the case of rotations, this is the rotation of servo 0 or 1 index = 0 # the index within the total moves where this move happens def __init__(self, face=None, servo_num=None, angle=None, index=None): self.face = face self.servoNum = servo_num self.angle = angle self.index = index def __repr__(self): return str(self.index) + ": F:" + self.face + " A:" + str(self.angle) + " S:" + str(self.servoNum) def set_servo(servo_num, angle): pass # requests.post(url, data={ # "servo": servo_num, # "position": position, # }) class Servos: counter = 0 servoAngles = [1, 1, 1, 1] rotator_servo_angles = { 0: [[176], [175], [178], [173]], 1: [[123], [123], [123], [115]], 2: [[63], [60], [63], [56]], 3: [[10], [0], [7], [2]], } retraction_servo_angles = { 'extend': [3, 7, 12, 57], 'retract': [25, 30, 35, 80], } # how long to wait after each length of move before turning the servo back moveTimings = [0.1, 0.2, 0.35] def __init__(self): x = True def tick(self): self.counter += 1 def goto(self, index, position): if self.servoAngles[index] > position: set_servo(index * 2, self.rotator_servo_angles[position][index][0] + 5) elif self.servoAngles[index] < position: set_servo(index * 2, self.rotator_servo_angles[position][index][-1] - 5) diff = abs(self.servoAngles[index] - position) scheduler.enter(self.moveTimings[diff - 1], 1, set_servo, (index * 2, self.rotator_servo_angles[position][index][-1],)) self.servoAngles[index] = position def move(self, index, move_by): if (self.servoAngles[index] + move_by) % 4 != self.servoAngles[index] + move_by: raise "servo overflowed" else: move_to = (self.servoAngles[index] + move_by) % 4 self.goto(index, move_to) def retract(self, index): set_servo((index * 2) + 1, self.retraction_servo_angles['retract'][index]) def extend(self, index): set_servo((index * 2) + 1, self.retraction_servo_angles['extend'][index]) # ___VARIABLES___ # timing stuff scheduler = sched.scheduler(time.time, time.sleep) scheduler.run() # Network Stuff pi_ip = '192.168.0.102' url = "http://{0}:5000/receive".format(pi_ip) # cube stuff # cubeString = 'FFFFUFFFFUUUURUUUURRRRFRRRRBBBBDBBBBDDDDLDDDDLLLLBLLLL' # strSolution = sv.solve(cubeString) strSolution = 'L2 B2 U2 B2 R1 D2 L3 B3 L1 R2 B1 D3 F2 L2 U1 R3 U3 F3 L1 (19f)' #strSolution = 'F2 U2 D1 B3 L1 (19f)' if __name__ == '__main__': cube = Cube() # first, convert the solution to a list. Split it by whitespace and remove the final item solution = str.split(strSolution)[0:-1] new_solution = [] for i, move in enumerate(solution): new_solution.append(Move(face=move[0], angle=int(move[1]))) solution = new_solution # the structure of [1, 1, 1] is Up/down, Sides, Front/back rotationCompatDict = {'U': [0, 1, 1], 'D': [0, 1, 1], 'L': [1, 0, 1], 'R': [1, 0, 1], 'F': [1, 1, 0], 'B': [1, 1, 0], } compatibilities = [rotationCompatDict[move.face] for move in solution] # 0 is orientations for each move, performed before the move. The list is offset by one by the initial cube # position of 0. 1 is for the number of orientation changes rotationLists = [[[0], 0]] for move in solution: new_moveLists = [] for mlist in rotationLists: # if the current move list doesn't match the current move, spawn two more and pick both to fix it. if rotationCompatDict[move.face][mlist[0][-1]] == 0: new_moveLists.append([mlist[0] + [(mlist[0][-1] + 1) % 3], mlist[1] + 1]) new_moveLists.append([mlist[0] + [(mlist[0][-1] - 1) % 3], mlist[1] + 1]) # else, keep the cube orientation the same else: new_moveLists.append([mlist[0] + [mlist[0][-1]], mlist[1]]) # sort the new rotationLists in terms of fewest rotations each time we add new items new_moveLists.sort(key=lambda item: item[1]) # loop through the rotationLists and cull all of them with two or more rotations than the lowest moveList # probably not the most efficient way of culling, but it reduces the computation from 1000s to 100s. minMoves = new_moveLists[0][1] rotationLists = [] for mlist in new_moveLists: if mlist[1] <= minMoves + 1: rotationLists.append(mlist) # find a list of only the move-lists with the fewest number of rotations. minRotations = rotationLists[0][1] bestRotations = [] for mlist in rotationLists: if mlist[1] == minRotations: # remove the 0th item we used earlier mlist[0].pop(0) bestRotations.append(mlist) else: break print("best: " + str(bestRotations)) # next, find the string of servo moves based on the rotation moves. Loop through the original solution allMoves = [] moveIndex = 0 # stores the index of the move - useful for stitching everything back together later for i, move in enumerate(solution): if cube.currentRotation != bestRotations[0][0][i]: # rotate the cube faceLocations1 = cube.rotate_rl(False) # if the index is greater than 3, then it must be either 4 or 5. This is good. if faceLocations1[upDownDict[bestRotations[0][0][i]]] > 3: cube.faceLocation = faceLocations1 allMoves.append([Move(face='TRL', index=moveIndex, angle=1)]) moveIndex += 1 else: cube.faceLocation = cube.rotate_fb(False) allMoves.append([Move(face='TFB', index=moveIndex, angle=1)]) moveIndex += 1 cube.currentRotation = bestRotations[0][0][i] # try to combine opposing moves into one move try: if cube.faceLocation[allMoves[-1][0].face] == ((cube.faceLocation[move.face] + 2) % 4): allMoves[-1].append(move) moveIndex -= 1 # subtract one from the move index so when finally adds one, it stays the same # allMoves[-1] = [allMoves[-1], str(cube.faceLocation[move[0]]) + move[1]] else: allMoves.append([move]) # allMoves.append(str(cube.faceLocation[move[0]]) + move[1]) except KeyError: # accessing the cube dictionary with a rotation move (e.g. tfb) yields an error)) allMoves.append([move]) except IndexError: allMoves.append([move]) finally: move.index = moveIndex moveIndex += 1 print("moves: " + str(allMoves)) # first, split up the moves based on where rotations occur, and create a list of potential tweaks. cube2 = Cube() splitAllMoves02, splitAllMoves13 = [[]], [[]] for moveList in allMoves: for j, move in enumerate(moveList): if move.face[0] == 'T': # check if the move is a rotation if move.face == 'TRL': # branch for both types of rotation splitAllMoves13[-1].append(move) splitAllMoves02.append([]) cube2.faceLocation = cube2.rotate_rl(False) else: splitAllMoves02[-1].append(move) splitAllMoves13.append([]) cube2.faceLocation = cube2.rotate_fb(False) else: move.servoNum = cube2.faceLocation[move.face] if move.servoNum == 0 or move.servoNum == 2: splitAllMoves02[-1].append(move) else: splitAllMoves13[-1].append(move) # finally, loop through all splitAllMoves lists again, and combine moves with the same indices lastIndex = -1 for splitAllMoves in (splitAllMoves02, splitAllMoves13): for i, splitList in enumerate(splitAllMoves): new_splitList = [] for move in splitList: if lastIndex == move.index: new_splitList[-1] += [move] else: new_splitList.append([move]) lastIndex = move.index splitAllMoves[i] = new_splitList # print(tweaks) print("Split::(230)") print(splitAllMoves02) print(splitAllMoves13) # tries to calculate the solution as far as it can. # recursively calls itself and accepts the longest solution # returns: the move list, the servo it failed on, how far it got. def run_solution(moves, servo_angles): result_optimal = 0 for i, move1 in enumerate(moves): for move in move1: # for each move, check what type of move it is. if move.face[0] == 'T': # for rotation moves, spawn two new functions for each direction of possible rotation rotator_servos = [] if move.face == 'TFB': rotator_servos = [0, 2] else: # move.face == 'TRL': rotator_servos = [1, 3] # if neither rotation is possible, call add_retraction # I'm brute forcing this without being clever because I'm lazy clockwise_possible = True anticlockwise_possible = True if servo_angles[rotator_servos[0]] == 0: anticlockwise_possible = False elif servo_angles[rotator_servos[0]] == 3: clockwise_possible = False if servo_angles[rotator_servos[1]] == 3: anticlockwise_possible = False elif servo_angles[rotator_servos[1]] == 0: clockwise_possible = False possible_solutions = [] next_moves = copy.copy(moves[i + 1::]) insert_moves = [] if clockwise_possible or anticlockwise_possible: if clockwise_possible: insert_moves.append([ [ Move('retract', servo_num=(rotator_servos[0]+1) % 4, angle=0, index=move.index-0.1), Move('retract', servo_num=(rotator_servos[1]+1) % 4, angle=0, index=move.index-0.1) ], [Move(move.face, servo_num=rotator_servos[0], angle=1, index=move.index), Move(move.face, servo_num=rotator_servos[1], angle=-1, index=move.index)], [ Move('extend', servo_num=(rotator_servos[0]+1) % 4, angle=0, index=move.index+0.1), Move('extend', servo_num=(rotator_servos[1]+1) % 4, angle=0, index=move.index+0.1) ], ]) next_servo_angles = copy.copy(servo_angles) next_servo_angles[rotator_servos[0]] += 1 next_servo_angles[rotator_servos[1]] -= 1 possible_solutions.append(run_solution(next_moves, next_servo_angles)) if anticlockwise_possible: insert_moves.append([ [ Move('retract', servo_num=(rotator_servos[0]+1) % 4, angle=0, index=move.index-0.1), Move('retract', servo_num=(rotator_servos[1]+1) % 4, angle=0, index=move.index-0.1) ], [ Move(move.face, servo_num=rotator_servos[0], angle=-1, index=move.index), Move(move.face, servo_num=rotator_servos[1], angle=1, index=move.index) ], [ Move('extend', servo_num=(rotator_servos[0]+1) % 4, angle=0, index=move.index-0.1), Move('extend', servo_num=(rotator_servos[1]+1) % 4, angle=0, index=move.index-0.1) ], ]) next_servo_angles = copy.copy(servo_angles) next_servo_angles[rotator_servos[0]] += 1 next_servo_angles[rotator_servos[1]] -= 1 possible_solutions.append(run_solution(next_moves, next_servo_angles)) else: result_optimal -= 100 # for now, brute force a retraction move which shifts the first servo # into a position usable by the second # there are only 2 cases where both rotation directions are impossible: # servo 1 == 0 and servo 2 == 0 # servo 1 == 3 and servo 2 == 0 next_servo_angles = copy.copy(servo_angles) if servo_angles[rotator_servos[1]] == 3: insert_moves.append([[Move(face='Retraction', servo_num=rotator_servos[0], angle=-1, index=move.index + .1)]]) next_servo_angles[rotator_servos[1]] -= 1 elif servo_angles[rotator_servos[1]] == 0: insert_moves.append([[Move(face='Retraction', servo_num=rotator_servos[0], angle=1, index=move.index + .1)]]) next_servo_angles[rotator_servos[1]] += 1 possible_solutions.append(run_solution(next_moves, next_servo_angles)) max_score = -2147483648 best_index = 1 # evaluate the possible solutions and pick the one with the highest optimization score for x, tried_solution in enumerate(possible_solutions): if tried_solution[1] > max_score: best_index = x max_score = tried_solution[1] return [moves[0:i] + insert_moves[best_index] + possible_solutions[best_index][0], result_optimal + possible_solutions[best_index][1]] else: if move.position == 2: # double moves are always possible. all we need to do is check which way the double move can go, # and if needed, set 2 to -2 if servo_angles[move.servoNum] > 1: move.position = -2 else: # single moves are again always possible. # However, sometimes we need to do an inverted 270-degree rotation # this can be simplified by finding the final position by adding the position to the current position, # and taking modulo 4 final_position = (servo_angles[move.servoNum] + move.position) % 4 # then we can just calculate the position by the change in current position and final position move.position = final_position - servo_angles[move.servoNum] # finally, apply the move to the servo_angles servo_angles[move.servoNum] += move.position # reduce optimal score if the move position is 3 if abs(move.position) == 3: result_optimal -= 1 return [moves, result_optimal] print("") def solve_moves(all_split_moves, which_servos): result = [] rotation_info = [] result += run_solution(all_split_moves[0], [1, 1, 1, 1])[0] # solve the rest, inputting every possible combination of servo angles (since there are only 9) for splitMoves in all_split_moves[1::]: calculated_solutions = [] for s1 in range(3): for s2 in range(3): if which_servos == [0, 2]: servos_start = [s1, 0, s2, 0] else: servos_start = [0, s1, 0, s2] calculated_solutions.append(run_solution(copy.copy(splitMoves), servos_start) + [[s1, s2]]) # find the best calculated_solution max_optimal = -2147483648 best_solution = [] for _solution in calculated_solutions: if _solution[1] > max_optimal: max_optimal = _solution[1] best_solution = _solution print(best_solution) result += best_solution[0] rotation_info.append(best_solution[2]) return result, rotation_info splitAllMoves02, rotationInfo02 = solve_moves(splitAllMoves02, [0, 2]) splitAllMoves13, rotationInfo13 = solve_moves(splitAllMoves13, [1, 3]) # combine the splitmoves into two lists. Handle the different rotation directions allMoves = [] # combinedMoves02 = [] # combinedMoves13 = [] # stores the position in both splitMoves lists c_02 = 0 c_13 = 0 # whether we flip over the servos for each thing _02Reversed = False _13Reversed = False # the currently stored move index index = -1 while True: # bit of a janky way of doing this. The first half is for handling ending the loop if c_02 != len(splitAllMoves02) and (c_13 == len(splitAllMoves13) or splitAllMoves02[c_02][0].index < splitAllMoves13[c_13][0].index): if _02Reversed and not splitAllMoves02[c_02][0].face[0] == 'T': # do not reverse rotation moves for move in splitAllMoves02[c_02]: move.servoNum = (move.servoNum + 2) % 4 allMoves.append(splitAllMoves02[c_02]) # if the move is a rotation, check whether it is an inverse rotation if splitAllMoves02[c_02][0].face[0] == 'T' and splitAllMoves02[c_02][0].position == -1: _13Reversed = not _13Reversed c_02 += 1 elif c_13 != len(splitAllMoves13): # splitAllMoves13[c_02][0].index < splitAllMoves02[c_13][0].index: if _13Reversed and not splitAllMoves13[c_13][0].face[0] == 'T': # do not reverse rotation moves for move in splitAllMoves13[c_13]: move.servoNum = (move.servoNum + 2) % 4 allMoves.append(splitAllMoves13[c_13]) # if the move is a rotation, check whether it is an inverse rotation if splitAllMoves13[c_13][0].face[0] == 'T' and splitAllMoves13[c_13][0] == -1: _02Reversed = not _02Reversed c_13 += 1 else: break for i in allMoves: for j in i: print(j.face + ", S: ", str(j.servoNum) + ", A: ", j.angle) # Servos Class servos = Servos() cube = Cube() # self check whether moves work rotationCounter02 = 0 rotationCounter13 = 0 for j in allMoves: output = "" if j[0].face[0] == 'T': # handle servos opposite to the rotation. Set them to the starting angles based on 'rotationInfo' if j[0].face == 'TFB': servos.goto(1, rotationInfo02[rotationCounter02][0]) servos.goto(3, rotationInfo02[rotationCounter02][1]) cube.rotate_fb(True) rotationCounter02 += 1 elif j[0].face == 'TRL': servos.goto(0, rotationInfo13[rotationCounter13][0]) servos.goto(2, rotationInfo13[rotationCounter13][1]) cube.rotate_rl(True) rotationCounter13 += 1 for k, move in enumerate(j): if move.face == "retract": servos.retract(move.servoNum) elif move.face == "extend": servos.extend(move.servoNum) elif move.face == "retraction": raise "No worky" else: servos.move(move.servoNum, move.position) # output += str(i.servoNum) + " " + str(i.position) + " | " scheduler.run(True) #time.sleep(0.25) print(output) for i in range(4): servos.retract(i) #time.sleep(0.2) servos.goto(i, 0) servos.goto(i, 1) #time.sleep(0.2) servos.extend(2) #time.sleep(.5) servos.extend(0) #time.sleep(0.25) servos.extend(1) servos.extend(3) #input() rotationCounter02 = 0 rotationCounter13 = 0 for j in allMoves: output = "" if j[0].face[0] == 'T': # handle servos opposite to the rotation. Set them to the starting angles based on 'rotationInfo' if j[0].face == 'TFB': servos.goto(1, rotationInfo02[rotationCounter02][0]) servos.goto(3, rotationInfo02[rotationCounter02][1]) rotationCounter02 += 1 elif j[0].face == 'TRL': servos.goto(0, rotationInfo13[rotationCounter13][0]) servos.goto(2, rotationInfo13[rotationCounter13][1]) rotationCounter13 += 1 for k, move in enumerate(j): if move.face == "retract": servos.retract(move.servoNum) elif move.face == "extend": servos.extend(move.servoNum) elif move.face == "retraction": raise "No worky" else: servos.move(move.servoNum, move.position) # output += str(i.servoNum) + " " + str(i.position) + " | " scheduler.run(True) #time.sleep(0.25) print(output) # for splitMoves0 in splitAllMoves02: # for splitMoves in splitMoves0: # x = 0 # # first, add an item to combinedMoves02 # todo: # add in retractions where other solutions are not possible # change the initial position of servos (except for at the start of the solution) # def run_solution(moves, servo_start, which_servos, move_index_start): # servo_angles = [servo_start] # counter = 0 # #result_moves = [] # move_index = move_index_start # while True: # servo_angles.append(copy.copy(servo_angles[-1])) # for k, f_move in enumerate(moves[counter]): # # first, check for rotations # if f_move[0] == 'T': # # create a list of remaining moves to pass higher up the stack # new_moves = copy.copy(moves[counter:]) # # pop out the first item from the first moves # # (prevents the move from being handled twice higher up the stack) # new_moves[0].pop(k) # # inverted rotation: false. # # # create new variables to pass to the next function # new_servo_angles = copy.copy(servo_angles[-1]) # new_servo_angles[which_servos[0]] += 1 # new_servo_angles[which_servos[1]] -= 1 # val = validate_servos(new_servo_angles) # # not sure but it seems important # try_ = [] # [[result_moves, counter, val[1]]] # if val[0]: # # append to the try_ list the result_moves from the next function, # # and whether the rotation is inverted (false) # try_.append([run_solution(new_moves, new_servo_angles, which_servos), False]) # # create new variables to pass to the next function # new_servo_angles = copy.copy(servo_angles[-1]) # new_servo_angles[which_servos[0]] -= 1 # new_servo_angles[which_servos[1]] += 1 # if validate_servos(new_servo_angles)[0]: # # append to the try_ list the result_moves from the next function, # # and whether the rotation is inverted (true) # try_.append([run_solution(new_moves, new_servo_angles, which_servos), True]) # # find the longest of the three lists # max_ = -1 # max_try_result = None # for y, try_result in enumerate(try_): # if int(try_result[0][1]) > max_: # max_try_result = try_result # # # look for what the try_result indicates whether the rotation is inverted or not. # if max_try_result is None: # return "aaaaaaa" # elif not max_try_result[1]: # result_moves[-1].append(str(which_servos[0]) + "+1") # result_moves[-1].append(str(which_servos[1]) + "-1") # elif max_try_result[1]: # result_moves[-1].append(str(which_servos[0]) + "-1") # result_moves[-1].append(str(which_servos[1]) + "+1") # result_moves += max_try_result[0][0] # return [result_moves, max_try_result[0][1], max_try_result[0][2]] # # return try_[max_index] # # # if the move is a double move, try to pick a direction which works. # # if the direction doesn't work, return the current result_moves. # elif f_move[2] == 2: # servo_num = int(f_move[0]) # if servo_angles[servo_num] == 1: # # spawn two new instances for each possible retraction move # return [result_moves, counter, servo_num] # elif servo_angles[servo_num] == 0: # servo_angles[servo_num] = 2 # result_moves[-1].append(str(servo_num) + "+2") # else: # servo_angles[servo_num] = 0 # result_moves[-1].append(str(servo_num) + "-2") # # for all regular moves, apply them and then check if the servo is still valid # else: # servo_num = int(f_move[0]) # # for forward moves # if f_move[1] == '+': # servo_angles[-1][servo_num] += 1 # if servo_angles[-1][servo_num > 2]: # # spawn two new instances for each possible retraction move # # new_result = copy.copy(result_moves) # # result_moves.append(str(servo_num) + "+1") # return [result_moves, counter, servo_num] # else: # result_moves[-1].append(str(servo_num) + "+1") # # for reverse moves # else: # servo_angles[-1][servo_num] -= 1 # if servo_angles[-1][servo_num < 0]: # return [result_moves, counter, servo_num] # else: # result_moves[-1].append(str(servo_num) + "-1") # counter += 1 # if counter == len(moves) - 1: # return [result_moves, counter, -2] # # def validate_servos(servo_angles): # valid = True # failed = -1 # num = 0 # for a, ang in enumerate(servo_angles): # if ang > 2 or ang < 0: # valid = False # failed = a # num = ang # return valid, failed, num # solve first two lists where we can't change the servo angles # #print( # #print(run_solution(splitAllMoves13[0], [1, 1, 1, 1])) # splitSolutions02 = [] # splitSolution12 = [] # # solve the rest, inputting every possible combination of servo angles (since there are only 9) # for splitMoves in splitAllMoves02[1::]: # calculated_solutions = [] # for s1 in range(3): # for s2 in range(3): # calculated_solutions.append(run_solution(copy.copy(splitMoves), [s1, 0, s2, 0])) # # find the best calculated_solution # maxOptimal = -2147483648 # best_solution = [] # for solution in calculated_solutions: # if solution[1] > maxOptimal: # maxOptimal = solution[1] # best_solution = solution # print(best_solution) # for splitMoves in splitAllMoves13[1::]: # calculated_solutions = [] # for s1 in range(3): # for s2 in range(3): # calculated_solutions.append(run_solution(splitMoves, [0, s1, 0, s2])) # # find the best calculated_solution # maxOptimal = -2147483648 # best_solution = [] # for solution in calculated_solutions: # if solution[1] > maxOptimal: # maxOptimal = solution[1] # best_solution = solution # moves13.append(best_solution) # #print(best_solution) # # next, stitch all of the lists back together # def apply_move(move, servo_angles, inverted): # if move # # first, count the number of tweaks. # tweaksCounter = 0 # for mList in allMoves: # for move in mList: # if move[1] == '2' or move[0] == 'T': # tweaksCounter += 1 # print(tweaksCounter) # # tried = [] # for tweaksTweaker in range(int(math.pow(2, tweaksCounter))): # tried.append(try_moves(format(tweaksTweaker, ('0' + str(tweaksCounter) + 'b')))) # # tried.sort(key=lambda item: item[1], reverse=True) # print(tried[0]) # print(servoAngles) # print(moveList) # if j == len(move_list): # return [moves, millis]11 # elif move_list[j][0] == 'T': # break # cube.servoAngles.append(copy.copy(cube.servoAngles[-1])) # try: # r = add_move(move_list[j], cube) # # if ValueError is thrown, then the move item contains a list of multiple parallel moves. # # handle as such. # except ValueError: # r = [[], []] # for m in move_list[j]: # r1 = add_move(m, cube) # r[0].append(r1[0]) # r[1].append(r1[1]) # # before we append the moves, check if the servoList is in range. # for k, ang in enumerate(cube.servoAngles[-1]): # if ang > 2: # # loop back through the doubles list to find if we can invert a double to fix things # for servo_ang in cube.servoAngles: # elif ang < 0: # moves.append(r[0]) # doubles.append(r[1]) # j += 1 # # spawn two new solveMoves and pick whichever one completes in less time # new_cube = copy.copy(f_cube) # if move_list[j] == 'TRL': # new_cube.faceLocation = f_cube.rotateRL(True) # result1 = solve_moves(move_list[j + 1:], new_cube) # new_cube.faceLocation = f_cube.rotateRL(False) # result2 = solve_moves(move_list[j + 1:], new_cube) # rotate_move = 'RL' # else: # new_cube.faceLocation = f_cube.rotateFB(True) # result1 = solve_moves(move_list[j + 1:], new_cube) # new_cube.faceLocation = f_cube.rotateFB(False) # result2 = solve_moves(move_list[j + 1:], new_cube) # rotate_move = 'FB' # # pick the lowest time solution # if result1[1] < result2[1]: # result = [moves + ["I" + rotate_move] + result1[0], millis + result1[1]] # else: # result = [moves + ["N" + rotate_move] + result1[0], millis + result1[1]] # return result # # I'm putting the function here because even though i'm not supposed to , it makes the code easier to read. # def solve_moves(move_list, f_cube: Cube): # moves = [] # # stores the servo on which a double move occurred. Stores -1 when there is no double move. # doubles = [] # j = 0 # millis = 0 # while True: # if j == len(move_list): # return [moves, millis] # elif move_list[j][0] == 'T': # break # cube.servoAngles.append(copy.copy(cube.servoAngles[-1])) # try: # r = add_move(move_list[j], f_cube) # # if ValueError is thrown, then the move item contains a list of multiple parallel moves. # # handle as such. # except ValueError: # r = [[], []] # for m in move_list[j]: # r1 = add_move(m, f_cube) # r[0].append(r1[0]) # r[1].append(r1[1]) # # before we append the moves, check if the servoList is in range. # for k, ang in enumerate(cube.servoAngles[-1]): # if ang > 2: # # loop back through the doubles list to find if we can invert a double to fix things # for servo_ang in cube.servoAngles: # # elif ang < 0: # moves.append(r[0]) # doubles.append(r[1]) # j += 1 # # spawn two new solveMoves and pick whichever one completes in less time # new_cube = copy.copy(f_cube) # if move_list[j] == 'TRL': # new_cube.faceLocation = f_cube.rotateRL(True) # result1 = solve_moves(move_list[j + 1:], new_cube) # new_cube.faceLocation = f_cube.rotateRL(False) # result2 = solve_moves(move_list[j + 1:], new_cube) # rotate_move = 'RL' # else: # new_cube.faceLocation = f_cube.rotateFB(True) # result1 = solve_moves(move_list[j + 1:], new_cube) # new_cube.faceLocation = f_cube.rotateFB(False) # result2 = solve_moves(move_list[j + 1:], new_cube) # rotate_move = 'FB' # # pick the lowest time solution # if result1[1] < result2[1]: # result = [moves + ["I" + rotate_move] + result1[0], millis + result1[1]] # else: # result = [moves + ["N" + rotate_move] + result1[0], millis + result1[1]] # return result # # # modifies cube and returns the new move # def add_move(_move, f_cube: Cube): # num = int(_move[1]) # servo = f_cube.faceLocation[_move[0]] # if num == 2: # doubles = servo # else: # doubles = -1 # if num != 3: # f_cube.servoAngles[-1][servo] += num # else: # f_cube.servoAngles[-1][servo] -= 1 # return _move, doubles # cube = Cube() # final_solution = solve_moves(allMoves, cube) # print(cube.servoAngles) # print(final_solution) # print(servoMoves) # print(servoAngles) # # # write to serial port # print(num) # print(len(servoMoves)) # # servoSend = "" # for signal in servoMoves: # if len(signal) == 2: # servoSend = servoSend + signal + "zz" # # else: # servoSend = servoSend + signal # print(servoSend) # arduino.write(bytes(servoSend, 'utf-8')) # for signal in servoMoves: # arduino.write(bytes(signal, 'utf-8')) # time.sleep(1.2) # See PyCharm help at https://www.jetbrains.com/help/pycharm/