File size: 7,941 Bytes
079c32c |
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 |
import numpy as np
class FeatureEncoder:
"""
Feature encoder referred to [football-pairs](https://github.com/seungeunrho/football-paris/blob/main/encoders/encoder_basic.py)
"""
def __init__(self):
self.active = -1
self.player_pos_x, self.player_pos_y = 0, 0
self.n_player = 10
def get_feature_dims(self):
dims = {
'player': 36,
'ball': 18,
'left_team': 7,
'left_team_closest': 7,
'right_team': 7,
'right_team_closest': 7,
}
return dims
def encode(self, obs):
player_num = obs['active']
player_pos_x, player_pos_y = obs['left_team'][player_num]
player_direction = np.array(obs['left_team_direction'][player_num])
player_speed = np.linalg.norm(player_direction)
player_role = obs['left_team_roles'][player_num]
player_role_onehot = np.eye(self.n_player)[player_role]
player_tired = obs['left_team_tired_factor'][player_num]
is_dribbling = obs['sticky_actions'][9]
is_sprinting = obs['sticky_actions'][8]
ball_x, ball_y, ball_z = obs['ball']
ball_x_relative = ball_x - player_pos_x
ball_y_relative = ball_y - player_pos_y
ball_x_speed, ball_y_speed, _ = obs['ball_direction']
ball_distance = np.linalg.norm([ball_x_relative, ball_y_relative])
ball_speed = np.linalg.norm([ball_x_speed, ball_y_speed])
ball_owned = 0.0
if obs['ball_owned_team'] == -1:
ball_owned = 0.0
else:
ball_owned = 1.0
ball_owned_by_us = 0.0
if obs['ball_owned_team'] == 0:
ball_owned_by_us = 1.0
elif obs['ball_owned_team'] == 1:
ball_owned_by_us = 0.0
else:
ball_owned_by_us = 0.0
ball_which_zone = self._encode_ball_which_zone(ball_x, ball_y)
if ball_distance > 0.03:
ball_far = 1.0
else:
ball_far = 0.0
avail = self._get_avail(obs, ball_distance)
player_state = np.concatenate(
(
avail[2:], obs['left_team'][player_num], player_direction * 100, [player_speed * 100],
player_role_onehot, [ball_far, player_tired, is_dribbling, is_sprinting]
)
)
ball_state = np.concatenate(
(
np.array(obs['ball']), np.array(ball_which_zone), np.array([ball_x_relative, ball_y_relative]),
np.array(obs['ball_direction']) * 20,
np.array([ball_speed * 20, ball_distance, ball_owned, ball_owned_by_us])
)
)
obs_left_team = np.delete(obs['left_team'], player_num, axis=0)
obs_left_team_direction = np.delete(obs['left_team_direction'], player_num, axis=0)
left_team_relative = obs_left_team
left_team_distance = np.linalg.norm(left_team_relative - obs['left_team'][player_num], axis=1, keepdims=True)
left_team_speed = np.linalg.norm(obs_left_team_direction, axis=1, keepdims=True)
left_team_tired = np.delete(obs['left_team_tired_factor'], player_num, axis=0).reshape(-1, 1)
left_team_state = np.concatenate((left_team_relative*2, obs_left_team_direction*100, left_team_speed*100, \
left_team_distance*2, left_team_tired), axis=1)
left_closest_idx = np.argmin(left_team_distance)
left_closest_state = left_team_state[left_closest_idx]
obs_right_team = np.array(obs['right_team'])
obs_right_team_direction = np.array(obs['right_team_direction'])
right_team_distance = np.linalg.norm(obs_right_team - obs['left_team'][player_num], axis=1, keepdims=True)
right_team_speed = np.linalg.norm(obs_right_team_direction, axis=1, keepdims=True)
right_team_tired = np.array(obs['right_team_tired_factor']).reshape(-1, 1)
right_team_state = np.concatenate((obs_right_team*2, obs_right_team_direction*100, right_team_speed*100, \
right_team_distance*2, right_team_tired), axis=1)
right_closest_idx = np.argmin(right_team_distance)
right_closest_state = right_team_state[right_closest_idx]
state_dict = {
"player": player_state,
"ball": ball_state,
"left_team": left_team_state,
"left_closest": left_closest_state,
"right_team": right_team_state,
"right_closest": right_closest_state,
"avail": avail
}
return state_dict
def _get_avail(self, obs, ball_distance):
avail = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
NO_OP, MOVE, LONG_PASS, HIGH_PASS, SHORT_PASS, SHOT, SPRINT, RELEASE_MOVE, \
RELEASE_SPRINT, SLIDE, DRIBBLE, RELEASE_DRIBBLE = 0, 1, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18
if obs['ball_owned_team'] == 1: # opponents owning ball
avail[LONG_PASS], avail[HIGH_PASS], avail[SHORT_PASS], avail[SHOT], avail[DRIBBLE] = 0, 0, 0, 0, 0
elif obs['ball_owned_team'] == -1 and ball_distance > 0.03 and obs['game_mode'
] == 0: # Ground ball and far from me
avail[LONG_PASS], avail[HIGH_PASS], avail[SHORT_PASS], avail[SHOT], avail[DRIBBLE] = 0, 0, 0, 0, 0
else: # my team owning ball
avail[SLIDE] = 0
# Dealing with sticky actions
sticky_actions = obs['sticky_actions']
if sticky_actions[8] == 0: # sprinting
avail[RELEASE_SPRINT] = 0
if sticky_actions[9] == 1: # dribbling
avail[SLIDE] = 0
else:
avail[RELEASE_DRIBBLE] = 0
if np.sum(sticky_actions[:8]) == 0:
avail[RELEASE_MOVE] = 0
# if too far, no shot
ball_x, ball_y, _ = obs['ball']
if ball_x < 0.64 or ball_y < -0.27 or 0.27 < ball_y:
avail[SHOT] = 0
elif (0.64 <= ball_x and ball_x <= 1.0) and (-0.27 <= ball_y and ball_y <= 0.27):
avail[HIGH_PASS], avail[LONG_PASS] = 0, 0
if obs['game_mode'] == 2 and ball_x < -0.7: # Our GoalKick
avail = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
avail[LONG_PASS], avail[HIGH_PASS], avail[SHORT_PASS] = 1, 1, 1
return np.array(avail)
elif obs['game_mode'] == 4 and ball_x > 0.9: # Our CornerKick
avail = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
avail[LONG_PASS], avail[HIGH_PASS], avail[SHORT_PASS] = 1, 1, 1
return np.array(avail)
elif obs['game_mode'] == 6 and ball_x > 0.6: # Our PenaltyKick
avail = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
avail[SHOT] = 1
return np.array(avail)
return np.array(avail)
def _encode_ball_which_zone(self, ball_x, ball_y):
MIDDLE_X, PENALTY_X, END_X = 0.2, 0.64, 1.0
LEFT_PENALTY, LEFT_HALF, HALF, RIGHT_PENALTY, RIGHT_HALF, OTHERS = 0, 1, 2, 3, 4, 5
PENALTY_Y, END_Y = 0.27, 0.42
res = np.eye(6)
if (-END_X <= ball_x and ball_x < -PENALTY_X) and (-PENALTY_Y < ball_y and ball_y < PENALTY_Y):
return res[LEFT_PENALTY]
elif (-END_X <= ball_x and ball_x < -MIDDLE_X) and (-END_Y < ball_y and ball_y < END_Y):
return res[LEFT_HALF]
elif (-MIDDLE_X <= ball_x and ball_x <= MIDDLE_X) and (-END_Y < ball_y and ball_y < END_Y):
return res[HALF]
elif (PENALTY_X < ball_x and ball_x <= END_X) and (-PENALTY_Y < ball_y and ball_y < PENALTY_Y):
return res[RIGHT_PENALTY]
elif (MIDDLE_X < ball_x and ball_x <= END_X) and (-END_Y < ball_y and ball_y < END_Y):
return res[RIGHT_HALF]
else:
return res[OTHERS]
|