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

Source Code for Module maps

   1  import heapq 
   2  import math 
   3  import os 
   4  import re 
   5  import sys 
   6  import random 
   7   
   8  from collections import defaultdict 
   9   
  10  from cocos.batch import BatchableNode 
  11  from cocos import euclid, collision_model 
  12  from cocos.draw import Line 
  13  from cocos.layer import scrolling 
  14  from cocos.actions.interval_actions import Delay 
  15  from cocos.actions.instant_actions import CallFunc 
  16  from cocos.sprite import Sprite 
  17  import constants 
  18  from constants import TROOP_SLOT_SCALE, CELL_SIZE, NUM_OF_SLOTS, AS_OPACITY, \ 
  19      SLOT_Z, BUILD_OFFSET_X, BUILD_OFFSET_Y, AS_EDGE_WIDTH, AS_COLORS, AS_SCALE, \ 
  20      VERTEX_Z, AS_CIRCLE_Z, EDGE_COLOR, AS_EDGE_COLOR, EDGE_Z, MINIMAPCIRCLE_OPACITY, \ 
  21      HALF_VISIBLE, PLAYER_COLORS 
  22  from heapItem import HeapItem 
  23  import objects 
  24  from utils import aabb_to_aa_rect, set_slots, get_action_menu_slots 
  25  from models import * 
  26   
27 -class EmptyTroopSlot(Sprite):
28 - def __init__(self, x, y, opacity=0):
29 super(EmptyTroopSlot, self).__init__(os.path.join("images", 30 "maps", "empty_slot.png")) 31 self.position = euclid.Vector2(x, y) 32 self.scale = TROOP_SLOT_SCALE 33 self.opacity = opacity 34 self.troop = None
35 36
37 -class EmptyBuildingSlot(Sprite):
38 - def __init__(self, x, y, opacity=0):
39 super(EmptyBuildingSlot, self).__init__( 40 os.path.join("images", "maps", "empty_building_slot.png")) 41 self.position = euclid.Vector2(x, y) 42 self.opacity = opacity
43 44
45 -class Vertex(Sprite):
46
47 - def __init__(self, col, row, vid, asID, visibilityState=0, pid=0,image=None):
48 if not image: 49 super(Vertex, self).__init__(os.path.join("images", "maps", 50 "vertex.png")) 51 else: 52 super(Vertex, self).__init__(image) #init core 53 54 self.position = euclid.Vector2(col * CELL_SIZE, row * CELL_SIZE) 55 self.cshape = aabb_to_aa_rect(self.get_AABB()) 56 self.cshape.center = self.position 57 self.visibilityState = visibilityState 58 59 self.pid = pid # pid of the human player 60 61 self.numOfSlots = NUM_OF_SLOTS 62 63 self.slotPositions = [] 64 65 self.edges = [] 66 # array of edge objects attached to this vertex. key is edgeNum, 67 # val is Edge object. 68 69 self.building = None 70 71 self.vid = vid 72 73 self.asID = asID 74 75 self.asCircle = None 76 77 self.adjacentVertices = [] 78 79 self.actionMenuSlots = get_action_menu_slots(4, self.position[0], self.position[1]) 80 self.borderVertices = defaultdict(list) 81 # This will contain all of the Vertices that this vertex is connected to 82 # that are in a different AS. k: asID v: list of vertices in that AS 83 self.emptySlots = [] 84 self.emptyTroopSlots = {} 85 86 self.transTroopSlots = {} # for booking slots while unit is moving 87 #value is a tuple of index,slot 88 89 self.troopSlots = {} 90 91 self.buildingSlot = None 92 93 self.opacity = visibilityState * 255 94 95 # for Djiskstra 96 self.heapItem = None
97
98 - def is_blocking_troop(self, troop, action=None):
99 # TODO: flush this out to include encrypted unit, ping, db, firewall etc... 100 if action == "attack": #an attack 101 # bool1 = (type(troop) == SQLInjection and self.building and (type(self.building) != Database or type(self.building) != Server) and troop.pid != self.building.pid) 102 # print "RETURNING in is_blocking_troop", bool1 103 return False,False 104 bool2 = (self.building and type(self.building) == Firewall and type(troop) != EncryptedTroop and self.building.pid != troop.pid) 105 is_firewall = (type(self.building) == Firewall and self.building.pid != troop.pid) 106 return bool2, is_firewall
107
108 - def add_troop(self, troop):
109 if not self.emptyTroopSlots: 110 return False 111 i, slot = self.emptyTroopSlots.popitem() 112 slot.troop = troop 113 troop.slotIndex = i 114 self.troopSlots[i] = slot 115 troop.update_opacity(255 * math.floor(troop.curVertex.visibilityState)) 116 if troop.pid == self.pid: 117 self._update_visibility() 118 self.set_neighbors_visibility() 119 return slot
120
121 - def add_building(self, building):
122 if self.building: 123 return False 124 building.slotIndex = 0 125 self.building = building 126 building.opacity = 255 * math.floor(building.curVertex.visibilityState) 127 self.buildingSlot.color = PLAYER_COLORS[self.building.pid] 128 if self.building.pid == self.pid: 129 self._update_visibility() 130 self.set_neighbors_visibility() 131 return True
132
133 - def remove_building(self):
134 if not self.building: 135 return False 136 self.buildingSlot.color = (255,255,255) 137 if self.building.pid == self.pid: 138 self._update_visibility() 139 self.set_neighbors_visibility() 140 141 142 if type(self.building) == Database: 143 self.numOfSlots = 4 144 self.actionMenuSlots = get_action_menu_slots(self.numOfSlots, self.position[0], self.position[1]) 145 for troopSlot in self.troopSlots.keys()[4:]: 146 troop = self.troopSlots[troopSlot].troop 147 self.remove_troop(troop) # Why isn't this working? 148 self.building = None 149 return True
150 151 152 # books a slot at the dest index when troop starts moving 153 # returns index and slot
154 - def add_trans_troop(self, troop):
155 if self.emptyTroopSlots: 156 i, slot = self.emptyTroopSlots.popitem() 157 self.transTroopSlots[troop] = (i, slot) 158 return slot
159 160 # move troop from trans to troop once done moving
161 - def set_trans_troop(self, troop):
162 index, slot = self.transTroopSlots.pop(troop) 163 slot.troop = troop 164 troop.slotIndex = index 165 self.troopSlots[index] = slot 166 troop.update_opacity(255 * math.floor(troop.curVertex.visibilityState)) 167 if troop.pid == self.pid: 168 self._update_visibility() 169 self.set_neighbors_visibility()
170 171
172 - def remove_troop(self, troop):
173 if troop.slotIndex in self.troopSlots.keys(): 174 slot = self.troopSlots.pop(troop.slotIndex) 175 elif troop not in self.transTroopSlots.keys(): 176 # Silly bug fix 177 return 178 else: 179 slot = self.transTroopSlots.pop(troop.slotIndex) 180 slot.troop = None 181 self.emptyTroopSlots[troop.slotIndex] = slot 182 if troop.pid == self.pid: 183 self._update_visibility() 184 self.set_neighbors_visibility()
185 186
187 - def _update_visibility(self):
188 visible=0 189 c = objects.Objects.get_controller() 190 if self.asID in c.visibleASes: 191 visible = HALF_VISIBLE 192 for v in self.adjacentVertices + [self]: 193 if v.asID == self.asID: 194 for slot in v.troopSlots.values(): 195 if slot.troop.pid == self.pid: 196 visible = 1 197 break 198 if v.building and v.building.pid == self.pid: 199 visible = 1 200 self.set_visibility(visible)
201
202 - def set_neighbors_visibility(self):
203 for neighbor in self.adjacentVertices: 204 if neighbor.asID == self.asID: 205 neighbor._update_visibility()
206
207 - def set_visibility(self, visibilityState, minimap=None):
208 if self.visibilityState == visibilityState: 209 return 210 self.visibilityState = visibilityState 211 212 for slot in self.troopSlots.values(): 213 slot.opacity = math.floor(visibilityState) * 255 214 slot.troop.update_opacity(math.floor(visibilityState) * 255) 215 216 for slot in self.transTroopSlots.values(): 217 slot[1].opacity = math.floor(visibilityState) * 255 218 219 for slot in self.emptyTroopSlots.values(): 220 slot.opacity = math.floor(visibilityState) * 255 221 222 223 self.opacity = math.ceil(visibilityState) * 255 224 self.buildingSlot.opacity = math.floor(visibilityState) * 255 225 if self.building: 226 self.building.opacity = math.floor(visibilityState) * 255 227 for edge in self.edges: 228 edge.visible = bool(math.ceil(visibilityState)) 229 self.asCircle.opacity = math.ceil(visibilityState) * 255 * AS_OPACITY
230
231 - def highlight_adjacents(self, isHighlighted):
232 if isHighlighted: 233 for edge in self.edges: 234 edge.old_stroke_width = edge.stroke_width 235 edge.stroke_width = edge.stroke_width + 3 236 # edge.color = ADJ_EDGE_COLOR 237 # for vertex in self.adjacentVertices: 238 # if vertex.opacity == 255: 239 # vertex.color = HIGHLIGHTED_VERTEX_COLOR 240 else: 241 for edge in self.edges: 242 edge.stroke_width = edge.old_stroke_width
243 # edge.color = EDGE_COLOR 244 # for vertex in self.adjacentVertices: 245 # vertex.color = VERTEX_COLOR 246 247 248
249 - def draw_empty_slot_sprites(self, gameMap):
250 # This gets called on init and when a Database is added/removed. This 251 # if accounts for both cases. 252 253 curTroops = [] 254 curBuilding = None 255 # remove current slots 256 257 for slot in self.emptyTroopSlots.values(): 258 gameMap.batch_remove(slot) 259 260 for slot in self.troopSlots.values(): 261 curTroops.append(slot.troop) 262 gameMap.batch_remove(slot) 263 self.remove_troop(slot.troop) 264 265 for slot in self.transTroopSlots.values(): 266 curTroops.append(slot.troop) 267 gameMap.batch_remove(slot) 268 self.remove_troop(slot.troop) 269 270 if self.buildingSlot: 271 gameMap.batch_remove(self.buildingSlot) 272 273 self.slotPositions = set_slots(self.numOfSlots, self.position[0], self.position[1]) 274 275 # add troop slots 276 for i in range(self.numOfSlots): 277 emptySlot = EmptyTroopSlot( 278 self.slotPositions[i][0], self.slotPositions[i][1], self.opacity) 279 gameMap.batch_add(emptySlot, z=SLOT_Z) 280 self.emptyTroopSlots[i] = emptySlot 281 282 self.actionMenuSlots = get_action_menu_slots(self.numOfSlots, self.position[0], self.position[1]) 283 284 # add building slot 285 self.buildingSlot = EmptyBuildingSlot( 286 self.position[0] + BUILD_OFFSET_X, self.position[1] + BUILD_OFFSET_Y, self.opacity) 287 gameMap.batch_add(self.buildingSlot, z=SLOT_Z) 288 self._update_visibility() 289 290 for t in curTroops: 291 self.add_troop(t)
292 293
294 -class Core(Vertex):
295 - def __init__(self, col, row, vid, adjacentVertices, visibilityState=0, pid=0):
296 super(Core, self).__init__(col, row, vid, adjacentVertices, visibilityState=0, pid=0,image=os.path.join("images", "maps", "core.png")) 297 self.color = (255,255,255)
298
299 - def add_building(self, building):
300 if self.building: 301 return False 302 building.slotIndex = 0 303 self.building = building 304 if building.pid == self.pid: 305 self._update_visibility() 306 return True
307
308 - def remove_building(self):
309 if not self.building: 310 return False 311 self.building = None 312 if building.pid == self.pid: 313 self._update_visibility() 314 return True
315
316 - def set_visibility(self, visibilityState, minimap=None):
317 if visibilityState == self.visibilityState: 318 return 319 self.visibilityState = visibilityState 320 self.opacity = 255 * math.ceil(visibilityState) 321 if self.building: 322 self.building.opacity = 255 * math.floor(visibilityState)
323
324 - def is_blocking_troop(self, troop, action):
325 return False, False # this second return value is to deal with the firewall sound
326
327 - def _update_visibility(self):
328 if self.building and self.building.pid == self.pid: 329 self.set_visibility(1) 330 else: 331 self.set_visibility(HALF_VISIBLE)
332
333 - def set_neighbors_visibility(self):
334 pass
335 336
337 -class Edge(Line):
338 - def __init__(self, sourcePos, destPos, sourceV, destV, color, strokeWidth=3, visible=False):
339 # Note: sourcePos and destPos are coordinates from the map file, NOT 340 # points. 341 start = (sourcePos[0] * CELL_SIZE, sourcePos[1] * CELL_SIZE) 342 end = (destPos[0] * CELL_SIZE, destPos[1] * CELL_SIZE) 343 super(Edge, self).__init__(start, end, color, strokeWidth) 344 self.visible = visible 345 self.color = color 346 self.stroke_width = strokeWidth 347 self.v1 = sourceV 348 self.v2 = destV
349 350
351 -class ASEdge(Edge):
352 - def __init__(self, sourcePoint, destPoint, sourceV, destV, color, strokeWidth=AS_EDGE_WIDTH, visible=False, speed=0.1):
353 # sourcePoint and destPoints are Points (x,y) 354 super(ASEdge, self).__init__( 355 sourcePoint, destPoint, sourceV, destV, color, strokeWidth) 356 self.visible = visible 357 self.color = color 358 self.speed = speed # Used for AS edges. 359 self.stroke_width = strokeWidth 360 self.v1 = sourceV 361 self.v2 = destV
362 363
364 -class AS(object):
365 - def __init__(self, asID, vids, position=None, opacity=0):
366 self.circles = {} # vid is the key, asCircle object is the value. 367 self.asID = int(asID) 368 self.vids = vids 369 self.vertices = {} 370 self.cores = {} 371 self.usedCores = {} 372 self.position = position 373 self.opacity = opacity * 255 374 self.color = AS_COLORS[self.asID]
375 376
377 -class ASCircle(Sprite):
378 - def __init__(self, position, color, scale=AS_SCALE, opacity=0):
379 super(ASCircle, self).__init__(os.path.join("images", 380 "maps", "as_circle.png")) 381 self.color = color 382 self.scale = scale 383 self.opacity = opacity 384 self.position = euclid.Vector2(position[0], position[1])
385 386
387 -class Map(scrolling.ScrollableLayer):
388
389 - def __init__(self, mapFileName, cm=None, numPlayers = None, AIPlayers = None, seed = None):
390 super(Map, self).__init__() 391 self.cm = cm 392 393 self.batchableNode = BatchableNode() 394 self.batchableNode.position = 0,0 395 self.add(self.batchableNode,z=VERTEX_Z) 396 397 # Initiliaze variables - these will be set in parse_map_file. 398 self.vertexPositions = {} 399 self.edgePositions = defaultdict( 400 list) # key: vid of one end, v: vid of other end 401 402 self.AS = {} # we can get list of vertices from here 403 self.cores = {} # k: just the cores 404 self.vertices = {} # k: all vertices and cores 405 self.edges = [] # list of all edges 406 407 self.startingUnits = defaultdict(lambda: defaultdict(list)) 408 self.startingResearch = defaultdict(list) 409 self.availResearch = [] 410 411 self.w = - 1 412 # Width of map in terms of num of cells. set in parse_map_file 413 self.h = - 1 414 # Height of map in terms of num of cells. set in parse_map_file 415 416 self.players = [] # list of pids 417 self.AIPlayers = [] 418 self.pid = 0 419 if mapFileName == "random": 420 # Generate a random map. Creates a map and writes it to the /maps/ directory. Returns the file name. 421 # Set the seed. 422 random.seed(seed) 423 # Map Dimensions 424 numRows = int(random.randint(6 * numPlayers, 14 * numPlayers)) # if 2 players, min of 12x12, max of 28x28 425 numCols = int(random.randint(6 * numPlayers, 14 * numPlayers)) 426 427 # Number of ASes 428 numASes = int(math.ceil((numCols * numRows) / 140.0)) # If 2 players, min of 2 ASes, max of 6. 429 430 # Verts per AS 431 minVertsPerAS = 2 432 maxVertsPerAS = 4 433 434 playerStartUnits = {0:[],1:[]} 435 playerResearch = {0:[],1:[]} 436 437 maxCoresPerAS = 2 438 439 mapFileName = self.generate_random_map(numPlayers, numCols, numRows, playerStartUnits, playerResearch, minVertsPerAS, maxVertsPerAS, numASes, maxCoresPerAS, AIPlayers, seed) 440 441 # Parses the map file and sets all relevant information. 442 self.parse_map_file(mapFileName) 443 444 self.minimap = None # Starts as None. Eventually stores the instance of MiniMap built off this Map (happens in controller)
445
446 - def batch_add(self,cocosNode,z=1):
447 self.batchableNode.add(cocosNode,z=z)
448
449 - def batch_remove(self,cocosNode):
450 self.batchableNode.remove(cocosNode)
451
452 - def parse_map_file(self, mapFileName):
453 454 with open(mapFileName, 'r') as f: 455 row = 0 # updated when size found in map file 456 isCellInfo = False 457 for line in f: 458 line = line.split() 459 if len(line) == 0 or line[0] == "#": 460 continue 461 elif line[0] == "MAP": 462 isCellInfo = True 463 self.w = int(line[1]) - 1 464 self.h = int(line[2]) - 1 465 elif line[0] == "ENDMAP": 466 isCellInfo = False # Rot to the end of cell info 467 elif line[0] == "EDGES:" or line[0] == "CORE_EDGES:": 468 edgeList = [] 469 for edge in line[1:]: 470 formattedEdge = re.findall("[0-9]+|[a-z]+", edge) 471 edgeList.append(formattedEdge) 472 for edge in edgeList: 473 self.edgePositions[edge[0] 474 ].append(edge[1]) # edge[0] is key 475 self.edgePositions[edge[1] 476 ].append(edge[0]) # edge[1] is key 477 elif line[0] == "AS": 478 vertsInAS = [] 479 for i in range(2, len(line)): # Loop through info in AS line to grab relevant vertices, if only one, do a "n-n" 480 r = re.split('\-', line[i]) 481 if r[0].isdigit(): 482 vertsInAS += [str(v) for v in range(int(r[0]), int(r[1]) + 1)] 483 else: #add core 484 vertsInAS += [r[0]] 485 self.AS[int(line[1])] = AS(int(line[1]), vertsInAS) # key is AS's ID 486 elif "PLAYER" == line[0]: 487 # Loop through starting units and add them to the 488 # self.playerStartingUnits 489 pid = int(line[1]) 490 self.players.append(pid) 491 for unit in line[2:]: 492 unit = unit.split(",") 493 unitType = unit[0] # For clarity. 494 unitVert = unit[1] 495 self.startingUnits[pid][unitVert].append(unitType) 496 elif line[0] == "AI": 497 self.AIPlayers = line[1:] 498 self.AIPlayers = [int(i) for i in self.AIPlayers] 499 for p in self.AIPlayers: 500 self.players.remove(p) 501 elif line[0] == "STARTRESEARCH": 502 pid = int(line[1]) 503 for research in line[2:]: 504 self.startingResearch[pid].append(research) 505 elif line[0] == "AVAILRESEARCH": 506 for research in line[1:]: 507 self.availResearch.append(research) 508 elif isCellInfo == True: 509 col = 0 510 for cell in line: 511 if cell.isalpha() or cell.isdigit(): 512 r = self.h - row 513 self.vertexPositions[cell] = (col, r) 514 col += 1 515 row += 1 516 f.close()
517
518 - def draw_map(self):
519 for asID in self.AS.keys(): 520 curAS = self.AS[asID] 521 522 # create vertices 523 for vid in curAS.vids: 524 position = self.vertexPositions[str(vid)] 525 if vid.isalpha(): 526 v = Core(position[0], position[1], vid, asID, pid=self.pid) 527 self.cores[vid] = v 528 self.vertices[vid] = v 529 curAS.cores[vid] = v 530 else: 531 v = Vertex(position[0], position[1], vid, asID, pid=self.pid) 532 v.asCircle = ASCircle(v.position, curAS.color) 533 v.color = curAS.color 534 curAS.circles[vid] = v.asCircle 535 v.draw_empty_slot_sprites(self) 536 curAS.vertices[vid] = v 537 self.vertices[vid] = v 538 #selfbatch_add(v.asCircle, z=AS_CIRCLE_Z) 539 self.batch_add(v, z=VERTEX_Z) 540 for v1, v2s in self.edgePositions.items(): 541 for v2 in v2s: 542 position1 = self.vertexPositions[v1] 543 position2 = self.vertexPositions[v2] 544 vert1 = self.vertices[v1] 545 vert2 = self.vertices[v2] 546 if v1.isalpha() or (v2.isdigit() and (int(v1) > int(v2))): 547 # create edges 548 if vert1.asID != vert2.asID: # create asEdge 549 vert1.borderVertices[vert2.asID].append(vert2) 550 vert2.borderVertices[vert1.asID].append(vert1) 551 edge = ASEdge( 552 position1, position2, vert1, vert2, AS_EDGE_COLOR) 553 else: # create normal edge 554 edge = Edge( 555 position1, position2, vert1, vert2, EDGE_COLOR) 556 vert1.edges.append(edge) 557 vert2.edges.append(edge) 558 if not v1.isalpha(): 559 vert1.adjacentVertices.append(vert2) 560 vert2.adjacentVertices.append(vert1) 561 self.edges.append(edge) 562 563 # draw edges 564 for v in self.edges: 565 self.add(v, z=EDGE_Z) 566 pass
567
568 - def generate_random_map(self, numPlayers, numCols, numRows, playerStartUnits, playerResearch, minVertsPerAS, maxVertsPerAS, numASes, maxCoresPerAS, AIPlayers, seed):
569 # ---Add vertices and cores to each AS--- 570 ASvertices, AScores = self.add_verts_and_cores(numRows, numCols, minVertsPerAS, maxVertsPerAS, numASes, maxCoresPerAS) 571 572 573 # ---Generate the map lines (cells)--- 574 mapLines, numCols, numRows, ASvertices, AScores = self.create_map_lines(numCols, numRows, ASvertices, AScores, maxVertsPerAS) 575 576 # ---Generate edges in ASes and between ASes--- 577 edges, coreEdges = self.make_edges(numCols, numRows, ASvertices, AScores) 578 579 # ---Write the map info to a file--- 580 playerStartUnits = [["Server","CPU"],["Server","CPU"]] 581 fullMapName = self.write_map_info_to_file(mapLines, ASvertices, AScores, edges, coreEdges, numPlayers, playerStartUnits, playerResearch, AIPlayers) 582 583 return fullMapName
584
585 - def add_verts_and_cores(self, numRows, numCols, minVertsPerAS, maxVertsPerAS, numASes, maxCoresPerAS):
586 # Adds vertices and cores to each AS based on random info above. 587 # RETURNS: tuple: (ASvertices (list), AScores (list)) 588 endVert = random.randint(minVertsPerAS, maxVertsPerAS) 589 ASvertices = [range(0,endVert + 1)] # Fill first AS with vertices so we start at 0. This is used for attaching vertex ID to each AS. 590 591 for i in range(1, numASes): 592 # Add one here since we are using range to generate the list. 593 ASvertices.append(range(endVert + 1, 1 + random.randint(endVert + 2, endVert + 2 + random.randint(minVertsPerAS, maxVertsPerAS)))) 594 endVert = ASvertices[i][-1] 595 596 endCore = 'b' # First AS will have 'a' and 'b' 597 598 valList = range(ord('a'), ord(endCore) + 1) 599 for j in range(len(valList)): 600 valList[j] = chr(valList[j]) 601 602 # Generate CORES 603 AScores = [valList] 604 nextDigit = '' 605 for i in range(1, numASes): 606 # Fill AScores with cores. 607 608 # Generate int list that corresponds to ASCII vals of chars 609 lowerVal = ord(endCore[-1]) + 1 610 upperVal = ord(endCore[-1]) + 1 + random.randint(1,maxCoresPerAS) 611 if upperVal > 122 and nextDigit == '': 612 # TODO: fix this! We have too many cores. Add an a to the '26' digit slot to start going into two digit core count. 613 nextDigit = 'a' 614 upperVal = (upperVal % 122) + 97 615 elif upperVal > 122 and nextDigit != '': 616 # We have too many cores. Increment our '26' digit slot. 617 nextDigit = chr(ord(nextDigit) + 1) 618 upperVal = (upperVal % 122) + 97 619 valList = range(lowerVal, upperVal) 620 621 # Convert int list to char list 622 for j in range(len(valList)): 623 valList[j] = nextDigit + chr(valList[j]) 624 AScores.append(valList) 625 endCore = AScores[i][-1] 626 627 return ASvertices, AScores
628
629 - def create_map_lines(self, numCols, numRows, ASvertices, AScores, maxVertices):
630 # Generate map grid of proper dimensions 631 # RETURNS: mapLines (list of lists of chars) 632 mapLines = [["-" for i in range(int(numCols))] for j in range(int(numRows))] 633 634 # Figure out where to place the ASes -> Use a bounding box to isolate AS locations 635 numASes = len(ASvertices) 636 # SET BOUNDING BOX SIZE -> If we want to change how 'spread out' ASes are, this is where to do it!!! 637 dimensionFacilitator = maxVertices + 2 638 boundingBoxWidth = random.randint(3,dimensionFacilitator) 639 dimensionFacilitator -= boundingBoxWidth 640 boundingBoxHeight = random.randint(3,min(max(3,dimensionFacilitator),maxVertices)) 641 642 # Define initial bounding box 643 topLeft = (0,0) 644 botRight = (topLeft[0] + boundingBoxWidth, topLeft[1] + boundingBoxHeight) 645 646 # Define boundingBoxes 647 boundingBoxes = [(topLeft,botRight)] # stores a tuple of topLeft, bottomRight points that define a boundingBox. 648 649 # Populate the list <boundingBoxes> with the coordinates of all possible boundingBoxes for ASes 650 boxesPlaced = 0 651 while boxesPlaced < (numASes + numASes): 652 # While guarantees we have placed sufficient boundingBoxes. 653 if botRight[0] + boundingBoxWidth > numCols: 654 # We're at the right edge of mapLines and can't fit another box, so wrap around. 655 topLeft = (1,botRight[1] + 3) # +3 so we don't overlap. and to spread the ASes out a bit 656 else: 657 # Set topLeft to be the next boundingBox over 658 topLeft = (botRight[0] + 3, topLeft[1]) # +3 so we don't overlap. and to spread the ASes out a bit 659 if (botRight[1] + boundingBoxHeight) > numRows: 660 # We're at the bottom and haven't added enough bounding boxes yet. Expand the map a bit. 661 numCols += 1 662 numRows += 1 663 for i in range(len(mapLines)): 664 row = mapLines[i] 665 row.append("-") 666 mapLines[i] = row 667 mapLines.append(["-" for row in range(int(numCols))]) 668 else: 669 # Set bottomRight point to be the topLeft point, + boundBoxWidth and + boundBoxHeight 670 botRight = (topLeft[0] + boundingBoxWidth, topLeft[1] + boundingBoxHeight) 671 boundingBoxes.append((topLeft,botRight)) 672 boxesPlaced += 1 673 674 # Loop through each AS and add its vertices and cores to a bounding box. 675 for i in range(numASes): 676 # For clarity 677 nextASverts = ASvertices[i] 678 nextAScores = AScores[i] 679 680 # Pick a bounding box. Remove the boundingBox from <boundingBoxes>. 681 try: 682 topLeft, botRight = random.choice(boundingBoxes) 683 boundingBoxes.remove((topLeft, botRight)) 684 except: 685 # We've used every bounding box! Can't place any more ASes 686 break 687 688 # Add vertices to the AS bounding box. 689 vertIndex = 0 690 while vertIndex < len(nextASverts): 691 vert = nextASverts[vertIndex] 692 vertRow = random.randint(topLeft[0], max(min(len(mapLines) - 1, botRight[0]),topLeft[0] + 1)) # Pick a ROW 693 vertCol = random.randint(topLeft[1], max(min(len(mapLines[-1]) - 1, botRight[1]),topLeft[1] + 1)) # Pick a COLUMN 694 try: 695 while mapLines[vertRow][vertCol] != "-": 696 # Slot we picked was already taken. Look for an empty slot. 697 vertRow = random.randint(topLeft[0], min(len(mapLines) - 1, botRight[0])) # Pick a ROW 698 vertCol = random.randint(topLeft[1], min(len(mapLines[-1]) - 1, botRight[1])) # Pick a COLUMN 699 # Found an empty slot in the bounding box. Place the vertex. 700 mapLines[vertRow][vertCol] = vert 701 vertIndex += 1 702 except: 703 ASvertices[i].remove(vert) 704 705 # Decrement vertices we haven't added yet. 706 for j in range(len(ASvertices[i])): 707 if ASvertices[i][j] > vert: 708 ASvertices[i][j] -= 1 709 # Don't increment our index, we just want to redo the next vert since we removed.. 710 711 try: 712 # Add cores to the AS bounding box. 713 for core in nextAScores: 714 coreRow = random.randint(topLeft[0], max(min(len(mapLines) - 1, botRight[0]),topLeft[0] + 1)) # Pick a ROW 715 coreCol = random.randint(topLeft[1], max(min(len(mapLines[-1]) - 1, botRight[1]),topLeft[1] + 1)) # Pick a COLUMN 716 while mapLines[coreRow][coreCol] != "-": 717 # Slot we picked was already taken. Look for an empty slot. 718 coreRow = random.randint(topLeft[0], min(len(mapLines) - 1, botRight[0])) # Pick a ROW 719 coreCol = random.randint(topLeft[1], min(len(mapLines[-1]) - 1, botRight[1])) # Pick a COLUMN 720 # Found an empty slot in the bounding box. Place the vertex. 721 mapLines[coreRow][coreCol] = core 722 except: 723 AScores[i].remove(core) 724 725 return mapLines, numCols, numRows, ASvertices, AScores
726
727 - def make_edges(self, numCols, numRows, ASvertices, AScores):
728 # Randomizes edges between the vertices. Ensures they are connected. 729 # RETURNS: <tuple> (edgeList, coreEdgeList) 730 731 edgeList = [] 732 coreEdgeList = [] 733 734 # Generate edge for each individual AS 735 for AS in ASvertices: 736 # Loop through each AS to get the list of vertices in that AS. 737 vertList = sorted(list(AS)) # Copies the vertices in AS to vertList (need to do this since we will end up destroying vertList) 738 739 # Generate a random prufer seq based on the vertices. 740 pruferSeq = self.generate_prufer_seq(sorted(list(AS))) 741 742 edgeList += self.generate_mst(pruferSeq, vertList) 743 744 # Connect the ASes 745 asBorderRouters = [] 746 for AS in ASvertices: 747 if len(AS) > 0: 748 asBorderRouters.append(random.choice(AS)) 749 750 ASpruferSeq = self.generate_prufer_seq(list(asBorderRouters)) # Once again, have to list() asBorderRouters so it doesn't destroy it. 751 752 edgeList += self.generate_mst(ASpruferSeq, asBorderRouters) 753 754 # Generate core edges. # TODO: IMPROVE 755 for i in range(len(AScores)): 756 for j in range(len(AScores[i])): 757 if len(ASvertices[i]) > 0: 758 core = AScores[i][j] 759 randomVertInSameAS = random.choice(ASvertices[i]) 760 coreEdgeList.append([randomVertInSameAS, core]) 761 762 return edgeList, coreEdgeList
763
764 - def generate_prufer_seq(self, vertList):
765 # Given a list of vertices, generates a random prufer code. 766 767 # Take lowest and put it in to exhausted. 768 if len(vertList) == 0: 769 return [] 770 exhausted = [vertList.pop(0)] 771 pruferSeq = [] 772 773 while len(vertList) > 0: 774 try: 775 randVert = random.choice(exhausted) 776 pruferSeq.append(randVert) 777 new = vertList.pop(0) # remove from vertList and add it to exhausted 778 exhausted.append(new) 779 except: 780 # Handles corner cases. 781 break 782 783 return pruferSeq
784
785 - def generate_mst(self, pruferSeq, vertList):
786 # Build an MST from prufer code. 787 if len(pruferSeq) == 0: 788 return [] 789 vertList.remove(pruferSeq[0]) 790 edgeList = [] 791 while True: 792 if len(pruferSeq) == 1: 793 edgeList.append([vertList[0], pruferSeq[0]]) 794 break 795 elif len(vertList) == 2: 796 # Done. 797 edgeList.append([vertList[0], pruferSeq[0]]) 798 edgeList.append([vertList[1], pruferSeq[1]]) 799 break 800 else: 801 nextVert = None 802 for vert in vertList: 803 if vert != pruferSeq[0]: 804 nextVert = vert 805 vertList.remove(vert) 806 break 807 otherVert = pruferSeq.pop(0) 808 edgeList.append([nextVert, otherVert]) 809 return edgeList
810
811 - def write_map_info_to_file(self, mapLines, ASvertices, AScores, edges, coreEdges, numPlayers, playerStartUnits, playerResearch, AIPlayers):
812 # Writes a map file based on the randomized information. 813 mapName = "random" + str(random.randint(1, 1000)) + ".map" 814 numRows = len(mapLines) 815 numCols = len(mapLines[0]) 816 # DEBUG print "mapName: ", mapName 817 fullMapName = os.path.join("maps", "random", mapName) 818 newMapFile = file(fullMapName, "w") 819 820 # Add edges 821 edgeString = " " 822 for edge in edges: 823 temp = str(edge).replace(" ", "") # Remove the space to work with the parser. 824 edgeString += temp + " " 825 826 # Add coreEdges 827 coreEdgeString = " " 828 for coreEdge in coreEdges: 829 temp = str(coreEdge).replace(" ", "").replace("\'","") # Remove the space to work with the parser. 830 coreEdgeString += temp + " " 831 newMapFile.write("EDGES:" + edgeString + "\n") 832 newMapFile.write("CORE_EDGES:" + coreEdgeString + "\n") 833 834 for index in range(len(ASvertices)): 835 AS = ASvertices[index] 836 # Format cores to write 837 AScoreList = "" 838 for core in AScores[index]: 839 AScoreList += core + " " 840 # Write the cores and vertices to the AS line 841 if len(AS) > 0: 842 newMapFile.write("AS " + str(index) + " " + str(AS[0]) + "-" + str(AS[-1]) + " " + AScoreList + "\n") 843 844 newMapFile.write("\n") 845 # Write dimensions to the map 846 newMapFile.write("MAP " + str(numCols) + " " + str(numRows) + "\n") 847 848 # Write the mapLines 849 for line in mapLines: 850 newMapFile.write(self.list_to_string(line) + "\n") 851 newMapFile.write("ENDMAP\n") 852 newMapFile.write("\n") 853 854 # Add player units 855 for i in range(0,numPlayers): 856 nextUnitString = "" 857 for j in range(len(playerStartUnits[i])): 858 if len(ASvertices[i]) > 0: 859 # Assign a vertex and/or core to each unit/cpu. 860 if playerStartUnits[i][j] != "CPU": 861 nextUnitString += playerStartUnits[i][j] + "," + str(random.choice(ASvertices[i])) + " " 862 elif len(AScores) > 0: 863 nextUnitString += playerStartUnits[i][j] + "," + str(random.choice(AScores[i])) + " " 864 if len(ASvertices[i]) > 0: 865 newMapFile.write("PLAYER " + str(i) + " " + str(nextUnitString) + "\n") 866 if AIPlayers != None: 867 newMapFile.write("\n") 868 for AI in AIPlayers: 869 AIstring = str(AI) 870 newMapFile.write("AI " + AIstring + "\n") 871 newMapFile.write("\n") 872 newMapFile.close() 873 return fullMapName
874
875 - def list_to_string(self, l):
876 # Takes a list and converts its contents to a string. (basically a custom list-> string casting function) 877 s = "" 878 for item in l: 879 s += str(item) + " " 880 return s
881
882 - def get_path(self, source, dest, pid, troop, action=None):
883 ''' 884 Djikstra's Algorithm from Wikipedia: 885 http://en.wikipedia.org/wiki/Dijkstra's_algorithm 886 ''' 887 888 vertices = [] 889 890 for vertex in self.vertices.values(): 891 if not vertex.is_blocking_troop(troop, action)[0]: 892 h = HeapItem(vertex, sys.maxint) 893 vertex.heapItem = h 894 vertices.append(h) 895 if vertex == source: 896 h.depth = 0 897 898 heapq.heapify(vertices) 899 900 while vertices: 901 u = heapq.heappop(vertices) 902 if u.vertex == dest and u.depth != sys.maxint: 903 # Bingo 904 path = [] 905 while u: 906 path.append(u.vertex.vid) 907 u = u.parent 908 path.reverse() 909 return path 910 911 for vertex in u.vertex.adjacentVertices: 912 if not vertex.is_blocking_troop(troop, action)[0]: 913 v = vertex.heapItem 914 915 alt = u.depth + 1 916 if alt < v.depth: 917 v.depth = alt 918 v.parent = u 919 vertices.append(v) 920 # TODO: make this more efficient, can't use siftdown because of garbage collector 921 heapq.heapify(vertices) 922 923 return None
924 925 # DEPRECATED FOR MULTIPLAYER, use get_path instead
926 - def get_moveable_path(self, sourceVertex, destVertex, pid, troop=None):
927 # Returns the shortest path of vertices to get from sourceVertex to destVertex that the player can move to (cannot use vertices where opponent has a FireWall, or invisible vertices) 928 # NOTE: only works for PLAYER move. We need to write account for enemy moving into invisible vertices (changes movability logic) 929 # |-> should change this so it works for both. just don't allow exploration beyond an AS if the AS hasn't been discovered yet. 930 vertices = sourceVertex.adjacentVertices 931 932 if (type(troop) != EncryptedTroop) and (type(destVertex.building) == Firewall or type(destVertex.building) == Database) and destVertex.building.pid != pid: 933 return False 934 935 head = HeapItem(sourceVertex, 0) 936 937 heapVertices = [] 938 for vertex in vertices: 939 # CONSOLIDATE THIS INTO A vertex.isBlocked property 940 if vertex.visibilityState > 0 and (type(troop) == EncryptedTroop or type(vertex.building) != Firewall or vertex.building.pid == pid): 941 heapVertex = HeapItem(vertex, 1, head) 942 heapq.heappush(heapVertices, heapVertex) 943 else: 944 pass 945 numVerticesInMap = len(self.vertices) 946 # So we don't have to calculate this each time through the loop. 947 deadVerts = [] 948 while head.vertex != destVertex: 949 if len(deadVerts) > numVerticesInMap: 950 # NOTE: This condition is really bad... We should probably fix 951 # it. 952 return False 953 954 head = heapq.heappop(heapVertices) 955 newMoves = head.vertex.adjacentVertices 956 957 for vertex in newMoves: 958 if vertex.visibilityState > 0 and (type(troop) == EncryptedTroop or type(vertex.building) != Firewall or vertex.building.pid == pid): 959 # Only add a vertex to the path if it's visible, or if it 960 # does not have an Enemy Firewall in the way. 961 heapVertex = HeapItem(vertex, head.depth + 1, head) 962 # Don't add the vertex if we've already found it, or if it 963 # contains an Enemy firewall | BUT we might want to check 964 # to see if we've gotten to that vertex with less depth. 965 if heapVertex.vertex.vid not in [heapVert.vertex.vid for heapVert in heapVertices] and heapVertex.vertex not in deadVerts: 966 # or (heapVertex.vertex in [heapVert.vertex for 967 # heapVert in heapVertices] and heapVertex.head.depth < 968 # heapVertices[heapVertices.indexOf(heapVertex)].head.depth) 969 heapq.heappush(heapVertices, heapVertex) 970 else: 971 deadVerts.append(vertex) 972 973 path = [] 974 while head.getParent(): 975 path.insert(0, head.vertex) 976 head = head.getParent() 977 path.insert(0, head.vertex) 978 return path
979
980 - def get_vertex(self, vid):
981 if vid.isalpha(): 982 return self.cores[vid] 983 return self.vertices[vid]
984 985
986 -class MiniMap(Sprite):
987 - def __init__(self, ASes, numMapCols, numMapRows, edges, player):
988 super(MiniMap, self).__init__(os.path.join("images", 989 "maps", "minimap_bg.png")) 990 self.edges = edges 991 self.minimapBuildings = {} 992 self.minimapTroops = {} 993 self.opacity = 255 994 self.scale = 0.6 995 self.mapCellWidth = numMapCols * CELL_SIZE 996 self.mapCellHeight = numMapRows * CELL_SIZE 997 self.ASes = ASes 998 self.cshape = aabb_to_aa_rect(self.get_AABB()) 999 self.player = player 1000 self.setup()
1001
1002 - def setup(self):
1003 self.miniMapCircles = {} 1004 self.edgeList = [] 1005 for asID in self.ASes: 1006 for vid in self.ASes[asID].circles.keys(): 1007 # NOTE: these two lines will DEFINITELY need to change if we 1008 # change the size of the minimap, or its location!!!! 1009 # HARDCODED. 1010 circ = self.ASes[asID].circles[vid] 1011 newX = self.get_rect().width * circ.position[0] / self.mapCellWidth - 124 1012 newY = self.get_rect().height * circ.position[1] / self.mapCellHeight - 142 1013 1014 newPosition = (newX, newY) 1015 color = circ.color 1016 self.miniMapCircles[vid] = MiniMapCircle( 1017 newPosition, circ.position, color, 1)
1018 # for edge in self.edges: 1019 # edgeStart = None 1020 # edgeEnd = None 1021 # if type(edge.v1) == Vertex: 1022 # edgeStart = self.miniMapCircles[edge.v1.vid] 1023 # if type(edge.v2) == Vertex: 1024 # edgeEnd = self.miniMapCircles[edge.v2.vid] 1025 # if edgeStart and edgeEnd: 1026 # # coords: 615 138 1027 # # coords: 594 99 1028 # edge = MiniMapEdge((615, 138), (594, 99), 1029 # (615, 138), (594, 99), EDGE_COLOR) 1030 # self.edgeList.append(edge) 1031 1032 # Silly fix for now. 1033 1034
1035 -class MiniMapEdge(Line):
1036 - def __init__(self, miniCircleSourcePos, miniCircleDestPos, sourceV, destV, color, strokeWidth=1):
1037 # Note: start and end are endpoints 1038 start = miniCircleSourcePos 1039 end = miniCircleDestPos 1040 super(MiniMapEdge, self).__init__(start, end, color, strokeWidth) 1041 self.color = color 1042 self.stroke_width = strokeWidth 1043 self.v1 = sourceV 1044 self.v2 = destV
1045 1046
1047 -class MiniMapCircle(Sprite):
1048 - def __init__(self, position, asPosition, color, scale, building=False):
1049 super(MiniMapCircle, self).__init__(os.path.join("images", 1050 "maps", "minimap_circle.png")) 1051 self.color = color 1052 self.scale = scale 1053 self.position = euclid.Vector2(float(position[0]), float(position[1])) 1054 self.asPosition = asPosition # position of AS associated with this circle 1055 # self.cshape = aabb_to_aa_rect(self.get_AABB()) 1056 self.cshape = collision_model.CircleShape( 1057 euclid.Vector2(x=self.position[0], y=self.position[1]), 14) 1058 if building: 1059 self.building = Sprite(os.path.join( 1060 'images', 'maps', 'minimap_building.png'), position=euclid.Vector2(float(position[0]), float(position[1])))
1061 # self.batch_add(circle, z=MINMAP_CIRCLE_Z) Circle is undefined 1062