Beispiel #1
0
    def __init__(self, image, position=(0, 0), rotation=0, scale=1,
                 opacity=255, color=(255, 255, 255), anchor=None, **kwargs):
    
        if isinstance(image, string_types):
            image = pyglet.resource.image(image)

        self.transform_anchor_x = 0
        self.transform_anchor_y = 0
        self._image_anchor_x = 0
        self._image_anchor_y = 0

        # These need to be forward-defined here because pyglet sprites don't have them.
        self._scale_x = 1
        self._scale_y = 1

        pyglet.sprite.Sprite.__init__(self, image, **kwargs)
        BatchableNode.__init__(self)

        if anchor is None:
            if isinstance(self.image, pyglet.image.Animation):
                anchor = (image.frames[0].image.width // 2,
                          image.frames[0].image.height // 2)
            else:
                anchor = image.width // 2, image.height // 2

        self.image_anchor = anchor

        # group.
        # This is for batching
        self.group = None

        # children group.
        # This is for batching
        self.children_group = None

        #: position of the sprite in (x, y) coordinates
        self.position = position

        #: rotation in degrees of the sprite. Default: 0 degrees
        self.rotation = rotation

        #: scale of the sprite where 1.0 is the default value
        self.scale = scale

        #: additional horizontal-only scale of the sprite where 1.0 is the default value
        self.scale_x = 1

        #: additional vertical-only scale of the sprite where 1.0 is the default value
        self.scale_y = 1

        #: opacity of the sprite where 0 is transparent and 255 is solid
        self.opacity = opacity

        #: color of the sprite in R, G, B format where 0, 0, 0 is black and 
        #: 255, 255, 255 is white
        self.color = color
Beispiel #2
0
    def __init__(self, mapFileName, cm=None, numPlayers = None, AIPlayers = None, seed = None):
        super(Map, self).__init__()
        self.cm = cm

        self.batchableNode = BatchableNode()
        self.batchableNode.position = 0,0
        self.add(self.batchableNode,z=VERTEX_Z)

        # Initiliaze variables - these will be set in parse_map_file.
        self.vertexPositions = {}
        self.edgePositions = defaultdict(
            list)  # key: vid of one end, v: vid of other end

        self.AS = {}  # we can get list of vertices from here
        self.cores = {}  # k: just the cores
        self.vertices = {}  # k: all vertices and cores
        self.edges = []  # list of all edges

        self.startingUnits = defaultdict(lambda: defaultdict(list))
        self.startingResearch = defaultdict(list)
        self.availResearch = []

        self.w = - 1
        # Width of map in terms of num of cells. set in parse_map_file
        self.h = - 1
        # Height of map in terms of num of cells. set in parse_map_file

        self.players = []  # list of pids
        self.AIPlayers = []
        self.pid = 0
        if mapFileName == "random":
            # Generate a random map. Creates a map and writes it to the /maps/ directory. Returns the file name.
            # Set the seed.
            random.seed(seed)
            # Map Dimensions
            numRows = int(random.randint(6 * numPlayers, 14 * numPlayers)) # if 2 players, min of 12x12, max of 28x28
            numCols = int(random.randint(6 * numPlayers, 14 * numPlayers))
         
            # Number of ASes
            numASes = int(math.ceil((numCols * numRows) / 140.0)) # If 2 players, min of 2 ASes, max of 6.

            # Verts per AS
            minVertsPerAS = 2
            maxVertsPerAS = 4

            playerStartUnits = {0:[],1:[]}
            playerResearch = {0:[],1:[]}

            maxCoresPerAS = 2

            mapFileName = self.generate_random_map(numPlayers, numCols, numRows, playerStartUnits, playerResearch, minVertsPerAS, maxVertsPerAS, numASes, maxCoresPerAS, AIPlayers, seed)

        # Parses the map file and sets all relevant information.
        self.parse_map_file(mapFileName)
        
        self.minimap = None  # Starts as None. Eventually stores the instance of MiniMap built off this Map (happens in controller)
Beispiel #3
0
    def __init__(self, image, position=(0, 0), rotation=0, scale=1,
                 opacity=255, color=(255, 255, 255), anchor=None, **kwargs):

        if isinstance(image, string_types):
            image = pyglet.resource.image(image)

        self.transform_anchor_x = 0
        self.transform_anchor_y = 0
        self._image_anchor_x = 0
        self._image_anchor_y = 0

        # These need to be forward-defined here because pyglet<1.3 sprites don't have them.
        self._scale_x = 1
        self._scale_y = 1

        pyglet.sprite.Sprite.__init__(self, image, **kwargs)
        BatchableNode.__init__(self)

        if anchor is None:
            if isinstance(self.image, pyglet.image.Animation):
                anchor = (image.frames[0].image.width // 2,
                          image.frames[0].image.height // 2)
            else:
                anchor = image.width // 2, image.height // 2

        self.image_anchor = anchor

        # group.
        # This is for batching
        self.group = None

        # children group.
        # This is for batching
        self.children_group = None

        #: position of the sprite in (x, y) coordinates
        self.position = position

        #: rotation in degrees of the sprite. Default: 0 degrees
        self.rotation = rotation

        #: scale of the sprite where 1.0 is the default value
        self.scale = scale

        #: additional horizontal-only scale of the sprite where 1.0 is the default value
        self.scale_x = 1

        #: additional vertical-only scale of the sprite where 1.0 is the default value
        self.scale_y = 1

        #: opacity of the sprite where 0 is transparent and 255 is solid
        self.opacity = opacity

        #: color of the sprite in R, G, B format where 0, 0, 0 is black and 
        #: 255, 255, 255 is white
        self.color = color
Beispiel #4
0
    def __init__(self, top_left, pokemon, height=15):
        """

        :param top_left:
        :type top_left: tuple[int, int]
        :param pokemon:
        :type pokemon: pokemon.Pokemon
        :param height:
        :type height: int
        """
        BatchableNode.__init__(self)
        self.x, self.y = top_left
        self.pokemon = pokemon
        self.max_width = 150
        self.height = height
        self.pokemon.push_handlers(self)
Beispiel #5
0
 def _set_scale_y(self, s):
     BatchableNode._set_scale_y(self, s)
     self._update_position()
Beispiel #6
0
 def _set_scale(self, s):
     BatchableNode._set_scale(self, s)
     pyglet.sprite.Sprite._set_scale(self, s)
Beispiel #7
0
 def _set_rotation(self, a):
     BatchableNode._set_rotation(self, a)
     pyglet.sprite.Sprite._set_rotation(self, a)
Beispiel #8
0
    def __init__(self,
                 image,
                 position=(0, 0),
                 rotation=0,
                 scale=1,
                 opacity=255,
                 color=(255, 255, 255),
                 anchor=None):
        '''Initialize the sprite

        :Parameters:
                `image` : string or image
                    name of the image resource or a pyglet image.
                `position` : tuple
                    position of the anchor. Defaults to (0,0)
                `rotation` : float
                    the rotation (degrees). Defaults to 0.
                `scale` : float
                    the zoom factor. Defaults to 1.
                `scale_x` : float
                    additional horizontal-only zoom factor. Defaults to 1.
                `scale_y` : float
                    additional vertical-only zoom factor. Defaults to 1.
                `opacity` : int
                    the opacity (0=transparent, 255=opaque). Defaults to 255.
                `color` : tuple
                    the color to colorize the child (RGB 3-tuple). Defaults to (255,255,255).
                `anchor` : (float, float)
                    (x,y)-point from where the image will be positions, rotated and scaled in pixels. For example (image.width/2, image.height/2) is the center (default).
        '''

        if isinstance(image, string_types):
            image = pyglet.resource.image(image)

        self.transform_anchor_x = 0
        self.transform_anchor_y = 0
        self._image_anchor_x = 0
        self._image_anchor_y = 0

        # These need to be forward-defined here because pyglet sprites don't have them.
        self._scale_x = 1
        self._scale_y = 1

        pyglet.sprite.Sprite.__init__(self, image)
        BatchableNode.__init__(self)

        if anchor is None:
            if isinstance(self.image, pyglet.image.Animation):
                anchor = (image.frames[0].image.width // 2,
                          image.frames[0].image.height // 2)
            else:
                anchor = image.width // 2, image.height // 2

        self.image_anchor = anchor

        # group.
        # This is for batching
        self.group = None

        # children group.
        # This is for batching
        self.children_group = None

        #: position of the sprite in (x,y) coordinates
        self.position = position

        #: rotation degrees of the sprite. Default: 0 degrees
        self.rotation = rotation

        #: scale of the sprite where 1.0 the default value
        self.scale = scale

        #: additional horizontal-only scale of the sprite where 1.0 the default value
        self.scale_x = 1

        #: additional vertical-only scale of the sprite where 1.0 the default value
        self.scale_y = 1

        #: opacity of the sprite where 0 is transparent and 255 is solid
        self.opacity = opacity

        #: color of the sprite in R,G,B format where 0,0,0 is black and 255,255,255 is white
        self.color = color
Beispiel #9
0
 def _set_y(self, y):
     BatchableNode._set_y(self, y)
     pyglet.sprite.Sprite._set_y(self, y)
Beispiel #10
0
 def _set_y(self, y):
     BatchableNode._set_y(self, y)
     pyglet.sprite.Sprite._set_y(self, y)
Beispiel #11
0
 def _set_scale_y(self, s):
     BatchableNode._set_scale_y(self, s)
     self._update_position()
Beispiel #12
0
 def _set_scale(self, s):
     BatchableNode._set_scale(self, s)
     pyglet.sprite.Sprite._set_scale(self, s)
Beispiel #13
0
 def _set_rotation(self, a):
     BatchableNode._set_rotation(self, a)
     pyglet.sprite.Sprite._set_rotation(self, a)
 def getFakeObject(self, position, width = 2, height = 2):
     obj = BatchableNode()
     obj.cshape = cm.AARectShape(position, width // 2,height // 2)
     return obj
Beispiel #15
0
 def _set_position(self, p):
     BatchableNode._set_position(self, p)
     pyglet.sprite.Sprite.set_position(self, *p)
Beispiel #16
0
 def _set_position(self, p):
     BatchableNode._set_position(self, p)
     pyglet.sprite.Sprite.set_position(self, *p)
Beispiel #17
0
 def _set_x(self, x):
     BatchableNode._set_x(self, x)
     pyglet.sprite.Sprite._set_x(self, x)
Beispiel #18
0
 def _set_x(self, x):
     BatchableNode._set_x(self, x)
     pyglet.sprite.Sprite._set_x(self, x)
Beispiel #19
0
    def __init__(self, image, position=(0, 0), rotation=0, scale=1,
                 opacity = 255, color=(255, 255, 255), anchor = None):
        """Initialize the sprite

        :Parameters:
                `image` : string or image
                    name of the image resource or a pyglet image.
                `position` : tuple
                    position of the anchor. Defaults to (0,0)
                `rotation` : float
                    the rotation (degrees). Defaults to 0.
                `scale` : float
                    the zoom factor. Defaults to 1.
                `scale_x` : float
                    additional horizontal-only zoom factor. Defaults to 1.
                `scale_y` : float
                    additional vertical-only zoom factor. Defaults to 1.
                `opacity` : int
                    the opacity (0=transparent, 255=opaque). Defaults to 255.
                `color` : tuple
                    the color to colorize the child (RGB 3-tuple). Defaults to (255,255,255).
                `anchor` : (float, float)
                    (x,y)-point from where the image will be positions, rotated and scaled in pixels. For example (image.width/2, image.height/2) is the center (default).
        """

        if isinstance(image, string_types):
            image = pyglet.resource.image(image)

        self.transform_anchor_x = 0
        self.transform_anchor_y = 0
        self._image_anchor_x = 0
        self._image_anchor_y = 0

        # These need to be forward-defined here because pyglet sprites don't have them.
        self._scale_x = 1
        self._scale_y = 1

        pyglet.sprite.Sprite.__init__(self, image)
        BatchableNode.__init__(self)

        if anchor is None:
            if isinstance(self.image, pyglet.image.Animation):
                anchor = (image.frames[0].image.width // 2,
                          image.frames[0].image.height // 2)
            else:
                anchor = image.width // 2, image.height // 2

        self.image_anchor = anchor

        # group.
        # This is for batching
        self.group = None

        # children group.
        # This is for batching
        self.children_group = None

        #: position of the sprite in (x,y) coordinates
        self.position = position

        #: rotation degrees of the sprite. Default: 0 degrees
        self.rotation = rotation

        #: scale of the sprite where 1.0 the default value
        self.scale = scale

        #: additional horizontal-only scale of the sprite where 1.0 the default value
        self.scale_x = 1

        #: additional vertical-only scale of the sprite where 1.0 the default value
        self.scale_y = 1

        #: opacity of the sprite where 0 is transparent and 255 is solid
        self.opacity = opacity

        #: color of the sprite in R,G,B format where 0,0,0 is black and 255,255,255 is white
        self.color = color
Beispiel #20
0
class Map(scrolling.ScrollableLayer):

    def __init__(self, mapFileName, cm=None, numPlayers = None, AIPlayers = None, seed = None):
        super(Map, self).__init__()
        self.cm = cm

        self.batchableNode = BatchableNode()
        self.batchableNode.position = 0,0
        self.add(self.batchableNode,z=VERTEX_Z)

        # Initiliaze variables - these will be set in parse_map_file.
        self.vertexPositions = {}
        self.edgePositions = defaultdict(
            list)  # key: vid of one end, v: vid of other end

        self.AS = {}  # we can get list of vertices from here
        self.cores = {}  # k: just the cores
        self.vertices = {}  # k: all vertices and cores
        self.edges = []  # list of all edges

        self.startingUnits = defaultdict(lambda: defaultdict(list))
        self.startingResearch = defaultdict(list)
        self.availResearch = []

        self.w = - 1
        # Width of map in terms of num of cells. set in parse_map_file
        self.h = - 1
        # Height of map in terms of num of cells. set in parse_map_file

        self.players = []  # list of pids
        self.AIPlayers = []
        self.pid = 0
        if mapFileName == "random":
            # Generate a random map. Creates a map and writes it to the /maps/ directory. Returns the file name.
            # Set the seed.
            random.seed(seed)
            # Map Dimensions
            numRows = int(random.randint(6 * numPlayers, 14 * numPlayers)) # if 2 players, min of 12x12, max of 28x28
            numCols = int(random.randint(6 * numPlayers, 14 * numPlayers))
         
            # Number of ASes
            numASes = int(math.ceil((numCols * numRows) / 140.0)) # If 2 players, min of 2 ASes, max of 6.

            # Verts per AS
            minVertsPerAS = 2
            maxVertsPerAS = 4

            playerStartUnits = {0:[],1:[]}
            playerResearch = {0:[],1:[]}

            maxCoresPerAS = 2

            mapFileName = self.generate_random_map(numPlayers, numCols, numRows, playerStartUnits, playerResearch, minVertsPerAS, maxVertsPerAS, numASes, maxCoresPerAS, AIPlayers, seed)

        # Parses the map file and sets all relevant information.
        self.parse_map_file(mapFileName)
        
        self.minimap = None  # Starts as None. Eventually stores the instance of MiniMap built off this Map (happens in controller)

    def batch_add(self,cocosNode,z=1):
        self.batchableNode.add(cocosNode,z=z)

    def batch_remove(self,cocosNode):
        self.batchableNode.remove(cocosNode)

    def parse_map_file(self, mapFileName):

        with open(mapFileName, 'r') as f:
            row = 0  # updated when size found in map file
            isCellInfo = False
            for line in f:
                line = line.split()
                if len(line) == 0 or line[0] == "#":
                    continue
                elif line[0] == "MAP":
                    isCellInfo = True
                    self.w = int(line[1]) - 1
                    self.h = int(line[2]) - 1
                elif line[0] == "ENDMAP":
                    isCellInfo = False  # Rot to the end of cell info
                elif line[0] == "EDGES:" or line[0] == "CORE_EDGES:":
                    edgeList = []
                    for edge in line[1:]:
                        formattedEdge = re.findall("[0-9]+|[a-z]+", edge)
                        edgeList.append(formattedEdge)
                    for edge in edgeList:
                        self.edgePositions[edge[0]
                                           ].append(edge[1])  # edge[0] is key
                        self.edgePositions[edge[1]
                                           ].append(edge[0])  # edge[1] is key
                elif line[0] == "AS":
                    vertsInAS = []
                    for i in range(2, len(line)):  # Loop through info in AS line to grab relevant vertices, if only one, do a "n-n"
                        r = re.split('\-', line[i])
                        if r[0].isdigit():
                            vertsInAS += [str(v) for v in range(int(r[0]), int(r[1]) + 1)]
                        else: #add core
                            vertsInAS += [r[0]]
                    self.AS[int(line[1])] = AS(int(line[1]), vertsInAS)  # key is AS's ID
                elif "PLAYER" == line[0]:
                    # Loop through starting units and add them to the
                    # self.playerStartingUnits
                    pid = int(line[1])
                    self.players.append(pid)
                    for unit in line[2:]:
                        unit = unit.split(",")
                        unitType = unit[0]  # For clarity.
                        unitVert = unit[1]
                        self.startingUnits[pid][unitVert].append(unitType)
                elif line[0] == "AI":  
                    self.AIPlayers = line[1:]
                    self.AIPlayers = [int(i) for i in self.AIPlayers]
                    for p in self.AIPlayers:
                        self.players.remove(p)
                elif line[0] == "STARTRESEARCH":
                    pid = int(line[1])
                    for research in line[2:]:
                        self.startingResearch[pid].append(research)
                elif line[0] == "AVAILRESEARCH":
                    for research in line[1:]:
                        self.availResearch.append(research)
                elif isCellInfo == True:
                    col = 0
                    for cell in line:
                        if cell.isalpha() or cell.isdigit():
                            r = self.h - row
                            self.vertexPositions[cell] = (col, r)
                        col += 1
                    row += 1
        f.close()

    def draw_map(self):
        for asID in self.AS.keys():
            curAS = self.AS[asID]

            # create vertices
            for vid in curAS.vids:
                position = self.vertexPositions[str(vid)]
                if vid.isalpha():
                    v = Core(position[0], position[1], vid, asID, pid=self.pid)
                    self.cores[vid] = v
                    self.vertices[vid] = v
                    curAS.cores[vid] = v
                else:
                    v = Vertex(position[0], position[1], vid, asID, pid=self.pid)
                    v.asCircle = ASCircle(v.position, curAS.color)
                    v.color = curAS.color
                    curAS.circles[vid] = v.asCircle
                    v.draw_empty_slot_sprites(self)
                    curAS.vertices[vid] = v
                    self.vertices[vid] = v
                    #selfbatch_add(v.asCircle, z=AS_CIRCLE_Z)
                self.batch_add(v, z=VERTEX_Z)
        for v1, v2s in self.edgePositions.items():
            for v2 in v2s:
                position1 = self.vertexPositions[v1]
                position2 = self.vertexPositions[v2]
                vert1 = self.vertices[v1]
                vert2 = self.vertices[v2]
                if v1.isalpha() or (v2.isdigit() and (int(v1) > int(v2))):
                    # create edges
                    if vert1.asID != vert2.asID:  # create asEdge
                        vert1.borderVertices[vert2.asID].append(vert2)
                        vert2.borderVertices[vert1.asID].append(vert1)
                        edge = ASEdge(
                            position1, position2, vert1, vert2, AS_EDGE_COLOR)
                    else:  # create normal edge
                        edge = Edge(
                            position1, position2, vert1, vert2, EDGE_COLOR)
                    vert1.edges.append(edge)
                    vert2.edges.append(edge)
                    if not v1.isalpha():
                        vert1.adjacentVertices.append(vert2)
                        vert2.adjacentVertices.append(vert1)
                    self.edges.append(edge)

        # draw edges
        for v in self.edges:
            self.add(v, z=EDGE_Z)
            pass

    def generate_random_map(self, numPlayers, numCols, numRows, playerStartUnits, playerResearch, minVertsPerAS, maxVertsPerAS, numASes, maxCoresPerAS, AIPlayers, seed):
        # ---Add vertices and cores to each AS---
        ASvertices, AScores = self.add_verts_and_cores(numRows, numCols, minVertsPerAS, maxVertsPerAS, numASes, maxCoresPerAS)


        # ---Generate the map lines (cells)---
        mapLines, numCols, numRows, ASvertices, AScores = self.create_map_lines(numCols, numRows, ASvertices, AScores, maxVertsPerAS)

        # ---Generate edges in ASes and between ASes---
        edges, coreEdges = self.make_edges(numCols, numRows, ASvertices, AScores)

        # ---Write the map info to a file---
        playerStartUnits = [["Server","CPU"],["Server","CPU"]]
        fullMapName = self.write_map_info_to_file(mapLines, ASvertices, AScores, edges, coreEdges, numPlayers, playerStartUnits, playerResearch, AIPlayers)

        return fullMapName

    def add_verts_and_cores(self, numRows, numCols, minVertsPerAS, maxVertsPerAS, numASes, maxCoresPerAS):
        # Adds vertices and cores to each AS based on random info above.
        # RETURNS: tuple: (ASvertices (list), AScores (list))
        endVert = random.randint(minVertsPerAS, maxVertsPerAS)
        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.

        for i in range(1, numASes):
            # Add one here since we are using range to generate the list.
            ASvertices.append(range(endVert + 1, 1 + random.randint(endVert + 2, endVert + 2 + random.randint(minVertsPerAS, maxVertsPerAS))))
            endVert = ASvertices[i][-1]

        endCore = 'b' # First AS will have 'a' and 'b'

        valList = range(ord('a'), ord(endCore) + 1)
        for j in range(len(valList)):
                valList[j] = chr(valList[j])

        # Generate CORES
        AScores = [valList]
        nextDigit = ''
        for i in range(1, numASes):
            # Fill AScores with cores.

            # Generate int list that corresponds to ASCII vals of chars
            lowerVal = ord(endCore[-1]) + 1
            upperVal = ord(endCore[-1]) + 1 + random.randint(1,maxCoresPerAS)
            if upperVal > 122 and nextDigit == '':
                # TODO: fix this! We have too many cores. Add an a to the '26' digit slot to start going into two digit core count.
                nextDigit = 'a'
                upperVal = (upperVal % 122) + 97
            elif upperVal > 122 and nextDigit != '':
                # We have too many cores. Increment our '26' digit slot.
                nextDigit = chr(ord(nextDigit) + 1)
                upperVal = (upperVal % 122) + 97
            valList = range(lowerVal, upperVal)
            
            # Convert int list to char list
            for j in range(len(valList)):
                valList[j] = nextDigit + chr(valList[j])
            AScores.append(valList)
            endCore = AScores[i][-1]

        return ASvertices, AScores

    def create_map_lines(self, numCols, numRows, ASvertices, AScores, maxVertices):
        # Generate map grid of proper dimensions
        # RETURNS: mapLines (list of lists of chars)
        mapLines = [["-" for i in range(int(numCols))] for j in range(int(numRows))]

        # Figure out where to place the ASes -> Use a bounding box to isolate AS locations
        numASes = len(ASvertices)
        # SET BOUNDING BOX SIZE -> If we want to change how 'spread out' ASes are, this is where to do it!!!
        dimensionFacilitator = maxVertices + 2
        boundingBoxWidth = random.randint(3,dimensionFacilitator)
        dimensionFacilitator -= boundingBoxWidth
        boundingBoxHeight = random.randint(3,min(max(3,dimensionFacilitator),maxVertices))

        # Define initial bounding box
        topLeft = (0,0)
        botRight = (topLeft[0] + boundingBoxWidth, topLeft[1] + boundingBoxHeight)

        # Define boundingBoxes
        boundingBoxes = [(topLeft,botRight)] # stores a tuple of topLeft, bottomRight points that define a boundingBox.

        # Populate the list <boundingBoxes> with the coordinates of all possible boundingBoxes for ASes
        boxesPlaced = 0
        while boxesPlaced < (numASes + numASes):
            # While guarantees we have placed sufficient boundingBoxes.
            if botRight[0] + boundingBoxWidth > numCols:
                # We're at the right edge of mapLines and can't fit another box, so wrap around.
                topLeft = (1,botRight[1] + 3) # +3 so we don't overlap. and to spread the ASes out a bit
            else:
                # Set topLeft to be the next boundingBox over
                topLeft = (botRight[0] + 3, topLeft[1]) # +3 so we don't overlap. and to spread the ASes out a bit
            if (botRight[1] + boundingBoxHeight) > numRows:
                # We're at the bottom and haven't added enough bounding boxes yet. Expand the map a bit.
                numCols += 1
                numRows += 1
                for i in range(len(mapLines)):
                    row = mapLines[i]
                    row.append("-")
                    mapLines[i] = row
                mapLines.append(["-" for row in range(int(numCols))])
            else:
                # Set bottomRight point to be the topLeft point, + boundBoxWidth and + boundBoxHeight
                botRight = (topLeft[0] + boundingBoxWidth, topLeft[1] + boundingBoxHeight)
                boundingBoxes.append((topLeft,botRight))
                boxesPlaced += 1

        # Loop through each AS and add its vertices and cores to a bounding box.
        for i in range(numASes):
            # For clarity
            nextASverts = ASvertices[i]
            nextAScores = AScores[i]

            # Pick a bounding box. Remove the boundingBox from <boundingBoxes>.
            try:
                topLeft, botRight = random.choice(boundingBoxes)
                boundingBoxes.remove((topLeft, botRight))
            except:
                # We've used every bounding box! Can't place any more ASes
                break

            # Add vertices to the AS bounding box.
            vertIndex = 0
            while vertIndex < len(nextASverts):
                vert = nextASverts[vertIndex]
                vertRow = random.randint(topLeft[0], max(min(len(mapLines) - 1, botRight[0]),topLeft[0] + 1)) # Pick a ROW
                vertCol = random.randint(topLeft[1], max(min(len(mapLines[-1]) - 1, botRight[1]),topLeft[1] + 1)) # Pick a COLUMN
                try:
                    while mapLines[vertRow][vertCol] != "-":
                        # Slot we picked was already taken. Look for an empty slot.
                        vertRow = random.randint(topLeft[0], min(len(mapLines) - 1, botRight[0])) # Pick a ROW
                        vertCol = random.randint(topLeft[1], min(len(mapLines[-1]) - 1, botRight[1])) # Pick a COLUMN
                    # Found an empty slot in the bounding box. Place the vertex.
                    mapLines[vertRow][vertCol] = vert
                    vertIndex += 1
                except:
                    ASvertices[i].remove(vert)

                    # Decrement vertices we haven't added yet.
                    for j in range(len(ASvertices[i])):
                        if ASvertices[i][j] > vert:
                            ASvertices[i][j] -= 1
                    # Don't increment our index, we just want to redo the next vert since we removed..

            try:
                # Add cores to the AS bounding box.
                for core in nextAScores:
                    coreRow = random.randint(topLeft[0], max(min(len(mapLines) - 1, botRight[0]),topLeft[0] + 1)) # Pick a ROW
                    coreCol = random.randint(topLeft[1], max(min(len(mapLines[-1]) - 1, botRight[1]),topLeft[1] + 1)) # Pick a COLUMN
                    while mapLines[coreRow][coreCol] != "-":
                        # Slot we picked was already taken. Look for an empty slot.
                        coreRow = random.randint(topLeft[0], min(len(mapLines) - 1, botRight[0])) # Pick a ROW
                        coreCol = random.randint(topLeft[1], min(len(mapLines[-1]) - 1, botRight[1])) # Pick a COLUMN
                    # Found an empty slot in the bounding box. Place the vertex.
                    mapLines[coreRow][coreCol] = core
            except:
                AScores[i].remove(core)

        return mapLines, numCols, numRows, ASvertices, AScores

    def make_edges(self, numCols, numRows, ASvertices, AScores):
        # Randomizes edges between the vertices. Ensures they are connected.
        # RETURNS: <tuple> (edgeList, coreEdgeList)

        edgeList = []
        coreEdgeList = []

        # Generate edge for each individual AS
        for AS in ASvertices:
            # Loop through each AS to get the list of vertices in that AS.
            vertList = sorted(list(AS)) # Copies the vertices in AS to vertList (need to do this since we will end up destroying vertList)
            
            # Generate  a random prufer seq based on the vertices.
            pruferSeq = self.generate_prufer_seq(sorted(list(AS)))
            
            edgeList += self.generate_mst(pruferSeq, vertList)

        # Connect the ASes
        asBorderRouters = []
        for AS in ASvertices:
            if len(AS) > 0:
                asBorderRouters.append(random.choice(AS))

        ASpruferSeq = self.generate_prufer_seq(list(asBorderRouters)) # Once again, have to list() asBorderRouters so it doesn't destroy it.
        
        edgeList += self.generate_mst(ASpruferSeq, asBorderRouters)

        # Generate core edges. # TODO: IMPROVE
        for i in range(len(AScores)):
            for j in range(len(AScores[i])):
                if len(ASvertices[i]) > 0:
                    core = AScores[i][j]
                    randomVertInSameAS = random.choice(ASvertices[i])
                    coreEdgeList.append([randomVertInSameAS, core])

        return edgeList, coreEdgeList

    def generate_prufer_seq(self, vertList):
        # Given a list of vertices, generates a random prufer code.

        # Take lowest and put it in to exhausted.
        if len(vertList) == 0:
            return []
        exhausted = [vertList.pop(0)]
        pruferSeq = []

        while len(vertList) > 0:
            try:
                randVert = random.choice(exhausted)
                pruferSeq.append(randVert)
                new = vertList.pop(0) # remove from vertList and add it to exhausted
                exhausted.append(new)
            except:
                # Handles corner cases.
                break

        return pruferSeq

    def generate_mst(self, pruferSeq, vertList):
        # Build an MST from prufer code.
        if len(pruferSeq) == 0:
            return []
        vertList.remove(pruferSeq[0])
        edgeList = []
        while True:
            if len(pruferSeq) == 1:
                edgeList.append([vertList[0], pruferSeq[0]])
                break
            elif len(vertList) == 2:
                # Done.
                edgeList.append([vertList[0], pruferSeq[0]])
                edgeList.append([vertList[1], pruferSeq[1]])
                break
            else:
                nextVert = None
                for vert in vertList:
                    if vert != pruferSeq[0]:
                        nextVert = vert
                        vertList.remove(vert)
                        break
                otherVert = pruferSeq.pop(0)
                edgeList.append([nextVert, otherVert])
        return edgeList

    def write_map_info_to_file(self, mapLines, ASvertices, AScores, edges, coreEdges, numPlayers, playerStartUnits, playerResearch, AIPlayers):
        # Writes a map file based on the randomized information.
        mapName = "random" + str(random.randint(1, 1000)) + ".map"
        numRows = len(mapLines)
        numCols = len(mapLines[0])
        # DEBUG print "mapName: ", mapName
        fullMapName = os.path.join("maps", "random", mapName)
        newMapFile = file(fullMapName, "w")

        # Add edges
        edgeString = " "
        for edge in edges:
            temp = str(edge).replace(" ", "")  # Remove the space to work with the parser.
            edgeString += temp + " "
        
        # Add coreEdges
        coreEdgeString = " "
        for coreEdge in coreEdges:
            temp = str(coreEdge).replace(" ", "").replace("\'","")  # Remove the space to work with the parser.
            coreEdgeString += temp + " "
        newMapFile.write("EDGES:" + edgeString + "\n")
        newMapFile.write("CORE_EDGES:" + coreEdgeString + "\n")

        for index in range(len(ASvertices)):
            AS = ASvertices[index]
            # Format cores to write
            AScoreList = ""
            for core in AScores[index]:
                AScoreList += core + " "
            # Write the cores and vertices to the AS line
            if len(AS) > 0:
                newMapFile.write("AS " + str(index) + " " + str(AS[0]) + "-" + str(AS[-1]) + " " + AScoreList + "\n")

        newMapFile.write("\n")
        # Write dimensions to the map
        newMapFile.write("MAP " + str(numCols) + " " + str(numRows) + "\n")

        # Write the mapLines
        for line in mapLines:
            newMapFile.write(self.list_to_string(line) + "\n")
        newMapFile.write("ENDMAP\n")
        newMapFile.write("\n")

        # Add player units
        for i in range(0,numPlayers):
            nextUnitString = ""
            for j in range(len(playerStartUnits[i])):
                if len(ASvertices[i]) > 0:
                    # Assign a vertex and/or core to each unit/cpu. 
                    if playerStartUnits[i][j] != "CPU":
                        nextUnitString += playerStartUnits[i][j] + "," + str(random.choice(ASvertices[i])) + " "
                    elif len(AScores) > 0:
                        nextUnitString += playerStartUnits[i][j] + "," + str(random.choice(AScores[i])) + " "
            if len(ASvertices[i]) > 0: 
                newMapFile.write("PLAYER " + str(i) + " " + str(nextUnitString) + "\n")
        if AIPlayers != None:
            newMapFile.write("\n")
            for AI in AIPlayers:
                AIstring = str(AI)
                newMapFile.write("AI " + AIstring + "\n")
        newMapFile.write("\n")
        newMapFile.close()
        return fullMapName

    def list_to_string(self, l):
        # Takes a list and converts its contents to a string. (basically a custom list-> string casting function)
        s = ""
        for item in l:
            s += str(item) + " "
        return s

    def get_path(self, source, dest, pid, troop, action=None):
        '''
            Djikstra's Algorithm from Wikipedia:
            http://en.wikipedia.org/wiki/Dijkstra's_algorithm
        '''

        vertices = []

        for vertex in self.vertices.values():
            if not vertex.is_blocking_troop(troop, action)[0]:
                h = HeapItem(vertex, sys.maxint)
                vertex.heapItem = h
                vertices.append(h)
                if vertex == source:
                    h.depth = 0

        heapq.heapify(vertices)

        while vertices:
            u = heapq.heappop(vertices)
            if u.vertex == dest and u.depth != sys.maxint:
                # Bingo
                path = []
                while u:
                    path.append(u.vertex.vid)
                    u = u.parent
                path.reverse()
                return path

            for vertex in u.vertex.adjacentVertices:
                if not vertex.is_blocking_troop(troop, action)[0]:
                    v = vertex.heapItem

                    alt = u.depth + 1
                    if alt < v.depth:
                        v.depth = alt
                        v.parent = u
                        vertices.append(v)
                        # TODO: make this more efficient, can't use siftdown because of garbage collector
                        heapq.heapify(vertices)
            
        return None

    # DEPRECATED FOR MULTIPLAYER, use get_path instead
    def get_moveable_path(self, sourceVertex, destVertex, pid, troop=None):
        # 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)
        # NOTE: only works for PLAYER move. We need to write account for enemy moving into invisible vertices (changes movability logic)
        # |-> should change this so it works for both. just don't allow exploration beyond an AS if the AS hasn't been discovered yet.
        vertices = sourceVertex.adjacentVertices

        if (type(troop) != EncryptedTroop) and (type(destVertex.building) == Firewall or type(destVertex.building) == Database) and destVertex.building.pid != pid:
            return False

        head = HeapItem(sourceVertex, 0)

        heapVertices = []
        for vertex in vertices:
            # CONSOLIDATE THIS INTO A vertex.isBlocked property
            if vertex.visibilityState > 0 and (type(troop) == EncryptedTroop or type(vertex.building) != Firewall or vertex.building.pid == pid):
                heapVertex = HeapItem(vertex, 1, head)
                heapq.heappush(heapVertices, heapVertex)
            else:
                pass
        numVerticesInMap = len(self.vertices)
        # So we don't have to calculate this each time through the loop.
        deadVerts = []
        while head.vertex != destVertex:
            if len(deadVerts) > numVerticesInMap:
                # NOTE: This condition is really bad... We should probably fix
                # it.
                return False

            head = heapq.heappop(heapVertices)
            newMoves = head.vertex.adjacentVertices

            for vertex in newMoves:
                if vertex.visibilityState > 0 and (type(troop) == EncryptedTroop or type(vertex.building) != Firewall or vertex.building.pid == pid):
                    # Only add a vertex to the path if it's visible, or if it
                    # does not have an Enemy Firewall in the way.
                    heapVertex = HeapItem(vertex, head.depth + 1, head)
                    # Don't add the vertex if we've already found it, or if it
                    # contains an Enemy firewall | BUT we might want to check
                    # to see if we've gotten to that vertex with less depth.
                    if heapVertex.vertex.vid not in [heapVert.vertex.vid for heapVert in heapVertices] and heapVertex.vertex not in deadVerts:
                        # or (heapVertex.vertex in [heapVert.vertex for
                        # heapVert in heapVertices] and heapVertex.head.depth <
                        # heapVertices[heapVertices.indexOf(heapVertex)].head.depth)
                        heapq.heappush(heapVertices, heapVertex)
                else:
                    deadVerts.append(vertex)

        path = []
        while head.getParent():
            path.insert(0, head.vertex)
            head = head.getParent()
        path.insert(0, head.vertex)
        return path

    def get_vertex(self, vid):
        if vid.isalpha():
            return self.cores[vid]
        return self.vertices[vid]