Commit 63820e45 authored by Matthew Hausknecht's avatar Matthew Hausknecht

Refactored code for launching agents.

parent b9548574
...@@ -62,7 +62,7 @@ class Communicator(object): ...@@ -62,7 +62,7 @@ class Communicator(object):
raise TimeoutError raise TimeoutError
else: else:
retryCount -= 1 retryCount -= 1
print 'error receiving message, trying again' print '[Trainer] waiting for message'
time.sleep(0.3) time.sleep(0.3)
#raise ValueError('Error while receiving message') #raise ValueError('Error while receiving message')
(msg,sep,rest) = msg.partition('\0') (msg,sep,rest) = msg.partition('\0')
......
#!/usr/bin/env python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
import sys, numpy, time, os, subprocess, re import sys, numpy, time, os, subprocess
from signal import SIGINT
from Communicator import ClientCommunicator, TimeoutError from Communicator import ClientCommunicator, TimeoutError
class DoneError(Exception): class DoneError(Exception):
...@@ -12,22 +11,6 @@ class DoneError(Exception): ...@@ -12,22 +11,6 @@ class DoneError(Exception):
def __str__(self): def __str__(self):
return 'Done due to %s' % self.msg return 'Done due to %s' % self.msg
class DummyPopen(object):
""" Emulates a Popen object without actually starting a process. """
def __init__(self, pid):
self.pid = pid
def poll(self):
try:
os.kill(self.pid, 0)
return None
except OSError:
return 0
def send_signal(self, sig):
try:
os.kill(self.pid, sig)
except OSError:
pass
class Trainer(object): class Trainer(object):
""" Trainer is responsible for setting up the players and game. """ Trainer is responsible for setting up the players and game.
""" """
...@@ -68,9 +51,9 @@ class Trainer(object): ...@@ -68,9 +51,9 @@ class Trainer(object):
self._numAgents = args.offenseAgents + args.defenseAgents self._numAgents = args.offenseAgents + args.defenseAgents
self._offenseAgents = args.offenseAgents self._offenseAgents = args.offenseAgents
self._defenseAgents = args.defenseAgents self._defenseAgents = args.defenseAgents
self._agentReady = set([]) # Unums of ready agents
self._agentTeams = [] # Names of the teams the agents are playing for self._agentTeams = [] # Names of the teams the agents are playing for
self._agentNumInt = [] # List of agents internal team numbers self._agentNumInt = [] # List of agents internal team numbers
self._agentNumExt = [] # List of agents external team numbers
self._agentServerPort = args.port # Base Port for agent's server self._agentServerPort = args.port # Base Port for agent's server
self._agentOnBall = args.agent_on_ball # If true, agent starts with the ball self._agentOnBall = args.agent_on_ball # If true, agent starts with the ball
# =============== MISC =============== # # =============== MISC =============== #
...@@ -85,14 +68,43 @@ class Trainer(object): ...@@ -85,14 +68,43 @@ class Trainer(object):
self._teamHoldingBall = None # Team currently in control of the ball self._teamHoldingBall = None # Team currently in control of the ball
self._playerHoldingBall = None # Player current in control of ball self._playerHoldingBall = None # Player current in control of ball
self._agentPopen = [] # Agent's processes self._agentPopen = [] # Agent's processes
self._npcPopen = [] # NPC's processes
self._connectedPlayers = []
self.initMsgHandlers() self.initMsgHandlers()
def launch_player(self, player_num, play_offense):
"""Launches a player using sample_player binary
Returns a Popen process object
"""
if play_offense:
team_name = self._offenseTeamName
else:
team_name = self._defenseTeamName
binary_dir = os.path.dirname(os.path.realpath(__file__))
config_dir = os.path.join(binary_dir, '../config/formations-dt')
player_conf = os.path.join(binary_dir, '../config/player.conf')
player_cmd = os.path.join(binary_dir, 'sample_player')
player_cmd += ' -t %s -p %i --config_dir %s ' \
' --log_dir %s --player-config %s' \
%(team_name, self._serverPort, config_dir,
self._logDir, player_conf)
if self._record:
player_cmd += ' --record'
if player_num == 1:
player_cmd += ' -g'
kwargs = {'stdout':open('/dev/null', 'w'),
'stderr':open('/dev/null', 'w')}
p = subprocess.Popen(player_cmd.split(' '), shell = False, **kwargs)
return p
def launch_agent(self, agent_num, play_offense, port): def launch_agent(self, agent_num, play_offense, port):
"""Launch the learning agent using the start_agent.sh script and """Launches a learning agent using the agent binary
return a DummyPopen for the process.
Returns a Popen process object
""" """
print '[Trainer] Launching Agent', str(agent_num) print '[Trainer] Launching %s Agent %d' % \
('offense' if play_offense else 'defense', agent_num)
if play_offense: if play_offense:
assert self._numOffense > 0 assert self._numOffense > 0
team_name = self._offenseTeamName team_name = self._offenseTeamName
...@@ -111,29 +123,24 @@ class Trainer(object): ...@@ -111,29 +123,24 @@ class Trainer(object):
numTeammates = self._numDefense - 1 numTeammates = self._numDefense - 1
numOpponents = self._numOffense numOpponents = self._numOffense
ext_num = self.convertToExtPlayer(team_name, internal_player_num) ext_num = self.convertToExtPlayer(team_name, internal_player_num)
self._agentNumExt.append(ext_num)
binary_dir = os.path.dirname(os.path.realpath(__file__)) binary_dir = os.path.dirname(os.path.realpath(__file__))
agentCmd = 'start_agent.sh -t %s -u %i -p %i -P %i --log-dir %s'\ config_dir = os.path.join(binary_dir, '../config/formations-dt')
' --numTeammates %i --numOpponents %i'\ player_conf = os.path.join(binary_dir, '../config/player.conf')
' --playingOffense %i --serverPort %i'\ agent_cmd = os.path.join(binary_dir, 'agent')
%(team_name, ext_num, self._serverPort, agent_cmd += ' -t %s -p %i --numTeammates %i --numOpponents %i' \
self._coachPort, self._logDir, numTeammates, ' --playingOffense %i --serverPort %i --log_dir %s' \
numOpponents, play_offense, port) ' --player-config %s --config_dir %s' \
%(team_name, self._serverPort, numTeammates,
numOpponents, play_offense, port, self._logDir,
player_conf, config_dir)
if ext_num == 1:
agent_cmd += ' -g'
if self._record: if self._record:
agentCmd += ' --record' agent_cmd += ' --record'
agentCmd = os.path.join(binary_dir, agentCmd) kwargs = {'stdout':open('/dev/null', 'w'),
agentCmd = agentCmd.split(' ') 'stderr':open('/dev/null', 'w')}
# Ignore stderr because librcsc continually prints to it p = subprocess.Popen(agent_cmd.split(' '), shell = False, **kwargs)
kwargs = {'stderr':open('/dev/null','w')} return p
p = subprocess.Popen(agentCmd, **kwargs)
p.wait()
pid_file = os.path.join(self._logDir, 'start%i'%p.pid)
print '[Trainer] Parsing agent\'s pid from file:', pid_file
assert os.path.isfile(pid_file)
with open(pid_file,'r') as f:
output = f.read()
pid = int(re.findall('PID: (\d+)',output)[0])
return DummyPopen(pid)
def getDefensiveRoster(self, team_name): def getDefensiveRoster(self, team_name):
"""Returns a list of player numbers on a given team that are thought """Returns a list of player numbers on a given team that are thought
...@@ -161,6 +168,10 @@ class Trainer(object): ...@@ -161,6 +168,10 @@ class Trainer(object):
else: else:
return [11,7,8,9,10,6,3,2,4,5] return [11,7,8,9,10,6,3,2,4,5]
def addTeam(self, team_name):
""" Adds a team to the team list"""
self._teams.append(team_name)
def setTeams(self): def setTeams(self):
""" Sets the offensive and defensive teams and player rosters. """ """ Sets the offensive and defensive teams and player rosters. """
self._offenseTeamInd = 0 self._offenseTeamInd = 0
...@@ -217,10 +228,8 @@ class Trainer(object): ...@@ -217,10 +228,8 @@ class Trainer(object):
def _hear(self, body): def _hear(self, body):
""" Handle a hear message. """ """ Handle a hear message. """
timestep,playerInfo,msg = body timestep,playerInfo,msg = body
if len(playerInfo) != 3:
return
_,team,player = playerInfo
try: try:
_,team,player = playerInfo[:3]
length = int(msg[0]) length = int(msg[0])
except: except:
return return
...@@ -232,6 +241,9 @@ class Trainer(object): ...@@ -232,6 +241,9 @@ class Trainer(object):
self.startGame() self.startGame()
elif msg == 'DONE': elif msg == 'DONE':
raise DoneError raise DoneError
elif msg == 'ready':
print '[Trainer] Agent Ready:', team, player
self._agentReady.add((team, player))
else: else:
print '[Trainer] Unhandled message from agent: %s' % msg print '[Trainer] Unhandled message from agent: %s' % msg
...@@ -361,9 +373,9 @@ class Trainer(object): ...@@ -361,9 +373,9 @@ class Trainer(object):
val = param[1] val = param[1]
self._SP[param[0]] = val self._SP[param[0]] = val
def listenAndProcess(self): def listenAndProcess(self, retry_count=None):
""" Gather messages and process them. """ """ Gather messages and process them. """
msg = self.recv() msg = self.recv(retry_count)
assert((msg[0] == '(') and (msg[-1] == ')')),'|%s|' % msg assert((msg[0] == '(') and (msg[-1] == ')')),'|%s|' % msg
msg = self.parseMsg(msg) msg = self.parseMsg(msg)
self.handleMsg(msg) self.handleMsg(msg)
...@@ -389,6 +401,26 @@ class Trainer(object): ...@@ -389,6 +401,26 @@ class Trainer(object):
#self.unregisterMsgHandler(*partial) #self.unregisterMsgHandler(*partial)
self.ignoreMsg(*partial,quiet=True) 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.
"""
self.send('(look)')
partial = ['ok','look']
self._numPlayers = 0
def f(body):
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:
self.listenAndProcess()
self.send('(look)')
self.ignoreMsg(*partial,quiet=True)
def checkIfAllPlayersConnected(self): def checkIfAllPlayersConnected(self):
""" Returns true if all players are connected. """ """ Returns true if all players are connected. """
self.send('(look)') self.send('(look)')
...@@ -397,8 +429,8 @@ class Trainer(object): ...@@ -397,8 +429,8 @@ class Trainer(object):
def f(x): def f(x):
self._numPlayers = len(x) - 4 # -4 for time, ball, goal_l, and goal_r self._numPlayers = len(x) - 4 # -4 for time, ball, goal_l, and goal_r
self.send('(look)') self.send('(look)')
self.registerMsgHandler(f,*partial) self.registerMsgHandler(f,*partial,quiet=True)
while self._numPlayers != 2 * 11: while self._numPlayers != 2 * 11: # self._numOffense + self._numDefense:
self.listenAndProcess() self.listenAndProcess()
self.ignoreMsg(*partial,quiet=True) self.ignoreMsg(*partial,quiet=True)
...@@ -543,9 +575,7 @@ class Trainer(object): ...@@ -543,9 +575,7 @@ class Trainer(object):
ends to setup for the next trial. ends to setup for the next trial.
""" """
# Always Move the offensive goalie to the left goal # Move the offense
self.movePlayer(self._offenseTeamName, 0, [-0.5 * self.PITCH_LENGTH, 0])
# Move the rest of the offense
for i in xrange(1, self._numOffense + 1): for i in xrange(1, self._numOffense + 1):
self.movePlayer(self._offenseTeamName, i, self.getOffensiveResetPosition()) self.movePlayer(self._offenseTeamName, i, self.getOffensiveResetPosition())
# Move the agent to the ball # Move the agent to the ball
...@@ -558,26 +588,8 @@ class Trainer(object): ...@@ -558,26 +588,8 @@ class Trainer(object):
for i in xrange(1, self._numDefense): for i in xrange(1, self._numDefense):
self.movePlayer(self._defenseTeamName, i, self.getDefensiveResetPosition()) self.movePlayer(self._defenseTeamName, i, self.getDefensiveResetPosition())
def removeNonHFOPlayers(self):
"""Removes players that aren't involved in HFO game.
The players whose numbers are greater than numOffense/numDefense
are sent to left-field.
"""
offensive_agent_numbers = self._agentNumInt[:self._offenseAgents]
defensive_agent_numbers = self._agentNumInt[self._offenseAgents:]
for i in xrange(self._numOffense + 1, 11):
if i not in offensive_agent_numbers:
self.movePlayer(self._offenseTeamName, i, [-0.25 * self.PITCH_LENGTH, 0])
for i in xrange(self._numDefense, 11):
if i not in defensive_agent_numbers:
self.movePlayer(self._defenseTeamName, i, [-0.25 * self.PITCH_LENGTH, 0])
def step(self): def step(self):
""" Takes a simulated step. """ """ Takes a simulated step. """
# self.send('(check_ball)')
self.removeNonHFOPlayers()
self._teamHoldingBall, self._playerHoldingBall = self.calcBallHolder() self._teamHoldingBall, self._playerHoldingBall = self.calcBallHolder()
if self._teamHoldingBall is not None: if self._teamHoldingBall is not None:
self._lastFrameBallTouched = self._frame self._lastFrameBallTouched = self._frame
...@@ -654,16 +666,66 @@ class Trainer(object): ...@@ -654,16 +666,66 @@ class Trainer(object):
def run(self, necProcesses): def run(self, necProcesses):
""" Run the trainer """ """ Run the trainer """
try: try:
for agent_num in xrange(self._offenseAgents): self.setTeams()
port = self._agentServerPort + agent_num offense_unums = self._offenseOrder[1: self._numOffense + 1]
agent = self.launch_agent(agent_num, play_offense=True, port=port) sorted_offense_agent_unums = sorted(self._offenseOrder[1:self._offenseAgents+1])
self._agentPopen.append(agent) defense_unums = self._defenseOrder[: self._numDefense]
necProcesses.append([agent, 'offense_agent_' + str(agent_num)]) sorted_defense_agent_unums = sorted(self._defenseOrder[:self._defenseAgents])
for agent_num in xrange(self._defenseAgents): unnecessary_players = []
port = self._agentServerPort + agent_num + self._offenseAgents
agent = self.launch_agent(agent_num, play_offense=False, port=port) # Launch offense
self._agentPopen.append(agent) agent_num = 0
necProcesses.append([agent, 'defense_agent_' + str(agent_num)]) for player_num in xrange(1, 12):
if agent_num < self._offenseAgents and \
sorted_offense_agent_unums[agent_num] == player_num:
port = self._agentServerPort + agent_num
agent = self.launch_agent(agent_num, play_offense=True, port=port)
self._agentPopen.append(agent)
necProcesses.append([agent, 'offense_agent_' + str(agent_num)])
agent_num += 1
else:
player = self.launch_player(player_num, play_offense = True)
if player_num in offense_unums:
self._npcPopen.append(player)
necProcesses.append([player, 'offense_npc_' + str(player_num)])
else:
unnecessary_players.append(player)
self.waitOnPlayer(player_num, on_offense=True)
self.waitOnTeam(first = False)
# Launch defense
agent_num = 0
for player_num in xrange(1, 12):
if agent_num < self._defenseAgents and \
sorted_defense_agent_unums[agent_num] == player_num:
port = self._agentServerPort + agent_num + self._offenseAgents
agent = self.launch_agent(agent_num, play_offense=False, port=port)
self._agentPopen.append(agent)
necProcesses.append([agent, 'defense_agent_' + str(agent_num)])
agent_num += 1
else:
player = self.launch_player(player_num, play_offense = False)
if player_num in defense_unums:
self._npcPopen.append(player)
necProcesses.append([player, 'defense_npc_' + str(player_num)])
else:
unnecessary_players.append(player)
self.waitOnPlayer(player_num, on_offense=False)
self.waitOnTeam(first = False)
self.checkIfAllPlayersConnected()
print '[Trainer] 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(1000)
# Terminate unnecessary players
print '[Trainer] Removing unnecessary players'
for player in unnecessary_players:
player.terminate()
# Broadcast the HFO configuration # Broadcast the HFO configuration
offense_nums = ' '.join([str(self.convertToExtPlayer(self._offenseTeamName, i)) offense_nums = ' '.join([str(self.convertToExtPlayer(self._offenseTeamName, i))
for i in xrange(1, self._numOffense + 1)]) for i in xrange(1, self._numOffense + 1)])
...@@ -674,6 +736,7 @@ class Trainer(object): ...@@ -674,6 +736,7 @@ class Trainer(object):
%(self._offenseTeamName, self._defenseTeamName, %(self._offenseTeamName, self._defenseTeamName,
self._numOffense, self._numDefense, self._numOffense, self._numDefense,
offense_nums, defense_nums)) offense_nums, defense_nums))
print '[Trainer] Starting game'
self.startGame() self.startGame()
while self.checkLive(necProcesses): while self.checkLive(necProcesses):
prevFrame = self._frame prevFrame = self._frame
...@@ -685,11 +748,23 @@ class Trainer(object): ...@@ -685,11 +748,23 @@ class Trainer(object):
except (KeyboardInterrupt, DoneError): except (KeyboardInterrupt, DoneError):
print '[Trainer] Finished' print '[Trainer] Finished'
finally: finally:
for p in self._agentPopen:
p.send_signal(SIGINT)
try: try:
self._comm.sendMsg('(bye)') self._comm.sendMsg('(bye)')
except: except:
pass pass
for p in self._agentPopen:
try:
p.terminate()
time.sleep(0.1)
p.kill()
except:
pass
for p in self._npcPopen:
try:
p.terminate()
time.sleep(0.1)
p.kill()
except:
pass
self._comm.close() self._comm.close()
self.printStats() self.printStats()
...@@ -86,6 +86,7 @@ ...@@ -86,6 +86,7 @@
#include <rcsc/player/say_message_builder.h> #include <rcsc/player/say_message_builder.h>
#include <rcsc/player/audio_sensor.h> #include <rcsc/player/audio_sensor.h>
#include <rcsc/player/freeform_parser.h> #include <rcsc/player/freeform_parser.h>
#include <rcsc/player/free_message.h>
#include <rcsc/common/basic_client.h> #include <rcsc/common/basic_client.h>
#include <rcsc/common/logger.h> #include <rcsc/common/logger.h>
...@@ -108,6 +109,7 @@ ...@@ -108,6 +109,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <poll.h>
using namespace rcsc; using namespace rcsc;
using namespace hfo; using namespace hfo;
...@@ -119,7 +121,7 @@ Agent::Agent() ...@@ -119,7 +121,7 @@ Agent::Agent()
M_action_generator(createActionGenerator()), M_action_generator(createActionGenerator()),
lastTrainerMessageTime(-1), lastTrainerMessageTime(-1),
server_port(6008), server_port(6008),
server_running(false), client_connected(false),
num_teammates(-1), num_teammates(-1),
num_opponents(-1), num_opponents(-1),
playing_offense(false) playing_offense(false)
...@@ -230,12 +232,14 @@ bool Agent::initImpl(CmdLineParser & cmd_parser) { ...@@ -230,12 +232,14 @@ bool Agent::initImpl(CmdLineParser & cmd_parser) {
assert(num_teammates >= 0); assert(num_teammates >= 0);
assert(num_opponents >= 0); assert(num_opponents >= 0);
startServer(server_port);
return true; return true;
} }
void Agent::startServer(int server_port) { void Agent::startServer(int server_port) {
std::cout << "[Agent Server] Starting Server on Port " << server_port << std::endl; std::cout << "[Agent Server] Starting Server on Port " << server_port << std::endl;
struct sockaddr_in serv_addr, cli_addr; struct sockaddr_in serv_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0); sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) { if (sockfd < 0) {
perror("[Agent Server] ERROR opening socket"); perror("[Agent Server] ERROR opening socket");
...@@ -250,16 +254,32 @@ void Agent::startServer(int server_port) { ...@@ -250,16 +254,32 @@ void Agent::startServer(int server_port) {
exit(1); exit(1);
} }
listen(sockfd, 5); listen(sockfd, 5);
socklen_t clilen = sizeof(cli_addr); }
std::cout << "[Agent Server] Waiting for client to connect... " << std::endl;
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); void Agent::listenForConnection() {
if (newsockfd < 0) { int rv;
perror("[Agent Server] ERROR on accept"); struct pollfd ufd;
close(sockfd); ufd.fd = sockfd;
exit(1); 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();
}
} }
std::cout << "[Agent Server] Connected" << std::endl;
server_running = true;
} }
void Agent::clientHandshake() { void Agent::clientHandshake() {
...@@ -316,6 +336,9 @@ void Agent::clientHandshake() { ...@@ -316,6 +336,9 @@ void Agent::clientHandshake() {
exit(1); exit(1);
} }
std::cout << "[Agent Server] Handshake complete" << std::endl; 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, FeatureExtractor* Agent::getFeatureExtractor(feature_set_t feature_set_indx,
...@@ -367,9 +390,13 @@ status_t Agent::getGameStatus(const rcsc::AudioSensor& audio_sensor, ...@@ -367,9 +390,13 @@ status_t Agent::getGameStatus(const rcsc::AudioSensor& audio_sensor,
virtual method in super class virtual method in super class
*/ */
void Agent::actionImpl() { void Agent::actionImpl() {
if (!server_running) { // For now let's not worry about turning the neck or setting the vision.
startServer(server_port); this->setViewAction(new View_Tactical());
clientHandshake(); this->setNeckAction(new Neck_TurnToBallOrScan());
if (!client_connected) {
listenForConnection();
return;
} }
// Update and send the game status // Update and send the game status
...@@ -451,10 +478,6 @@ void Agent::actionImpl() { ...@@ -451,10 +478,6 @@ void Agent::actionImpl() {
close(sockfd); close(sockfd);
exit(1); exit(1);
} }
// For now let's not worry about turning the neck or setting the vision.
this->setViewAction(new View_Tactical());
this->setNeckAction(new Neck_TurnToBallOrScan());
} }
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
......
...@@ -72,7 +72,7 @@ protected: ...@@ -72,7 +72,7 @@ protected:
// Start the server and listen for a connection. // Start the server and listen for a connection.
void startServer(int server_port=6008); void startServer(int server_port=6008);
void listenForConnection();
// Transmit information to the client and ensure it can recieve. // Transmit information to the client and ensure it can recieve.
void clientHandshake(); void clientHandshake();
...@@ -80,7 +80,7 @@ protected: ...@@ -80,7 +80,7 @@ protected:
FeatureExtractor* feature_extractor; FeatureExtractor* feature_extractor;
long lastTrainerMessageTime; // Last time the trainer sent a message long lastTrainerMessageTime; // Last time the trainer sent a message
int server_port; // Port to start the server on int server_port; // Port to start the server on
bool server_running; // Is the server running? bool client_connected; // Has the client connected and handshake?
int sockfd, newsockfd; // Server sockets int sockfd, newsockfd; // Server sockets
int num_teammates, num_opponents; int num_teammates, num_opponents;
bool playing_offense; bool playing_offense;
......
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