77import pacai .core .features
88import pacai .search .distance
99
10+ GHOST_IGNORE_RANGE : float = 2.5
11+
1012class DefensiveAgent (pacai .agents .greedy .GreedyFeatureAgent ):
1113 """
1214 A capture agent that prioritizes defending its own territory.
1315 """
1416
15- def __init__ (self , ** kwargs ) -> None :
17+ def __init__ (self ,
18+ override_weights : dict [str , float ] | None = None ,
19+ ** kwargs ) -> None :
1620 kwargs ['feature_extractor_func' ] = _extract_baseline_defensive_features
1721 super ().__init__ (** kwargs )
1822
1923 self ._distances : pacai .search .distance .DistancePreComputer = pacai .search .distance .DistancePreComputer ()
2024 """ Precompute distances. """
2125
26+ # Set base weights.
2227 self .weights ['on_home_side' ] = 100.0
2328 self .weights ['stopped' ] = - 100.0
24- self .weights ['reverse' ] = - 2
29+ self .weights ['reverse' ] = - 2.0
2530 self .weights ['num_invaders' ] = - 1000.0
2631 self .weights ['distance_to_invader' ] = - 10.0
2732
33+ if (override_weights is None ):
34+ override_weights = {}
35+
36+ for (key , weight ) in override_weights .items ():
37+ self .weights [key ] = weight
38+
2839 def game_start (self , initial_state : pacai .core .gamestate .GameState ) -> None :
2940 self ._distances .compute (initial_state .board )
3041
@@ -33,16 +44,25 @@ class OffensiveAgent(pacai.agents.greedy.GreedyFeatureAgent):
3344 A capture agent that prioritizes defending its own territory.
3445 """
3546
36- def __init__ (self , ** kwargs ) -> None :
47+ def __init__ (self ,
48+ override_weights : dict [str , float ] | None = None ,
49+ ** kwargs ) -> None :
3750 kwargs ['feature_extractor_func' ] = _extract_baseline_offensive_features
3851 super ().__init__ (** kwargs )
3952
4053 self ._distances : pacai .search .distance .DistancePreComputer = pacai .search .distance .DistancePreComputer ()
4154 """ Precompute distances. """
4255
56+ # Set base weights.
4357 self .weights ['score' ] = 100.0
4458 self .weights ['distance_to_food' ] = - 1.0
4559
60+ if (override_weights is None ):
61+ override_weights = {}
62+
63+ for (key , weight ) in override_weights .items ():
64+ self .weights [key ] = weight
65+
4666 def game_start (self , initial_state : pacai .core .gamestate .GameState ) -> None :
4767 self ._distances .compute (initial_state .board )
4868
@@ -95,18 +115,40 @@ def _extract_baseline_offensive_features(
95115 features : pacai .core .features .FeatureDict = pacai .core .features .FeatureDict ()
96116 features ['score' ] = state .get_normalized_score (agent .agent_index )
97117
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+
98130 current_position = state .get_agent_position (agent .agent_index )
99131 if (current_position is None ):
100132 # We are dead and waiting to respawn.
101133 return features
102134
103135 food_positions = state .get_food (agent_index = agent .agent_index )
104-
105136 if (len (food_positions ) > 0 ):
106137 food_distances = [agent ._distances .get_distance (current_position , food_position ) for food_position in food_positions ]
107138 features ['distance_to_food' ] = min (distance for distance in food_distances if (distance is not None ))
108139 else :
109140 # There is no food left, give a large score.
110141 features ['distance_to_food' ] = - 100000
111142
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+
112154 return features
0 commit comments