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()
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
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)
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)
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))
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)
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
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)
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
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)
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