def jungle_island_animals(self):
     """Creats an island that consists of a 3X3 jungle square in tha middle,
     that contains 4 animals(3 Herbivores and one carnivore) for later convenience """
     added_list = [{
         'loc': (3, 3),
         'pop': [{
             'species': 'Herbivore',
             'age': 21,
             'weight': 17.3
         }, {
             'species': 'Herbivore',
             'age': 30,
             'weight': 19.3
         }, {
             'species': 'Herbivore',
             'age': 10,
             'weight': 107.3
         }, {
             'species': 'Carnivore',
             'age': 1,
             'weight': 20.3
         }]
     }]
     a = Island("OOOOO\nOJJJO\nOJJJO\nOJJJO\nOOOOO")
     a.add_animals(added_list)
     b = Herbivores()
     return a, b
 def jungle_island_animals(self):
     """
     Function to create an island that can be used for testing the Herbivore-class
     :return: Returns the Island-class with a map and animals and the Herbivore-class
     """
     added_list = [{
         'loc': (3, 3),
         'pop': [{
             'species': 'Herbivore',
             'age': 21,
             'weight': 17.3
         }, {
             'species': 'Herbivore',
             'age': 30,
             'weight': 19.3
         }, {
             'species': 'Herbivore',
             'age': 10,
             'weight': 107.3
         }, {
             'species': 'Herbivore',
             'age': 1,
             'weight': 20.3
         }]
     }]
     a = Island("OOOOO\nOJJJO\nOJJJO\nOJJJO\nOOOOO")
     a.add_animals(added_list)
     b = Herbivores()
     return a, b
 def test_not_enough_herbs_to_eat(self):
     """Tests that the animal eats what is left and nothing more when they cannot fill their F"""
     animal_list = [{
         'loc': (1, 1),
         'pop': [{
             'species': 'Carnivore',
             'age': 5,
             'weight': 53.3
         }, {
             'species': 'Herbivore',
             'age': 500,
             'weight': 20
         }, {
             'species': 'Herbivore',
             'age': 500,
             'weight': 20
         }]
     }]
     a = Island('OOO\nOJO\nOOO')
     b = Carnivores()
     c = Herbivores()
     a.add_animals(animal_list)
     b.set_new_params({'beta': 1, 'F': 50, 'DeltaPhiMax': 0.0001})
     c.calculate_fitness((1, 1), a.herbs)
     b.calculate_fitness((1, 1), a.carns)
     b.carnivores_eat((1, 1), a, a.carns)
     assert len(a.herbs[(1, 1)]) == 0
     assert a.carns[(1, 1)][0]['weight'] == 93.3
 def test_more_migration(self):
     """Testing that the animals moves into every tile and in every direction"""
     animals = [{
         'loc': (2, 2),
         'pop': [{
             'species': 'Herbivore',
             'age': 5,
             'weight': 13.3
         }]
     }, {
         'loc': (2, 2),
         'pop': [{
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }]
     }]
     a = Island("OOOOO\nOJJJO\nOJJJO\nOJJJO\nOOOOO")
     b = Herbivores()
     a.add_animals(animals)
     for i in range(a.rader):
         for j in range(a.col):
             a.set_food((i, j))
     b.calculate_fitness((2, 2), a.herbs)
     for _ in range(20):
         b.migration_calculations(a, a.herbs)
         b.migration_execution(a, a.herbs)
     for i in range(1, 3):
         for j in range(1, 3):
             assert (i, j) in a.herbs.keys()
 def test_invalid_moving(self):
     """Tests that the animals does not move to tiles that they are not allowed to enter"""
     population = [{
         'loc': (2, 2),
         'pop': [{
             'species': 'Herbivore',
             'age': 5,
             'weight': 13.3
         }]
     }, {
         'loc': (2, 2),
         'pop': [{
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }]
     }]
     a = Island("OOOOO\nOOOOO\nOOJOO\nOOMOO\nOOOOO")
     b = Herbivores()
     for i in range(a.rader):
         for j in range(a.col):
             a.set_food((i, j))
     a.add_animals(population)
     b.calculate_fitness((2, 2), a.herbs)
     for _ in range(30):
         b.migration_calculations(a, a.herbs)
         b.migration_execution(a, a.herbs)
     assert (2, 1) not in a.herbs.keys()
     assert len(a.herbs[(2, 2)]) == 9
 def test_invalid_moving_carns(self):
     """ Tests invalid moving is not happening"""
     population1 = [{
         'loc': (2, 2),
         'pop': [{
             'species': 'Carnivore',
             'age': 5,
             'weight': 13.3
         }]
     }, {
         'loc': (2, 2),
         'pop': [{
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }]
     }]
     a = Island("OOOOO\nOOMOO\nOOJOO\nOOMOO\nOOJOO")
     b = Carnivores()
     c = Herbivores()
     a.add_animals(population1)
     b.calculate_fitness((2, 2), a.carns)
     b.migration_calculations(a, c, a.carns)
     assert len(a.carns[(2, 2)]) == 9
     assert (3, 2) not in a.carns.keys()
     assert len(b.idx_for_animals_to_remove) == 0
     b.migration_execution(a, a.carns)
     assert len(a.carns[(2, 2)]) == 9
 def test_add_animals(self):
     """
     Testing add_animals and total_animals_per_species methods
     """
     map_str = """   OOOOOOOOOOOO
                     OMSOOOOOSMMO
                     OOOOOOOOOOOO"""
     island = Island(map_str)
     animals = [
         {
             "loc": (1, 1),
             "pop": [
                 {
                     "species": "Herbivore",
                     "age": 10,
                     "weight": 10.0
                 },
                 {
                     "species": "Carnivore",
                     "age": 11,
                     "weight": 11.0
                 },
             ],
         },
         {
             "loc": (1, 2),
             "pop": [
                 {
                     "species": "Herbivore",
                     "age": 10,
                     "weight": 10.0
                 },
                 {
                     "species": "Herbivore",
                     "age": 11,
                     "weight": 11.0
                 },
                 {
                     "species": "Carnivore",
                     "age": 12,
                     "weight": 12.0
                 },
             ],
         },
     ]
     island.add_animals(animals)
     assert island.total_animals_per_species('Herbivore') == 3
     assert island.total_animals_per_species('Carnivore') == 2
 def test_carnivores_eat_sometimes_when_they_have_somewhat_higher_fitness(
         self):
     """
     Testing that the carnivores do eat with some kind of probability when their fitness is somewhat higher than the
     herbivores
     """
     animal_list = [{
         'loc': (1, 1),
         'pop': [{
             'species': 'Carnivore',
             'age': 1,
             'weight': 53.3
         }, {
             'species': 'Herbivore',
             'age': 1,
             'weight': 23.3
         }, {
             'species': 'Herbivore',
             'age': 1,
             'weight': 23.3
         }, {
             'species': 'Herbivore',
             'age': 1,
             'weight': 23.3
         }, {
             'species': 'Herbivore',
             'age': 1,
             'weight': 23.3
         }]
     }]
     a = Island('OOO\nOJO\nOOO')
     b = Carnivores()
     c = Herbivores()
     a.add_animals(animal_list)
     b.set_new_params({'beta': 1, 'F': 50})
     c.calculate_fitness((1, 1), a.herbs)
     b.calculate_fitness((1, 1), a.carns)
     for _ in range(40):
         b.carnivores_eat((1, 1), a, a.carns)
     assert len(a.herbs[(1, 1)]) < 4
     assert len(a.herbs[(1, 1)]) != 0
 def test_carnivore_leaves_food(self):
     """Testing that the carnivores stops eating when it has received f amount of food"""
     animal_list = [{
         'loc': (1, 1),
         'pop': [{
             'species': 'Carnivore',
             'age': 5,
             'weight': 53.3
         }, {
             'species': 'Carnivore',
             'age': 5,
             'weight': 53.3
         }, {
             'species': 'Herbivore',
             'age': 500,
             'weight': 13.3
         }, {
             'species': 'Herbivore',
             'age': 500,
             'weight': 13.3
         }, {
             'species': 'Herbivore',
             'age': 500,
             'weight': 13.3
         }, {
             'species': 'Herbivore',
             'age': 500,
             'weight': 13.3
         }]
     }]
     a = Island('OOO\nOJO\nOOO')
     b = Carnivores()
     c = Herbivores()
     a.add_animals(animal_list)
     b.set_new_params({'beta': 1, 'F': 5, 'DeltaPhiMax': 0.0001})
     c.calculate_fitness((1, 1), a.herbs)
     b.calculate_fitness((1, 1), a.carns)
     b.carnivores_eat((1, 1), a, a.carns)
     assert len(a.herbs[(1, 1)]) == 2
     assert a.carns[(1, 1)][0]['weight'] == 58.3
     assert a.carns[(1, 1)][1]['weight'] == 58.3
 def test_carnivores_does_not_eat_with_lower_fitness(self):
     """Testing that the carnivores do not eat when they have lower fitness than the herbivores"""
     animal_list = [{
         'loc': (1, 1),
         'pop': [{
             'species': 'Carnivore',
             'age': 500,
             'weight': 53.3
         }, {
             'species': 'Herbivore',
             'age': 1,
             'weight': 23.3
         }, {
             'species': 'Herbivore',
             'age': 1,
             'weight': 23.3
         }, {
             'species': 'Herbivore',
             'age': 1,
             'weight': 23.3
         }, {
             'species': 'Herbivore',
             'age': 1,
             'weight': 23.3
         }]
     }]
     a = Island('OOO\nOJO\nOOO')
     b = Carnivores()
     c = Herbivores()
     a.add_animals(animal_list)
     b.set_new_params({'beta': 1, 'F': 50})
     c.calculate_fitness((1, 1), a.herbs)
     b.calculate_fitness((1, 1), a.carns)
     for _ in range(40):
         b.carnivores_eat((1, 1), a, a.carns)
     assert len(a.herbs[(1, 1)]) == 4
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.
        """
        self.landscapes = {'O': Ocean,
                           'S': Savannah,
                           'M': Mountain,
                           'J': Jungle,
                           'D': Desert}
        self.landscapes_with_parameters = [Savannah, Jungle]

        self.animal_species = {'Carnivore': Carnivore, 'Herbivore': Herbivore}

        for char in island_map.replace('\n', ''):
            if char not in self.landscapes:
                raise ValueError('This given string contains unknown '
                                 'geographies')

        lengths = [len(line) for line in island_map.splitlines()]
        if len(set(lengths)) > 1:
            raise ValueError('This given string is not uniform')
        self.island_map = island_map
        self._map = Island(island_map)
        random.seed(seed)
        self.add_population(ini_pop)

        if ymax_animals is None:
            self.ymax_animals = 20000
        else:
            self.ymax_animals = ymax_animals

        if cmax_animals is None:
            self.cmax_animals = {'Herbivore': 5, 'Carnivore': 5}
        else:
            self.cmax_animals = cmax_animals

        if img_base is None:
            self.img_base = DEFAULT_GRAPHICS_DIR + DEFAULT_GRAPHICS_NAME
        else:
            self.img_base = img_base

        self.img_fmt = img_fmt
        self.img_counter = 0

        self.vis = None
        self._year = 0
        self.final_year = None

    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 in self.animal_species:
            species_type = self.animal_species[species]
            animal = species_type()
            animal.set_parameters(params)
        else:
            raise TypeError(species + ' parameters can\'t be assigned, '
                                      'there is no such data type')

    def set_landscape_parameters(self, landscape, params):
        """
        Set parameters for landscape type.

        :param landscape: String, code letter for landscape
        :param params: Dict with valid parameter specification for landscape
        """
        if landscape in self.landscapes:
            landscape_type = self.landscapes[landscape]
            if landscape_type in \
                    self.landscapes_with_parameters:
                landscape_type.set_parameters(params)
            else:
                raise ValueError(landscape + ' parameter is not valid')

        else:
            raise TypeError(landscape + ' parameters can\'t be assigned, '
                                        'there is no such data type')

    def simulate(self, num_years, vis_years=200, img_years=None):
        """
        Run simulation while visualizing the result.

        :param num_years: number of years to simulate
        :param vis_years: years between visualization updates
        :param img_years: years between visualizations saved to files
        (default: vis_years)

        Image files will be numbered consecutively.
        """
        if img_years is None:
            img_years = vis_years

        self.final_year = self._year + num_years
        self.setup_graphics()
        if self._year > 1:
            self.vis.generate_animal_graphs(self.final_year,
                                            self.ymax_animals,
                                            recreate=True)

        while self._year < self.final_year:
            if self._year % vis_years == 0:
                self.update_graphics()

            if (self._year + 1) % img_years == 0:
                self.save_graphics()

            self._map.life_cycle()
            self._year += 1

            df = self.animal_distribution
            df.to_csv('results/data.csv', sep='\t', encoding='utf-8')

    def setup_graphics(self):
        """
        Setup the graphics
        """
        map_dims = self._map.map_dims

        if self.vis is None:
            fig = plt.figure()
            self.vis = Graphics(self.island_map,
                                fig, map_dims)

            self.vis.generate_island_graph()
            self.vis.generate_animal_graphs(self.final_year, self.ymax_animals)

            self.vis.animal_dist_graphs()

    def update_graphics(self):
        """
        Updates graphics with current data.
        """
        df = self.animal_distribution
        rows, cols = self._map.map_dims
        dist_matrix_carnivore = np.array(df[['Carnivore']]).reshape(rows, cols)
        dist_matrix_herbivore = np.array(df[['Herbivore']]).reshape(rows, cols)

        # updates the line graphs
        herb_count, carn_count = list(self.num_animals_per_species.values())
        self.vis.update_graphs(self._year, herb_count, carn_count)

        self.vis.update_herbivore_dist(dist_matrix_herbivore)
        self.vis.update_carnivore_dist(dist_matrix_carnivore)
        plt.pause(1e-6)
        self.vis.set_year(self._year)

    def save_graphics(self):
        """
        Save the graphics
        """
        if self.img_base is None:
            return

        plt.savefig('{base}_{num:05d}.{type}'.format(base=self.img_base,
                                                     num=self.img_counter,
                                                     type=self.img_fmt))
        self.img_counter += 1

    def add_population(self, population):
        """
        Add a population to the island

        :param population: List of dictionaries specifying population
        """
        self._map.add_animals(population)

    def make_movie(self, movie_fmt=DEFAULT_MOVIE_FORMAT):
        """Create MPEG4 movie from visualization images saved."""
        if self.img_base is None:
            raise RuntimeError('No filename defines')

        if movie_fmt == 'mp4':
            try:
                subprocess.check_call([FFMPEG_BINARY,
                                       '-i',
                                       '{}_%05d.png'.format(self.img_base),
                                       '-y',
                                       '-profile:v', 'baseline',
                                       '-level', '3.0',
                                       '-pix_fmt', 'yuv420p',
                                       '{}.{}'.format(self.img_base,
                                                      movie_fmt)])
            except subprocess.CalledProcessError as err:
                raise RuntimeError('Error: ffmpeg failed with: {}'.format(err))
        elif movie_fmt == 'gif':
            try:
                subprocess.check_call([CONVERT_BINARY,
                                       '-delay', '1',
                                       '-loop', '0',
                                       '{}_*.png'.format(self.img_base),
                                       '{}.{}'.format(self.img_base,
                                                      movie_fmt)])
            except subprocess.CalledProcessError as err:
                raise RuntimeError(
                    'Error: convert failed with: {}'.format(err))
        else:
            raise ValueError('Unknown movie format')

    @property
    def year(self):
        """Last year simulated."""
        return self._year

    @property
    def num_animals(self):
        """Total number of animals on island."""
        animal_count = 0
        for species in self.animal_species:
            animal_count += self._map.total_animals_per_species(species)
            return animal_count

    @property
    def num_animals_per_species(self):
        """Number of animals per species in island, as dictionary."""
        num_fauna_per_species = {}
        for species in self.animal_species:
            num_fauna_per_species[species] = self._map.\
                total_animals_per_species(species)
        return num_fauna_per_species

    @property
    def animal_distribution(self):
        """Pandas DataFrame with animal count per species for each cell
        on island."""
        animal_df = []
        rows, cols = self._map.map_dims
        for row in range(rows):
            for col in range(cols):
                cell = self._map.cells[row, col]
                animal_count = cell.cell_fauna_count
                animal_df.append({'Row': row, 'Col': col,
                                  'Herbivore': animal_count['Herbivore'],
                                  'Carnivore': animal_count['Carnivore']})
        return pd.DataFrame(animal_df)
class BioSim:
    def __init__(self,
                 island_map,
                 ini_pop,
                 seed,
                 ymax_animals=None,
                 cmax_animals=None,
                 img_base=None,
                 img_fmt="png",
                 hist_specs=None):
        """
        :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 hist_specs: Specifications for histograms, see below
        :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}
        hist_specs is a dictionary with one entry per property for which a histogram
        shall be shown. \n
        For each property, a dictionary providing the maximum value and the bin width must be
        given, e.g., \n
        {'weight': {'max': 80, 'delta': 2}, 'fitness': {'max': 1.0, 'delta': 0.05}} \n
        Permitted properties are 'weight', 'age', 'fitness'. \n
        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. \n
        img_base should contain a path and beginning of a file name. \n
        """

        self.landscapes = {
            'W': Water,
            'L': Lowland,
            'H': Highland,
            'D': Desert
        }
        self.landscapes_with_parameters = [Highland, Lowland]

        self.animal_species = {'Carnivore': Carnivore, 'Herbivore': Herbivore}

        for char in island_map.replace('\n', ''):
            if char not in self.landscapes:
                raise ValueError(
                    'This given string contains unknown geographies')

        lengths = [len(line) for line in island_map.splitlines()]
        if len(set(lengths)) > 1:
            raise ValueError('This given string is not uniform')
        self.island_map = island_map
        self._map = Island(island_map)
        np.random.seed(seed)
        self.add_population(ini_pop)

        if ymax_animals is None:
            self.ymax_animals = 20000
        else:
            self.ymax_animals = ymax_animals

        if cmax_animals is None:
            self.cmax_animals = {'Herbivore': 50, 'Carnivore': 20}
        else:
            self.cmax_animals = cmax_animals

        if img_base is None:
            self.img_base = DEFAULT_GRAPHICS_DIR + DEFAULT_GRAPHICS_NAME
        else:
            self.img_base = img_base

        if hist_specs is None:
            self._hist_specs = {
                'weight': {
                    'max': 80,
                    'delta': 2
                },
                'fitness': {
                    'max': 1.0,
                    'delta': 0.05
                },
                'age': {
                    'max': 80,
                    'delta': 2
                }
            }

        self.img_fmt = img_fmt
        self.img_counter = 0

        self.vis = None
        self._year = 0
        self.final_year = None

    def set_animal_parameters(self, species, params):
        """
        Set parameters for animal species. \n

        :param species: String, name of animal species \n
        :param params: Dict with valid parameter specification for species \n
        """
        if species in self.animal_species:
            species_type = self.animal_species[species]
            animal = species_type()
            animal.set_parameters(params)
        else:
            raise TypeError(
                species +
                ' parameters cant be assigned,there is no such data type')

    def set_landscape_parameters(self, landscape, params):
        """
        Set parameters for landscape type. \n
        :param landscape: String, code letter for landscape \n
        :param params: Dict with valid parameter specification for landscape \n
        """
        if landscape in self.landscapes:
            landscape_type = self.landscapes[landscape]
            if landscape_type in self.landscapes_with_parameters:
                landscape_type.set_parameters(params)
            else:
                raise ValueError(landscape + ' parameter is not valid')

        else:
            raise TypeError(landscape +
                            ' parameters cannot be assigned, there is no such '
                            'data type')

    def simulate(self, num_years, vis_years=200, img_years=None):
        """
        Run simulation while visualizing the result. \n
        :param num_years: number of years to simulate \n
        :param vis_years: years between visualization updates \n
        :param img_years: years between visualizations saved to files \n
        (default: vis_years) \n
        Image files will be numbered consecutively. \n
        """
        if img_years is None:
            img_years = vis_years

        self.final_year = self._year + num_years
        self.setup_graphics()
        if self._year > 1:
            self.vis.create_animal_graphs(self.final_year,
                                          self.ymax_animals,
                                          recreate=True)

        while self._year < self.final_year:
            if self._year % vis_years == 0:
                self.update_graphics()

            if (self._year + 1) % img_years == 0:
                self.save_graphics()

            self._map.life_cycle_in_rossumoya()
            self._year += 1

            df = self.animal_distribution
            df.to_csv('data.csv', sep='\t', encoding='utf-8')

    def setup_graphics(self):
        """
        Setup the graphics \n
        """
        map_dims = self._map.map_dims

        if self.vis is None:
            fig = plt.figure(figsize=(16, 9))
            self.vis = Graphics(self.island_map, fig, map_dims)

            self.vis.create_island_graph()
            self.vis.create_animal_graphs(self.final_year, self.ymax_animals)

            self.vis.animal_distribution_graphs()
            self.vis.create_histograms_setup()

    def update_graphics(self):
        """
        Updates graphics with current data. \n
        """
        df = self.animal_distribution
        rows, cols = self._map.map_dims
        dist_matrix_carnivore = np.array(df[['Carnivore']]).reshape(rows, cols)
        dist_matrix_herbivore = np.array(df[['Herbivore']]).reshape(rows, cols)

        # updates the line graphs
        herb_count, carn_count = list(self.num_animals_per_species.values())
        self.vis.update_graphs(self._year, herb_count, carn_count)

        self.vis.update_herbivore_distribution(dist_matrix_herbivore)
        self.vis.update_carnivore_distribution(dist_matrix_carnivore)

        # histogram
        self.vis.update_histogram(fit_list=self.animals_fitness,
                                  age_list=self.animal_ages,
                                  wt_list=self.animal_weights)
        # plt.pause(1)
        plt.pause(1e-6)
        self.vis.set_year

    def save_graphics(self):
        """
        Save the graphics \n
        """
        if self.img_base is None:
            return

        plt.savefig('{base}_{num:05d}.{type}'.format(base=self.img_base,
                                                     num=self.img_counter,
                                                     type=self.img_fmt))
        self.img_counter += 1

    def add_population(self, population):
        """
        Add a population to the island \n

        :param population: List of dictionaries specifying population \n
        """
        self._map.add_animals(population)

    def make_movie(self, movie_fmt=DEFAULT_MOVIE_FORMAT):
        """
        Source: Hans Ekkehard Plesser, Randviz project \n
        Create MPEG4 movie from visualization images saved. \n
        """
        if self.img_base is None:
            raise RuntimeError('No filename defined')

        if movie_fmt == 'mp4':
            try:
                subprocess.check_call([
                    FFMPEG_BINARY, '-i', '{}_%05d.png'.format(self.img_base),
                    '-y', '-profile:v', 'baseline', '-level', '3.0',
                    '-pix_fmt', 'yuv420p',
                    '{}.{}'.format(self.img_base, movie_fmt)
                ])
            except subprocess.CalledProcessError as err:
                raise RuntimeError('Error: ffmpeg failed with: {}'.format(err))
        elif movie_fmt == 'gif':
            try:
                subprocess.check_call([
                    CONVERT_BINARY, '-delay', '1', '-loop', '0',
                    '{}_*.png'.format(self.img_base),
                    '{}.{}'.format(self.img_base, movie_fmt)
                ])
            except subprocess.CalledProcessError as err:
                raise RuntimeError(
                    'Error: convert failed with: {}'.format(err))
        else:
            raise ValueError('Unknown movie format')

    @property
    def year(self):
        """
        Last year simulated. \n
        """
        return self._year

    @property
    def num_animals(self):
        """
        Total number of animals on island. \n
        """
        animal_count = 0
        for species in self.animal_species:
            animal_count += self._map.number_of_animals_per_species(species)
            return animal_count

    @property
    def num_animals_per_species(self):
        """
        Number of animals per species in island, as dictionary. \n
        """
        num_fauna_per_species = {}
        for species in self.animal_species:
            num_fauna_per_species[
                species] = self._map.number_of_animals_per_species(species)
        return num_fauna_per_species

    @property
    def animal_distribution(self):
        """
        Pandas DataFrame with animal count per species for each cell on the island. \n
        """
        animal_df = []
        rows, cols = self._map.map_dims
        for row in range(rows):
            for col in range(cols):
                cell = self._map.cells[row, col]
                animal_count = cell.cell_fauna_count
                animal_df.append({
                    'Row': row,
                    'Col': col,
                    'Herbivore': animal_count['Herbivore'],
                    'Carnivore': animal_count['Carnivore']
                })
        return pd.DataFrame(animal_df)

    @property
    def animal_weights(self):
        """
        Returns a dictionary with weights of each animal according to the species /n
        """

        weights = {"Herbivore": [], "Carnivore": []}
        rows, cols = self._map.map_dims
        for row in range(rows):
            for col in range(cols):
                cell = self._map.cells[row][col]
                for animal in cell.fauna_dict["Herbivore"]:
                    weights['Herbivore'].append(animal.weight)
                for animal in cell.fauna_dict["Carnivore"]:
                    weights['Carnivore'].append(animal.weight)

        return weights

    @property
    def animals_fitness(self):
        """
        Returns a dictionary with weights of each animal according to the species /n
        """
        fitness = {"Herbivore": [], "Carnivore": []}
        rows, cols = self._map.map_dims
        for row in range(rows):
            for col in range(cols):
                cell = self._map.cells[row][col]
                for animal in cell.fauna_dict["Herbivore"]:
                    fitness['Herbivore'].append(animal.animal_fitness)
                for animal in cell.fauna_dict["Carnivore"]:
                    fitness['Carnivore'].append(animal.animal_fitness)

        return fitness

    @property
    def animal_ages(self):
        """
        Returns a dictionary with weights of each animal according to the species /n
        """
        age = {"Herbivore": [], "Carnivore": []}
        rows, cols = self._map.map_dims
        for row in range(rows):
            for col in range(cols):
                cell = self._map.cells[row][col]
                for animal in cell.fauna_dict["Herbivore"]:
                    age['Herbivore'].append(animal.age)
                for animal in cell.fauna_dict["Carnivore"]:
                    age['Carnivore'].append(animal.age)

        return age
 def test_migration_carnivores(self):
     """Tests that the carnivores migrate and that they do not move before the execution-function is called"""
     population = [{
         'loc': (3, 2),
         'pop': [{
             'species': 'Herbivore',
             'age': 5,
             'weight': 13.3
         }]
     }, {
         'loc': (3, 2),
         'pop': [{
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }]
     }]
     population1 = [{
         'loc': (3, 1),
         'pop': [{
             'species': 'Carnivore',
             'age': 5,
             'weight': 13.3
         }]
     }, {
         'loc': (3, 1),
         'pop': [{
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }]
     }]
     a = Island("OOOOO\nOJJJO\nOJJJO\nOJJJO\nOJJJO\nOOOOO")
     b = Carnivores()
     c = Herbivores()
     a.add_animals(population)
     a.add_animals(population1)
     b.calculate_fitness((3, 1), a.carns)
     b.migration_calculations(a, c, a.carns)
     assert len(a.carns[(3, 1)]) == 9
     assert (3, 2) not in a.carns.keys()
     assert len(b.idx_for_animals_to_remove) > 0
     b.migration_execution(a, a.carns)
     assert len(a.carns[(3, 2)]) > 0
     assert len(a.carns[(3, 1)]) < 9
 def test_migration(self):
     """
     Tests that two of the animals gets moved with the given inputs and seed
     """
     population = [{
         'loc': (3, 1),
         'pop': [{
             'species': 'Herbivore',
             'age': 5,
             'weight': 13.3
         }]
     }, {
         'loc': (3, 1),
         'pop': [{
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }]
     }]
     a = Island("OOOOO\nOOOOO\nOJOOO\nOJOOO\nOOOOO\nOOOOO")
     b = Herbivores()
     a.add_animals(population)
     a.set_food((3, 1))
     a.set_food((2, 1))
     a.set_food((3, 2))
     a.set_food((3, 0))
     a.set_food((4, 1))
     b.calculate_fitness((3, 1), a.herbs)
     b.migration_calculations(a, a.herbs)
     assert len(a.herbs[(3, 1)]) == 9
     assert len(b.idx_for_animals_to_remove) > 0
     b.migration_execution(a, a.herbs)
     assert len(a.herbs[(3, 1)]) == 7
     assert len(a.herbs[(2, 1)]) == 2
 def test_more_migration(self):
     """Tests that the carnivores migrates to all cells"""
     population = [{
         'loc': (2, 2),
         'pop': [{
             'species': 'Herbivore',
             'age': 5,
             'weight': 13.3
         }]
     }, {
         'loc': (2, 2),
         'pop': [{
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Herbivore',
             'age': 4,
             'weight': 10
         }]
     }]
     population1 = [{
         'loc': (2, 2),
         'pop': [{
             'species': 'Carnivore',
             'age': 5,
             'weight': 13.3
         }]
     }, {
         'loc': (2, 2),
         'pop': [{
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }, {
             'species': 'Carnivore',
             'age': 4,
             'weight': 10
         }]
     }]
     a = Island("OOOOO\nOJJJO\nOJJJO\nOJJJO\nOJJJO\nOOOOO")
     b = Carnivores()
     c = Herbivores()
     a.add_animals(population)
     a.add_animals(population1)
     for i in range(a.rader):
         for j in range(a.col):
             a.set_food((i, j))
     b.calculate_fitness((2, 2), a.carns)
     c.calculate_fitness((2, 2), a.herbs)
     for _ in range(20):
         c.migration_calculations(a, a.herbs)
         c.migration_execution(a, a.herbs)
     for _ in range(20):
         b.migration_calculations(a, c, a.carns)
         b.migration_execution(a, a.carns)
     for i in range(1, 3):
         for j in range(1, 3):
             assert (i, j) in a.carns.keys()
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'])