Commit 439cc197 authored by drallensmith's avatar drallensmith

Exploration of offense actions

parent 687ff761
#!/usr/bin/env python
"""
This is a script to explore what actions are available under what circumstances, plus testing out feedback.
"""
from __future__ import print_function
# encoding: utf-8
import argparse
#import functools
import itertools
import math
import os
import random
import struct
import subprocess
import sys
import time
import warnings
try:
import hfo
except ImportError:
print('Failed to import hfo. To install hfo, in the HFO directory'\
' run: \"pip install .\"')
exit()
HALF_FIELD_WIDTH = 68 # y coordinate
HALF_FIELD_FULL_WIDTH = HALF_FIELD_WIDTH * 1.2
HALF_FIELD_LENGTH = 52.5 # x coordinate
HALF_FIELD_FULL_LENGTH = HALF_FIELD_LENGTH * 1.2
GOAL_WIDTH = 14.02
MAX_RADIUS = math.sqrt((HALF_FIELD_WIDTH**2) + ((HALF_FIELD_LENGTH/2)**2)) # latter is incorrect for _actual_ maximum distance...
ERROR_TOLERANCE = math.pow(sys.float_info.epsilon,0.25)
POS_ERROR_TOLERANCE = 0.05
MAX_REAL_X_VALID = 1.1*HALF_FIELD_LENGTH
MIN_REAL_X_VALID = -0.1*HALF_FIELD_LENGTH
MAX_REAL_Y_VALID = 1.1*(HALF_FIELD_WIDTH/2)
MIN_REAL_Y_VALID = -1.1*(HALF_FIELD_WIDTH/2)
def unnormalize(pos, min_val, max_val, silent=False):
assert max_val > min_val
if (not silent) and (abs(pos) > 1.0+ERROR_TOLERANCE):
print("Pos {0:n} fed to unnormalize (min_val {1:n}, max_val {2:n})".format(
pos, min_val, max_val), file=sys.stderr)
sys.stderr.flush()
pos = min(1.0,max(-1.0,pos))
top = (pos - -1.0)/(1 - -1.0)
bot = max_val - min_val
return (top*bot) + min_val
def get_y_unnormalized(y_pos, silent=False):
y_pos_real = unnormalize(y_pos, MIN_REAL_Y_VALID, MAX_REAL_Y_VALID, silent=silent)
est_y_pos = get_y_normalized(y_pos_real, silent=silent)
if abs(y_pos - est_y_pos) > POS_ERROR_TOLERANCE:
raise RuntimeError(
"Bad denormalization/normalization of {0:n} to {1:n}; reverse {2:n}".format(
y_pos, y_pos_real, est_y_pos))
return y_pos_real
def get_x_unnormalized(x_pos, silent=False):
x_pos_real = unnormalize(x_pos, MIN_REAL_X_VALID, MAX_REAL_X_VALID, silent=silent)
est_x_pos = get_x_normalized(x_pos_real, silent=silent)
if abs(x_pos - est_x_pos) > POS_ERROR_TOLERANCE:
raise RuntimeError(
"Bad denormalization/normalization of {0:n} to {1:n}; reverse {2:n}".format(
x_pos, x_pos_real, est_x_pos))
return x_pos_real
def normalize(pos, min_val, max_val, silent=False):
assert max_val > min_val
top = (pos - min_val)/(max_val - min_val)
bot = 1.0 - -1.0
num = (top*bot) + -1.0
if abs(num) > 1.0+POS_ERROR_TOLERANCE:
if abs(num) > 1.1:
raise RuntimeError("Pos {0:n} gives normalized num {1:n} (min_val {2:n}, max_val {3:n})".format(
pos, num, min_val, max_val))
elif not silent:
print(
"Pos {0:n} gives normalized num {1:n} (min_val {2:n}, max_val {3:n})".format(
pos, num, min_val, max_val), file=sys.stderr)
sys.stderr.flush()
return min(1.0,max(-1.0,num))
def get_y_normalized(y_pos, silent=False):
y_pos_norm = normalize(y_pos, MIN_REAL_Y_VALID, MAX_REAL_Y_VALID, silent=silent)
## est_y_pos = get_y_unnormalized(y_pos_norm, silent=silent)
## if abs(y_pos - est_y_pos) > POS_ERROR_TOLERANCE:
## raise RuntimeError("Bad normalization/denormalization of {0:n} to {1:n}; reverse {2:n}".format(
## y_pos, y_pos_norm, est_y_pos))
return y_pos_norm
def get_x_normalized(x_pos, silent=False):
return normalize(x_pos, MIN_REAL_X_VALID, MAX_REAL_X_VALID, silent=silent)
GOAL_POS_X = get_x_normalized(HALF_FIELD_LENGTH)
GOAL_TOP_POS_Y = get_y_normalized(-1*(GOAL_WIDTH/2))
GOAL_BOTTOM_POS_Y = get_y_normalized(GOAL_WIDTH/2)
MAX_POS_Y_BALL_SAFE = get_y_normalized(HALF_FIELD_WIDTH/2) - POS_ERROR_TOLERANCE
MIN_POS_Y_BALL_SAFE = get_y_normalized(-0.5*HALF_FIELD_WIDTH) + POS_ERROR_TOLERANCE
MAX_POS_X_BALL_SAFE = get_x_normalized(HALF_FIELD_LENGTH) - POS_ERROR_TOLERANCE
MIN_POS_X_BALL_SAFE = get_x_normalized(0) + POS_ERROR_TOLERANCE
MAX_POS_X_OK = 1.0 - ERROR_TOLERANCE
MIN_POS_X_OK = -1.0 + ERROR_TOLERANCE
MAX_POS_Y_OK = MAX_POS_X_OK
MIN_POS_Y_OK = MIN_POS_X_OK
def get_dist_real(ref_x, ref_y, src_x, src_y, silent=False):
ref_x_real = get_x_unnormalized(ref_x, silent=silent)
ref_y_real = get_y_unnormalized(ref_y, silent=silent)
src_x_real = get_x_unnormalized(src_x, silent=silent)
src_y_real = get_y_unnormalized(src_y, silent=silent)
return math.sqrt(math.pow((ref_x_real - src_x_real),2) +
math.pow((ref_y_real - src_y_real),2))
def get_dist_from_real(ref_x_real, ref_y_real, src_x_real, src_y_real):
return math.sqrt(math.pow((ref_x_real - src_x_real),2) +
math.pow((ref_y_real - src_y_real),2))
def get_dist_normalized(ref_x, ref_y, src_x, src_y):
return math.sqrt(math.pow((ref_x - src_x),2) +
math.pow(((HALF_FIELD_WIDTH/HALF_FIELD_LENGTH)*(ref_y - src_y)),2))
#Instead of adding six as a dependency, this code was copied from the six implementation.
#six is Copyright (c) 2010-2015 Benjamin Peterson
if sys.version_info[0] >= 3:
def iterkeys(d, **kw):
return iter(d.keys(**kw))
def iteritems(d, **kw):
return iter(d.items(**kw))
def itervalues(d, **kw):
return iter(d.values(**kw))
else:
def iterkeys(d, **kw):
return iter(d.iterkeys(**kw))
def iteritems(d, **kw):
return iter(d.iteritems(**kw))
def itervalues(d, **kw):
return iter(d.itervalues(**kw))
# Later expansion: INTENT_PLAYER_COLLISION (put a do-nothing player on the field)
INTENT_BALL_COLLISION = 0
INTENT_BALL_KICKABLE = 1
INTENT_GOAL_COLLISION = 2
INTENT_DICT = {INTENT_BALL_COLLISION: 'INTENT_BALL_COLLISION',
INTENT_BALL_KICKABLE: 'INTENT_BALL_KICKABLE',
INTENT_GOAL_COLLISION: 'INTENT_GOAL_COLLISION'}
BIT_LIST_LEN = 7
STRUCT_PACK = "BB"
def get_dist_from_proximity(proximity, max_dist=MAX_RADIUS):
proximity_real = unnormalize(proximity, 0.0, 1.0)
dist = (1 - proximity_real)*max_dist
if (dist > (max_dist+ERROR_TOLERANCE)) or (dist <= (-1.0*ERROR_TOLERANCE)):
warnings.warn("Proximity {0:n} gives dist {1:n} (max_dist {2:n})".format(proximity,dist,max_dist))
return min(max_dist,max(0,dist))
def get_proximity_from_dist(dist, max_dist=MAX_RADIUS):
if dist > (max_dist+ERROR_TOLERANCE):
print("Dist {0:n} is above max_dist {1:n}".format(dist, max_dist), file=sys.stderr)
sys.stderr.flush()
proximity_real = min(1.0, max(0.0, (1.0 - (dist/max_dist))))
return normalize(proximity_real, 0.0, 1.0)
# MAX_GOAL_DIST - may wish to work out...
def get_angle(sin_angle,cos_angle):
if max(abs(sin_angle),abs(cos_angle)) <= sys.float_info.epsilon:
warnings.warn(
"Unable to accurately determine angle from sin_angle {0:n} cos_angle {1:n}".format(
sin_angle, cos_angle))
return None
angle = math.degrees(math.atan2(sin_angle,cos_angle))
if angle < 0:
angle += 360
return angle
def get_abs_angle(body_angle,rel_angle):
if (body_angle is None) or (rel_angle is None):
return None
angle = body_angle + rel_angle
while angle >= 360:
angle -= 360
if angle < 0:
if abs(angle) <= ERROR_TOLERANCE:
return 0.0
warnings.warn("Bad body_angle {0:n} and/or rel_angle {1:n}".format(body_angle,rel_angle))
return None
return angle
def get_angle_diff(angle1, angle2):
if angle1 > angle2:
return min((angle1 - angle2),abs(angle1 - angle2 - 360))
elif angle1 < angle2:
return min((angle2 - angle1),abs(angle2 - angle1 - 360))
return 0.0
def reverse_angle(angle):
angle += 180
while angle >= 360:
angle -= 360
return angle
def get_abs_x_y_pos(abs_angle, dist, self_x_pos, self_y_pos, warn=True, of_what=""):
poss_xy_pos_real = {}
max_deviation_xy_pos_real = {}
total_deviation_xy_pos_real = {}
dist_xy_pos_real = {}
self_x_pos_real = get_x_unnormalized(self_x_pos)
self_y_pos_real = get_y_unnormalized(self_y_pos)
start_string = ""
if of_what:
start_string = of_what + ': '
for angle in [(abs_angle-1),(abs_angle+1),abs_angle]:
angle_radians = math.radians(angle)
sin_angle = math.sin(angle_radians)
cos_angle = math.cos(angle_radians)
est_x_pos_real = (cos_angle*dist) + self_x_pos_real
est_y_pos_real = (sin_angle*dist) + self_y_pos_real
if ((MIN_REAL_X_VALID*(1-POS_ERROR_TOLERANCE))
<= est_x_pos_real <=
(MAX_REAL_X_VALID*(1+POS_ERROR_TOLERANCE))) and ((MIN_REAL_Y_VALID*(1-POS_ERROR_TOLERANCE))
<= est_y_pos_real <=
(MAX_REAL_Y_VALID*(1+POS_ERROR_TOLERANCE))):
poss_xy_pos_real[angle] = (est_x_pos_real, est_y_pos_real)
if est_x_pos_real < MIN_REAL_X_VALID:
x_deviation = (MIN_REAL_X_VALID-est_x_pos_real)/(MAX_REAL_X_VALID-MIN_REAL_X_VALID)
elif est_x_pos_real > MAX_REAL_X_VALID:
x_deviation = (est_x_pos_real-MAX_REAL_X_VALID)/(MAX_REAL_X_VALID-MIN_REAL_X_VALID)
else:
x_deviation = 0.0
if est_y_pos_real < MIN_REAL_Y_VALID:
y_deviation = (MIN_REAL_Y_VALID-est_y_pos_real)/(MAX_REAL_Y_VALID-MIN_REAL_Y_VALID)
elif est_y_pos_real > MAX_REAL_Y_VALID:
y_deviation = (est_y_pos_real-MAX_REAL_Y_VALID)/(MAX_REAL_Y_VALID-MIN_REAL_Y_VALID)
else:
y_deviation = 0.0
max_deviation_xy_pos_real[angle] = max(x_deviation,y_deviation)
total_deviation_xy_pos_real[angle] = x_deviation+y_deviation
dist_xy_pos_real[angle] = get_dist_from_real(est_x_pos_real,
est_y_pos_real,
min(MAX_REAL_X_VALID,
max(MIN_REAL_X_VALID,
est_x_pos_real)),
min(MAX_REAL_Y_VALID,
max(MIN_REAL_Y_VALID,
est_y_pos_real)))
elif (angle == abs_angle) and (not poss_xy_pos_real):
error_strings = []
if not ((MIN_REAL_X_VALID*(1-POS_ERROR_TOLERANCE))
<= est_x_pos_real <=
(MAX_REAL_X_VALID*(1+POS_ERROR_TOLERANCE))):
error_strings.append(
"{0!s}Bad est_x_pos_real {1:n} from self_x_pos_real {2:n} (self_x_pos {3:n}), angle {4:n} ({5:n} {6:n}), dist {7:n}".format(
start_string,est_x_pos_real, self_x_pos_real, self_x_pos, abs_angle, sin_angle, cos_angle, dist))
if not ((MIN_REAL_Y_VALID*(1-POS_ERROR_TOLERANCE))
<= est_y_pos_real <=
(MAX_REAL_Y_VALID*(1+POS_ERROR_TOLERANCE))):
error_strings.append(
"{0!s}Bad est_y_pos_real {1:n} from self_y_pos_real {2:n} (self_y_pos {3:n}), angle {4:n} ({5:n} {6:n}), dist {7:n}".format(
start_string, est_y_pos_real, self_y_pos_real, self_y_pos, abs_angle, sin_angle, cos_angle, dist))
if dist < 10:
raise RuntimeError("\n".join(error_strings))
else:
if warn or (dist < 60):
print("\n".join(error_strings), file=sys.stderr)
sys.stderr.flush()
return (None, None)
poss_angles = list(poss_xy_pos_real.keys())
if len(poss_angles) > 1:
poss_angles.sort(key=lambda angle: abs(abs_angle-angle))
poss_angles.sort(key=lambda angle: total_deviation_xy_pos_real[angle])
poss_angles.sort(key=lambda angle: max_deviation_xy_pos_real[angle])
poss_angles.sort(key=lambda angle: dist_xy_pos_real[angle])
est_x_pos_real, est_y_pos_real = poss_xy_pos_real[poss_angles[0]]
if warn:
est_x_pos = get_x_normalized(est_x_pos_real)
est_y_pos = get_y_normalized(est_y_pos_real)
else:
est_x_pos = get_x_normalized(est_x_pos_real, silent=True)
est_y_pos = get_y_normalized(est_y_pos_real, silent=True)
if warn:
est_dist = get_dist_real(self_x_pos, self_y_pos,
est_x_pos, est_y_pos)
if abs(dist - est_dist) > ERROR_TOLERANCE:
est2_dist = get_dist_from_real(self_x_pos_real, self_y_pos_real,
est_x_pos_real, est_y_pos_real)
print(
"{0!s}Difference in input ({1:n}), output ({2:n}) distances (est2 {3:n}) for get_abs_x_y_pos".format(
start_string, dist, est_dist, est2_dist),
file=sys.stderr)
print(
"Input angle {0:n}, used angle {1:n}, self_x_pos {2:n}, self_y_pos {3:n}".format(
abs_angle, poss_angles[0], self_x_pos, self_y_pos),
file=sys.stderr)
print(
"Self_x_pos_real {0:n}, self_y_pos_real {1:n}, est_x_pos_real {2:n}, est_y_pos_real {3:n}".format(
self_x_pos_real, self_y_pos_real, est_x_pos_real, est_y_pos_real), file=sys.stderr)
print("Est_x_pos {0:n}, est_y_pos {1:n}".format(est_x_pos, est_y_pos), file=sys.stderr)
sys.stderr.flush()
return (est_x_pos, est_y_pos)
landmark_start_to_location = {13: (GOAL_POS_X, get_y_normalized(0.0)), # center of goal
#31: (get_x_normalized(HALF_FIELD_LENGTH/2), 0.0), # center field
34: (get_x_normalized(0.0), get_y_normalized(-0.5*HALF_FIELD_WIDTH)), # Top Left
37: (get_x_normalized(HALF_FIELD_LENGTH), get_y_normalized(-0.5*HALF_FIELD_WIDTH)), # Top Right
40: (get_x_normalized(HALF_FIELD_LENGTH), get_y_normalized(HALF_FIELD_WIDTH/2)), # Bottom Right
43: (get_x_normalized(0,0), get_y_normalized(HALF_FIELD_WIDTH/2))} # Bottom Left
def filter_low_level_state(state):
bit_list = []
for pos in (0, 1, 9, 11, 12, 50, 54):
if state[pos] > 0:
bit_list.append(True)
else:
bit_list.append(False)
if len(bit_list) != BIT_LIST_LEN:
raise RuntimeError(
"Length of bit_list {0:n} not same as BIT_LIST_LEN {1:n}".format(
len(bit_list), BIT_LIST_LEN))
self_dict = {}
if state[0] > 0: # self position valid
if max(abs(state[5]),abs(state[6])) <= sys.float_info.epsilon:
warnings.warn("Validity {0:d}{1:d} but invalid body angle {2:n}, {3:n}".format(
int(bit_list[0]),int(bit_list[1]),state[5],state[6]))
self_dict['body_angle'] = None
else:
self_dict['body_angle'] = get_angle(state[5],state[6])
x_pos_from = {}
x_pos_weight = {}
y_pos_from = {}
y_pos_weight = {}
x_pos1_real = get_dist_from_proximity(state[46],HALF_FIELD_LENGTH)
x_pos2_real = HALF_FIELD_LENGTH - get_dist_from_proximity(state[47],HALF_FIELD_LENGTH)
x_pos_from['OOB'] = get_x_normalized((x_pos1_real+x_pos2_real)/2)
x_pos_weight['OOB'] = max(ERROR_TOLERANCE,(1.0-min(1.0,abs(x_pos_from['OOB']))))
y_pos1_real = get_dist_from_proximity(state[48],HALF_FIELD_WIDTH) - (HALF_FIELD_WIDTH/2)
y_pos2_real = (HALF_FIELD_WIDTH/2) - get_dist_from_proximity(state[49],HALF_FIELD_WIDTH)
y_pos_from['OOB'] = get_y_normalized((y_pos1_real+y_pos2_real)/2)
y_pos_weight['OOB'] = max(ERROR_TOLERANCE,(1.0-min(1.0,abs(y_pos_from['OOB']))))
if self_dict['body_angle'] is not None:
for landmark_start, xy_location in iteritems(landmark_start_to_location):
rel_angle = get_angle(state[landmark_start],state[landmark_start+1])
abs_angle = get_abs_angle(self_dict['body_angle'],rel_angle)
if abs_angle is not None:
rev_angle = reverse_angle(abs_angle)
dist = get_dist_from_proximity(state[landmark_start+2])
x_pos, y_pos = get_abs_x_y_pos(abs_angle=rev_angle,
dist=dist,
self_x_pos=xy_location[0],
self_y_pos=xy_location[1],
warn=False,
of_what="Rev " + str(landmark_start))
if x_pos is not None:
x_pos_from[landmark_start] = x_pos
y_pos_from[landmark_start] = y_pos
x_pos_weight[landmark_start] = y_pos_weight[landmark_start] = max(ERROR_TOLERANCE,
((state[landmark_start+2]+1)/2))
# except extremely close can be a problem for the angle...
if state[landmark_start+2] > (1.0-POS_ERROR_TOLERANCE):
max_weight = (1.0-state[landmark_start+2])/POS_ERROR_TOLERANCE
max_weight = max(max_weight, ERROR_TOLERANCE)
x_pos_weight[landmark_start] = y_pos_weight[landmark_start] = min(x_pos_weight[landmark_start],
max_weight)
x_pos_total = 0.0
x_pos_weight_total = 0.0
for from_which, pos in iteritems(x_pos_from):
x_pos_total += pos*x_pos_weight[from_which]
x_pos_weight_total += x_pos_weight[from_which]
self_dict['x_pos'] = x_pos_total/x_pos_weight_total
for from_which, pos in iteritems(x_pos_from):
if ((x_pos_weight[from_which]/x_pos_weight_total)*abs(pos-self_dict['x_pos'])) > POS_ERROR_TOLERANCE:
print("Source {0!r} (weight {1:n}) x_pos {2:n} vs overall {3:n}".format(from_which,
x_pos_weight[from_which]/
x_pos_weight_total,
pos,
self_dict['x_pos']),
file=sys.stderr)
sys.stderr.flush()
y_pos_total = 0.0
y_pos_weight_total = 0.0
for from_which, pos in iteritems(y_pos_from):
y_pos_total += pos*y_pos_weight[from_which]
y_pos_weight_total += y_pos_weight[from_which]
self_dict['y_pos'] = y_pos_total/y_pos_weight_total
for from_which, pos in iteritems(y_pos_from):
if ((y_pos_weight[from_which]/y_pos_weight_total)*abs(pos-self_dict['y_pos'])) > POS_ERROR_TOLERANCE:
print("Source {0!r} (weight {1:n}) y_pos {2:n} vs overall {3:n}".format(from_which,
y_pos_weight[from_which]/
y_pos_weight_total,
pos,
self_dict['y_pos']),
file=sys.stderr)
sys.stderr.flush()
if max(abs(state[31]),abs(state[32])) <= sys.float_info.epsilon:
warnings.warn("Validity {0:d}{1:d} but invalid center angle {2:n}, {3:n}".format(
int(bit_list[0]),int(bit_list[1]),state[31],state[32]))
self_dict['center_angle'] = None
else:
self_dict['center_angle'] = get_angle(state[31],state[32])
else:
self_dict['x_pos'] = None
self_dict['y_pos'] = None
if max(abs(state[5]),abs(state[6])) > sys.float_info.epsilon:
warnings.warn("Validity {0:d}{1:d} but valid body angle {2:n}, {3:n}".format(
int(bit_list[0]),int(bit_list[1]),state[5],state[6]))
self_dict['body_angle'] = get_angle(state[5],state[6])
else:
self_dict['body_angle'] = None
if max(abs(state[31]),abs(state[32])) > sys.float_info.epsilon:
warnings.warn("Validity {0:d}{1:d} but valid center angle {2:n}, {3:n}".format(
int(bit_list[0]),int(bit_list[1]),state[31],state[32]))
self_dict['center_angle'] = get_angle(state[31],state[32])
else:
self_dict['center_angle'] = None
if state[1] > 0: # self velocity valid
self_dict['vel_rel_angle'] = get_angle(state[2],state[3])
self_dict['vel_mag'] = state[4]
else:
self_dict['vel_rel_angle'] = None
self_dict['vel_mag'] = -1
goal_dict = {}
if (state[0] > 0) and (max(state[18],state[21]) > -1): # self position valid, goal pos possible
if state[18] > state[21]: # top post closer
which_goal = "Top"
goal_dict['dist'] = get_dist_from_proximity(state[18])
goal_dict['rel_angle'] = get_angle(state[16],state[17])
else:
which_goal = "Bottom"
goal_dict['dist'] = get_dist_from_proximity(state[21])
goal_dict['rel_angle'] = get_angle(state[19],state[20])
if (state[11] < 0) and (goal_dict['rel_angle']
is not None) and (goal_dict['dist']
< (0.15+0.03)) and (get_angle_diff(0.0,
goal_dict['rel_angle'])
<= 1.0):
raise RuntimeError("Should be in collision with goal - distance is {0:n}".format(goal_dict['dist']))
elif goal_dict['dist'] > (MAX_RADIUS-POS_ERROR_TOLERANCE):
raise RuntimeError(
"Should not be possible to have dist {0:n} for goal (state[18] {1:n}, state[21] {2:n})".format(
goal_dict['dist'], state[18], state[21]))
goal_dict['abs_angle'] = get_abs_angle(self_dict['body_angle'],goal_dict['rel_angle'])
if goal_dict['abs_angle'] is not None:
est_x_pos, est_y_pos = get_abs_x_y_pos(abs_angle=goal_dict['abs_angle'],
dist=goal_dict['dist'],
self_x_pos=self_dict['x_pos'],
self_y_pos=self_dict['y_pos'],
warn=bool(goal_dict['dist'] < 10),
of_what=which_goal+" goal post")
if est_x_pos is not None:
if (abs(est_x_pos - GOAL_POS_X) > POS_ERROR_TOLERANCE) and (goal_dict['dist'] < 10):
print(
"Estimated x_pos of goal is {0:n} (should be {1:n}; from est abs_angle {2:n}, dist {3:n}, self_x_pos {4:n})".format(
est_x_pos,GOAL_POS_X,goal_dict['abs_angle'],goal_dict['dist'],self_dict['x_pos']),
file=sys.stderr)
sys.stderr.flush()
goal_dict['x_pos'] = GOAL_POS_X
if (state[18] > state[21]):
goal_dict['y_pos'] = GOAL_TOP_POS_Y
if est_y_pos is not None:
if (abs(est_y_pos-GOAL_TOP_POS_Y) > POS_ERROR_TOLERANCE) and (goal_dict['dist'] < 10):
print(
"Estimated y_pos of top goalpost is {0:n} (should be {1:n}; from est abs_angle {2:n}, dist {3:n}, self_y_pos {4:n})".format(
est_y_pos,GOAL_TOP_POS_Y,goal_dict['abs_angle'],goal_dict['dist'],self_dict['y_pos']),
file=sys.stderr)
sys.stderr.flush()
else:
goal_dict['y_pos'] = GOAL_BOTTOM_POS_Y
if est_y_pos is not None:
if (abs(est_y_pos-GOAL_BOTTOM_POS_Y) > POS_ERROR_TOLERANCE) and (goal_dict['dist'] < 10):
print(
"Estimated y_pos of bottom goalpost is {0:n} (should be {1:n}; from est abs_angle {2:n}, dist {3:n}, self_y_pos {4:n})".format(
est_y_pos,GOAL_BOTTOM_POS_Y,goal_dict['abs_angle'],goal_dict['dist'],self_dict['y_pos']),
file=sys.stderr)
sys.stderr.flush()
if (state[11] < 0) and (get_angle_diff(0.0, goal_dict['rel_angle'])
<= 1.0) and (get_dist_real(self_dict['x_pos'],
self_dict['y_pos'],
goal_dict['x_pos'],
goal_dict['y_pos']) < (0.15+0.03)):
print(
"Should be in collision with goal ({0:n}, {1:n} vs {2:n}, {3:n}; angle {4:n})".format(
self_dict['x_pos'],self_dict['y_pos'],
goal_dict['x_pos'],goal_dict['y_pos'],
goal_dict['rel_angle']),
file=sys.stderr)
sys.stderr.flush()
else:
goal_dict['x_pos'] = GOAL_POS_X
if state[18] > state[21]:
goal_dict['y_pos'] = GOAL_TOP_POS_Y
else:
goal_dict['y_pos'] = GOAL_BOTTOM_POS_Y
else:
goal_dict['dist'] = get_dist_from_proximity(-1)
goal_dict['rel_angle'] = None
goal_dict['abs_angle'] = None
goal_dict['x_pos'] = GOAL_POS_X
if random.random() < 0.5:
goal_dict['y_pos'] = GOAL_BOTTOM_POS_Y
else:
goal_dict['y_pos'] = GOAL_TOP_POS_Y
ball_dict = {}
if state[50] > 0: # ball position valid
ball_dict['dist'] = get_dist_from_proximity(state[53])
ball_dict['rel_angle'] = get_angle(state[51],state[52])
if (state[9] < 0) and (ball_dict['rel_angle']
is not None) and (ball_dict['dist']
< (0.15+0.0425)) and (get_angle_diff(0.0, ball_dict['rel_angle'])
<= 1.0):
raise RuntimeError("Should be in collision with ball - distance is {0:n}".format(ball_dict['dist']))
ball_dict['abs_angle'] = get_abs_angle(self_dict['body_angle'],ball_dict['rel_angle'])
if ball_dict['abs_angle'] is not None:
ball_dict['x_pos'], ball_dict['y_pos'] = get_abs_x_y_pos(abs_angle=ball_dict['abs_angle'],
dist=ball_dict['dist'],
self_x_pos=self_dict['x_pos'],
self_y_pos=self_dict['y_pos'],
warn=bool(ball_dict['dist'] < 10),
of_what='Ball')
if (ball_dict['x_pos'] is None) or (ball_dict['y_pos'] is None):
if (ball_dict['dist'] < 10):
raise RuntimeError("Unknown ball position despite state[50] > 0")
else:
if (state[9] < 0) and (get_angle_diff(0.0, ball_dict['rel_angle'])
<= 1.0) and (get_dist_real(self_dict['x_pos'],self_dict['y_pos'],
ball_dict['x_pos'],ball_dict['y_pos']) < (0.15+0.0425)):
raise RuntimeError(
"Should be in collision with ball ({0:n}, {1:n} vs {2:n}, {3:n}; rel_angle {4:n})".format(
self_dict['x_pos'],self_dict['y_pos'],
ball_dict['x_pos'],ball_dict['y_pos'],
ball_dict['rel_angle']))
else:
ball_dict['dist'] = get_dist_from_proximity(-1)
ball_dict['rel_angle'] = None
ball_dict['abs_angle'] = None
ball_dict['x_pos'] = None
ball_dict['y_pos'] = None
if state[54] > 0: # ball velocity valid
ball_dict['vel_rel_angle'] = get_angle(state[55],state[56])
ball_dict['vel_mag'] = state[57]
else:
ball_dict['vel_rel_angle'] = None
ball_dict['vel_mag'] = 0
return (bit_list, self_dict, goal_dict, ball_dict)
def bool_list_to_int(bit_list):
return sum(2**i*b for i, b in enumerate(bit_list))
def int_to_bool_list(intval, num_bits=BIT_LIST_LEN):
if num_bits < intval.bit_length():
raise RuntimeError(
"Integer value {0:d} will not fit into {1:d} bits (needs {2:d})".format(intval,
num_bits,
intval.bit_length()))
result = []
for bit in range(num_bits):
mask = 1 << bit
result.append(bool((intval & mask) == mask))
return result
def pack_action_bit_list(action, bit_list):
bool_int = bool_list_to_int(bit_list)
return struct.pack(STRUCT_PACK, action, bool_int)
def unpack_action_bit_list(bytestring):
action, bool_int = struct.unpack(STRUCT_PACK, bytestring)
bit_list = int_to_bool_list(bool_int)
return (action, bit_list)
def evaluate_previous_action(hfo_env,
state,
namespace):
if namespace.action in (hfo.NOOP, hfo.QUIT):
warnings.warn("evaluate_previous_action should not have been called with 'action' NOOP/QUIT")
return
bit_list, self_dict, goal_dict, ball_dict = filter_low_level_state(state)
action_status = hfo_env.getLastActionStatus(namespace.action)
if action_status == hfo.ACTION_STATUS_UNKNOWN:
print(
"Last action {0!s} (prestate bit_list {1!s}, current bit list {2!s}) gave status {3:n}".format(
hfo_env.actionToString(namespace.action),
"".join(map(str,map(int,namespace.prestate_bit_list))),
"".join(map(str,map(int,bit_list))), action_status),
file=sys.stderr)
sys.stderr.flush()
return
action_string = hfo_env.actionToString(namespace.action)
if namespace.action_params:
action_string += '(' + ",".join(list(["{:n}".format(x) for x in namespace.action_params])) + ')'
action_status_observed = hfo.ACTION_STATUS_UNKNOWN # NOTE: Check intent + other bitflags!
if namespace.action == hfo.DASH:
pass # TODO
elif namespace.action == hfo.TURN:
if (namespace.prestate_self_dict['body_angle'] is not None) and (self_dict['body_angle'] is not None):
intended_body_angle = namespace.prestate_self_dict['body_angle'] + namespace.action_params[0]
if get_angle_diff(namespace.prestate_self_dict['body_angle'],
intended_body_angle) > get_angle_diff(self_dict['body_angle'],
intended_body_angle):
action_status_observed = hfo.ACTION_STATUS_MAYBE
else:
action_status_observed = hfo.ACTION_STATUS_BAD
elif namespace.action == hfo.KICK:
pass # TODO
elif namespace.action == hfo.KICK_TO:
if (namespace.prestate_ball_dict['x_pos'] is not None) and (ball_dict['x_pos'] is not None):
dist_before = get_dist_real(namespace.prestate_ball_dict['x_pos'],
namespace.prestate_ball_dict['y_pos'],
namespace.action_params[0],
namespace.action_params[1])
dist_after = get_dist_real(ball_dict['x_pos'],
ball_dict['y_pos'],
namespace.action_params[0],
namespace.action_params[1])
if dist_before > dist_after:
action_status_observed = hfo.ACTION_STATUS_MAYBE
else:
action_status_observed = hfo.ACTION_STATUS_BAD
elif namespace.action == hfo.MOVE_TO:
if (namespace.prestate_self_dict['x_pos'] is not None) and (self_dict['x_pos'] is not None):
dist_before = get_dist_real(namespace.prestate_self_dict['x_pos'],
namespace.prestate_self_dict['y_pos'],
namespace.action_params[0],
namespace.action_params[1])
dist_after = get_dist_real(self_dict['x_pos'],
self_dict['y_pos'],
namespace.action_params[0],
namespace.action_params[1])
if dist_before > dist_after:
action_status_observed = hfo.ACTION_STATUS_MAYBE
else:
action_status_observed = hfo.ACTION_STATUS_BAD
elif namespace.action == hfo.DRIBBLE_TO:
if namespace.prestate_bit_list[4]:
if not bit_list[4]:
action_status_observed = hfo.ACTION_STATUS_BAD
elif (namespace.prestate_self_dict['x_pos'] is not None) and (self_dict['x_pos'] is not None):
dist_before = get_dist_real(namespace.prestate_self_dict['x_pos'],
namespace.prestate_self_dict['y_pos'],
namespace.action_params[0],
namespace.action_params[1])
dist_after = get_dist_real(self_dict['x_pos'],
self_dict['y_pos'],
namespace.action_params[0],
namespace.action_params[1])
if dist_before > dist_after:
action_status_observed = hfo.ACTION_STATUS_MAYBE
else:
action_status_observed = hfo.ACTION_STATUS_BAD
elif bit_list[4]:
action_status_observed = hfo.ACTION_STATUS_MAYBE
elif namespace.prestate_ball_dict['dist'] > ball_dict['dist']:
action_status_observed = hfo.ACTION_STATUS_MAYBE
elif namespace.prestate_ball_dict['dist'] < ball_dict['dist']:
action_status_observed = hfo.ACTION_STATUS_BAD
elif ball_dict['dist'] < ((1-ERROR_TOLERANCE)*MAX_RADIUS):
action_status_observed = hfo.ACTION_STATUS_BAD
elif (namespace.action == hfo.INTERCEPT) or (namespace.action == hfo.GO_TO_BALL):
if namespace.prestate_bit_list[4]:
if bit_list[4]:
action_status_observed = hfo.ACTION_STATUS_MAYBE
else:
action_status_observed = hfo.ACTION_STATUS_BAD
elif bit_list[4]:
action_status_observed = hfo.ACTION_STATUS_MAYBE
elif namespace.prestate_ball_dict['dist'] > ball_dict['dist']:
action_status_observed = hfo.ACTION_STATUS_MAYBE
elif namespace.prestate_ball_dict['dist'] < ball_dict['dist']:
action_status_observed = hfo.ACTION_STATUS_BAD
elif ball_dict['dist'] < ((1-ERROR_TOLERANCE)*MAX_RADIUS):
action_status_observed = hfo.ACTION_STATUS_BAD
else:
raise RuntimeError("Unknown how to evaluate {0!s}".format(action_string))
if action_status_observed == hfo.ACTION_STATUS_UNKNOWN:
action_status_guessed = action_status
else:
action_status_guessed = action_status_observed
if action_status != action_status_observed:
print(
"{0!s}: Difference between feedback ({1!s}), observed ({2!s}) action_status (prestate bit_list {3!s}, current bit list {4!s})".format(
action_string,
hfo_env.actionStatusToString(action_status),
hfo_env.actionStatusToString(action_status_observed),
"".join(map(str,map(int,namespace.prestate_bit_list))),
"".join(map(str,map(int,bit_list)))),
file=sys.stderr)
sys.stderr.flush()
if (action_status_guessed != hfo.ACTION_STATUS_MAYBE) and (not namespace.checking_intent):
# unexpected lack of success
print(
"Unexpected lack of success for last action {0!s} (prestate bit_list {1!s}, current bit list {2!s})".format(
action_string,
"".join(map(str,map(int,namespace.prestate_bit_list))),
"".join(map(str,map(int,bit_list)))),
file=sys.stderr)
if namespace.intent == INTENT_GOAL_COLLISION:
print("Prestate goal distance is {0:n}, current goal distance is {1:n}".format(namespace.prestate_goal_dict['dist'],
goal_dict['dist']),
file=sys.stderr)
elif namespace.intent == INTENT_BALL_COLLISION:
print("Prestate ball distance is {0:n}, current ball distance is {1:n}".format(namespace.prestate_ball_dict['dist'],
ball_dict['dist']),
file=sys.stderr)
sys.stderr.flush()
namespace.action_tried[namespace.action] += 1
namespace.struct_tried[pack_action_bit_list(namespace.action,namespace.prestate_bit_list)] += 1
# further analysis...
# further analysis...
if namespace.prestate_bit_list[3] and (namespace.action in (hfo.DASH, hfo.MOVE_TO)) and (action_status_guessed == hfo.ACTION_STATUS_MAYBE):
print("OK status from {0!s} despite goal collision in prestate (poststate: {1!s})".format(
action_string, bit_list[3]))
sys.stdout.flush()
if namespace.prestate_bit_list[2] and (namespace.action in (hfo.DASH, hfo.TURN, hfo.MOVE_TO)) and (action_status_guessed == hfo.ACTION_STATUS_MAYBE):
print("OK status from {0!s} despite ball collision in prestate (poststate: {1!s})".format(
action_string, bit_list[2]))
sys.stdout.flush()
def save_action_prestate(action,
prestate_bit_list,
prestate_self_dict,
prestate_goal_dict,
prestate_ball_dict,
intent,
checking_intent,
namespace,
action_params=None):
namespace.action = action
namespace.prestate_bit_list = prestate_bit_list
namespace.prestate_self_dict = prestate_self_dict
namespace.prestate_goal_dict = prestate_goal_dict
namespace.prestate_ball_dict = prestate_ball_dict
if action_params is None:
action_params = []
namespace.action_params = action_params
namespace.intent = intent
if intent is not None:
namespace.intent_done[intent] += 1
namespace.checking_intent = checking_intent
if checking_intent:
namespace.action_tried[action] += 1
namespace.struct_tried[pack_action_bit_list(action, prestate_bit_list)] += 1
if action_params:
return [action] + action_params
return action
def determine_which_action(poss_actions_list, namespace, bit_list):
"""
Decides between poss_actions based on lowest struct_tried,
then lowest action_tried, then random.
"""
if len(poss_actions_list) > 1:
random.shuffle(poss_actions_list)
poss_actions_list.sort(key=lambda action: namespace.action_tried[action])
if namespace.action_tried[poss_actions_list[0]] == namespace.action_tried[poss_actions_list[1]]:
poss_actions_list.sort(key=lambda action: namespace.struct_tried[pack_action_bit_list(action, bit_list)])
return poss_actions_list[0]
def do_intent(hfo_env,
state,
namespace):
bit_list, self_dict, goal_dict, ball_dict = filter_low_level_state(state)
prestate_dict = {'prestate_bit_list': bit_list,
'prestate_self_dict': self_dict,
'prestate_goal_dict': goal_dict,
'prestate_ball_dict': ball_dict,
'checking_intent': False,
'intent': namespace.intent,
'namespace': namespace}
if (namespace.intent == INTENT_BALL_COLLISION) and bit_list[4]: # ball is kickable
ball_rel_angle = ball_dict['rel_angle']
if ball_rel_angle > 180:
ball_rel_angle -= 360
poss_actions_list = []
if bit_list[0] and (ball_dict['x_pos'] is not None): # self position valid
if get_dist_real(self_dict['x_pos'],self_dict['y_pos'],
ball_dict['x_pos'],ball_dict['y_pos']) < (0.15+0.0425):
if abs(ball_rel_angle) > 0.5:
hfo_env.act(*save_action_prestate(action=hfo.TURN,action_params=[ball_rel_angle],
**prestate_dict))
else:
print(
"Should already be in collision with ball ({0:n}, {1:n} vs {2:n}, {3:n})".format(
self_dict['x_pos'],self_dict['y_pos'],
ball_dict['x_pos'],ball_dict['y_pos']),
file=sys.stderr)
sys.stderr.flush()
hfo_env.act(*save_action_prestate(action=hfo.DASH,
action_params=[80, ball_rel_angle],
**prestate_dict))
return
poss_actions_list.append(hfo.MOVE_TO)
if abs(ball_rel_angle) > 0.5:
poss_actions_list.append(hfo.TURN)
if abs(ball_rel_angle) < 1.0:
poss_actions_list.append(hfo.DASH)
action = determine_which_action(poss_actions_list, namespace, bit_list)
if action == hfo.MOVE_TO:
hfo_env.act(*save_action_prestate(action=hfo.MOVE_TO,
action_params=[ball_dict['x_pos'],
ball_dict['y_pos']],
**prestate_dict))
elif action == hfo.TURN:
hfo_env.act(*save_action_prestate(action=hfo.TURN,
action_params=[ball_rel_angle],
**prestate_dict))
elif action == hfo.DASH:
hfo_env.act(*save_action_prestate(action=hfo.DASH,
action_params=[80, ball_rel_angle],
**prestate_dict))
else:
raise RuntimeError("Unknown action {0!r}".format(action))
return
elif (namespace.intent == INTENT_BALL_KICKABLE) or (namespace.intent == INTENT_BALL_COLLISION):
poss_actions_list = [hfo.INTERCEPT]
if bit_list[0]:
poss_actions_list.append(hfo.GO_TO_BALL)
ball_rel_angle = ball_dict['rel_angle']
if ball_rel_angle > 180:
ball_rel_angle -= 360
if abs(ball_rel_angle) > 0.5:
poss_actions_list.append(hfo.TURN)
if abs(ball_rel_angle) < 1.0:
poss_actions_list.append(hfo.DASH)
action = determine_which_action(poss_actions_list, namespace, bit_list)
if (action == hfo.GO_TO_BALL) or (action == hfo.INTERCEPT):
hfo_env.act(save_action_prestate(action=action,**prestate_dict))
elif action == hfo.TURN:
hfo_env.act(*save_action_prestate(action=hfo.TURN,
action_params=[ball_rel_angle],
**prestate_dict))
elif action == hfo.DASH:
hfo_env.act(*save_action_prestate(action=hfo.DASH,
action_params=[80, ball_rel_angle],
**prestate_dict))
else:
raise RuntimeError("Unknown action {0!r}".format(action))
return
elif namespace.intent == INTENT_GOAL_COLLISION:
goal_rel_angle = goal_dict['rel_angle']
if goal_rel_angle > 180:
goal_rel_angle -= 360
poss_actions_list = []
if (get_dist_real(self_dict['x_pos'],
self_dict['y_pos'],
goal_dict['x_pos'],
goal_dict['y_pos']) < (0.15+0.03)):
if abs(goal_rel_angle) > 0.5:
hfo_env.act(*save_action_prestate(action=hfo.TURN,
action_params=[goal_rel_angle],
**prestate_dict))
else:
print(
"Should already be in collision with goal ({0:n}, {1:n} vs {2:n}, {3:n})".format(
self_dict['x_pos'],self_dict['y_pos'],
goal_dict['x_pos'],goal_dict['y_pos']),
file=sys.stderr)
sys.stderr.flush()
hfo_env.act(*save_action_prestate(action=hfo.DASH,
action_params=[80, goal_rel_angle],
**prestate_dict))
return
poss_actions_list.append(hfo.MOVE_TO)
if goal_rel_angle is not None:
if abs(goal_rel_angle) > 0.5:
poss_actions_list.append(hfo.TURN)
if abs(goal_rel_angle) < 1.0:
poss_actions_list.append(hfo.DASH)
action = determine_which_action(poss_actions_list, namespace, bit_list)
if (action == hfo.TURN):
hfo_env.act(*save_action_prestate(action=hfo.TURN,
action_params=[goal_rel_angle],
**prestate_dict))
elif (action == hfo.MOVE_TO):
hfo_env.act(*save_action_prestate(action=hfo.MOVE_TO,
action_params=[goal_dict['x_pos'], goal_dict['y_pos']],
**prestate_dict))
elif (action == hfo.DASH):
hfo_env.act(*save_action_prestate(action=hfo.DASH,
action_params=[80, goal_rel_angle],
**prestate_dict))
else:
raise RuntimeError("Unknown action {0!r}".format(action))
return
else:
raise RuntimeError("Unexpected intent {0!r}".format(namespace.intent))
def do_next_action(hfo_env,
state,
namespace):
bit_list, self_dict, goal_dict, ball_dict = filter_low_level_state(state)
if not bit_list[1]: # self velocity invalid
# long-term: Turn, Kick, Kick_To
raise NotImplementedError("Not yet set up for self velocity invalid")
prior_intent = namespace.intent
# admittedly, need to check for some of "intent" circumstances in combo!
if namespace.intent == INTENT_BALL_COLLISION:
if bit_list[2]:
namespace.intent = None
else:
if (not bit_list[5]) or (ball_dict['x_pos'] is None): # first: ball location not valid
namespace.intent_done[INTENT_BALL_COLLISION] -= 1
if bit_list[0]: # self location valid
if bit_list[3]:
namespace.intent = None
else:
namespace.intent = INTENT_GOAL_COLLISION
else:
raise NotImplementedError("Not yet set up for self+ball location invalid")
elif namespace.intent == INTENT_BALL_KICKABLE:
if bit_list[4]:
namespace.intent = None
else:
if (not bit_list[5]) or (ball_dict['x_pos'] is None): # first: ball location not valid
namespace.intent_done[INTENT_BALL_KICKABLE] -= 1
if bit_list[0]: # self location valid
if bit_list[3]:
namespace.intent = None
else:
namespace.intent = INTENT_GOAL_COLLISION
else:
raise NotImplementedError("Not yet set up for self+ball location invalid")
elif namespace.intent == INTENT_GOAL_COLLISION:
if bit_list[3]:
namespace.intent = None
else:
if (not bit_list[0]) or (goal_dict['rel_angle'] is None): # first: self location not valid
namespace.intent_done[INTENT_GOAL_COLLISION] -= 1
if bit_list[4] or bit_list[2]:
namespace.intent = None
elif bit_list[5]: # ball location valid
if namespace.intent_done[INTENT_BALL_COLLISION] > namespace.intent_done[INTENT_BALL_KICKABLE]:
namespace.intent = INTENT_BALL_KICKABLE
elif namespace.intent_done[INTENT_BALL_COLLISION] < namespace.intent_done[INTENT_BALL_KICKABLE]:
namespace.intent = INTENT_BALL_COLLISION
elif random.random() < 0.5:
namespace.intent = INTENT_BALL_KICKABLE
else:
namespace.intent = INTENT_BALL_COLLISION
else:
raise NotImplementedError("Not yet set up for self+ball location invalid")
if namespace.intent is not None:
return do_intent(hfo_env, state, namespace)
# figure out what to do next
if not (bit_list[2] or bit_list[3] or bit_list[4]):
poss_intent_set = set([INTENT_BALL_KICKABLE,INTENT_BALL_COLLISION,INTENT_GOAL_COLLISION])
if not bit_list[5]:
poss_intent_set.remove(INTENT_BALL_KICKABLE)
poss_intent_set.remove(INTENT_BALL_COLLISION)
if not bit_list[0]:
poss_intent_set.remove(INTENT_GOAL_COLLISION)
if not poss_intent_set:
raise NotImplementedError("Not yet set up for self+ball location invalid")
poss_intent = list(poss_intent_set)
if len(poss_intent) > 1:
random.shuffle(poss_intent)
poss_intent.sort(key=lambda intent: namespace.intent_done[intent])
namespace.intent = poss_intent[0]
print("INTENT: {}".format(INTENT_DICT[namespace.intent]))
sys.stdout.flush()
return do_intent(hfo_env, state, namespace)
elif bit_list[4]: # kickable
actions_want_check = set([hfo.INTERCEPT, hfo.GO_TO_BALL])
if bit_list[2] or bit_list[3]:
actions_want_check |= set([hfo.KICK, hfo.KICK_TO])
else: # colliding
actions_want_check = set([hfo.DASH, hfo.TURN, hfo.KICK, hfo.KICK_TO,
hfo.MOVE_TO, hfo.DRIBBLE_TO, hfo.INTERCEPT,
hfo.GO_TO_BALL])
if bit_list[4] and (prior_intent not in (None, INTENT_BALL_KICKABLE)):
namespace.intent_done[INTENT_BALL_KICKABLE] += 1
if bit_list[2] and (prior_intent not in (None, INTENT_BALL_COLLISION)):
namespace.intent_done[INTENT_BALL_COLLISION] += 1
if bit_list[3] and (prior_intent not in (None, INTENT_GOAL_COLLISION)):
namespace.intent_done[INTENT_GOAL_COLLISION] += 1
actions_maybe_check = set([hfo.DASH, hfo.TURN, hfo.MOVE_TO])
if not bit_list[6]: # ball velocity invalid
# long-term: Kick, Kick_To, Intercept - if know ball location; Kick_To requires self location valid
raise NotImplementedError("Not yet set up for ball velocity invalid")
if bit_list[5]: # ball position valid
actions_maybe_check |= set([hfo.DRIBBLE_TO, hfo.INTERCEPT, hfo.GO_TO_BALL])
if bit_list[4]: # ball kickable
actions_maybe_check |= set([hfo.KICK, hfo.KICK_TO])
poss_actions = actions_want_check & actions_maybe_check
if not poss_actions:
raise RuntimeError(
"Unknown what to do - actions_want_check {0!r}, actions_maybe_check {1!r}".format(actions_want_check,
actions_maybe_check))
action = determine_which_action(list(poss_actions), namespace, bit_list)
prestate_dict = {'action': action,
'prestate_bit_list': bit_list,
'prestate_self_dict': self_dict,
'prestate_goal_dict': goal_dict,
'prestate_ball_dict': ball_dict,
'checking_intent': True,
'intent': None,
'namespace': namespace}
# ends due to out-of-bounds are annoying/noise in the data
if (action in (hfo.DRIBBLE_TO, hfo.KICK_TO)) or ((action == hfo.MOVE_TO) and (bit_list[4] or bit_list[2])):
if ball_dict['x_pos'] < -1*POS_ERROR_TOLERANCE:
x_pos = random.uniform(-0.5,min(0.9,MAX_POS_X_BALL_SAFE))
elif ball_dict['x_pos'] > POS_ERROR_TOLERANCE:
x_pos = random.uniform(max(-0.9,MIN_POS_X_BALL_SAFE),0.5)
else:
x_pos = random.uniform(max(-0.9,MIN_POS_X_BALL_SAFE),min(0.9,MAX_POS_X_BALL_SAFE))
if ball_dict['y_pos'] < -1*POS_ERROR_TOLERANCE:
y_pos = random.uniform(-0.5,min(0.9,MAX_POS_Y_BALL_SAFE))
elif ball_dict['y_pos'] > POS_ERROR_TOLERANCE:
y_pos = random.uniform(max(-0.9,MIN_POS_Y_BALL_SAFE),0.5)
else:
y_pos = random.uniform(max(-0.9,MIN_POS_Y_BALL_SAFE),min(0.9,MAX_POS_Y_BALL_SAFE))
else:
x_pos = random.uniform(MIN_POS_X_OK,MAX_POS_X_OK)
y_pos = random.uniform(MIN_POS_Y_OK,MAX_POS_Y_OK)
max_power = 80
if (action == hfo.KICK) and (get_dist_normalized(ball_dict['x_pos'],
ball_dict['y_pos'],
0.0,0.0) > 1):
if (self_dict['center_angle'] is not None):
max_power = 100
direction = self_dict['center_angle']
if direction > 180:
direction -= 360
else:
max_power = 50
direction = random.uniform(-180,180)
else:
direction = random.uniform(-180,180)
if action in (hfo.INTERCEPT, hfo.GO_TO_BALL):
hfo_env.act(save_action_prestate(**prestate_dict))
elif action in (hfo.MOVE_TO, hfo.DRIBBLE_TO):
hfo_env.act(*save_action_prestate(action_params=[x_pos,y_pos],
**prestate_dict))
elif action == hfo.KICK_TO:
if min(abs(x_pos),abs(y_pos)) > 0.5:
max_speed = 1
elif max(abs(x_pos),abs(y_pos)) > 0.5:
max_speed = 2
else:
max_speed = 3
hfo_env.act(*save_action_prestate(action_params=[x_pos,y_pos,
random.uniform(0,max_speed)],
**prestate_dict))
elif action == hfo.DASH:
hfo_env.act(*save_action_prestate(action_params=[random.uniform(-100,100), # power
direction],
**prestate_dict))
elif action == hfo.TURN:
hfo_env.act(*save_action_prestate(action_params=[direction],**prestate_dict))
elif action == hfo.KICK:
hfo_env.act(*save_action_prestate(action_params=[random.uniform(0,max_power),
direction],**prestate_dict))
else:
raise RuntimeError("Unknown action {!r}".format(action))
return None
def main_explore_offense_actions_fullstate():
hfo_env = hfo.HFOEnvironment()
parser = argparse.ArgumentParser()
parser.add_argument('--port', type=int, default=6000,
help="Server port")
parser.add_argument('--seed', type=int, default=None,
help="Python randomization seed; uses python default if 0 or not given")
parser.add_argument('--server-seed', type=int, default=None,
help="Server randomization seed; uses default if 0 or not given")
parser.add_argument('--record', default=False, action='store_true',
help="Doing HFO --record")
parser.add_argument('--rdir', type=str, default='log/',
help="Set directory to use if doing HFO --record")
args=parser.parse_args()
namespace = argparse.Namespace()
namespace.intent_done = {}
namespace.intent_done[INTENT_BALL_COLLISION] = 0
namespace.intent_done[INTENT_BALL_KICKABLE] = 0
namespace.intent_done[INTENT_GOAL_COLLISION] = 0
namespace.action_tried = {}
namespace.struct_tried = {}
for n in list(range(hfo.NUM_HFO_ACTIONS)):
namespace.action_tried[n] = 0
for x in list(range((2**(BIT_LIST_LEN+1))-1)):
namespace.struct_tried[struct.pack(STRUCT_PACK, n, x)] = 0
script_dir = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
binary_dir = os.path.normpath(script_dir + "/../bin")
conf_dir = os.path.join(binary_dir, 'teams/base/config/formations-dt')
bin_HFO = os.path.join(binary_dir, "HFO")
popen_list = [sys.executable, "-x", bin_HFO,
"--frames-per-trial=3000", "--untouched-time=2000",
"--port={0:d}".format(args.port),
"--offense-agents=1", "--defense-npcs=0",
"--offense-npcs=0", "--trials=20", "--headless",
"--fullstate"]
if args.seed:
random.seed(args.seed)
if args.record:
popen_list.append("--record")
if args.server_seed:
popen_list.append("--seed={0:d}".format(args.server_seed))
HFO_process = subprocess.Popen(popen_list)
time.sleep(0.2)
assert (HFO_process.poll() is
None), "Failed to start HFO with command '{}'".format(" ".join(popen_list))
hfo_env = hfo.HFOEnvironment()
time.sleep(4.8)
connect_args = [hfo.LOW_LEVEL_FEATURE_SET, conf_dir, args.port, "localhost",
"base_left", False]
if args.record:
connect_args.append(record_dir=args.rdir)
try:
hfo_env.connectToServer(*connect_args)
for ignored_episode in itertools.count():
status = hfo.IN_GAME
namespace.action = hfo.NOOP
namespace.action_params = []
namespace.prestate_bit_list = [0,0,0,0,0,0,0]
namespace.prestate_self_dict = {'x_pos': None,
'y_pos': None,
'body_angle': None}
namespace.prestate_goal_dict = {'dist': get_dist_from_proximity(-1),
'rel_angle': None,
'abs_angle': None,
'x_pos': GOAL_POS_X,
'y_pos': 0.0}
namespace.prestate_ball_dict = {'dist': get_dist_from_proximity(-1),
'rel_angle': None,
'abs_angle': None,
'x_pos': None,
'y_pos': None}
namespace.intent = None
namespace.checking_intent = True
while status == hfo.IN_GAME:
state = hfo_env.getState()
if namespace.action != hfo.NOOP:
evaluate_previous_action(hfo_env, state, namespace)
do_next_action(hfo_env, state, namespace)
status = hfo_env.step()
if status == hfo.SERVER_DOWN:
# summarize results
hfo_env.act(hfo.QUIT)
break
finally:
if HFO_process.poll() is None:
HFO_process.terminate()
os.system("killall -9 rcssserver") # remove if ever start doing multi-server testing!
if __name__ == '__main__':
main_explore_offense_actions_fullstate()
...@@ -19,7 +19,7 @@ except ImportError: ...@@ -19,7 +19,7 @@ except ImportError:
' run: \"pip install .\"') ' run: \"pip install .\"')
exit() exit()
GOAL_POS_X = 1.0 GOAL_POS_X = 0.9
GOAL_POS_Y = 0.0 GOAL_POS_Y = 0.0
# below - from hand_coded_defense_agent.cpp except LOW_KICK_DIST # below - from hand_coded_defense_agent.cpp except LOW_KICK_DIST
......
...@@ -6,11 +6,12 @@ import os ...@@ -6,11 +6,12 @@ import os
hfo_lib = cdll.LoadLibrary(os.path.join(os.path.dirname(__file__), hfo_lib = cdll.LoadLibrary(os.path.join(os.path.dirname(__file__),
'libhfo_c.so')) 'libhfo_c.so'))
''' Possible feature sets ''' """Possible feature sets"""
NUM_FEATURE_SETS = 2 NUM_FEATURE_SETS = 2
LOW_LEVEL_FEATURE_SET, HIGH_LEVEL_FEATURE_SET = list(range(NUM_FEATURE_SETS)) LOW_LEVEL_FEATURE_SET, HIGH_LEVEL_FEATURE_SET = list(range(NUM_FEATURE_SETS))
''' An enum of the possible HFO actions """
An enum of the possible HFO actions, including:
[Low-Level] Dash(power, relative_direction) [Low-Level] Dash(power, relative_direction)
[Low-Level] Turn(direction) [Low-Level] Turn(direction)
[Low-Level] Tackle(direction) [Low-Level] Tackle(direction)
...@@ -25,27 +26,58 @@ LOW_LEVEL_FEATURE_SET, HIGH_LEVEL_FEATURE_SET = list(range(NUM_FEATURE_SETS)) ...@@ -25,27 +26,58 @@ LOW_LEVEL_FEATURE_SET, HIGH_LEVEL_FEATURE_SET = list(range(NUM_FEATURE_SETS))
[High-Level] Dribble(): Offensive dribble [High-Level] Dribble(): Offensive dribble
[High-Level] Catch(): Catch the ball (Goalie Only) [High-Level] Catch(): Catch the ball (Goalie Only)
NOOP(): Do Nothing NOOP(): Do Nothing
QUIT(): Quit the game ''' QUIT(): Quit the game
"""
NUM_HFO_ACTIONS = 19 NUM_HFO_ACTIONS = 19
DASH, TURN, TACKLE, KICK, KICK_TO, MOVE_TO, DRIBBLE_TO, INTERCEPT, \ DASH,TURN,TACKLE,KICK,KICK_TO,MOVE_TO,DRIBBLE_TO,INTERCEPT,MOVE,SHOOT,PASS,DRIBBLE,CATCH,NOOP,QUIT,REDUCE_ANGLE_TO_GOAL,MARK_PLAYER,DEFEND_GOAL,GO_TO_BALL = list(range(NUM_HFO_ACTIONS))
MOVE, SHOOT, PASS, DRIBBLE, CATCH, NOOP, QUIT, REDUCE_ANGLE_TO_GOAL,MARK_PLAYER,DEFEND_GOAL,GO_TO_BALL = list(range(NUM_HFO_ACTIONS)) ACTION_STRINGS = {DASH: "Dash",
ACTION_STRINGS = ["Dash", "Turn", "Tackle", "Kick", "KickTo", "MoveTo", "DribbleTo", "Intercept", "Move", "Shoot", "Pass", "Dribble", "Catch", "No-op", "Quit", "Reduce_Angle_To_Goal", "Mark_Player", "Defend_Goal", "Go_To_Ball"] TURN: "Turn",
TACKLE: "Tackle",
''' Possible game status KICK: "Kick",
KICK_TO: "KickTo",
MOVE_TO: "MoveTo",
DRIBBLE_TO: "DribbleTo",
INTERCEPT: "Intercept",
MOVE: "Move",
SHOOT: "Shoot",
PASS: "Pass",
DRIBBLE: "Dribble",
CATCH: "Catch",
NOOP: "No-op",
QUIT: "Quit",
REDUCE_ANGLE_TO_GOAL: "Reduce_Angle_To_Goal",
MARK_PLAYER: "Mark_Player",
DEFEND_GOAL: "Defend_Goal",
GO_TO_BALL: "Go_To_Ball"}
"""
Possible game statuses:
[IN_GAME] Game is currently active [IN_GAME] Game is currently active
[GOAL] A goal has been scored by the offense [GOAL] A goal has been scored by the offense
[CAPTURED_BY_DEFENSE] The defense has captured the ball [CAPTURED_BY_DEFENSE] The defense has captured the ball
[OUT_OF_BOUNDS] Ball has gone out of bounds [OUT_OF_BOUNDS] Ball has gone out of bounds
[OUT_OF_TIME] Trial has ended due to time limit [OUT_OF_TIME] Trial has ended due to time limit
[SERVER_DOWN] Server is not alive [SERVER_DOWN] Server is not alive
''' """
NUM_GAME_STATUS_STATES = 6 NUM_GAME_STATUS_STATES = 6
IN_GAME, GOAL, CAPTURED_BY_DEFENSE, OUT_OF_BOUNDS, OUT_OF_TIME, SERVER_DOWN = list(range(NUM_GAME_STATUS_STATES)) IN_GAME, GOAL, CAPTURED_BY_DEFENSE, OUT_OF_BOUNDS, OUT_OF_TIME, SERVER_DOWN = list(range(NUM_GAME_STATUS_STATES))
STATUS_STRINGS = ["InGame", "Goal", "CapturedByDefense", "OutOfBounds", "OutOfTime", "ServerDown"] STATUS_STRINGS = {IN_GAME: "InGame",
GOAL: "Goal",
''' Possible sides ''' CAPTURED_BY_DEFENSE: "CapturedByDefense",
OUT_OF_BOUNDS: "OutOfBounds",
OUT_OF_TIME: "OutOfTime",
SERVER_DOWN: "ServerDown"}
"""Possible sides."""
RIGHT, NEUTRAL, LEFT = list(range(-1,2)) RIGHT, NEUTRAL, LEFT = list(range(-1,2))
"""Possible action result statuses."""
ACTION_STATUS_UNKNOWN, ACTION_STATUS_BAD, ACTION_STATUS_MAYBE = list(range(-1,2))
ACTION_STATUS_MAYBE_OK = ACTION_STATUS_MAYBE # typos
ACTION_STATUS_STRINGS = {ACTION_STATUS_UNKNOWN: "Unknown",
ACTION_STATUS_BAD: "Bad",
ACTION_STATUS_MAYBE: "MaybeOK"}
class Player(Structure): pass class Player(Structure): pass
Player._fields_ = [ Player._fields_ = [
('side', c_int), ('side', c_int),
...@@ -170,8 +202,13 @@ class HFOEnvironment(object): ...@@ -170,8 +202,13 @@ class HFOEnvironment(object):
def getLastActionStatus(self, last_action): def getLastActionStatus(self, last_action):
""" """
If last_action is the last action with a recorded status, If last_action is the last action with a recorded status,
returns a 1 for possible success, 0 for no possibility of success, returns ACTION_STATUS_MAYBE for possible success,
or -1 if unknown. If it is not the last action with a ACTION_STATUS_BAD for no possibility of success,
recorded status, returns a -1. or ACTION_STATUS_UNKNOWN if unknown. If it is not the
last action with a recorded status, returns ACTION_STATUS_UNKNOWN.
""" """
return hfo_lib.getLastActionStatus(self.obj, last_action) return hfo_lib.getLastActionStatus(self.obj, last_action)
def actionStatusToString(self, status):
"""Returns a string representation of an action status."""
return ACTION_STATUS_STRINGS[status]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment