Skip to content

Commit

Permalink
Merge pull request #4 from muodov/py3
Browse files Browse the repository at this point in the history
Py3 compatibility
  • Loading branch information
muodov committed Dec 24, 2015
2 parents d2097c2 + d590152 commit 993c616
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 143 deletions.
27 changes: 22 additions & 5 deletions kociemba/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,42 @@ class SlowContextWarning(Warning):

try:
import os
from ckociembawrapper import ffi, lib
from .ckociembawrapper import ffi, lib
cache_dir = os.path.join(os.path.dirname(__file__), 'cprunetables')

def _solve(s):
res = lib.solve(s.encode('utf-8'), cache_dir.encode('utf-8'))
if res != ffi.NULL:
return ffi.string(res).strip()
return ffi.string(res).strip().decode('utf-8')
else:
raise ValueError('Error. Probably cubestring is invalid')
except ImportError:
except ImportError as e:
print(e)
warnings.warn("Native version of the package is not available. "
"We have to fallback to pure-Python implementation of "
"the algorithm, which is usually many times slower. "
"If performance is important to you, check official "
"project page for a native implementation: "
"https://github.com/muodov/kociemba",
SlowContextWarning)
import pykociemba.search
_solve = lambda s: pykociemba.search.Search().solution(s, 24, 1000, False).strip()
from .pykociemba import search

def _solve(s):
errors = {
'Error 1': 'There is not exactly one facelet of each colour',
'Error 2': 'Not all 12 edges exist exactly once',
'Error 3': 'Flip error: One edge has to be flipped',
'Error 4': 'Not all corners exist exactly once',
'Error 5': 'Twist error: One corner has to be twisted',
'Error 6': 'Parity error: Two corners or two edges have to be exchanged',
'Error 7': 'No solution exists for the given maxDepth',
'Error 8': 'Timeout, no solution within given time'
}
res = search.Search().solution(s, 24, 1000, False).strip()
if res in errors:
raise ValueError(errors[res])
else:
return res


def solve(cubestring):
Expand Down
129 changes: 69 additions & 60 deletions kociemba/pykociemba/coordcube.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,51 @@
from builtins import range
import logging
import os.path
import cPickle

try:
import cPickle
except ImportError:
import pickle as cPickle

from .cubiecube import CubieCube, moveCube, getURtoDF

log = logging.getLogger(__name__)

cache_dir = os.path.join(os.path.dirname(__file__), 'prunetables')


def setPruning(table, index, value):
"""Set pruning value in table. Two values are stored in one byte."""
if ((index & 1) == 0):
table[index / 2] &= 0xf0 | value
table[index // 2] &= 0xf0 | value
else:
table[index / 2] &= 0x0f | (value << 4)
table[index // 2] &= 0x0f | (value << 4)
# table[index] = value & 0xf


def getPruning(table, index):
"""Extract pruning value"""

if ((index & 1) == 0):
res = table[index / 2] & 0x0f
res = table[index // 2] & 0x0f
else:
res = (table[index / 2] & 0xf0) >> 4
res = (table[index // 2] & 0xf0) >> 4
return res
# return table[index] & 0xf


def load_cachetable(name):
obj = None
try:
with open(os.path.join(cache_dir, name + '.pkl')) as f:
with open(os.path.join(cache_dir, name + '.pkl'), 'rb') as f:
obj = cPickle.load(f)
except IOError as e:
log.warning('could not read cache for %s: %s. Recalculating it...', name, e)
return obj


def dump_cachetable(obj, name):
with open(os.path.join(cache_dir, name + '.pkl'), 'w') as f:
with open(os.path.join(cache_dir, name + '.pkl'), 'wb') as f:
cPickle.dump(obj, f)


Expand Down Expand Up @@ -111,12 +120,12 @@ def move(self, m):
# twist = 0 in phase 2.
twistMove = load_cachetable('twistMove')
if not twistMove:
twistMove = [[0] * N_MOVE for i in xrange(N_TWIST)] # new short[N_TWIST][N_MOVE]
twistMove = [[0] * N_MOVE for i in range(N_TWIST)] # new short[N_TWIST][N_MOVE]
a = CubieCube()
for i in xrange(N_TWIST):
for i in range(N_TWIST):
a.setTwist(i)
for j in xrange(6):
for k in xrange(3):
for j in range(6):
for k in range(3):
a.cornerMultiply(moveCube[j])
twistMove[i][3 * j + k] = a.getTwist()
a.cornerMultiply(moveCube[j]) # 4. faceturn restores
Expand All @@ -131,12 +140,12 @@ def move(self, m):

flipMove = load_cachetable('flipMove')
if not flipMove:
flipMove = [[0] * N_MOVE for i in xrange(N_FLIP)] # new short[N_FLIP][N_MOVE]
flipMove = [[0] * N_MOVE for i in range(N_FLIP)] # new short[N_FLIP][N_MOVE]
a = CubieCube()
for i in xrange(N_FLIP):
for i in range(N_FLIP):
a.setFlip(i)
for j in xrange(6):
for k in xrange(3):
for j in range(6):
for k in range(3):
a.edgeMultiply(moveCube[j])
flipMove[i][3 * j + k] = a.getFlip()
a.edgeMultiply(moveCube[j])
Expand All @@ -147,8 +156,8 @@ def move(self, m):
# Parity of the corner permutation. This is the same as the parity for the edge permutation of a valid cube.
# parity has values 0 and 1
parityMove = [
[ 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1 ],
[ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 ],
[1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1],
[0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0],
]

# ***********************************Phase 1 and 2 movetable********************************************************
Expand All @@ -161,12 +170,12 @@ def move(self, m):

FRtoBR_Move = load_cachetable('FRtoBR_Move')
if not FRtoBR_Move:
FRtoBR_Move = [[0] * N_MOVE for i in xrange(N_FRtoBR)] # new short[N_FRtoBR][N_MOVE]
FRtoBR_Move = [[0] * N_MOVE for i in range(N_FRtoBR)] # new short[N_FRtoBR][N_MOVE]
a = CubieCube()
for i in xrange(N_FRtoBR):
for i in range(N_FRtoBR):
a.setFRtoBR(i)
for j in xrange(6):
for k in xrange(3):
for j in range(6):
for k in range(3):
a.edgeMultiply(moveCube[j])
FRtoBR_Move[i][3 * j + k] = a.getFRtoBR()
a.edgeMultiply(moveCube[j])
Expand All @@ -182,12 +191,12 @@ def move(self, m):
log.info('Preparing move table for permutation of six corners. The positions of the DBL and DRB corners are determined by the parity.')
URFtoDLF_Move = load_cachetable('URFtoDLF_Move')
if not URFtoDLF_Move:
URFtoDLF_Move = [[0] * N_MOVE for i in xrange(N_URFtoDLF)] # new short[N_URFtoDLF][N_MOVE]
URFtoDLF_Move = [[0] * N_MOVE for i in range(N_URFtoDLF)] # new short[N_URFtoDLF][N_MOVE]
a = CubieCube()
for i in xrange(N_URFtoDLF):
for i in range(N_URFtoDLF):
a.setURFtoDLF(i)
for j in xrange(6):
for k in xrange(3):
for j in range(6):
for k in range(3):
a.cornerMultiply(moveCube[j])
URFtoDLF_Move[i][3 * j + k] = a.getURFtoDLF()
a.cornerMultiply(moveCube[j])
Expand All @@ -202,12 +211,12 @@ def move(self, m):
log.info('Preparing move table for the permutation of six U-face and D-face edges in phase2. The positions of the DL and DB edges are')
URtoDF_Move = load_cachetable('URtoDF_Move')
if not URtoDF_Move:
URtoDF_Move = [[0] * N_MOVE for i in xrange(N_URtoDF)] # new short[N_URtoDF][N_MOVE]
URtoDF_Move = [[0] * N_MOVE for i in range(N_URtoDF)] # new short[N_URtoDF][N_MOVE]
a = CubieCube()
for i in xrange(N_URtoDF):
for i in range(N_URtoDF):
a.setURtoDF(i)
for j in xrange(6):
for k in xrange(3):
for j in range(6):
for k in range(3):
a.edgeMultiply(moveCube[j])
URtoDF_Move[i][3 * j + k] = a.getURtoDF()
# Table values are only valid for phase 2 moves!
Expand All @@ -221,12 +230,12 @@ def move(self, m):
log.info('Preparing move table for the three edges UR,UF and UL in phase1.')
URtoUL_Move = load_cachetable('URtoUL_Move')
if not URtoUL_Move:
URtoUL_Move = [[0] * N_MOVE for i in xrange(N_URtoUL)] # new short[N_URtoUL][N_MOVE]
URtoUL_Move = [[0] * N_MOVE for i in range(N_URtoUL)] # new short[N_URtoUL][N_MOVE]
a = CubieCube()
for i in xrange(N_URtoUL):
for i in range(N_URtoUL):
a.setURtoUL(i)
for j in xrange(6):
for k in xrange(3):
for j in range(6):
for k in range(3):
a.edgeMultiply(moveCube[j])
URtoUL_Move[i][3 * j + k] = a.getURtoUL()
a.edgeMultiply(moveCube[j])
Expand All @@ -237,12 +246,12 @@ def move(self, m):
log.info('Preparing move table for the three edges UB,DR and DF in phase1.')
UBtoDF_Move = load_cachetable('UBtoDF_Move')
if not UBtoDF_Move:
UBtoDF_Move = [[0] * N_MOVE for i in xrange(N_UBtoDF)] # new short[N_UBtoDF][N_MOVE]
UBtoDF_Move = [[0] * N_MOVE for i in range(N_UBtoDF)] # new short[N_UBtoDF][N_MOVE]
a = CubieCube()
for i in xrange(N_UBtoDF):
for i in range(N_UBtoDF):
a.setUBtoDF(i)
for j in xrange(6):
for k in xrange(3):
for j in range(6):
for k in range(3):
a.edgeMultiply(moveCube[j])
UBtoDF_Move[i][3 * j + k] = a.getUBtoDF()
a.edgeMultiply(moveCube[j])
Expand All @@ -253,11 +262,11 @@ def move(self, m):
log.info('Preparing table to merge the coordinates of the UR,UF,UL and UB,DR,DF edges at the beginning of phase2')
MergeURtoULandUBtoDF = load_cachetable('MergeURtoULandUBtoDF')
if not MergeURtoULandUBtoDF:
MergeURtoULandUBtoDF = [[0] * 336 for i in xrange(336)] # new short[336][336]
MergeURtoULandUBtoDF = [[0] * 336 for i in range(336)] # new short[336][336]
# for i, j <336 the six edges UR,UF,UL,UB,DR,DF are not in the
# UD-slice and the index is <20160
for uRtoUL in xrange(336):
for uBtoDF in xrange(336):
for uRtoUL in range(336):
for uBtoDF in range(336):
MergeURtoULandUBtoDF[uRtoUL][uBtoDF] = getURtoDF(uRtoUL, uBtoDF)
dump_cachetable(MergeURtoULandUBtoDF, 'MergeURtoULandUBtoDF')

Expand All @@ -268,18 +277,18 @@ def move(self, m):
log.info('Preparing pruning table for the permutation of the corners and the UD-slice edges in phase2.')
Slice_URFtoDLF_Parity_Prun = load_cachetable('Slice_URFtoDLF_Parity_Prun')
if not Slice_URFtoDLF_Parity_Prun:
Slice_URFtoDLF_Parity_Prun = [-1] * (N_SLICE2 * N_URFtoDLF * N_PARITY / 2) # new byte[N_SLICE2 * N_URFtoDLF * N_PARITY / 2]
Slice_URFtoDLF_Parity_Prun = [-1] * (N_SLICE2 * N_URFtoDLF * N_PARITY // 2) # new byte[N_SLICE2 * N_URFtoDLF * N_PARITY / 2]
# Slice_URFtoDLF_Parity_Prun = [-1] * (N_SLICE2 * N_URFtoDLF * N_PARITY)
depth = 0
setPruning(Slice_URFtoDLF_Parity_Prun, 0, 0)
done = 1
while (done != N_SLICE2 * N_URFtoDLF * N_PARITY):
for i in xrange(N_SLICE2 * N_URFtoDLF * N_PARITY):
for i in range(N_SLICE2 * N_URFtoDLF * N_PARITY):
parity = i % 2
URFtoDLF = (i / 2) / N_SLICE2
_slice = (i / 2) % N_SLICE2
URFtoDLF = (i // 2) // N_SLICE2
_slice = (i // 2) % N_SLICE2
if getPruning(Slice_URFtoDLF_Parity_Prun, i) == depth:
for j in xrange(18):
for j in range(18):
if j in (3, 5, 6, 8, 12, 14, 15, 17):
continue
else:
Expand All @@ -303,18 +312,18 @@ def move(self, m):
log.info('Preparing pruning table for the permutation of the edges in phase2.')
Slice_URtoDF_Parity_Prun = load_cachetable('Slice_URtoDF_Parity_Prun')
if not Slice_URtoDF_Parity_Prun:
Slice_URtoDF_Parity_Prun = [-1] * (N_SLICE2 * N_URtoDF * N_PARITY / 2) # new byte[N_SLICE2 * N_URtoDF * N_PARITY / 2]
Slice_URtoDF_Parity_Prun = [-1] * (N_SLICE2 * N_URtoDF * N_PARITY // 2) # new byte[N_SLICE2 * N_URtoDF * N_PARITY / 2]
# Slice_URtoDF_Parity_Prun = [-1] * (N_SLICE2 * N_URtoDF * N_PARITY) # new byte[N_SLICE2 * N_URtoDF * N_PARITY / 2]
depth = 0
setPruning(Slice_URtoDF_Parity_Prun, 0, 0)
done = 1
while (done != N_SLICE2 * N_URtoDF * N_PARITY):
for i in xrange(N_SLICE2 * N_URtoDF * N_PARITY):
for i in range(N_SLICE2 * N_URtoDF * N_PARITY):
parity = i % 2
URtoDF = (i / 2) / N_SLICE2
_slice = (i / 2) % N_SLICE2
URtoDF = (i // 2) // N_SLICE2
_slice = (i // 2) % N_SLICE2
if (getPruning(Slice_URtoDF_Parity_Prun, i) == depth):
for j in xrange(18):
for j in range(18):
if j in (3, 5, 6, 8, 12, 14, 15, 17):
continue
else:
Expand All @@ -337,18 +346,18 @@ def move(self, m):
log.info('Pruning table for the twist of the corners and the position (not permutation) of the UD-slice edges in phase1')
Slice_Twist_Prun = load_cachetable('Slice_Twist_Prun')
if not Slice_Twist_Prun:
Slice_Twist_Prun = [-1] * (N_SLICE1 * N_TWIST / 2 + 1) # new byte[N_SLICE1 * N_TWIST / 2 + 1]
Slice_Twist_Prun = [-1] * (N_SLICE1 * N_TWIST // 2 + 1) # new byte[N_SLICE1 * N_TWIST / 2 + 1]
# Slice_Twist_Prun = [-1] * (N_SLICE1 * N_TWIST + 1) # new byte[N_SLICE1 * N_TWIST / 2 + 1]
depth = 0
setPruning(Slice_Twist_Prun, 0, 0)
done = 1
while (done != N_SLICE1 * N_TWIST):
for i in xrange(N_SLICE1 * N_TWIST):
twist = i / N_SLICE1
for i in range(N_SLICE1 * N_TWIST):
twist = i // N_SLICE1
_slice = i % N_SLICE1
if (getPruning(Slice_Twist_Prun, i) == depth):
for j in xrange(18):
newSlice = FRtoBR_Move[_slice * 24][j] / 24
for j in range(18):
newSlice = FRtoBR_Move[_slice * 24][j] // 24
newTwist = twistMove[twist][j]
if (getPruning(Slice_Twist_Prun, N_SLICE1 * newTwist + newSlice) == 0x0f):
setPruning(Slice_Twist_Prun, N_SLICE1 * newTwist + newSlice, (depth + 1) & 0xff)
Expand All @@ -363,18 +372,18 @@ def move(self, m):
log.info('Pruning table for the flip of the edges and the position (not permutation) of the UD-slice edges in phase1')
Slice_Flip_Prun = load_cachetable('Slice_Flip_Prun')
if not Slice_Flip_Prun:
Slice_Flip_Prun = [-1] * (N_SLICE1 * N_FLIP / 2) # new byte[N_SLICE1 * N_FLIP / 2]
Slice_Flip_Prun = [-1] * (N_SLICE1 * N_FLIP // 2) # new byte[N_SLICE1 * N_FLIP / 2]
# Slice_Flip_Prun = [-1] * (N_SLICE1 * N_FLIP) # new byte[N_SLICE1 * N_FLIP / 2]
depth = 0
setPruning(Slice_Flip_Prun, 0, 0)
done = 1
while (done != N_SLICE1 * N_FLIP):
for i in xrange(N_SLICE1 * N_FLIP):
flip = i / N_SLICE1
for i in range(N_SLICE1 * N_FLIP):
flip = i // N_SLICE1
_slice = i % N_SLICE1
if (getPruning(Slice_Flip_Prun, i) == depth):
for j in xrange(18):
newSlice = FRtoBR_Move[_slice * 24][j] / 24
for j in range(18):
newSlice = FRtoBR_Move[_slice * 24][j] // 24
newFlip = flipMove[flip][j]
if (getPruning(Slice_Flip_Prun, N_SLICE1 * newFlip + newSlice) == 0x0f):
setPruning(Slice_Flip_Prun, N_SLICE1 * newFlip + newSlice, (depth + 1) & 0xff)
Expand Down
Loading

0 comments on commit 993c616

Please sign in to comment.