def getNeighbors(node, endNode, wrld): listOfNeighbors = [] # Go through neighboring cells for dx in [-1, 0, 1]: # Avoid out-of-bounds access if (node.x + dx >= 0) and (node.x + dx < wrld.width()): for dy in [-1, 0, 1]: # Avoid out-of-bounds access if (node.y + dy >= 0) and (node.y + dy < wrld.height()): # add node if it's the end node, useful if calculating distance to monster if endNode.x == node.x + dx and endNode.y == node.y + dy: listOfNeighbors.append( customEntities.Node(node.x + dx, node.y + dy, parent=node)) # Is this cell not a wall or an explosion? elif not (wrld.wall_at(node.x + dx, node.y + dy) or wrld.explosion_at(node.x + dx, node.y + dy)): if not (dx == 0 and dy == 0): listOfNeighbors.append( customEntities.Node(node.x + dx, node.y + dy, parent=node)) # All done return listOfNeighbors
def calculateAStarPath(startNode, endNode, wrld, listOfMonsters, shouldPathAroundMonsters): openNodes = [] closedNodes = [] # End node is position 7, 18 startNode = customEntities.Node(startNode.x, startNode.y) openNodes.append(startNode) while len(openNodes) > 0: minNode = openNodes[0] currIndex = 0 # find the smallest node fval and append it for index, currNode in enumerate(openNodes): if currNode.fval < minNode.fval: minNode = currNode currIndex = index openNodes.pop(currIndex) closedNodes.append(minNode) if minNode.x == endNode.x and minNode.y == endNode.y: path = [] currNode = minNode while currNode is not None: path.append(currNode) currNode = currNode.parent return True, path[::-1] for neighbor in getNeighbors(minNode, endNode, wrld): isClosed = False isOpen = False sameNode = None nodeIndex = None for closedNode in closedNodes: if neighbor.x == closedNode.x and neighbor.y == closedNode.y: isClosed = True break for index, openNode in enumerate(openNodes): if neighbor.x == openNode.x and neighbor.y == openNode.y: isOpen = True sameNode = openNode nodeIndex = index break if not isClosed: neighbor.gval = minNode.gval + 1 neighbor.hval = chebyshevDistance(neighbor, endNode, listOfMonsters, shouldPathAroundMonsters) neighbor.fval = neighbor.gval + neighbor.hval if isOpen and neighbor.fval < sameNode.fval: openNodes[nodeIndex] = neighbor elif not isOpen: openNodes.append(neighbor) return False, closedNodes
def calculateCharacterPath(self, endNode, wrld, shouldPathAroundMonsters): startNode = customEntities.Node(self.x, self.y) path = astar.calculateAStarPath(startNode, endNode, wrld, self.monsters, shouldPathAroundMonsters) if path[0]: self.path = path[1] else: # if I can't find the exit after searching all possible nodes, I should find the node closest to the exit. # > Also, flag that spot for bombing minNode = startNode minNode.hval = math.inf for node in path[1]: node.hval = astar.manhattanDistance(node, endNode) if node.hval < minNode.hval: minNode = node if astar.manhattanDistance(minNode, endNode) == astar.manhattanDistance(self, endNode) \ and self.bombTimer == 0 and len(wrld.explosions.items()) == 0: self.placeBombAtEnd = True else: self.calculateCharacterPath(minNode, wrld, True)
def do(self, wrld): # recalculate the AStar path and distance from exit every time self.distanceFromExit = astar.chebyshevDistance( customEntities.Node(self.x, self.y), customEntities.Node(7, 18), self.monsters, False) self.calculateCharacterPath(customEntities.Node(7, 18), wrld, True) self.pathIterator = 0 if self.shouldPanic and self.bombTimer == 0 and self.explosionTimer == 0: self.placeBombAtEnd = True if self.bombTimer == 0 and self.explosionTimer > 0: self.explosionTimer -= 1 if self.explosionTimer == 0: self.bombPosition = None if self.bombTimer > 0: self.bombTimer -= 1 if self.bombTimer == 0: self.numberOfBombsPlaced += 1 self.explosionTimer = wrld.expl_duration + 1 if len(self.path) == 1: self.runAway(wrld) if not self.monsters or len(self.monsters) != len( wrld.monsters.items()): self.monsters = [] for key, monsterlist in wrld.monsters.items(): for monster in monsterlist: self.monsters.append( customEntities.Monster(monster.x, monster.y)) else: i = 0 for key, monsterlist in wrld.monsters.items(): for monster in monsterlist: self.monsters[i].x = monster.x self.monsters[i].y = monster.y i += 1 for monster in self.monsters: monster.checkVelocity(wrld) if self.bombTimer <= 2 and self.bombPosition is not None: self.runAway(wrld) if self.checkIfNearMonster(self, wrld): # if my path ends at the exit selfIsCloserToExitThanMonster = True myPathToExit = astar.calculateAStarPath(self, customEntities.Node(7, 18), wrld, self.monsters, False) # if there isn't currently a path to the exit if not myPathToExit[0]: selfIsCloserToExitThanMonster = False else: for key, monsterlist in wrld.monsters.items(): for monster in monsterlist: if len(myPathToExit[1]) >= len( astar.calculateAStarPath( monster, customEntities.Node(7, 18), wrld, self.monsters, False)[1]): selfIsCloserToExitThanMonster = False break if self.shouldPanic and self.bombTimer == 0 and self.explosionTimer <= wrld.expl_duration - 1: self.placeBombAtEnd = True elif not selfIsCloserToExitThanMonster or ( self.bombTimer <= 2 and self.bombPosition is not None): # pick the spot out of the 8 cardinal directions that is least near to a monster. self.runAway(wrld) else: self.calculateCharacterPath(customEntities.Node(7, 18), wrld, False) if self.placeBombAtEnd: self.bombPosition = customEntities.Node(self.x, self.y) self.place_bomb() self.runAway(wrld) self.bombTimer = wrld.bomb_time self.placeBombAtEnd = False self.pathIterator += 1 self.move(self.path[self.pathIterator].x - self.x, self.path[self.pathIterator].y - self.y) return
def runAway(self, wrld): # put current position into the path path = [customEntities.Node(self.x, self.y)] possibleNodes = [] shouldRun = False # start by setting the lowest sum to infinity highestSum = -math.inf # get the set of neighbors, including ourself neighbors = astar.getNeighbors(self, customEntities.Node(7, 18), wrld) neighbors.append(customEntities.Node(self.x, self.y)) # iterate over the available spots of the eight cardinal directions for node in neighbors: currSum = 0 # if there's an explosion, don't add if wrld.explosion_at(node.x, node.y): continue shouldContinue = False # TODO account for other players bombs # do not include the nodes that would be in bomb's path of explosion if self.bombTimer <= 2 and self.bombPosition is not None: bombRange = wrld.expl_range for x in range(-bombRange, bombRange + 1): if node.x == self.bombPosition.x + x and node.y == self.bombPosition.y: shouldContinue = True break for y in range(-bombRange, bombRange + 1): if node.x == self.bombPosition.x and node.y == self.bombPosition.y + y: shouldContinue = True break if shouldContinue: continue node.hval = math.inf # for monster in monsterlist: for monster in self.monsters: pathBetweenSelfAndMonster = astar.calculateAStarPath( node, monster, wrld, self.monsters, False) myDistance = len( astar.calculateAStarPath(self, monster, wrld, self.monsters, False)[1]) # if there is a path between myself and the monster if pathBetweenSelfAndMonster[0]: distance = len(pathBetweenSelfAndMonster[1]) # distance = self.chebyshevDistance(node, monster, False) if myDistance < self.distanceSmart and monster.type == "smart": if distance < (self.distanceSmart - 1): # try very hard not to get into detection range currSum -= 5 elif distance < (self.distanceSmart - 2): currSum -= 10 shouldRun = True currSum += distance node.hval = astar.manhattanDistance(node, monster) elif myDistance < self.distanceStupid + 1: currSum += distance if distance < self.distanceStupid - 1: shouldRun = True if currSum == highestSum: possibleNodes.append(node) elif currSum > highestSum: highestSum = currSum possibleNodes = [node] pathToStart = (astar.calculateAStarPath(self, customEntities.Node(5, 13), wrld, self.monsters, True))[1] self.calculateCharacterPath(customEntities.Node(7, 18), wrld, True) if not possibleNodes: # accept death. self.path = [ customEntities.Node(self.x, self.y), customEntities.Node(self.x, self.y) ] return bestNode = None highestSum = -math.inf if shouldRun: for node in possibleNodes: if node.hval > highestSum: bestNode = node highestSum = node.hval if bestNode is None: # append possible nodes for node in possibleNodes: # if node is in the path to the end if len(self.path) > 1 and self.path[ 1].x == node.x and self.path[1].y == node.y: bestNode = node break if bestNode is None: for node in possibleNodes: # if node is in the path to the start if len(pathToStart) > 1 and pathToStart[ 1].x == node.x and pathToStart[1].y == node.y: bestNode = node break # if no node was added before, just add the first node if bestNode is not None: path.append(bestNode) else: path.append(possibleNodes[0]) self.path = path