""" Student portion of Zombie Apocalypse mini-project """ #http://www.codeskulptor.org/#user43_yCIa8eafEzbL3Am_20.py import random import poc_grid import poc_queue import poc_zombie_gui # global constants EMPTY = 0 FULL = 1 FOUR_WAY = 0 EIGHT_WAY = 1 OBSTACLE = 5 HUMAN = 6 ZOMBIE = 7 class Apocalypse(poc_grid.Grid): """ Class for simulating zombie pursuit of human on grid with obstacles """ def __init__(self, grid_height, grid_width, obstacle_list = None, zombie_list = None, human_list = None): """ Create a simulation of given size with given obstacles, humans, and zombies """ poc_grid.Grid.__init__(self, grid_height, grid_width) if obstacle_list != None: for cell in obstacle_list: self.set_full(cell[0], cell[1]) if zombie_list != None: self._zombie_list = list(zombie_list) else: self._zombie_list = [] if human_list != None: self._human_list = list(human_list) else: self._human_list = [] def clear(self): """ Set cells in obstacle grid to be empty Reset zombie and human lists to be empty """ poc_grid.Grid.clear(self) self._zombie_list = [] self._human_list = [] return def add_zombie(self, row, col): """ Add zombie to the zombie list """ self._zombie_list.append((row,col)) return def num_zombies(self): """ Return number of zombies """ return len(self._zombie_list) def zombies(self): """ Generator that yields the zombies in the order they were added. """ for zombie in self._zombie_list: yield zombie def add_human(self, row, col): """ Add human to the human list """ self._human_list.append((row,col)) def num_humans(self): """ Return number of humans """ return len(self._human_list) def humans(self): """ Generator that yields the humans in the order they were added. """ for human in self._human_list: yield human def print_all(self): """ prints out a grid showing where humans, zombies, and blocks are. """ grid_copy = list(self._cells) for human in self.humans(): grid_copy[human[0]][human[1]] = 6 for zombie in self.zombies(): grid_copy[zombie[0]][zombie[1]] = 7 ans = "" for row in range(self._grid_height): ans += str(grid_copy[row]) ans += "\n" return ans def compute_distance_field(self, entity_type): """ Function computes and returns a 2D distance field Distance at member of entity_list is zero Shortest paths avoid obstacles and use four-way distances """ boundary = poc_queue.Queue() visited = poc_grid.Grid(self.get_grid_height(),self.get_grid_width()) distance_field = [[self.get_grid_height()*self.get_grid_width() for dummy_col in range(self.get_grid_width())] for dummy_row in range(self.get_grid_height())] #Que entities to boundary if entity_type == HUMAN: for human in self.humans(): boundary.enqueue(human) elif entity_type == ZOMBIE: for zombie in self.zombies(): boundary.enqueue(zombie) else: print "invalid input" return #set entity positions in visited grid as "full" and in distance_field as "0" for item in boundary: visited.set_full(item[0], item[1]) distance_field[item[0]][item[1]] = 0 #iterate to add new cells surrounding boundary members to boundary #set new cells as full in visited and #steps in dist_field; dequeue original boundary cell while boundary: location = boundary.dequeue() location_dist = distance_field[location[0]][location[1]] adjacent = visited.four_neighbors(location[0], location[1]) for adj in adjacent: if self.is_empty(adj[0],adj[1]) and visited.is_empty(adj[0],adj[1]): boundary.enqueue(adj) visited.set_full(adj[0], adj[1]) distance_field[adj[0]][adj[1]] = location_dist+1 return distance_field def is_cell_empty(self, cell): """ returns if a cell is empty """ #takes single tuple instead of row, col... allows function to be used as filter return self.is_empty(cell[0], cell[1]) def move_humans(self, zombie_distance_field): """ Function that moves humans away from zombies, diagonal moves are allowed """ new_humans = [] for human in self.humans(): possible_moves = filter(self.is_cell_empty, self.eight_neighbors(human[0], human[1])) best_move = self.get_best_move(human, possible_moves, zombie_distance_field, True) new_humans.append(best_move) self._human_list = new_humans def move_zombies(self, human_distance_field): """ Function that moves zombies towards humans, no diagonal moves are allowed """ new_zombies = [] for zombie in self.zombies(): possible_moves = filter(self.is_cell_empty, self.four_neighbors(zombie[0], zombie[1])) best_move = self.get_best_move(zombie, possible_moves, human_distance_field, False) new_zombies.append(best_move) self._zombie_list = new_zombies def get_best_move(self, current_position, possible_moves, distance_field, flee): """ returns one of possible best moves for a zombie or human """ best_move = [] best_move.append(current_position) for move in possible_moves: if flee: if distance_field[move[0]][move[1]] > distance_field[best_move[0][0]][best_move[0][1]]: best_move = [move] elif distance_field[move[0]][move[1]] == distance_field[best_move[0][0]][best_move[0][1]]: best_move.append(move) elif not flee: if distance_field[move[0]][move[1]] < distance_field[best_move[0][0]][best_move[0][1]]: best_move = [move] elif distance_field[move[0]][move[1]] == distance_field[best_move[0][0]][best_move[0][1]]: best_move.append(move) random.shuffle(best_move) return best_move.pop() # Start up gui for simulation - You will need to write some code above # before this will work without errors poc_zombie_gui.run_gui(Apocalypse(30, 40)) #game = Apocalypse(3, 3, [], [(2, 2)], [(1, 1)]) #dist = [[4, 3, 2], [3, 2, 1], [2, 1, 0]] #print game #print #print "Print all:" #print game.print_all() #print ##dist = game.compute_distance_field(HUMAN) #print "distance field:" #for row in range(len(dist)): # print dist[row] #game.move_humans(dist) #print "Moved humans" #print game.print_all() #i = 0 #j = 1 #rows = 10 #cols = 10 #blocks = [(3,8)] ##zombies = [(i,col) for col in range(cols)] ##humans = [(j,col) for col in range(cols)] #zombies = [(0,4)] #humans = [(1,4)] # #game = Apocalypse(rows, cols, blocks, zombies, humans) #dist = game.compute_distance_field(ZOMBIE) #print "distance field:" #for row in range(len(dist)): # print dist[row] #print #print game.print_all() #game.move_humans(dist) #print game.print_all() #game.clear() #print game