def from_arguments(args: Namespace, cube: Cube, parser: ArgumentParser) -> Optional["Label"]: if args.label_data is None: return None image, scale = args.label_data texture = Texture(image, GL_RGBA, flip=True, mipmap=True) if args.label_side is not None: side = args.label_side else: side = Side.TOP face = cube.get_side(Orientation.regular(side)) if args.label_position is not None: i, j = args.label_position if i < 0 or i >= face.rows: parser.error(f"invalid label row: it must be between 0 and {face.rows - 1}") return None if j < 0 or j >= face.columns: parser.error(f"invalid label column: it must be between 0 and {face.columns - 1}") return None else: i = face.rows // 2 j = face.columns // 2 return Label(scale, texture, side, i, j)
def test_runtime_globals(): runtime = CubeRuntime(Cube((3, 3, 3)), Orientation(), lambda action: None, lambda: None) existing = set(runtime.functions.global_values.keys()) assert existing.issuperset(set(CubeRuntime.COLOR_NAMES.keys())) assert existing.issuperset(set(CubeRuntime.SIDE_NAMES.keys())) assert "push_orientation" in existing
def _get_colors(self, side: Side, i: int, j: int) -> List[Color]: orientation = Orientation.regular(side) front_side = self.cube.get_side(orientation) colors = [front_side.colors[i, j]] if i == 0 or i == front_side.rows - 1: if i == 0: top_side = self.cube.get_side(orientation.to_top) color = top_side.colors[top_side.rows - 1, j] else: bottom_side = self.cube.get_side(orientation.to_bottom) color = bottom_side.colors[0, j] colors.insert(0 if side in {Side.FRONT, Side.BACK} else 1, color) if j == 0 or j == front_side.columns - 1: if j == 0: left_side = self.cube.get_side(orientation.to_left) color = left_side.colors[i, left_side.columns - 1] else: right_side = self.cube.get_side(orientation.to_right) color = right_side.colors[i, 0] colors.insert(0, color) if len(colors) == 3: return [colors[2], colors[0], colors[1]] return colors
class TestApplySide: orientation = Orientation(Side.RIGHT, Side.BOTTOM) def test_apply_side(self): cube = Cube((2, 2, 2)) colors = [[Color.WHITE, Color.RED], [Color.ORANGE, Color.GREEN]] apply_side(cube, self.orientation, colors) actual_colors = [[ cube.get_side(self.orientation).colors[i, j] for j in [0, 1] ] for i in [0, 1]] assert colors == actual_colors def test_wrong_columns(self): cube = Cube((2, 2, 2)) colors = [[Color.WHITE, Color.RED, Color.BLUE], [Color.ORANGE, Color.GREEN, Color.BLUE]] with raises(argparse.ArgumentTypeError) as e: apply_side(cube, self.orientation, colors) assert str(e.value) == "Incorrect number of columns" def test_wrong_lines(self): cube = Cube((2, 2, 2)) colors = [[Color.WHITE, Color.RED]] with raises(argparse.ArgumentTypeError) as e: apply_side(cube, self.orientation, colors) assert str(e.value) == "Incorrect number of lines"
def test_flip_flops(): code = """ let count: int while top[1, 1] != top[2, 2] or count == 0 do RUR'U' count = count + 1 end out(count) """ out_fn = MagicMock() stack = Stack() finish_function = MagicMock() cube_runtime = CubeRuntime(Cube((3, 3, 3)), Orientation(), lambda action: None, finish_function) cube_runtime.functions.initialize_stack(stack) stdlib.initialize_stack(stack) stack.add_global("out", Function(([Integer], Void))) globals = {"out": out_fn, **stdlib.exec_globals, **cube_runtime.functions.exec_globals} executor = ExecutionContext(globals) executor.compile(parser.parse(code, stack)) executor.execute(MockTracebackWriter()) cube_runtime.finished() out_fn.assert_called_once_with(6) finish_function.assert_called_once()
def test_rotations(around: Side, front: Side, top: Side, twice: bool) -> None: orientation = Orientation(Side.FRONT, Side.TOP) action = Rotate(around, twice) # noinspection PyTypeChecker orientation = action.perform(None, orientation) assert orientation.front == front assert orientation.top == top
def assert_cube(cube: Cube, front: str, right: str, back: str, left: str, top: str, bottom: str) -> None: orientation = Orientation() assert front == side_to_string(cube.get_side(orientation)) assert right == side_to_string(cube.get_side(orientation.to_right)) assert back == side_to_string(cube.get_side(orientation.to_right.to_right)) assert left == side_to_string(cube.get_side(orientation.to_left)) assert top == side_to_string(cube.get_side(orientation.to_top)) assert bottom == side_to_string(cube.get_side(orientation.to_bottom))
def test_get_side(): orientation: Orientation = Orientation(Side.LEFT, Side.TOP) orientation.get_side_rotation = MagicMock() orientation.get_side_rotation.return_value = 0 cube = Cube((3, 3, 3)) side = cube.get_side(orientation) assert isinstance(side, CubeSide) assert side == cube.sides[Side.LEFT]
def test_rotation_2(around: Side): action1 = Rotate(around, False) action2 = Rotate(around.opposite(), False) initial = Orientation(Side.LEFT, Side.BACK) orientation = action1.perform(None, initial) orientation = action2.perform(None, orientation) assert orientation == initial
def test_action_callback(): callback: Callable[[Action], None] = MagicMock() runtime = CubeRuntime(Cube((3, 3, 3)), Orientation(), callback, lambda: None) runtime.perform_turn(Side.LEFT, 2, [1]) callback: MagicMock action = callback.call_args_list[0][0][0] assert isinstance(action, Turn) assert action.type == TurningType.VERTICAL
def _generate(self) -> List[CubePart]: parts = [] for side, i, j in self.cube.iterate_components(): x, y, z = self.cube.get_absolute_coordinates(side, i, j) y = self.cube.shape[2] - 1 - y z = self.cube.shape[1] - 1 - z part = self._create_part(x, y, z, self._get_colors(side, i, j)) parts.append(part) self.cube.set_data(Orientation.regular(side), i, j, part) return parts
def test_data_set(sample_cube: Cube) -> None: orientation = Orientation(Side.RIGHT, Side.BACK) sample_cube.set_data(orientation, 1, 1, "a") sample_cube.set_data(orientation, 0, 0, "b") sample_cube.set_data(orientation, 1, 2, "c") sample_cube.set_data(orientation, 2, 1, "d") def get_side(orient: Orientation) -> str: return data_to_string(sample_cube.get_side(orient)) orientation = Orientation(Side.FRONT, Side.TOP) assert get_side(orientation) == "None None None/None None d/None None None" assert get_side( orientation.to_top) == "None None b/None None None/None None None" assert get_side(orientation.to_right) == "None None b/d a None/None c None" assert get_side(orientation.to_right.to_right ) == "b None None/None None None/None None None" assert get_side( orientation.to_bottom) == "None None None/None None c/None None None"
def assert_solved(cube: Cube): for front in Side: face = cube.get_side(Orientation.regular(front)) color = None for row in range(face.rows): for column in range(face.columns): c = face.colors[row, column] if color is None: color = c else: assert c == color
def test_scramble(self): result = [] actions = [ MockAction(result, string.ascii_uppercase[i]) for i in range(10) ] builder = CubeBuilder((2, 2, 2)) builder.scramble(actions) _, orientation = builder.get() assert orientation == Orientation(Side.LEFT, Side.RIGHT) assert result == list("ABCDEFGHIJ")
class TestBuilder: def test_create(self): builder = CubeBuilder((2, 2, 2)) cube, orientation = builder.get() assert cube.shape == (2, 2, 2) assert orientation.top == Side.TOP assert orientation.front == Side.FRONT @mock.patch("cubelang.cli.cube_builder.apply_side") @pytest.mark.parametrize( "side, exp_orientation", [(Side.FRONT, Orientation(Side.FRONT, Side.TOP)), (Side.LEFT, Orientation(Side.LEFT, Side.TOP)), (Side.RIGHT, Orientation(Side.RIGHT, Side.TOP)), (Side.BACK, Orientation(Side.BACK, Side.TOP)), (Side.TOP, Orientation(Side.TOP, Side.BACK)), (Side.BOTTOM, Orientation(Side.BOTTOM, Side.FRONT))]) def test_side(self, apply_side_fn, side, exp_orientation): builder = CubeBuilder((2, 2, 2)) builder.side(side, []) apply_side_fn.assert_called_once_with(builder.cube, exp_orientation, []) def test_scramble(self): result = [] actions = [ MockAction(result, string.ascii_uppercase[i]) for i in range(10) ] builder = CubeBuilder((2, 2, 2)) builder.scramble(actions) _, orientation = builder.get() assert orientation == Orientation(Side.LEFT, Side.RIGHT) assert result == list("ABCDEFGHIJ")
def test_suspend_rotations(): actions = [] runtime = CubeRuntime(Cube((2, 2, 2)), Orientation(), actions.append, lambda: None) runtime.perform_turn(Side.FRONT, 1, [1]) runtime.perform_rotate(Side.TOP, False) runtime.suspend_rotations() for _ in range(3): runtime.perform_turn(Side.FRONT, 1, [1]) runtime.perform_rotate(Side.TOP, False) runtime.resume_rotations() assert "FYFRBY'" == "".join(map(str, actions))
def sample_cube() -> Cube: cube = Cube((3, 3, 3)) orientation = Orientation() assert orientation.get_side_rotation() == 0 red = cube.get_side(orientation) _set_side_colors(red, [[Color.ORANGE, Color.RED, Color.GREEN], [Color.YELLOW, Color.RED, Color.GREEN], [Color.BLUE, Color.WHITE, Color.GREEN]]) green = cube.get_side(orientation.to_right) _set_side_colors(green, [[Color.RED, Color.ORANGE, Color.ORANGE], [Color.RED, Color.GREEN, Color.WHITE], [Color.RED, Color.GREEN, Color.WHITE]]) orange = cube.get_side(orientation.to_left.to_left) _set_side_colors(orange, [[Color.YELLOW, Color.YELLOW, Color.RED], [Color.ORANGE, Color.ORANGE, Color.WHITE], [Color.ORANGE, Color.ORANGE, Color.GREEN]]) blue = cube.get_side(orientation.to_left) _set_side_colors(blue, [[Color.YELLOW, Color.YELLOW, Color.YELLOW], [Color.BLUE, Color.BLUE, Color.RED], [Color.ORANGE, Color.WHITE, Color.WHITE]]) yellow = cube.get_side(orientation.to_top) _set_side_colors(yellow, [[Color.BLUE, Color.GREEN, Color.GREEN], [Color.BLUE, Color.YELLOW, Color.YELLOW], [Color.BLUE, Color.BLUE, Color.WHITE]]) white = cube.get_side(orientation.to_bottom) _set_side_colors(white, [[Color.RED, Color.GREEN, Color.YELLOW], [Color.RED, Color.WHITE, Color.ORANGE], [Color.WHITE, Color.BLUE, Color.BLUE]]) assert_cube(cube, "ORG/YRG/BWG", "ROO/RGW/RGW", "YYR/OOW/OOG", "YYY/BBR/OWW", "BGG/BYY/BBW", "RGY/RWO/WBB") return cube
def test_state_stack(): actions = [] runtime = CubeRuntime(Cube((2, 2, 2)), Orientation(), actions.append, lambda: None) runtime.perform_turn(Side.FRONT, 1, [1]) runtime.perform_rotate(Side.TOP, False) runtime.push_orientation() for _ in range(3): runtime.perform_turn(Side.FRONT, 1, [1]) runtime.perform_rotate(Side.TOP, False) runtime.pop_orientation() assert "FYFYFYFYY" == "".join(map(str, actions))
def test_get_color(side, orientation): class Colors: def __getitem__(self, item): pass class Side: def __init__(self): self.colors = Colors() with patch.object(Cube, 'get_side', return_value=Side()) as mock_method: runtime = CubeRuntime(Cube((3, 3, 3)), Orientation(), lambda action: None, lambda: None) runtime.get_color(side, 0, 0) mock_method.assert_called_once_with(orientation)
def test_orient_full(): cube = Cube((3, 3, 3)) for action in parse_actions("RUR'U'"): action.perform(cube, Orientation()) match = cube.orient(Orientation(), top=Pattern([[Color.WHITE, None, None], [None, "a", None], ["a", None, None]]), front=Pattern([[None, None, None], [None, None, Color.ORANGE], [None, None, None]]), right=Pattern([[None, None, None], [Color.YELLOW, None, None], ["a", None, None]]), back=Pattern([[None, None, None], [None, None, None], [Color.BLUE, None, None]]), left=Pattern([[None, Color.ORANGE, None], [None, None, None], [None, Color.GREEN, None]]), bottom=Pattern([[Color.BLUE, None, Color.RED], [None, None, None], [None, None, None]])) assert Orientation(Side.RIGHT, Side.BOTTOM) == match
def test_data_rotation(sample_cube: Cube) -> None: orientation = Orientation(Side.FRONT, Side.TOP) sample_cube.set_data(orientation, 0, 2, "b") sample_cube.set_data(orientation, 1, 2, "a") sample_cube.turn_vertical(orientation, 3, 1) def get_side(orient: Orientation) -> str: return data_to_string(sample_cube.get_side(orient)) assert get_side( orientation) == "None None None/None None None/None None None" assert get_side( orientation.to_right) == "None a b/None None None/None None None" assert get_side( orientation.to_top) == "None None b/None None a/None None None"
def test_rotations(current: Tuple[Side, Side], left: Tuple[Side, Side], top: Tuple[Side, Side], right: Tuple[Side, Side], bottom: Tuple[Side, Side]) -> None: current_orientation = Orientation(*current) assert current_orientation.to_top.front == top[0] assert current_orientation.to_top.top == top[1] assert current_orientation.to_right.front == right[0] assert current_orientation.to_right.top == right[1] assert current_orientation.to_bottom.front == bottom[0] assert current_orientation.to_bottom.top == bottom[1] assert current_orientation.to_left.front == left[0] assert current_orientation.to_left.top == left[1]
def test_turning_vertical(side: Side, func: str, out_sides: List[int], out_amount: int) -> None: cube: Cube = CubeMock() mock = MagicMock() setattr(cube, func, mock) orientation = Orientation() action = Turn(side, [1, 2], 1) assert action.perform(cube, orientation) == orientation assert len(mock.call_args_list) == len(out_sides) print(mock.call_args_list[0]) for args, side in zip(mock.call_args_list, out_sides): arg_orientation, arg_index, arg_turn = tuple(args)[0] assert arg_orientation == orientation assert arg_index == side assert arg_turn == out_amount
def _create_turn_animation(self, action: Turn) -> Animation: turns = action.turns if turns == 3: turns = -1 if action.type == TurningType.HORIZONTAL: turns = -turns angle = math.radians(90 * turns) if action.type == TurningType.SLICE: axis = "z" side = Side.FRONT elif action.type == TurningType.VERTICAL: axis = "x" side = Side.LEFT else: axis = "y" side = Side.TOP orientation = Orientation.regular(side) width = self.cube.cube.get_side(orientation).columns components = set() for index in Turn.normalize_indices(action.indices, width): index -= 1 if index == 0: components.update(self._get_parts_front(orientation)) elif index == width - 1: components.update( self._get_parts_front(orientation.to_right.to_right)) components.update( self._get_parts_slice(orientation.to_right, index)) def execution_callback(value: float) -> None: for component in components: component.set_temp_rotation(**{axis: value}) def completion_callback() -> None: for component in components: component.apply_temp_rotation() self._run_animation() self.completed_count += 1 return FloatAnimation(0.0, angle, execution_callback, ease_in_out_quad, completion_callback, fraction_callback=self._fraction_callback)
def run_test(seed, filename, dimension): scramble = subprocess.check_output( ["python", "-m", "cubelang.scrambler", "-d", str(dimension), "-s", str(seed)]) scramble = scramble.decode(stdout.encoding).strip() cube = Cube((dimension, dimension, dimension)) orientation = Orientation() for action in parse_actions(scramble): orientation = action.perform(cube, orientation) arguments = ["python", "-m", "cubelang", "-d", str(dimension), "-s", scramble, str(Path(__file__).parents[1] / "examples" / filename)] solution = subprocess.check_output(arguments).decode("utf-8") for action in parse_actions(solution): orientation = action.perform(cube, orientation) assert_solved(cube)
def test_orient(): code = """ orient top: {G--/---/---}, bottom: {--Y/---/---} then out(red) else-orient top: {-W-/---/---}, right: {---/---/-O-} then out(top[1, 1]) end """ out_fn = MagicMock() stack = Stack() cube_runtime = CubeRuntime(Cube((3, 3, 3)), Orientation(), lambda action: None, lambda: None) cube_runtime.functions.initialize_stack(stack) stdlib.initialize_stack(stack) stack.add_global("out", Function(([Color], Void))) globals = {"out": out_fn, **stdlib.exec_globals, **cube_runtime.functions.exec_globals} executor = ExecutionContext(globals) executor.compile(parser.parse(code, stack)) executor.execute(MockTracebackWriter()) cube_runtime.finished() out_fn.assert_called_once_with(orientation.Color.WHITE)
def __init__(self, cube: CubeModel[CubePart], label: Optional[Label], color_theme: Dict[Color, Tuple[float, float, float]]): self.cube: CubeModel[CubePart] = cube self.label = label self.color_theme = color_theme self.shader = Program("object") self.shader.use() DirectionalLight.push_uniform_array(self.shader, "lights", CubePart.LIGHTS) models_path = Path(__file__).parents[1] / "models" self.vao_corner = load_obj(models_path / "corner.obj") self.vao_edge = load_obj(models_path / "edge.obj") self.vao_flat = load_obj(models_path / "flat.obj") self.stickers_texture = Texture.load("stickers", flip=True, mipmap=True) self.labels_texture = Texture.load("labels", flip=True, mipmap=True) self.parts: List[CubePart] = self._generate() if self.label is not None: orientation = Orientation.regular(self.label.side) side = self.cube.get_side(orientation) row, column = self.label.row, self.label.column rot_i = 0 if row == 0 else 2 if row == side.rows - 1 else 1 rot_j = 0 if column == 0 else 2 if column == side.columns - 1 else 1 part = side[row, column].data rotation, color = CubePart.LABEL_ROTATIONS[ self.label.side][rot_i][rot_j] part.label_rotation = rotation part.label_visible = color self.rotation = IDENTITY self.temp_rotation = [0, 0, 0]
def main(): arg_parser = ArgumentParser() arg_parser.add_argument("-d", dest="dimension", help="dimensions of a cube", default=3, metavar="N", type=integer_type(2)) arg_parser.add_argument("-n", dest="turns_num", help="number of turns", type=integer_type(1), default=20) arg_parser.add_argument( "-a", dest="output_args", action="store_true", help= "display the state of the cube after the turns instead of the formula") arg_parser.add_argument( "-s", dest="seed", help="the seed for the pseudorandom number generator") args = arg_parser.parse_args() dim = args.dimension if args.seed is not None: random.seed(args.seed) actions: List[Turn] = [] prev_side = None for i in range(args.turns_num): if prev_side is None: sides = SIDES else: sides = [x for x in SIDES if x != prev_side] prev_side = random.choice(sides) first_index = random.randint(1, dim // 2) last_index = random.randint(1, first_index) if first_index == last_index: indices = [first_index] else: indices = [last_index, ..., first_index] turn = Turn(prev_side, indices, random.randint(1, 3)) actions.append(turn) if not args.output_args: for action in actions: print(str(action), end="") print() else: cube = Cube((dim, ) * 3) orientation = Orientation() for action in actions: action.perform(cube, orientation) print("--front", repr(cube.get_side(orientation).colors)) print("--right", repr(cube.get_side(orientation.to_right).colors)) print("--left", repr(cube.get_side(orientation.to_left).colors)) print("--back", repr(cube.get_side(orientation.to_right.to_right).colors)) print("--top", repr(cube.get_side(orientation.to_top).colors)) print("--bottom", repr(cube.get_side(orientation.to_bottom).colors))
(Side.RIGHT, Side.TOP, TurningType.SLICE, 1, 1), (Side.FRONT, Side.TOP, TurningType.VERTICAL, 1, 3), (Side.FRONT, Side.RIGHT, TurningType.HORIZONTAL, 1, 3), (Side.TOP, Side.RIGHT, TurningType.SLICE, -1, 3), ]) def test_turning_transform(side: Side, turn: Side, res_side: TurningType, res_sides: int, res_turns: int): action = Turn(side, [1], 1) transformed = action._transform(turn) assert transformed.type == res_side assert transformed.turns == res_turns assert transformed.indices[0] == res_sides @pytest.mark.parametrize("orientation, action, type, indices", [(Orientation(Side.BACK, Side.LEFT), Turn(Side.RIGHT, 1, 3), TurningType.HORIZONTAL, -1), (Orientation(Side.TOP, Side.BACK), Turn(Side.TOP, 1, 1), TurningType.SLICE, -1), (Orientation(Side.RIGHT, Side.BOTTOM), Turn(Side.FRONT, 1, 1), TurningType.VERTICAL, -1)]) def test_orientation_changes(orientation: Side, action: Turn, type: TurningType, indices: int): new_action = action.from_orientation(orientation) assert new_action.type == type assert new_action.indices[0] == indices @pytest.mark.parametrize("indices, expected", [([2], {2}), ([2, 4], {2, 4}), ([2, ..., 4], {2, 3, 4}),
def _iterate_orientations(): for side in Side: orientation = Orientation.regular(side) for i in range(4): yield orientation orientation = orientation.rotate_clockwise()