#!/usr/bin/env python # encoding: utf-8 import subprocess, os, time, numpy, sys # Global list of all/essential running processes processes, necProcesses = [], [] # Command to run the rcssserver. Edit as needed. SERVER_BIN = 'rcssserver' # Command to run the monitor. Edit as needed. MONITOR_BIN = 'soccerwindow2' def launch(cmd, name = 'Unknown', necessary = True, supressOutput = True): """Launch a process. Appends to list of processes and (optionally) necProcesses if necessary flag is True. Returns: The launched process. """ kwargs = {} if supressOutput: kwargs = {'stdout': open('/dev/null', 'w'), 'stderr': open('/dev/null', 'w')} p = subprocess.Popen(cmd.split(' '), shell = False, **kwargs) processes.append(p) if necessary: necProcesses.append([p,name]) return p def main(args): """Sets up the teams, launches the server and monitor, starts the trainer. """ if args.logging and not os.path.exists(args.logDir): os.makedirs(args.logDir) num_agents = args.offenseAgents + args.defenseAgents binary_dir = os.path.dirname(os.path.realpath(__file__)) 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 ' \ 'server::game_logging=%i server::text_logging=%i ' \ 'server::hfo_logging=%i server::hfo_log_dir=%s ' \ 'server::game_log_dir=%s server::text_log_dir=%s '\ 'server::synch_mode=%i server::hfo=1 ' \ 'server::fullstate_l=%i server::fullstate_r=%i ' \ 'server::coach_w_referee=1 server::hfo_max_trial_time=%i ' \ 'server::hfo_max_trials=%i server::hfo_max_frames=%i ' \ 'server::hfo_offense_on_ball=%i server::random_seed=%i ' \ 'server::hfo_max_untouched_time=%i ' \ 'server::say_msg_size=%i' \ %(server_port, coach_port, olcoach_port, args.logging, args.logging, args.logging, args.logDir, args.logDir, args.logDir, args.sync, args.fullstate, args.fullstate, args.maxFramesPerTrial, args.numTrials, args.numFrames, args.offenseOnBall, args.seed, args.maxUntouchedTime, args.messageSize) # server::record_messages=on -- useful for debug try: # Launch the Server server = launch(serverCommand + serverOptions, name='server', supressOutput=True) time.sleep(0.2) assert server.poll() is None,\ '[start.py] Failed to launch Server with command: \"%s\"' \ '\n\nAnother rcssserver may be running on the same port?' \ '\nTry: \"killall -9 rcssserver\"' \ %(serverCommand + serverOptions) if not args.headless: monitorCommand = os.path.join(binary_dir, MONITOR_BIN) monitorOptions = ' --connect --port=%i'%(server_port) launch(monitorCommand + monitorOptions, name='monitor') # Launch the Trainer from Trainer import Trainer trainer = Trainer(args=args, server_port=server_port, coach_port=coach_port) trainer.initComm() # Run trainer.run(necProcesses, args.offenseTeam, args.defenseTeam) except KeyboardInterrupt: print '[start.py] Exiting for CTRL-C' finally: print '[start.py] Cleaning up server and other processes' for p in reversed(processes): try: p.terminate() time.sleep(0.1) p.kill() except: pass def parseArgs(): import argparse team_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'teams') installed_teams = os.listdir(team_dir) p = argparse.ArgumentParser(description='Start Half Field Offense.', formatter_class=argparse.RawTextHelpFormatter) p.add_argument('--headless', dest='headless', action='store_true', help='Run without a monitor.') p.add_argument('--trials', dest='numTrials', type=int, default=-1, help='Number of trials to run.\n'\ 'Negative values mean unlimited. Default: -1.') p.add_argument('--frames', dest='numFrames', type=int, default=-1, help='Number of frames to run for.\n'\ 'Negative values mean unlimited. Default: -1.') p.add_argument('--frames-per-trial', dest='maxFramesPerTrial', type=int, default=1000, help='Max number of frames per trial.\n'\ 'Negative values mean unlimited. Default: 1000.') p.add_argument('--untouched-time', dest='maxUntouchedTime', type=int, default=100, help='Ends trial if ball is untouched for this long.\n'\ 'Negative values mean unlimited. Default: 100.') p.add_argument('--offense-agents', dest='offenseAgents', type=int, default=0, help='Number of offensive agents. Default: 0.') p.add_argument('--defense-agents', dest='defenseAgents', type=int, default=0, help='Number of defensive agents. Default: 0.') p.add_argument('--offense-npcs', dest='offenseNPCs', type=int, default=0, help='Number of offensive uncontrolled players. Default: 0.') p.add_argument('--defense-npcs', dest='defenseNPCs', type=int, default=0, help='Number of defensive uncontrolled players. Default: 0.') p.add_argument('--offense-team', dest='offenseTeam', type=str, default='base', help='Offense team binary. Options: '+str(installed_teams)+'. Default: base.') p.add_argument('--defense-team', dest='defenseTeam', type=str, default='base', help='Defense team binary. Options: '+str(installed_teams)+'. Default: base.') p.add_argument('--no-sync', dest='sync', action='store_false', default=True, help='Run server in non-sync mode.') p.add_argument('--port', dest='port', type=int, default=6000, help='Agent server\'s port. Default: 6000.\n'\ 'rcssserver, coach, and ol_coach will be '\ 'incrementally allocated the following ports.') p.add_argument('--no-logging', dest='logging', action='store_false', default=True, help='Disable rcssserver logging.') p.add_argument('--log-dir', dest='logDir', default='log/', help='Directory to store logs. Default: log/') p.add_argument('--record', dest='record', action='store_true', help='Record logs of states and actions.') p.add_argument('--offense-on-ball', dest='offenseOnBall', type=int, default=0, help='Ball given to the player represented by the value.\n'\ 'If value greater than the number of offense players, '\ 'ball given to a random offense player.\n'\ 'If value non-positive, ball is not given to any player.\n'\ 'Default: 0.') p.add_argument('--fullstate', dest='fullstate', action='store_true', help='Server provides full-state information to agents.') p.add_argument('--seed', dest='seed', type=int, default=-1, help='Seed the server\'s RNG. Default: time.') p.add_argument('--message-size', dest='messageSize', type=int, default=1000, help='Message size limit for communication') args = p.parse_args() if args.offenseAgents not in xrange(0, 11): p.error('argument --offense-agents: invalid choice: '\ + str(args.offenseAgents) + ' (choose from [0-10])') if args.offenseNPCs not in xrange(0, 11): p.error('argument --offense-npcs: invalid choice: '\ + str(args.offenseNPCs) + ' (choose from [0-10])') if args.defenseAgents not in xrange(0, 12): p.error('argument --defense-agents: invalid choice: '\ + str(args.defenseAgents) + ' (choose from [0-11])') if args.defenseNPCs not in xrange(0, 12): p.error('argument --offense-npcs: invalid choice: '\ + str(args.defenseNPCs) + ' (choose from [0-11])') if args.offenseAgents + args.offenseNPCs not in xrange(1, 11): p.error('Offense players (offense-agents + offense-npcs): '\ 'invalid choice: ' + str(args.offenseAgents + args.offenseNPCs) +\ ' (choose from [1,10])') if args.defenseAgents + args.defenseNPCs not in xrange(0, 12): p.error('Defense players (defense-agents + defense-npcs): '\ 'invalid choice: ' + str(args.defenseAgents + args.defenseNPCs) +\ ' (choose from [0,11])') if args.offenseTeam not in installed_teams: p.error('Unrecognized offense team: ' + str(args.offenseTeam)) if args.defenseTeam not in installed_teams: p.error('Unrecognized defense team: ' + str(args.defenseTeam)) return args if __name__ == '__main__': main(parseArgs())