Spaces:
Sleeping
Sleeping
""" | |
## referenced https://www.kaggle.com/eugenkeil/simple-baseline-bot by @eugenkeil | |
## referenced https://www.kaggle.com/david1013/tunable-baseline-bot by @david1013 | |
""" | |
from kaggle_environments.envs.football.helpers import * | |
from math import sqrt | |
from enum import Enum | |
import random | |
import torch | |
import torch.nn as nn | |
import numpy as np | |
from ding.torch_utils import tensor_to_list, one_hot, to_ndarray | |
from ding.utils import MODEL_REGISTRY | |
from ding.torch_utils import to_tensor, to_dtype | |
""" | |
Readable Reminder | |
********************* | |
class Action(Enum): | |
Idle = 0 | |
Left = 1 | |
TopLeft = 2 | |
Top = 3 | |
TopRight = 4 | |
Right = 5 | |
BottomRight = 6 | |
Bottom = 7 | |
BottomLeft = 8 | |
LongPass= 9 | |
HighPass = 10 | |
ShortPass = 11 | |
Shot = 12 | |
Sprint = 13 | |
ReleaseDirection = 14 | |
ReleaseSprint = 15 | |
Slide = 16 | |
Dribble = 17 | |
ReleaseDribble = 18 | |
sticky_index_to_action = [ | |
Action.Left, | |
Action.TopLeft, | |
Action.Top, | |
Action.TopRight, | |
Action.Right, | |
Action.BottomRight, | |
Action.Bottom, | |
Action.BottomLeft, | |
Action.Sprint, | |
Action.Dribble | |
] | |
class PlayerRole(Enum): | |
GoalKeeper = 0 | |
CenterBack = 1 | |
LeftBack = 2 | |
RightBack = 3 | |
DefenceMidfield = 4 | |
CentralMidfield = 5 | |
LeftMidfield = 6 | |
RIghtMidfield = 7 | |
AttackMidfield = 8 | |
CentralFront = 9 | |
class GameMode(Enum): | |
Normal = 0 | |
KickOff = 1 | |
GoalKick = 2 | |
FreeKick = 3 | |
Corner = 4 | |
ThrowIn = 5 | |
Penalty = 6 | |
""" | |
class Stiuation(Enum): | |
Delaying = 0 | |
Offencing = 1 | |
Deffencing = 2 | |
class Line(object): | |
def __init__(self, pos1, pos2): | |
self.a = 1 | |
x1, y1 = pos1 | |
x2, y2 = pos2 | |
if (y2 - y1) != 0.0: | |
self.b = (x2 - x1) / (y2 - y1) | |
else: | |
self.b = 1e5 | |
self.c = -x1 - (self.b * y2) | |
self.length = dist(pos1, pos2) | |
def distToLine(self, pos): | |
return (self.a * pos[0] + self.b * pos[1] + self.c) / sqrt(self.a ** 2 + self.b ** 2) | |
roles = [0, 7, 9, 2, 1, 1, 3, 5, 5, 5, 6] | |
passes = [Action.ShortPass, Action.LongPass, Action.HighPass] | |
offenseScore = { | |
0: [-8.0, 0.0], | |
1: [0.6, 0.8], | |
2: [0.6, 0.85], | |
3: [0.6, 0.85], | |
4: [0.7, 0.9], | |
5: [0.8, 0.9], | |
6: [1, 1], | |
7: [1, 1], | |
8: [1, 1.1], | |
9: [1.1, 1.2] | |
} | |
passBias = 2.0 | |
defenceThreatDist = 0.3 | |
threatAvg = 3.0 | |
shotDistAbs = 0.03 | |
shotDistFactor = 0.6 | |
offenseGoalDistFactor = 3.0 | |
offenseKeeperDistFactor = 0.5 | |
offenseTirenessFactor = 0.3 | |
sprintTirenessFactor = 0.5 | |
passForShotFactor = 0.6 | |
FREEKICK_SHOT_AREA = [[0.5, 1], [-0.2, 0.2]] | |
START_SHOT_AREA1 = [[0.6, 0.75], [-0.2, 0.2]] | |
START_SHOT_AREA2 = [[0.75, 0.95], [-0.13, 0.13]] | |
PASS_FOR_SHOT_AREA1 = [[0.75, 1], [-0.42, -0.18]] | |
PASS_FOR_SHOT_AREA2 = [[0.75, 1], [0.18, 0.42]] | |
KEEPER_ZONE_AREA = [[0.75, 1], [-0.2, 0.2]] | |
LONG_SHOT_RANGE_AREA = [[0.5, 1], [-0.25, 0.25]] | |
SPRINT_AREA = [[-0.1, 0.6], [-0.42, 0.42]] | |
DEFENCE_SPRING_AREA = [[-0.7, 0.4], [-0.4, 0.4]] | |
# DRIBBLE_AREA = [[-0.1, 0.2], [-0.3, 0.3]] | |
SLIDE_AREA = [[-0.65, 0], [-0.42, 0.42]] | |
takenSelfFactor = 0.5 | |
passFactors = {Action.HighPass: [1.0, 1.2, 3.0], Action.ShortPass: [1.1, 1.5, 1.5], Action.LongPass: [1.0, 1.2, 2]} | |
# top right/ Bottom left corner are: | |
# [1, -0.42] and [-1, 0.42], respectively. | |
def dist(pos1, pos2): | |
return sqrt((pos1[1] - pos2[1]) ** 2 + (pos1[0] - pos2[0]) ** 2) | |
def dirSign(x): | |
if abs(x) < 0.01: | |
return 1 | |
elif x < 0: | |
return 0 | |
return 2 | |
def plusPos(pos1, pos2): | |
return [pos1[0] + pos2[0], pos1[1] + pos2[1]] | |
def vec2dir(vec): | |
p = sqrt(vec[0] ** 2 + vec[1] ** 2) | |
coef = 1 / p | |
return [vec[0] * coef, vec[1] * coef] | |
TOTAL_STEP = 3000 | |
# functions help moving | |
directions = [ | |
[Action.TopLeft, Action.Top, Action.TopRight], [Action.Left, Action.Idle, Action.Right], | |
[Action.BottomLeft, Action.Bottom, Action.BottomRight] | |
] | |
def insideArea(pos, area): | |
return area[0][0] <= pos[0] <= area[0][1] and area[1][0] <= pos[1] <= area[1][1] | |
def gotoDir(x, y): | |
xdir = dirSign(x) | |
ydir = dirSign(y) | |
return directions[ydir][xdir] | |
class Processer(object): | |
def __init__(self): | |
self._obs = {} | |
self._curPos = None | |
self._keeperPos = None | |
self._goalPos = [1, 0] | |
self._shot_dir_ready = False | |
self._pass_dir_ready = False | |
self._ball_is_free = False | |
self._we_have_ball = False | |
self._enemy_have_ball = False | |
self._our_goalkeeper_have_ball = False | |
self._shot_buf_player = None | |
self._shot_buf_step = -1 | |
self._pass_buf_player = None | |
self._pass_buf_step = -1 | |
self._score_diff = 0 | |
self._pass_type = Action.ShortPass | |
def preprocess(self): | |
self._game_mode = self._obs['game_mode'] | |
self._cur_player = self._obs['active'] | |
if self._obs['score'].shape[0] == 2: | |
self._score_diff = self._obs['score'][0] - self._obs['score'][1] | |
else: | |
self._score_diff = self._obs['score'] | |
self._curPos = self._obs['left_team'][self._obs['active']] | |
self._curDir = self._obs['left_team_direction'][self._obs['active']] | |
self._keeperPos = self._obs['right_team'][0] | |
self._ballPos = self._obs['ball'] | |
self._ourPos = self._obs['left_team'] | |
self._enemyPos = self._obs['right_team'] | |
self._ball_is_free = self._obs['ball_owned_team'] == -1 | |
self._we_have_ball = self._obs['ball_owned_team'] == 0 | |
self._enemy_have_ball = self._obs['ball_owned_team'] == 1 | |
self._our_goalkeeper_have_ball = self._obs['ball_owned_player'] == 0 and self._we_have_ball | |
self._our_active_have_ball = self._we_have_ball and self._obs['ball_owned_player'] == self._obs['active'] | |
self._controlled_role = self._obs['left_team_roles'][self._obs['active']] | |
self._most_foward_enemy_pos = self.getMostForwardEnemyPos() | |
self._closest_enemey_pos = self.getClosestEnemyPos() | |
self._closest_enemey_to_cur_vec = [ | |
self._curPos[0] - self._closest_enemey_pos[0], self._curPos[1] - self._closest_enemey_pos[1] | |
] | |
self._closest_enemey_to_cur_dir = vec2dir(self._closest_enemey_to_cur_vec) | |
self._cloest_enemey_dist = dist(self._curPos, self._closest_enemey_pos) | |
self._remain_step = self._obs['steps_left'] | |
self._cur_tireness = self._obs['left_team_tired_factor'][self._obs['active']] | |
self._our_tireness = self._obs['left_team_tired_factor'] | |
self._dribbling = Action.Dribble in self._obs['sticky_actions'] | |
self._sprinting = Action.Sprint in self._obs['sticky_actions'] | |
self._our_goalkeeper_active = self._cur_player == 0 | |
# TODO | |
self._ball_dir = self._obs['ball_direction'] | |
self._ball_owner_dir = self.getBallOwnerDir() | |
self._ball_owner_pos = self.getBallOwnerPos() | |
if self._enemy_have_ball: | |
self._closest_to_enemy_pos, self._closest_to_enemy_player = self.getClosestToEnemy() | |
if not self._shot_dir_ready: | |
self._shot_buf_player = -1 | |
# general helper | |
################################ | |
def getRole(self, i): | |
return roles[i] | |
# general helper for init | |
################################# | |
def getBallOwnerPos(self): | |
if self._ball_is_free: | |
return None | |
elif self._we_have_ball: | |
return self._obs['left_team'][self._obs['ball_owned_player']] | |
else: | |
return self._obs['right_team'][self._obs['ball_owned_player']] | |
def getBallOwnerDir(self): | |
if self._ball_is_free: | |
return None | |
elif self._we_have_ball: | |
return self._obs['left_team_direction'][self._obs['ball_owned_player']] | |
else: | |
return self._obs['right_team_direction'][self._obs['ball_owned_player']] | |
# general movement | |
################################## | |
def gobetweenKeeperGate(self): | |
xdir = dirSign(self._keeperPos[0] / 2 + self._goalPos[0] / 2 - self._curPos[0] - 0.05) | |
ydir = dirSign(self._keeperPos[1] / 2 + self._goalPos[1] / 2 - self._curPos[1]) | |
return directions[ydir][xdir] | |
def gotoDst(self, x, y): | |
xdir = dirSign(x - self._curPos[0]) | |
ydir = dirSign(y - self._curPos[1]) | |
return directions[ydir][xdir] | |
def getMostForwardEnemyPos(self): | |
ret = [0, 0] | |
i = 0 | |
for pos in self._obs['right_team']: | |
if i == 0: | |
i += 1 | |
continue | |
if pos[0] > ret[0]: | |
ret = pos | |
return ret | |
def getAvgDefenceDistToPlayer(self, *args): | |
if len(args) == 0: | |
i = self._cur_player | |
else: | |
i = args[0] | |
sumDist = 0 | |
for pos in self._enemyPos: | |
if dist(pos, self._ourPos[i]) < defenceThreatDist: | |
sumDist += dist(pos, self._ourPos[i]) | |
return sumDist / threatAvg | |
def getClosestEnemy(self, *args): | |
if len(args) == 0: | |
i = self._cur_player | |
else: | |
i = args[0] | |
closest_pos = self._keeperPos | |
closest_index = 0 | |
index = 0 | |
closest_dist = 2 | |
for pos in self._obs['right_team']: | |
if dist(pos, self._ourPos[i]) < dist(self._ourPos[i], closest_pos): | |
closest_pos = pos | |
closest_index = index | |
closest_dist = dist(pos, self._ourPos[i]) | |
index += 1 | |
return [closest_pos, closest_index, closest_dist] | |
def getClosestEnemyPos(self, *args): | |
if len(args) == 0: | |
i = self._cur_player | |
else: | |
i = args[0] | |
return self.getClosestEnemy(i)[0] | |
def getClosestEnemyDist(self, *args): | |
if len(args) == 0: | |
i = self._cur_player | |
else: | |
i = args[0] | |
return self.getClosestEnemy(i)[2] | |
def should_sprint(self): | |
if self._cur_tireness * sprintTirenessFactor > ((TOTAL_STEP - self._remain_step) / TOTAL_STEP) + 0.2: | |
return False | |
if self._enemy_have_ball: | |
return insideArea(self._curPos, DEFENCE_SPRING_AREA) | |
if self._we_have_ball: | |
return insideArea(self._curPos, SPRINT_AREA) | |
# help Judge Shooting | |
def shotWill(self): | |
if insideArea(self._curPos, START_SHOT_AREA1) or insideArea(self._curPos, START_SHOT_AREA2): | |
return True | |
elif not insideArea(self._keeperPos, KEEPER_ZONE_AREA) and insideArea(self._curPos, LONG_SHOT_RANGE_AREA): | |
return True | |
if dist(self._curPos, self._keeperPos) < shotDistFactor * dist(self._keeperPos, self._goalPos) + shotDistAbs: | |
return True | |
return False | |
# short pass | |
# def shortPassForShot(self): | |
# if insideArea(self._curPos, PASS_FOR_SHOT_AREA1) or insideArea(self._curPos, PASS_FOR_SHOT_AREA2): | |
# if not self.judgeOffside(): | |
# return True | |
# return False | |
# help defense | |
######################### | |
def getClosestToEnemy(self): | |
retpos = self._obs['left_team'][0] | |
index = 0 | |
retindex = index | |
for pos in self._obs['left_team']: | |
if dist(pos, self._ball_owner_pos) < dist(retpos, self._ball_owner_pos): | |
retpos = pos | |
retindex = index | |
index += 1 | |
return retpos, retindex | |
def getMinxLeftTeam(self): | |
i = 0 | |
retpos = [1, 0] | |
for pos in self._ourPos: | |
if i == 0: | |
i += 1 | |
continue | |
if pos[0] < retpos[0]: | |
retpos = pos | |
return retpos | |
# After testing we know that sliding is not good, so no slide | |
def should_slide(self): | |
if not self._enemy_have_ball: | |
return False | |
# TODO | |
# replace 'and True' -> 'has yellow card' | |
if self._curPos[0] < self._ball_owner_pos[0] - 0.01 and self._curPos[0] < self._ballPos[0] - 0.007 and dist( | |
self._curPos, self._ball_owner_pos) < 0.03 and self._curDir[0] < 0 and insideArea(self._curPos, | |
SLIDE_AREA) and True: | |
return True | |
return False | |
# TODO | |
# can this be smarter? | |
def should_chase(self): | |
if self._curPos[0] > self._ball_owner_pos[0] + 0.02 and self._curPos[0] != self._closest_to_enemy_pos[0]: | |
return False | |
minLeftTeamPos = self.getMinxLeftTeam() | |
if self._curPos[0] > self._ball_owner_pos[0] + 0.03 and self._ball_owner_pos[0] - minLeftTeamPos[0] > 1.5 * abs( | |
self._ball_owner_pos[1] - minLeftTeamPos[1]): | |
return False | |
return True | |
# help not in our zone | |
def shotAway(self): | |
# disable or enable ? | |
return False | |
if self._curPos[0] < -0.7 and self._our_active_have_ball: | |
return True | |
return False | |
# def passAway(self): | |
# if self._curPos[0] < -0.4 and self._our_active_have_ball: | |
# return True | |
# return False | |
# functions use to judge passing | |
def judgeOffside(self, *args): | |
if len(args) == 0: | |
LeftTeam = 0 | |
for pos in self._obs['left_team']: | |
LeftTeam = max(LeftTeam, pos[0]) | |
else: | |
LeftTeam = self._ourPos[args[0]][0] | |
maxRightTeam = self.getMostForwardEnemyPos()[0] | |
return LeftTeam > maxRightTeam | |
# TODO | |
def passWill(self): | |
curOffenceMark = self.offenseMark(self._cur_player) | |
bestPassMark, bestPassType, bestPassIndex = self.getBestPass() | |
if bestPassMark > curOffenceMark + passBias: | |
# print("cur pos=", self._curPos) | |
# print("cur off score = ", curOffenceMark) | |
# print("best pass mark = ", bestPassMark) | |
# print("remain step = ", self._remain_step) | |
# print("best pass type = ", bestPassType) | |
# print("want to pass to = ", bestPassIndex) | |
return True, bestPassType, bestPassIndex | |
else: | |
return False, Action.ShortPass, -1 | |
# TODO | |
def getBestPass(self): | |
if not self._our_active_have_ball: | |
return -1, Action.ShortPass, -1 | |
bestPassType = Action.ShortPass | |
bestPassIndex = -1 | |
bestPassMark = -10 | |
for index in range(11): | |
# can't pass to yourself | |
if index == self._cur_player: | |
continue | |
passMark, passType = self.passMarkTo(index) | |
if passMark > bestPassMark: | |
bestPassMark = passMark | |
bestPassType = passType | |
bestPassIndex = index | |
return bestPassMark, bestPassType, bestPassIndex | |
# TODO | |
def passMarkTo(self, i): | |
bestPassType = Action.ShortPass | |
bestPassMark = -10 | |
for t in passes: | |
if self.getPassSuccessMark(i, t) + self.offenseMark(i) > bestPassMark: | |
bestPassType = t | |
bestPassMark = self.getPassSuccessMark(i, t) + self.offenseMark(i) | |
return bestPassMark, bestPassType | |
def getRoleOffenceScore(self, i): | |
r = roles[i] | |
adder, multier = offenseScore[r] | |
return adder, multier | |
# TODO | |
# around 1.0 to 10.0 | |
def offenseMark(self, i): | |
mark = 0.0 | |
mark += self.getClosestEnemyDist(i) | |
mark += self.getAvgDefenceDistToPlayer(i) | |
# the closer to enemy goal the better | |
mark += 3.0 / (dist(self._ourPos[i], self._goalPos) + 0.2) | |
# but should be further to goalie | |
mark -= 0.5 / (dist(self._ourPos[i], self._keeperPos) + 0.2) | |
# offense pluser for role | |
adder, multier = self.getRoleOffenceScore(i) | |
mark *= multier | |
mark += adder | |
# ADD tireness | |
mark += 1.0 - self._our_tireness[i] * offenseTirenessFactor | |
if insideArea(self._ourPos[i], PASS_FOR_SHOT_AREA1) or insideArea(self._ourPos[i], PASS_FOR_SHOT_AREA2): | |
mark = mark * passForShotFactor | |
return mark | |
# TODO | |
# range from | |
def getPassSuccessMark(self, i, passType): | |
# you can't pass to yourself right? | |
if i == self._cur_player: | |
return -10 | |
# can't pass offside ball | |
if self.judgeOffside(i): | |
return -10 | |
mark = 0.0 | |
# calculate intercept | |
# if passType == Action.HighPass: | |
# interceptFactor = 1.0 | |
# distFactor = 1.2 | |
# takenFactor = 3.0 | |
# elif passType == Action.ShortPass: | |
# interceptFactor = 1.0 | |
# distFactor = 1.5 | |
# takenFactor = 1.5 | |
# else: | |
# interceptFactor = 1.2 | |
# distFactor = 1.2 | |
# takenFactor = 1.5 | |
interceptFactor = passFactors[passType][0] | |
distFactor = passFactors[passType][1] | |
takenFactor = passFactors[passType][2] | |
l = Line(self._curPos, self._ourPos[i]) | |
minDist = 2 | |
for pos in self._enemyPos: | |
minDist = min(minDist, l.distToLine(pos)) | |
mark += (minDist * interceptFactor) | |
# calculate taken | |
taken = self.getClosestEnemyDist(i) + takenSelfFactor * self.getClosestEnemyDist() | |
mark += (taken * takenFactor) | |
# calculate dist | |
mark += (l.length * distFactor) | |
return mark | |
# freeKick | |
def shotFreeKick(self): | |
if insideArea(self._curPos, FREEKICK_SHOT_AREA): | |
return True | |
return False | |
# TODO | |
def cutAngleWithClosest(self): | |
x = self._keeperPos[0] / 2 + self._goalPos[0] / 2 - self._curPos[0] | |
y = self._keeperPos[1] / 2 + self._goalPos[1] / 2 - self._curPos[1] | |
x += self._closest_enemey_to_cur_dir[0] * (0.05 / (self._cloest_enemey_dist + 0.03)) | |
y += self._closest_enemey_to_cur_dir[1] * (0.05 / (self._cloest_enemey_dist + 0.03)) | |
return gotoDir(x, y) | |
def process(self, obs): | |
self._obs = obs | |
self.preprocess() | |
# TODO | |
# of course you can only shot in penalty | |
if self._game_mode == GameMode.Penalty: | |
return Action.Shot | |
if self._game_mode == GameMode.Corner: | |
if self._pass_dir_ready: | |
return self._pass_type | |
bestPassMark, bestPassType, bestPassIndex = self.getBestPass() | |
self._pass_dir_ready = True | |
self._pass_type = bestPassType | |
return self.gotoDst(self._ourPos[bestPassIndex][0], self._ourPos[bestPassIndex][1]) | |
if self._game_mode == GameMode.FreeKick: | |
if self.shotFreeKick(): | |
return Action.Shot | |
else: | |
if self._pass_dir_ready: | |
return self._pass_type | |
bestPassMark, bestPassType, bestPassIndex = self.getBestPass() | |
self._pass_dir_ready = True | |
self._pass_type = bestPassType | |
return self.gotoDst(self._ourPos[bestPassIndex][0], self._ourPos[bestPassIndex][1]) | |
if self._game_mode == GameMode.KickOff: | |
return Action.ShortPass | |
if self._game_mode == GameMode.ThrowIn: | |
if self._pass_dir_ready: | |
return self._pass_type | |
bestPassMark, bestPassType, bestPassIndex = self.getBestPass() | |
self._pass_dir_ready = True | |
self._pass_type = bestPassType | |
return self.gotoDst(self._ourPos[bestPassIndex][0], self._ourPos[bestPassIndex][1]) | |
if self._our_active_have_ball and not self._our_goalkeeper_have_ball: | |
if self._shot_dir_ready and self._cur_player == self._shot_buf_player and self._remain_step == self._shot_buf_step - 1: | |
self._shot_dir_ready = False | |
self._shot_buf_player = -1 | |
self._shot_buf_step = -1 | |
return Action.Shot | |
if self.shotWill(): | |
self._shot_buf_player = self._cur_player | |
self._shot_buf_step = self._remain_step | |
self._shot_dir_ready = True | |
# TODO | |
# improve shot direction | |
return self.gobetweenKeeperGate() | |
if self._pass_dir_ready and self._cur_player == self._pass_buf_player and self._remain_step == self._pass_buf_step - 1: | |
self._pass_dir_ready = False | |
self._pass_buf_player = -1 | |
self._pass_buf_step = -1 | |
return self._pass_type | |
# elif self.passAway() and self._curDir[0] > 0.0: | |
# return Action.HighPass | |
# elif self.shortPassForShot(): | |
# return Action.ShortPass | |
else: | |
self._shot_dir_ready = False | |
self._pass_dir_ready = False | |
doPass, doPassType, doPassIndex = self.passWill() | |
if doPass: | |
self._pass_dir_ready = True | |
self._pass_type = doPassType | |
self._pass_buf_step = self._remain_step | |
self._pass_buf_player = self._cur_player | |
return self.gotoDst(self._ourPos[doPassIndex][0], self._ourPos[doPassIndex][1]) | |
# ADD avoid opponent | |
if self._closest_enemey_to_cur_vec[0] > 0: | |
# closest enemy behind me and left | |
if not self._sprinting and self.should_sprint(): | |
return Action.Sprint | |
if self._dribbling and dist(self._curPos, self._closest_enemey_pos) > 0.02: | |
return Action.ReleaseDribble | |
return self.gobetweenKeeperGate() | |
elif dist(self._curPos, self._closest_enemey_pos) < 0.02: | |
# enemy too close, start dribble | |
# if not self._dribbling: | |
# return Action.Dribble | |
# enemy infront of me, try to cut an angle | |
return self.cutAngleWithClosest() | |
else: | |
# no enemy near me | |
if self._dribbling: | |
return Action.ReleaseDribble | |
if not self._sprinting: | |
return Action.Sprint | |
# ADD release sprint | |
# if self._sprinting and not self.should_sprint(): | |
# return Action.ReleaseSprintt | |
# elif not insideArea(curPos, SPRINT_AREA) and Action.Sprint in obs['sticky_actions']: | |
# return Action.ReleaseSprint | |
return self.gobetweenKeeperGate() | |
elif self._we_have_ball and not self._our_goalkeeper_have_ball and not self._our_active_have_ball: | |
self._shot_dir_ready = False | |
return self.gotoDst(self._goalPos[0], self._goalPos[1]) | |
elif self._our_goalkeeper_have_ball: | |
self._shot_dir_ready = False | |
if self._our_goalkeeper_active: | |
return Action.HighPass | |
if self._sprinting: | |
return Action.ReleaseSprint | |
return self.gobetweenKeeperGate() | |
self._shot_dir_ready = False | |
# ball in enemy or ball free | |
if self._dribbling: | |
return Action.ReleaseDribble | |
if self._ball_is_free: | |
if not self._sprinting and self.should_sprint(): | |
return Action.Sprint | |
return self.gotoDst(self._ballPos[0] + 2 * self._ball_dir[0], self._ballPos[1] + 2 * self._ball_dir[1]) | |
if self._enemy_have_ball: | |
# TODO | |
# defense now! | |
# if you are can't catch him and you are not the closest one to gate, just quit chasing. | |
""" | |
if not self.should_chase(): | |
if self._sprinting: | |
return Action.ReleaseSprint | |
return Action.Idle | |
if self.should_slide(): | |
return Action.Slide | |
""" | |
if not self._sprinting and self.should_sprint() and self.should_chase(): | |
return Action.Sprint | |
# intersect the ball, see https://www.kaggle.com/c/google-football/discussion/191804 | |
return self.gotoDst( | |
self._ballPos[0] + 1 * self._ball_dir[0] + 1 * self._ball_owner_dir[0], | |
self._ballPos[1] + 1 * self._ball_dir[1] + 1 * self._ball_owner_dir[1] | |
) | |
return self.gotoDst(self._goalPos[0], self._goalPos[1]) | |
processer = Processer() | |
# @human_readable_agent | |
def agent(obs): | |
global processer | |
return processer.process(obs) | |
def raw_obs_to_readable(obs): | |
# print("obs = ", obs) | |
# print("obs sticky=", obs['active_player_sticky_actions']) | |
obs['sticky_actions'] = {sticky_index_to_action[nr] for nr, action in enumerate(obs['sticky_actions']) if action} | |
# Turn 'game_mode' into an enum. | |
obs['game_mode'] = GameMode(obs['game_mode']) | |
# In case of single agent mode, 'designated' is always equal to 'active'. | |
if 'designated' in obs: | |
del obs['designated'] | |
# Conver players' roles to enum. | |
obs['left_team_roles'] = [PlayerRole(role) for role in obs['left_team_roles']] | |
obs['right_team_roles'] = [PlayerRole(role) for role in obs['right_team_roles']] | |
return obs | |
def rule_agent(obs): | |
# obs = obs[0] | |
obs = raw_obs_to_readable(obs) | |
return agent(obs).value | |
def idel_agent(obs): | |
return 0 | |
def random_agent(obs): | |
return random.randint(0, 18) | |
agents_map = {"random": random_agent, "rule": rule_agent, "idel": idel_agent} | |
class FootballRuleBaseModel(torch.nn.Module): | |
def __init__(self, cfg={}): | |
super(FootballRuleBaseModel, self).__init__() | |
self.agent_type = cfg.get('agent_type', 'rule') | |
self._agent = agents_map[self.agent_type] | |
# be compatiable with bc policy | |
# to avoid: ValueError: optimizer got an empty parameter list | |
self._dummy_param = nn.Parameter(torch.zeros(1, 1)) | |
def forward(self, data): | |
actions = [] | |
data = data['raw_obs'] | |
if isinstance(data['score'], list): | |
# to be compatiable with collect phase in subprocess mode | |
data['score'] = torch.stack(data['score'], dim=-1) | |
# dict of raw observations -> list of dict, each element in the list is the raw obs in one timestep | |
data = [{k: v[i] for k, v in data.items()} for i in range(data['left_team'].shape[0])] | |
for d in data: | |
# the rew obs in one timestep | |
if isinstance(d['steps_left'], torch.Tensor): | |
d = {k: v.cpu() for k, v in d.items()} | |
d = to_ndarray(d) | |
for k in ['active', 'designated', 'ball_owned_player', 'ball_owned_team']: | |
d[k] = int(d[k]) | |
actions.append(self._agent(d)) | |
return {'action': torch.LongTensor(actions), 'logit': one_hot(torch.LongTensor(actions), 19)} | |