예제 #1
0
    def _select_starting_pieces_from_clusters(self):
        """
        From the segment clusters, this function builds the starting pieces for each of the output puzzles.
        """

        piece_to_cluster_map = {}
        cluster_has_seed = {}
        for cluster in self._segment_clusters:
            # Used to determine if each cluster has a starting piece
            cluster_has_seed[MultiPuzzleSolver.create_cluster_key(cluster.id_number)] = False

            # Build a map from piece ID number to cluster
            for piece_id in cluster.get_pieces():
                key = PuzzlePiece.create_key(piece_id)

                # Verify no duplicates
                if config.PERFORM_ASSERT_CHECKS:
                    assert key not in piece_to_cluster_map
                piece_to_cluster_map[key] = cluster.id_number

        # Get the starting piece ordering
        starting_piece_ordering = self._paikin_tal_solver.get_initial_starting_piece_ordering()

        # Find the start pieces.
        self._final_starting_pieces = []
        starting_piece_cnt = 0
        while len(self._final_starting_pieces) != len(self._segment_clusters):

            # Get the cluster number (if any) for the starting piece candidate
            starting_piece_candidate = starting_piece_ordering[starting_piece_cnt]
            piece_key = PuzzlePiece.create_key(starting_piece_candidate)
            try:
                cluster_number = piece_to_cluster_map[piece_key]
                cluster_key = MultiPuzzleSolver.create_cluster_key(cluster_number)

                # If the cluster has no seed, use this seed piece
                if not cluster_has_seed[cluster_key]:
                    self._final_starting_pieces.append(starting_piece_candidate)
                    cluster_has_seed[cluster_key] = True

                    # Log the clustering information
                    piece_initial_puzzle = self._paikin_tal_solver.get_piece_original_puzzle_id(starting_piece_candidate)
                    logging.info("Seed piece for cluster %d is piece id %d from initial puzzle %d" % (cluster_number,
                                                                                                      starting_piece_candidate,
                                                                                                      piece_initial_puzzle))
            except KeyError:
                pass
            starting_piece_cnt += 1

        if MultiPuzzleSolver._ALLOW_POST_SELECT_STARTING_PIECES_PICKLE_EXPORT:
            self._pickle_export_after_select_starting_pieces()
예제 #2
0
    def _get_stitching_pieces(self):
        """
        Iterates through all of the segments found by the initial solver, and builds a list of the piece identification
        numbers for all of the stitching pieces.

        Returns (List[List[StitchingPieceSegment]]): List of stitching pieces for each segment.
        """
        all_stitching_pieces = []
        existing_piece_ids = {}
        for segment_id_numb in xrange(0, len(self._segments)):

            # Verify the identification number matches what is stored in the array
            if config.PERFORM_ASSERT_CHECKS:
                assert segment_id_numb == self._segments[segment_id_numb].id_number

            # Separate the stitching pieces by segments
            all_stitching_pieces.append([])
            segment_stitching_pieces = self._segments[segment_id_numb].select_pieces_for_segment_stitching()
            for segmentation_piece_id in segment_stitching_pieces:
                all_stitching_pieces[segment_id_numb].append(StitchingPieceInfo(segmentation_piece_id, segment_id_numb))

                # Verify no duplicate stitching pieces
                if config.PERFORM_ASSERT_CHECKS:
                    key = PuzzlePiece.create_key(segmentation_piece_id)
                    assert key not in existing_piece_ids
                    existing_piece_ids[key] = segmentation_piece_id

        return all_stitching_pieces
예제 #3
0
    def add_solver_piece(self, solved_piece_id, piece_segment_id):
        """
        After the solver is run using the stitching piece as the seed, pieces from the solved puzzle are added to the
        stitching piece information object.

        Args:
            solved_piece_id (int): Identification number of piece in stitching piece solver

            piece_segment_id (int): Identification number of the segment where the piece with identification
                number of "solved_piece_id" was assigned in initial segmentation.  This value is "None" if the piece
                has no associated segment.
        """
        self._total_numb_solved_pieces += 1

        # Ensure no duplicate pieces.
        if config.PERFORM_ASSERT_CHECKS:
            key = PuzzlePiece.create_key(solved_piece_id)
            assert key not in self._all_solver_pieces
            self._all_solver_pieces[key] = solved_piece_id

        # Handle the case where the piece was not assigned to any segment initially
        if piece_segment_id is None:
            self._solver_piece_without_segment.append(solved_piece_id)
            return

        while len(self._solver_piece_segments) < piece_segment_id + 1:
            self._solver_piece_segments.append([])
        self._solver_piece_segments[piece_segment_id].append(solved_piece_id)
예제 #4
0
    def _select_segment_for_solver(self, selected_segment):
        """
        Segment is selected for use by the solver.

        Args:
            selected_segment (PuzzleSegment): Segment selected to be used by the solver
        """
        initial_segment_id = selected_segment.id_number

        # Add the segment to the list
        selected_segment.update_segment_for_multipuzzle_solver(len(self._segments))
        self._segments.append(selected_segment)

        # Store the segment
        for piece_id in selected_segment.get_piece_ids():
            self._paikin_tal_solver.disallow_piece_placement(piece_id)
            # Store the mapping of piece to segment.
            key = PuzzlePiece.create_key(piece_id)
            self._piece_id_to_segment_map[key] = selected_segment.id_number

        logging.info("Saved segment #%d has %d pieces." % (selected_segment.id_number, selected_segment.numb_pieces))

        # Optionally output the segment image to a file.
        if MultiPuzzleSolver._SAVE_SELECTED_SEGMENTS_TO_AN_IMAGE_FILE:
            zfill_width = 4
            filename_descriptor = "segment_number_" + str(selected_segment.id_number).zfill(zfill_width)
            filename_descriptor += "_puzzle_round_" + str(self._numb_segmentation_rounds).zfill(zfill_width)

            single_puzzle_id = 0
            self._paikin_tal_solver.save_segment_to_image_file(PuzzleSolver.MultiPuzzle, single_puzzle_id,
                                                               initial_segment_id, filename_descriptor,
                                                               self._image_filenames, self._start_timestamp)
예제 #5
0
    def _process_stitching_piece_solver_result(self, segment_numb, stitching_piece_numb):
        """
        After the solver is run for a stitching piece, this function process those results including adding the
        pieces in the solved result to the StitchingPieceInfo object.  This includes tracking the segment to which
        each piece in the solved output belows (if any).

        Args:
            segment_numb (int): Identification number of the segment whose stitching piece is being processed

            stitching_piece_numb (int): Index of the stitching piece in the associated segment list
        """
        solved_puzzle, _ = self._paikin_tal_solver.get_solved_puzzles()

        single_puzzle_id_number = 0
        for piece in solved_puzzle[single_puzzle_id_number]:
            piece_id = piece.id_number
            # Get segment associated with the piece if any
            try:
                piece_segment_numb = self._piece_id_to_segment_map[PuzzlePiece.create_key(piece_id)]
            except KeyError:
                piece_segment_numb = None
            # Add the piece to the stitching piece information
            self._stitching_pieces[segment_numb][stitching_piece_numb].add_solver_piece(piece_id,
                                                                                        piece_segment_numb)

        self._stitching_pieces[segment_numb][stitching_piece_numb].log_piece_to_segment_mapping(len(self._segments))
예제 #6
0
 def test_puzzle_polygon(self):
     # Define the color side pairing for the test
     color_side = [(PuzzlePieceSide.top, PieceDirectAccuracyResult.different_puzzle.value),
                   (PuzzlePieceSide.right, PieceDirectAccuracyResult.correct_placement.value),
                   (PuzzlePieceSide.bottom, PieceDirectAccuracyResult.wrong_location.value),
                   (PuzzlePieceSide.left, PieceDirectAccuracyResult.wrong_rotation.value)]
     # noinspection PyUnusedLocal
     image = PuzzlePiece.create_side_polygon_image(color_side, 25, 25)
예제 #7
0
    def test_calculate_placed_piece_rotation_degree_180(self):

        # Calculate for unrotated pieces placed in complementary locations (TOP)
        placed_rotation = PuzzlePiece._calculate_placed_piece_rotation(PuzzlePieceSide.top, PuzzlePieceSide.top,
                                                                       PuzzlePieceRotation.degree_0)
        assert placed_rotation == PuzzlePieceRotation.degree_180
        placed_rotation = PuzzlePiece._calculate_placed_piece_rotation(PuzzlePieceSide.top, PuzzlePieceSide.left,
                                                                       PuzzlePieceRotation.degree_90)
        assert placed_rotation == PuzzlePieceRotation.degree_180

        # Calculate for unrotated pieces placed in complementary locations (RIGHT)
        placed_rotation = PuzzlePiece._calculate_placed_piece_rotation(PuzzlePieceSide.right, PuzzlePieceSide.right,
                                                                       PuzzlePieceRotation.degree_0)
        assert placed_rotation == PuzzlePieceRotation.degree_180
        placed_rotation = PuzzlePiece._calculate_placed_piece_rotation(PuzzlePieceSide.right, PuzzlePieceSide.top,
                                                                       PuzzlePieceRotation.degree_90)
        assert placed_rotation == PuzzlePieceRotation.degree_180

        # Calculate for unrotated pieces placed in complementary locations (BOTTOM)
        placed_rotation = PuzzlePiece._calculate_placed_piece_rotation(PuzzlePieceSide.bottom, PuzzlePieceSide.bottom,
                                                                       PuzzlePieceRotation.degree_0)
        assert placed_rotation == PuzzlePieceRotation.degree_180

        # Calculate for unrotated pieces placed in complementary locations (LEFT)
        placed_rotation = PuzzlePiece._calculate_placed_piece_rotation(PuzzlePieceSide.left, PuzzlePieceSide.left,
                                                                       PuzzlePieceRotation.degree_0)
        assert placed_rotation == PuzzlePieceRotation.degree_180
        # Calculate for unrotated pieces placed in complementary locations (LEFT)
        placed_rotation = PuzzlePiece._calculate_placed_piece_rotation(PuzzlePieceSide.left, PuzzlePieceSide.top,
                                                                       PuzzlePieceRotation.degree_270)
        assert placed_rotation == PuzzlePieceRotation.degree_180
예제 #8
0
    def test_get_neighbor_locations_and_sides(self):

        # Test with no rotation
        test_loc = (10, 20)
        test_rotation = PuzzlePieceRotation.degree_0
        location_and_sides = PuzzlePiece._get_neighbor_locations_and_sides(test_loc, test_rotation)
        assert location_and_sides[0] == ((test_loc[0] - 1, test_loc[1]), PuzzlePieceSide.top)
        assert location_and_sides[1] == ((test_loc[0], test_loc[1] + 1), PuzzlePieceSide.right)
        assert location_and_sides[2] == ((test_loc[0] + 1, test_loc[1]), PuzzlePieceSide.bottom)
        assert location_and_sides[3] == ((test_loc[0], test_loc[1] - 1), PuzzlePieceSide.left)

        # Test with 90 degrees of rotation
        test_loc = (35, 44)
        test_rotation = PuzzlePieceRotation.degree_90
        location_and_sides = PuzzlePiece._get_neighbor_locations_and_sides(test_loc, test_rotation)
        assert location_and_sides[0] == ((test_loc[0] - 1, test_loc[1]), PuzzlePieceSide.left)
        assert location_and_sides[1] == ((test_loc[0], test_loc[1] + 1), PuzzlePieceSide.top)
        assert location_and_sides[2] == ((test_loc[0] + 1, test_loc[1]), PuzzlePieceSide.right)
        assert location_and_sides[3] == ((test_loc[0], test_loc[1] - 1), PuzzlePieceSide.bottom)

        # Test with 180 degrees of rotation
        test_loc = (66, -15)
        test_rotation = PuzzlePieceRotation.degree_180
        location_and_sides = PuzzlePiece._get_neighbor_locations_and_sides(test_loc, test_rotation)
        assert location_and_sides[0] == ((test_loc[0] - 1, test_loc[1]), PuzzlePieceSide.bottom)
        assert location_and_sides[1] == ((test_loc[0], test_loc[1] + 1), PuzzlePieceSide.left)
        assert location_and_sides[2] == ((test_loc[0] + 1, test_loc[1]), PuzzlePieceSide.top)
        assert location_and_sides[3] == ((test_loc[0], test_loc[1] - 1), PuzzlePieceSide.right)

        # Test with 270 degrees of rotation
        test_loc = (-56, 23)
        test_rotation = PuzzlePieceRotation.degree_270
        location_and_sides = PuzzlePiece._get_neighbor_locations_and_sides(test_loc, test_rotation)
        assert location_and_sides[0] == ((test_loc[0] - 1, test_loc[1]), PuzzlePieceSide.right)
        assert location_and_sides[1] == ((test_loc[0], test_loc[1] + 1), PuzzlePieceSide.bottom)
        assert location_and_sides[2] == ((test_loc[0] + 1, test_loc[1]), PuzzlePieceSide.left)
        assert location_and_sides[3] == ((test_loc[0], test_loc[1] - 1), PuzzlePieceSide.top)
예제 #9
0
    def test__get_neighbor_piece_rotated_side(self):

        # Test calculation of the top side.
        assert PuzzlePiece._get_neighbor_piece_rotated_side((-1, 0), (0, 0)) == PuzzlePieceSide.top
        assert PuzzlePiece._get_neighbor_piece_rotated_side((10, 10), (11, 10)) == PuzzlePieceSide.top

        # Test calculation of the bottom side.
        assert PuzzlePiece._get_neighbor_piece_rotated_side((0, 4), (-1, 4)) == PuzzlePieceSide.bottom
        assert PuzzlePiece._get_neighbor_piece_rotated_side((6, 8), (5, 8)) == PuzzlePieceSide.bottom

        # Test calculation of the left side.
        assert PuzzlePiece._get_neighbor_piece_rotated_side((0, -1), (0, 0)) == PuzzlePieceSide.left
        assert PuzzlePiece._get_neighbor_piece_rotated_side((20, 10), (20, 11)) == PuzzlePieceSide.left

        # Test calculation of the right side.
        assert PuzzlePiece._get_neighbor_piece_rotated_side((0, 0), (0, -1)) == PuzzlePieceSide.right
        assert PuzzlePiece._get_neighbor_piece_rotated_side((13, 6), (13, 5)) == PuzzlePieceSide.right
예제 #10
0
    def test_puzzle_piece_maker(self):
        """
        Puzzle Piece Maker Checker

        Checks that puzzle pieces are made as expected.  It also checks the get puzzle piece row/column values.
        """

        # Build a known test puzzle.
        puzzle = PuzzleTester.build_dummy_puzzle()

        # Get the puzzle pieces
        pieces = puzzle.pieces
        for piece in pieces:
            orig_loc = piece._orig_loc
            upper_left_dim = orig_loc[0] * PuzzleTester.PIECE_WIDTH * PuzzleTester.row_to_row_step_size()
            upper_left_dim += orig_loc[1] * PuzzleTester.piece_to_piece_step_size()

            # Test the Extraction of row pixel values
            for row in range(0, PuzzleTester.PIECE_WIDTH):
                first_dim_val = upper_left_dim + row * PuzzleTester.row_to_row_step_size()

                # Test the extraction of pixel values.
                test_arr = PuzzleTester.build_pixel_list(first_dim_val, True)
                row_val = piece.get_row_pixels(row)
                assert(np.array_equal(row_val, test_arr))  # Verify the two arrays are equal.

                # Test the reversing
                reverse_list = True
                test_arr = PuzzleTester.build_pixel_list(first_dim_val, True, reverse_list)
                row_val = piece.get_row_pixels(row, reverse_list)
                assert(np.array_equal(row_val, test_arr))

            for col in range(0, PuzzleTester.PIECE_WIDTH):
                first_dim_val = upper_left_dim + col * PuzzleTester.NUMB_PIXEL_DIMENSIONS

                # Test the extraction of pixel values.
                is_col = False
                test_arr = PuzzleTester.build_pixel_list(first_dim_val, is_col)
                col_val = piece.get_column_pixels(col)
                assert(np.array_equal(col_val, test_arr))  # Verify the two arrays are equal.

                # Test the reversing
                reverse_list = True
                test_arr = PuzzleTester.build_pixel_list(first_dim_val, is_col, reverse_list)
                col_val = piece.get_column_pixels(col, reverse_list)
                assert(np.array_equal(col_val, test_arr))

        # Calculate the asymmetric distance for two neighboring pieces on ADJACENT SIDES
        asym_dist = PuzzlePiece.calculate_asymmetric_distance(pieces[0], PuzzlePieceSide.right,
                                                              pieces[1], PuzzlePieceSide.left)
        assert(asym_dist == 0)
        asym_dist = PuzzlePiece.calculate_asymmetric_distance(pieces[1], PuzzlePieceSide.left,
                                                              pieces[0], PuzzlePieceSide.right)
        assert(asym_dist == 0)
        # Calculate the asymmetric distance for two neighboring pieces on ADJACENT SIDES
        pieces_per_row = int(math.sqrt(PuzzleTester.NUMB_PUZZLE_PIECES))
        asym_dist = PuzzlePiece.calculate_asymmetric_distance(pieces[0], PuzzlePieceSide.bottom,
                                                              pieces[pieces_per_row], PuzzlePieceSide.top)
        assert(asym_dist == 0)
        asym_dist = PuzzlePiece.calculate_asymmetric_distance(pieces[pieces_per_row], PuzzlePieceSide.top,
                                                              pieces[0], PuzzlePieceSide.bottom)
        assert(asym_dist == 0)

        # Calculate the asymmetric distance for pieces two spaces away
        asym_dist = PuzzlePiece.calculate_asymmetric_distance(pieces[0], PuzzlePieceSide.right,
                                                              pieces[2], PuzzlePieceSide.left)
        expected_dist = PuzzleTester.PIECE_WIDTH * PuzzleTester.NUMB_PIXEL_DIMENSIONS * PuzzleTester.piece_to_piece_step_size()
        assert(asym_dist == expected_dist)
        # Calculate the asymmetric distance for pieces two spaces away
        asym_dist = PuzzlePiece.calculate_asymmetric_distance(pieces[2], PuzzlePieceSide.left,
                                                              pieces[0], PuzzlePieceSide.right)
        expected_dist = PuzzleTester.PIECE_WIDTH * PuzzleTester.NUMB_PIXEL_DIMENSIONS * PuzzleTester.piece_to_piece_step_size()
        assert(asym_dist == expected_dist)

        # Calculate the asymmetric distance for two neighboring pieces on non-adjacent
        asym_dist = PuzzlePiece.calculate_asymmetric_distance(pieces[0], PuzzlePieceSide.top,
                                                              pieces[1], PuzzlePieceSide.top)
        # Distance between first pixel in top row of piece to last pixel of piece j almost like to puzzle pieces
        pixel_to_pixel_dist = -1 * ((2 * PuzzleTester.PIECE_WIDTH - 1) * PuzzleTester.NUMB_PIXEL_DIMENSIONS)
        pixel_to_pixel_dist -= PuzzleTester.row_to_row_step_size()
        # Calculate the expected distance
        expected_dist = 0
        for i in range(0, PuzzleTester.PIECE_WIDTH * PuzzleTester.NUMB_PIXEL_DIMENSIONS):
            expected_dist += abs(pixel_to_pixel_dist)
            if i % PuzzleTester.NUMB_PIXEL_DIMENSIONS == PuzzleTester.NUMB_PIXEL_DIMENSIONS - 1:
                pixel_to_pixel_dist += 2 * PuzzleTester.NUMB_PIXEL_DIMENSIONS
        assert(asym_dist == expected_dist)
예제 #11
0
    def test_determine_unrotated_side(self):

        # Find the unrotated side of a puzzle piece
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_0, PuzzlePieceSide.top) == PuzzlePieceSide.top
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_0, PuzzlePieceSide.right) == PuzzlePieceSide.right
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_0, PuzzlePieceSide.bottom) == PuzzlePieceSide.bottom
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_0, PuzzlePieceSide.left) == PuzzlePieceSide.left

        # Find the puzzle piece side when the piece is rotated 90 degrees
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_90, PuzzlePieceSide.top) == PuzzlePieceSide.left
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_90, PuzzlePieceSide.right) == PuzzlePieceSide.top
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_90, PuzzlePieceSide.bottom) == PuzzlePieceSide.right
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_90, PuzzlePieceSide.left) == PuzzlePieceSide.bottom

        # Find the puzzle piece side when the piece is rotated 180 degrees
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_180, PuzzlePieceSide.top) == PuzzlePieceSide.bottom
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_180, PuzzlePieceSide.right) == PuzzlePieceSide.left
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_180, PuzzlePieceSide.bottom) == PuzzlePieceSide.top
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_180, PuzzlePieceSide.left) == PuzzlePieceSide.right

        # Find the puzzle piece side when the piece is rotated 270 degrees
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_270, PuzzlePieceSide.top) == PuzzlePieceSide.right
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_270, PuzzlePieceSide.right) == PuzzlePieceSide.bottom
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_270, PuzzlePieceSide.bottom) == PuzzlePieceSide.left
        assert PuzzlePiece._determine_unrotated_side(PuzzlePieceRotation.degree_270, PuzzlePieceSide.left) == PuzzlePieceSide.top