def move_ghost(self, direction, ghost_index): """Moves the given ghost in direction, where direction is leads the ghost to a valid location. """ if ghost_index >= len(self.ghost_coords): # This ghost does not exist return new_coord = coord_class.Coordinate(self.ghost_coords[ghost_index].x, self.ghost_coords[ghost_index].y) # Adjust new_coord depending on pacman's desired direction if direction == d.Direction.UP: new_coord.y += 1 elif direction == d.Direction.DOWN: new_coord.y -= 1 elif direction == d.Direction.LEFT: new_coord.x -= 1 elif direction == d.Direction.RIGHT: new_coord.x += 1 self.prev_ghost_coords[ghost_index] = coord_class.Coordinate( self.ghost_coords[ghost_index].x, self.ghost_coords[ghost_index].y) self.ghost_coords[ghost_index] = coord_class.Coordinate( new_coord.x, new_coord.y)
def move_pacman(self, directions): """Moves all pacman in self.pacman_coords in directions[i] (indexed the same as self.pacman_coords), where each direction is leads pacman to a valid location. """ for index, direction in enumerate(directions): if direction == d.Direction.NONE: # No action needed return new_coord = coord_class.Coordinate(self.pacman_coords[index].x, self.pacman_coords[index].y) # Adjust new_coord depending on pacman's desired direction if direction == d.Direction.UP: new_coord.y += 1 elif direction == d.Direction.DOWN: new_coord.y -= 1 elif direction == d.Direction.LEFT: new_coord.x -= 1 elif direction == d.Direction.RIGHT: new_coord.x += 1 self.prev_pacman_coords[index] = coord_class.Coordinate(self.pacman_coords[index].x, self.pacman_coords[index].y) self.pacman_coords[index] = coord_class.Coordinate(new_coord.x, new_coord.y)
def move_pacman(self, direction, pacman_index): """Moves the pacman in self.pacman_coords at pacman_index in direction, where the direction leads pacman to a valid location. If a pacman is marked as dead, it will not be moved. """ if self.pacman_coords[pacman_index] in self.dead_pacmen: # This pacman is dead, don't move it return if direction == d.Direction.NONE: # No action needed return new_coord = coord_class.Coordinate(self.pacman_coords[pacman_index].x, self.pacman_coords[pacman_index].y) # Adjust new_coord depending on pacman's desired direction if direction == d.Direction.UP: new_coord.y += 1 elif direction == d.Direction.DOWN: new_coord.y -= 1 elif direction == d.Direction.LEFT: new_coord.x -= 1 elif direction == d.Direction.RIGHT: new_coord.x += 1 self.prev_pacman_coords[pacman_index] = coord_class.Coordinate( self.pacman_coords[pacman_index].x, self.pacman_coords[pacman_index].y) self.pacman_coords[pacman_index] = coord_class.Coordinate( new_coord.x, new_coord.y)
def move(self): """Moves the WallCarver, generating new travel parameters if the travel distance is reached. """ if self.travel_distance <= 0: self.get_travel_distance() self.get_direction() new_coord = coord_class.Coordinate(self.coord.x, self.coord.y) if self.direction == d.Direction.UP: new_coord.y += 1 elif self.direction == d.Direction.DOWN: new_coord.y -= 1 elif self.direction == d.Direction.LEFT: new_coord.x -= 1 elif self.direction == d.Direction.RIGHT: new_coord.x += 1 if new_coord.x >= 0 and new_coord.y >= 0 and new_coord.x < self.max_x and new_coord.y < self.max_y: self.coord = coord_class.Coordinate(new_coord.x, new_coord.y) self.travel_distance -= 1
def __init__(self, config, initial_instance=False): """Initializes the GPacWorld class. Where config is a Config object for the GPac problem and initial_instance determines if a random world should be generated yet. """ self.config = config # Load configuration file settings self.width = int(self.config.settings['width']) self.height = int(self.config.settings['height']) self.pill_density = float(self.config.settings['pill density']) / 100 self.wall_density = float(self.config.settings['wall density']) / 100 self.num_pacmen = int(self.config.settings['num pacmen']) self.num_ghosts = int(self.config.settings['num ghosts']) self.fruit_spawn_prob = float(self.config.settings['fruit spawn probability']) self.fruit_score = int(self.config.settings['fruit score']) self.time_multiplier = int(self.config.settings['time multiplier']) # Create initial world attributes self.pacman_coords= [coord_class.Coordinate(0, self.height - 1) for _ in range(self.num_pacmen)] self.ghost_coords = [coord_class.Coordinate(self.width - 1, 0) for _ in range(self.num_ghosts)] self.wall_coords = set([]) self.pill_coords = set([]) self.fruit_coord = set([]) self.time_remaining = self.time_multiplier * self.width * self.height self.total_time = self.time_remaining self.num_pills_consumed = 0 self.num_fruit_consumed = 0 self.score = 0 self.prev_pacman_coords = [] for pacman_coord in self.pacman_coords: self.prev_pacman_coords.append(coord_class.Coordinate(pacman_coord.x, pacman_coord.y)) self.prev_ghost_coords = [] for ghost_coord in self.ghost_coords: self.prev_ghost_coords.append(coord_class.Coordinate(ghost_coord.x, ghost_coord.y)) # Create helper set of all coordinates self.all_coords = set([]) for x in range(self.width): for y in range(self.height): self.all_coords.add(coord_class.Coordinate(x, y)) # Place walls and pills in the world # Only do this if the world is not being created for the first time # Subsequent runs of the GP will ensure random board generation if not initial_instance: self.generate_world() # Create & write to world file self.world_file = world_file_class.WorldFile(self.config) self.world_file.save_first_snapshot(self.width, self.height, self.pacman_coords, self.wall_coords, self.ghost_coords, self.pill_coords, self.time_remaining)
def get_move(self, ghost_id, game_state): """Produces a move based on game_state. Note: for assignment 2b, the move is randomized. """ while True: # Choose a random direction direction_to_try = random.choice(self.POSSIBLE_MOVES) # Determine if this direction is valid new_coord = coord_class.Coordinate(game_state.ghost_coords[ghost_id].x, game_state.ghost_coords[ghost_id].y) if direction_to_try == d.Direction.UP: new_coord.y += 1 elif direction_to_try == d.Direction.DOWN: new_coord.y -= 1 elif direction_to_try == d.Direction.LEFT: new_coord.x -= 1 elif direction_to_try == d.Direction.RIGHT: new_coord.x += 1 if self.check_valid_location(new_coord, game_state): break # We have found a valid direction return direction_to_try
def randomly_spawn_fruit(self): """Probabilistically spawns a fruit in the world. Note that if a fruit already exists, another cannot be spawned. A fruit can only spawn in cell that is not occupied by pacman, not occupied by a wall, and not occupied by a pill. """ if len(self.fruit_coord): # A fruit already exists return if random.random() <= self.fruit_spawn_prob: possible_coords = [] for coord in list( self.all_coords.difference(set( self.pacman_coords)).difference( self.wall_coords).difference(self.pill_coords)): possible_coords.append(coord_class.Coordinate( coord.x, coord.y)) random.shuffle(possible_coords) for possible_fruit_coord in possible_coords: self.fruit_coord.add(possible_fruit_coord) break
def get_nearest_distance(ghost_coord, object): """Returns the distance between the given ghost coordinate and the nearest instance of type object. """ def get_distance(coord1, coord2): """Returns the Manhattan distance between the given coordinates.""" if not coord1 or not coord2: return -1 return abs(coord1.x - coord2.x) + abs(coord1.y - coord2.y) if object == 'ghost': coords_to_search = game_state.ghost_coords coords_to_search = [ coord_class.Coordinate(c.x, c.y) for c in game_state.ghost_coords if not c.x == ghost_coord.x and not c.y == ghost_coord.y ] elif object == 'pacman': coords_to_search = game_state.pacman_coords else: coords_to_search = [] min_distance = ARBITRARY_LARGE_NUMBER for coord in coords_to_search: min_distance = min(min_distance, get_distance(ghost_coord, coord)) return min_distance
def __init__(self, config): """Initializes the WorldFile class. Where config is a Config object. """ self.config = config self.file_str = '' self.prev_seen_fruit = coord_class.Coordinate(-1, -1)
def get_adj_coords(self, coord): """Returns a list of coordinates adjacent to coord. Where the returned coordinate list includes only valid coordinates. """ adj_coords = [] if not coord.x == 0: adj_coords.append(coord_class.Coordinate(coord.x - 1, coord.y)) if not coord.x == self.width - 1: adj_coords.append(coord_class.Coordinate(coord.x + 1, coord.y)) if not coord.y == 0: adj_coords.append(coord_class.Coordinate(coord.x, coord.y - 1)) if not coord.y == self.height - 1: adj_coords.append(coord_class.Coordinate(coord.x, coord.y + 1)) return adj_coords
def get_move(self, game_state): """Checks all possible moves against the state evaluator and determines which move is optimal, returning that move. This is performed for each pacman presented as part of the game state. """ def move_pacman(pacman_coord, direction): """Alters pacman's new coordinate to indicate movement in direction. Returns True if the new position is valid, False otherwise. """ if direction == d.Direction.NONE: # No action needed return True # Adjust new_coord depending on pacman's desired direction if direction == d.Direction.UP: pacman_coord.y += 1 elif direction == d.Direction.DOWN: pacman_coord.y -= 1 elif direction == d.Direction.LEFT: pacman_coord.x -= 1 elif direction == d.Direction.RIGHT: pacman_coord.x += 1 if self.check_valid_location(pacman_coord, game_state): return True return False best_eval_directions = [] for pacman_coord in game_state.pacman_coords: best_eval_result = -1 * ARBITRARY_LARGE_NUMBER best_eval_direction = d.Direction.NONE for direction in POSSIBLE_MOVES: tmp_pacman_coord = coord_class.Coordinate(pacman_coord.x, pacman_coord.y) if move_pacman(tmp_pacman_coord, direction): eval_result = self.evaluate_state(game_state, tmp_pacman_coord)[0] if eval_result > best_eval_result: best_eval_result = eval_result best_eval_direction = direction best_eval_directions.append(best_eval_direction) return best_eval_directions
def reset(self): """Resets all world elements except for wall placements.""" # Regenerate unit coordinates self.pacman_coords = [ coord_class.Coordinate(0, self.height - 1) for _ in range(self.num_pacmen) ] self.ghost_coords = [ coord_class.Coordinate(self.width - 1, 0) for _ in range(self.num_ghosts) ] self.prev_pacman_coords = [] for pacman_coord in self.pacman_coords: self.prev_pacman_coords.append( coord_class.Coordinate(pacman_coord.x, pacman_coord.y)) self.dead_pacmen = set([]) self.prev_ghost_coords = [] for ghost_coord in self.ghost_coords: self.prev_ghost_coords.append( coord_class.Coordinate(ghost_coord.x, ghost_coord.y)) self.fruit_coord = set([]) self.pill_coords = set([]) # Add pills to the world for c in self.orig_pill_coords: self.pill_coords.add(c) self.time_remaining = self.time_multiplier * self.width * self.height self.world_file.flush() self.num_pills_consumed = 0 self.num_fruit_consumed = 0 self.score = 0
def get_move(self, game_state, ghost_index): """Checks all possible moves against the state evaluator and determines which move is optimal, returning that move for the ghost at ghost_index in game_state. """ def move_ghost(ghost_coord, direction): """Alters ghost's new coordinate to indicate movement in direction. Returns True if the new position is valid, False otherwise. """ if direction == d.Direction.NONE: # No action needed return True # Adjust new_coord depending on ghost's desired direction if direction == d.Direction.UP: ghost_coord.y += 1 elif direction == d.Direction.DOWN: ghost_coord.y -= 1 elif direction == d.Direction.LEFT: ghost_coord.x -= 1 elif direction == d.Direction.RIGHT: ghost_coord.x += 1 if self.check_valid_location(ghost_coord, game_state): return True return False best_eval_result = -1 * ARBITRARY_LARGE_NUMBER best_eval_direction = d.Direction.NONE for direction in POSSIBLE_MOVES: tmp_ghost_coord = coord_class.Coordinate( game_state.ghost_coords[ghost_index].x, game_state.ghost_coords[ghost_index].y) if move_ghost(tmp_ghost_coord, direction): eval_result = self.evaluate_state(game_state, tmp_ghost_coord) if eval_result > best_eval_result: best_eval_result = eval_result best_eval_direction = direction return best_eval_direction
def __init__(self, x, y, config): """Initializes the WallCarver class.""" self.config = config self.max_travel_dist = int(self.config.settings['max wall carver travel distance']) self.min_travel_dist = int(self.config.settings['min wall carver travel distance']) self.max_x = int(self.config.settings['width']) self.max_y = int(self.config.settings['height']) self.POSSIBLE_MOVES = [d.Direction.UP, d.Direction.DOWN, d.Direction.LEFT, d.Direction.RIGHT] self.coord = coord_class.Coordinate(x, y) self.get_travel_distance() self.get_direction() self.seen_coords = set([]) self.marked_for_death = False
def add_walls(): """Places (carves out) walls in the world such that every non-wall cell is accessible from every other non-wall cell. """ def assign_unit_starting_coords(wall_carver_list): """Assigns the first two WallCarvers in wall_carver_list to have the unit (pacman & ghosts) starting coords, ensuring these coords are carved out. """ wall_carver_list[0].coord = self.pacman_coords[0] wall_carver_list[1].coord = self.ghost_coords[0] # Create WallCarver population # Note: num wall carvers should be greater than or equal to two wall_carvers = [ wall_carver_class.WallCarver(random.randint(0, self.width), random.randint(0, self.height), self.config) for _ in range(int(self.config.settings['num wall carvers'])) ] assign_unit_starting_coords(wall_carvers) # Get walls to carve walls_to_carve = [] for coord in self.all_coords: walls_to_carve.append(coord_class.Coordinate(coord.x, coord.y)) past_seen_carved_coords = set([]) # Calculate wall density num_coords = len(walls_to_carve) num_walls = num_coords wall_density = num_walls / num_coords completed_circuit = False while wall_density > self.wall_density or not completed_circuit: if not len(wall_carvers): # Re-spawn WallCarvers wall_carvers = [] for i in range( int(self.config. settings['num respawn wall carvers'])): spawn_coord = random.choice(list(walls_to_carve)) wall_carvers.append( wall_carver_class.WallCarver( spawn_coord.x, spawn_coord.y, self.config)) completed_circuit = False for wall_carver in wall_carvers: if wall_carver.coord_already_carved( wall_carvers ) or wall_carver.coord in past_seen_carved_coords: # This coord has already been seen by another WallCarver wall_carver.mark_for_death() elif wall_carver.coord in walls_to_carve: # This is a new coord. Remove the wall walls_to_carve.remove(wall_carver.coord) wall_carver.seen_coords.add(wall_carver.coord) num_walls -= 1 wall_carver.move() # Update past seen carved coordinates with coordinate sets of agents marked for death for marked_wall_carver in [ wall_carver for wall_carver in wall_carvers if wall_carver.marked_for_death ]: past_seen_carved_coords = past_seen_carved_coords.union( marked_wall_carver.seen_coords) # Kill WallCarvers marked for death wall_carvers = [ wall_carver for wall_carver in wall_carvers if not wall_carver.marked_for_death ] if not len(wall_carvers): completed_circuit = True wall_density = num_walls / num_coords self.wall_coords = walls_to_carve