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

Source Code for Module main

  1  ################################################################################## 
  2  # Copyright (c) 2012, 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 print 'Illegal options combination - exiting...' 107 sys.exit()
108 109 # TODO: Once we support mixed ship types, check combinations 110 # per each ship 111 112 113 114
115 -def readCommand( argv ):
116 """ 117 Processes the command used to run the simulation from the command line. 118 """ 119 from optparse import OptionParser 120 usageStr = """ 121 USAGE: python main.py <options> 122 EXAMPLE: python main.py --option <value> # TODO complete with real values 123 - #TODO explain what the example command do 124 """ 125 parser = OptionParser(usageStr) 126 127 # parser.add_option('-n', '--numGames', dest='numGames', type='int', 128 # help=default('the number of GAMES to play'), metavar='GAMES', default=1) 129 # parser.add_option('-l', '--layout', dest='layout', 130 # help=default('the LAYOUT_FILE from which to load the map layout'), 131 # metavar='LAYOUT_FILE', default='oneHunt') 132 # parser.add_option('-p', '--pacman', dest='pacman', 133 # help=default('the agent TYPE in the pacmanAgents module to use'), 134 # metavar='TYPE', default='BustersKeyboardAgent') 135 # parser.add_option('-a','--agentArgs',dest='agentArgs', 136 # help='Comma seperated values sent to agent. e.g. "opt1=val1,opt2,opt3=val3"') 137 # parser.add_option('-g', '--ghosts', dest='ghost', 138 # help=default('the ghost agent TYPE in the ghostAgents module to use'), 139 # metavar = 'TYPE', default='RandomGhost') 140 141 parser.add_option('-q', '--quietTextGraphics', action='store_true', dest='quietGraphics', 142 help=default('Generate minimal output and no graphics'), default=False ) 143 parser.add_option('-k', '--numSteps', dest='numSteps', type='int', 144 help=default("Number of steps to simulate"), default=10000) 145 parser.add_option('-d', '--inputFilesDir', dest='inputFilesDir', 146 help=default('the DIR in which input files are searched for'), 147 metavar='DIR', default='input_files') 148 parser.add_option('-f', '--taskFile', dest='taskFile', 149 help=default('A task-definition-language file. The file is searched for under the input files directory (defined by the flag -d)'), 150 default='task.py') 151 # parser.add_option('-r', '--resultsType', dest='resultsType', 152 # help=default('Type of results the simulator outputs.'), 153 # default='edgeGraphData') 154 parser.add_option('-t', '--shipType', dest='shipType', 155 help=default("The ship's model"), default='basic') 156 parser.add_option('-w', '--worldModel', dest='worldModel', 157 help=default("World model that the agent maintains"), default='complete') 158 parser.add_option('-s', '--strategy', dest='strategy', 159 help=default("Agent's desicion making strategy"), default='staticpatrol') 160 parser.add_option('-y', '--rulesOfTheSea', action='store_true', dest='rulesOfTheSea', 161 help=default('Respect the rules of the sea and yield when needed'), default=False ) 162 #### numShips is disabled because determined by the number of paths 163 # parser.add_option('-n', '--numShips', dest='numShips', type='int', 164 # help=default("Number of ships"), default=1) 165 166 167 # parser.add_option('-z', '--zoom', type='float', dest='zoom', 168 # help=default('Zoom the size of the graphics window'), default=1.0) 169 # parser.add_option('-f', '--fixRandomSeed', action='store_true', dest='fixRandomSeed', 170 # help='Fixes the random seed to always play the same game', default=False) 171 # parser.add_option('-t', '--frameTime', dest='frameTime', type='float', 172 # help=default('Time to delay between frames; <0 means keyboard'), default=0.1) 173 174 options, other = parser.parse_args() 175 if len(other) != 0: 176 raise Exception('Command line input not understood: ' + other) 177 args = dict() 178 179 # # Fix the random seed 180 # if options.fixRandomSeed: random.seed('bustersPacman') 181 # 182 # # Choose a layout 183 # args['layout'] = layout.getLayout( options.layout ) 184 # if args['layout'] == None: raise Exception("The layout " + options.layout + " cannot be found") 185 # 186 # # Choose a ghost agent 187 # ghostType = loadAgent(options.ghost, options.quietGraphics) 188 # args['ghosts'] = [ghostType( i+1 ) for i in range( options.numGhosts )] 189 # 190 # # Choose a Pacman agent 191 # noKeyboard = options.quietGraphics 192 # pacmanType = loadAgent(options.pacman, noKeyboard) 193 # agentOpts = parseAgentArgs(options.agentArgs) 194 # agentOpts['ghostAgents'] = args['ghosts'] 195 # pacman = pacmanType(**agentOpts) # Instantiate Pacman with agentArgs 196 # args['pacman'] = pacman 197 # 198 # import graphicsDisplay 199 # args['display'] = graphicsDisplay.FirstPersonPacmanGraphics(options.zoom, \ 200 # options.showGhosts, \ 201 # frameTime = options.frameTime) 202 # args['numGames'] = options.numGames 203 204 # Choose GUI / non-GUI 205 if options.quietGraphics: 206 args['withDisplay'] = False 207 else: 208 args['withDisplay'] = True 209 210 args['shipType'] = options.shipType 211 args['worldModel'] = options.worldModel 212 args['strategy'] = options.strategy 213 214 # args['numShips'] = options.numShips 215 216 args['numSteps'] = options.numSteps 217 218 args['inputFilesDir'] = options.inputFilesDir 219 220 args['taskFile'] = options.taskFile 221 222 # args['resultsType'] = options.resultsType 223 224 args['rulesOfTheSea'] = options.rulesOfTheSea 225 226 validateOptionCombinations(args) 227 228 return args
229 230 ###################################################### 231 # Initialization Functions - # 232 # Those function are here temporarily # 233 # until there is a flexible solution # 234 # for loading different models, perhaps # 235 # as a part of the task definition language # 236 ######################################################
237 -def initWindFromFile(filePath):
238 # just execute the (python) file and return a variable generated 239 exec(compile(open(filePath).read(), filePath, 'exec')) 240 return wind
241
242 -def initWaterCurrentsFromFile(filePath):
243 # just execute the (python) file and return a variable generated 244 exec(compile(open(filePath).read(), filePath, 'exec')) 245 return waterCurrents
246
247 -def initWavesFromFile(filePath):
248 # just execute the (python) file and return a variable generated 249 exec(compile(open(filePath).read(), filePath, 'exec')) 250 return waves
251
252 -def initObstaclesFromFile(filePath):
253 # just execute the (python) file and return a variable generated 254 exec(compile(open(filePath).read(), filePath, 'exec')) 255 return obstacles
256 257 #def initShipPatrolPointsAndPathsFromFile(filePath): 258 # # just execute the (python) file and return variables generated 259 # exec(compile(open(filePath).read(), filePath, 'exec')) 260 # return paths, patrolPoints, shipExternalStates 261 262 ###################################################### 263 # End initialization Functions # 264 ###################################################### 265 266 267 268 ###################################################### 269 # Results writing functions # 270 ###################################################### 271
272 -def writeResults(resultsType, env, patrolPoints):
273 """ 274 Write the simulation results. 275 276 TODO: this function needs to be further organized 277 once we have a better picture regarding what 278 types of results are usually needed. 279 Also the function arguments should be revised as a part 280 of the function reorganization. 281 282 @type resultsType: string 283 @param resultsType: a string identifier for the type of result to write 284 285 @type resultsType: SimulationEnvironment 286 @param resultsType: The simulation environment, used to extract data from 287 288 @type patrolPoints: array of (x,y) tuples 289 @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. 290 """ 291 #TODO: this function needs to be further organized 292 # once we have a better picture regarding what 293 # types of results are usually needed. 294 if resultsType == 'worstFreq': 295 296 f = open(resultsType + '.res', 'w') 297 298 # Simulation results, K+1, K-1 299 point2freq = dict([(point, mean([l[i] - l[i-1] for i in range(1,len(l))])) for point, l in env.visitTimes.items() ]) 300 worstCaseFreq1 = max(point2freq.values()) 301 # print >> sys.stderr, 'WORSTCASE FREQ:', worstCaseFreq1 302 f.write('WORSTCASE FREQ: ' + str(worstCaseFreq1) + '\n') 303 f.write('patrolPoints=' + str(patrolPoints) + '\n') 304 f.write('visitTimes=' + str(env.visitTimes) + '\n') 305 # print >>sys.stderr, 'visitTimes =', env.visitTimes 306 307 # print >> sys.stderr, 'numShipsForPatrol', numShips 308 # worstCaseFreq2 = max( [mean([l[i] - l[i-1] for i in range(1,len(l))]) for l in env.state.times.values()] ) 309 # print >> sys.stderr, 'verification WORSTCASE FREQ:', worstCaseFreq2 310 311 # if worstCaseFreq1 != worstCaseFreq2: 312 # raise Exception('Whats going on? different worst cases freqs') 313 314 f.close() 315 316 elif resultsType == 'edgeGraphData': 317 318 f = open(resultsType + '.py', 'w') 319 f.write('patrolPoints=' + str(patrolPoints) + '\n') 320 f.write('edgeLengths=' + str(env.edgeLengths) + '\n') 321 f.close()
322 323 ###################################################### 324 # End Results writing functions # 325 ###################################################### 326 327 328 329 330 331 332 333 334 #def run(withDisplay, shipType, worldModel, strategy, numShips, inputFilesDir, taskFile): 335 # disabled numShips, because determined by the number of paths
336 -def run(withDisplay, shipType, worldModel, strategy, inputFilesDir, taskFile, numSteps, rulesOfTheSea):
337 """ 338 The main flow of the application. The arguments are generated by command line options, or their defaults. 339 340 @type withDisplay: boolean 341 @param withDisplay: Tells whether to run in a GUI mode or in non-GUI mode 342 343 @type shipType: string 344 @param shipType: The ship model to allocate 345 346 @type worldModel: string 347 @param worldModel: The world model the agent has 348 349 @type strategy: string 350 @param strategy: The decision-making strategy the agent has 351 352 @type inputFilesDir: string 353 @param inputFilesDir: The directory in which the input files are 354 355 @type taskFile: string 356 @param taskFile: A task-definition-language file 357 358 @type numSteps: int 359 @param numSteps: Number of steps to simulate 360 361 @type rulesOfTheSea: boolean 362 @param rulesOfTheSea: Tells whether to respect the rules of the sea 363 """ 364 365 366 367 ########### TASK INITIALIZATION - DEFINED BY TASK DEFINITION FILE ########### 368 369 370 # defaults 371 windFile = 'wind.py' 372 waterFile = 'waterCurrents.py' 373 wavesFile = 'waves.py' 374 obstaclesFile = 'obstacles.py' 375 resultsType = 'edgeGraphData' 376 377 378 379 # LOAD TASK DEFINITION FILE, POSSIBLY OVERRIDES DEFAULTS 380 381 pathToTaskFile = os.path.join(inputFilesDir, taskFile) 382 exec(compile(open(pathToTaskFile).read(), pathToTaskFile, 'exec')) 383 384 385 386 # LOAD ENVIRONMENT MODEL 387 388 # task definition file possible overrided default filenames 389 wind = initWindFromFile(os.path.join(inputFilesDir, windFile)) 390 waterCurrents = initWaterCurrentsFromFile(os.path.join(inputFilesDir, waterFile)) 391 waves = initWavesFromFile(os.path.join(inputFilesDir, wavesFile)) 392 obstacles = initObstaclesFromFile(os.path.join(inputFilesDir, obstaclesFile)) 393 sea = seaModels.Sea(wind, waterCurrents, waves, obstacles) 394 395 396 397 # CREATE A TASK TO BE EXECUTED 398 399 # task variable was loaded from task definition file, along with other related vars 400 try: 401 task 402 except NameError: 403 print "-E- task definition file must define a 'task' variable" 404 sys.exit(1) 405 if task == 'TASK_STATIC_PATROL': 406 try: 407 patrolPoints 408 shipInitialPositions 409 paths 410 except NameError: 411 print "-E- not all variable are defined for task TASK_STATIC_PATROL" 412 sys.exit(1) 413 shipExternalStates= [state.ShipExternalState(*shipInitialPositions[i]) for i in range(len(paths))] 414 numShips = len(shipExternalStates) 415 strategy = 'staticpatrol' #overriding strategy 416 417 elif task == 'TASK_ONLINE_HEURISTIC_DIVIDE': 418 try: 419 patrolPoints 420 edgeLengths 421 numShips 422 except NameError: 423 print "-E- not all variable are defined for task TASK_ONLINE_HEURISTIC_DIVIDE" 424 sys.exit(1) 425 # bound patrol area and distribute ships randomly 426 maxX = max(patrolPoints, key=operator.itemgetter(0))[0] + 100 # why 100? no special reason 427 minX = min(patrolPoints, key=operator.itemgetter(0))[0] - 100 # why 100? no special reason 428 maxY = max(patrolPoints, key=operator.itemgetter(1))[1] + 100 # why 100? no special reason 429 minY = min(patrolPoints, key=operator.itemgetter(1))[1] - 100 # why 100? no special reason 430 numJoiningShips = 3 431 numRegularShips = numShips - numJoiningShips 432 shipExternalStates = [state.ShipExternalState(random.randint(minX,maxX), 433 random.randint(minY,maxY), 434 orientation=random.random() * 360 - 180, 435 speed=0, 436 angularSpeed = 0) for i in range(numRegularShips)] 437 # appending states for joining ships 438 offset = 0 439 Y = maxY + 100 440 for i in range(numJoiningShips): 441 shipExternalStates.append(state.ShipExternalState(minX + offset, Y, orientation=-90)) 442 offset += 50 443 444 strategy = 'coordinatedpatrol' #overriding strategy 445 else: 446 raise Exception('Unknown task type') 447 448 try: 449 shipExternalStates 450 numShips 451 strategy 452 except NameError: 453 print "-E- numShips and strategy must be defined here" 454 sys.exit(1) 455 456 457 458 459 ############# AGENT INITIALIZATION - DEFINED BY CMD LINE FLAGS ############## 460 461 462 # CREATE SHIPS 463 464 if shipType == 'basic': 465 shipFactory = factories.BasicShipFactory() 466 else: 467 raise Exception('Unknown ship type: ' + shipType) 468 ships = [shipFactory.create() for i in range(numShips)] 469 # TODO: When we support mixed ship models, 470 # allocate an agent based on ship, and send it the possible actions 471 # for this ship 472 473 474 475 # CREATE SHIP-CONTROLLING-AGENTS 476 477 # Choose a world model factory 478 if worldModel == 'complete': 479 worldModelFactory = factories.CompleteWorldModelFactory() 480 else: 481 raise Exception('Unknown world model type: ' + worldModel) 482 483 # Choose a decision making strategy 484 if strategy == 'circling': 485 strategyFactories = [factories.CirclingStrategyFactory() for i in range(len(ships))] 486 elif strategy == 'rrt': 487 strategyFactories = [factories.RRTStrategyFactory(paths, rulesOfTheSea) for i in range(len(ships))] 488 elif strategy == 'pid': 489 strategyFactories = [factories.PIDStrategyFactory(rulesOfTheSea) for i in range(len(ships))] 490 elif strategy == 'measuredist': 491 strategyFactories = [factories.MeasureEdgeLengthsFactory() for i in range(len(ships))] 492 elif strategy == 'staticpatrol': 493 strategyFactories = [factories.AgentStaticPatrolStrategyFactory(paths, rulesOfTheSea) for i in range(len(ships))] 494 elif strategy == 'coordinatedpatrol': 495 # regular ships + joining ships 496 strategyFactories = [factories.AgentCoordinatedPatrolStrategyFactory(patrolPoints, 497 edgeLengths, 498 rulesOfTheSea) 499 for i in range(numShips - numJoiningShips)] 500 for i in range(numJoiningShips): 501 strategyFactories.append(factories.AgentJoiningPatrolStrategyFactory(patrolPoints, edgeLengths, rulesOfTheSea)) 502 else: 503 raise Exception('Unknown decision making strategy: ' + strategy) 504 505 # Allocate agents with the above factories 506 shipAgents = [agents.Agent( worldModel = worldModelFactory.create(shipIndex=i), 507 strategy = strategyFactories[i].create(agentIndex=i) ) 508 for i,s in enumerate(ships)] 509 print 'shipAgents', len(shipAgents) 510 511 512 ######################### SIMULATION ENVIRONMENT ############################ 513 514 515 # world state 516 initialState = state.State( sea, ships, shipExternalStates ) 517 518 # Simulation module 519 env = simulationEnvironment.SimulationEnvironment( initialState, shipAgents ) 520 521 # start the simulation 522 import mainGUI 523 if withDisplay: 524 mainGUI.runGui(env, shipType, worldModel, strategy, numSteps) 525 else: 526 env.run(numSteps) 527 528 # write results 529 writeResults(resultsType, env, patrolPoints)
530 531 532 533 534 if __name__ == '__main__': 535 """ 536 The main function called when running the simulation 537 538 > python main.py 539 540 See the usage string for more details. 541 542 > python main.py --help 543 """ 544 args = readCommand( sys.argv[1:] ) # Get game components based on input 545 print 'Running simulation with the following options:' 546 for k, v in sorted(args.items()): 547 print k, '=', v 548 run( **args ) 549