Commit b146090a authored by Matthew Hausknecht's avatar Matthew Hausknecht

Major overhaul - agents now connect directly to server.

parent 45572095
......@@ -85,7 +85,7 @@ set(PLAYER_CPP bhv_basic_move.cpp bhv_basic_offensive_kick.cpp
role_side_back.cpp role_side_forward.cpp role_side_half.cpp
role_keepaway_keeper.cpp role_keepaway_taker.cpp
sample_communication.cpp keepaway_communication.cpp
sample_field_evaluator.cpp sample_player.cpp strategy.cpp)
sample_field_evaluator.cpp sample_player.cpp strategy.cpp agent.cpp)
foreach(src ${PLAYER_CPP})
list(APPEND PLAYER_SOURCES ${SOURCE_DIR}/${src})
......@@ -95,7 +95,7 @@ file(GLOB CHAIN_ACTION_SOURCES ${SOURCE_DIR}/chain_action/*.cpp)
list(APPEND LINK_LIBS rcsc_agent rcsc_geom rcsc_param rcsc_ann
rcsc_net rcsc_gz rcsc_time rcsc_rcg)
add_library(player_chain_action STATIC ${PLAYER_SOURCES} ${CHAIN_ACTION_SOURCES})
add_library(player_chain_action SHARED ${PLAYER_SOURCES} ${CHAIN_ACTION_SOURCES})
add_executable(sample_coach ${SOURCE_DIR}/main_coach.cpp ${SOURCE_DIR}/sample_coach.cpp)
add_executable(sample_player ${SOURCE_DIR}/HFO.cpp ${SOURCE_DIR}/main_player.cpp ${SOURCE_DIR}/sample_player.cpp ${SOURCE_DIR}/agent.cpp)
add_executable(sample_trainer ${SOURCE_DIR}/main_trainer.cpp ${SOURCE_DIR}/sample_trainer.cpp)
......@@ -110,18 +110,25 @@ add_library(hfo-lib SHARED ${SOURCE_DIR}/HFO.hpp ${SOURCE_DIR}/HFO.cpp)
set_target_properties(hfo-lib PROPERTIES OUTPUT_NAME hfo)
set_target_properties(hfo-lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
add_library(hfo-c-lib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/hfo/hfo_c_wrapper.cpp ${SOURCE_DIR}/HFO.cpp)
set_target_properties(hfo-c-lib PROPERTIES OUTPUT_NAME hfo_c)
set_target_properties(hfo-c-lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/hfo)
add_dependencies(player_chain_action librcsc)
add_dependencies(sample_coach librcsc)
add_dependencies(sample_player librcsc)
add_dependencies(sample_trainer librcsc)
add_dependencies(agent librcsc)
add_dependencies(hfo-lib librcsc)
add_dependencies(hfo-lib player_chain_action)
add_dependencies(hfo-c-lib player_chain_action)
target_link_libraries(player_chain_action ${LINK_LIBS})
target_link_libraries(sample_coach ${LINK_LIBS})
target_link_libraries(sample_player ${LINK_LIBS} player_chain_action)
target_link_libraries(sample_trainer ${LINK_LIBS})
target_link_libraries(agent ${LINK_LIBS} player_chain_action)
target_link_libraries(hfo-lib ${LINK_LIBS} player_chain_action)
target_link_libraries(hfo-c-lib ${LINK_LIBS} player_chain_action)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)
add_executable(hfo_example_agent ${CMAKE_CURRENT_SOURCE_DIR}/example/hfo_example_agent.cpp)
......
......@@ -35,9 +35,9 @@ def main(args):
os.makedirs(args.logDir)
num_agents = args.offenseAgents + args.defenseAgents
binary_dir = os.path.dirname(os.path.realpath(__file__))
server_port = args.port + num_agents
coach_port = args.port + num_agents + 1
olcoach_port = args.port + num_agents + 2
server_port = args.port
coach_port = args.port + 1
olcoach_port = args.port + 2
serverCommand = os.path.join(binary_dir, SERVER_BIN)
serverOptions = ' server::port=%i server::coach_port=%i ' \
'server::olcoach_port=%i server::coach=1 ' \
......
......@@ -47,7 +47,7 @@ class Trainer(object):
self._done = False # Are we finished?
self._agentPopen = [] # Agent's processes
self._npcPopen = [] # NPC's processes
self._connectedPlayers = []
self._connectedPlayers = [] # List of connected players
self.initMsgHandlers()
def launch_agent(self, agent_num, agent_ext_num, play_offense, port, wait_until_join=True):
......@@ -55,8 +55,6 @@ class Trainer(object):
Returns a Popen process object
"""
print 'Launch agent %s-%d'%(self._offenseTeamName if play_offense
else self._defenseTeamName, agent_num)
if play_offense:
assert self._numOffense > 0
team_name = self._offenseTeamName
......@@ -64,37 +62,24 @@ class Trainer(object):
# First offense number is reserved for inactive offensive goalie
internal_player_num = agent_num + 1
self._agentNumInt.append(internal_player_num)
numTeammates = self._numOffense - 1
numOpponents = self._numDefense
else:
assert self._numDefense > 0
team_name = self._defenseTeamName
self._agentTeams.append(team_name)
internal_player_num = agent_num
self._agentNumInt.append(internal_player_num)
numTeammates = self._numDefense - 1
numOpponents = self._numOffense
binary_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'teams', 'base')
binary_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)),
'teams', 'base')
config_dir = os.path.join(binary_dir, 'config/formations-dt')
player_conf = os.path.join(binary_dir, '/config/player.conf')
agent_cmd = os.path.join(binary_dir, 'agent')
agent_cmd += ' -t %s -p %i --numTeammates %i --numOpponents %i' \
' --playingOffense %i --serverPort %i --log_dir %s' \
' --player-config %s --config_dir %s' \
%(team_name, self._serverPort, numTeammates,
numOpponents, play_offense, port, self._logDir,
player_conf, config_dir)
if agent_ext_num == 1:
agent_cmd += ' -g'
if self._record:
agent_cmd += ' --record'
# Comment next two lines to show output from agent.cpp and the server
kwargs = {'stdout':open('/dev/null', 'w'),
'stderr':open('/dev/null', 'w')}
p = subprocess.Popen(agent_cmd.split(' '), shell = False, **kwargs)
print("Waiting for player-controlled agent %s-%d: config_dir=%s, "\
"uniform_number=%d, server_port=%d, server_addr=%s, team_name=%s, "\
"play_goalie=%r"
% (self._offenseTeamName if play_offense else self._defenseTeamName,
agent_num, config_dir, agent_ext_num, self._serverPort, "localhost",
team_name, agent_ext_num==1))
if wait_until_join:
self.waitOnPlayer(agent_ext_num, play_offense)
return p
return None
def createTeam(self, requested_team_name, play_offense):
""" Given a team name, returns the team object. """
......@@ -198,6 +183,7 @@ class Trainer(object):
(self._numGoals, self._numTrials, self._frame, event)
self._numFrames += self._frame - self._lastTrialStart
self._lastTrialStart = self._frame
self.getConnectedPlayers()
def _hear(self, body):
""" Handle a hear message. """
......@@ -319,46 +305,37 @@ class Trainer(object):
def disconnectPlayer(self, player, player_num, on_offense):
"""Wait on a launched player to disconnect from the server. """
# print 'Disconnect %s-%d'%(self._offenseTeamName if on_offense
# else self._defenseTeamName, player_num)
team_name = self._offenseTeamName if on_offense else self._defenseTeamName
self.send('(disconnect_player %s %d)'%(team_name, player_num))
player.kill()
def waitOnPlayer(self, player_num, on_offense):
"""Wait on a launched player to connect and be reported by the
server.
"""
def getConnectedPlayers(self):
""" Get the list of connected players. Populates self._connectedPlayers. """
self._gotLook = False
self.send('(look)')
partial = ['ok','look']
self._numPlayers = 0
def f(body):
self._gotLook = True
del self._connectedPlayers[:]
for i in xrange(4, len(body)):
_,team,num = body[i][0][:3]
if (team, num) not in self._connectedPlayers:
self._connectedPlayers.append((team,num))
self.registerMsgHandler(f,*partial,quiet=True)
team_name = self._offenseTeamName if on_offense else self._defenseTeamName
while (team_name, str(player_num)) not in self._connectedPlayers:
while not self._gotLook:
self.listenAndProcess()
self.send('(look)')
self.ignoreMsg(*partial,quiet=True)
def checkIfAllPlayersConnected(self):
""" Returns true if all players are connected. """
print 'Checking all players are connected'
self.send('(look)')
partial = ['ok','look']
self._numPlayers = 0
def f(x):
self._numPlayers = len(x) - 4 # -4 for time, ball, goal_l, and goal_r
self.send('(look)')
self.registerMsgHandler(f,*partial,quiet=True)
while self._numPlayers != self._numOffense + self._numDefense:
self.listenAndProcess()
self.ignoreMsg(*partial,quiet=True)
def waitOnPlayer(self, player_num, on_offense):
""" Wait on a launched player to connect and be reported by the server. """
team_name = self._offenseTeamName if on_offense else self._defenseTeamName
while (team_name, str(player_num)) not in self._connectedPlayers:
self.getConnectedPlayers()
def allPlayersConnected(self):
""" Returns true all players are connected. """
return len(self._connectedPlayers) == self._numOffense + self._numDefense
def startGame(self):
""" Starts a game of HFO. """
......@@ -382,7 +359,7 @@ class Trainer(object):
"""
for p,name in necProcesses:
if p.poll() is not None:
if p is not None and p.poll() is not None:
print 'Something necessary closed (%s), exiting' % name
return False
return True
......@@ -434,15 +411,9 @@ class Trainer(object):
else:
self.disconnectPlayer(player, player_num, on_offense=False)
self.checkIfAllPlayersConnected()
if self._numAgents > 0:
print 'Agents awaiting your connections'
necOff = set([(self._offenseTeamName,str(x)) for x in sorted_offense_agent_unums])
necDef = set([(self._defenseTeamName,str(x)) for x in sorted_defense_agent_unums])
necAgents = necOff.union(necDef)
while self.checkLive(necProcesses) and self._agentReady != necAgents:
self.listenAndProcess()
print 'Checking all players connected'
while not self.allPlayersConnected():
self.getConnectedPlayers()
# Broadcast the HFO configuration
offense_nums = ' '.join([str(self.convertToExtPlayer(self._offenseTeamName, i))
......@@ -456,7 +427,7 @@ class Trainer(object):
offense_nums, defense_nums))
print 'Starting game'
self.startGame()
while self.checkLive(necProcesses) and not self._done:
while self.allPlayersConnected() and self.checkLive(necProcesses) and not self._done:
prevFrame = self._frame
self.listenAndProcess()
except TimeoutError:
......
......@@ -5,44 +5,72 @@
#include <stdio.h>
#include <math.h>
#include <iostream>
using namespace std;
using namespace hfo;
// Before running this program, first Start HFO server:
// $./bin/HFO --offense-agents 1
// This agent is intended to be run as a part of the passing_agents.sh script
// Server Connection Options. See printouts from bin/HFO.
feature_set_t features = HIGH_LEVEL_FEATURE_SET;
string config_dir = "bin/teams/base/config/formations-dt";
int unum = 11;
int port = 6001;
string server_addr = "localhost";
string team_name = "base_left";
bool goalie = false;
#define PI 3.14159265
int main(int argc, char** argv) {
int port = 6000;
if (argc > 1) {
port = atoi(argv[1]);
if (argc > 2) {
unum = atoi(argv[1]);
port = atoi(argv[2]);
}
// Create the HFO environment
HFOEnvironment hfo;
// Connect to the agent's server on port 6000 and request low-level
// feature set. See manual for more information on feature sets.
hfo.connectToAgentServer(port, HIGH_LEVEL_FEATURE_SET);
// Play 5 episodes
for (int episode=0; ; episode++) {
int step = 0;
// Connect to the server and request feature set. See manual for
// more information on feature sets.
hfo.connectToServer(features, config_dir, unum, port, server_addr,
team_name, goalie);
for (int episode=0; episode<10; episode++) {
int agent_on_ball = 7;
status_t status = IN_GAME;
while (status == IN_GAME) {
// Get the vector of state features for the current state
const vector<float>& feature_vec = hfo.getState();
// Get any incoming communication
std::string msg = hfo.hear();
// Do something with incoming communication
cout << "HEARD: " << msg.c_str() << endl;
float target_x = sin((step % 360) * PI/180);
float target_y = cos((step % 360) * PI/180);
hfo.act(DRIBBLE_TO, target_x, target_y);
// Do something with outgoing communication
hfo.say("Message");
if (!msg.empty()) {
cout << "Agent-" << unum << " HEARD: " << msg.c_str() << endl;
if (msg == "Pass") {
agent_on_ball = unum;
}
}
float x_pos = feature_vec[0];
float y_pos = feature_vec[1];
float target_x = 0;
float target_y = unum == 11 ? .3 : -.3;
bool in_position = (pow(x_pos-target_x, 2) + pow(y_pos-target_y,2)) < .0005;
bool able_to_kick = feature_vec[5] > 0;
if (agent_on_ball == unum && in_position && able_to_kick) {
int teammate_unum = unum == 11 ? 7 : 11;
float teammate_x_pos = 0;
float teammate_y_pos = -target_y;
hfo.act(KICK_TO, teammate_x_pos, teammate_y_pos, 2.0);
hfo.say("Pass");
agent_on_ball = teammate_unum;
} else {
float dist_to_ball = feature_vec[3];
float dist_to_teammate = feature_vec[13];
action_t action = unum == agent_on_ball ? DRIBBLE_TO : MOVE_TO;
hfo.act(action, target_x, target_y);
}
// Advance the environment and get the game status
status = hfo.step();
step+=2;
}
// Check what the outcome of the episode was
cout << "Episode " << episode << " ended with status: "
<< StatusToString(status) << std::endl;;
}
hfo.act(QUIT);
};
#!/usr/bin/env python
# encoding: utf-8
import sys
# Before running this program, first Start HFO server:
# $> ./bin/HFO --offense-agents 1
# First Start the server: $> bin/start.py
import sys
from hfo import *
if __name__ == '__main__':
port = 6000
if len(sys.argv) > 1:
port = int(sys.argv[1])
try:
from hfo import *
except:
print 'Failed to import hfo. To install hfo, in the HFO directory'\
' run: \"pip install .\"'
exit()
# Create the HFO Environment
hfo_env = hfo.HFOEnvironment()
# Connect to the agent server on port 6000 with the specified
hfo = hfo.HFOEnvironment()
# Connect to the server with the specified
# feature set. See feature sets in hfo.py/hfo.hpp.
hfo_env.connectToAgentServer(port, HFO_Features.HIGH_LEVEL_FEATURE_SET)
# Play 5 episodes
for episode in xrange(5):
status = HFO_Status.IN_GAME
while status == HFO_Status.IN_GAME:
hfo.connectToServer(HIGH_LEVEL_FEATURE_SET,
'bin/teams/base/config/formations-dt', 11, 6000,
'localhost', 'base_left', False)
for episode in xrange(10):
status = IN_GAME
while status == IN_GAME:
# Grab the state features from the environment
features = hfo_env.getState()
features = hfo.getState()
# Get any incoming communication
msg = hfo_env.hear()
msg = hfo.hear()
# Do something with incoming communication
print 'Heard: ', msg
# Take an action and get the current game status
hfo_env.act(HFO_Actions.DASH, 20.0, 0)
hfo.act(DASH, 20.0, 0)
# Do something with outgoing communication
hfo_env.say('Message')
status = hfo_env.step()
print 'Episode', episode, 'ended with',
# Check what the outcome of the episode was
if status == HFO_Status.GOAL:
print 'goal', hfo.playerOnBall().unum
elif status == HFO_Status.CAPTURED_BY_DEFENSE:
print 'captured by defense', hfo.playerOnBall().unum
elif status == HFO_Status.OUT_OF_BOUNDS:
print 'out of bounds'
elif status == HFO_Status.OUT_OF_TIME:
print 'out of time'
else:
print 'Unknown status', status
exit()
# Cleanup when finished
hfo_env.cleanup()
hfo.say('Message')
status = hfo.step()
print 'Episode', episode, 'ended with', hfo.statusToString(status)
#!/usr/bin/env python
# encoding: utf-8
# First Start the server: $> bin/start.py
import random, threading, argparse
try:
from hfo import *
except:
print 'Failed to import hfo. To install hfo, in the HFO directory'\
' run: \"pip install .\"'
exit()
# Before running this program, first Start HFO server:
# $> ./bin/HFO --offense-agents 1
def get_random_action():
"""Returns a random high-level action. Pass is omitted for simplicity."""
high_lv_actions = [HFO_Actions.SHOOT, HFO_Actions.DRIBBLE]
return random.choice(high_lv_actions)
def play_hfo(num):
""" Method called by a thread to play 5 games of HFO """
hfo_env = hfo.HFOEnvironment()
hfo_env.connectToAgentServer(6000 + num, HFO_Features.HIGH_LEVEL_FEATURE_SET)
try:
for episode in xrange(5):
status = HFO_Status.IN_GAME
while status == HFO_Status.IN_GAME:
state = hfo_env.getState()
if state[5] == 1: #state[5] is 1 when player has the ball
hfo_env.act(get_random_action())
else:
hfo_env.act(HFO_Actions.MOVE)
status = hfo_env.step()
except:
pass
finally:
print "Agent " + str(num) + " exiting."
hfo_env.cleanup()
import random
from hfo import *
def main():
parser = argparse.ArgumentParser()
parser.add_argument('num_agents', type=int, help='Number of agents to start. '\
'NOTE: server must be started with this number of agents.')
args = parser.parse_args()
for i in xrange(args.num_agents):
t = threading.Thread(target=play_hfo, args=(i,))
t.start()
# Create the HFO Environment
hfo = HFOEnvironment()
# Connect to the server with the specified
# feature set. See feature sets in hfo.py/hfo.hpp.
hfo.connectToServer(HIGH_LEVEL_FEATURE_SET,
'bin/teams/base/config/formations-dt', 11, 6000,
'localhost', 'base_left', False)
for episode in xrange(10):
status = IN_GAME
while status == IN_GAME:
state = hfo.getState()
if state[5] == 1: # State[5] is 1 when the player can kick the ball
hfo.act(random.choice([SHOOT, DRIBBLE]))
else:
hfo.act(MOVE)
status = hfo.step()
print 'Episode', episode, 'ended with', hfo.statusToString(status)
if __name__ == '__main__':
main()
......@@ -7,14 +7,24 @@ using namespace std;
using namespace hfo;
// Before running this program, first Start HFO server:
// $./bin/start.py --offense-agents 1
// $> ./bin/HFO --offense-agents 1
// Server Connection Options. See printouts from bin/HFO.
feature_set_t features = LOW_LEVEL_FEATURE_SET;
string config_dir = "bin/teams/base/config/formations-dt";
int unum = 11;
int port = 6001;
string server_addr = "localhost";
string team_name = "base_left";
bool goalie = false;
int main() {
// Create the HFO environment
HFOEnvironment hfo;
// Connect to the agent's server on port 6000 and request low-level
// feature set. See manual for more information on feature sets.
hfo.connectToAgentServer(6000, LOW_LEVEL_FEATURE_SET);
// Connect to the server and request low-level feature set. See
// manual for more information on feature sets.
hfo.connectToServer(features, config_dir, unum, port, server_addr,
team_name, goalie);
// Play 5 episodes
for (int episode=0; episode<5; episode++) {
status_t status = IN_GAME;
......@@ -27,23 +37,8 @@ int main() {
status = hfo.step();
}
// Check what the outcome of the episode was
cout << "Episode " << episode << " ended with status: ";
switch (status) {
case GOAL:
cout << "goal" << endl;
break;
case CAPTURED_BY_DEFENSE:
cout << "captured by defense" << endl;
break;
case OUT_OF_BOUNDS:
cout << "out of bounds" << endl;
break;
case OUT_OF_TIME:
cout << "out of time" << endl;
break;
default:
cout << "Unknown status " << status << endl;
exit(1);
}
cout << "Episode " << episode << " ended with status: "
<< StatusToString(status) << std::endl;;
}
hfo.act(QUIT);
};
#!/usr/bin/env python
# encoding: utf-8
import sys
# Before running this program, first Start HFO server:
# $> ./bin/HFO --offense-agents 1
from hfo import *
# First Start the server: $> bin/start.py
if __name__ == '__main__':
port = 6000
if len(sys.argv) > 1:
port = int(sys.argv[1])
try:
from hfo import *
except:
print 'Failed to import hfo. To install hfo, in the HFO directory'\
' run: \"pip install .\"'
exit()
# Create the HFO Environment
hfo = hfo.HFOEnvironment()
# Connect to the agent server on port 6000 with the specified
hfo = HFOEnvironment()
# Connect to the server with the specified
# feature set. See feature sets in hfo.py/hfo.hpp.
hfo.connectToAgentServer(port, HFO_Features.HIGH_LEVEL_FEATURE_SET)
# Play 100 episodes
for episode in xrange(100):
status = HFO_Status.IN_GAME
while status == HFO_Status.IN_GAME:
hfo.connectToServer(LOW_LEVEL_FEATURE_SET,
'bin/teams/base/config/formations-dt', 11, 6000,
'localhost', 'base_left', False)
for episode in xrange(10):
status = IN_GAME
while status == IN_GAME:
# Grab the state features from the environment
features = hfo.getState()
# Take an action and get the current game status
hfo.act(HFO_Actions.DASH, 20.0, 0)
hfo.act(DASH, 20.0, 0)
status = hfo.step()
print 'Episode', episode, 'ended with',
# Check what the outcome of the episode was
if status == HFO_Status.GOAL:
print 'goal', hfo.playerOnBall().unum
elif status == HFO_Status.CAPTURED_BY_DEFENSE:
print 'captured by defense', hfo.playerOnBall().unum
elif status == HFO_Status.OUT_OF_BOUNDS:
print 'out of bounds'
elif status == HFO_Status.OUT_OF_TIME:
print 'out of time'
else:
print 'Unknown status', status
exit()
# Cleanup when finished
hfo.cleanup()
print 'Episode', episode, 'ended with', hfo.statusToString(status)
......@@ -9,6 +9,15 @@ using namespace hfo;
// Before running this program, first Start HFO server:
// $./bin/HFO --offense-agents 1
// Server Connection Options. See printouts from bin/HFO.
feature_set_t features = HIGH_LEVEL_FEATURE_SET;
string config_dir = "bin/teams/base/config/formations-dt";
int unum = 11;
int port = 6001;
string server_addr = "localhost";
string team_name = "base_left";
bool goalie = false;
// Returns a random high-level action
action_t get_random_high_lv_action() {
action_t action_indx = (action_t) ((rand() % 5) + MOVE);
......@@ -16,17 +25,13 @@ action_t get_random_high_lv_action() {
}
int main(int argc, char** argv) {
int port = 6000;
if (argc > 1) {
port = atoi(argv[1]);
}
// Create the HFO environment
HFOEnvironment hfo;
// Connect to the agent's server on port 6000 and request low-level
// feature set. See manual for more information on feature sets.
hfo.connectToAgentServer(port, HIGH_LEVEL_FEATURE_SET);
// Play 5 episodes
for (int episode=0; ; episode++) {
// Connect to the server and request high-level feature set. See
// manual for more information on feature sets.
hfo.connectToServer(features, config_dir, unum, port, server_addr,
team_name, goalie);
for (int episode=0; episode<10; episode++) {
status_t status = IN_GAME;
while (status == IN_GAME) {
// Get the vector of state features for the current state
......@@ -36,26 +41,9 @@ int main(int argc, char** argv) {
// Advance the environment and get the game status
status = hfo.step();
}
// Check what the outcome of the episode was
cout << "Episode " << episode << " ended with status: ";
switch (status) {
case GOAL:
cout << "goal " << hfo.playerOnBall().unum << endl;
break;
case CAPTURED_BY_DEFENSE:
cout << "captured by defense " << hfo.playerOnBall().unum << endl;
break;
case OUT_OF_BOUNDS:
cout << "out of bounds" << endl;
break;
case OUT_OF_TIME:
cout << "out of time" << endl;
break;
default:
cout << "Unknown status " << status << endl;
exit(1);
}
cout << "Episode " << episode << " ended with status: "
<< StatusToString(status) << std::endl;
}
hfo.act(QUIT);
};
......@@ -9,6 +9,15 @@ using namespace hfo;
// Before running this program, first Start HFO server:
// $./bin/HFO --offense-agents 1
// Server Connection Options. See printouts from bin/HFO.
feature_set_t features = LOW_LEVEL_FEATURE_SET;
string config_dir = "bin/teams/base/config/formations-dt";
int unum = 11;
int port = 6001;
string server_addr = "localhost";
string team_name = "base_left";
bool goalie = false;
float arg1, arg2;
// Returns a random low-level action
......@@ -40,16 +49,12 @@ action_t get_random_low_lv_action() {
}
int main(int argc, char** argv) {
int port = 6000;
if (argc > 1) {
port = atoi(argv[1]);
}
// Create the HFO environment
HFOEnvironment hfo;
// Connect to the agent's server on port 6000 and request low-level
// feature set. See manual for more information on feature sets.
hfo.connectToAgentServer(port, LOW_LEVEL_FEATURE_SET);
// Play 5 episodes
// Connect to the server and request low-level feature set. See
// manual for more information on feature sets.
hfo.connectToServer(features, config_dir, unum, port, server_addr,
team_name, goalie);
for (int episode=0; ; episode++) {
status_t status = IN_GAME;
while (status == IN_GAME) {
......@@ -57,8 +62,9 @@ int main(int argc, char** argv) {
const vector<float>& feature_vec = hfo.getState();
// Perform the action and recieve the current game status
hfo.act(get_random_low_lv_action(), arg1, arg2);
// Advance the environment and recieve current game status
status = hfo.step();
}
}
hfo.act(QUIT);
};
......@@ -7,23 +7,29 @@
using namespace std;
using namespace hfo;
#define PI 3.14159265
// This agent demonstrates the use of the DRIBBLE_TO action. Before
// running this program, first Start HFO server: $./bin/HFO
// --offense-agents 1
// running this program, first Start HFO server:
// $./bin/HFO --offense-agents 1
#define PI 3.14159265
// Server Connection Options. See printouts from bin/HFO.
feature_set_t features = HIGH_LEVEL_FEATURE_SET;
string config_dir = "bin/teams/base/config/formations-dt";
int unum = 11;
int port = 6001;
string server_addr = "localhost";
string team_name = "base_left";
bool goalie = false;
int main(int argc, char** argv) {
int port = 6000;
if (argc > 1) {
port = atoi(argv[1]);
}
// Create the HFO environment
HFOEnvironment hfo;
// Connect to the agent's server on port 6000 and request low-level
// feature set. See manual for more information on feature sets.
hfo.connectToAgentServer(port, HIGH_LEVEL_FEATURE_SET);
for (int episode=0; ; episode++) {
// Connect to the server and request low-level feature set. See
// manual for more information on feature sets.
hfo.connectToServer(features, config_dir, unum, port, server_addr,
team_name, goalie);
for (int episode=0; episode<10; episode++) {
status_t status = IN_GAME;
int step = 0;
while (status == IN_GAME) {
......@@ -37,6 +43,9 @@ int main(int argc, char** argv) {
status = hfo.step();
step += 2;
}
// Check what the outcome of the episode was
cout << "Episode " << episode << " ended with status: "
<< StatusToString(status) << std::endl;;
}
hfo.act(QUIT);
};
......@@ -11,17 +11,23 @@ using namespace hfo;
// running this program, first Start HFO server: $./bin/HFO
// --offense-agents 1
// Server Connection Options. See printouts from bin/HFO.
feature_set_t features = HIGH_LEVEL_FEATURE_SET;
string config_dir = "bin/teams/base/config/formations-dt";
int unum = 11;
int port = 6001;
string server_addr = "localhost";
string team_name = "base_left";
bool goalie = false;
int main(int argc, char** argv) {
int port = 6000;
if (argc > 1) {
port = atoi(argv[1]);
}
// Create the HFO environment
HFOEnvironment hfo;
// Connect to the agent's server on port 6000 and request low-level
// feature set. See manual for more information on feature sets.
hfo.connectToAgentServer(port, HIGH_LEVEL_FEATURE_SET);
for (int episode=0; ; episode++) {
// Connect to the server and request low-level feature set. See
// manual for more information on feature sets.
hfo.connectToServer(features, config_dir, unum, port, server_addr,
team_name, goalie);
for (int episode=0; episode < 10; episode++) {
status_t status = IN_GAME;
while (status == IN_GAME) {
// Get the vector of state features for the current state
......@@ -47,24 +53,8 @@ int main(int argc, char** argv) {
status = hfo.step();
}
// Check what the outcome of the episode was
cout << "Episode " << episode << " ended with status: ";
switch (status) {
case GOAL:
cout << "goal " << hfo.playerOnBall().unum << endl;
break;
case CAPTURED_BY_DEFENSE:
cout << "captured by defense " << hfo.playerOnBall().unum << endl;
break;
case OUT_OF_BOUNDS:
cout << "out of bounds" << endl;
break;
case OUT_OF_TIME:
cout << "out of time" << endl;
break;
default:
cout << "Unknown status " << status << endl;
exit(1);
}
cout << "Episode " << episode << " ended with status: "
<< StatusToString(status) << std::endl;;
}
hfo.act(QUIT);
};
......@@ -9,19 +9,26 @@ using namespace hfo;
// This agent demonstrates the use of the MOVE_TO action to visit the
// corners of the play field. Before running this program, first Start
// HFO server: $./bin/HFO --offense-agents 1
// Server Connection Options. See printouts from bin/HFO.
feature_set_t features = HIGH_LEVEL_FEATURE_SET;
string config_dir = "bin/teams/base/config/formations-dt";
int unum = 11;
int port = 6001;
string server_addr = "localhost";
string team_name = "base_left";
bool goalie = false;
int main(int argc, char** argv) {
int port = 6000;
if (argc > 1) {
port = atoi(argv[1]);
}
// Create the HFO environment
HFOEnvironment hfo;
// Connect to the agent's server on port 6000 and request low-level
// feature set. See manual for more information on feature sets.
hfo.connectToAgentServer(port, HIGH_LEVEL_FEATURE_SET);
float target_x = 1.0;
float target_y = 1.0;
for (int episode=0; ; episode++) {
// Connect to the server and request feature set. See manual for
// more information on feature sets.
hfo.connectToServer(features, config_dir, unum, port, server_addr,
team_name, goalie);
float target_x = .82;
float target_y = .9;
for (int episode = 0; episode < 10; episode++) {
status_t status = IN_GAME;
if (episode % 2 != 0) {
target_x *= -1;
......@@ -37,6 +44,9 @@ int main(int argc, char** argv) {
// Advance the environment and get the game status
status = hfo.step();
}
// Check what the outcome of the episode was
cout << "Episode " << episode << " ended with status: "
<< StatusToString(status) << std::endl;;
}
hfo.act(QUIT);
};
#!/bin/bash
./bin/HFO --offense-agents=2 --no-sync &
sleep 1
./example/communication_agent 7 6000 &
sleep 1
./example/communication_agent 11 6000 &
# The magic line
# $$ holds the PID for this script
# Negation means kill by process group id instead of PID
trap "kill -TERM -$$" SIGINT
wait
from hfo import *
from .hfo import *
import socket, struct, thread, time, collections
from ctypes import *
import numpy as np
from numpy.ctypeslib import as_ctypes
import os
class HFO_Features:
''' An enum of the possible HFO feature sets. For descriptions see
https://github.com/mhauskn/HFO/blob/master/doc/manual.pdf
'''
LOW_LEVEL_FEATURE_SET, HIGH_LEVEL_FEATURE_SET = range(2)
hfo_lib = cdll.LoadLibrary(os.path.join(os.path.dirname(__file__),
'libhfo_c.so'))
''' Possible feature sets '''
LOW_LEVEL_FEATURE_SET, HIGH_LEVEL_FEATURE_SET = range(2)
class HFO_Actions:
''' An enum of the possible HFO actions
''' An enum of the possible HFO actions
[Low-Level] Dash(power, relative_direction)
[Low-Level] Turn(direction)
[Low-Level] Tackle(direction)
......@@ -24,174 +24,117 @@ class HFO_Actions:
[High-Level] Dribble(): Offensive dribble
[High-Level] Catch(): Catch the ball (Goalie Only)
NOOP(): Do Nothing
QUIT(): Quit the game
'''
DASH, TURN, TACKLE, KICK, KICK_TO, MOVE_TO, DRIBBLE_TO, INTERCEPT, \
QUIT(): Quit the game '''
DASH, TURN, TACKLE, KICK, KICK_TO, MOVE_TO, DRIBBLE_TO, INTERCEPT, \
MOVE, SHOOT, PASS, DRIBBLE, CATCH, NOOP, QUIT = range(15)
HFO_Player = collections.namedtuple("HFO_Player", "side unum")
class HFO_Status:
''' Current status of the HFO game. '''
IN_GAME, GOAL, CAPTURED_BY_DEFENSE, OUT_OF_BOUNDS, OUT_OF_TIME = range(5)
class HFO_SideID:
''' Team side for a player or object'''
RIGHT, NEUTRAL, LEFT = range(-1, 2)
''' Possible game status
[IN_GAME] Game is currently active
[GOAL] A goal has been scored by the offense
[CAPTURED_BY_DEFENSE] The defense has captured the ball
[OUT_OF_BOUNDS] Ball has gone out of bounds
[OUT_OF_TIME] Trial has ended due to time limit
[SERVER_DOWN] Server is not alive
'''
IN_GAME, GOAL, CAPTURED_BY_DEFENSE, OUT_OF_BOUNDS, OUT_OF_TIME, SERVER_DOWN = range(6)
''' Possible sides '''
RIGHT, NEUTRAL, LEFT = range(-1,2)
class Player(Structure): pass
Player._fields_ = [
('side', c_int),
('unum', c_int),
]
hfo_lib.HFO_new.argtypes = None
hfo_lib.HFO_new.restype = c_void_p
hfo_lib.HFO_del.argtypes = [c_void_p]
hfo_lib.HFO_del.restype = None
hfo_lib.connectToServer.argtypes = [c_void_p, c_int, c_char_p, c_int,
c_int, c_char_p, c_char_p, c_bool]
hfo_lib.connectToServer.restype = None
hfo_lib.getStateSize.argtypes = [c_void_p]
hfo_lib.getStateSize.restype = c_int
hfo_lib.getState.argtypes = [c_void_p, c_void_p]
hfo_lib.getState.restype = None
hfo_lib.act.argtypes = [c_void_p, c_int, c_void_p]
hfo_lib.act.restype = None
hfo_lib.say.argtypes = [c_void_p, c_char_p]
hfo_lib.say.restype = None
hfo_lib.hear.argtypes = [c_void_p]
hfo_lib.hear.restype = c_char_p
hfo_lib.playerOnBall.argtypes = [c_void_p]
hfo_lib.playerOnBall.restype = Player
hfo_lib.step.argtypes = [c_void_p]
hfo_lib.step.restype = c_int
hfo_lib.numParams.argtypes = [c_int]
hfo_lib.numParams.restype = c_int
hfo_lib.actionToString.argtypes = [c_int]
hfo_lib.actionToString.restype = c_char_p
hfo_lib.statusToString.argtypes = [c_int]
hfo_lib.statusToString.restype = c_char_p
class HFOEnvironment(object):
''' The HFOEnvironment is designed to be the main point of contact
between a learning agent and the Half-Field-Offense domain.
'''
def __init__(self):
self.socket = None # Socket connection to server
self.numFeatures = None # Given by the server in handshake
self.features = None # The state features
self.requested_action = None # Action to execute and parameters
self.say_msg = '' # Outgoing message to say
self.hear_msg = '' # Incoming heard message
self.player_on_ball = None # Current player holding the ball
def NumParams(self, action_type):
''' Returns the number of required parameters for each action type. '''
return {
HFO_Actions.DASH : 2,
HFO_Actions.TURN : 1,
HFO_Actions.TACKLE : 1,
HFO_Actions.KICK : 2,
HFO_Actions.KICK_TO : 3,
HFO_Actions.MOVE_TO : 2,
HFO_Actions.DRIBBLE_TO : 2,
HFO_Actions.INTERCEPT : 0,
HFO_Actions.MOVE : 0,
HFO_Actions.SHOOT : 0,
HFO_Actions.PASS : 1,
HFO_Actions.DRIBBLE : 0,
HFO_Actions.CATCH : 0,
HFO_Actions.NOOP : 0,
HFO_Actions.QUIT : 0}.get(action_type, -1);
def connectToAgentServer(self, server_port=6000,
feature_set=HFO_Features.HIGH_LEVEL_FEATURE_SET):
'''Connect to the server that controls the agent on the specified port. '''
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print '[Agent Client] Connecting to Agent Server on port', server_port
retry = 10
while retry > 0:
try:
self.socket.connect(('localhost', server_port))
except:
time.sleep(1)
retry -= 1
continue
else:
break
if retry <= 0:
print '[Agent Client] ERROR Unable to communicate with server'
exit(1)
print '[Agent Client] Connected'
self.handshakeAgentServer(feature_set)
# Get the initial state
state_data = self.socket.recv(struct.calcsize('f')*self.numFeatures)
if not state_data:
print '[Agent Client] ERROR Recieved bad data from Server. Perhaps server closed?'
self.cleanup()
exit(1)
self.features = struct.unpack('f'*self.numFeatures, state_data)
# Get first hear message
hearMsgLengthData = self.socket.recv(struct.calcsize('I'))
hearMsgLength = struct.unpack('I', hearMsgLengthData)[0]
if hearMsgLength > 0:
hearMsgData = self.socket.recv(struct.calcsize('c')*hearMsgLength)
self.hear_msg = struct.unpack(str(hearMsgLength)+'s', hearMsgData)[0]
def handshakeAgentServer(self, feature_set):
'''Handshake with the agent's server. '''
# Recieve float 123.2345
data = self.socket.recv(struct.calcsize("f"))
f = struct.unpack("f", data)[0]
assert abs(f - 123.2345) < 1e-4, "Float handshake failed"
# Send float 5432.321
self.socket.send(struct.pack("f", 5432.321))
# Send the feature set request
self.socket.send(struct.pack("i", feature_set))
# Recieve the number of features
data = self.socket.recv(struct.calcsize("i"))
self.numFeatures = struct.unpack("i", data)[0]
# Send what we recieved
self.socket.send(struct.pack("i", self.numFeatures))
# Get the current game status
data = self.socket.recv(struct.calcsize("iii"))
status, side, unum = struct.unpack("iii", data)
self.player_on_ball = HFO_Player(side,unum)
assert status == HFO_Status.IN_GAME, "Status check failed"
print '[Agent Client] Handshake complete'
def getState(self):
'''Get the current state of the world. Returns a list of floats with
size numFeatures. '''
return self.features
self.obj = hfo_lib.HFO_new()
def __del__(self):
hfo_lib.HFO_del(self.obj)
def connectToServer(self,
feature_set=LOW_LEVEL_FEATURE_SET,
config_dir='bin/teams/base/config/formations-dt',
uniform_number=11,
server_port=6000,
server_addr='localhost',
team_name='base_left',
play_goalie=False):
""" Connect to the server """
hfo_lib.connectToServer(self.obj, feature_set, config_dir, uniform_number,
server_port, server_addr, team_name, play_goalie)
def getStateSize(self):
""" Returns the number of state features """
return hfo_lib.getStateSize(self.obj)
def getState(self, state_data=None):
""" Returns the current state features """
if state_data is None:
state_data = np.zeros(self.getStateSize(), dtype=np.float32)
hfo_lib.getState(self.obj, as_ctypes(state_data))
return state_data
def act(self, *args):
''' Send an action and recieve the game status.'''
""" Performs an action in the environment """
assert len(args) > 0, 'Not enough arguments provided to act'
action_type = args[0]
n_params = self.NumParams(action_type)
n_params = hfo_lib.numParams(action_type)
assert n_params == len(args) - 1, 'Incorrect number of params to act: '\
'Required %d provided %d'%(n_params, len(args)-1)
self.requested_action = args
'Required %d, provided %d'%(n_params, len(args)-1)
hfo_lib.act(self.obj, action_type,
as_ctypes(np.asarray(args[1:], dtype=np.float32)))
def say(self, message):
''' Send a communication message to other agents. '''
self.say_msg = message
""" Transmit a message """
hfo_lib.say(self.obj, message)
def hear(self):
''' Receive incoming communications from other players. '''
return self.hear_msg
""" Returns the message heard from another player """
return hfo_lib.hear(self.obj)
def playerOnBall(self):
''' Get the current player holding the ball'''
return self.player_on_ball
""" Returns a player object who last touched the ball """
return hfo_lib.playerOnBall(self.obj)
def step(self):
''' Indicates the agent is done and the environment should
progress. Returns the game status after the step'''
# Send action and parameters
self.socket.send(struct.pack('i'+'f'*(len(self.requested_action)-1),
*self.requested_action))
# [Sanmit] Send self.say_msg
self.socket.send(struct.pack('I', len(self.say_msg)))
if len(self.say_msg) > 0:
self.socket.send(struct.pack(str(len(self.say_msg))+'s', self.say_msg))
self.say_msg = ''
# Get the current game status
data = self.socket.recv(struct.calcsize("iii"))
status, side, unum = struct.unpack("iii", data)
self.player_on_ball = HFO_Player(side,unum)
# Get the next state features
state_data = self.socket.recv(struct.calcsize('f')*self.numFeatures)
if not state_data:
print '[Agent Client] ERROR Recieved bad data from Server. Perhaps server closed?'
self.cleanup()
exit(1)
self.features = struct.unpack('f'*self.numFeatures, state_data)
self.hear_msg = ''
# [Sanmit] Receive self.hear_msg
hearMsgLengthData = self.socket.recv(struct.calcsize('I'))
hearMsgLength = struct.unpack('I', hearMsgLengthData)[0]
if hearMsgLength > 0:
hearMsgData = self.socket.recv(struct.calcsize('c')*hearMsgLength)
self.hear_msg = struct.unpack(str(hearMsgLength)+'s', hearMsgData)[0]
return status
def cleanup(self):
''' Send a quit and close the connection to the agent's server. '''
self.socket.send(struct.pack("i", HFO_Actions.QUIT))
self.socket.close()
""" Advances the state of the environment """
return hfo_lib.step(self.obj)
def actionToString(self, action):
""" Returns a string representation of an action """
return hfo_lib.actionToString(action)
def statusToString(self, status):
""" Returns a string representation of a game status """
return hfo_lib.statusToString(status)
#include "hfo_c_wrapper.h"
#ifndef __HFO_C_WRAPPER_H__
#define __HFO_C_WRAPPER_H__
#include <vector>
#include <HFO.hpp>
#include <common.hpp>
extern "C" {
hfo::HFOEnvironment* HFO_new() { return new hfo::HFOEnvironment(); }
void HFO_del(hfo::HFOEnvironment *hfo) { delete hfo; }
void connectToServer(hfo::HFOEnvironment *hfo,
hfo::feature_set_t feature_set,
char* config_dir,
int uniform_number,
int server_port,
char* server_addr,
char* team_name,
bool play_goalie) {
return hfo->connectToServer(feature_set, config_dir, uniform_number,
server_port, server_addr, team_name,
play_goalie);
}
int getStateSize(hfo::HFOEnvironment *hfo) { return hfo->getState().size(); }
void getState(hfo::HFOEnvironment *hfo, float *state_data) {
const float* state_features = hfo->getState().data();
memcpy(state_data, state_features, getStateSize(hfo) * sizeof(float));
}
void act(hfo::HFOEnvironment *hfo, hfo::action_t action, float* params) {
int n_params = NumParams(action);
std::vector<float> p(n_params);
for (int i=0; i<n_params; ++i) {
p[i] = params[i];
}
hfo->act(action, p);
}
void say(hfo::HFOEnvironment *hfo, const char *message) { hfo->say(message); }
const char* hear(hfo::HFOEnvironment *hfo) { return hfo->hear().c_str(); }
hfo::Player playerOnBall(hfo::HFOEnvironment *hfo) { return hfo->playerOnBall(); }
hfo::status_t step(hfo::HFOEnvironment *hfo) { return hfo->step(); }
int numParams(const hfo::action_t action) { return NumParams(action); }
const char* actionToString(const hfo::action_t action) {
return ActionToString(action).c_str();
}
const char* statusToString(const hfo::status_t status) {
return StatusToString(status).c_str();
}
}
#endif
import os
import setuptools
from distutils.core import setup, Extension
import os.path, sys
setuptools.setup(
name='hfo',
version='0.1.2',
packages=setuptools.find_packages(),
hfo_c_lib = 'hfo/libhfo_c.so'
if not os.path.isfile(hfo_c_lib):
print('ERROR: Unable to find required library: %s.'%(hfo_c_lib))
sys.exit()
module1 = Extension('hfo.hfo_c_wrapper',
libraries = ['hfo_c'],
include_dirs = ['src','src/chain_action','build/librcsc-prefix/src/librcsc'],
library_dirs = ['hfo'],
extra_compile_args=['-D__STDC_CONSTANT_MACROS'],
sources=['hfo/hfo_c_wrapper.cpp'])
setup(name = 'hfo',
version='0.1.5',
author='Matthew Hausknecht',
author_email='matthew.hausknecht@gmail.com',
description='Half Field Offense in 2D RoboCup Soccer.',
license='MIT',
ext_modules = [module1],
keywords=('Robocup '
'Half-field-offense '
),
......@@ -18,4 +28,7 @@ setuptools.setup(
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
],
)
# packages=find_packages(),
packages=['hfo'],
package_dir={'hfo': 'hfo'},
package_data={'hfo': ['libhfo_c.so']})
......@@ -9,359 +9,88 @@
#include <assert.h>
#include <netdb.h>
#include <iostream>
#include <sstream>
#include <stdarg.h>
#include <agent.h>
using namespace hfo;
std::string HFOEnvironment::ActionToString(Action action) {
std::stringstream ss;
switch (action.action) {
case DASH:
ss << "Dash(" << action.arg1 << "," << action.arg2 << ")";
break;
case TURN:
ss << "Turn(" << action.arg1 << ")";
break;
case TACKLE:
ss << "Tackle(" << action.arg1 << ")";
break;
case KICK:
ss << "Kick(" << action.arg1 << "," << action.arg2 << ")";
break;
case MOVE:
ss << "Move";
break;
case SHOOT:
ss << "Shoot";
break;
case PASS:
ss << "Pass";
break;
case DRIBBLE:
ss << "Dribble";
break;
case CATCH:
ss << "Catch";
break;
case NOOP:
ss << "No-op";
break;
case QUIT:
ss << "Quit";
break;
}
return ss.str();
};
int HFOEnvironment::NumParams(action_t action) {
switch (action) {
case DASH:
return 2;
case TURN:
return 1;
case TACKLE:
return 1;
case KICK:
return 2;
case KICK_TO:
return 3;
case MOVE_TO:
return 2;
case DRIBBLE_TO:
return 2;
case INTERCEPT:
return 0;
case MOVE:
return 0;
case SHOOT:
return 0;
case PASS:
return 1;
case DRIBBLE:
return 0;
case CATCH:
return 0;
case NOOP:
return 0;
case QUIT:
return 0;
}
std::cerr << "Unrecognized Action: " << action;
return -1;
}
bool HFOEnvironment::ParseConfig(const std::string& message, Config& config) {
config.num_offense = -1;
config.num_defense = -1;
std::istringstream iss(message);
std::string header = "HFO_SETUP";
std::string key, val;
iss >> key;
if (header.compare(key) != 0) {
std::cerr << "Got unexpected message header: " << header;
return false;
}
while (iss >> key) {
if (key.compare("offense_name") == 0) {
iss >> config.offense_team_name;
} else if (key.compare("defense_name") == 0) {
iss >> config.defense_team_name;
} else if (key.compare("num_offense") == 0) {
iss >> val;
config.num_offense = strtol(val.c_str(), NULL, 0);
} else if (key.compare("num_defense") == 0) {
iss >> val;
config.num_defense = strtol(val.c_str(), NULL, 0);
} else if (key.compare("offense_nums") == 0) {
assert(config.num_offense >= 0);
for (int i=0; i<config.num_offense; ++i) {
iss >> val;
config.offense_nums.push_back(strtol(val.c_str(), NULL, 0));
}
} else if (key.compare("defense_nums") == 0) {
assert(config.num_defense >= 0);
for (int i=0; i<config.num_defense; ++i) {
iss >> val;
config.defense_nums.push_back(strtol(val.c_str(), NULL, 0));
}
} else {
std::cerr << "Unrecognized key: " << key << std::endl;
return false;
}
}
assert(config.offense_nums.size() == config.num_offense);
assert(config.defense_nums.size() == config.num_defense);
return true;
};
HFOEnvironment::HFOEnvironment() {}
HFOEnvironment::~HFOEnvironment() {
// Send a quit action and close the connection to the agent's server
action_t quit = QUIT;
if (send(sockfd, &quit, sizeof(int), 0) < 0) {
perror("[Agent Client] ERROR sending from socket");
}
close(sockfd);
exit(1);
}
void HFOEnvironment::connectToAgentServer(int server_port,
feature_set_t feature_set) {
std::cout << "[Agent Client] Connecting to Agent Server on port "
<< server_port << std::endl;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
struct hostent *server = gethostbyname("localhost");
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(1);
}
struct sockaddr_in serv_addr;
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(server_port);
int status = -1;
int retry = 10;
while (status < 0 && retry > 0) {
status = connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr));
sleep(1);
retry--;
}
if (status < 0) {
perror("[Agent Client] ERROR Unable to communicate with server");
close(sockfd);
exit(1);
}
std::cout << "[Agent Client] Connected" << std::endl;
handshakeAgentServer(feature_set);
// Get the initial game state
feature_vec.resize(numFeatures);
if (recv(sockfd, &(feature_vec.front()), numFeatures*sizeof(float), 0) < 0) {
perror("[Agent Client] ERROR recieving state features from socket");
close(sockfd);
exit(1);
}
// First get the length of the hear message
uint32_t msgLength;
if (recv(sockfd, &msgLength, sizeof(uint32_t), 0) < 0){
perror("[Agent Client] ERROR recieving hear message length from socket");
close(sockfd);
exit(1);
}
// Next, receive the message
if (msgLength > 0){
std::vector<char> hearMsgBuffer;
hearMsgBuffer.resize(msgLength);
if (recv(sockfd, &hearMsgBuffer[0], msgLength, 0) < 0){
perror("[Agent Client] ERROR recieving hear message from socket");
close(sockfd);
exit(1);
}
hear_msg.assign(&(hearMsgBuffer[0]), hearMsgBuffer.size());
void HFOEnvironment::connectToServer(feature_set_t feature_set,
std::string config_dir,
int uniform_number,
int server_port,
std::string server_addr,
std::string team_name,
bool play_goalie) {
agent.setFeatureSet(feature_set);
rcsc::PlayerConfig& config = agent.mutable_config();
config.setConfigDir(config_dir);
config.setPlayerNumber(uniform_number);
config.setPort(server_port);
config.setHost(server_addr);
config.setTeamName(team_name);
config.setGoalie(play_goalie);
if (!agent.init(&client, 0, NULL)) {
std::cerr << "Init failed" << std::endl;
exit(1);
}
client.setClientMode(rcsc::BasicClient::ONLINE);
if (!client.startAgent(&agent)) {
std::cerr << "Unable to start agent" << std::endl;
exit(1);
}
assert(client.isServerAlive() == true);
while (agent.getState().empty()) {
step();
}
}
void HFOEnvironment::handshakeAgentServer(feature_set_t feature_set) {
// Recieve float 123.2345
float f;
if (recv(sockfd, &f, sizeof(float), 0) < 0) {
perror("[Agent Client] ERROR recv from socket");
close(sockfd);
exit(1);
}
// Check that error is within bounds
if (abs(f - 123.2345) > 1e-4) {
perror("[Agent Client] Handshake failed. Improper float recieved.");
close(sockfd);
exit(1);
}
// Send float 5432.321
f = 5432.321;
if (send(sockfd, &f, sizeof(float), 0) < 0) {
perror("[Agent Client] ERROR sending from socket");
close(sockfd);
exit(1);
}
// Send the feature set request
if (send(sockfd, &feature_set, sizeof(int), 0) < 0) {
perror("[Agent Client] ERROR sending from socket");
close(sockfd);
exit(1);
}
// Recieve the number of features
if (recv(sockfd, &numFeatures, sizeof(int), 0) < 0) {
perror("[Agent Client] ERROR recv from socket");
close(sockfd);
exit(1);
}
if (send(sockfd, &numFeatures, sizeof(int), 0) < 0) {
perror("[Agent Client] ERROR sending from socket");
close(sockfd);
exit(1);
}
// Recieve the game status
int game_status[3];
if (recv(sockfd, &(game_status[0]), 3 * sizeof(int), 0) < 0) {
perror("[Agent Client] ERROR receiving game status from socket");
close(sockfd);
exit(1);
}
if (game_status[0] != IN_GAME) {
std::cout << "[Agent Client] Handshake failed: status check." << std::endl;
close(sockfd);
exit(1);
}
player_on_ball.side = (SideID)game_status[1];
player_on_ball.unum = game_status[2];
std::cout << "[Agent Client] Handshake complete" << std::endl;
}
const std::vector<float>& HFOEnvironment::getState() {
return feature_vec;
return agent.getState();
}
void HFOEnvironment::act(action_t action, ...) {
requested_action = action;
agent.setAction(action);
int n_args = NumParams(action);
if (n_args > action_params.size()) {
action_params.resize(n_args);
}
std::vector<float> *params = agent.mutable_params();
params->resize(n_args);
va_list vl;
va_start(vl, action);
for (int i = 0; i < n_args; ++i) {
action_params[i] = va_arg(vl, double);
(*params)[i] = va_arg(vl, double);
}
va_end(vl);
}
void HFOEnvironment::act(action_t action, const std::vector<float>& params) {
agent.setAction(action);
int n_args = NumParams(action);
assert(n_args == params.size());
std::vector<float> *agent_params = agent.mutable_params();
(*agent_params) = params;
}
void HFOEnvironment::say(const std::string& message) {
say_msg = message;
agent.setSayMsg(message);
}
std::string HFOEnvironment::hear() {
return hear_msg;
return agent.getHearMsg();
}
Player HFOEnvironment::playerOnBall() {
return player_on_ball;
return agent.getPlayerOnBall();
}
status_t HFOEnvironment::step() {
status_t game_status;
// Send the action_type
if (send(sockfd, &requested_action, sizeof(action_t), 0) < 0) {
perror("[Agent Client] ERROR sending from socket");
close(sockfd);
exit(1);
}
// Send the arguments
int n_args = NumParams(requested_action);
if (n_args > 0) {
if (send(sockfd, action_params.data(), sizeof(float) * n_args, 0) < 0) {
perror("[Agent Client] ERROR sending from socket");
close(sockfd);
exit(1);
}
}
// Send the message length
uint32_t sendMsgLength = say_msg.size();
if (send(sockfd, &sendMsgLength, sizeof(uint32_t), 0) < 0) {
perror("[Agent Client] ERROR sending from socket");
close(sockfd);
exit(1);
}
// Send the say message
if (sendMsgLength > 0) {
if (send(sockfd, say_msg.c_str(), say_msg.size(), 0) < 0) {
perror("[Agent Client] ERROR sending from socket");
close(sockfd);
exit(1);
}
}
// Clear say message buffer
say_msg.clear();
// Get the game status
int full_status[3];
if (recv(sockfd, &(full_status[0]), 3 * sizeof(int), 0) < 0) {
perror("[Agent Client] ERROR receiving game status from socket");
close(sockfd);
exit(1);
}
game_status = (status_t)full_status[0];
player_on_ball.side = (SideID)full_status[1];
player_on_ball.unum = full_status[2];
// Get the next game state
if (recv(sockfd, &(feature_vec.front()), numFeatures * sizeof(float), 0) < 0) {
perror("[Agent Client] ERROR receiving state features from socket");
close(sockfd);
exit(1);
}
// Clear last message
hear_msg.clear();
// Receive message length
uint32_t msgLength;
if (recv(sockfd, &msgLength, sizeof(uint32_t), 0) < 0) {
perror("[Agent Client] ERROR receiving hear message length from socket");
close(sockfd);
exit(1);
}
// Receive the message
if (msgLength > 0) {
std::vector<char> hearMsgBuffer;
hearMsgBuffer.resize(msgLength);
if (recv(sockfd, &hearMsgBuffer[0], msgLength, 0) < 0) {
perror("[Agent Client] ERROR receiving hear message from socket");
close(sockfd);
exit(1);
bool end_of_trial = agent.getGameStatus() != IN_GAME;
long start_cycle = agent.cycle();
while ((agent.cycle() <= start_cycle)
|| (agent.getLastDecisionTime() < agent.cycle())
|| (end_of_trial && agent.getGameStatus() != IN_GAME)) {
client.runStep(&agent);
if (!client.isServerAlive()) {
return SERVER_DOWN;
}
hear_msg.assign(&(hearMsgBuffer[0]), hearMsgBuffer.size());
}
return game_status;
return agent.getGameStatus();
}
......@@ -3,101 +3,45 @@
#include <string>
#include <vector>
#include <rcsc/common/basic_client.h>
#include <rcsc/player/player_config.h>
#include <rcsc/player/world_model.h>
#include "agent.h"
#include "common.hpp"
namespace hfo {
// For descriptions of the different feature sets see
// https://github.com/mhauskn/HFO/blob/master/doc/manual.pdf
enum feature_set_t
{
LOW_LEVEL_FEATURE_SET,
HIGH_LEVEL_FEATURE_SET
};
// The actions available to the agent
enum action_t
{
DASH, // [Low-Level] Dash(power [0,100], direction [-180,180])
TURN, // [Low-Level] Turn(direction [-180,180])
TACKLE, // [Low-Level] Tackle(direction [-180,180])
KICK, // [Low-Level] Kick(power [0,100], direction [-180,180])
KICK_TO, // [Mid-Level] Kick_To(target_x [-1,1], target_y [-1,1], speed [0,3])
MOVE_TO, // [Mid-Level] Move(target_x [-1,1], target_y [-1,1])
DRIBBLE_TO, // [Mid-Level] Dribble(target_x [-1,1], target_y [-1,1])
INTERCEPT, // [Mid-Level] Intercept(): Intercept the ball
MOVE, // [High-Level] Move(): Reposition player according to strategy
SHOOT, // [High-Level] Shoot(): Shoot the ball
PASS, // [High-Level] Pass(teammate_unum [0,11]): Pass to the most open teammate
DRIBBLE, // [High-Level] Dribble(): Offensive dribble
CATCH, // [High-Level] Catch(): Catch the ball (Goalie only!)
NOOP, // Do nothing
QUIT // Special action to quit the game
};
// An Action consists of the discreet action as well as required
// arguments (parameters).
struct Action {
action_t action;
float arg1;
float arg2;
};
// Status of a HFO game
enum status_t
{
IN_GAME, // Game is currently active
GOAL, // A goal has been scored by the offense
CAPTURED_BY_DEFENSE, // The defense has captured the ball
OUT_OF_BOUNDS, // Ball has gone out of bounds
OUT_OF_TIME // Trial has ended due to time limit
};
enum SideID {
RIGHT = -1,
NEUTRAL = 0,
LEFT = 1
};
// Configuration of the HFO domain including the team names and player
// numbers for each team. This struct is populated by ParseConfig().
struct Config {
std::string offense_team_name;
std::string defense_team_name;
int num_offense; // Number of offensive players
int num_defense; // Number of defensive players
std::vector<int> offense_nums; // Offensive player numbers
std::vector<int> defense_nums; // Defensive player numbers
};
struct Player {
SideID side;
int unum;
};
class HFOEnvironment {
public:
HFOEnvironment();
~HFOEnvironment();
// Returns a string representation of an action.
static std::string ActionToString(Action action);
// Get the number of parameters needed for a action.
static int NumParams(action_t action);
// Parse a Trainer message to populate config. Returns a bool
// indicating if the struct was correctly parsed.
static bool ParseConfig(const std::string& message, Config& config);
// Connect to the server that controls the agent on the specified port.
void connectToAgentServer(int server_port=6000,
feature_set_t feature_set=HIGH_LEVEL_FEATURE_SET);
HFOEnvironment() {};
virtual ~HFOEnvironment() {};
/**
* Connect to the server on the specified port. The
* following information is provided by the ./bin/HFO
*
* feature_set: High or low level state features
* config_dir: Config directory. Typically HFO/bin/teams/base/config/
* uniform_number: player's uniform number.
* server_port: port to connect to server on
* server_addr: address of server
* team_name: Name of team to join.
* play_goalie: is this player the goalie
*/
void connectToServer(feature_set_t feature_set=HIGH_LEVEL_FEATURE_SET,
std::string config_dir="bin/teams/base/config/formations-dt",
int uniform_number=11,
int server_port=6000,
std::string server_addr="localhost",
std::string team_name="base_left",
bool play_goalie=false);
// Get the current state of the domain. Returns a reference to feature_vec.
const std::vector<float>& getState();
// Specify action type to take followed by parameters for that action
virtual void act(action_t action, ...);
virtual void act(action_t action, const std::vector<float>& params);
// Send/receive communication from other agents
virtual void say(const std::string& message);
......@@ -111,17 +55,8 @@ class HFOEnvironment {
virtual status_t step();
protected:
int numFeatures; // The number of features in this domain
int sockfd; // Socket file desriptor for connection to agent server
std::vector<float> feature_vec; // Holds the features
action_t requested_action; // Action requested
std::vector<float> action_params; // Action parameters
std::string say_msg, hear_msg; // Messages to hear/say
Player player_on_ball;
// Handshake with the agent server to ensure data is being correctly
// passed. Also sets the number of features to expect.
virtual void handshakeAgentServer(feature_set_t feature_set);
rcsc::BasicClient client;
Agent agent;
};
} // namespace hfo
......
// -*-c++-*-
/*
*Copyright:
Copyright (C) Hidehisa AKIYAMA
This code is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this code; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*EndCopyright:
*/
/////////////////////////////////////////////////////////////////////
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
......@@ -121,13 +95,13 @@ Agent::Agent()
M_communication(),
M_field_evaluator(createFieldEvaluator()),
M_action_generator(createActionGenerator()),
feature_set(LOW_LEVEL_FEATURE_SET),
feature_extractor(NULL),
lastTrainerMessageTime(-1),
lastTeammateMessageTime(-1),
server_port(6008),
client_connected(false),
num_teammates(-1),
num_opponents(-1),
playing_offense(false)
lastDecisionTime(-1),
game_status(IN_GAME),
action(NOOP)
{
boost::shared_ptr< AudioMemory > audio_memory( new AudioMemory );
......@@ -176,10 +150,9 @@ Agent::Agent()
}
Agent::~Agent() {
if (feature_extractor != NULL) {
delete feature_extractor;
std::cout << "[Agent Server] Closing Server." << std::endl;
close(newsockfd);
close(sockfd);
}
}
bool Agent::initImpl(CmdLineParser & cmd_parser) {
......@@ -189,11 +162,6 @@ bool Agent::initImpl(CmdLineParser & cmd_parser) {
result &= Strategy::instance().init(cmd_parser);
rcsc::ParamMap my_params("Additional options");
my_params.add()
("numTeammates", "", &num_teammates)
("numOpponents", "", &num_opponents)
("playingOffense", "", &playing_offense)
("serverPort", "", &server_port);
cmd_parser.parse(my_params);
if (cmd_parser.count("help") > 0) {
my_params.printHelp(std::cout);
......@@ -233,123 +201,9 @@ bool Agent::initImpl(CmdLineParser & cmd_parser) {
<< std::endl;
}
assert(num_teammates >= 0);
assert(num_opponents >= 0);
startServer(server_port);
return true;
}
void Agent::startServer(int server_port) {
std::cout << "[Agent Server] Starting Server on Port " << server_port << std::endl;
struct sockaddr_in serv_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("[Agent Server] ERROR opening socket");
exit(1);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(server_port);
int reuse = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
(const char*)&reuse, sizeof(reuse)) < 0) {
perror("[Agent Server] setsockopt(SO_REUSEADDR) failed");
exit(1);
}
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("[Agent Server] ERROR on binding");
exit(1);
}
listen(sockfd, 5);
}
void Agent::listenForConnection() {
int rv;
struct pollfd ufd;
ufd.fd = sockfd;
ufd.events = POLLIN;
rv = poll(&ufd, 1, 1000);
if (rv == -1) {
perror("poll"); // error occurred in poll()
} else if (rv == 0) {
std::cout << "[Agent Server] Waiting for client to connect... " << std::endl;
} else {
if (ufd.revents & POLLIN) {
struct sockaddr_in cli_addr;
socklen_t clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
perror("[Agent Server] ERROR on accept");
close(sockfd);
exit(1);
}
std::cout << "[Agent Server] Connected" << std::endl;
clientHandshake();
}
}
}
void Agent::clientHandshake() {
// Send float 123.2345
float f = 123.2345;
if (send(newsockfd, &f, sizeof(float), 0) < 0) {
perror("[Agent Server] ERROR sending from socket");
close(sockfd);
exit(1);
}
// Recieve float 5432.321
if (recv(newsockfd, &f, sizeof(float), 0) < 0) {
perror("[Agent Server] ERROR recv from socket");
close(sockfd);
exit(1);
}
// Check that error is within bounds
if (abs(f - 5432.321) > 1e-4) {
perror("[Agent Server] Handshake failed. Improper float recieved.");
close(sockfd);
exit(1);
}
// Recieve the feature set to use
hfo::feature_set_t feature_set;
if (recv(newsockfd, &feature_set, sizeof(int), 0) < 0) {
perror("[Agent Server] PERROR recv from socket");
close(sockfd);
exit(1);
}
// Create the corresponding FeatureExtractor
if (feature_extractor != NULL) {
delete feature_extractor;
}
feature_extractor = getFeatureExtractor(feature_set, num_teammates,
num_opponents, playing_offense);
// Send the number of features
int numFeatures = feature_extractor->getNumFeatures();
assert(numFeatures > 0);
if (send(newsockfd, &numFeatures, sizeof(int), 0) < 0) {
perror("[Agent Server] ERROR sending from socket");
close(sockfd);
exit(1);
}
// Check that client has recieved correctly
int client_response = -1;
if (recv(newsockfd, &client_response, sizeof(int), 0) < 0) {
perror("[Agent Server] ERROR recv from socket");
close(sockfd);
exit(1);
}
if (client_response != numFeatures) {
perror("[Agent Server] Client incorrectly parsed the number of features.");
close(sockfd);
exit(1);
}
std::cout << "[Agent Server] Handshake complete" << std::endl;
client_connected = true;
rcsc::FreeMessage<5> *free_msg = new FreeMessage<5>("ready");
addSayMessage(free_msg);
}
FeatureExtractor* Agent::getFeatureExtractor(feature_set_t feature_set_indx,
int num_teammates,
int num_opponents,
......@@ -370,188 +224,64 @@ FeatureExtractor* Agent::getFeatureExtractor(feature_set_t feature_set_indx,
}
}
std::vector<int> Agent::getGameStatus(const rcsc::AudioSensor& audio_sensor,
long& lastTrainerMessageTime) {
std::vector<int> status;
status_t game_status = IN_GAME;
int playerIndex = -1; // Keeps track of which defender stopped the shot
int playerTeam = hfo::LEFT;
if (audio_sensor.trainerMessageTime().cycle() > lastTrainerMessageTime) {
const std::string& message = audio_sensor.trainerMessage();
bool recognized_message = true;
if (message.find("GOAL") != std::string::npos){
playerIndex = atoi((message.substr(message.find("-")+1)).c_str());
playerTeam = hfo::LEFT;
game_status = GOAL;
} else if (message.find("CAPTURED_BY_DEFENSE") != std::string::npos) {
playerIndex = atoi((message.substr(message.find("-")+1)).c_str());
playerTeam = hfo::RIGHT;
game_status = CAPTURED_BY_DEFENSE;
} else if (message.compare("OUT_OF_BOUNDS") == 0) {
game_status = OUT_OF_BOUNDS;
} else if (message.compare("OUT_OF_TIME") == 0) {
game_status = OUT_OF_TIME;
} else if (message.find("IN_GAME") != std::string::npos){
switch (message.at(message.find("-")+1)){
case 'L':
playerTeam = hfo::LEFT;
break;
case 'R':
playerTeam = hfo::RIGHT;
break;
case 'U':
playerTeam = hfo::NEUTRAL;
break;
}
playerIndex = atoi((message.substr(message.find("-")+2)).c_str());
}
else {
recognized_message = false;
}
if (recognized_message) {
lastTrainerMessageTime = audio_sensor.trainerMessageTime().cycle();
}
}
status.push_back(game_status);
status.push_back(playerTeam);
status.push_back(playerIndex);
return status;
}
/*!
main decision
virtual method in super class
*/
void Agent::actionImpl() {
lastDecisionTime = world().time().cycle();
// For now let's not worry about turning the neck or setting the vision.
this->setViewAction(new View_Tactical());
this->setNeckAction(new Neck_TurnToBallOrScan());
if (!client_connected) {
listenForConnection();
return;
// Process new trainer messages
if (audioSensor().trainerMessageTime().cycle() > lastTrainerMessageTime) {
const std::string& message = audioSensor().trainerMessage();
if (feature_extractor == NULL) {
hfo::Config hfo_config;
if (hfo::ParseConfig(message, hfo_config)) {
bool playing_offense = world().ourSide() == rcsc::LEFT;
int num_teammates = playing_offense ?
hfo_config.num_offense - 1 : hfo_config.num_defense - 1;
int num_opponents = playing_offense ?
hfo_config.num_defense : hfo_config.num_offense;
feature_extractor = getFeatureExtractor(
feature_set, num_teammates, num_opponents, playing_offense);
}
}
hfo::ParseGameStatus(message, game_status);
hfo::ParsePlayerOnBall(message, player_on_ball);
lastTrainerMessageTime = audioSensor().trainerMessageTime().cycle();
}
// Process new teammate message
hear_msg.clear();
if (audioSensor().teammateMessageTime().cycle() > lastTeammateMessageTime) {
const std::list<HearMessage> teammateMessages = audioSensor().teammateMessages();
for (std::list<HearMessage>::const_iterator msgIter = teammateMessages.begin();
msgIter != teammateMessages.end(); msgIter++) {
if ((*msgIter).unum_ != world().self().unum()) {
hear_msg = (*msgIter).str_;
break; // For now we just take one.
}
}
lastTeammateMessageTime = audioSensor().teammateMessageTime().cycle();
}
// Update and send the game status
std::vector<int> game_status = getGameStatus(audioSensor(), lastTrainerMessageTime);
if (send(newsockfd, &(game_status.front()), game_status.size() * sizeof(int), 0) < 0){
perror("[Agent Server] ERROR sending game state from socket");
close(sockfd);
exit(1);
}
// Update and send the state features
const std::vector<float>& features =
feature_extractor->ExtractFeatures(this->world());
#ifdef ELOG
if (config().record()) {
elog.addText(Logger::WORLD, "GameStatus %d", game_status[0]);
if (config().record() && feature_extractor != NULL) {
elog.addText(Logger::WORLD, "GameStatus %d", game_status);
elog.flush();
feature_extractor->LogFeatures();
}
#endif
if (send(newsockfd, &(features.front()),
features.size() * sizeof(float), 0) < 0) {
perror("[Agent Server] ERROR sending state features from socket");
close(sockfd);
exit(1);
}
// [Sanmit] Send the communication heard by the agent
// Hear for teammate messages and send them via socket to the HFO interface.
std::string teammateMessage = "";
// Received a new message
if (audioSensor().teammateMessageTime().cycle() > lastTeammateMessageTime){
// Receive all teammate messages
std::list<HearMessage> teammateMessages = audioSensor().teammateMessages();
for (std::list<HearMessage>::iterator msgIterator = teammateMessages.begin(); msgIterator != teammateMessages.end(); msgIterator++){
if ((*msgIterator).unum_ != world().self().unum()){
teammateMessage = (*msgIterator).str_;
break; // For now we just take one. Remove this and concatenate messages if desired -- though technically, agents should only be able to hear one message.
}
}
}
// Send message size
uint32_t hearMsgLength = teammateMessage.size();
if (send(newsockfd, &hearMsgLength, sizeof(uint32_t), 0) < 0){
perror("[Agent Server] ERROR sending hear message length from socket");
close(sockfd);
exit(1);
}
// Send message
if (hearMsgLength > 0){
if (send(newsockfd, teammateMessage.c_str(), teammateMessage.size(), 0) < 0){
perror("[Agent Server] ERROR sending hear message from socket");
close(sockfd);
exit(1);
}
}
// Get the action type
action_t action;
if (recv(newsockfd, &action, sizeof(action_t), 0) < 0) {
perror("[Agent Server] ERROR recv from socket");
close(sockfd);
exit(1);
}
// Get the parameters for that action
int n_args = HFOEnvironment::NumParams(action);
float params[n_args];
if (n_args > 0) {
if (recv(newsockfd, &params, sizeof(float)*n_args, 0) < 0) {
perror("[Agent Server] ERROR recv from socket");
close(sockfd);
exit(1);
}
}
// [Sanmit] Receive the outgoing communication
// Receive message length
uint32_t sayMsgLength;
if (recv(newsockfd, &sayMsgLength, sizeof(uint32_t), 0) < 0){
perror("[Agent Server] ERROR recv size of say message from socket");
close(sockfd);
exit(1);
}
// Receive message
std::vector<char> sayMsgBuffer;
sayMsgBuffer.resize(sayMsgLength);
std::string msgString = "";
// Check message size
if (sayMsgLength > ServerParam::i().playerSayMsgSize()){
perror("[Agent Server] ERROR message size too large. Increase size by starting bin/HFO with larger --messageSize argument");
close(sockfd);
exit(1);
}
if (sayMsgLength > 0) {
if (recv(newsockfd, &sayMsgBuffer[0], sayMsgLength, 0) < 0){
perror("[Agent Server] ERROR recv say message from socket");
close(sockfd);
exit(1);
// Update state features
if (feature_extractor != NULL) {
state = feature_extractor->ExtractFeatures(this->world());
}
msgString.assign(&(sayMsgBuffer[0]),sayMsgBuffer.size());
// [Sanmit] "Say" in the actual game
addSayMessage(new CustomMessage(msgString));
}
if (action == SHOOT) {
const ShootGenerator::Container & cont =
ShootGenerator::instance().courses(this->world(), false);
ShootGenerator::Container::const_iterator best_shoot
= std::min_element(cont.begin(), cont.end(), ShootGenerator::ScoreCmp());
Body_SmartKick(best_shoot->target_point_, best_shoot->first_ball_speed_,
best_shoot->first_ball_speed_ * 0.99, 3).execute(this);
} else if (action == PASS) {
Force_Pass pass;
int receiver = int(params[0]);
pass.get_pass_to_player(this->world(), receiver);
pass.execute(this);
if (!say_msg.empty()) {
addSayMessage(new CustomMessage(say_msg));
say_msg.clear();
}
// Execute the action
assert(hfo::NumParams(action) <= params.size());
switch(action) {
case DASH:
this->doDash(params[0], params[1]);
......@@ -566,19 +296,25 @@ void Agent::actionImpl() {
this->doKick(params[0], params[1]);
break;
case KICK_TO:
if (feature_extractor != NULL) {
Body_SmartKick(Vector2D(feature_extractor->absoluteXPos(params[0]),
feature_extractor->absoluteYPos(params[1])),
params[2], params[2] * 0.99, 3).execute(this);
}
break;
case MOVE_TO:
if (feature_extractor != NULL) {
Body_GoToPoint(Vector2D(feature_extractor->absoluteXPos(params[0]),
feature_extractor->absoluteYPos(params[1])), 0.25,
ServerParam::i().maxDashPower()).execute(this);
}
break;
case DRIBBLE_TO:
if (feature_extractor != NULL) {
Body_Dribble(Vector2D(feature_extractor->absoluteXPos(params[0]),
feature_extractor->absoluteYPos(params[1])), 1.0,
ServerParam::i().maxDashPower(), 2).execute(this);
}
break;
case INTERCEPT:
Body_Intercept().execute(this);
......@@ -587,8 +323,11 @@ void Agent::actionImpl() {
this->doMove();
break;
case SHOOT:
this->doSmartKick();
break;
case PASS:
std::cout << "Inagent pass: 0: " << params[0] << std::endl;
this->doPassTo(int(params[0]));
break;
case DRIBBLE:
this->doDribble();
......@@ -600,12 +339,10 @@ void Agent::actionImpl() {
break;
case QUIT:
std::cout << "[Agent Server] Got quit from agent." << std::endl;
close(sockfd);
exit(0);
default:
std::cerr << "[Agent Server] ERROR Unsupported Action: "
<< action << std::endl;
close(sockfd);
exit(1);
}
}
......@@ -795,11 +532,12 @@ Agent::handlePlayerType()
void
Agent::communicationImpl()
{
if ( M_communication )
{
// [Sanmit]: Turning this off since it adds default communication messages which can conflict with our comm messages.
// Disabled since it adds default communication messages which
// can conflict with our comm messages.
// if ( M_communication )
// {
// M_communication->execute( this );
}
// }
}
/*-------------------------------------------------------------------*/
......@@ -943,6 +681,18 @@ Agent::doShoot()
return false;
}
bool
Agent::doSmartKick()
{
const ShootGenerator::Container & cont =
ShootGenerator::instance().courses(this->world(), false);
ShootGenerator::Container::const_iterator best_shoot
= std::min_element(cont.begin(), cont.end(), ShootGenerator::ScoreCmp());
Body_SmartKick(best_shoot->target_point_, best_shoot->first_ball_speed_,
best_shoot->first_ball_speed_ * 0.99, 3).execute(this);
return true;
}
/*-------------------------------------------------------------------*/
/*!
......@@ -957,6 +707,15 @@ Agent::doPass()
return true;
}
bool
Agent::doPassTo(int receiver)
{
Force_Pass pass;
pass.get_pass_to_player(this->world(), receiver);
pass.execute(this);
return true;
}
/*-------------------------------------------------------------------*/
/*!
......
// -*-c++-*-
/*
*Copyright:
Copyright (C) Hidehisa AKIYAMA
This code is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this code; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*EndCopyright:
*/
/////////////////////////////////////////////////////////////////////
#ifndef AGENT_H
#define AGENT_H
#include "action_generator.h"
#include "field_evaluator.h"
#include "communication.h"
#include "HFO.hpp"
#include "feature_extractor.h"
#include "common.hpp"
#include <rcsc/player/player_agent.h>
#include <vector>
......@@ -42,10 +16,6 @@ public:
virtual ~Agent();
virtual FieldEvaluator::ConstPtr getFieldEvaluator() const;
// Get the current game status, and the side and uniform number of the player holding the ball
static std::vector<int> getGameStatus(const rcsc::AudioSensor& audio_sensor,
long& lastTrainerMessageTime);
// Returns the feature extractor corresponding to the feature_set_t
static FeatureExtractor* getFeatureExtractor(hfo::feature_set_t feature_set,
int num_teammates,
......@@ -70,26 +40,38 @@ protected:
virtual FieldEvaluator::ConstPtr createFieldEvaluator() const;
virtual ActionGenerator::ConstPtr createActionGenerator() const;
// Start the server and listen for a connection.
void startServer(int server_port=6008);
void listenForConnection();
// Transmit information to the client and ensure it can recieve.
void clientHandshake();
protected:
FeatureExtractor* feature_extractor;
hfo::feature_set_t feature_set; // Requested feature set
FeatureExtractor* feature_extractor; // Extracts the features
long lastTrainerMessageTime; // Last time the trainer sent a message
long lastTeammateMessageTime; // Last time a teammate sent a message
int server_port; // Port to start the server on
bool client_connected; // Has the client connected and handshake?
int sockfd, newsockfd; // Server sockets
int num_teammates, num_opponents;
bool playing_offense;
long lastDecisionTime; // Last time we made a decision
hfo::status_t game_status; // Current status of the game
hfo::Player player_on_ball; // Player in posession of the ball
std::vector<float> state; // Vector of current state features
std::string say_msg, hear_msg; // Messages to/from teammates
hfo::action_t action; // Currently requested action
std::vector<float> params; // Parameters of current action
public:
inline const std::vector<float>& getState() { return state; }
inline hfo::status_t getGameStatus() { return game_status; }
inline const hfo::Player& getPlayerOnBall() { return player_on_ball; }
inline const std::string& getHearMsg() { return hear_msg; }
inline long cycle() { return world().time().cycle(); }
inline long getLastDecisionTime() { return lastDecisionTime; }
inline void setFeatureSet(hfo::feature_set_t fset) { feature_set = fset; }
inline std::vector<float>* mutable_params() { return &params; }
inline void setAction(hfo::action_t a) { action = a; }
inline void setSayMsg(const std::string& message) { say_msg = message; }
private:
bool doPreprocess();
bool doSmartKick();
bool doShoot();
bool doPass();
bool doPassTo(int receiver);
bool doDribble();
bool doMove();
bool doForceKick();
......
#ifndef __COMMON_HPP__
#define __COMMON_HPP__
#include <sstream>
namespace hfo {
// For descriptions of the different feature sets see
// https://github.com/mhauskn/HFO/blob/master/doc/manual.pdf
enum feature_set_t
{
LOW_LEVEL_FEATURE_SET,
HIGH_LEVEL_FEATURE_SET
};
// The actions available to the agent
enum action_t
{
DASH, // [Low-Level] Dash(power [0,100], direction [-180,180])
TURN, // [Low-Level] Turn(direction [-180,180])
TACKLE, // [Low-Level] Tackle(direction [-180,180])
KICK, // [Low-Level] Kick(power [0,100], direction [-180,180])
KICK_TO, // [Mid-Level] Kick_To(target_x [-1,1], target_y [-1,1], speed [0,3])
MOVE_TO, // [Mid-Level] Move(target_x [-1,1], target_y [-1,1])
DRIBBLE_TO, // [Mid-Level] Dribble(target_x [-1,1], target_y [-1,1])
INTERCEPT, // [Mid-Level] Intercept(): Intercept the ball
MOVE, // [High-Level] Move(): Reposition player according to strategy
SHOOT, // [High-Level] Shoot(): Shoot the ball
PASS, // [High-Level] Pass(teammate_unum [0,11]): Pass to the most open teammate
DRIBBLE, // [High-Level] Dribble(): Offensive dribble
CATCH, // [High-Level] Catch(): Catch the ball (Goalie only!)
NOOP, // Do nothing
QUIT // Special action to quit the game
};
// Status of a HFO game
enum status_t
{
IN_GAME, // Game is currently active
GOAL, // A goal has been scored by the offense
CAPTURED_BY_DEFENSE, // The defense has captured the ball
OUT_OF_BOUNDS, // Ball has gone out of bounds
OUT_OF_TIME, // Trial has ended due to time limit
SERVER_DOWN // Server is not alive
};
// Configuration of the HFO domain including the team names and player
// numbers for each team. This struct is populated by ParseConfig().
struct Config {
std::string offense_team_name;
std::string defense_team_name;
int num_offense; // Number of offensive players
int num_defense; // Number of defensive players
std::vector<int> offense_nums; // Offensive player numbers
std::vector<int> defense_nums; // Defensive player numbers
};
enum SideID {
RIGHT = -1,
NEUTRAL = 0,
LEFT = 1
};
// A Player is described by its uniform number and side
struct Player {
SideID side;
int unum;
};
/**
* Returns the number of parameters required for each action.
*/
inline int NumParams(const action_t action) {
switch (action) {
case DASH:
return 2;
case TURN:
return 1;
case TACKLE:
return 1;
case KICK:
return 2;
case KICK_TO:
return 3;
case MOVE_TO:
return 2;
case DRIBBLE_TO:
return 2;
case INTERCEPT:
return 0;
case MOVE:
return 0;
case SHOOT:
return 0;
case PASS:
return 1;
case DRIBBLE:
return 0;
case CATCH:
return 0;
case NOOP:
return 0;
case QUIT:
return 0;
}
std::cerr << "Unrecognized Action: " << action << std::endl;
return -1;
};
/**
* Returns a string representation of an action.
*/
inline std::string ActionToString(action_t action) {
switch (action) {
case DASH:
return "Dash";
case TURN:
return "Turn";
case TACKLE:
return "Tackle";
case KICK:
return "Kick";
case KICK_TO:
return "KickTo";
case MOVE_TO:
return "MoveTo";
case DRIBBLE_TO:
return "DribbleTo";
case INTERCEPT:
return "Intercept";
case MOVE:
return "Move";
case SHOOT:
return "Shoot";
case PASS:
return "Pass";
case DRIBBLE:
return "Dribble";
case CATCH:
return "Catch";
case NOOP:
return "No-op";
case QUIT:
return "Quit";
default:
return "Unknown";
}
};
/**
* Returns a string representation of a game_status.
*/
inline std::string StatusToString(status_t status) {
switch (status) {
case IN_GAME:
return "InGame";
case GOAL:
return "Goal";
case CAPTURED_BY_DEFENSE:
return "CapturedByDefense";
case OUT_OF_BOUNDS:
return "OutOfBounds";
case OUT_OF_TIME:
return "OutOfTime";
case SERVER_DOWN:
return "ServerDown";
default:
return "Unknown";
}
};
/**
* Parse a Trainer message to populate config. Returns a bool
* indicating if the struct was correctly parsed.
*/
inline bool ParseConfig(const std::string& message, Config& config) {
config.num_offense = -1;
config.num_defense = -1;
std::istringstream iss(message);
std::string header = "HFO_SETUP";
std::string key, val;
iss >> key;
if (header.compare(key) != 0) {
std::cerr << "Got unexpected message header: " << header;
return false;
}
while (iss >> key) {
if (key.compare("offense_name") == 0) {
iss >> config.offense_team_name;
} else if (key.compare("defense_name") == 0) {
iss >> config.defense_team_name;
} else if (key.compare("num_offense") == 0) {
iss >> val;
config.num_offense = strtol(val.c_str(), NULL, 0);
} else if (key.compare("num_defense") == 0) {
iss >> val;
config.num_defense = strtol(val.c_str(), NULL, 0);
} else if (key.compare("offense_nums") == 0) {
assert(config.num_offense >= 0);
for (int i=0; i<config.num_offense; ++i) {
iss >> val;
config.offense_nums.push_back(strtol(val.c_str(), NULL, 0));
}
} else if (key.compare("defense_nums") == 0) {
assert(config.num_defense >= 0);
for (int i=0; i<config.num_defense; ++i) {
iss >> val;
config.defense_nums.push_back(strtol(val.c_str(), NULL, 0));
}
} else {
std::cerr << "Unrecognized key: " << key << std::endl;
return false;
}
}
assert(config.offense_nums.size() == config.num_offense);
assert(config.defense_nums.size() == config.num_defense);
return true;
};
/**
* Parse a trainer message to extract the player on the ball
*/
inline bool ParsePlayerOnBall(const std::string& message, Player& player) {
if (message.find("GOAL") != std::string::npos){
player.unum = atoi((message.substr(message.find("-")+1)).c_str());
player.side = LEFT;
} else if (message.find("CAPTURED_BY_DEFENSE") != std::string::npos) {
player.unum = atoi((message.substr(message.find("-")+1)).c_str());
player.side = RIGHT;
} else if (message.find("IN_GAME") != std::string::npos){
switch (message.at(message.find("-")+1)){
case 'L':
player.side = LEFT;
break;
case 'R':
player.side = RIGHT;
break;
case 'U':
player.side = NEUTRAL;
break;
}
player.unum = atoi((message.substr(message.find("-")+2)).c_str());
} else {
return false;
}
return true;
};
/**
* Parse a trainer message to extract the game status
*/
inline bool ParseGameStatus(const std::string& message, status_t& status) {
status = IN_GAME;
if (message.find("GOAL") != std::string::npos){
status = GOAL;
} else if (message.find("CAPTURED_BY_DEFENSE") != std::string::npos) {
status = CAPTURED_BY_DEFENSE;
} else if (message.compare("OUT_OF_BOUNDS") == 0) {
status = OUT_OF_BOUNDS;
} else if (message.compare("OUT_OF_TIME") == 0) {
status = OUT_OF_TIME;
} else if (message.find("IN_GAME") != std::string::npos){
status = IN_GAME;
} else {
return false;
}
return true;
};
}
#endif
......@@ -233,7 +233,7 @@ bool SamplePlayer::getHFOConfig() {
const AudioSensor& audio_sensor = audioSensor();
if (audio_sensor.trainerMessageTime().cycle() > lastTrainerMessageTime) {
const std::string& message = audio_sensor.trainerMessage();
if (hfo::HFOEnvironment::ParseConfig(message, hfo_config)) {
if (hfo::ParseConfig(message, hfo_config)) {
lastTrainerMessageTime = audio_sensor.trainerMessageTime().cycle();
if (config().teamName().compare(hfo_config.offense_team_name) == 0) {
playing_offense = true;
......@@ -269,10 +269,15 @@ SamplePlayer::actionImpl()
hfo::LOW_LEVEL_FEATURE_SET, num_teammates, num_opponents, playing_offense);
}
} else {
std::vector<int> game_status = Agent::getGameStatus(
audioSensor(), lastTrainerMessageTime);
elog.addText(Logger::WORLD, "GameStatus %d", game_status[0]);
if (audioSensor().trainerMessageTime().cycle() > lastTrainerMessageTime) {
const std::string& message = audioSensor().trainerMessage();
hfo::status_t game_status;
if (hfo::ParseGameStatus(message, game_status)) {
elog.addText(Logger::WORLD, "GameStatus %d", game_status);
elog.flush();
}
lastTrainerMessageTime = audioSensor().trainerMessageTime().cycle();
}
feature_extractor->ExtractFeatures(this->world());
feature_extractor->LogFeatures();
}
......
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