pacai.capture.agents

  1import typing
  2
  3import pacai.agents.greedy
  4import pacai.core.action
  5import pacai.core.agent
  6import pacai.core.gamestate
  7import pacai.core.features
  8import pacai.search.distance
  9
 10GHOST_IGNORE_RANGE: float = 2.5
 11
 12class DefensiveAgent(pacai.agents.greedy.GreedyFeatureAgent):
 13    """
 14    A capture agent that prioritizes defending its own territory.
 15    """
 16
 17    def __init__(self,
 18            override_weights: dict[str, float] | None = None,
 19            **kwargs: typing.Any) -> None:
 20        kwargs['feature_extractor_func'] = _extract_baseline_defensive_features
 21        super().__init__(**kwargs)
 22
 23        self._distances: pacai.search.distance.DistancePreComputer = pacai.search.distance.DistancePreComputer()
 24        """ Precompute distances. """
 25
 26        # Set base weights.
 27        self.weights['on_home_side'] = 100.0
 28        self.weights['stopped'] = -100.0
 29        self.weights['reverse'] = -2.0
 30        self.weights['num_invaders'] = -1000.0
 31        self.weights['distance_to_invader'] = -10.0
 32
 33        if (override_weights is None):
 34            override_weights = {}
 35
 36        for (key, weight) in override_weights.items():
 37            self.weights[key] = weight
 38
 39    def game_start(self, initial_state: pacai.core.gamestate.GameState) -> None:
 40        self._distances.compute(initial_state.board)
 41
 42class OffensiveAgent(pacai.agents.greedy.GreedyFeatureAgent):
 43    """
 44    A capture agent that prioritizes defending its own territory.
 45    """
 46
 47    def __init__(self,
 48            override_weights: dict[str, float] | None = None,
 49            **kwargs: typing.Any) -> None:
 50        kwargs['feature_extractor_func'] = _extract_baseline_offensive_features
 51        super().__init__(**kwargs)
 52
 53        self._distances: pacai.search.distance.DistancePreComputer = pacai.search.distance.DistancePreComputer()
 54        """ Precompute distances. """
 55
 56        # Set base weights.
 57        self.weights['score'] = 100.0
 58        self.weights['distance_to_food'] = -1.0
 59
 60        if (override_weights is None):
 61            override_weights = {}
 62
 63        for (key, weight) in override_weights.items():
 64            self.weights[key] = weight
 65
 66    def game_start(self, initial_state: pacai.core.gamestate.GameState) -> None:
 67        self._distances.compute(initial_state.board)
 68
 69def _extract_baseline_defensive_features(
 70        state: pacai.core.gamestate.GameState,
 71        action: pacai.core.action.Action,
 72        agent: pacai.core.agent.Agent | None = None,
 73        **kwargs: typing.Any) -> pacai.core.features.FeatureDict:
 74    agent = typing.cast(DefensiveAgent, agent)
 75    state = typing.cast(pacai.capture.gamestate.GameState, state)
 76
 77    features: pacai.core.features.FeatureDict = pacai.core.features.FeatureDict()
 78
 79    current_position = state.get_agent_position(agent.agent_index)
 80    if (current_position is None):
 81        # We are dead and waiting to respawn.
 82        return features
 83
 84    # Note the side of the board we are on.
 85    features['on_home_side'] = int(state.is_ghost(agent_index = agent.agent_index))
 86
 87    # Prefer moving over stopping.
 88    features['stopped'] = int(action == pacai.core.action.STOP)
 89
 90    # Prefer not turning around.
 91    # Remember that the state we get is already a successor, so we have to look two actions back.
 92    agent_actions = state.get_agent_actions(agent.agent_index)
 93    if (len(agent_actions) > 1):
 94        features['reverse'] = int(action == state.get_reverse_action(agent_actions[-2]))
 95
 96    # We don't like any invaders on our side.
 97    invader_positions = state.get_invader_positions(agent_index = agent.agent_index)
 98    features['num_invaders'] = len(invader_positions)
 99
100    # Hunt down the closest invader!
101    if (len(invader_positions) > 0):
102        invader_distances = [agent._distances.get_distance(current_position, invader_position) for invader_position in invader_positions.values()]
103        features['distance_to_invader'] = min(distance for distance in invader_distances if (distance is not None))
104
105    return features
106
107def _extract_baseline_offensive_features(
108        state: pacai.core.gamestate.GameState,
109        action: pacai.core.action.Action,
110        agent: pacai.core.agent.Agent | None = None,
111        **kwargs: typing.Any) -> pacai.core.features.FeatureDict:
112    agent = typing.cast(OffensiveAgent, agent)
113    state = typing.cast(pacai.capture.gamestate.GameState, state)
114
115    features: pacai.core.features.FeatureDict = pacai.core.features.FeatureDict()
116    features['score'] = state.get_normalized_score(agent.agent_index)
117
118    # Note the side of the board we are on.
119    features['on_home_side'] = int(state.is_ghost(agent_index = agent.agent_index))
120
121    # Prefer moving over stopping.
122    features['stopped'] = int(action == pacai.core.action.STOP)
123
124    # Prefer not turning around.
125    # Remember that the state we get is already a successor, so we have to look two actions back.
126    agent_actions = state.get_agent_actions(agent.agent_index)
127    if (len(agent_actions) > 1):
128        features['reverse'] = int(action == state.get_reverse_action(agent_actions[-2]))
129
130    current_position = state.get_agent_position(agent.agent_index)
131    if (current_position is None):
132        # We are dead and waiting to respawn.
133        return features
134
135    food_positions = state.get_food(agent_index = agent.agent_index)
136    if (len(food_positions) > 0):
137        food_distances = [agent._distances.get_distance(current_position, food_position) for food_position in food_positions]
138        features['distance_to_food'] = min(distance for distance in food_distances if (distance is not None))
139    else:
140        # There is no food left, give a large score.
141        features['distance_to_food'] = -100000
142
143    ghost_positions = state.get_nonscared_opponent_positions(agent_index = agent.agent_index)
144    if (len(ghost_positions) > 0):
145        ghost_distances = [agent._distances.get_distance(current_position, ghost_position) for ghost_position in ghost_positions.values()]
146        features['distance_to_ghost'] = min(distance for distance in ghost_distances if (distance is not None))
147        if (features['distance_to_ghost'] > GHOST_IGNORE_RANGE):
148            features['distance_to_ghost'] = 1000
149
150        features['distance_to_ghost_squared'] = features['distance_to_ghost'] ** 2
151    else:
152        features['distance_to_ghost'] = 0
153
154    return features
GHOST_IGNORE_RANGE: float = 2.5
class DefensiveAgent(pacai.agents.greedy.GreedyFeatureAgent):
13class DefensiveAgent(pacai.agents.greedy.GreedyFeatureAgent):
14    """
15    A capture agent that prioritizes defending its own territory.
16    """
17
18    def __init__(self,
19            override_weights: dict[str, float] | None = None,
20            **kwargs: typing.Any) -> None:
21        kwargs['feature_extractor_func'] = _extract_baseline_defensive_features
22        super().__init__(**kwargs)
23
24        self._distances: pacai.search.distance.DistancePreComputer = pacai.search.distance.DistancePreComputer()
25        """ Precompute distances. """
26
27        # Set base weights.
28        self.weights['on_home_side'] = 100.0
29        self.weights['stopped'] = -100.0
30        self.weights['reverse'] = -2.0
31        self.weights['num_invaders'] = -1000.0
32        self.weights['distance_to_invader'] = -10.0
33
34        if (override_weights is None):
35            override_weights = {}
36
37        for (key, weight) in override_weights.items():
38            self.weights[key] = weight
39
40    def game_start(self, initial_state: pacai.core.gamestate.GameState) -> None:
41        self._distances.compute(initial_state.board)

A capture agent that prioritizes defending its own territory.

DefensiveAgent(override_weights: dict[str, float] | None = None, **kwargs: Any)
18    def __init__(self,
19            override_weights: dict[str, float] | None = None,
20            **kwargs: typing.Any) -> None:
21        kwargs['feature_extractor_func'] = _extract_baseline_defensive_features
22        super().__init__(**kwargs)
23
24        self._distances: pacai.search.distance.DistancePreComputer = pacai.search.distance.DistancePreComputer()
25        """ Precompute distances. """
26
27        # Set base weights.
28        self.weights['on_home_side'] = 100.0
29        self.weights['stopped'] = -100.0
30        self.weights['reverse'] = -2.0
31        self.weights['num_invaders'] = -1000.0
32        self.weights['distance_to_invader'] = -10.0
33
34        if (override_weights is None):
35            override_weights = {}
36
37        for (key, weight) in override_weights.items():
38            self.weights[key] = weight
def game_start(self, initial_state: pacai.core.gamestate.GameState) -> None:
40    def game_start(self, initial_state: pacai.core.gamestate.GameState) -> None:
41        self._distances.compute(initial_state.board)

Notify this agent that the game is about to start. The provided agent index is the game's index/id for this agent. The state represents the initial state of the game. Any precomputation for this game should be done in this method. Calls to this method may be subject to a timeout.

class OffensiveAgent(pacai.agents.greedy.GreedyFeatureAgent):
43class OffensiveAgent(pacai.agents.greedy.GreedyFeatureAgent):
44    """
45    A capture agent that prioritizes defending its own territory.
46    """
47
48    def __init__(self,
49            override_weights: dict[str, float] | None = None,
50            **kwargs: typing.Any) -> None:
51        kwargs['feature_extractor_func'] = _extract_baseline_offensive_features
52        super().__init__(**kwargs)
53
54        self._distances: pacai.search.distance.DistancePreComputer = pacai.search.distance.DistancePreComputer()
55        """ Precompute distances. """
56
57        # Set base weights.
58        self.weights['score'] = 100.0
59        self.weights['distance_to_food'] = -1.0
60
61        if (override_weights is None):
62            override_weights = {}
63
64        for (key, weight) in override_weights.items():
65            self.weights[key] = weight
66
67    def game_start(self, initial_state: pacai.core.gamestate.GameState) -> None:
68        self._distances.compute(initial_state.board)

A capture agent that prioritizes defending its own territory.

OffensiveAgent(override_weights: dict[str, float] | None = None, **kwargs: Any)
48    def __init__(self,
49            override_weights: dict[str, float] | None = None,
50            **kwargs: typing.Any) -> None:
51        kwargs['feature_extractor_func'] = _extract_baseline_offensive_features
52        super().__init__(**kwargs)
53
54        self._distances: pacai.search.distance.DistancePreComputer = pacai.search.distance.DistancePreComputer()
55        """ Precompute distances. """
56
57        # Set base weights.
58        self.weights['score'] = 100.0
59        self.weights['distance_to_food'] = -1.0
60
61        if (override_weights is None):
62            override_weights = {}
63
64        for (key, weight) in override_weights.items():
65            self.weights[key] = weight
def game_start(self, initial_state: pacai.core.gamestate.GameState) -> None:
67    def game_start(self, initial_state: pacai.core.gamestate.GameState) -> None:
68        self._distances.compute(initial_state.board)

Notify this agent that the game is about to start. The provided agent index is the game's index/id for this agent. The state represents the initial state of the game. Any precomputation for this game should be done in this method. Calls to this method may be subject to a timeout.