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

Source Code for Module playerai

  1  import time 
  2  import threading 
  3  import thread 
  4  import random 
  5   
  6  from random import choice, randint 
  7  from pyglet.event import EventDispatcher 
  8  from cocos.layer import * 
  9  from cocos.text import * 
 10  from cocos.scene import Scene 
 11  from cocos.actions.interval_actions import Delay 
 12  from constants import * 
 13  from research import RESEARCH 
 14  from models import * 
 15  from maps import Core 
 16  from game_layers import TransTimer 
 17  from utils import * 
 18  from imageLayer import * 
 19  from player_new import Player 
 20  from utils import * 
 21  import objects 
 22   
 23  ''' 
 24  AI TODO: Non Gratum Anus Rodentum: Yasin Dara 
 25   
 26  - [DONE] Need to prevent crashing by preventing "move" and "attack" from being called too rapidly. (check if action is being performed, cocos method) 
 27  - [DONE] Need to figure out how to make the AI detect TYPES of troops/units so that we don't try to attack with a server or something stupid like that. 
 28  - [DONE] Also need to figure out how to read from the map file directly to "cheat" and make the AI knowledgeable about all starting locations of Servers. 
 29  - [DONE (Fix Swarm)] Heatmap: AI needs to prioritize attacking groups of four human units that are on a single vertex. 
 30  - [DEFERRED] N-Gram 
 31  - [DONE] Yasin just discovered the need for spoof. 
 32  - [DONE] Build a function called "Swarm" that does exactly what you think it does. }:-) 
 33  - [DONE] Trigger AI only after 60 seconds or when AI AS is invaded. 
 34  - [DONE] Randomly attack troops and buildings with free range AI troops to add variety to AI movement and gameplay. 
 35  - [DEFERRED] Allow the AI to use different attack strategies for different types of troops and units.   
 36   
 37        .o.       ooooo  
 38       .888.      `888'  
 39      .8"888.      888   
 40     .8' `888.     888   
 41    .88ooo8888.    888   
 42   .8'     `888.   888   
 43  o88o     o8888o o888o  
 44   , _                                    _        
 45  /|/ \                                  | |       
 46   |   |   __ _|_  _           __   ,_   | |   ,   
 47   |   |  /  \_|  |/  |  |  |_/  \_/  |  |/_) / \_ 
 48   |   |_/\__/ |_/|__/ \/ \/  \__/    |_/| \_/ \/  
 49   
 50  (C) Carleton College 2013 
 51   
 52  ''' 
 53   
 54   
55 -class ComputerPlayer(Player):
56 ''' 57 The premise of this AI is to use a FINITE STATE MACHINE to react "intelligently" to a human player. 58 The states of this machine are defined below. An "N-Gram" will be used to collect data about player moves. 59 The "A-Star" pathfinding algorithm will be employed to find the most efficient way to attack. The AI will 60 prioritize vertices containing human-built troops based on a "heatmap", which allows the AI to prioritize 61 resources and its own troops. 62 '''
63 - def __init__(self, ID):
64 super(ComputerPlayer, self).__init__(ID) 65 self.server = objects.Objects.get_controller() 66 self.totalTime = 0 67 self.aiRunning = False 68 self.theMap = self.server.map 69 self.aiID = ID 70 self.aiLevels = ["Easy", "Medium", "Hard", "Godlike"] # I wish there was an enumerator in Python. 71 self.aiLevel = "Easy" # Don't mess with this, it isn't implemented yet. 72 self.aiStates = ["Initial", "Waiting", "Scanning", "Researching", "Attacking", "Building", "Defending", 73 "Determining"] # Finite State Machine States. 74 self.maxMovePerMove = 4 # This could be better named. 75 self.maxAttackPerMove = 5 # Same here. 76 self.enemy = self.server.players # That's the human. 77 self.color = (70, 0, 180) # Feel free to change. 78 self.ai_cur_state = "" # Always set to blank on new instance of AI! 79 self.ai_prev_state = "Initial" # Always set to initial on new instance of AI! 80 self.aiTroops = 0 # The number of troops available to the AI. 81 self.humanTroops = 0 # Number of troops available to human player. 82 self.allTroops = 0 # All troops on the board. 83 self.humanCpus = 1 # Will always be at least one, else AI wins. 84 self.boardResearchLevel = 1 # The level of research currently allowed on the board. 85 self.discovered = False # Set to true when humans make first contact (discover) AI troops. 86 self.health = 1000 # Set to what Kat decides for the top menu. 87 self.humanHealth = 1000 # Self explanatory. 88 self.adjacentFreeSlots = 0 # To a vertex. 89 self.genericCounter = 0 # Used as a makeshift timer, sometimes. 90 self.numUnitsToBuild = 0 # This is just here. 91 self.enemyBuildingVertices = [] # Will be filled in by scan function. 92 self.enemyTroopVertices = [] # Will be filed in by scan function. 93 self.highPriorityEnemyVertices = [] # If four troops are in a vertex, it becomes a higher priority than the server. 94 self.aiTroopVertices = [] # Will keep track of ai troops [unit, unit.curVertex] 95 self.defensePriorityOneLocation = [] # [unit, vid] Location (vid) of my server! Defend at all costs! 96 self.timeWaited = 0 # Self explanatory. Add "dt" to this periodically to get the total time waited since the AI was first called. 97 self.swarmCompletion = 0 # Number of units already built into a partially completed "swarm". 1 swarm = 4 units on 1 vertex. 98 self.swarmVertices = [] # Vertices with available swarms. 99 self.swarmVertex = None # The current vertex where a swarm is being built. 100 self.swarmNumber = 0 # The number of the current swarm that we are about to marshall as our troops. 101 self.troopvertex = None # A counter to keep track of unit delegation within a swarm. 102 self.lastAttackTroop = None # if this is still moving, then we can't ask it to attack! 103 self.troopsPOWMIA = [] # Troops that have been sent off on their duty. These might be destroyed! 104 self.troopsAtTheReady = [] # Troops that we have at the ready! 105 self.troopsPoised = [] # The AI can't issue an attack command right away. These units are in position to attack on the next loop. 106 self.threatVertices = [] # Vertices that are within 2 distance of our Server. 107 self.respondingTroops = [] # Troops designated as currently attacking or moving towards a unit to attack. 108 self.myASes = [] # A list of the AI ASes. 109 self.aiVertices = [] # A list of the vertices that I own. 110 self.buildLimit = 0 # The number of troops that I am allowed to build for this level. 111 self.numTroopsBuilt = 0 # The number of troops that the AI has built. 112 self.triggerTime = 60 # The number of seconds the AI waits before attacking.
113
114 - def scan(self):
115 pass
116
117 - def scan_all_vertices(self):
118 119 for player in self.server.players.values(): 120 if player.pid != self.pid: 121 listOfUnits = player.units.values() 122 self.humanTroops = len(listOfUnits) 123 if player.pid == self.pid: 124 listOfUnits = player.units.values() 125 self.aiTroops = len(listOfUnits) 126 127 # Where is my server? 128 for unit in player.units.values(): 129 if issubclass(type(unit), Building) == True: 130 # # /print "The type of this unit is: ", str(type(unit)), "and the type of that is ", type(type(unit)) 131 if type(unit) == Server: 132 # if str(type(unit)) == "<class 'models.Server'>": # OH GOD WHY. 133 # # /print "My server is located at ", unit.curVertex.vid 134 self.defensePriorityOneLocation = [unit, int(unit.curVertex.vid)] 135 136 # Where are the enemy servers? 137 self.enemyBuildingVertices = [] # Reset "global" list. 138 for vertex in self.server.map.vertices.values(): 139 if type(vertex.building) == Server and vertex.building.pid != self.pid: 140 # # /print "There is an enemy server located at: ", vertex.vid 141 self.enemyBuildingVertices.append(int(vertex.vid)) 142 # # /print "This is the list containing enemy servers: ", self.enemyBuildingVertices 143 144 # Where are my troops? 145 self.aiTroopVertices = [] # Reset "global" list. 146 for unit in player.units.values(): 147 # print unit, type(unit), issubclass(type(unit), Troop) 148 if issubclass(type(unit), Troop) == True and unit.pid == self.pid: 149 if unit.power > 0: 150 self.aiTroopVertices.append([unit, unit.curVertex.vid]) 151 # # /print "My (AI) Troops: ", self.aiTroopVertices 152 153 # Where are the enemy troops? 154 self.enemyTroopVertices = [] 155 for player in self.server.players.values(): 156 if player.pid != self.pid: 157 for unit in player.units.values(): 158 if issubclass(type(unit), Troop) == True: 159 self.enemyTroopVertices.append([unit, unit.curVertex.vid]) 160 # # /print "Enemy Troops: ", self.enemyTroopVertices 161 162 # What should we prioritize? 163 # Logic: If there are 4 troops clustered somewhere, attack this first, then the server. 164 # The above may be flawed logic, but no AI is perfect. 165 self.highPriorityEnemyVertices = [] 166 for vertex in self.server.map.vertices.values(): 167 priorityCounter = 0 168 for slot in vertex.troopSlots.values(): 169 if issubclass(type(slot.troop), Troop) and slot.troop.pid != self.pid: 170 priorityCounter += 1 171 if priorityCounter > 3: 172 self.highPriorityEnemyVertices.append(vertex) 173 # # /print "Enemy Vertices with 4 Troops: ", self.highPriorityEnemyVertices 174 175 # Where are my ASes? And where are my vertices? 176 self.myASes = [] 177 self.aiVertices = [] 178 for vertex in self.server.map.vertices.values(): 179 if vertex.building != None and vertex.building.pid == self.pid: 180 self.myASes.append(vertex.asID) 181 for myvertex in self.server.map.vertices.values(): 182 if myvertex.asID in self.myASes: 183 self.aiVertices.append(myvertex) 184 185 # Set build limit. 186 self.buildLimit = 4 * len(self.aiVertices) 187 # print "INITIAL BUILD LIMIT: ", self.buildLimit 188 189 ''' 190 # DISCARDED CODE --------------------------------------------------------------------------------- 191 #for slot in vertex.troopSlots.values(): 192 #for slot in vertex.troopSlots.values(): 193 ## /print "At vertex ", vertex.vid, " there is a ", vertex.building, " with this stuff ", slot.troop 194 195 # A GOOD SET OF TROOPS: DOS,2 DOS,2 DOS,18 DOS,18 Ping,31 DOS,17 Ping,35 DOS,14 DOS,14 DOS,14 DOS,14 Ping,35 Ping,35 Ping,35 Ping,33 Ping,33 196 197 #self.human_troops = 0 198 #self.ai_troops = 0 199 200 #print self.troops 201 #for vertex in self.enemy.availableTroops[0].curVertex.adjacentVertices: 202 #print vertex.troopSlots 203 '''
204 - def trigger(self):
205 if len(self.aiVertices) > 0: 206 for vertex in self.aiVertices: 207 for slot in vertex.troopSlots.values(): 208 if slot.troop != None: 209 if slot.troop.pid != self.pid: 210 #print "[AI] TRIGGERED!!!", slot.troop, slot.troop.curVertex.vid 211 return True 212 return False
213 214
215 - def mathematical_probability(self, boundary=50):
216 randomInt = randint(1,100) 217 if randomInt <= boundary: 218 return True 219 else: 220 return False
221 222 # Pick a unit that isn't in a swarm, choose something random on the map to attack, and send if off!
224 #Attack with Poised Units 225 attackingUnit = None 226 if len(self.troopsPoised) > 0: 227 for poisedTroop in self.troopsPoised: 228 self.perform_single_target_attack(poisedTroop[0],poisedTroop[1]) 229 # /print "[ai] A random attack against", poisedTroop[0], "by", poisedTroop[1], "from", poisedTroop[1].curVertex.vid, "to", poisedTroop[0].curVertex.vid 230 if poisedTroop in self.troopsPoised: 231 self.troopsPoised.remove(poisedTroop) 232 ## /print "LIST OF THINGS GETTING ATTACKED:", self.troopsPoised 233 234 if len(self.troopsAtTheReady) == 0: 235 for unit in self.units.values(): 236 #print "[AI] ALL AI UNITS", unit 237 if unit.curVertex != self.swarmVertex and unit.curVertex.vid not in self.swarmVertices and issubclass(type(unit), DOS) == True: 238 #print "[AI] FREE RANGE UNIT: ", unit 239 attackingUnit = unit 240 self.troopsAtTheReady.append(attackingUnit) 241 # /print "[ai] AVAILABLE FREE RANGE TROOPS: ", self.troopsAtTheReady 242 else: 243 for unit in self.troopsAtTheReady: 244 attackingUnit = unit 245 246 #print "[AI] ATTACKING UNIT MUST BE DEFINED: ", attackingUnit 247 if attackingUnit == None: 248 #print "[AI] There are no free range units left. Cannot attack!" 249 self.reassign_troops() 250 return 251 #Randomly Assign a Target 252 253 254 if self.mathematical_probability(50) == True: 255 #Attack Building 256 for vertex in self.server.map.vertices.values(): 257 if vertex.building != None and vertex.building.pid != self.pid: 258 attackFrom = self.get_closest_safe_adjacent_vertex(vertex.vid, False) 259 if attackingUnit.isSelectable == True and attackFrom != None: 260 #print "[AI] Moving troop to attack building: ", attackingUnit 261 self.perform_single_target_random_move(attackFrom, attackingUnit) 262 self.troopsPoised.append([vertex.building, attackingUnit]) 263 if attackingUnit in self.troopsAtTheReady: 264 self.troopsAtTheReady.remove(attackingUnit) 265 266 else: 267 #Attack Troop 268 for player in self.server.players.values(): 269 if player.pid != self.pid: 270 for eunit in player.units.values(): 271 if issubclass(type(eunit), Troop) == True: 272 # /print "UNIT GETTING ATTACKED: ", eunit, "at", eunit.curVertex.vid 273 attackFrom = self.get_closest_safe_adjacent_vertex(eunit.curVertex.vid, False) 274 if attackingUnit.isSelectable == True and attackFrom != None: 275 #print "[AI] Moving troop to attack troop: ", attackingUnit 276 self.perform_single_target_random_move(attackFrom, attackingUnit) 277 self.troopsPoised.append([eunit, attackingUnit]) 278 if attackingUnit in self.troopsAtTheReady: 279 self.troopsAtTheReady.remove(attackingUnit)
280 281 ## /print "Available Units: ", unit, "at", unit.curVertex.vid 282 #for swarmVid in self.swarmVertices: 283 ## /print "THINGS NOT TO INCLUDE!", swarmVid, self.server.map.vertices[str(swarmVid)] 284 #if unit.curVertex != self.server.map.vertices[str(swarmVid)]: 285 286 # Builds a swarm vertex (i.e. fills a vertex with DOS troops belonging to AI)
287 - def swarm_vertex(self, swarmCompletion, swarmVertex=None):
288 if swarmVertex == None: 289 self.swarmVertex = self.get_closest_empty_vertex() 290 returnVal = self.build_a_troop("DOS", self.swarmVertex.vid) 291 if returnVal != None: 292 self.swarmCompletion += 1 293 if self.swarmCompletion == 4: 294 self.swarmCompletion = 0 295 self.swarmVertices.append(returnVal.curVertex.vid) 296 # /print "[ai] A new swarm is available: ", self.swarmVertices 297 self.swarmVertex = None 298 else: 299 return self.swarmCompletion 300 return self.swarmCompletion
301 302 303 # Builds a DOS at a specified location. TODO: Update this function to build more than DOS troops.
304 - def build_a_troop(self, unitType, locationVid):
305 # /print "[ai] Building a ", unitType," at location ", locationVid 306 # print "CUR BUILD LIMIT BEFORE ATTEMPT TO BUILD: ", self.buildLimit, self.numTroopsBuilt 307 if self.numTroopsBuilt > self.buildLimit: 308 returnVal = None 309 return returnVal 310 else: 311 returnVal = self.server.build_unit("DOS", self, locationVid) 312 self.numTroopsBuilt += 1 313 return returnVal
314 315 # Finds a fully empty vertex on the map and returns it. Checks for both buildings and troops.
316 - def get_closest_empty_vertex(self):
317 for vertex in self.server.map.vertices.values(): 318 ## /print "SUBCLASSING VERTICES: ", type(vertex) 319 if not type(vertex) == Core and vertex.building == None and vertex.troopSlots.values() == []: 320 return vertex
321 322 # DEPRECATED.
323 - def grab_inactive_troop(self):
324 pass
325 326 # Move troops accordingly, and attack with them... 327 # A swarm consists of 4 DOS units that work as a unit.
328 - def marshall_troops(self, swarmNumber=0):
329 numTroopsToActivate = 0 330 if swarmNumber != 0: 331 swarmNumber = self.swarmNumber 332 if len(self.swarmVertices) != 0 and len(self.highPriorityEnemyVertices) != 0: 333 marshallVertex = self.swarmVertices[swarmNumber] 334 attackersPosition = self.highPriorityEnemyVertices[swarmNumber] 335 attackFromPosition = self.get_closest_safe_adjacent_vertex(attackersPosition.vid, safe=False) 336 ## /print "ATTACKING FROM", attackFromPosition.vid 337 ## /print "ATTACKING THIS VERTEX", attackersPosition.vid 338 ## /print "NUMBER OF TROOPS THAT YOU CAN SEND", 4 - len(attackFromPosition.troopSlots.values()) 339 ## /print "THE TROOP THAT YOU'RE MOVING", self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.values()[troopvertex].troop 340 ## /print "THE TROOP THAT YOU'RE ATTACKING", self.highPriorityEnemyVertices[swarmNumber].troopSlots.values()[troopvertex].troop 341 numTroopsToActivate = 4 - len(attackFromPosition.troopSlots.values()) 342 ## /print "SWARMNUMBER", swarmNumber 343 344 #Move! 345 ## /print "UNIT 1", self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.values()[].troop 346 ## /print "UNIT 2", self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.values()[].troop 347 ## /print "UNIT 3", self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.values()[].troop 348 ## /print "UNIT 4", self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.values()[].troop 349 ## /print "ATTEMPTING TO MOVE -----------------------------------------------------------" 350 for key in self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.keys(): 351 attackTroop = self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots[key].troop 352 self.perform_single_target_random_move(attackFromPosition, attackTroop) 353 354 #self.perform_single_target_random_move(attackFromPosition, self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.values()[0].troop) 355 #self.perform_single_target_random_move(attackFromPosition, self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.values()[1].troop) 356 #self.perform_single_target_random_move(attackFromPosition, self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.values()[0].troop) 357 #self.perform_single_target_random_move(attackFromPosition, self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.values()[0].troop) 358 359 #Attack! 360 #self.perform_single_target_attack(self.highPriorityEnemyVertices[swarmNumber].troopSlots.values()[troopvertex].troop, self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.values()[troopvertex].troop) 361 ## /print "[ai] MOVED AND ATTACKED -----------------------------------------------------------" 362 self.highPriorityEnemyVertices.pop(0) 363 self.swarmVertices.pop(0) 364 # /print "[ai] A high priority vertex has been swarmed!" 365 self.troopvertex = attackFromPosition 366 self.lastAttackTroop = attackTroop 367 return 368 else: 369 # /print "[ai] No swarm ready, or no high priority vertices." 370 return 0
371
372 - def swarm_attack(self, attackFromPosition):
373 # /print "[ai] NOW ATTACKING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" 374 if self.lastAttackTroop.isSelectable == False: 375 # /print "[ai] STILL MOVING! DAMMIT!" 376 return 377 else: 378 pass 379 # /print "[ai] ATTACK OK" 380 attackTroops = [] 381 victimTroops = [] 382 for key in attackFromPosition.troopSlots.keys(): 383 attackTroops.append(attackFromPosition.troopSlots[key].troop) 384 if len(self.highPriorityEnemyVertices) > 0: 385 vertexOfInterest = self.highPriorityEnemyVertices[0] 386 if vertexOfInterest == None: 387 #print "[AI] Couldn't locate any high priority vertices." 388 return 389 for key in vertexOfInterest.troopSlots.keys(): 390 victimTroops.append(vertexOfInterest.troopSlots[key].troop) 391 for i in range(4): 392 self.perform_single_target_attack(victimTroops[i], attackTroops[i]) 393 self.troopvertex = None 394 self.highPriorityEnemyVertices.remove(vertexOfInterest) # We don't care if attack was successful. 395 else: 396 #print "[AI] An enemy swarm was detected but troops are not available to respond yet." 397 return
398 399 400 401 # Returns an empty vertex (a vertex with no enemy troops or one enemy troop) 402 # that is near a vertex of interest.
403 - def get_closest_safe_adjacent_vertex(self, vidOfInterest, safe=True):
404 vertexOfInterest = self.server.map.vertices[str(vidOfInterest)] 405 if safe == False: 406 for vertex in self.server.map.vertices.values(): 407 if vertexOfInterest in vertex.adjacentVertices: 408 ## /print "[ai] The adjacent vertex is: ", vertex.vid 409 return vertex 410 elif safe == True: 411 for vertex in self.server.map.vertices.values(): 412 if vertexOfInterest in vertex.adjacentVertices: 413 if vertex.troopSlots.values() == []: 414 ## /print "[ai] The safest vertex is: ", vertex.vid 415 return vertex
416
417 - def check_if_valid_for_attack(self, attacker, victim):
418 # /print "[ai] Check if this unit is a valid attacker: ", attacker 419 return issubclass(type(attacker), Troop) and attacker.power > 0
420
421 - def perform_single_target_attack(self, victim="void", attacker="void"):
422 if victim == "void": 423 for player in self.server.players.values(): 424 if player.pid != self.pid: 425 listOfUnits = player.units.values() 426 victim = choose(listOfUnits) 427 break 428 if attacker == "void": 429 listOfUnits = self.units.values() 430 attacker = choose(listOfUnits) 431 if self.check_if_valid_for_attack(attacker, victim): 432 # # /print "Attacker: ", attacker 433 # # /print "Victim: ", victim 434 self.server.attack_unit(victim, attacker) 435 else: 436 # /print "[ai] can't attack" 437 pass
438
439 - def perform_single_target_random_move(self, destination=0, troopToMove=0):
440 if destination == 0 or troopToMove == 0: 441 for unit in self.units.values(): 442 thesource = unit.curVertex 443 thedest = choice(unit.curVertex.adjacentVertices) 444 thetroop = unit 445 thepid = self.pid 446 ## /print "[ai] self.pid", self.pid 447 # thepath = self.server.map.get_path(thesource, thedest, thepid, thetroop) # WTF, Don't look at client controller. 448 # # /print "thepath", thepath # We apparently don't need a dijkstra's path. I don't know why, but who cares. 449 if not thetroop.are_actions_running(): 450 returnVal = self.server.move_unit(thedest, thetroop, thepid) 451 thesource = "" 452 thedest = "" 453 thetroop = "" 454 thepid = "" 455 thepath = "" 456 ## /print "[ai] I have moved a unit! ", returnVal 457 elif not troopToMove.are_actions_running(): 458 returnVal = self.server.move_unit(destination, troopToMove, self.pid)
459 ## /print "[ai] Path: ", returnVal 460 # # /print "[ai] Execute Action: I've moved a unit!" 461 462 # Check if there is an enemy unit that has a coincident vertex with one of our units. 463 # This is flawed: Careful players will craftily attack from one vertex away.
464 - def check_defenses(self):
465 for player in self.server.players.values(): 466 if player.pid != self.pid: 467 enemyUnits = player.units.values() 468 for enemyUnit in enemyUnits: 469 for aiUnit in self.units.values(): 470 if aiUnit.curVertex == enemyUnit.curVertex: 471 # /print "[ai] The two coincident units are: ", aiUnit, " at ", aiUnit.curVertex.vid, " and ", enemyUnit, " at ", enemyUnit.curVertex.vid 472 return True 473 return False
474 475 # If there is an enemy troop within 2 distance of our server, just kill it using Free Range troops. 476 # The AI is smart enough to realize that if you handshake to the server's vertex, it will defend the 477 # server from units located at the new vertex as well.
478 - def defend_our_server(self):
479 #print "[AI] defend_our_server function called." 480 threateningTroops = [] 481 serverVid = self.defensePriorityOneLocation[1] 482 serverUnit = self.defensePriorityOneLocation[0] 483 serverVertex = self.server.map.vertices[str(serverVid)] 484 #print "SERVER VERTEX", serverVertex, serverVertex.vid 485 #print "SERVER VERTEX ADJACENT VERTICES", serverVertex.adjacentVertices 486 for adjVertex in serverVertex.adjacentVertices: 487 if adjVertex not in self.threatVertices: 488 self.threatVertices.append(adjVertex) 489 for distantVertex in adjVertex.adjacentVertices: 490 if distantVertex not in self.threatVertices: 491 self.threatVertices.append(distantVertex) 492 #print "THREAT VERTICES: ", self.threatVertices 493 for player in self.server.players.values(): 494 if player.pid != self.pid: 495 enemyUnits = player.units.values() 496 #print "ENEMY UNITS: ", enemyUnits 497 for eUnit in enemyUnits: 498 for vertex in self.threatVertices: 499 if eUnit.curVertex == vertex: 500 threateningTroops.append(eUnit) 501 #print "[AI] Detected troops close to or attacking our server:" 502 #for item in threateningTroops: 503 #print "[AI] <Detected> ", item, item.curVertex.vid 504 self.attack_with_list_of_troops(threateningTroops)
505 506
507 - def attack_with_list_of_troops(self, victimList):
508 for victim in victimList: 509 availableTroops = self.get_units_within_attacking_distance(victim.curVertex) 510 if len(availableTroops) > 0: 511 attackingTroop = choice(availableTroops) 512 self.perform_single_target_attack(victim, attackingTroop) 513 self.respondingTroops.append(attackingTroop) 514 else: 515 #print "[AI] No troops within range to respond to threat. Building." 516 responseVertex = self.get_closest_safe_adjacent_vertex(victim.curVertex.vid) 517 if responseVertex == None: 518 #print "[AI] Troop has already been taken care of, no need to build." 519 return 520 self.build_a_troop("DOS", responseVertex.vid)
521 522 523
524 - def get_units_within_attacking_distance(self, passVertex):
525 #print "PASSVERTEX", passVertex.vid 526 theAdjacentVertices = [passVertex] 527 theAttackingTroops = [] 528 for adjVertex in passVertex.adjacentVertices: 529 theAdjacentVertices.append(adjVertex) 530 for distantVertex in adjVertex.adjacentVertices: 531 theAdjacentVertices.append(distantVertex) 532 for searchVertex in theAdjacentVertices: 533 for key in searchVertex.troopSlots.keys(): 534 if searchVertex.troopSlots[key].troop.pid == self.pid and searchVertex.troopSlots[key].troop not in self.respondingTroops: 535 theAttackingTroops.append(searchVertex.troopSlots[key].troop) 536 #print "[AI] Troops available to respond to threat:", theAttackingTroops 537 return theAttackingTroops
538
539 - def reassign_troops(self):
540 #print "[AI] Reassigning Troops" 541 for unit in self.units.values(): 542 if issubclass(type(unit), DOS) == True and not unit.is_attacking: 543 if unit in self.respondingTroops: 544 self.respondingTroops.remove(unit) 545 #print "[AI] Unit designated as no longer responding: ", unit, unit.curVertex.vid 546 if unit not in self.troopsAtTheReady: 547 self.troopsAtTheReady.append(unit)
548 #print "[AI] Unit designated as at the ready: ", unit, unit.curVertex.vid 549 550 # DEPRECATED. Keep here for reference to how the AI reacts to things.
552 # If an exploratory human troop comes to rest in a vertex with one of our troops in it: 553 # Our troop (the AI troop) will immediately start attacking the incoming troop. 554 for player in self.server.players.values(): 555 if player.pid != self.pid: 556 listOfUnits = player.units.values() 557 for unit in self.units.values(): 558 for enemyUnit in listOfUnits: 559 if unit.curVertex == enemyUnit.curVertex: 560 # destination = self.server.map.vertices[str(self.enemyBuildingVertices[0])] 561 #destination = self.get_closest_safe_adjacent_vertex(unit.curVertex.vid, False) 562 destination = self.server.map.vertices[str(self.enemyBuildingVertices[0])] 563 # /print "[ai] The troop we're marshalling: ", self.aiTroopVertices[0][0], " and location ", destination.vid 564 for defender in self.units.values(): 565 if issubclass(type(defender), DOS) == True and defender.pid == self.pid and not defender.are_actions_running(): 566 if defender.power > 0: 567 returnVal = self.server.move_unit(destination, self.aiTroopVertices[0][0], self.pid) 568 ## /print "Are actions running: ", self.aiTroopVertices[0][0].are_actions_running() 569 if not self.aiTroopVertices[0][0].are_actions_running(): 570 self.perform_single_target_attack(enemyUnit, self.aiTroopVertices[0][0]) 571 return
572 573 574 # FSM [Finite State Machine]
575 - def ai_loop(self, dt):
576 self.totalTime += dt 577 578 if self.ai_cur_state == "": 579 #print "[AI] cur_state is blank, setting to initial." 580 print "[AI] is ON." 581 self.ai_cur_state = "Initial" 582 583 if self.ai_cur_state == "Initial": 584 #print "[AI] state: Initial" 585 self.scan_all_vertices() 586 self.ai_cur_state = "Waiting" 587 588 if self.ai_cur_state == "Waiting": 589 #print "[AI] state: Waiting" 590 self.timeWaited += dt 591 if self.timeWaited < self.triggerTime and not self.trigger(): 592 #print "[AI] The AI is being patient: Elapsed Time: ", self.timeWaited, "Time Limit: ", self.triggerTime 593 return 594 self.timeWaited = 0 595 self.genericCounter += 1 596 self.ai_cur_state = "Determining" # Change state to scannning always at the end of waiting. 597 598 if self.ai_cur_state == "Researching": 599 # # /print "[ai] state: Researching" 600 pass 601 602 if self.ai_cur_state == "Scanning": 603 # /print "[ai] state: Scanning" 604 self.scan_all_vertices() 605 self.ai_prev_state = "Scanning" 606 607 if self.ai_cur_state == "Determining": 608 #print "[AI] state: Determining (an action to take)" 609 if self.ai_prev_state == "Initial": 610 self.ai_prev_state = "Building" 611 #print "[AI] Set To Build..." 612 return 613 614 #print "[AI] NOW ai_cur_state", self.ai_cur_state 615 #print "[AI] NOW ai_prev_state", self.ai_prev_state 616 617 if self.ai_prev_state == "Building": 618 self.ai_cur_state = "Attacking" 619 if self.ai_prev_state == "Attacking": 620 self.ai_cur_state = "Defending" 621 if self.ai_prev_state == "Defending": 622 self.ai_cur_state = "Building" 623 624 if self.troopvertex != None: 625 #print "PROBLEM VERTEX: ", self.troopvertex, self.troopvertex.vid 626 self.swarm_attack(self.troopvertex) 627 628 if self.check_defenses() == True: 629 #print "[AI] <<< Detected Enemy Attack >>> " 630 pass 631 632 if self.ai_cur_state == "Building": 633 #print "[AI] state: Building" 634 if len(self.swarmVertices) > 2: 635 self.swarm_vertex(self.swarmCompletion, self.swarmVertex) 636 self.ai_prev_state = "Building" 637 self.ai_cur_state = "Determining" 638 return 639 else: 640 self.ai_prev_state = "Building" 641 self.ai_cur_state = "Determining" 642 return 643 644 if self.ai_cur_state == "Attacking": 645 #print "[AI] state: Attacking" 646 self.random_search_and_destroy() 647 self.marshall_troops(self.swarmNumber) 648 self.reassign_troops() 649 self.ai_prev_state = "Attacking" 650 self.ai_cur_state = "Determining" 651 return 652 653 if self.ai_cur_state == "Defending": 654 #print "[AI] state: Defending" 655 self.defend_our_server() 656 self.ai_prev_state = "Defending" 657 self.ai_cur_state = "Determining" 658 return
659 660 # Deprecated. Keep for reference.
661 - def select_target(self, unit):
662 for vertex in unit.curVertex.adjacentVertices + [unit.curVertex]: 663 for troop in vertex.troops: 664 # have to incorporate range somehow, can't just look at adjacent vertices 665 # implement something like objs_within_range 666 if troop is not None and troop.pid != self.ID: 667 return troop 668 if vertex.building != None: 669 return vertex.building 670 return None
671