def create_phase2_edgemerge_table(): """phase2_edgemerge retrieves the initial phase 2 ud_edges coordinate from the u_edges and d_edges coordinates.""" table_name = "phase2_edgemerge" table_path = get_pruning_table_path(table_name) global u_edges_plus_d_edges_to_ud_edges c_u = cb.CubieCube() c_d = cb.CubieCube() c_ud = cb.CubieCube() edge_u = [Ed.UR, Ed.UF, Ed.UL, Ed.UB] edge_d = [Ed.DR, Ed.DF, Ed.DL, Ed.DB] edge_ud = [Ed.UR, Ed.UF, Ed.UL, Ed.UB, Ed.DR, Ed.DF, Ed.DL, Ed.DB] if not path.isfile(table_path): cnt = 0 print("creating " + table_name + " table...") u_edges_plus_d_edges_to_ud_edges = ar.array( 'H', [0 for i in range(N_U_EDGES_PHASE2 * N_PERM_4)]) for i in range(N_U_EDGES_PHASE2): c_u.set_u_edges(i) for j in range(N_CHOOSE_8_4): c_d.set_d_edges(j * N_PERM_4) invalid = False for e in edge_ud: c_ud.ep[e] = -1 # invalidate edges if c_u.ep[e] in edge_u: c_ud.ep[e] = c_u.ep[e] if c_d.ep[e] in edge_d: c_ud.ep[e] = c_d.ep[e] if c_ud.ep[e] == -1: invalid = True # edge collision break if not invalid: for k in range(N_PERM_4): c_d.set_d_edges(j * N_PERM_4 + k) for e in edge_ud: if c_u.ep[e] in edge_u: c_ud.ep[e] = c_u.ep[e] if c_d.ep[e] in edge_d: c_ud.ep[e] = c_d.ep[e] u_edges_plus_d_edges_to_ud_edges[ N_PERM_4 * i + k] = c_ud.get_ud_edges() cnt += 1 if cnt % 2000 == 0: print('.', end='', flush=True) print() fh = open(table_path, "wb") u_edges_plus_d_edges_to_ud_edges.tofile(fh) fh.close() print() else: fh = open(table_path, "rb") u_edges_plus_d_edges_to_ud_edges = ar.array('H') u_edges_plus_d_edges_to_ud_edges.fromfile(fh, N_U_EDGES_PHASE2 * N_PERM_4)
def create_phase2_cornsliceprun_table(): """Creates/loads the cornslice_depth pruning table for phase 2. With this table we do a fast precheck at the beginning of phase 2.""" table_name = "phase2_cornsliceprun" table_path = get_pruning_table_path(table_name) global cornslice_depth if not path.isfile(table_path): print("creating " + table_name + " table...") cornslice_depth = ar.array('b', [-1] * (defs.N_CORNERS * defs.N_PERM_4)) corners = 0 # values for solved phase 2 slice_ = 0 cornslice_depth[defs.N_PERM_4 * corners + slice_] = 0 done = 1 depth = 0 idx = 0 while done != defs.N_CORNERS * defs.N_PERM_4: for corners in range(defs.N_CORNERS): for slice_ in range(defs.N_PERM_4): if cornslice_depth[defs.N_PERM_4 * corners + slice_] == depth: for m in (enums.Move.U1, enums.Move.U2, enums.Move.U3, enums.Move.R2, enums.Move.F2, enums.Move.D1, enums.Move.D2, enums.Move.D3, enums.Move.L2, enums.Move.B2): corners1 = mv.corners_move[18 * corners + m] slice_1 = mv.slice_sorted_move[18 * slice_ + m] idx1 = defs.N_PERM_4 * corners1 + slice_1 if cornslice_depth[ idx1] == -1: # entry not yet filled cornslice_depth[idx1] = depth + 1 done += 1 if done % 20000 == 0: print('.', end='', flush=True) depth += 1 print() fh = open(table_path, "wb") cornslice_depth.tofile(fh) else: # print("loading " + table_name + " table...") fh = open(table_path, "rb") cornslice_depth = ar.array('b') cornslice_depth.fromfile(fh, defs.N_CORNERS * defs.N_PERM_4) fh.close()
# #### generate the table for the conjugation of a move m by a symmetry s. conj_move[m, s] = s*m*s^-1################### conj_move = np.empty([N_MOVE, N_SYM], dtype=np.uint8) for s in range(N_SYM): for m in Mv: ss = cb.CubieCube(symCube[s].cp, symCube[s].co, symCube[s].ep, symCube[s].eo) # copy cube ss.multiply(cb.moveCube[m]) # s*m ss.multiply(symCube[inv_idx[s]]) # s*m*s^-1 for m2 in Mv: if ss == cb.moveCube[m2]: conj_move[m][s] = m2 ######################################################################################################################## # ####### generate the phase 1 table for the conjugation of the twist t by a symmetry s. conj_twist[t, s] = s*t*s^-1#### table_name = "conj_twist" table_path = get_pruning_table_path(table_name) if not path.isfile(table_path): print('On the first run, several tables will be created. This takes from 1/2 hour (e.g. PC) to 6 hours ' '(e.g. RaspberryPi3), depending on the hardware.') print("creating " + table_name + " table...") twist_conj = ar.array('H', [0] * (N_TWIST * N_SYM_D4h)) for t in range(N_TWIST): cc = cb.CubieCube() cc.set_twist(t) for s in range(N_SYM_D4h): ss = cb.CubieCube(symCube[s].cp, symCube[s].co, symCube[s].ep, symCube[s].eo) # copy cube ss.corner_multiply(cc) # s*t ss.corner_multiply(symCube[inv_idx[s]]) # s*t*s^-1 twist_conj[N_SYM_D4h * t + s] = ss.get_twist() fh = open(table_path, "wb") twist_conj.tofile(fh)
def create_phase1_prun_table(): """Creates/loads the flipslice_twist_depth3 pruning table for phase 1.""" global flipslice_twist_depth3 total = defs.N_FLIPSLICE_CLASS * defs.N_TWIST table_name = "phase1_prun" table_path = get_pruning_table_path(table_name) if not path.isfile(table_path): print("creating " + table_name + " table...") print( 'This may take half an hour or even longer, depending on the hardware.' ) flipslice_twist_depth3 = ar.array('L', [0xffffffff] * (total // 16 + 1)) # #################### create table with the symmetries of the flipslice classes ############################### cc = cb.CubieCube() fs_sym = ar.array('H', [0] * defs.N_FLIPSLICE_CLASS) for i in range(defs.N_FLIPSLICE_CLASS): if (i + 1) % 1000 == 0: print('.', end='', flush=True) rep = sy.flipslice_rep[i] cc.set_slice(rep // defs.N_FLIP) cc.set_flip(rep % defs.N_FLIP) for s in range(defs.N_SYM_D4h): ss = cb.CubieCube(sy.symCube[s].cp, sy.symCube[s].co, sy.symCube[s].ep, sy.symCube[s].eo) # copy cube ss.edge_multiply(cc) # s*cc ss.edge_multiply(sy.symCube[sy.inv_idx[s]]) # s*cc*s^-1 if ss.get_slice() == rep // defs.N_FLIP and ss.get_flip( ) == rep % defs.N_FLIP: fs_sym[i] |= 1 << s print() # ################################################################################################################## fs_classidx = 0 # value for solved phase 1 twist = 0 set_flipslice_twist_depth3(defs.N_TWIST * fs_classidx + twist, 0) done = 1 depth = 0 backsearch = False print('depth:', depth, 'done: ' + str(done) + '/' + str(total)) while done != total: depth3 = depth % 3 if depth == 9: # backwards search is faster for depth >= 9 print('flipping to backwards search...') backsearch = True if depth < 8: mult = 5 else: mult = 1 idx = 0 for fs_classidx in range(defs.N_FLIPSLICE_CLASS): if (fs_classidx + 1) % (200 * mult) == 0: print('.', end='', flush=True) if (fs_classidx + 1) % (16000 * mult) == 0: print('') twist = 0 while twist < defs.N_TWIST: # ########## if table entries are not populated, this is very fast: ################################ if not backsearch and idx % 16 == 0 and flipslice_twist_depth3[idx // 16] == 0xffffffff \ and twist < defs.N_TWIST - 16: twist += 16 idx += 16 continue #################################################################################################### if backsearch: match = (get_flipslice_twist_depth3(idx) == 3) else: match = (get_flipslice_twist_depth3(idx) == depth3) if match: flipslice = sy.flipslice_rep[fs_classidx] flip = flipslice % 2048 # defs.N_FLIP = 2048 slice_ = flipslice >> 11 # // defs.N_FLIP for m in enums.Move: twist1 = mv.twist_move[18 * twist + m] # defs.N_MOVE = 18 flip1 = mv.flip_move[18 * flip + m] slice1 = mv.slice_sorted_move[ 432 * slice_ + m] // 24 # defs.N_PERM_4 = 24, 18*24 = 432 flipslice1 = (slice1 << 11) + flip1 fs1_classidx = sy.flipslice_classidx[flipslice1] fs1_sym = sy.flipslice_sym[flipslice1] twist1 = sy.twist_conj[(twist1 << 4) + fs1_sym] idx1 = 2187 * fs1_classidx + twist1 # defs.N_TWIST = 2187 if not backsearch: if get_flipslice_twist_depth3( idx1) == 3: # entry not yet filled set_flipslice_twist_depth3( idx1, (depth + 1) % 3) done += 1 # ####symmetric position has eventually more than one representation ############### sym = fs_sym[fs1_classidx] if sym != 1: for j in range(1, 16): sym >>= 1 if sym % 2 == 1: twist2 = sy.twist_conj[ (twist1 << 4) + j] # fs2_classidx = fs1_classidx due to symmetry idx2 = 2187 * fs1_classidx + twist2 if get_flipslice_twist_depth3( idx2) == 3: set_flipslice_twist_depth3( idx2, (depth + 1) % 3) done += 1 #################################################################################### else: # backwards search if get_flipslice_twist_depth3(idx1) == depth3: set_flipslice_twist_depth3( idx, (depth + 1) % 3) done += 1 break twist += 1 idx += 1 # idx = defs.N_TWIST * fs_class + twist depth += 1 print() print('depth:', depth, 'done: ' + str(done) + '/' + str(total)) fh = open(table_path, "wb") flipslice_twist_depth3.tofile(fh) else: # print("loading " + table_name + " table...") fh = open(table_path, "rb") flipslice_twist_depth3 = ar.array('L') flipslice_twist_depth3.fromfile(fh, total // 16 + 1) fh.close()
def create_phase2_prun_table(): """Creates/loads the corners_ud_edges_depth3 pruning table for phase 2.""" total = defs.N_CORNERS_CLASS * defs.N_UD_EDGES table_name = "phase2_prun" table_path = get_pruning_table_path(table_name) global corners_ud_edges_depth3 if not path.isfile(table_path): print("creating " + table_name + " table...") corners_ud_edges_depth3 = ar.array('L', [0xffffffff] * (total // 16)) # ##################### create table with the symmetries of the corners classes ################################ cc = cb.CubieCube() c_sym = ar.array('H', [0] * defs.N_CORNERS_CLASS) for i in range(defs.N_CORNERS_CLASS): if (i + 1) % 1000 == 0: print('.', end='', flush=True) rep = sy.corner_rep[i] cc.set_corners(rep) for s in range(defs.N_SYM_D4h): ss = cb.CubieCube(sy.symCube[s].cp, sy.symCube[s].co, sy.symCube[s].ep, sy.symCube[s].eo) # copy cube ss.corner_multiply(cc) # s*cc ss.corner_multiply(sy.symCube[sy.inv_idx[s]]) # s*cc*s^-1 if ss.get_corners() == rep: c_sym[i] |= 1 << s print() ################################################################################################################ c_classidx = 0 # value for solved phase 2 ud_edge = 0 set_corners_ud_edges_depth3(defs.N_UD_EDGES * c_classidx + ud_edge, 0) done = 1 depth = 0 print('depth:', depth, 'done: ' + str(done) + '/' + str(total)) while depth < 10: # we fill the table only do depth 9 + 1 depth3 = depth % 3 idx = 0 mult = 2 if depth > 9: mult = 1 for c_classidx in range(defs.N_CORNERS_CLASS): if (c_classidx + 1) % (20 * mult) == 0: print('.', end='', flush=True) if (c_classidx + 1) % (1600 * mult) == 0: print('') ud_edge = 0 while ud_edge < defs.N_UD_EDGES: # ################ if table entries are not populated, this is very fast: ########################## if idx % 16 == 0 and corners_ud_edges_depth3[idx // 16] == 0xffffffff \ and ud_edge < defs.N_UD_EDGES - 16: ud_edge += 16 idx += 16 continue #################################################################################################### if get_corners_ud_edges_depth3(idx) == depth3: corner = sy.corner_rep[c_classidx] # only iterate phase 2 moves for m in (enums.Move.U1, enums.Move.U2, enums.Move.U3, enums.Move.R2, enums.Move.F2, enums.Move.D1, enums.Move.D2, enums.Move.D3, enums.Move.L2, enums.Move.B2): ud_edge1 = mv.ud_edges_move[18 * ud_edge + m] corner1 = mv.corners_move[18 * corner + m] c1_classidx = sy.corner_classidx[corner1] c1_sym = sy.corner_sym[corner1] ud_edge1 = sy.ud_edges_conj[(ud_edge1 << 4) + c1_sym] idx1 = 40320 * c1_classidx + ud_edge1 # N_UD_EDGES = 40320 if get_corners_ud_edges_depth3( idx1) == 3: # entry not yet filled set_corners_ud_edges_depth3( idx1, (depth + 1) % 3) # depth + 1 <= 10 done += 1 # ######symmetric position has eventually more than one representation ############# sym = c_sym[c1_classidx] if sym != 1: for j in range(1, 16): sym >>= 1 if sym % 2 == 1: ud_edge2 = sy.ud_edges_conj[ (ud_edge1 << 4) + j] # c1_classidx does not change idx2 = 40320 * c1_classidx + ud_edge2 if get_corners_ud_edges_depth3( idx2) == 3: set_corners_ud_edges_depth3( idx2, (depth + 1) % 3) done += 1 #################################################################################### ud_edge += 1 idx += 1 # idx = defs.N_UD_EDGEPERM * corner_classidx + ud_edge depth += 1 print() print('depth:', depth, 'done: ' + str(done) + '/' + str(total)) print('remaining unfilled entries have depth >=11') fh = open(table_path, "wb") corners_ud_edges_depth3.tofile(fh) else: # print("loading " + table_name + " table...") fh = open(table_path, "rb") corners_ud_edges_depth3 = ar.array('L') corners_ud_edges_depth3.fromfile(fh, total // 16) fh.close()
# ################### Movetables describe the transformation of the coordinates by cube moves. ######################### from os import path import array as ar import cubie as cb import enums from defs import N_TWIST, N_FLIP, N_SLICE_SORTED, N_CORNERS, N_UD_EDGES, N_MOVE from misc import get_pruning_table_path a = cb.CubieCube() # ########### Move table for the twists of the corners. twist < 2187 in phase 1, twist = 0 in phase 2. ################# # The twist coordinate describes the 3^7 = 2187 possible orientations of the 8 corners table_name = "move_twist" table_path = get_pruning_table_path(table_name) if not path.isfile(table_path): print("creating " + table_name + " table...") twist_move = ar.array('H', [0 for i in range(N_TWIST * N_MOVE)]) for i in range(N_TWIST): a.set_twist(i) for j in enums.Color: # six faces U, R, F, D, L, B for k in range( 3 ): # three moves for each face, for example U, U2, U3 = U' a.corner_multiply(cb.basicMoveCube[j]) twist_move[N_MOVE * i + 3 * j + k] = a.get_twist() a.corner_multiply(cb.basicMoveCube[j]) # 4. move restores face fh = open(table_path, "wb") twist_move.tofile(fh) else: # print("loading " + table_name + " table...")