Ejemplo n.º 1
0
    def run(self):
        cb = None
        if self.rot == 0:  # no rotation
            cb = cubie.CubieCube(self.cb_cube.cp, self.cb_cube.co,
                                 self.cb_cube.ep, self.cb_cube.eo)
        elif self.rot == 1:  # conjugation by 120° rotation
            cb = cubie.CubieCube(sy.symCube[32].cp, sy.symCube[32].co,
                                 sy.symCube[32].ep, sy.symCube[32].eo)
            cb.multiply(self.cb_cube)
            cb.multiply(sy.symCube[16])
        elif self.rot == 2:  # conjugation by 240° rotation
            cb = cubie.CubieCube(sy.symCube[16].cp, sy.symCube[16].co,
                                 sy.symCube[16].ep, sy.symCube[16].eo)
            cb.multiply(self.cb_cube)
            cb.multiply(sy.symCube[32])
        if self.inv == 1:  # invert cube
            tmp = cubie.CubieCube()
            cb.inv_cubie_cube(tmp)
            cb = tmp

        self.co_cube = coord.CoordCube(
            cb)  # the rotated/inverted cube in coordinate representation

        dist = self.co_cube.get_depth_phase1()
        for togo1 in range(
                dist,
                20):  # iterative deepening, solution has at least dist moves
            self.sofar_phase1 = []
            self.search(self.co_cube.flip, self.co_cube.twist,
                        self.co_cube.slice_sorted, dist, togo1)
Ejemplo n.º 2
0
def create_phase2_edgemerge_table():
    """phase2_edgemerge retrieves the initial phase 2 ud_edges coordinate from the u_edges and d_edges coordinates."""
    fname = "phase2_edgemerge"
    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(path.join(FOLDER, fname)):
        cnt = 0
        print("creating " + fname + " table...")
        u_edges_plus_d_edges_to_ud_edges = ar.array(
            'H', [0 for _ 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(path.join(FOLDER, fname), "wb")
        u_edges_plus_d_edges_to_ud_edges.tofile(fh)
        fh.close()
        print()
    else:
        print("loading " + fname + " table...")
        fh = open(path.join(FOLDER, fname), "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)
Ejemplo n.º 3
0
def solveto(cubestring, goalstring, max_length=20, timeout=3):
    """Solve a cube defined by cubstring to a position defined by goalstring.
     :param cubestring: The format of the string is given in the Facelet class defined in the file enums.py
     :param goalstring: The format of the string is given in the Facelet class defined in the file enums.py
     :param max_length: The function will return if a maneuver of length <= max_length has been found
     :param timeout: If the function times out, the best solution found so far is returned. If there has not been found
     any solution yet the computation continues until a first solution appears.
    """
    fc0 = face.FaceCube()
    fcg = face.FaceCube()
    s = fc0.from_string(cubestring)
    if s != cubie.CUBE_OK:
        return 'first cube ' + s  # no valid cubestring, gives invalid facelet cube
    s = fcg.from_string(goalstring)
    if s != cubie.CUBE_OK:
        return 'second cube ' + s  # no valid goalstring, gives invalid facelet cube
    cc0 = fc0.to_cubie_cube()
    s = cc0.verify()
    if s != cubie.CUBE_OK:
        return 'first cube ' + s  # no valid facelet cube, gives invalid cubie cube
    ccg = fcg.to_cubie_cube()
    s = ccg.verify()
    if s != cubie.CUBE_OK:
        return 'second cube ' + s  # no valid facelet cube, gives invalid cubie cube
    # cc0 * S = ccg  <=> (ccg^-1 * cc0) * S = Id
    cc = cubie.CubieCube()
    ccg.inv_cubie_cube(cc)
    cc.multiply(cc0)

    my_threads = []
    s_time = time.monotonic()

    # these mutable variables are modidified by all six threads
    s_length = [999]
    solutions = []
    terminated = thr.Event()
    terminated.clear()
    syms = cc.symmetries()
    if len(list({16, 20, 24, 28} & set(syms))
           ) > 0:  # we have some rotational symmetry along a long diagonal
        tr = [0, 3]  # so we search only one direction and the inverse
    else:
        tr = range(6)  # This means search in 3 directions + inverse cube
    if len(list(set(range(48, 96)) & set(syms))
           ) > 0:  # we have some antisymmetry so we do not search the inverses
        tr = list(filter(lambda x: x < 3, tr))
    for i in tr:
        th = SolverThread(cc, i % 3, i // 3, max_length, timeout, s_time,
                          solutions, terminated, [999])
        my_threads.append(th)
        th.start()
    for t in my_threads:
        t.join()  # wait until all threads have finished
    s = ''
    if len(solutions) > 0:
        for m in solutions[-1]:  # the last solution is the shortest
            s += m.name + ' '
    return s + '(' + str(len(s) // 3) + 'f)'
def random():
    """Generate a random cube and set the corresponding facelet colors."""
    cc = cubie.CubieCube()
    cc.randomize()
    fc = cc.to_facelet_cube()
    idx = 0
    for f in range(6):
        for row in range(3):
            for col in range(3):
                canvas.itemconfig(facelet_id[f][row][col],
                                  fill=cols[fc.f[idx]])
                idx += 1
Ejemplo n.º 5
0
def test(n, t):
    """
    :param n: The number of generated random cubes
    :param t: The time in seconds to spend on each cube
    :return: A dictionary with the solving statistics
    """
    cc = cubie.CubieCube()
    cnt = [0] * 31
    for i in range(n):
        cc.randomize()
        fc = cc.to_facelet_cube()
        s = fc.to_string()
        print(s)
        s = sv.solve(s, 0, t)
        print(s)
        print()
        cnt[int(s.split('(')[1].split('f')[0])] += 1
    avr = 0
    for i in range(31):
        avr += i * cnt[i]
    avr /= n
    return 'average ' + '%.2f' % avr + ' moves', dict(zip(range(31), cnt))
epROT_F2 = [Ed.DL, Ed.DF, Ed.DR, Ed.DB, Ed.UL, Ed.UF, Ed.UR, Ed.UB, Ed.FL, Ed.FR, Ed.BR, Ed.BL]
eoROT_F2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

# 90° clockwise rotation around the axis through the U and D centers
cpROT_U4 = [Co.UBR, Co.URF, Co.UFL, Co.ULB, Co.DRB, Co.DFR, Co.DLF, Co.DBL]
coROT_U4 = [0, 0, 0, 0, 0, 0, 0, 0]
epROT_U4 = [Ed.UB, Ed.UR, Ed.UF, Ed.UL, Ed.DB, Ed.DR, Ed.DF, Ed.DL, Ed.BR, Ed.FR, Ed.FL, Ed.BL]
eoROT_U4 = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]

# reflection at the plane through the U, D, F, B centers
cpMIRR_LR2 = [Co.UFL, Co.URF, Co.UBR, Co.ULB, Co.DLF, Co.DFR, Co.DRB, Co.DBL]
coMIRR_LR2 = [3, 3, 3, 3, 3, 3, 3, 3]
epMIRR_LR2 = [Ed.UL, Ed.UF, Ed.UR, Ed.UB, Ed.DL, Ed.DF, Ed.DR, Ed.DB, Ed.FL, Ed.FR, Ed.BR, Ed.BL]
eoMIRR_LR2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

basicSymCube = [cb.CubieCube()] * 4
basicSymCube[BS.ROT_URF3] = cb.CubieCube(cpROT_URF3, coROT_URF3, epROT_URF3, eoROT_URF3)
basicSymCube[BS.ROT_F2] = cb.CubieCube(cpROT_F2, coROT_F2, epROT_F2, eoROT_F2)
basicSymCube[BS.ROT_U4] = cb.CubieCube(cpROT_U4, coROT_U4, epROT_U4, eoROT_U4)
basicSymCube[BS.MIRR_LR2] = cb.CubieCube(cpMIRR_LR2, coMIRR_LR2, epMIRR_LR2, eoMIRR_LR2)
# ######################################################################################################################

# ######################################## Fill SymCube list ###########################################################

# 48 CubieCubes will represent the 48 cube symmetries
symCube = []
cc = cb.CubieCube()  # Identity cube
idx = 0
for urf3 in range(3):
    for f2 in range(2):
        for u4 in range(4):
Ejemplo n.º 7
0
def create_phase1_prun_table():
    """Create/load the flipslice_twist_depth3 pruning table for phase 1."""
    global flipslice_twist_depth3
    total = defs.N_FLIPSLICE_CLASS * defs.N_TWIST
    fname = "phase1_prun"
    if not path.isfile(path.join(defs.FOLDER, fname)):
        print("creating " + fname + " 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  # controls the output a few lines below
            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 k in range(1, 16):
                                            sym >>= 1
                                            if sym % 2 == 1:
                                                twist2 = sy.twist_conj[
                                                    (twist1 << 4) + k]
                                                # 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(path.join(defs.FOLDER, fname), "wb")
        flipslice_twist_depth3.tofile(fh)
    else:
        print("loading " + fname + " table...")
        fh = open(path.join(defs.FOLDER, fname), "rb")
        flipslice_twist_depth3 = ar.array('L')
        flipslice_twist_depth3.fromfile(fh, total // 16 + 1)
    fh.close()
Ejemplo n.º 8
0
def create_phase2_prun_table():
    """Create/load the corners_ud_edges_depth3 pruning table for phase 2."""
    total = defs.N_CORNERS_CLASS * defs.N_UD_EDGES
    fname = "phase2_prun"
    global corners_ud_edges_depth3
    if not path.isfile(path.join(defs.FOLDER, fname)):
        print("creating " + fname + " 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 k in range(1, 16):
                                        sym >>= 1
                                        if sym % 2 == 1:
                                            ud_edge2 = sy.ud_edges_conj[
                                                (ud_edge1 << 4) + k]
                                            # 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(path.join(defs.FOLDER, fname), "wb")
        corners_ud_edges_depth3.tofile(fh)
    else:
        print("loading " + fname + " table...")
        fh = open(path.join(defs.FOLDER, fname), "rb")
        corners_ud_edges_depth3 = ar.array('L')
        corners_ud_edges_depth3.fromfile(fh, total // 16)

    fh.close()
Ejemplo n.º 9
0
# ################### Movetables describe the transformation of the coordinates by cube moves. #########################

from os import path
import array as ar
import twophase.cubie as cb
import twophase.enums as enums
from twophase.defs import FOLDER, N_TWIST, N_FLIP, N_SLICE_SORTED, N_CORNERS, N_UD_EDGES, N_MOVE

a = cb.CubieCube()
# ######################################### Move table for the twists of the corners. ##################################

# The twist coordinate describes the 3^7 = 2187 possible orientations of the 8 corners
# 0 <= twist < 2187 in phase 1, twist = 0 in phase 2
fname = "move_twist"
if not path.isfile(path.join(FOLDER, fname)):
    print("creating " + fname + " 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(path.join(FOLDER, fname), "wb")
    twist_move.tofile(fh)
else:
    print("loading " + fname + " table...")
    fh = open(path.join(FOLDER, fname), "rb")
    twist_move = ar.array('H')
    twist_move.fromfile(fh, N_TWIST * N_MOVE)