Module main
[hide private]
[frames] | no frames]

Source Code for Module main

  1  ################################################################################## 
  2  # Copyright (c) 2010, 2011, 2012, 2013, Daniel Urieli, Peter Stone 
  3  # University of Texas at Austin 
  4  # All right reserved 
  5  #  
  6  # Based On: 
  7  #  
  8  # Copyright (c) 2000-2003, Jelle Kok, University of Amsterdam 
  9  # All rights reserved. 
 10  #  
 11  # Redistribution and use in source and binary forms, with or without 
 12  # modification, are permitted provided that the following conditions are met: 
 13  #  
 14  # 1. Redistributions of source code must retain the above copyright notice, this 
 15  # list of conditions and the following disclaimer. 
 16  #  
 17  # 2. Redistributions in binary form must reproduce the above copyright notice, 
 18  # this list of conditions and the following disclaimer in the documentation 
 19  # and/or other materials provided with the distribution. 
 20  #  
 21  # 3. Neither the name of the University of Amsterdam nor the names of its 
 22  # contributors may be used to endorse or promote products derived from this 
 23  # software without specific prior written permission. 
 24  #  
 25  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 26  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 27  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 28  # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
 29  # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 30  # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 31  # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 32  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
 33  # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 34  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 35  ################################################################################## 
 36   
 37   
 38   
 39   
 40  import sys 
 41  import os 
 42  import random 
 43  import operator 
 44  from math import * 
 45  from numpy import * 
 46   
 47  import seaModels 
 48  import shipModels 
 49  import state 
 50  import agents 
 51  import simulationEnvironment 
 52  import factories 
 53   
 54  #from heuristicDivide import * 
 55  from geometry import * 
 56   
57 -def default(str):
58 return str + ' [Default: %default]'
59
60 -def validateOptionCombinations(args):
61 """ 62 This functions validates we didn't mistakenly chosen options 63 that don't work with each other. 64 If options are verified we just return to the the program 65 execution. If the options are incorrect, we gracefully exit. 66 """ 67 # if( args['shipType'] == 'basic' 68 # and args['worldModel'] == 'complete' 69 # and args['strategy'] == 'circling' ): 70 # return 71 72 # if( args['shipType'] == 'basic' 73 # and args['worldModel'] == 'complete' 74 # and args['strategy'] == 'holonomicrrt' ): 75 # return 76 77 # if( args['shipType'] == 'basic' 78 # and args['worldModel'] == 'complete' 79 # and args['strategy'] == 'pid' ): 80 # return 81 82 # if( args['shipType'] == 'basic' 83 # and args['worldModel'] == 'complete' 84 # and args['strategy'] == 'measuredist' ): 85 # print 'Warning: change this combination in the future' 86 # return 87 88 # if( args['shipType'] == 'basic' 89 # and args['worldModel'] == 'complete' 90 # and args['strategy'] == 'staticpatrol' ): 91 # print 'Warning: change this combination in the future' 92 # return 93 94 # if( args['shipType'] == 'basic' 95 # and args['worldModel'] == 'complete' 96 # and args['strategy'] == 'coordinatedpatrol' ): 97 # print 'Warning: change this combination in the future' 98 # return 99 100 # if( args['shipType'] == 'basic' 101 # and args['worldModel'] == 'complete' 102 # and args['strategy'] == 'rrt' ): 103 # print 'Warning: change this combination in the future' 104 # return 105 106 # currently disabled option checking - it doesn't seem to be needed 107 return 108 109 print 'Illegal options combination - exiting...' 110 sys.exit()
111 112 # TODO: Once we support mixed ship types, check combinations 113 # per each ship 114 115 # Used to turn-off prints
116 -class NullDevice():
117 - def write(self, s):
118 pass
119
120 -def readCommand( argv ):
121 """ 122 Processes the command used to run the simulation from the command line. 123 """ 124 from optparse import OptionParser 125 usageStr = """ 126 USAGE: python main.py <options> 127 """ 128 parser = OptionParser(usageStr) 129 130 parser.add_option('-q', '--quietTextGraphics', action='store_true', dest='quietGraphics', 131 help=default('Generate minimal output and no graphics'), default=False ) 132 parser.add_option('-n', '--numSteps', dest='numSteps', type='int', 133 help=default("Number of steps to simulate"), default=10000) 134 parser.add_option('-s', '--speed', dest='speed', type='int', 135 help=default("frames per second in GUI mode (each frame is one simulated second)"), default=60) 136 parser.add_option('-i', '--inputFilesDir', dest='inputFilesDir', 137 help=default('the directory in which input files are searched for'), 138 metavar='DIR', default='input_files') 139 parser.add_option('-f', '--taskFile', dest='taskFile', 140 help=default('A task-definition-language file. The file is searched for under the input files directory (specified by the flag -i)'), 141 default='task_multiple_ships_tracking_multiple_targets.py') 142 parser.add_option('-r', '--randomSeed', dest='randomSeed', type='int', 143 help=default('reloads a random seed - for simulation replay'), default=None) 144 parser.add_option('-d', '--debug', action='store_true', dest='debug', 145 help=default('turns on debug prints'), 146 default=False) 147 148 # parser.add_option('-z', '--zoom', type='float', dest='zoom', 149 # help=default('Zoom the size of the graphics window'), default=1.0) 150 # parser.add_option('-t', '--frameTime', dest='frameTime', type='float', 151 # help=default('Time to delay between frames; <0 means keyboard'), default=0.1) 152 153 options, other = parser.parse_args() 154 if len(other) != 0: 155 raise Exception('Command line input not understood: ' + other) 156 args = dict() 157 158 # if options.randomSeed is not None, this fixes a random seed. 159 # if it is None, then system time is used 160 random.seed(options.randomSeed) 161 162 # Choose GUI / non-GUI 163 if options.quietGraphics: 164 args['withDisplay'] = False 165 else: 166 args['withDisplay'] = True 167 168 169 args['numSteps'] = options.numSteps 170 args['speed'] = options.speed 171 args['inputFilesDir'] = options.inputFilesDir 172 args['taskFile'] = options.taskFile 173 args['debug'] = options.debug 174 175 validateOptionCombinations(args) 176 177 return args
178 179 180 ###################################################### 181 # Initialization Functions - # 182 # Those function are here temporarily # 183 # until there is a flexible solution # 184 # for loading different models, perhaps # 185 # as a part of the task definition language # 186 ######################################################
187 -def initWindFromFile(filePath):
188 # just execute the (python) file and return a variable generated 189 exec(compile(open(filePath).read(), filePath, 'exec')) 190 return wind
191
192 -def initWaterCurrentsFromFile(filePath):
193 # just execute the (python) file and return a variable generated 194 exec(compile(open(filePath).read(), filePath, 'exec')) 195 return waterCurrents
196
197 -def initWavesFromFile(filePath):
198 # just execute the (python) file and return a variable generated 199 exec(compile(open(filePath).read(), filePath, 'exec')) 200 return waves
201
202 -def initObstaclesFromFile(filePath):
203 # just execute the (python) file and return a variable generated 204 exec(compile(open(filePath).read(), filePath, 'exec')) 205 return obstacles
206 207 208 ###################################################### 209 # Results writing functions # 210 ######################################################
211 -def writeResults(resultsType, env, patrolPoints):
212 """ 213 Write the simulation results. 214 215 TODO: this function needs to be further organized 216 once we have a better picture regarding what 217 types of results are usually needed. 218 Also the function arguments should be revised as a part 219 of the function reorganization. 220 221 @type resultsType: string 222 @param resultsType: a string identifier for the type of result to write 223 224 @type resultsType: SimulationEnvironment 225 @param resultsType: The simulation environment, used to extract data from 226 227 @type patrolPoints: array of (x,y) tuples 228 @param patrolPoints: All the points that participate in the patrol. This is required when writing data for heuristicDivide algorithm, and might be removed at some point. 229 """ 230 #TODO: this function needs to be further organized 231 # once we have a better picture regarding what 232 # types of results are usually needed. 233 if resultsType == 'visit_times': 234 235 f = open(resultsType + '.res', 'w') 236 237 # write only if all points have at least two visits 238 if len(env.visitTimes.values()) >= 1 and min([len(l) for l in env.visitTimes.values()]) >= 2: 239 # Simulation results, K+1, K-1 240 point2freq = dict([(point, mean([l[i] - l[i-1] for i in range(1,len(l))])) for point, l in env.visitTimes.items() ]) 241 if len(point2freq) > 0: 242 worstCaseFreq1 = max(point2freq.values()) 243 f.write('WORSTCASE FREQ: ' + str(worstCaseFreq1) + '\n') 244 f.write('patrolPoints=' + str(patrolPoints) + '\n') 245 f.write('visitTimes=' + str(env.visitTimes) + '\n') 246 247 f.close() 248 249 elif resultsType == 'edgeGraphData': 250 251 f = open(resultsType + '.py', 'w') 252 f.write('patrolPoints=' + str(patrolPoints) + '\n') 253 f.write('edgeLengths=' + str(env.edgeLengths) + '\n') 254 f.close()
255 256 257 ############### 258 # Main Function 259 ############### 260 #def run(withDisplay, shipType, worldModel, strategy, numShips, inputFilesDir, taskFile): 261 # disabled numShips, because determined by the number of paths 262 #def run(withDisplay, shipType, worldModel, strategy, inputFilesDir, taskFile, numSteps, rulesOfTheSea):
263 -def run(withDisplay, inputFilesDir, taskFile, numSteps, speed, debug):
264 """ 265 The main flow of the application. The arguments are generated by command line options, or their defaults. 266 267 @type withDisplay: boolean 268 @param withDisplay: Tells whether to run in a GUI mode or in non-GUI mode 269 270 @type inputFilesDir: string 271 @param inputFilesDir: The directory in which the input files are 272 273 @type taskFile: string 274 @param taskFile: A task-definition-language file 275 276 @type numSteps: int 277 @param numSteps: Number of steps to simulate 278 279 @type speed: int 280 @param speed: frames-per-second (each frame is one simulated second) 281 """ 282 283 origstdout = sys.stdout 284 origstderr = sys.stderr 285 if not debug: 286 sys.stdout = NullDevice() 287 288 289 ########### TASK INITIALIZATION - DEFINED BY TASK DEFINITION FILE ########### 290 291 292 # defaults 293 windFile = 'wind.py' 294 waterFile = 'waterCurrents.py' 295 wavesFile = 'waves.py' 296 obstaclesFile = 'obstacles.py' 297 shipType = 'basic' 298 strategy = 'staticpatrol' 299 worldModel = 'complete' 300 rulesOfTheSea = True #False 301 resultsType = 'edgeGraphData' 302 303 304 305 # LOAD TASK DEFINITION FILE, POSSIBLY OVERRIDES DEFAULTS 306 307 pathToTaskFile = os.path.join(inputFilesDir, taskFile) 308 exec(compile(open(pathToTaskFile).read(), pathToTaskFile, 'exec')) 309 310 311 312 # CREATE A TASK TO BE EXECUTED 313 314 # task variable should be loaded from task definition file, along with other related vars 315 try: 316 task 317 except Exception as e: 318 print >> sys.stderr, '-E- Exception Raised:', e 319 sys.exit(1) 320 if task == 'TASK_STATIC_PATROL': 321 try: 322 # checking existence 323 shipInitialStates 324 patrolPoints 325 paths 326 except Exception as e: 327 print >> sys.stderr, '-E- Exception Raised:', e 328 sys.exit(1) 329 #shipInitialStates = [state.ShipExternalState(*shipInitialPositions[i]) for i in range(len(paths))] 330 strategy = 'staticpatrol' #overriding strategy 331 332 elif task == 'TASK_PATROL_WITH_ONLINE_ASSIGNMENT': 333 try: 334 # checking existence 335 patrolPoints 336 edgeLengths 337 shipInitialStates 338 roles 339 if len(shipInitialStates) != len(roles): 340 raise Exception("shipInitialStates and roles must be at the same length") 341 except Exception as e: 342 print >> sys.stderr, '-E- Exception Raised:', e 343 sys.exit(1) 344 345 strategy = 'coordinatedpatrol' #overriding strategy 346 347 elif task == 'TASK_TARGET_TRACKING': 348 try: 349 # checking existence 350 roles 351 shipInitialStates 352 patrolPoints 353 paths 354 trackingDistance 355 positionOffsets 356 if len(shipInitialStates) != len(roles): 357 raise Exception("shipInitialStates and roles must be at the same length") 358 except Exception as e: 359 print >> sys.stderr, '-E- Exception Raised:', e 360 sys.exit(1) 361 362 strategy = 'targettracking' #overriding strategy 363 364 else: 365 raise Exception('Unknown task type') 366 367 try: 368 # checking existence 369 shipInitialStates 370 strategy 371 except Exception as e: 372 print >> sys.stderr, '-E- Exception Raised:', e 373 sys.exit(1) 374 375 376 377 378 ############# AGENT INITIALIZATION ############## 379 380 numShips = len(shipInitialStates) 381 382 # CREATE SHIPS 383 384 if shipType == 'basic': 385 shipFactory = factories.BasicShipFactory() 386 else: 387 raise Exception('Unknown ship type: ' + shipType) 388 ships = [shipFactory.create() for i in range(numShips)] 389 # TODO: When we support mixed ship models, 390 # allocate an agent based on ship, and send it the possible actions 391 # for this ship 392 393 394 395 # CREATE SHIP-CONTROLLING-AGENTS 396 397 # Choose a world model factory 398 if worldModel == 'complete': 399 worldModelFactory = factories.CompleteWorldModelFactory() 400 else: 401 raise Exception('Unknown world model type: ' + worldModel) 402 403 # Choose a decision making strategy 404 if strategy == 'circling': 405 strategyFactories = [factories.CirclingStrategyFactory() for i in range(len(ships))] 406 elif strategy == 'rrt': 407 strategyFactories = [factories.RRTStrategyFactory(paths, rulesOfTheSea) for i in range(len(ships))] 408 elif strategy == 'pid': 409 strategyFactories = [factories.PIDStrategyFactory(rulesOfTheSea) for i in range(len(ships))] 410 elif strategy == 'measuredist': 411 strategyFactories = [factories.MeasureEdgeLengthsFactory() for i in range(len(ships))] 412 elif strategy == 'staticpatrol': 413 strategyFactories = [factories.AgentStaticPatrolStrategyFactory(paths, rulesOfTheSea) for i in range(len(ships))] 414 elif strategy == 'coordinatedpatrol': 415 # regular ships + joining ships 416 strategyFactories = [] 417 for role in roles: 418 if role == 'originalPatroller': 419 strategyFactories.append( 420 factories.AgentCoordinatedPatrolStrategyFactory(patrolPoints, 421 edgeLengths, rulesOfTheSea)) 422 elif role == 'joinsLater': 423 strategyFactories.append( 424 factories.AgentJoiningPatrolStrategyFactory(patrolPoints, 425 edgeLengths, rulesOfTheSea)) 426 else: 427 raise Exception('Unknown ship role: ' + role) 428 elif strategy == 'targettracking': 429 trackedShipIndices = [i for i in range(len(roles)) if roles[i] == 'tracked'] 430 trackingShipIndices = [i for i in range(len(roles)) if roles[i] == 'tracker'] 431 if len(trackingShipIndices) != len(positionOffsets): 432 raise Exception('Number of tracking ships must equal to the length of positionOffsets') 433 if len(trackedShipIndices) != len(paths): 434 raise Exception('Number of tracked ships must equal to the number of paths') 435 # regular ships + joining ships 436 strategyFactories = [] 437 for role in roles: 438 if role == 'tracked': 439 strategyFactories.append( 440 factories.AgentStaticPatrolStrategyFactory(paths, rulesOfTheSea)) 441 elif role == 'tracker': 442 strategyFactories.append( 443 factories.AgentTrackingPatrolStrategyFactory(trackedShipIndices, 444 trackingShipIndices, trackingDistance, positionOffsets, rulesOfTheSea)) 445 else: 446 raise Exception('Unknown ship role: ' + role) 447 else: 448 raise Exception('Unknown decision making strategy: ' + strategy) 449 450 # Allocate agents with the above factories 451 shipAgents = [agents.Agent( worldModel = worldModelFactory.create(shipIndex=i), 452 strategy = strategyFactories[i].create(agentIndex=i) ) 453 for i,s in enumerate(ships)] 454 print 'shipAgents', len(shipAgents) 455 456 457 458 # LOAD ENVIRONMENT MODEL 459 460 # task definition file possible overrided default filenames 461 wind = initWindFromFile(os.path.join(inputFilesDir, windFile)) 462 waterCurrents = initWaterCurrentsFromFile(os.path.join(inputFilesDir, waterFile)) 463 waves = initWavesFromFile(os.path.join(inputFilesDir, wavesFile)) 464 obstacles = initObstaclesFromFile(os.path.join(inputFilesDir, obstaclesFile)) 465 sea = seaModels.Sea(wind, waterCurrents, waves, obstacles) 466 467 468 ######################### SIMULATION ENVIRONMENT ############################ 469 470 471 # world state 472 initialState = state.State( sea, ships, shipInitialStates ) 473 474 # Simulation module 475 env = simulationEnvironment.SimulationEnvironment( initialState, shipAgents ) 476 477 # start the simulation 478 import mainGUI 479 if withDisplay: 480 mainGUI.runGui(env, shipType, worldModel, strategy, numSteps, speed) 481 else: 482 env.run(numSteps) 483 484 # write results 485 writeResults(resultsType, env, patrolPoints) 486 487 sys.stdout = origstdout
488 489 490 491 492 if __name__ == '__main__': 493 """ 494 The main function called when running the simulation 495 496 > python main.py 497 498 See the usage string for more details. 499 500 > python main.py --help 501 """ 502 args = readCommand( sys.argv[1:] ) # Get game components based on input 503 print 'Running simulation with the following options:' 504 for k, v in sorted(args.items()): 505 print k, '=', v 506 run( **args ) 507