예제 #1
0
 def __init__(self):
     """
     Defines a goal state and initializes basic objects (heuristics, statistics).
     """
     # State representation: tile value => (x,y)
     self.goal_state = {
         'tiles': {
             1: (1,1), 2: (1,2), 3: (1,3),
             8: (2,1), 0: (2,2), 4: (2,3),
             7: (3,1), 6: (3,2), 5: (3,3)
         },
         'moved_tile': None,
         'h_value': 0
     }
     # Basic objects
     self.heuristics = PuzzleHeuristics(self.goal_state)
     self.statistics = PuzzleStatistics(self)
예제 #2
0
class PuzzleWorld(object):

    def __init__(self):
        """
        Defines a goal state and initializes basic objects (heuristics, statistics).
        """
        # State representation: tile value => (x,y)
        self.goal_state = {
            'tiles': {
                1: (1,1), 2: (1,2), 3: (1,3),
                8: (2,1), 0: (2,2), 4: (2,3),
                7: (3,1), 6: (3,2), 5: (3,3)
            },
            'moved_tile': None,
            'h_value': 0
        }
        # Basic objects
        self.heuristics = PuzzleHeuristics(self.goal_state)
        self.statistics = PuzzleStatistics(self)

    # PUBLIC main methods
    def solve_given_puzzle(self, start_state, h_type):
        """
        Tries to solve a given puzzle with the selected heuristic.
        Args:
            start_state (Hash)
            h_type (int):
                1 ... MIN number of tiles on wrong positions.
                2 ... MIN sum of manhattan distances from goal posisitions.
        Returns:
            boolean: Whether a solution from the start state has been found or not.
        """
        # Reset solution path (start state -> goal state).
        solution_path = []
        # Add start state to the path.
        solution_path.append(start_state)
        current_state = solution_path[0]
        current_state['h_value'] = self.heuristics.calculate_value(h_type,current_state)
        # Search for a solution.
        while current_state['tiles'] != self.goal_state['tiles']:
            #print "======CUR STATE======"
            #self._show_state(current_state)
            # Find possible moves towards zero tile.
            possible_moves = self._find_possible_moves(current_state)
            # For every move create a new state.
            new_states = self._create_states_from_moves(current_state, possible_moves)
            # Check if the states are valid.
            valid_states = self._get_valid_states(new_states, solution_path)
            if not valid_states:
                print('No solution found in %d moves (using h%d).') % (len(solution_path)-1, h_type)
                return False
            # Calculate heuristics value for every new state.
            for state in valid_states:
                state['h_value'] = self.heuristics.calculate_value(h_type,state)
            # Choose state with the lowest heuristics value.
            best_state = self._get_best_state(valid_states)
            solution_path.append(best_state)
            current_state = best_state
        # Goal state was reached
        print('The goal state was reached in %d moves (using h%d).') % (len(solution_path)-1,h_type)
        #print(solution_path)
        return solution_path

    def solve_random_puzzle(self, h_type):
        """
        Solve a randomly generated puzzle (using given h type).
        """
        result = self.solve_given_puzzle(self.generate_start_state(), h_type)
        return result

    # PUBLIC statistical methods
    def bulk_solve(self, number_of_puzzles, h_type):
        """
        Perform a simulation of solving many puzzles at once (using given h type).
        """
        self.statistics.bulk_solve(number_of_puzzles, h_type)

    def bulk_solve_compare(self, number_of_puzzles):
        """
        Compare both heuristics by performing a simulation.
        """
        self.statistics.solve_and_compare_bulk(number_of_puzzles)

    # PUBLIC working methods
    def generate_start_state(self):
        """
        Generates a random start state.
        """
        state_a = {
            'tiles': {
                5: (1,1), 4: (1,2), 0: (1,3),
                6: (2,1), 1: (2,2), 8: (2,3),
                7: (3,1), 3: (3,2), 2: (3,3)
            },
            'moved_tile': None,
            'h_value': None
        }
        random_tiles = {i: None for i in range(0,9)}
        positions_used = []
        i = 0
        while i < 9:
            r_pos = (random.randint(1,3), random.randint(1,3))
            if r_pos not in positions_used:
                positions_used.append(r_pos)
                random_tiles[i] = r_pos
                i += 1
        r_state = {
            'tiles': random_tiles, 'moved_tile': None, 'h_value': None
        }
        print random_tiles
        return r_state

    # PRIVATE working methods
    def _find_possible_moves(self, current_state):
        """
        Returns tiles with which the zero tile can change its position.
        Args:
            current_state (Hash)
        Returns:
            list
        """
        # Get zero tile coordinates
        tile_zero = current_state['tiles'][0]
        # Count new coordinates for 4 movements.
        x_up = tile_zero[0] - 1
        x_down = tile_zero[0] + 1
        y_left = tile_zero[1] - 1
        y_right = tile_zero[1] + 1
        #print tile_zero, x_down, x_up, y_left, y_right
        # Check coordinates
        movable_tiles= []
        if x_up > 0 and x_up <= 3:
            movable_tiles.append((x_up, tile_zero[1]))
        if x_down > 0 and x_down <= 3:
            movable_tiles.append((x_down, tile_zero[1]))
        if y_left > 0 and y_left <= 3:
            movable_tiles.append((tile_zero[0],y_left))
        if y_right > 0 and y_right <= 3:
            movable_tiles.append((tile_zero[0],y_right))
        # Result
        return movable_tiles

    def _create_states_from_moves(self, current_state, possible_moves):
        """
        Create new states from given moves.
        Args:
            current_state (Hash)
            possible_moves: List of tiles with which the zero tile can change its position.
        Returns:
            list: new states
        """
        new_states = []
        zero_tile = current_state['tiles'][0]
        for moved_tile in possible_moves:
            new_zero = moved_tile
            new_move_tile = zero_tile
            new_state = {}
            new_state['tiles'] = current_state['tiles'].copy()
            new_state['tiles'][0] = new_zero
            new_state['tiles'][self._get_tile_name_by_coordinates(current_state, moved_tile)] = new_move_tile
            new_state['moved_tile'] = moved_tile
            new_states.append(new_state)
        # result
        return new_states

    def _get_valid_states(self, new_states, solution_path):
        """
        Eliminate all already visited states (in solution path).
        """
        valid_states = []
        for new_state in new_states:
            ok = True
            for sel_state in solution_path:
                if sel_state['tiles'] == new_state['tiles']:
                    ok = False
                    break
            if ok:
                valid_states.append(new_state)
        # result
        return valid_states

    def _get_best_state(self, valid_states):
        """
        Select a state with the lowest h value.
        [Not implemented: If there are more states with same value, choose the one which will place a non-zero title on the right place.]
        """
        return min(valid_states, key=lambda x: x['h_value'])

    # HELPER functions
    def _get_tile_name_by_coordinates(self, state, coordinates):
        """
        Given x and y, return tile name from the given state.
        """
        for t_name, xy in state['tiles'].iteritems():
            if xy == coordinates:
                return t_name

    def _show_state(self, state):
        """
        A nice matrix representation of the state could be shown, but it's difficlut :).
        """
        print(state)