/usr/lib/pybik/pybiklib/cubestate.py is in pybik 3.0-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | #-*- coding:utf-8 -*-
# Copyright © 2009-2015, 2017 B. Clausius <barcc@gmx.de>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import random
from copy import deepcopy
from .debug import DEBUG_MSG, DEBUG_RAND, DEBUG_ROTATE
from .moves import MoveQueuePacked, MoveData
# This class is currently used to store the initial state of the cube
# and to provide the cube to plugins.
class CubeState:
rand = random.Random()
if DEBUG_RAND:
print('rand.seed(123)')
rand.seed(123)
def __init__(self, model):
self.model = model
self._blocksn = [] # list of (block-position, rotation-symbol)
self._blocksr = [] # list of (block-index, rotation-symbol)
# if self._blocksn[idx] == (pos, rot)
# then self._blocksr[pos] == (idx, rot)
def copy(self):
other = CubeState(self.model)
other._blocksn = deepcopy(self._blocksn)
other._blocksr = deepcopy(self._blocksr)
return other
@property
def rotations(self):
return [(idx, self.model.rotation_indices[rot], rot)
for pos, (idx, rot) in enumerate(self._blocksr)]
def set_solved(self):
# '' symbolic rotation for identity
self._blocksn = [(i, '') for i in range(self.model.cell_count)]
self._blocksr = [(i, '') for i in range(self.model.cell_count)]
def is_solved(self):
'''Check whether the cube is solved.
Only test the colors of the visible faces.
Cubes with rotated center faces are treated as solved.
'''
face_colors = {}
for blockpos, cells_visible_faces in enumerate(self.model.cells_visible_faces):
for face in cells_visible_faces:
color = self.get_colorsymbol(blockpos, face)
try:
color_ref = face_colors[face]
except KeyError:
face_colors[face] = color
else:
if color != color_ref:
return False
return True
def is_solved_strict(self):
'''Check whether the cube is solved.
Test the rotation and position of the blocks.
Cubes with rotated center faces are treated as not solved.
'''
#FIXME: currently unused function, should be used for faces with images
allrot = self._blocksn[0][1]
for index, (pos, rot) in enumerate(self._blocksn):
if rot != allrot:
return False # Cubie rotated
block = self.model.rotated_position[rot][index]
if pos != block:
return False # Cubie at wrong place
return True
def is_identity(self):
for index, (pos, rot) in enumerate(self._blocksn):
if index != pos or rot != '':
return False
return True
def identify_rotation_blocks(self, maxis, mslice):
if mslice == -1:
for i, blockn in enumerate(self._blocksn):
yield i, blockn
else:
for i, blockn in enumerate(self._blocksn):
bslice = self.model.cell_indices[blockn[0]][maxis]
if bslice == mslice:
yield i, blockn
def _rotate_slice(self, axis, slice_, dir_):
if DEBUG_ROTATE:
print('rotate axis={} slice={} dir={!s:5}\n blocks:'.format(axis, slice_, dir_), end=' ')
for idx, (pos, rot) in self.identify_rotation_blocks(axis, slice_):
if DEBUG_ROTATE:
print('{}:{}{}'.format(idx, pos, rot), end='')
pos, rot = self.model.rotate_symbolic(axis, dir_, pos, rot)
if DEBUG_ROTATE:
print('-{}{}'.format(pos, rot), end=' ')
self._blocksn[idx] = pos, rot
self._blocksr[pos] = idx, rot
if DEBUG_ROTATE:
print()
def get_colorsymbol(self, blockpos, facesym):
rot = self._blocksr[blockpos][1]
return self.model.face_symbolic_to_face_color(facesym, rot)
def get_colornum(self, blockpos, facesym):
colorsym = self.get_colorsymbol(blockpos, facesym)
return self.model.faces.index(colorsym)
def format_block(self):
# every block is stored as pos-sym, where sym is a symbolic rotation
blocks = ['{}-{}'.format(pos, sym) for pos, sym in self._blocksn]
return 'idx-rot: ' + ' '.join(blocks)
__str__ = format_block
def parse_block(self, blocks):
if blocks == 'solved':
return self.set_solved()
bformat, blocks = blocks.split(':', 1)
if bformat != 'idx-rot':
raise ValueError('unknown block format:', bformat)
blocks = blocks.strip().split(' ')
if len(blocks) != self.model.cell_count:
raise ValueError('wrong block count: %s, expected: %s' % (len(blocks), self.model.cell_count))
blocksn = []
for block in blocks:
# every block is stored as idx-rot, where idx: index to blocklist, rot: symbolic rotation
block = block.strip().split('-', 1)
index, rot = block
index = int(index)
rot = self.model.norm_symbol(rot)
blocksn.append((index, rot))
# test whether block indices is a permutation,
# in fact thats not enough, e.g. swap a corner cubie with an edge,
# also cubie rotation may be invalid, it can be possible that a
# label is rotated inside the cube.
for i1, i2 in enumerate(sorted(i for i, r in blocksn)):
if i1 != i2:
raise ValueError('block list is not a permutation')
self._blocksn = blocksn
self._blocksr = [None] * len(blocksn)
for i, (pos, rot) in enumerate(blocksn):
self._blocksr[pos] = (i, rot)
def random(self, count=-1):
sizes = self.model.sizes
all_moves = [(a,s,d) for a,_sz in enumerate(sizes) for s in range(_sz) for d in (False,True)]
randrange = self.rand.randrange
if count < 0:
count = 10 * len(all_moves) + randrange(len(all_moves))
if count:
# The solved cube should be randomly rotated (lp: 1411999)
for sym in self.rand.choice(self.model.rotation_symbols):
self.rotate_slice(self.model.sym_to_move(sym))
packed = MoveQueuePacked()
all_moves = [(a,s,d) for a,s,d in all_moves if self.model.is_user_slice(a, s)]
for i in range(count):
candidates = list(range(len(all_moves)))
while candidates:
maxis, mslice, mdir = all_moves[candidates.pop(randrange(len(candidates)))]
packed.push_unpacked(MoveData(maxis, mslice, mdir), self.model)
if packed.length_no_full(self.model) > i:
self._rotate_slice(maxis, mslice, mdir)
break
packed.push_unpacked(MoveData(maxis, mslice, not mdir), self.model)
else:
break
if DEBUG_MSG:
print(count, 'random moves:', packed.format(self.model)[0])
def rotate_slice(self, move_data):
self._rotate_slice(*move_data)
def swap_block(self, blockpos1, maxis, mslice, mdir):
if DEBUG_ROTATE:
print('rotate axis={} slice={} dir={!s:5}\n from:'.format(maxis, mslice, mdir), end=' ')
blockidx1, blockrot1 = self._blocksr[blockpos1]
blockpos2, blockrot1r = self.model.rotate_symbolic(maxis, mdir, blockpos1, blockrot1)
blockidx2, blockrot2 = self._blocksr[blockpos2]
blockpos2r, blockrot2r = self.model.rotate_symbolic(maxis, not mdir, blockpos2, blockrot2)
if DEBUG_ROTATE:
print('{}:{}{}->{}{}\n to: {}:{}{}->{}{}'.format(
blockidx1, blockpos1, blockrot1, blockpos2, blockrot1r,
blockidx2, blockpos2, blockrot2, blockpos2r, blockrot2r))
assert blockpos2r == blockpos1
self._blocksn[blockidx1] = blockpos2, blockrot1r
self._blocksr[blockpos2] = blockidx1, blockrot1r
self._blocksn[blockidx2] = blockpos2r, blockrot2r
self._blocksr[blockpos2r] = blockidx2, blockrot2r
def rotate_block(self, blockpos, rdir):
blockidx, blockrot = self._blocksr[blockpos]
try:
rot = self.model.inplace_rotations[blockpos][-1 if rdir else 0]
except IndexError:
# not every block can be rotated inline: e.g. edges and center faces on the 4×4×4-Cube,
# edges and corners on towers and bricks
#TODO: swap edge at pos n with the one at pos (size-1 - n),
# rotate all center faces on the same ring
return
blockrot2 = self.model.norm_symbol(blockrot + rot)
self._blocksn[blockidx] = blockpos, blockrot2
self._blocksr[blockpos] = blockidx, blockrot2
if DEBUG_ROTATE:
sym1, colorsym1 = self.model.blockpos_to_blocksym(blockpos, blockrot)
sym2, colorsym2 = self.model.blockpos_to_blocksym(blockpos, blockrot2)
print('{}:{}{}->{}{} ({}:{}->{}:{})'.format(blockidx, blockpos, blockrot, blockpos, blockrot2,
sym1, colorsym1, sym2, colorsym2))
self.debug_blocksymbols(allsyms=False)
def debug_blocksymbols(self, allsyms):
for blockpos, blockrot in self._blocksn:
blocksym, colorsym = self.model.blockpos_to_blocksym(blockpos, blockrot)
if allsyms or blocksym != colorsym:
print(' {}:{}'.format(blocksym, colorsym), end='')
print('')
|