def test_breeding_carnivores(self): """Tests if carnivores can give birth, and that the children get added to the same tile as their parents""" added_list = [{ 'loc': (3, 3), 'pop': [{ 'species': 'Carnivore', 'age': 20, 'weight': 17.3 }, { 'species': 'Carnivore', 'age': 30, 'weight': 19.3 }, { 'species': 'Carnivore', 'age': 10, 'weight': 107.3 }, { 'species': 'Carnivore', 'age': 1, 'weight': 20.3 }] }] a = self.jungle_island_animals()[0] b = Carnivores() a.set_food((3, 3)) a.add_animals(added_list) len_list1 = len(a.carns[(3, 3)]) b.calculate_fitness((3, 3), a.carns) for _ in range(10): b.breeding((3, 3), a, a.carns) len_list2 = len(a.carns[(3, 3)]) assert len_list2 > len_list1
class BioSim: def __init__(self, island_map, ini_pop, seed, ymax_animals=None, cmax_animals=None, img_base=None, img_fmt='png'): """ :param island_map: Multi-line string specifying island geography :param ini_pop: List of dictionaries specifying initial population :param seed: Integer used as random number seed :param ymax_animals: Number specifying y-axis limit for graph showing animal numbers :param cmax_animals: Dict specifying color-code limits for animal densities :param img_base: String with beginning of file name for figures, including path :param img_fmt: String with file type for figures, e.g. 'png' If ymax_animals is None, the y-axis limit should be adjusted automatically. If cmax_animals is None, sensible, fixed default values should be used. cmax_animals is a dict mapping species names to numbers, e.g., {'Herbivore': 50, 'Carnivore': 20} If img_base is None, no figures are written to file. Filenames are formed as '{}_{:05d}.{}'.format(img_base, img_no, img_fmt) where img_no are consecutive image numbers starting from 0. img_base should contain a path and beginning of a file name. """ # variables for code/setup/classes self.ini_pop = ini_pop self.island_map = island_map self.tot_num_years = 0 # variables for movie/save if img_base is not None: self._img_base = img_base else: self._img_base = None self._img_fmt = img_fmt self._img_ctr = 0 # Variables for visualization/plot self.ax1 = None self.ax2 = None self.ax3 = None self.ax4 = None self.line = None self.line2 = None if ymax_animals is None: self.ymax_animals = 20000 else: self.ymax_animals = ymax_animals if cmax_animals is None: self.cmax_animals = {'Herbivore': 100, 'Carnivore': 100} else: self.cmax_animals = cmax_animals # Initialization, setting classes and seed self.island = Island(island_map) self.island.limit_map_vals() self.herbivores = Herbivores() self.carnivores = Carnivores() self.animal = Animal() self.setup_simulation() np.random.seed(seed) random.seed(seed) def set_animal_parameters(self, species, params): """ Set parameters for animal species. :param species: String, name of animal species :param params: Dict with valid parameter specification for species """ if species == 'Herbivore': self.herbivores.set_new_params(params) else: self.carnivores.set_new_params(params) def set_landscape_parameters(self, terrain, params): """ Set parameters for landscape types. :param terrain: Tells which terrain shall get new parameters :param params: Dict with valid parameter specification for landscape """ self.island.set_new_params(terrain, params) def setup_simulation(self): """ Function to set up the simulation, sets up the food and inserts the initial population. """ for i in range(self.island.rader): for j in range(self.island.col): self.island.set_food((i, j)) self.island.add_animals(self.ini_pop) def simulate_one_year(self): """ Function used to simulate one year """ for i in range(self.island.rader): for j in range(self.island.col): pos = (i, j) self.island.grow_food(pos) self.herbivores.calculate_fitness(pos, self.island.herbs) self.herbivores.sort_by_fitness(pos, self.island.herbs) self.herbivores.animals_eat(pos, self.island, self.island.herbs) self.herbivores.sort_before_getting_hunted( pos, self.island.herbs) self.carnivores.calculate_fitness(pos, self.island.carns) self.carnivores.sort_by_fitness(pos, self.island.carns) self.carnivores.carnivores_eat(pos, self.island, self.island.carns) self.herbivores.breeding(pos, self.island, self.island.herbs) self.carnivores.calculate_fitness(pos, self.island.carns) self.carnivores.breeding(pos, self.island, self.island.carns) self.herbivores.calculate_fitness(pos, self.island.herbs) self.carnivores.calculate_fitness(pos, self.island.carns) self.herbivores.migration_calculations(self.island, self.island.herbs) self.carnivores.migration_calculations(self.island, self.herbivores, self.island.carns) self.herbivores.migration_execution(self.island, self.island.herbs) self.carnivores.migration_execution(self.island, self.island.carns) for i in range(self.island.rader): for j in range(self.island.col): pos = (i, j) self.herbivores.aging(pos, self.island.herbs) self.carnivores.aging(pos, self.island.carns) self.herbivores.loss_of_weight(pos, self.island.herbs) self.carnivores.loss_of_weight(pos, self.island.carns) self.herbivores.calculate_fitness(pos, self.island.herbs) self.carnivores.calculate_fitness(pos, self.island.carns) self.herbivores.death(pos, self.island.herbs) self.carnivores.death(pos, self.island.carns) def simulate(self, num_years, vis_years=1, img_years=None): """ Function used to simulate num_years, visualizing every vis_years and taking a picture every img_years. :param num_years: Number of years to simulate :param vis_years: Number that indicates how many years between each visualization :param img_years: Number that indicates how many years between taking images, if None a picture will be taken for each visualization """ self._setup_graphics(num_years) for year in range(num_years): if year % vis_years == 0: self._update_graphics() if year % img_years == 0: self._save_graphics() self.simulate_one_year() self.tot_num_years += 1 def add_population(self, population): """ Add a population to the island :param population: List of dictionaries specifying population """ self.island.add_animals(population) def _setup_graphics(self, num_years): """ This function is used to setup the figure of the grapichs, it creates a figure with 4 subplots and initializes the animal lines which are plotted in the upper right subplot. :param num_years: :return: """ fig = plt.figure() axt = fig.add_axes([0.4, 0.8, 0.2, 0.2]) axt.axis('off') self.ax1 = plt.subplot2grid((2, 2), (0, 0)) self.ax2 = plt.subplot2grid((2, 2), (0, 1)) self.ax3 = plt.subplot2grid((2, 2), (1, 0)) self.ax4 = plt.subplot2grid((2, 2), (1, 1)) self.ax2.set_xlim(self.year + 1, self.year + num_years) self.ax2.set_ylim(0, self.ymax_animals) self.line = self.ax2.plot(np.arange(num_years + self.year + 1), np.full(num_years + self.year + 1, np.nan), 'b-')[0] self.line2 = self.ax2.plot(np.arange(num_years + self.year + 1), np.full(num_years + self.year + 1, np.nan), 'r-')[0] def _update_graphics(self): """ Updates graphics with current data. """ plt.suptitle('Years since the simulation started: ' + str(self.year) + ' years') rgb_value = { "O": mcolors.to_rgba("navy"), "J": mcolors.to_rgba("forestgreen"), "S": mcolors.to_rgba("#e1ab62"), "D": mcolors.to_rgba("yellow"), "M": mcolors.to_rgba("lightslategrey") } kart_rgb = [[rgb_value[column] for column in row] for row in self.island_map.split()] self.ax1.imshow(kart_rgb) self.ax1.set_xticks(range(len(kart_rgb[0]))) self.ax1.set_xticklabels(range(1, 1 + len(kart_rgb[0]))) self.ax1.set_yticks(range(len(kart_rgb))) self.ax1.set_yticklabels(range(1, 1 + len(kart_rgb))) self.ax1.axis('scaled') self.ax1.set_title('Map') # Upper right subplot ydata = self.line.get_ydata() ydata2 = self.line2.get_ydata() ydata[self.year] = self.num_animals_per_species['Herbivore'] ydata2[self.year] = self.num_animals_per_species['Carnivore'] self.line.set_ydata(ydata) self.line2.set_ydata(ydata2) self.ax2.set_title('Total number of animals') self.ax3.imshow(self.animal_distribution_for_plot[:, :, 0], 'Greens', vmax=self.cmax_animals['Herbivore']) self.ax3.set_xticks(range(len(kart_rgb[0]))) self.ax3.set_xticklabels(range(1, 1 + len(kart_rgb[0]))) self.ax3.set_yticks(range(len(kart_rgb))) self.ax3.set_yticklabels(range(1, 1 + len(kart_rgb))) self.ax3.axis('scaled') self.ax3.set_title('Herbivore distribution: ' + str(self.num_animals_per_species['Herbivore'])) self.ax4.imshow(self.animal_distribution_for_plot[:, :, 1], 'Reds', vmax=self.cmax_animals['Carnivore']) self.ax4.set_xticks(range(len(kart_rgb[0]))) self.ax4.set_xticklabels(range(1, 1 + len(kart_rgb[0]))) self.ax4.set_yticks(range(len(kart_rgb))) self.ax4.set_yticklabels(range(1, 1 + len(kart_rgb))) self.ax4.axis('scaled') self.ax4.set_title('Carnivore distribution: ' + str(self.num_animals_per_species['Carnivore'])) plt.pause(1e-9) def _save_graphics(self): """Saves graphics to file if file name given.""" if self._img_base is None: return plt.savefig('{base}_{num:05d}.{type}'.format(base=self._img_base, num=self._img_ctr, type=self._img_fmt)) self._img_ctr += 1 def make_movie(self): import glob img_array = [] for filename in glob.glob(self._img_base + '*' + self._img_fmt): img = cv2.imread(filename) height, width, layers = img.shape size = (width, height) img_array.append(img) out = cv2.VideoWriter('project.avi', cv2.VideoWriter_fourcc(*'DIVX'), 6, size) for i in range(len(img_array)): out.write(img_array[i]) out.release() @property def year(self): """Last year simulated.""" return self.tot_num_years @property def num_animals(self): """Total number of animals on island.""" return sum(self.island.num_animals()) @property def num_animals_per_species(self): """Number of animals per species in island, as dictionary.""" herb, carn = self.island.num_animals() return {'Herbivore': herb, 'Carnivore': carn} @property def animal_distribution_for_plot(self): """Pandas DataFrame with animal count per species for each cell on island.""" animal_list = np.zeros((self.island.rader, self.island.col, 2)) for i in range(self.island.rader): for j in range(self.island.col): if (i, j) in self.island.herbs.keys(): animal_list[i, j, 0] = len(self.island.herbs[(i, j)]) if (i, j) in self.island.carns.keys(): animal_list[i, j, 1] = len(self.island.carns[(i, j)]) return animal_list @property def animal_distribution(self): a = np.zeros((self.island.rader * self.island.col, 4)) for i in range(self.island.rader): a[i * self.island.col:(i + 1) * self.island.col, 0] = i c = np.arange(self.island.col) for i in range(self.island.rader): a[i * self.island.col:(i + 1) * self.island.col, 1] = c for pos in self.island.herbs.keys(): a[pos[0] * self.island.col + pos[1], 2] += len(self.island.herbs[pos]) for pos in self.island.carns.keys(): a[pos[0] * self.island.col + pos[1], 3] += len(self.island.carns[pos]) return pd.DataFrame(a, columns=['Row', 'Col', 'Herbivore', 'Carnivore'])