-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfeatures.py
More file actions
229 lines (181 loc) · 8.41 KB
/
Copy pathfeatures.py
File metadata and controls
229 lines (181 loc) · 8.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
###############################################################################
#
# features.py
#
###############################################################################
from common import *
#------------------------------------------------------------------------------
#
# Features class
#
# This class extracts the relevant features that will be used by
# the version of Pac-Man that uses function approximation.
# The features include distance to the closest pellet in any
# direation, and the distance to the closest ghost within three
# tiles in any direction.
#
#------------------------------------------------------------------------------
class Features:
#------------------------------------------------------------------------------
# __init__()
#------------------------------------------------------------------------------
def __init__(self, state, game):
self.state = state
self.game = game
self.features = {}
self.reset_features()
#------------------------------------------------------------------------------
# reset_features()
#------------------------------------------------------------------------------
def reset_features(self):
"Resets all the features"
self.ghost_visible = False
# These are the features that will be used
self.features = {'ghost_distance_UP': 0.0, # Minimum distance from any ghost above Pac-Man
'ghost_distance_DOWN': 0.0, # Minimum distance from any ghost below Pac-Man
'ghost_distance_LEFT': 0.0, # Minimum distance from any ghost to the left Pac-Man
'ghost_distance_RIGHT': 0.0, # Minimum distance from any ghost to the right Pac-Man
'pellet_distance_UP': 0.0, # Minimum distance from any pellet above Pac-Man
'pellet_distance_DOWN': 0.0, # Minimum distance from any pellet below Pac-Man
'pellet_distance_LEFT': 0.0, # Minimum distance from any pellet to the left Pac-Man
'pellet_distance_RIGHT': 0.0} # Minimum distance from any pellet to the right Pac-Man
#------------------------------------------------------------------------------
# get_features_list()
#------------------------------------------------------------------------------
def make_features_list(self, f):
"Transforms the feature dictionary f into a list of feature values with a known order"
features_list = []
features_list.append( f['ghost_distance_UP'] )
features_list.append( f['ghost_distance_DOWN'] )
features_list.append( f['ghost_distance_LEFT'] )
features_list.append( f['ghost_distance_RIGHT'] )
features_list.append( f['pellet_distance_UP'] )
features_list.append( f['pellet_distance_DOWN'] )
features_list.append( f['pellet_distance_LEFT'] )
features_list.append( f['pellet_distance_RIGHT'] )
# Return the features we computed
return features_list
#------------------------------------------------------------------------------
# get_features()
#------------------------------------------------------------------------------
def get_features(self):
"Returns the set of features that are relevant to learning"
# First, reset all of the features
self.reset_features()
# Call each feature function in our list
self.get_ghost_distances()
self.get_pellet_distances()
# Return the features we computed
return self.features
#------------------------------------------------------------------------------
# get_ghost_distances()
#------------------------------------------------------------------------------
def get_ghost_distances(self):
"Computes the distance from each ghost in any direction"
# First get the current position of Pac-Man
pacman_position = self.state.pacman_rect.topleft
# TODO: This is a bare integer that is a parameter to our system
# We are not interested in ghost that are farther than 3 tiles away
minimum = 3 * self.game.manager.config_options['tile_size']
# Now look at each ghost to find the closest one in any direction
for i in range(GHOSTS):
# We only care to look at a ghost if it can hurt us
if self.state.ghost_mode[i] == NORMAL:
# Get the ghost's current position
ghost_position = self.state.ghost_rect[i].topleft
# Find the Manhattan distance from the ghost
distance_x = ghost_position[0] - pacman_position[0]
distance_y = ghost_position[1] - pacman_position[1]
manhattan_distance = abs(distance_x) + abs(distance_y)
# If this ghost is closer than the last closest one (or 3 tiles), then update the features
if manhattan_distance < minimum:
self.ghost_visible = True
# We have a new minimum
minimum = manhattan_distance
# If the lateral distance is greater than the vertical distance, then it is to our side
# otherwise it is above or below us
if abs(distance_x) >= abs(distance_y):
# If the distance is negative, the ghost is to our left
if distance_x <= 0:
self.features['ghost_distance_LEFT'] = 1.0
self.features['ghost_distance_RIGHT'] = 0.0
self.features['ghost_distance_UP'] = 0.0
self.features['ghost_distance_DOWN'] = 0.0
# The ghost is to our right
else:
self.features['ghost_distance_LEFT'] = 0.0
self.features['ghost_distance_RIGHT'] = 1.0
self.features['ghost_distance_UP'] = 0.0
self.features['ghost_distance_DOWN'] = 0.0
else:
# The ghost is above us
if distance_y <= 0:
self.features['ghost_distance_LEFT'] = 0.0
self.features['ghost_distance_RIGHT'] = 0.0
self.features['ghost_distance_UP'] = 1.0
self.features['ghost_distance_DOWN'] = 0.0
# The ghost is below us
else:
self.features['ghost_distance_LEFT'] = 0.0
self.features['ghost_distance_RIGHT'] = 0.0
self.features['ghost_distance_UP'] = 0.0
self.features['ghost_distance_DOWN'] = 1.0
#------------------------------------------------------------------------------
# get_pellet_distances()
#------------------------------------------------------------------------------
def get_pellet_distances(self):
"Computes the distance from each the closest pellet in each direction \
and the number of pellets in each direction"
# Get pacman's current position
pacman_position = self.state.pacman_rect.topleft
# Get Pac-Man's current tile position
tile_position = self.game.checker.get_tile_coordinates(self.state.pacman_rect.topleft)
# Get the tile size
tile_size = self.game.manager.config_options['tile_size']
# Our initial minimum is arbitrarily large
minimum = 999
if self.ghost_visible:
return
# Look at every tile in the game to find pellets
for row in range(self.state.level.level_dim[1]):
for col in range(self.state.level.level_dim[0]):
# Get the tile's ID
tile_id = self.state.level.level_layout[row][col]
# If the tile is a pellet, then look at it
if tile_id == '2' or tile_id == '3':
# Find the Manhattan distance between Pac-Man and the pellet
distance_x = col - tile_position[0]
distance_y = row - tile_position[1]
manhattan_distance = abs(distance_x) * tile_size + abs(distance_y) * tile_size
# If the distance is a new minimum and we're not eating the pellet right now, then register it
if manhattan_distance <= minimum and manhattan_distance != 0:
# We have a new minimum
minimum = manhattan_distance
# If the lateral distance is greater than the vertical distance, than it is to our side
# otherwise it is above or below us
if abs(distance_x) >= abs(distance_y):
# If the distance is negative, the pellet is to our left
if distance_x < 0:
self.features['pellet_distance_LEFT'] = 1.0
self.features['pellet_distance_RIGHT'] = 0.0
self.features['pellet_distance_UP'] = 0.0
self.features['pellet_distance_DOWN'] = 0.0
# It is to our right
elif distance_x > 0:
self.features['pellet_distance_LEFT'] = 0.0
self.features['pellet_distance_RIGHT'] = 1.0
self.features['pellet_distance_UP'] = 0.0
self.features['pellet_distance_DOWN'] = 0.0
else:
# It is above us
if distance_y < 0:
self.features['pellet_distance_LEFT'] = 0.0
self.features['pellet_distance_RIGHT'] = 0.0
self.features['pellet_distance_UP'] = 1.0
self.features['pellet_distance_DOWN'] = 0.0
# It is below us
elif distance_y > 0:
self.features['pellet_distance_LEFT'] = 0.0
self.features['pellet_distance_RIGHT'] = 0.0
self.features['pellet_distance_UP'] = 0.0
self.features['pellet_distance_DOWN'] = 1.0